| Internet-Draft | AAuth | March 2026 |
| Hardt | Expires 6 September 2026 | [Page] |
AAuth is an authentication and authorization protocol for modern distributed systems. It provides progressive authentication from abuse prevention to full authorization, verified agent identity alongside user identity, cryptographic proof of resource legitimacy, and unified authentication and authorization in a single flow. The protocol uses HTTP Message Signatures for proof-of-possession on every request, eliminating bearer tokens and shared secrets. Any endpoint may return deferred responses using standard HTTP async semantics (202 Accepted, Location, Prefer: wait), enabling uniform handling of user interaction, long-running authorization, and clarification chat.¶
Note: This section is to be removed before publishing as an RFC.¶
Source for this draft and an issue tracker can be found at https://github.com/DickHardt/draft-hardt-aauth.¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 6 September 2026.¶
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
OAuth 2.0 [RFC6749] was created to solve a specific problem: users were sharing their passwords with third-party web applications so those applications could access their data at other sites. OAuth replaced this anti-pattern with a delegation model — the user's browser redirects to the authorization server, the user consents, and the application receives an access token without ever seeing the user's credentials. OpenID Connect extended this to federated login. Together, they serve these use cases well and continue to be the right choice for them.¶
But the landscape has changed. New use cases have emerged that OAuth and OIDC were not designed to address:¶
The AAuth protocol is designed for these new use cases. It complements OAuth and OIDC rather than replacing them — where pre-registered clients, browser redirects, bearer tokens, and static scopes work well, they remain the right choice.¶
AAuth provides:¶
202 Accepted + Location + Prefer: wait) support headless agents, long-running consent, and clarification chat.¶
{::boilerplate bcp14-tagged}¶
/.well-known/aauth-agent.json.¶
/.well-known/aauth-issuer.json.¶
/.well-known/aauth-resource.json.¶
interaction_endpoint in entity metadata. Both auth servers and resources may have interaction endpoints.¶
AAuth has three participant types — agents, resources, and auth servers — though not all participate in every use case.¶
All AAuth requests are signed using HTTP Message Signatures ([RFC9421]). Keys are conveyed in the Signature-Key header ([I-D.hardt-httpbis-signature-key]). When the agent includes its public key directly (using scheme=hwk), the request is pseudonymous — the signature proves possession of the included key without establishing verified identity. When the agent presents a JWT (agent token or auth token) via scheme=jwt, the JWT's cnf claim binds the signing key to a verified identity or authorization.¶
AAuth defines three token types, all of which are proof-of-possession tokens:¶
agent+jwt): Binds an agent delegate's key to an agent server's identity¶
resource+jwt): Binds an access request to a resource's identity¶
auth+jwt): Binds identity claims and authorization for a given audience to an agent¶
All three token types use the cnf (confirmation) claim ([RFC7800]) to bind the token to a specific signing key, making them proof-of-possession tokens usable only by a specific agent.¶
Resources and auth servers indicate what is needed from the agent using the AAuth HTTP response header.¶
These are returned as 401 responses from resources, indicating what the agent must provide:¶
These are returned as 202 responses during authorization, indicating what must happen before the request can complete:¶
Scopes may request identity claims (using OpenID Connect scope values such as openid, profile, email) or resource authorization (using scopes defined by the resource, such as data.read or calendar.write as defined in the resource's scope_descriptions metadata), or both.¶
When the agent is the audience, scopes typically request identity claims. When accessing a resource, scopes are defined in the resource's scope_descriptions metadata.¶
Agents MAY act as a resource and include scope_descriptions in their metadata to enable users to grant scopes to resources at the agent during consent, enabling an agent delegate to obtain consent to access protected resources at the agent.¶
When an agent needs additional scopes beyond its current authorization, it requests a new auth token from the auth server's token_endpoint — with a new resource_token when accessing another resource, or with the new scope values when the agent is the audience. The new auth token replaces the previous one.¶
An agent accesses a resource that requires pseudonymous identity. The resource challenges with require=pseudonym, and the agent retries with a signed request using an inline public key. The resource can track the agent by key thumbprint without knowing its identity.¶
Agent Resource | | | unsigned request | |------------------------------>| | | | 401 Unauthorized | | AAuth: require=pseudonym | |<------------------------------| | | | HTTPSig request | | (scheme=hwk) | |------------------------------>| | | | verify signature, | | track by key | | thumbprint | | | | 200 OK | |<------------------------------| | |¶
If the agent already knows the resource requires pseudonymous access (from a previous interaction or metadata), it MAY sign the initial request directly without waiting for a 401 challenge.¶
An agent accesses a resource using only its agent identity, without authorization from an auth server.¶
Agent Resource | | | HTTPSig request | | (scheme=jwks_uri or jwt) | |------------------------------>| | | | verify agent identity | | | 200 OK | |<------------------------------| | |¶
A machine-to-machine agent obtains authorization directly without user interaction.¶
Agent Resource Auth Server | | | | HTTPSig request | | |------------------------>| | | | | | 401 + resource_token | | | + auth_server | | |<------------------------| | | | | | POST token_endpoint with resource_token | |--------------------------------------------------->| | | | | | validate resource_token, | | evaluate policy | | | | | auth_token (direct grant) | |<---------------------------------------------------| | | | | HTTPSig request | | | (with auth-token) | | |------------------------>| | | | | | 200 OK | | |<------------------------| | | | |¶
An agent requests an auth token where it is the audience — either for SSO (obtaining user identity) or for first-party resource access by its delegates. The agent calls the token endpoint with scope (and no resource_token), since the agent itself is the resource.¶
User Agent Auth Server | | | | | POST token_endpoint | | | scope (no resource_token) | | | Prefer: wait=45 | | |------------------------------>| | | | | | 202 Accepted | | | Location: /pending/def | | | AAuth: require=interaction; | | | code="EFGH5678" | | |<------------------------------| | | | | direct to | | | interaction_endpoint with code | |<-------------| | | | | | authenticate and consent | |--------------------------------------------->| | | | | redirect to callback_url | |<---------------------------------------------| | | | | | GET /pending/def | | |------------------------------>| | | 200 OK, auth_token | | |<------------------------------| | | | | | auth_token used for: | | | 1. User identity (SSO) | | | 2. API access by delegates | | | |¶
The auth server obtains approval directly — from a user (e.g., push notification, existing session, email) — without the agent facilitating a redirect. The agent simply polls until the request resolves.¶
User Agent Resource Auth Server | | | | | | HTTPSig request | | | |--------------------->| | | | | | | | 401 + resource_token | | | + auth_server | | | |<---------------------| | | | | | | | POST token_endpoint | | | resource_token, purpose | | | Prefer: wait=45 | | |------------------------------------------>| | | | | | | 202 Accepted | | | Location: /pending/jkl | | | AAuth: require=approval | | |<------------------------------------------| | | | | | push notification / existing session | | |<------------------------------------------------------------| | | | | | approve | | | |------------------------------------------------------------>| | | | | | | GET /pending/jkl | | | | Prefer: wait=45 | | | |------------------------------------------>| | | 200 OK, auth_token | | | |<------------------------------------------| | | | | | | HTTPSig request | | | | (with auth-token) | | | |--------------------->| | | | | | | | 200 OK | | | |<---------------------| | | | | |¶
In this flow, the auth server handles the approval process directly. The require=approval value tells the agent that the request is waiting on external approval, but the agent does not need to facilitate any user interaction.¶
When a resource requires user interaction (login, consent for downstream access), it returns 202 Accepted with a Location header and AAuth: require=interaction; code="...". Resource-level user interaction is a deferral, not a denial — the request has been accepted but requires user action before it can complete.¶
The agent directs the user to the resource's interaction_endpoint with the interaction code. The resource handles the interaction (which may involve its own AAuth, OAuth, or OIDC flow with a downstream auth server). After completion, the resource redirects the user back to the agent's callback URL. The agent polls the Location URL with GET until the response is ready.¶
User Agent Resource Auth Server | | | | | | HTTPSig request | | | | (with auth-token) | | | |--------------------->| | | | | | | | 202 Accepted | | | | Location: /pending/xyz | | | AAuth: require=interaction; | | | code="MNOP3456" | | |<---------------------| | | | | | | direct to resource | | | interaction_endpoint with code | | |<--------------| | | | | | | | authenticate/consent | | |------------------------------------->| | | | | | | | [Resource may perform AAuth, | | | OAuth, or OIDC flow] | | | | | | redirect to callback_url | | |<-------------------------------------| | | | | | | callback | | | |-------------->| | | | | | | | | GET /pending/xyz | | | |--------------------->| | | | | | | | 200 OK | | | |<---------------------| | | | | |¶
The agent directs the user to the resource's interaction URL with the code. For a direct redirect:¶
https://resource.example/interact?code="MNOP3456"&callback=https%3A%2F%2Fagent.example%2Fcallback%3Fstate%3Dxyz¶
After user interaction completes, the agent polls the pending URL:¶
GET /pending/xyz HTTP/1.1
Host: resource.example
Prefer: wait=45
Signature-Input: sig=("@method" "@authority" "@path" "signature-key");created=1730217600
Signature: sig=:...signature bytes...:
Signature-Key: sig=jwt;jwt="eyJhbGc..."
¶
Editor's Note: Call chaining is an exploratory feature. The mechanism described here may change in future versions.¶
When a resource needs to access a downstream resource on behalf of the caller, it acts as an agent. The resource presents the downstream resource's resource token along with the auth token it received from the original caller as the upstream_token. This allows the downstream auth server to verify the authorization chain.¶
The downstream auth server (AS2) evaluates its own policy based on both the upstream auth token and the resource token from Resource 2. The resulting authorization is not necessarily a subset of the upstream scopes — AS2 may grant scopes that are independent of those in the upstream auth token. For example, an upstream token granting calendar.read on Resource 1 might lead AS2 to grant availability.read on Resource 2 based on an organizational policy that allows calendar services to query availability. The upstream token provides provenance and user identity context, not a scope ceiling.¶
Because the resource acts as an agent, it MUST publish agent metadata at /.well-known/aauth-agent.json (see Agent Server Metadata) so that downstream resources and auth servers can verify its identity. The resource / agent MAY use the same jwks_uri in the /.well-known/aauth-resource.json and the /.well-known/aauth-agent.json.¶
When the downstream auth server can issue a token without user interaction:¶
Agent Resource 1 Resource 2 AS1 AS2 | | | | | | HTTPSig req | | | | | (auth_token | | | | | from AS1) | | | | |--------------->| | | | | | | | | | verify auth_token | | | | | | | | | | HTTPSig req | | | | | (as agent) | | | | |----------------->| | | | | | | | | | 401 + resource_token | | | | + auth_server=AS2 | | | |<-----------------| | | | | | | | | | POST token_endpoint | | | | resource_token from R2, | | | | upstream_token (from AS1) | | | |----------------------------------------------->| | | | | | | | | verify upstream_token,| | | | evaluate policy | | | | (fetch AS1 JWKS) | | | | | | | | auth_token for R2 | | | |<-----------------------------------------------| | | | | | | | HTTPSig req | | | | | (auth_token | | | | | from AS2) | | | | |----------------->| | | | | | | | | | 200 OK | | | | |<-----------------| | | | | | | | | 200 OK | | | | |<---------------| | | | | | | | |¶
When the downstream auth server requires user interaction, Resource 1 chains the interaction back to the original agent. Resource 1 receives a 202 with require=interaction from the downstream auth server, then returns its own 202 with require=interaction to the agent. The agent directs the user to Resource 1's interaction endpoint, and Resource 1 redirects the user onward to the downstream interaction endpoint. This keeps the downstream interaction URL opaque to the agent — each link in the chain manages only its own interaction redirect.¶
User Agent Resource 1 Resource 2 AS2 | | | | | | | HTTPSig req | | | | |---------------->| | | | | | | | | | | HTTPSig req | | | | | (as agent) | | | | |---------------->| | | | | | | | | | 401 + resource_token | | | | + auth_server=AS2 | | | |<----------------| | | | | | | | | | POST token_endpoint | | | | with upstream_token | | | |--------------------------------->| | | | | | | | | 202 Accepted | | | | require=interaction; | | | | code=WXYZ | | | |<---------------------------------| | | | | | | | 202 Accepted | | | | | Location: /pending/xyz | | | | AAuth: require=interaction; | | | | code="MNOP" | | | |<----------------| | | | | | | | | direct to R1 | | | | interaction_endpoint | | | | with code | | | | |<-----------| | | | | | | | | | interaction_endpoint | | | |----------------------------->| | | | | | | | | redirect to AS2 interaction_endpoint | | |<-----------------------------| | | | | | | | | authenticate and consent | | | |---------------------------------------------------------------->| | | | | | | redirect to R1 callback | | | |<----------------------------------------------------------------| | | | | | | | [R1 polls AS2 pending URL, | | | receives auth_token for R2] | | | | | | | | | HTTPSig req | | | | | (auth_token | | | | | from AS2) | | | | |---------------->| | | | | | | | | | 200 OK | | | | |<----------------| | | | | | | | redirect to agent callback_url | | |<-----------------------------| | | | | | | | | callback | | | | |----------->| | | | | | | | | | | GET /pending/xyz | | | |---------------->| | | | | | | | | | 200 OK | | | | |<----------------| | | | | | | |¶
The agent, resource, and issuer values that identify agents, resources, and auth servers MUST conform to the following:¶
https scheme¶
Valid identifiers:¶
Invalid identifiers:¶
http://agent.example (not HTTPS)¶
https://Agent.Example (not lowercase)¶
https://agent.example:8443 (contains port)¶
https://agent.example/v1 (contains path)¶
https://agent.example/ (trailing slash)¶
Implementations MUST perform exact string comparison on server identifiers. The lowercase and ACE requirements ensure that normalization happens at the source rather than requiring comparison logic at every point of use.¶
The token_endpoint, interaction_endpoint, resource_token_endpoint, and callback_endpoint values MUST conform to the following:¶
When localhost_callback_allowed is true in the agent's metadata, the agent MAY use a localhost callback URL as the callback parameter to the interaction endpoint. Localhost callback URLs MUST use the http scheme with a loopback address (127.0.0.1, [::1], or localhost), MUST include a port, MAY include a path, and MUST NOT contain a query string or fragment.¶
The jwks_uri, tos_uri, policy_uri, logo_uri, and logo_dark_uri values MUST use the https scheme.¶
Servers use the AAuth response header to indicate authentication and interaction requirements. The header value is a Structured Fields Dictionary ([RFC8941]) with a require key whose token value indicates the requirement level. Additional parameters provide context for the requirement.¶
When a resource requires only a signed request:¶
HTTP/1.1 401 Unauthorized AAuth: require=pseudonym¶
When a resource requires verified agent identity:¶
HTTP/1.1 401 Unauthorized AAuth: require=identity¶
When a resource requires an auth token:¶
HTTP/1.1 401 Unauthorized AAuth: require=auth-token; resource-token="..."; auth-server="https://auth.example"¶
Parameters:¶
When a server requires user interaction, it returns 202 Accepted per the Deferred Responses protocol with an AAuth header and a JSON body:¶
HTTP/1.1 202 Accepted
Location: /pending/res_abc123
Retry-After: 0
Cache-Control: no-store
AAuth: require=interaction; code="ABCD1234"
Content-Type: application/json
{
"status": "pending",
"location": "/pending/res_abc123",
"require": "interaction",
"code": "ABCD1234"
}
¶
The code field is REQUIRED when require is "interaction". The agent MUST direct the user to the server's interaction_endpoint with the code and poll the Location URL with GET for the result.¶
When the auth server is obtaining approval directly — from a user (e.g., push notification, existing session) — without the agent's involvement:¶
HTTP/1.1 202 Accepted
Location: /pending/res_def456
AAuth: require=approval
Retry-After: 0
Cache-Control: no-store
Content-Type: application/json
{
"status": "pending",
"location": "/pending/res_def456",
"require": "approval"
}
¶
The agent knows the request is waiting on external approval but does not need to take any action. The agent polls the Location URL until the request resolves.¶
Agent tokens enable agent servers to delegate signing authority to agent delegates while maintaining a stable agent identity.¶
An agent token is a JWT with typ: agent+jwt containing:¶
Header:
- alg: Signing algorithm
- typ: agent+jwt
- kid: Key identifier¶
Required payload claims:
- iss: Agent server URL (the agent identifier)
- sub: Agent delegate identifier (stable across key rotations)
- jti: Unique token identifier for replay detection and audit
- cnf: Confirmation claim ([RFC7800]) with jwk containing the delegate's public key
- iat: Issued at timestamp
- exp: Expiration timestamp¶
Optional payload claims:
- aud: Audience restriction. When present, the agent delegate MUST only present this agent token to the specified server(s). The value is a single URL or an array of URLs identifying the auth server(s) or resource(s) where this token is valid. Servers receiving an agent token with an aud claim MUST verify that their own identifier is listed.
- aud_sub: The user identifier (sub value) from a previous auth token issued by the auth server in aud. This signals to the auth server which user the agent server believes the delegate is acting on behalf of, enabling the auth server to skip interactive identification and proceed directly to authorization. The auth server MUST verify this claim against its own records and MAY ignore it if the binding is no longer valid.¶
When aud is absent, the agent token establishes agent identity across all interactions — the delegate uses the same agent token regardless of which resource or auth server it communicates with. When aud is present, the agent server is restricting the delegate to specific interactions, which limits exposure if the delegate's key is compromised.¶
Agent servers MAY include additional claims in the agent token to convey attestation evidence about the delegate's environment — for example, platform integrity, secure enclave status, or workload identity assertions. The semantics and verification of such claims are outside the scope of this specification but provide an extension point for deployments requiring stronger trust signals about agent delegates.¶
Agent delegates present agent tokens via the Signature-Key header using scheme=jwt:¶
Signature-Key: sig=jwt; jwt="eyJhbGciOiJFZERTQSIsInR5cCI6ImFnZW50K2p3dCJ9..."¶
Resource tokens provide cryptographic proof of resource identity, preventing confused deputy and MITM attacks.¶
A resource token is a JWT with typ: resource+jwt containing:¶
Header:
- alg: Signing algorithm
- typ: resource+jwt
- kid: Key identifier¶
Payload:
- iss: Resource URL
- aud: Auth server URL
- jti: Unique token identifier for replay detection and audit
- agent: Agent identifier
- agent_jkt: JWK Thumbprint ([RFC7638]) of the agent's current signing key
- iat: Issued at timestamp
- exp: Expiration timestamp
- scope: Requested scopes (optional)
- txn: Transaction identifier (optional). When present, correlates this resource token with the resulting auth token and all related protocol exchanges across parties and audit logs.¶
Resources include resource tokens in the AAuth header when requiring authorization:¶
AAuth: require=auth-token; resource-token="eyJ..."; auth-server="https://auth.example"¶
When a resource publishes a resource_token_endpoint in its metadata, agents MAY request a resource token proactively — without first making an API call and receiving a 401 challenge. This enables two patterns:¶
scope_descriptions metadata) and obtains authorization before making its first API call.¶
Request:¶
POST /resource-token HTTP/1.1
Host: resource.example
Content-Type: application/json
Signature-Input: sig=("@method" "@authority" "@path" "signature-key");created=1730217600
Signature: sig=:...signature bytes...:
Signature-Key: sig=jwt;jwt="eyJhbGc..."
{
"scope": "data.read data.write"
}
¶
Response:¶
{
"resource_token": "eyJhbGc...",
"auth_server": "https://auth.example",
"scope": "data.read data.write"
}
¶
The resource generates and signs a resource token and returns it along with the auth server URL and the granted scope. The scope in the response reflects what the resource included in the resource token, which MAY be narrower than what was requested. The agent then proceeds to the auth server's token endpoint as in the standard flow.¶
The resource MAY reject the request if the requested scopes are invalid or if the resource does not support proactive token requests for the given scopes:¶
{
"error": "invalid_scope",
"error_description": "Unknown scope: data.admin"
}
¶
Auth tokens grant agents access to resources after authentication and authorization.¶
An auth token is a JWT with typ: auth+jwt containing:¶
Header:
- alg: Signing algorithm
- typ: auth+jwt
- kid: Key identifier¶
Required payload claims:
- iss: Auth server URL
- aud: The URL of the resource the agent is authorized to access. When the agent is accessing its own resources (SSO or first-party use), the aud is the agent server's URL.
- jti: Unique token identifier for replay detection and audit
- agent: Agent identifier
- cnf: Confirmation claim with jwk containing the agent's public key
- iat: Issued at timestamp
- exp: Expiration timestamp¶
Conditional payload claims (at least one MUST be present):
- sub: User identifier
- scope: Authorized scopes¶
Conditional payload claims (REQUIRED when present in the resource token):
- txn: Transaction identifier copied from the resource token's txn claim. Enables correlation of the authorization grant with the original resource request across all parties and audit logs.¶
Editor's Note: A future version may define a URI-based authorization claim (referencing a Rich Authorization Request document with a SHA-256 hash of the contents) as an alternative to scope.¶
The auth token MAY include additional claims registered in the IANA JSON Web Token Claims Registry [RFC7519] or defined in OpenID Connect Core 1.0 [OpenID.Core] Section 5.1.¶
Agents present auth tokens via the Signature-Key header using scheme=jwt:¶
Signature-Key: sig=jwt; jwt="eyJhbGciOiJFZERTQSIsInR5cCI6ImF1dGgrand0In0..."¶
Any endpoint in AAuth — whether an auth server token endpoint or a resource endpoint — MAY return a 202 Accepted response ([RFC9110]) when it cannot immediately resolve a request. This is a first-class protocol primitive, not a special case. Agents MUST handle 202 responses regardless of the nature of the original request.¶
Reasons a 202 MAY be returned include:¶
The agent makes a request and signals its willingness to wait using the Prefer header ([RFC7240]):¶
POST /token HTTP/1.1
Host: auth.example
Content-Type: application/json
Prefer: wait=45
Signature-Input: sig=("@method" "@authority" "@path" "signature-key");created=1730217600
Signature: sig=:...signature bytes...:
Signature-Key: sig=jwt;jwt="eyJhbGc..."
{
"resource_token": "eyJhbGc..."
}
¶
The wait preference tells the server the agent is willing to hold the connection open for up to N seconds before the server MUST respond. The server confirms the honored duration with Preference-Applied:¶
Preference-Applied: wait=45¶
The server SHOULD respond within the requested wait duration. If the request cannot be resolved within that time, it returns a 202.¶
When the server cannot resolve the request within the wait period:¶
HTTP/1.1 202 Accepted
Location: /pending/f7a3b9c
Retry-After: 0
Cache-Control: no-store
Content-Type: application/json
{
"status": "pending",
"location": "/pending/f7a3b9c"
}
¶
Headers:¶
Location (REQUIRED): The pending URL. The server embeds its state in the URL path. The Location URL MUST be on the same origin as the responding server.¶
Retry-After (REQUIRED): Seconds the agent SHOULD wait before polling. 0 means retry immediately.¶
Cache-Control: no-store (REQUIRED): Prevents caching of pending responses.¶
Every 202 response MUST include a Location header, making each response self-contained.¶
Body fields:¶
status (REQUIRED): Always "pending".¶
location (REQUIRED): The pending URL (echoes the Location header).¶
require (OPTIONAL): The requirement level. "interaction" when the agent must direct the user to an interaction endpoint (with code). "approval" when the auth server is obtaining approval directly from a user.¶
code (OPTIONAL): The interaction code. Present only with require: "interaction". The agent MUST direct the user to the server's interaction_endpoint with this code.¶
clarification (OPTIONAL): A question from the user during consent. Present during clarification chat.¶
After receiving a 202, the agent switches to GET for all subsequent requests to the Location URL:¶
GET /pending/f7a3b9c HTTP/1.1
Host: auth.example
Prefer: wait=45
Signature-Input: sig=("@method" "@authority" "@path" "signature-key");created=1730217600
Signature: sig=:...signature bytes...:
Signature-Key: sig=jwt;jwt="eyJhbGc..."
¶
Location URL contains all state the server needs¶
Prefer: wait=N on every poll¶
202 including the same Location¶
POST to deliver a clarification response to the pending URL (see Clarification Chat). This is the only case where the agent sends a non-GET request to a pending URL.¶
The distinction between POST and GET is intentional:¶
A non-202 response terminates polling. The agent MUST stop polling and handle the response.¶
| Status | Meaning | Agent Behavior |
|---|---|---|
200
|
Success | Process response body |
403
|
Denied or abandoned | Surface to user; check error field |
408
|
Expired | MAY initiate a fresh request |
410
|
Gone — permanently invalid | MUST NOT retry |
500
|
Internal server error | Start over |
Once a terminal response is returned, the Location URL is no longer valid. Subsequent GET requests to it MUST return 404.¶
| Status | Meaning | Agent Behavior |
|---|---|---|
202
|
Pending | Continue polling with Prefer: wait
|
503
|
Server temporarily unavailable | Back off using Retry-After; MUST honor over Prefer: wait
|
Error responses during deferred processing use the standard error response format defined in the Error Responses section.¶
Initial POST (with Prefer: wait=N)
|
+-- 200 --> done (direct grant)
+-- 202 --> note Location URL, check require/code
+-- 400 --> invalid_request or invalid_resource_token — fix and retry
+-- 401 --> invalid_signature — check credentials
+-- 500 --> server_error — start over
+-- 503 --> back off (Retry-After), retry
|
GET Location (with Prefer: wait=N)
|
+-- 200 --> done
+-- 202 --> continue polling (check for clarification)
+-- 403 --> denied or abandoned — surface to user
+-- 408 --> expired — MAY retry with fresh request
+-- 410 --> invalid_code — do not retry
+-- 500 --> server_error — start over
+-- 503 --> temporarily_unavailable — back off (Retry-After)
¶
The auth server's token_endpoint is the endpoint for initiating authorization requests and refreshing auth tokens. Polling and clarification use the pending URL returned in 202 responses (see Deferred Responses).¶
The token endpoint serves multiple functions depending on the parameters provided:¶
| Mode | Key Parameters | Use Case |
|---|---|---|
| Resource access |
resource_token
|
Agent needs auth token for a resource |
| Self-access (SSO/1P) |
scope (no resource_token) |
Agent needs auth token for itself |
| Call chaining |
resource_token + upstream_token
|
Resource acting as agent |
| Token refresh |
auth_token (expired) |
Renew expired token |
The auth server validates the request and responds based on policy.¶
Direct grant response (200 — no user interaction needed):¶
{
"auth_token": "eyJhbGc...",
"expires_in": 3600
}
¶
User interaction required response (202 — deferred):¶
HTTP/1.1 202 Accepted
Location: /pending/abc123
Retry-After: 0
Cache-Control: no-store
AAuth: require=interaction; code="ABCD1234"
Content-Type: application/json
{
"status": "pending",
"location": "/pending/abc123",
"require": "interaction",
"code": "ABCD1234"
}
¶
The location field contains the pending URL the agent polls with GET. When require is "interaction", the agent directs the user to the auth server's interaction_endpoint with the code. When require is "approval", the auth server is obtaining approval directly from a user and the agent simply polls.¶
Error response (request validation failed):¶
{
"error": "invalid_resource_token",
"error_description": "Resource token has expired"
}
¶
See Token Endpoint Error Codes in the Error Responses section for the full set of error codes.¶
Polling, terminal responses, and error handling follow the Deferred Responses protocol described above.¶
Agents that support clarification chat MUST declare "clarification_supported": true in their agent server metadata. Auth servers SHOULD only send clarification questions to agents that declare support. If the field is absent or false, the auth server MUST NOT include clarification in polling responses.¶
During user consent, the user may ask questions about the agent's stated purpose. The auth server delivers these questions to the agent, and the agent responds:¶
User Agent Auth Server | | | | [Agent has Location URL; | | user is at interaction_endpoint] | | | | | | GET /pending/abc | | | Prefer: wait=45 | | |--------------------------->| | | [connection held open] | | | | "Why do you need calendar access?" | |------------------------------------------------->| | | | | | 202 with clarification | | | "Why do you need | | | calendar access?" | | |<---------------------------| | | | | | POST /pending/abc | | | "I need to find | | | available meeting | | | times for your | | | Tokyo trip next week" | | |--------------------------->| | | | | display agent response | |<-------------------------------------------------| | | | | grant consent | | |------------------------------------------------->| | | | | | GET /pending/abc | | | Prefer: wait=45 | | |--------------------------->| | | 200 OK, auth_token | | |<---------------------------| | | |¶
A 202 polling response may include a clarification field containing the user's question:¶
{
"status": "pending",
"location": "/pending/abc123",
"clarification": "Why do you need access to my calendar?"
}
¶
The agent responds by POSTing JSON with clarification_response to the pending URL:¶
POST /pending/abc123 HTTP/1.1
Host: auth.example
Content-Type: application/json
Signature-Input: sig=("@method" "@authority" "@path" "signature-key");created=1730217600
Signature: sig=:...signature bytes...:
Signature-Key: sig=jwt;jwt="eyJhbGc..."
{
"clarification_response": "I need to find available meeting times for your Tokyo trip next week"
}
¶
Auth servers SHOULD enforce limits on clarification rounds (recommended: 5 rounds maximum) and overall timeout to prevent abuse.¶
When a server responds with 202 and AAuth: require=interaction; code="...", the agent directs the user to the server's interaction_endpoint with the interaction code. The agent has three options:¶
Manual entry: Display the interaction_endpoint and the code separately. The agent MAY insert hyphens into the code for readability (e.g., ABCD-1234). The code itself MUST NOT contain hyphens.¶
QR code: Encode the full URL for scanning:¶
{interaction_endpoint}?code={interaction_code}
¶
Direct redirect (when the agent has a browser): Navigate the user directly, optionally with a callback:¶
{interaction_endpoint}?code={interaction_code}&callback={callback_url}
¶
Example redirect URL:¶
https://auth.example/interact?code="ABCD1234"&callback=https%3A%2F%2Fagent.example%2Fcallback%3Fstate%3Dabc123¶
The server authenticates the user and displays consent information (including the agent's purpose, identity, and requested scopes).¶
After consent, the server determines the callback behavior:¶
If the agent provided a callback parameter, the server redirects the user to that URL:¶
HTTP/1.1 303 See Other Location: https://agent.example/callback?state=abc123¶
If no callback was provided, the server displays a completion page telling the user they may close the window.¶
When an auth token expires, the agent requests a new one by presenting the expired auth token.¶
Request:¶
POST /token HTTP/1.1
Host: auth.example
Content-Type: application/json
Signature-Input: sig=("@method" "@authority" "@path" "signature-key");created=1730217600
Signature: sig=:...signature bytes...:
Signature-Key: sig=jwt;jwt="eyJhbGc..."
{
"auth_token": "eyJhbGc..."
}
¶
Response:¶
{
"auth_token": "eyJhbGc...",
"expires_in": 3600
}
¶
The auth server verifies the agent's HTTP signature, validates the expired auth token, and issues a new auth token. The auth server MAY reject the refresh if the token has been expired beyond its refresh window.¶
An agent delegate SHOULD be associated with at most one user. The auth server tracks the association between an agent delegate (identified by the sub claim in the agent token) and the user who authorized it. The user may be anonymous to the agent and to the resource, but the auth server always knows who authorized the delegate.¶
This association enables:¶
Participants publish metadata at well-known URLs ([RFC8615]) to enable discovery.¶
Published at /.well-known/aauth-agent.json:¶
{
"agent": "https://agent.example",
"jwks_uri": "https://agent.example/.well-known/jwks.json",
"client_name": "Example AI Assistant",
"logo_uri": "https://agent.example/logo.png",
"logo_dark_uri": "https://agent.example/logo-dark.png",
"callback_endpoint": "https://agent.example/callback",
"localhost_callback_allowed": true,
"clarification_supported": true,
"tos_uri": "https://agent.example/tos",
"policy_uri": "https://agent.example/privacy"
}
¶
Fields:¶
agent (REQUIRED): The agent's HTTPS URL¶
jwks_uri (REQUIRED): URL to the agent's JSON Web Key Set¶
client_name (OPTIONAL): Human-readable agent name (per [RFC7591])¶
logo_uri (OPTIONAL): URL to agent logo (per [RFC7591])¶
logo_dark_uri (OPTIONAL): URL to agent logo for dark backgrounds¶
callback_endpoint (OPTIONAL): The agent's HTTPS callback endpoint URL. The agent MAY append path and query parameters at runtime to construct the callback URL — any URL on the same origin is valid. If absent, the agent does not support callbacks.¶
localhost_callback_allowed (OPTIONAL): Boolean indicating whether the agent supports localhost callbacks (for CLI tools and desktop applications). Default: false. When true, any http URL with a loopback address (127.0.0.1, [::1], or localhost) and a port is permitted as a callback URL at runtime.¶
clarification_supported (OPTIONAL): Boolean indicating whether the agent supports clarification chat during consent. Default: false.¶
tos_uri (OPTIONAL): URL to terms of service (per [RFC7591])¶
policy_uri (OPTIONAL): URL to privacy policy (per [RFC7591])¶
Published at /.well-known/aauth-issuer.json:¶
{
"issuer": "https://auth.example",
"token_endpoint": "https://auth.example/token",
"interaction_endpoint": "https://auth.example/interact",
"jwks_uri": "https://auth.example/.well-known/jwks.json"
}
¶
Fields:¶
Published at /.well-known/aauth-resource.json:¶
{
"resource": "https://resource.example",
"jwks_uri": "https://resource.example/.well-known/jwks.json",
"client_name": "Example Data Service",
"logo_uri": "https://resource.example/logo.png",
"logo_dark_uri": "https://resource.example/logo-dark.png",
"resource_token_endpoint": "https://resource.example/resource-token",
"interaction_endpoint": "https://resource.example/interact",
"scope_descriptions": {
"data.read": "Read access to your data and documents",
"data.write": "Create and update your data and documents",
"data.delete": "Permanently delete your data and documents"
},
"additional_signature_components": ["content-type", "content-digest"]
}
¶
Fields:¶
resource (REQUIRED): The resource's HTTPS URL¶
jwks_uri (REQUIRED): URL to the resource's JSON Web Key Set¶
client_name (OPTIONAL): Human-readable resource name (per [RFC7591])¶
logo_uri (OPTIONAL): URL to resource logo (per [RFC7591])¶
logo_dark_uri (OPTIONAL): URL to resource logo for dark backgrounds¶
resource_token_endpoint (OPTIONAL): URL where agents can proactively request a resource token for specific scopes without first making an API call. See Resource Token Endpoint.¶
interaction_endpoint (OPTIONAL): URL where users are sent for resource-level interaction¶
scope_descriptions (OPTIONAL): Object mapping scope names to human-readable descriptions, for the auth server to use in consent UI¶
additional_signature_components (OPTIONAL): Additional HTTP message components that must be covered in signatures¶
The purpose parameter is an optional human-readable string that an agent passes to the auth server when requesting an auth token. It declares why access is being requested, providing context for authorization decisions without requiring the auth server or resource to evaluate or enforce the stated purpose.¶
When an agent requests an auth token from the auth server, it MAY include a purpose parameter that describes the reason for the access request in terms meaningful to the authorizing user.¶
The auth server SHOULD present the purpose value to the user during consent. The auth server MAY log the purpose for audit and monitoring purposes. The purpose is also available to the user during clarification chat.¶
The purpose parameter enables an agent to provide context for a request that is part of a larger task initiated earlier. A user may have numerous outstanding tasks managed by different agents, and the purpose helps the user understand which task triggered a particular authorization request. For example:¶
Without purpose, the user sees only "Agent X wants calendar.read" with no way to distinguish which of their active tasks prompted the request.¶
The purpose parameter is self-asserted by the requesting agent or application. Auth servers and users SHOULD treat it as informational context, not as a trusted assertion. A malicious agent could declare a benign purpose while intending harmful actions.¶
However, the declared purpose creates accountability. If an agent declares its purpose is to "find available meeting times" but then reads email content, the discrepancy between declared purpose and actual behavior is detectable by monitoring systems.¶
AAuth uses HTTP Message Signatures ([RFC9421]) for request authentication.¶
All AAuth requests MUST include:¶
The Signature-Key header ([I-D.hardt-httpbis-signature-key]) provides keying material:¶
HTTP Message Signatures in AAuth MUST cover:¶
@method: HTTP method¶
@authority: Target host¶
@path: Request path¶
signature-key: The Signature-Key header value¶
Resources MAY require additional components via additional_signature_components in metadata.¶
The Signature-Input header MUST include:¶
created: Signature creation timestamp (Unix time)¶
The created timestamp MUST NOT be more than 60 seconds in the past or future.¶
When a server (resource or auth server) receives a signed request, it MUST perform the following verification steps. Any failure MUST result in a 401 response with the appropriate error code.¶
Signature, Signature-Input, and Signature-Key headers. If any are missing, return invalid_signature.¶
Signature-Input covers the required components: @method, @authority, @path, and signature-key. If the resource requires additional components via additional_signature_components, verify those are covered as well.¶
created parameter is present and within 60 seconds of the current time. Reject if outside this window.¶
Obtain the public key from the Signature-Key header according to the scheme:¶
When a request includes a JWT (agent token or auth token) via scheme=jwt, the server MUST verify the JWT per [RFC7515] and [RFC7519]:¶
typ matches the expected token type (agent+jwt or auth+jwt).¶
jwks_uri in the issuer's metadata and selecting the key matching the JWT's kid header parameter. Keys are JSON Web Keys as defined in [RFC7517].¶
exp claim is in the future. Verify the iat claim is not in the future.¶
cnf claim ([RFC7800]) matches the key used to sign the HTTP request. This binds the JWT to the request signer.¶
When verifying an agent token (typ: agent+jwt):¶
jwks_uri in /.well-known/aauth-agent.json at the iss URL).¶
iss is a valid HTTPS URL conforming to the Server Identifier requirements.¶
cnf.jwk matches the key used to sign the HTTP request.¶
aud is present, verify that the server's own identifier is listed in the aud claim. If the server's identifier is not listed, reject the token.¶
When verifying an auth token (typ: auth+jwt):¶
jwks_uri in /.well-known/aauth-issuer.json at the iss URL).¶
iss is a valid HTTPS URL conforming to the Server Identifier requirements.¶
aud matches the resource's own identifier (or the agent's identifier for self-access tokens).¶
agent matches the agent identifier from the request's signing context.¶
cnf.jwk matches the key used to sign the HTTP request.¶
sub or scope is present.¶
When an auth server receives a resource token (typ: resource+jwt) in a token request:¶
jwks_uri in /.well-known/aauth-resource.json at the iss URL).¶
aud matches the auth server's own identifier.¶
agent matches the requesting agent's identifier.¶
agent_jkt ([RFC7638]) matches the JWK Thumbprint of the key used to sign the HTTP request.¶
exp is in the future.¶
jti has not been seen before (replay detection). The auth server SHOULD maintain a cache of seen jti values for the token's lifetime.¶
txn is present, carry it forward into the issued auth token.¶
Agents MUST verify responses from auth servers and resources to prevent token injection and confused deputy attacks.¶
When an agent receives an auth token from an auth server (either as a direct grant or via polling):¶
iss matches the auth server the agent sent the token request to.¶
aud matches the resource the agent intends to access (or the agent's own identifier for self-access tokens).¶
cnf.jwk matches the agent's own signing key.¶
agent matches the agent's own identifier.¶
When an agent receives a 401 response with AAuth: require=auth-token:¶
resource-token and auth-server parameters from the AAuth header.¶
typ is resource+jwt.¶
jwks_uri in /.well-known/aauth-resource.json).¶
iss matches the resource the agent sent the request to.¶
agent matches the agent's own identifier.¶
agent_jkt matches the JWK Thumbprint of the agent's signing key.¶
exp is in the future.¶
These checks prevent a malicious intermediary from substituting a different resource token or redirecting the agent to a different auth server.¶
AAuth defines error responses for both 401 authentication challenges and deferred response error codes. Token and signature errors use 401 with JSON error bodies. Deferred response errors use standard HTTP status codes with JSON error bodies as defined in the Deferred Responses section.¶
{
"error": "invalid_request",
"error_description": "Human-readable description"
}
¶
These errors are returned as 401 responses to API requests.¶
The HTTP Message Signature is missing, malformed, or verification failed. When the signature is missing required components, the response SHOULD include required_components:¶
{
"error": "invalid_signature",
"error_description": "Signature missing required components",
"required_components": ["@method", "@authority", "@path", "signature-key"]
}
¶
The agent token is missing, malformed, expired, or signature verification failed.¶
The resource token is missing, malformed, expired, or signature verification failed.¶
The auth token is missing, malformed, expired, or signature verification failed.¶
The key binding verification failed. The public key used to sign the request does not match the key bound in the token.¶
Errors returned in response to the initial POST to the token endpoint:¶
| Error | Status | Meaning |
|---|---|---|
invalid_request
|
400 | Malformed JSON, missing required fields, or invalid parameter values |
invalid_resource_token
|
400 | Resource token is invalid, expired, or malformed |
invalid_signature
|
401 | HTTP signature verification failed |
invalid_auth_token
|
400 | Expired auth token presented for refresh is invalid or beyond the refresh window |
server_error
|
500 | Internal error |
Errors returned as terminal responses when polling a pending URL:¶
| Error | Status | Meaning |
|---|---|---|
denied
|
403 | User or approver explicitly denied the request |
abandoned
|
403 | Interaction code was used but the user did not complete the interaction |
expired
|
408 | Timed out — interaction code was never used |
invalid_code
|
410 | Interaction code not recognized or already consumed |
server_error
|
500 | Internal error |
Agents MUST treat unknown error values as fatal.¶
AAuth uses standard HTTP async semantics (202 Accepted, Location, Prefer: wait, Retry-After) rather than custom polling mechanisms. This pattern:¶
Prefer: wait semantics negotiate connection hold duration between agent and server.¶
Location URL serves as the device code, Prefer: wait replaces the polling interval, and standard status codes replace custom error strings.¶
AAuth requires HTTP Message Signatures on every request to the auth server and resources. This differs from OAuth 2.0 where client authentication is optional or uses separate mechanisms (client secrets, mTLS, DPoP).¶
AAuth uses HTTPS URLs as agent identifiers rather than pre-registered client IDs. Agents publish metadata and JWKS at well-known endpoints.¶
ABCD1234) can be typed, read aloud, or entered from a different device.¶
GET. No custom token exchange endpoint is needed.¶
Prefer: wait.¶
require=interaction means the agent must facilitate a redirect with the interaction code. require=approval means the AS is handling approval directly — the agent just polls.¶
Interaction codes are restricted to unreserved URI characters ([RFC3986] Section 2.3: A-Z a-z 0-9 - . _ ~). Unreserved characters do not require percent-encoding in URI query parameters, eliminating double-encoding, missing encoding, and inconsistent encoding across implementations.¶
All AAuth tokens are proof-of-possession tokens. The holder must prove possession of the private key corresponding to the public key in the token's cnf claim.¶
HTTP Message Signatures provide:¶
Location values in 202 responses) MUST be unguessable and SHOULD have limited lifetime¶
GET poll to the pending URL¶
404 on subsequent requests¶
Resources include the auth-server parameter in their AAuth response header when returning a resource token. The agent MUST use the auth server URL from the resource's challenge — the resource determines which auth server to use for its tokens.¶
An agent MUST NOT substitute a different auth server than the one specified by the resource. Auth servers MUST verify that the resource token's aud matches their own identifier.¶
Servers that fetch JWKS documents for signature verification SHOULD cache the results with a TTL appropriate to their risk tolerance (recommended: 5 minutes for auth servers, 60 minutes for resources). Servers SHOULD support standard HTTP caching headers (Cache-Control, Expires) on JWKS responses.¶
When signature verification fails due to an unknown kid, the server SHOULD re-fetch the JWKS once before returning an error, to handle key rotation.¶
When a resource acts as an agent in call chaining, it uses its own signing key and presents its own credentials to the downstream resource. The downstream resource sees Resource 1 as the requesting agent. Resource 1 MUST publish agent metadata (/.well-known/aauth-agent.json) so that downstream resources and auth servers can verify its identity.¶
The upstream_token parameter in the token request allows the downstream auth server to verify the authorization chain — it can confirm that Resource 1 was authorized by the original user to access the upstream resource.¶
This specification does not define a token revocation mechanism. Auth tokens are short-lived and bound to specific signing keys, limiting the window of exposure. Auth servers SHOULD issue auth tokens with the shortest practical lifetime. When a resource detects misuse, it can reject the token and require re-authorization.¶
Auth servers MAY implement revocation by maintaining a deny list of jti values. Resources can check revocation by querying the auth server, though this adds latency to every request. A future specification may define a standardized revocation mechanism.¶
The created timestamp in HTTP Message Signatures limits signature validity to a 60-second window, providing basic replay protection. Servers MAY maintain a cache of recently seen signatures to detect replays within this window.¶
Resource tokens include a jti claim for replay detection at the auth server. Auth servers SHOULD maintain a cache of seen jti values for at least the token's lifetime to prevent resource token replay.¶
This specification registers the following Well-Known URIs:¶
This specification registers the following media types:¶
This specification registers the following HTTP header:¶
AAuth: Authentication, authorization, and interaction requirements¶
This appendix describes common patterns for how agent delegates obtain agent tokens from their agent server. The specific mechanism is out of scope for this specification.¶
Server workloads include containerized services, microservices, and serverless functions. These workloads prove identity using platform attestation.¶
SPIFFE-based: The workload obtains a SPIFFE SVID from the Workload API, generates an ephemeral key pair, and presents the SVID to the agent server via mTLS. The agent server issues an agent token with sub set to the SPIFFE ID.¶
WIMSE-based: The workload authenticates using platform credentials (cloud provider instance identity, Kubernetes service account tokens). The agent server evaluates delegation policies before issuing tokens.¶
Mobile apps prove legitimate installation using platform attestation APIs (iOS App Attest, Android Play Integrity API). Each installation generates a persistent ID and key pair. The agent server validates the attestation and issues an agent token with an installation-level sub.¶
Desktop and CLI tools use platform vaults (macOS Keychain, Windows TPM, Linux Secret Service) or ephemeral keys. After user authentication, the agent server issues tokens with an installation-level sub that persists across key rotations.¶
Browser-based applications generate ephemeral key pairs using the Web Crypto API. The web server acts as agent server and issues agent tokens to the browser session. Each session has a unique sub for tracking.¶
The author would like to thank reviewers for their feedback on concepts and earlier drafts: Aaron Pareki, Christian Posta, Frederik Krogsdal Jacobsen, Jared Hanson, Karl McGuinness, Nate Barbettini, Wils Dawson.¶