| Internet-Draft | AAuth | March 2026 |
| Hardt | Expires 3 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 3 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:¶
AAuth is a separate protocol 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.¶
sub identifier.¶
/.well-known/aauth-issuer.json.¶
/.well-known/aauth-resource.json.¶
interaction_endpoint in entity metadata. Both auth servers and resources MAY have interaction endpoints.¶
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, 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.¶
AAuth has three participant types — agents, resources, and auth servers — though not all are required for every interaction. All requests are signed using HTTP Message Signatures ([RFC9421]).¶
AAuth defines three token types, all of which are proof-of-possession tokens:¶
When a server returns 202 with user interaction required, it includes AAuth: require=interaction; code="..." in the response header. The interaction code binds the user's browser session to the pending request. The agent presents this code to the user via one of:¶
{interaction_endpoint}?code={interaction_code}&callback={callback_url}¶
{interaction_endpoint}?code={interaction_code} for scanning¶
interaction_endpoint (from metadata) and the code (with optional hyphens for readability)¶
The agent includes a callback URL in the interaction redirect. After user interaction completes, the server redirects the user to this URL, returning UX control to the agent. The agent MAY include state in the callback URL (e.g., as a query parameter) to maintain context when multiple sessions are in progress.¶
Resources can require different authentication levels via the AAuth response header:¶
require=pseudonym): Signed request proves possession of an included public key, providing consistent identity without verification¶
require=identity): Agent identity verified via JWKS or agent token¶
require=auth-token): Full authorization with auth token¶
During authorization, the auth server may indicate pending status via 202 responses:¶
An agent accesses a resource using only its agent identity, without authorization from an auth server.¶
sequenceDiagram
participant Agent as agent
participant Resource as resource
Agent->>Resource: HTTPSig request<br/>(scheme=jwks_uri or scheme=jwt)
Resource->>Resource: verify agent identity
Resource->>Agent: 200 OK
¶
A machine-to-machine agent obtains authorization directly without user interaction.¶
sequenceDiagram
participant Agent as agent
participant Resource as resource
participant Auth as auth server
Agent->>Resource: HTTPSig request
Resource->>Agent: 401 with<br/>resource_token + auth_server
Agent->>Auth: POST token_endpoint<br/>with resource_token
Auth->>Auth: validate resource_token<br/>evaluate policy
Auth->>Agent: auth_token (direct grant)
Agent->>Resource: HTTPSig request<br/>(scheme=jwt with auth-token)
Resource->>Agent: 200 OK
¶
An agent authenticates users to itself, combining SSO and API access.¶
sequenceDiagram
participant User as user
participant Agent as agent
participant Auth as auth server
Agent->>Auth: POST token_endpoint<br/>with scope (no resource_token),<br/>Prefer: wait=45
Auth->>Agent: 202 Accepted<br/>Location: /pending/def<br/>AAuth: require=interaction; code="EFGH5678"
Agent->>User: direct to interaction_endpoint<br/>with code
User->>Auth: authenticate and consent
Auth->>User: redirect to callback_url
Agent->>Auth: GET /pending/def
Auth->>Agent: 200 OK<br/>auth_token
Note over Agent: auth_token used for:<br/>1. User identity (SSO)<br/>2. API access by delegates
¶
An AI assistant accesses a user's data with explicit consent.¶
sequenceDiagram
participant User as user
participant Agent as agent
participant Resource as resource
participant Auth as auth server
Agent->>Resource: HTTPSig request
Resource->>Agent: 401 with<br/>resource_token + auth_server
Agent->>Auth: POST token_endpoint<br/>with resource_token, purpose,<br/>Prefer: wait=45
Auth->>Agent: 202 Accepted<br/>Location: /pending/ghi<br/>AAuth: require=interaction; code="IJKL9012"
Agent->>User: direct to interaction_endpoint<br/>with code
User->>Auth: authenticate and consent
Auth->>User: redirect to callback_url
Agent->>Auth: GET /pending/ghi
Auth->>Agent: 200 OK<br/>auth_token
Agent->>Resource: HTTPSig request<br/>(scheme=jwt with auth-token)
Resource->>Agent: 200 OK
¶
The auth server obtains approval directly — from a user (e.g., push notification, existing session, email) or an auth agent — without the agent facilitating a redirect. The agent simply polls until the request resolves.¶
sequenceDiagram
participant User as user / auth agent
participant Agent as agent
participant Resource as resource
participant Auth as auth server
Agent->>Resource: HTTPSig request
Resource->>Agent: 401 with<br/>resource_token + auth_server
Agent->>Auth: POST token_endpoint<br/>with resource_token, purpose,<br/>Prefer: wait=45
Auth->>Agent: 202 Accepted<br/>Location: /pending/jkl<br/>AAuth: require=approval
Auth->>User: push notification /<br/>existing session / auth agent
User->>Auth: approve
Agent->>Auth: GET /pending/jkl<br/>Prefer: wait=45
Auth->>Agent: 200 OK<br/>auth_token
Agent->>Resource: HTTPSig request<br/>(scheme=jwt with auth-token)
Resource->>Agent: 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.¶
An agent refreshes an expired auth token by presenting the expired token.¶
sequenceDiagram
participant Agent as agent
participant Auth as auth server
participant Resource as resource
Note over Agent: auth_token expired
Agent->>Auth: POST token_endpoint<br/>with expired auth_token
Auth->>Auth: verify agent identity<br/>and expired token
Auth->>Agent: new auth_token
Agent->>Resource: HTTPSig request<br/>(scheme=jwt with auth-token)
Resource->>Agent: 200 OK
¶
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 (from resource metadata) 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.¶
sequenceDiagram
participant User as user
participant Agent as agent
participant Resource as resource
participant Auth as auth server
Agent->>Resource: HTTPSig request<br/>(scheme=jwt with auth-token)
Resource->>Agent: 202 Accepted<br/>Location: /pending/xyz<br/>AAuth: require=interaction; code="MNOP3456"
Agent->>User: direct to resource<br/>interaction_endpoint with code
User->>Resource: authenticate/consent
Note over Resource,Auth: Resource may perform AAuth,<br/>OAuth, or OIDC flow
Resource->>User: redirect to agent<br/>callback_url
User->>Agent: callback
Agent->>Resource: GET /pending/xyz
Resource->>Agent: 200 OK (original response)
¶
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..."
¶
During consent, the user can ask questions about the agent's purpose. The auth server delivers questions to the agent via polling on the pending URL, and the agent responds.¶
sequenceDiagram
participant User as user
participant Agent as agent
participant Auth as auth server
Note over Agent,Auth: Agent has Location URL,<br/>user is at interaction_endpoint
Agent->>Auth: GET /pending/abc<br/>Prefer: wait=45
Note over Agent,Auth: Connection held open
User->>Auth: "Why do you need<br/>calendar access?"
Auth->>Agent: 202 with clarification<br/>"Why do you need calendar access?"
Agent->>Auth: POST /pending/abc<br/>clarification_response
Note over Agent: "I need to find available<br/>meeting times for your<br/>Tokyo trip next week"
Auth->>User: display agent response
User->>Auth: grant consent
Agent->>Auth: GET /pending/abc<br/>Prefer: wait=45
Auth->>Agent: 200 OK with auth_token
¶
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.¶
When the downstream auth server can issue a token without user interaction:¶
sequenceDiagram
participant Agent as agent
participant R1 as resource 1
participant R2 as resource 2
participant AS1 as auth server 1
participant AS2 as auth server 2
Agent->>R1: HTTPSig request<br/>(with auth_token from AS1)
R1->>R1: verify auth_token
R1->>R2: HTTPSig request (as agent)
R2->>R1: 401 with<br/>resource_token + auth_server=AS2
R1->>AS2: POST token_endpoint<br/>resource_token from R2,<br/>upstream_token (auth_token from AS1)
AS2->>AS2: verify upstream_token<br/>(fetch AS1 JWKS)
AS2->>R1: auth_token for R2
R1->>R2: HTTPSig request<br/>(with auth_token from AS2)
R2->>R1: 200 OK
R1->>Agent: 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.¶
sequenceDiagram
participant User as user
participant Agent as agent
participant R1 as resource 1
participant R2 as resource 2
participant AS2 as auth server 2
Agent->>R1: HTTPSig request
R1->>R2: HTTPSig request (as agent)
R2->>R1: 401 with resource_token + auth_server=AS2
R1->>AS2: POST token_endpoint<br/>with upstream_token
AS2->>R1: 202 Accepted<br/>require=interaction; code=WXYZ
R1->>Agent: 202 Accepted<br/>Location: /pending/xyz<br/>AAuth: require=interaction; code="MNOP"
Agent->>User: direct to R1<br/>interaction_endpoint with code
User->>R1: interaction_endpoint
R1->>User: redirect to AS2<br/>interaction_endpoint with code
User->>AS2: authenticate and consent
AS2->>User: redirect to R1 callback
R1->>R1: poll AS2 pending URL<br/>receive auth_token for R2
R1->>R2: HTTPSig request<br/>(with auth_token from AS2)
R2->>R1: 200 OK
R1->>User: redirect to agent callback_url
Agent->>R1: GET /pending/xyz
R1->>Agent: 200 OK
¶
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 (from metadata) 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) or an auth agent — 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¶
Payload:
- iss: Agent server URL (the agent identifier)
- sub: Agent delegate identifier (stable across key rotations)
- cnf: Confirmation claim with jwk containing the delegate's public key
- iat: Issued at timestamp
- exp: Expiration timestamp¶
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
- agent: Agent identifier
- agent_jkt: JWK thumbprint of the agent's current signing key
- exp: Expiration timestamp
- scope: Requested scopes (optional)¶
Resources include resource tokens in the AAuth header when requiring authorization:¶
AAuth: require=auth-token; resource-token="eyJ..."; auth-server="https://auth.example"¶
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: Resource URL
- 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¶
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 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 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 or auth agent.¶
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¶
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
|
All error responses return JSON with Content-Type: application/json and Cache-Control: no-store:¶
{
"error": "denied",
"error_description": "User denied the request",
"error_uri": "https://auth.example/docs/errors#denied"
}
¶
error (REQUIRED): Machine-readable error code.¶
error_description (OPTIONAL): Human-readable explanation for logging or developer display.¶
error_uri (OPTIONAL): URL to a human-readable page with additional information about the error.¶
See the Error Responses section for token endpoint error codes and polling error codes.¶
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 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 or auth agent) and the agent simply polls.¶
Error response (request validation failed):¶
{
"error": "invalid_resource_token",
"error_description": "Resource token has expired"
}
¶
See Token Endpoint Errors in the Deferred Responses section for the full set of error codes.¶
Polling, terminal responses, and error handling follow the Deferred Responses protocol described above.¶
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.¶
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 (from metadata) 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 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,
"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 RFC 7591)¶
logo_uri (OPTIONAL): URL to agent logo (per RFC 7591)¶
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.¶
tos_uri (OPTIONAL): URL to terms of service (per RFC 7591)¶
policy_uri (OPTIONAL): URL to privacy policy (per RFC 7591)¶
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",
"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 RFC 7591)¶
logo_uri (OPTIONAL): URL to resource logo (per RFC 7591)¶
logo_dark_uri (OPTIONAL): URL to resource logo for dark backgrounds¶
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 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.¶
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¶
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.¶