Add AuthZEN Access Request OAuth Profile (AROP) draft#531
Conversation
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`). |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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:
-
"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. -
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_codeat 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_endpointis 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
audnaming a different AS / crossing AS boundaries. In the
transaction challenge binding the client, not the originating AS, presents
the challenge to the AS named inaud, 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 inaudthen 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.
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
Why not provide client the requestable details so it can then opt in and use them for remediation?
There was a problem hiding this comment.
Are you asking about a shape similar to the following:
- The RS (as PEP) evaluates, gets a denied-but-requestable decision, and serializes it as your
insufficient_authorization/authorization_remediationchallenge (draft-zehavi-oauth-rar-metadata) carrying the requiredauthorization_details(and anauthorization_reference). - The client takes those
authorization_detailsto the AS in a fresh grant, opting into deferred completion (DTR) or via CIBA. - The AS runs the ARAP approval flow and, on approval, issues an access token whose granted
authorization_detailsfulfill the operation, audience-restricted to the RS. - The client re-presents the token; the RS matches it via
authorization_referenceand 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?
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
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:
draft-gerber-oauth-deferred-token-response): the client makes a token request, the AS returns adeferral_code, and the client polls the token endpoint.auth_req_id, and the principal approves out of band on a decoupled device (poll/ping redeem at the token endpoint; push delivers directly).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 atxn-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
draft-brossard-oauth-rar-authzen, noted in the draft.)reevaluate.evaluation_idvs PDP-signedbinding_tokendenial-binding for co-located vs separate Access Request Service.Notes
Makefile; the draft builds clean (kramdown-rfc -> xml2rfc) with all references resolving.authorization_details-typemapping to AuthZEN resource/action is deployment- or vocabulary-defined, and partial issuance across multipleauthorization_detailsis out of scope.