Skip to content

Add AuthZEN Access Request OAuth Profile (AROP) draft#531

Open
mcguinness wants to merge 6 commits into
mainfrom
access-request-oauth-profile
Open

Add AuthZEN Access Request OAuth Profile (AROP) draft#531
mcguinness wants to merge 6 commits into
mainfrom
access-request-oauth-profile

Conversation

@mcguinness

@mcguinness mcguinness commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds a new draft, the AuthZEN Access Request OAuth Profile (AROP), a companion to the AuthZEN Access Request and Approval Profile (ARAP).

ARAP defines a single completion mode (reevaluate) and explicitly leaves completion modes that bind approval to a specific issuance flow, such as OAuth token issuance where the issued token is the decision representation, to a downstream profile. AROP is that downstream profile for OAuth.

When a request is evaluated as an AuthZEN Access Evaluation and the decision is denied but requestable, AROP resolves the denial through an ARAP access request and completes by issuing an access token that carries the approved authorization. The completion semantics are the same regardless of how the asynchronous wait is carried; what varies is the OAuth mechanism.

Three transport bindings

The async wait is pluggable. AROP defines a transport-neutral processing core and three peer bindings:

  • Deferred Token Response (draft-gerber-oauth-deferred-token-response): the client makes a token request, the AS returns a deferral_code, and the client polls the token endpoint.
  • CIBA (OpenID Connect Client-Initiated Backchannel Authentication): the client makes a backchannel authentication request, the AS returns an auth_req_id, and the principal approves out of band on a decoupled device (poll/ping redeem at the token endpoint; push delivers directly).
  • OAuth Transaction Authorization Challenge (draft-rosomakho-oauth-txn-challenge): a protected resource returns a signed challenge for an operation the presented token cannot authorize; the client takes it to the AS, which issues a txn-bound token re-presented to the resource.

PEP placement

The bindings differ in where the requestable denial originates, and AROP accommodates all three ARAP deployments: AS as PEP (DTR, CIBA), resource as PEP (transaction challenge, where the resource signs the challenge), and both (the resource asserts the challenge and the AS also applies its own policy). The examples cover all three.

Design

  • Composition, not new protocol. No new OAuth grant type, endpoint, or parameter of its own; composes the three async mechanisms (transport), Rich Authorization Requests (the requested and approved authorization), and ARAP (the approval lifecycle), over any originating grant including Token Exchange.
  • Decision is not carried on the wire. The request carries a RAR request (or, for the transaction binding, a resource-signed challenge); the evaluating party maps it to an AuthZEN Access Evaluation internally; the issued token carries the approved grant, never an AuthZEN decision object. (Contrast with draft-brossard-oauth-rar-authzen, noted in the draft.)
  • Honest authority model. Token issuance moves the authoritative decision to issuance time, bounded by token lifetime; the draft says so plainly and bounds the credential (including refresh and token exchange) by the approval expiry. Completion remains token issuance, not reevaluate.
  • AuthZEN Integration section maps the OAuth request to AuthZEN/ARAP messages and covers evaluation_id vs PDP-signed binding_token denial-binding for co-located vs separate Access Request Service.
  • Examples: a full worked DTR trace showing every internal message (AuthZEN Access Evaluation request/response and ARAP submission, task, and re-evaluation) marked off the OAuth wire, plus CIBA self-approval and transaction challenge examples.

Notes

  • Includes a folder-local Makefile; the draft builds clean (kramdown-rfc -> xml2rfc) with all references resolving.
  • External-spec claims (DTR, CIBA, transaction challenge, RFC 9396) were verified against their sources.
  • Open seams are scoped explicitly rather than hand-waved: the per-authorization_details-type mapping to AuthZEN resource/action is deployment- or vocabulary-defined, and partial issuance across multiple authorization_details is out of scope.

New companion profile defining the OAuth token-issuance completion mode that
the Access Request and Approval Profile (ARAP) defers to a downstream spec.

A client requests a token (completion_mode=deferred, RAR authorization_details);
the AS evaluates it as an AuthZEN Access Evaluation; on a denied-but-requestable
decision it runs an ARAP access request and returns a Deferred Token Response
(draft-gerber-oauth-deferred-token-response) with a deferral_code; the client
polls the token endpoint; on approval the AS re-evaluates and issues an access
token carrying the approved authorization, bounded by the approval expiry.

