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.
Comment thread profiles/authzen-access-request-oauth/authzen-access-request-oauth-profile-1_0.md Outdated
- **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?

Comment thread profiles/authzen-access-request-oauth/authzen-access-request-oauth-profile-1_0.md Outdated
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.

@ogazitt ogazitt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I've reviewed the spec and agree that it is a great way to map the "abstract" ARAP requestable denial protocol to OAuth token issuance flows, via the currently available and actively worked-on asynchronous token issuance mechanisms (DTR, CIBA, TX auth challenge).

I believe section 8.2 could potentially point back to a more definitive AuthZEN SARC mapping for token issuance scenarios.

I'm also not sure why AROP depends on OAuth RAR - my guess is that this is because a Client that uses a RAR request is a signal that it is already "tuned in" to a richer/finer-grained authz model, and therefore more likely to support the deferred token issuance workflow?

In my read, AROP doesn't need to constrain itself to RAR requests, and if it didn't, it could be more generally applicable to a broader set of OAuth token issuance scenarios.


In every placement the access request, approval, and issuance-time re-evaluation are the same; only the origin of the requestable denial differs.

## Constructing the Access Evaluation

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think we (i.e. the OAuth/AuthZEN ecosystem) are missing some connective tissue that more precisely defines the OAuth token issuance -> AuthZEN SARC information model mapping. I have some ideas here that are complementary to AROP.

gffletch pushed a commit to gffletch/draft-mcguinness-oauth-mission that referenced this pull request Aug 10, 2026
…ge re-POST

Make the AROP Transaction Challenge flow use the spec's continuation handle
(openid/authzen#531) instead of re-POSTing the whole challenge each poll. The
client presents the challenge ONCE; the AS mints an opaque
transaction_authorization_id bound to the validated challenge AND the
requesting client's cnf, and the client polls by that handle. Settles two
conformance points raised in review: the flow is client-driven Transaction
Challenge (not a DTR conflation, no RS-as-requester), and the handle is the
AS-side binding token that captures the validated challenge state (D37: the AS
owns the txn pending id).

- provider.ts handleTransaction: branch on body shape after shared base-token+
  DPoP auth. Initiation { challenge } -> validate + D42 subset-gate + openForTxn
  -> mint transaction_authorization_id, store { taskId, txn, missionId, cnfJkt,
  parameter_digest, authorizationDetails, subject, audience, expiresAt } ->
  200 { transaction_authorization_id, expires_in, interval }. Poll
  { transaction_authorization_id } -> look up handle, verify base token cnf.jkt
  == stored cnf (handle bound to client), check the ARS task -> pending, or
  issue the txn-token from stored state (active mission unchanged, D42).
  Unknown id -> 404; cnf mismatch -> 403.
- txn-endpoint.test.ts: rewritten to the handle flow (initiate -> pending ->
  approve -> poll-by-id -> token, all claim assertions preserved) + 404-unknown
  and 403-cnf-mismatch negatives.
- exhibit.ts / server.ts: present the challenge once, poll by
  transaction_authorization_id; re-present the issued txn-token to the RS.

RS side, crypto, and the txn-token shape are unchanged. Gate: typecheck clean,
128 tests (126 + 2 negatives), exhibit green (challenge once -> handle -> poll
-> token -> PERMIT), browser + traces + evals + vendor-test pass.
@alexolivier

alexolivier commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

Will hold the vote for this next week (27th Aug)

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.

4 participants