Adds no new OAuth grant type or parameters: composes DTR (transport), RAR
(requested and approved authorization), and ARAP (approval lifecycle). States
the issuance-time authority shift honestly (decision at issuance, bounded by
token lifetime) and keeps the decision behind the AS so the token carries the
grant, not an AuthZEN decision object.
Restructures the profile so the asynchronous wait is pluggable across
three OAuth mechanisms rather than hardwired to the Deferred Token
Response.

- Transport-neutral Common Processing core: resolution outcomes,
  continuation handle, polling/completion, idempotent submission, and
  access request input, defined once and shared by all bindings.
- Three peer binding sections (Deferred Token Response, CIBA, OAuth
  Transaction Authorization Challenge) plus a per-transport comparison
  table. CIBA covers poll/ping/push delivery and self-approval;
  txn-challenge covers the resource-signed challenge flow.
- PEP-placement axis as a deployment dimension: AS as PEP (DTR, CIBA),
  resource as PEP (txn-challenge), or both. Examples cover all three.
- DTR example expanded into a full worked trace: AuthZEN Access
  Evaluation request/response and ARAP submission, task, and
  re-evaluation, each internal message marked off the OAuth wire.
- evaluation_id vs binding_token denial-binding shown for co-located
  vs separate Access Request Service.
- Adds CIBA (OIDC-CIBA Core 1.0) and txn-challenge references.

Completion mode remains token issuance (not reevaluate); no new OAuth
grant type, endpoint, or parameter of its own.

- **Allow.** The transport's normal success response is returned synchronously (a token, for the request mechanisms that issue one directly).
- **Deny, not requestable.** A synchronous error is returned in the transport's error form: at the token endpoint, `invalid_authorization_details` ({{RFC9396}}) when the denial concerns the requested `authorization_details`, `invalid_scope` when it concerns requested `scope`, and otherwise `invalid_grant`. The pending and terminal-denial codes (`authorization_pending`, `access_denied`, `expired_token`, `slow_down`) are reserved for the deferred path and MUST NOT be returned here.
- **Deny, requestable.** The evaluating party MUST NOT issue a token. It submits an {{ARAP}} access request on the client's behalf, binds it to a continuation handle, and returns the transport's pending response (carrying the handle, `expires_in`, and `interval`).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I read this as if for any client that signalled completion_mode=deferred, a transaction challenge for example will be automatically submitted on client's behalf?
If so, it presents several challenges:

  • The OAuth Transaction Authorization Challenge uses a dedicated endpoint, which may be different than the one the client used. That endpoint may have different client authentication requirements that were so far not fulfilled.
  • Client indicated it supports DTR but may not support transaction challenge, which may lead flow to break.
  • Transaction challenge draft allows the challenge JWTs audience to indicate a different AS than the one used so far. Submitting an ARAP on client's behalf may require crossing AS boundaries into another AS, which may fail because the originating AS cannot provide required client authentication on its behalf. Not to mention client may not be recognized by the indicated AS

@mcguinness mcguinness Jul 4, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this is a really useful catch. The premise (a completion_mode=deferred
request being turned into a transaction challenge on the client's behalf) is not
what the profile intends, but I can see the text invites that reading, so let me
clarify and then tighten it.

Two things are being conflated, and both are on me to make clearer:

  1. "ARAP access request" is not the OAuth Transaction Authorization Challenge.
    The "submits an ARAP access request on the client's behalf" step is the PEP
    submitting the AuthZEN/ARAP access request to its Access Request Service
    (the approval workflow), which the profile defines as internal to the
    deployment and not exposed to the client. It is an internal approval
    submission, not an outbound OAuth request to any authorization server. "On the
    client's behalf" just means the PEP opens the approval so the client does not
    have to construct AuthZEN or ARAP messages itself.

  2. The three transports are distinct entry paths, each with its own opt-in, and
    the evaluating party never converts one into another.
    completion_mode=deferred
    selects the Deferred Token Response binding and nothing else. CIBA is
    entered by a backchannel authentication request; the Transaction Authorization
    Challenge is entered only when a protected resource returns a signed challenge
    and the client chooses to present it. A client that opted into DTR always gets
    the DTR pending response (deferral_code at the same token endpoint); it is
    never handed a transaction challenge.

On your three specific points, with that framing:

  • Dedicated endpoint / different client auth. Correct that the
    transaction_authorization_endpoint is distinct with its own client
    authentication, but that endpoint is only reached in the transaction challenge
    binding, which the client enters explicitly (it received a challenge and chose
    to present it). A DTR or CIBA request never routes there.
  • Client supports DTR but not transaction challenge. Agreed, and that is
    exactly why they are separate opt-in bindings advertised independently in
    discovery. A DTR client is never asked to speak transaction challenge; there is
    nothing to break.
  • Challenge aud naming a different AS / crossing AS boundaries. In the
    transaction challenge binding the client, not the originating AS, presents
    the challenge to the AS named in aud, and authenticates to that AS per the
    transaction challenge draft. There is no server-to-server forwarding on the
    client's behalf and no boundary crossed by the originating deployment, so the
    "originating AS cannot authenticate to the target AS" problem does not arise.
    The AS in aud then submits the ARAP access request to its own internal
    Access Request Service.

To prevent this misreading I clarified the "Common Processing" section: state
that the ARAP access request goes to the internal Access Request Service; that the
continuation handle and pending response are always the same transport the
request was made under; and that a client's opt-in to one binding never causes the
evaluating party to initiate a different one. I will also make the "the client
presents the challenge" step in the transaction binding explicit that the client
drives the cross-AS step.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the clarification, I get it now

- **Allow.** The transport's normal success response is returned synchronously (a token, for the request mechanisms that issue one directly).
- **Deny, not requestable.** A synchronous error is returned in the transport's error form: at the token endpoint, `invalid_authorization_details` ({{RFC9396}}) when the denial concerns the requested `authorization_details`, `invalid_scope` when it concerns requested `scope`, and otherwise `invalid_grant`. The pending and terminal-denial codes (`authorization_pending`, `access_denied`, `expired_token`, `slow_down`) are reserved for the deferred path and MUST NOT be returned here.
- **Deny, requestable.** The evaluating party MUST NOT issue a token. It submits an {{ARAP}} access request on the client's behalf, binds it to a continuation handle, and returns the transport's pending response (carrying the handle, `expires_in`, and `interval`).
- **Deny, requestable, but the client did not opt into asynchronous completion.** Where a binding requires the client to signal that it can handle a deferred outcome (DTR's `completion_mode=deferred`), and the client did not, the evaluating party MUST NOT defer. It returns the same synchronous denial error it would for a non-requestable denial; the requestable nature is not exposed. A client that wishes to obtain access through approval MUST opt in.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not provide client the requestable details so it can then opt in and use them for remediation?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you asking about a shape similar to the following:

  1. The RS (as PEP) evaluates, gets a denied-but-requestable decision, and serializes it as your insufficient_authorization / authorization_remediation challenge (draft-zehavi-oauth-rar-metadata) carrying the required authorization_details (and an authorization_reference).
  2. The client takes those authorization_details to the AS in a fresh grant, opting into deferred completion (DTR) or via CIBA.
  3. The AS runs the ARAP approval flow and, on approval, issues an access token whose granted authorization_details fulfill the operation, audience-restricted to the RS.
  4. The client re-presents the token; the RS matches it via authorization_reference and proceeds. Fulfillment is in the token.

So draft-zehavi-oauth-rar-metadata is the RS-initiated discovery/remediation front-end and AROP is the approval back-end, with the grant carried in the token. It reads as the unsigned, fresh-grant counterpart to the Transaction Authorization Challenge (signed, RS-as-PEP assertion): since authorization_remediation is not a signed requestable-denial assertion, the AS re-evaluates the request itself rather than consuming the RS's decision.

Does that match what you had in mind?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's what I had in mind.
Thanks for explaining it through my draft, serves incidentally as a review :-) so I appreciate any feedback it generates. One note - authorization_reference is returned by RS but never sent by the client to AS or RS. It's only usage is (using RAR to "scope" hashing logic) to help client locate an existing token in its possession that can remediate without requiring it to request a new grant.

But since AROP is an internal process between AS-as-PEP and a PDP, not exposed to the client, and since the AS never changes the grant chosen by the client, I still don't understand why is explicit opt-in required? Not as important but also why is DTR the opt in signal?

I can imagine a requestable denial requiring end-user approval only, which could be resolved without deferral.
Why not always return the denial with remediation options to the client to proceed with remediation as it sees fit?


- **Approved.** The issuer issues the access token. The granted authorization (`authorization_details`, and `scope` if used) carries the approved access, which MUST NOT broaden the originating request, and the access token lifetime MUST NOT exceed the recorded approval expiry.

The approval bounds the credential, not a single token. In this completion mode the issuer SHOULD NOT issue a refresh token; if it does, the refresh token and every access token derived from it MUST be bounded by the recorded approval expiry, and a refresh after that expiry MUST fail with `invalid_grant` and requires a new access request. Likewise, a token issued by this profile MUST NOT be exchanged ({{RFC8693}}) into a credential that is longer-lived or broader than the approval.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fits well into single-use credential use-cases such as single payments.
Can the profile not be used for requesting longer term access? For example file system access on a certain path or some policy allowing additional privileges, expressed as RAR.
If so, then single-use semantics do not apply, the approval lifetime should govern access token renewals and refresh tokens make sense for all the reasons they're currently used for

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, and it depends on the authorization model and approval state, not the credential alone.

  • Token-side (this profile): standing access is a refresh bounded by approved_until, re-evaluated at each refresh. Refresh tokens make sense as you say.
  • Resource-side (ARAP reevaluate): the RS re-evaluates each call, so standing access needs no long-lived credential.

So single-use vs standing is a property of the approved task and the model, not a blanket rule. The invariant either way is that no credential outlives approved_until or broadens the grant.

I'll rework "Approved" to keep that invariant, permit refresh for standing token-side approvals, point to reevaluate when per-call currency is wanted, and reserve "no refresh" for single-use. Sound right?

Address PR review: a client engages exactly one binding and the
evaluating party never converts a request made under one binding into
another. The ARAP access request is an internal submission to the
Access Request Service, not an outbound OAuth request; the continuation
handle and pending response are always the same transport the request
was made under. In the transaction challenge binding the client itself
presents the challenge to the AS named in the challenge aud and
authenticates there, so the originating deployment crosses no
server-to-server credential.
… remediation

Address PR review on the non-opt-in path:

- Opt-in (DTR completion_mode=deferred) is a requirement of the deferral
  transport guarding the deferred response, not a precondition for
  approval; the internal access request and re-evaluation need no client
  opt-in.
- Rather than silently deny, the evaluating party MAY surface a
  requestable denial as remediation, and the client proceeds as it sees
  fit: retry opting into DTR/CIBA, reuse an existing token, or pursue
  interactive end-user approval. A requestable denial does not by itself
  imply deferral. Disclosure stays deployment-policy-gated.
- Add informative reference to draft-zehavi-oauth-rar-metadata as the
  resource-initiated discovery/remediation front-end that composes with
  this profile (what to request, transport carries the wait, token
  carries the fulfillment); the unsigned counterpart to the signed
  Transaction Authorization Challenge.
Address PR review on standing vs single-use access:

- Recast the Authority-model design goal from a one-way tradeoff into
  two first-class completion modes a deployment chooses per approval:
  re-evaluation completion (ARAP reevaluate, per-call currency, no
  long-lived credential) and token-issuance completion (this profile,
  grant carried in the token, issuance-time decision bounded by
  lifetime). Reworded the adjacent goal to 'Shared completion
  semantics, several transports' to avoid a model/mode clash.
- Rework the Approved refresh guidance: keep the invariant (no access
  token, refresh token, or exchanged token outlives approved_until or
  broadens the grant); under token-issuance completion, standing
  time-bounded approvals (path read, policy-granted privileges as RAR)
  are served by a refresh bounded by approved_until and re-evaluated at
  each refresh; single-use approvals (single payment, single-use
  Transaction Authorization Challenge) get none; per-call currency is
  re-evaluation completion, out of scope.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants