<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE rfc [
  <!ENTITY nbsp    "&#160;">
  <!ENTITY zwsp   "&#8203;">
  <!ENTITY nbhy   "&#8209;">
  <!ENTITY wj     "&#8288;">
]>
<!-- name="GENERATOR" content="github.com/mmarkdown/mmark Mmark Markdown Processor - mmark.miek.nl" -->
<rfc xmlns:xi="http://www.w3.org/2001/XInclude" version="3" ipr="trust200902" docName="draft-hardt-aauth-protocol-00" submissionType="IETF" category="std" xml:lang="en" indexInclude="true">

<front>
<title abbrev="AAuth-Protocol">AAuth Protocol</title><seriesInfo value="draft-hardt-aauth-protocol-00" stream="IETF" status="standard" name="Internet-Draft"/>
<author initials="D." surname="Hardt" fullname="Dick Hardt"><organization>Hellō</organization><address><postal><street/>
</postal><email>dick.hardt@gmail.com</email>
</address></author><date/>
<area>Security</area>
<workgroup>TBD</workgroup>
<keyword>agent</keyword>
<keyword>authentication</keyword>
<keyword>authorization</keyword>
<keyword>http</keyword>
<keyword>signatures</keyword>

<abstract>
<t>This document defines the AAuth authorization protocol, in which agents obtain proof-of-possession tokens from auth servers to access resources on behalf of users and organizations. It specifies three token types (agent, resource, and auth), a unified token endpoint with deferred response support, and cross-domain federation between auth servers. It builds on the AAuth Headers specification (<xref target="I-D.hardt-aauth-headers"/>), which defines the AAuth-Requirement response header and HTTP Message Signatures profile.</t>
</abstract>

<note><name>Discussion Venues</name>
<t><em>Note: This section is to be removed before publishing as an RFC.</em></t>
<t>This document is part of the AAuth specification family. Source for this draft and an issue tracker can be found at <eref target="https://github.com/dickhardt/AAuth">https://github.com/dickhardt/AAuth</eref>.</t>
</note>

</front>

<middle>

<section anchor="introduction"><name>Introduction</name>
<t>OAuth 2.0 <xref target="RFC6749"/> was created to solve a security 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.</t>
<t>But the landscape has changed. New use cases have emerged that OAuth and OIDC were not designed to address:</t>

<ul spacing="compact">
<li><strong>On-demand authorization</strong> where agents do not know what resources they will require until runtime. Long-running agents may execute tasks over hours or days and discover new authorization needs as they progress.</li>
<li><strong>Multi-hop resource access</strong> where a resource needs to obtain authorization to access a downstream resource to fulfill a request, with interaction requirements bubbling back to the user through the chain.</li>
<li><strong>Cross-domain trust</strong> where agents and resources have different auth servers. In OAuth, the client and resource share the same authorization server. In dynamic ecosystems, agents routinely access resources governed by a different auth server.</li>
<li><strong>Authorization negotiation</strong> where the user and agent engage in a back-and-forth during consent — the user asks why access is needed, the agent explains or adjusts its request — rather than a binary approve/deny decision.</li>
</ul>
<t>AAuth introduces the following features to address these use cases:</t>

<ul spacing="compact">
<li><strong>Agent identity without pre-registration</strong>: HTTPS URLs with self-published metadata and JWKS enable agents to establish identity without registering credentials at each authorization server.</li>
<li><strong>Per-instance agent identity</strong>: Each agent instance has its own identifier (<tt>local@domain</tt>) and signing key. Authorization grants are per-instance, not per-application.</li>
<li><strong>Resource identity and resource-defined authorization</strong>: Resources issue signed challenges binding the request to the resource's identity and the agent's key, defining authorization requirements at request time. This decouples resources from auth servers, and prevents MITM and confused deputy attacks.</li>
<li><strong>Multi-hop resource access</strong>: A resource acts as an agent to access downstream resources, with interaction requirements bubbling back to the user.</li>
<li><strong>AS-to-AS federation</strong>: An agent's auth server can call a resource's auth server to obtain an auth token on behalf of its agent, enabling cross-domain access without the agent or resource being aware of the federation.</li>
<li><strong>Deferred responses</strong>: <tt>202 Accepted</tt> with polling is a first-class primitive across all endpoints, supporting headless agents, long-running consent, and clarification chat.</li>
<li><strong>Clarification chat with justification</strong>: Agents declare why access is needed, and users can ask questions during consent. The agent can explain or adjust its request.</li>
</ul>
<t>AAuth also provides enhancements over OAuth:</t>

<ul spacing="compact">
<li><strong>Proof-of-possession by default</strong>: OAuth bearer tokens can be stolen and replayed by any holder. AAuth binds every token to a signing key via HTTP Message Signatures.</li>
<li><strong>Unified authentication and authorization</strong>: OAuth and OIDC are separate protocols with separate flows and token types. AAuth uses a single auth token that carries both identity claims and authorized scopes.</li>
<li><strong>No protocol artifacts in browser redirects</strong>: Unlike OAuth, where browser redirects carry authorization codes that are vulnerable to interception, AAuth uses browser redirects only to transition the user between parties.</li>
<li><strong>Reuse of OpenID Connect vocabulary</strong>: AAuth reuses OpenID Connect scope values, identity claims, and enterprise extensions, lowering the adoption barrier.</li>
</ul>
<t>AAuth 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. The AAuth Header specification (<xref target="I-D.hardt-aauth-headers"/>) defines how resources communicate authentication requirements via the <tt>AAuth-Requirement</tt> header and how agents present cryptographic identity using HTTP Message Signatures. This specification builds on that foundation to define the authorization protocol — how agents obtain auth tokens from auth servers to access protected resources.</t>
</section>

<section anchor="conventions-and-definitions"><name>Conventions and Definitions</name>
<t>{::boilerplate bcp14-tagged}</t>
</section>

<section anchor="terminology"><name>Terminology</name>

<ul spacing="compact">
<li><strong>Agent</strong>: An HTTP client (<xref target="RFC9110"/>, Section 3.5) acting on behalf of a legal person (user or organization). Identified by an agent identifier of the form <tt>local@domain</tt> <xref target="agent-identifiers"/>. An agent has exactly one auth server that it sends all token requests to.</li>
<li><strong>Agent Server</strong>: A server that manages agent identity and issues agent tokens to agents. Identified by an HTTPS URL <xref target="server-identifiers"/> and publishes metadata at <tt>/.well-known/aauth-agent.json</tt>.</li>
<li><strong>Agent Token</strong>: A JWT issued by an agent server to an agent, binding the agent's signing key to the agent's identity <xref target="agent-tokens"/>.</li>
<li><strong>Auth Server</strong>: A server that authenticates users, obtains consent, evaluates authorization policies, and issues auth tokens. The auth server maintains the association between agents and their legal persons. Identified by an HTTPS URL <xref target="server-identifiers"/> and publishes metadata at <tt>/.well-known/aauth-issuer.json</tt>.</li>
<li><strong>Auth Token</strong>: A JWT issued by an auth server that grants an agent access to a resource, containing user identity and/or authorized scopes <xref target="auth-tokens"/>.</li>
<li><strong>Resource</strong>: A server that requires authentication and/or authorization to protect access to its APIs and data. Identified by an HTTPS URL <xref target="server-identifiers"/> and publishes metadata at <tt>/.well-known/aauth-resource.json</tt>. A resource has exactly one auth server that it accepts auth tokens from.</li>
<li><strong>Resource Token</strong>: A JWT issued by a resource binding the agent's identifier (<tt>sub</tt>) and key thumbprint to the resource's auth server (<tt>aud</tt>) <xref target="resource-tokens"/>.</li>
<li><strong>Interaction</strong>: User authentication, consent, or other action at an interaction endpoint. Triggered when a server returns <tt>202 Accepted</tt> with <tt>requirement=interaction</tt>.</li>
<li><strong>Markdown String</strong>: A human-readable text value formatted as Markdown (CommonMark). Fields of this type MAY define recommended sections. Implementations MUST sanitize Markdown before rendering to users.</li>
<li><strong>Justification</strong>: A Markdown string provided by the agent declaring why access is needed, presented to the user by the auth server during consent. <strong>TODO:</strong> Define recommended sections.</li>
<li><strong>Clarification</strong>: A Markdown string containing a question posed to the agent by the user during consent via the auth server. The agent may respond with an explanation or an updated request.</li>
<li><strong>Assessment</strong>: A Markdown string containing an auth server's evaluation of a request during cross-domain federation, conveyed from AS2 to AS1. <strong>TODO:</strong> Define recommended sections.</li>
</ul>
</section>

<section anchor="trust-model"><name>Trust Model</name>
<t>Agents trust their auth server. Resources trust their auth server. When they are in different trust domains, their auth servers federate.</t>

<sourcecode type="ascii-art"><![CDATA[  Single Domain            Cross Domain

       ┌────┐          ┌─────┐      ┌─────┐
       │ AS │          │ AS1 │◄────►│ AS2 │
       └┬──┬┘          └──┬──┘      └──┬──┘
        ▲  ▲              ▲            ▲
        │  │              │            │
    Agent  Resource    Agent         Resource
]]></sourcecode>
</section>

<section anchor="bootstrapping"><name>Bootstrapping</name>
<t>Before protocol flows begin, each entity must be established with its identity, keys, and relationships.</t>

<section anchor="entity-metadata"><name>Entity Metadata</name>
<t>Each entity publishes metadata at a well-known URL:</t>

<ul spacing="compact">
<li>Agent servers publish at <tt>/.well-known/aauth-agent.json</tt> — including JWKS URI, display name, and capabilities <xref target="agent-server-metadata"/>.</li>
<li>Auth servers publish at <tt>/.well-known/aauth-issuer.json</tt> — including token endpoint and supported scopes <xref target="auth-server-metadata"/>.</li>
<li>Resources publish at <tt>/.well-known/aauth-resource.json</tt> — including auth server, required scopes, and resource token endpoint <xref target="resource-metadata"/>.</li>
</ul>
</section>

<section anchor="agent-identity"><name>Agent Identity</name>
<t>An agent obtains an agent token from its agent server. The agent token binds the agent's signing key to its agent identifier (<tt>local@domain</tt>). See <xref target="agent-token-acquisition"/> for common provisioning patterns.</t>
</section>

<section anchor="auth-server-association"><name>Auth Server Association</name>
<t>An agent has exactly one auth server that it sends all token requests to. How the agent learns its auth server is out of scope — this is determined by configuration during agent setup (e.g., set by the agent server or chosen by the person deploying the agent).</t>
</section>

<section anchor="person-agent-association"><name>Person-Agent Association</name>
<t>The auth server maintains the association between an agent and its legal person (user or organization). This association is typically established when the person first authorizes the agent at the auth server via the interaction flow. An organization administrator may also pre-authorize agents for the organization.</t>
<t>The auth server MAY establish a direct communication channel with the user (e.g., email, push notification, or messaging) to support out-of-band authorization, approval notifications, and revocation alerts.</t>
</section>
</section>

<section anchor="protocol-overview"><name>Protocol Overview</name>

<section anchor="tokens"><name>Tokens</name>
<t>AAuth defines three proof-of-possession token types, all JWTs bound to a specific signing key:</t>

<ul spacing="compact">
<li><strong>Agent Token</strong> (<tt>agent+jwt</tt>): Issued by an agent server to an agent, binding the agent's key to its identity <xref target="agent-tokens"/>.</li>
<li><strong>Resource Token</strong> (<tt>resource+jwt</tt>): Issued by a resource in response to a request, binding the access challenge to the resource's identity <xref target="resource-tokens"/>.</li>
<li><strong>Auth Token</strong> (<tt>auth+jwt</tt>): Issued by an auth server, granting an agent access to a specific audience <xref target="auth-tokens"/>.</li>
</ul>
</section>

<section anchor="agent-identity-1"><name>Agent Identity</name>
<t>Every agent holds an agent token issued by its agent server. The agent token binds the agent's signing key to its agent identifier. The agent's auth server maintains the association between the agent and its legal person.</t>
<t>The agent always presents its agent token via the <tt>Signature-Key</tt> header when calling its auth server's token endpoint.</t>
</section>

<section anchor="scopes"><name>Scopes</name>
<t>AAuth reuses the scope and claims vocabulary defined by OpenID Connect. Scopes may request identity claims (using OpenID Connect Core 1.0 <xref target="OpenID.Core"/> scope values such as <tt>openid</tt>, <tt>profile</tt>, <tt>email</tt>) or resource authorization (using scopes defined by the resource, such as <tt>data.read</tt> or <tt>calendar.write</tt> as defined in the resource's <tt>scope_descriptions</tt> metadata), or both.</t>
</section>

<section anchor="protocol-steps"><name>Protocol Steps</name>
<t>This section describes the fundamental protocol steps. Detailed end-to-end flows combining these steps are in <xref target="detailed-flows"/>.</t>

<section anchor="obtaining-a-resource-token"><name>Obtaining a Resource Token</name>
<t>When the agent knows the resource's requirements (from metadata or a previous request), it requests a resource token directly from the resource's <tt>resource_token_endpoint</tt>:</t>

<sourcecode type="ascii-art"><![CDATA[Agent                                       Resource
  |                                            |
  |  POST resource_token_endpoint              |
  |-------------------------------------------->|
  |                                            |
  |  resource_token                            |
  |<--------------------------------------------|
]]></sourcecode>
<t>Alternatively, a resource MAY respond to any request with <tt>401</tt> and an <tt>AAuth-Requirement</tt> containing a resource token, indicating what authorization is required. This is the discovery path when the agent does not know the resource's requirements in advance.</t>
<t>A resource MAY also return <tt>401</tt> with a new resource token to a request that includes an auth token — for example, when the request requires a higher level of authorization than the current token provides. Agents MUST be prepared for this step-up authorization at any time.</t>
</section>

<section anchor="obtaining-an-auth-token"><name>Obtaining an Auth Token</name>
<t>The agent presents a resource token (or scope for agent-as-audience) to its auth server's token endpoint. The auth server evaluates policy and returns an auth token immediately, or a <tt>202</tt> if user authorization is required.</t>

<sourcecode type="ascii-art"><![CDATA[Agent                                       Auth Server
  |                                            |
  |  POST token_endpoint                       |
  |  (resource_token or scope)                 |
  |-------------------------------------------->|
  |                                            |
  |  200 OK + auth_token                       |
  |  — or —                                    |
  |  202 Accepted                              |
  |  (requirement=interaction / approval)          |
  |<--------------------------------------------|
]]></sourcecode>
</section>

<section anchor="obtaining-authorization"><name>Obtaining Authorization</name>
<t>When the auth server requires user consent or authentication, it returns a <tt>202</tt> with an interaction <tt>url</tt> and <tt>code</tt>. The agent directs the user to <tt>{url}?code={code}</tt>. After the user completes the action, the agent polls for the result.</t>

<sourcecode type="ascii-art"><![CDATA[Agent                      User                       Auth Server
  |                          |                             |
  |  direct to               |                             |
  |  {url}?code={code}       |                             |
  |------------------------->|                             |
  |                          |                             |
  |                          |  authenticate               |
  |                          |  and consent                |
  |                          |---------------------------->|
  |                          |                             |
  |                          |  redirect to                |
  |                          |  callback_url               |
  |                          |<----------------------------|
  |                          |                             |
  |  GET pending URL                                       |
  |------------------------------------------------------->|
  |                          |                             |
  |  200 OK + auth_token                                   |
  |<-------------------------------------------------------|
]]></sourcecode>
<t>When the auth server can obtain authorization directly from the user without the agent's involvement <xref target="person-agent-association"/>, it returns <tt>requirement=approval</tt> and the agent simply polls.</t>
</section>

<section anchor="cross-domain-federation"><name>Cross-Domain Federation</name>
<t>When the resource's auth server differs from the agent's, the agent's auth server (AS1) federates with the resource's auth server (AS2). The agent is unaware of the federation.</t>

<sourcecode type="ascii-art"><![CDATA[Agent                      AS1                        AS2
  |                          |                          |
  |  POST /token             |                          |
  |  resource_token          |                          |
  |  (aud=AS2)               |                          |
  |------------------------->|                          |
  |                          |                          |
  |                          |  POST /token             |
  |                          |  resource_token          |
  |                          |  agent_token             |
  |                          |------------------------->|
  |                          |                          |
  |                          |  auth_token              |
  |                          |<-------------------------|
  |                          |                          |
  |  auth_token              |                          |
  |<-------------------------|                          |
]]></sourcecode>
</section>
</section>
</section>

<section anchor="aauth-requirement-requirement-levels"><name>AAuth-Requirement Requirement Levels</name>
<t>This document defines the following requirement level for the <tt>AAuth-Requirement</tt> response header (<xref target="I-D.hardt-aauth-headers"/>). These levels extend the <tt>pseudonym</tt> and <tt>identity</tt> levels defined by the header specification.</t>

<section anchor="auth-token-required"><name>Auth Token Required</name>
<t>When a resource requires an auth token, it responds with <tt>401 Unauthorized</tt> and includes the <tt>AAuth-Requirement</tt> header with a resource token:</t>

<sourcecode type="http"><![CDATA[HTTP/1.1 401 Unauthorized
AAuth-Requirement: requirement=auth-token; resource-token="eyJ..."
]]></sourcecode>
<t>The agent presents the resource token to its auth server's token endpoint to obtain an auth token. See <xref target="resource-tokens"/> and <xref target="token-endpoint"/> for details. A resource MAY also use <tt>402 Payment Required</tt> with the same <tt>AAuth-Requirement</tt> header when payment is additionally required (see draft-hardt-aauth-headers).</t>
<t>When the auth server requires user interaction to complete an authorization request (e.g., authentication, consent), it returns <tt>202 Accepted</tt> with <tt>requirement=interaction</tt>, a <tt>url</tt>, and a <tt>code</tt> (<xref target="I-D.hardt-aauth-headers"/>). The agent directs the user to the interaction URL with the code. See <xref target="user-interaction"/> for details.</t>
<t>When the auth server can obtain approval without the agent directing a user — for example, by contacting the user directly (push notification, email), or obtaining administrator approval — it returns <tt>202 Accepted</tt> with <tt>requirement=approval</tt>. This is the recommended flow once the auth server has established a direct communication channel with the user <xref target="person-agent-association"/>. The agent polls the pending URL until a terminal response is received. See <xref target="deferred-responses"/> for details.</t>
</section>
</section>

<section anchor="identifier-and-url-requirements"><name>Identifier and URL Requirements</name>

<section anchor="server-identifiers"><name>Server Identifiers</name>
<t>The <tt>agent</tt>, <tt>resource</tt>, and <tt>issuer</tt> values that identify agents, resources, and auth servers MUST conform to the following:</t>

<ul spacing="compact">
<li>MUST use the <tt>https</tt> scheme</li>
<li>MUST contain only scheme and host (no port, path, query, or fragment)</li>
<li>MUST NOT include a trailing slash</li>
<li>MUST be lowercase</li>
<li>Internationalized domain names MUST use the ASCII-Compatible Encoding (ACE) form (A-labels) as defined in <xref target="RFC5890"/></li>
</ul>
<t>Valid identifiers:</t>

<ul spacing="compact">
<li><tt>https://agent.example</tt></li>
<li><tt>https://xn--nxasmq6b.example</tt> (internationalized domain in ACE form)</li>
</ul>
<t>Invalid identifiers:</t>

<ul spacing="compact">
<li><tt>http://agent.example</tt> (not HTTPS)</li>
<li><tt>https://Agent.Example</tt> (not lowercase)</li>
<li><tt>https://agent.example:8443</tt> (contains port)</li>
<li><tt>https://agent.example/v1</tt> (contains path)</li>
<li><tt>https://agent.example/</tt> (trailing slash)</li>
</ul>
<t>Implementations MUST perform exact string comparison on server identifiers.</t>
</section>

<section anchor="agent-identifiers"><name>Agent Identifiers</name>
<t>Agent identifiers are of the form <tt>local@domain</tt> where <tt>domain</tt> is the agent server's domain. The <tt>local</tt> part MUST consist of lowercase ASCII letters (<tt>a-z</tt>), digits (<tt>0-9</tt>), hyphen (<tt>-</tt>), underscore (<tt>_</tt>), plus (<tt>+</tt>), and period (<tt>.</tt>). The <tt>local</tt> part MUST NOT be empty and MUST NOT exceed 255 characters. The <tt>domain</tt> part MUST be a valid domain name conforming to the server identifier requirements above (without scheme).</t>
<t>Valid agent identifiers:</t>

<ul spacing="compact">
<li><tt>assistant-v2@agent.example</tt></li>
<li><tt>cli+instance.1@tools.example</tt></li>
</ul>
<t>Invalid agent identifiers:</t>

<ul spacing="compact">
<li><tt>My Agent@agent.example</tt> (uppercase letters and space in local part)</li>
<li><tt>@agent.example</tt> (empty local part)</li>
<li><tt>agent@http://agent.example</tt> (domain includes scheme)</li>
</ul>
<t>Implementations MUST perform exact string comparison on agent identifiers (case-sensitive).</t>
</section>

<section anchor="endpoint-urls"><name>Endpoint URLs</name>
<t>The <tt>token_endpoint</tt>, <tt>resource_token_endpoint</tt>, and <tt>callback_endpoint</tt> values MUST conform to the following:</t>

<ul spacing="compact">
<li>MUST use the <tt>https</tt> scheme</li>
<li>MUST NOT contain a fragment</li>
<li>MUST NOT contain a query string</li>
</ul>
<t>When <tt>localhost_callback_allowed</tt> is <tt>true</tt> in the agent's metadata, the agent MAY use a localhost callback URL as the <tt>callback</tt> parameter to the interaction endpoint.</t>
</section>

<section anchor="other-urls"><name>Other URLs</name>
<t>The <tt>jwks_uri</tt>, <tt>tos_uri</tt>, <tt>policy_uri</tt>, <tt>logo_uri</tt>, and <tt>logo_dark_uri</tt> values MUST use the <tt>https</tt> scheme.</t>
</section>
</section>

<section anchor="agent-tokens"><name>Agent Tokens</name>
<t>Agent tokens bind an agent's signing key to its identity.</t>

<section anchor="agent-token-structure"><name>Agent Token Structure</name>
<t>An agent token is a JWT with <tt>typ: agent+jwt</tt> containing:</t>
<t>Header:
- <tt>alg</tt>: Signing algorithm. EdDSA is RECOMMENDED. Implementations MUST NOT accept <tt>none</tt>.
- <tt>typ</tt>: <tt>agent+jwt</tt>
- <tt>kid</tt>: Key identifier</t>
<t>Required payload claims:
- <tt>iss</tt>: Agent server URL
- <tt>dwk</tt>: <tt>aauth-agent.json</tt> — the well-known metadata document name for key discovery (<xref target="I-D.hardt-httpbis-signature-key"/>)
- <tt>sub</tt>: Agent identifier (stable across key rotations)
- <tt>jti</tt>: Unique token identifier for replay detection and audit
- <tt>cnf</tt>: Confirmation claim (<xref target="RFC7800"/>) with <tt>jwk</tt> containing the agent's public key
- <tt>iat</tt>: Issued at timestamp
- <tt>exp</tt>: Expiration timestamp. Agent tokens SHOULD NOT have a lifetime exceeding 24 hours.</t>
<t>Optional payload claims:
- <tt>aud</tt>: Audience restriction. When present, the agent MUST only present this agent token to the specified server(s).
- <tt>aud_sub</tt>: The user identifier (<tt>sub</tt> value) from a previous auth token issued by the auth server in <tt>aud</tt>. This is a hint to the auth server to identify which user the agent is associated with. The auth server MUST NOT treat this claim as authoritative — the auth server maintains its own person-agent associations and uses <tt>aud_sub</tt> only to optimize lookup.</t>
<t>Agent servers MAY include additional claims in the agent token. Companion specifications may define additional claims for use by auth servers in policy evaluation — for example, software attestation, platform integrity, secure enclave status, workload identity assertions, or software publisher identity. Auth servers MUST ignore unrecognized claims.</t>
</section>

<section anchor="agent-token-usage"><name>Agent Token Usage</name>
<t>Agents present agent tokens via the <tt>Signature-Key</tt> header (<xref target="I-D.hardt-httpbis-signature-key"/>) using <tt>scheme=jwt</tt>:</t>

<sourcecode type="http"><![CDATA[Signature-Key: sig=jwt; jwt="eyJhbGciOiJFZERTQSIsInR5cCI6ImFnZW50K2p3dCJ9..."
]]></sourcecode>
</section>
</section>

<section anchor="resource-tokens"><name>Resource Tokens</name>
<t>Resource tokens provide cryptographic proof of resource identity, preventing confused deputy and MITM attacks.</t>

<section anchor="resource-token-structure"><name>Resource Token Structure</name>
<t>A resource token is a JWT with <tt>typ: resource+jwt</tt> containing:</t>
<t>Header:
- <tt>alg</tt>: Signing algorithm. EdDSA is RECOMMENDED. Implementations MUST NOT accept <tt>none</tt>.
- <tt>typ</tt>: <tt>resource+jwt</tt>
- <tt>kid</tt>: Key identifier</t>
<t>Payload:
- <tt>iss</tt>: Resource URL
- <tt>dwk</tt>: <tt>aauth-resource.json</tt> — the well-known metadata document name for key discovery (<xref target="I-D.hardt-httpbis-signature-key"/>)
- <tt>aud</tt>: Auth server URL
- <tt>jti</tt>: Unique token identifier for replay detection and audit
- <tt>agent</tt>: Agent identifier
- <tt>agent_jkt</tt>: JWK Thumbprint (<xref target="RFC7638"/>) of the agent's current signing key
- <tt>iat</tt>: Issued at timestamp
- <tt>exp</tt>: Expiration timestamp
- <tt>scope</tt>: Requested scopes (optional), as a space-separated string of scope values</t>
<t>Resource tokens SHOULD NOT have a lifetime exceeding 5 minutes. Resource tokens are single-use; the auth server MUST reject a resource token whose <tt>jti</tt> has been seen before.</t>
</section>

<section anchor="resource-token-usage"><name>Resource Token Usage</name>
<t>Resources include resource tokens in the <tt>AAuth-Requirement</tt> header when requiring authorization:</t>

<sourcecode type="http"><![CDATA[AAuth-Requirement: requirement=auth-token; resource-token="eyJ..."
]]></sourcecode>
</section>

<section anchor="resource-token-endpoint"><name>Resource Token Endpoint</name>
<t>When a resource publishes a <tt>resource_token_endpoint</tt> in its metadata, agents MAY request a resource token proactively — without first making an API call and receiving a <tt>401</tt> challenge.</t>
<t><strong>Request:</strong></t>

<sourcecode type="http"><![CDATA[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"
}
]]></sourcecode>
<t><strong>Response:</strong></t>

<sourcecode type="json"><![CDATA[{
  "resource_token": "eyJhbGc...",
  "scope": "data.read data.write"
}
]]></sourcecode>
<t>The resource's auth server is identified by the <tt>aud</tt> claim in the resource token. The agent sends the resource token to its own auth server's token endpoint.</t>
</section>

<section anchor="resource-token-endpoint-error-responses"><name>Resource Token Endpoint Error Responses</name>
<table>
<thead>
<tr>
<th>Error</th>
<th>Status</th>
<th>Meaning</th>
</tr>
</thead>

<tbody>
<tr>
<td><tt>invalid_request</tt></td>
<td>400</td>
<td>Missing or invalid parameters</td>
</tr>

<tr>
<td><tt>invalid_signature</tt></td>
<td>401</td>
<td>HTTP signature verification failed</td>
</tr>

<tr>
<td><tt>invalid_scope</tt></td>
<td>400</td>
<td>Requested scope not recognized by the resource</td>
</tr>

<tr>
<td><tt>server_error</tt></td>
<td>500</td>
<td>Internal error</td>
</tr>
</tbody>
</table><t>Error responses use the same format as the token endpoint <xref target="error-response-format"/>.</t>
</section>
</section>

<section anchor="auth-tokens"><name>Auth Tokens</name>
<t>Auth tokens grant agents access to resources after authentication and authorization.</t>

<section anchor="auth-token-structure"><name>Auth Token Structure</name>
<t>An auth token is a JWT with <tt>typ: auth+jwt</tt> containing:</t>
<t>Header:
- <tt>alg</tt>: Signing algorithm. EdDSA is RECOMMENDED. Implementations MUST NOT accept <tt>none</tt>.
- <tt>typ</tt>: <tt>auth+jwt</tt>
- <tt>kid</tt>: Key identifier</t>
<t>Required payload claims:
- <tt>iss</tt>: Auth server URL
- <tt>dwk</tt>: <tt>aauth-issuer.json</tt> — the well-known metadata document name for key discovery (<xref target="I-D.hardt-httpbis-signature-key"/>)
- <tt>aud</tt>: 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 <tt>aud</tt> is the agent server's URL.
- <tt>jti</tt>: Unique token identifier for replay detection and audit
- <tt>agent</tt>: Agent identifier
- <tt>cnf</tt>: Confirmation claim with <tt>jwk</tt> containing the agent's public key
- <tt>iat</tt>: Issued at timestamp
- <tt>exp</tt>: Expiration timestamp. Auth tokens SHOULD NOT have a lifetime exceeding 1 hour and MUST NOT have a lifetime exceeding 24 hours.</t>
<t>Conditional payload claims (at least one MUST be present):
- <tt>sub</tt>: User identifier
- <tt>scope</tt>: Authorized scopes, as a space-separated string of scope values consistent with <xref target="RFC9068"/> Section 2.2.3</t>
<t>At least one of <tt>sub</tt> or <tt>scope</tt> MUST be present.</t>
<t>The auth token MAY include additional claims registered in the IANA JSON Web Token Claims Registry <xref target="RFC7519"/> or defined in OpenID Connect Core 1.0 <xref target="OpenID.Core"/> Section 5.1.</t>
</section>

<section anchor="auth-token-usage"><name>Auth Token Usage</name>
<t>Agents present auth tokens via the <tt>Signature-Key</tt> header (<xref target="I-D.hardt-httpbis-signature-key"/>) using <tt>scheme=jwt</tt>:</t>

<sourcecode type="http"><![CDATA[Signature-Key: sig=jwt; jwt="eyJhbGciOiJFZERTQSIsInR5cCI6ImF1dGgrand0In0..."
]]></sourcecode>
</section>
</section>

<section anchor="deferred-responses"><name>Deferred Responses</name>
<t>Any endpoint in AAuth — whether an auth server token endpoint or a resource endpoint — MAY return a <tt>202 Accepted</tt> response (<xref target="RFC9110"/>) when it cannot immediately resolve a request. This is a first-class protocol primitive, not a special case. Agents MUST handle <tt>202</tt> responses regardless of the nature of the original request.</t>

<section anchor="initial-request"><name>Initial Request</name>
<t>The agent makes a request and signals its willingness to wait using the <tt>Prefer</tt> header (<xref target="RFC7240"/>):</t>

<sourcecode type="http"><![CDATA[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..."
}
]]></sourcecode>
</section>

<section anchor="pending-response"><name>Pending Response</name>
<t>When the server cannot resolve the request within the wait period:</t>

<sourcecode type="http"><![CDATA[HTTP/1.1 202 Accepted
Location: /pending/f7a3b9c
Retry-After: 0
Cache-Control: no-store
Content-Type: application/json

{
  "status": "pending",
  "location": "/pending/f7a3b9c"
}
]]></sourcecode>
<t>Headers:</t>

<ul spacing="compact">
<li><tt>Location</tt> (REQUIRED): The pending URL. The <tt>Location</tt> URL MUST be on the same origin as the responding server.</li>
<li><tt>Retry-After</tt> (REQUIRED): Seconds the agent SHOULD wait before polling. <tt>0</tt> means retry immediately.</li>
<li><tt>Cache-Control: no-store</tt> (REQUIRED): Prevents caching of pending responses.</li>
</ul>
<t>Body fields:</t>

<ul spacing="compact">
<li><tt>status</tt> (REQUIRED): <tt>"pending"</tt> while the request is waiting. <tt>"interacting"</tt> when the user has arrived at the interaction endpoint. Agents MUST treat unrecognized <tt>status</tt> values as <tt>"pending"</tt> and continue polling.</li>
<li><tt>location</tt> (REQUIRED): The pending URL (echoes the <tt>Location</tt> header).</li>
<li><tt>requirement</tt> (OPTIONAL): <tt>"interaction"</tt> when the agent must direct the user to an interaction endpoint (with <tt>code</tt>). <tt>"approval"</tt> when the auth server is obtaining approval directly.</li>
<li><tt>code</tt> (OPTIONAL): The interaction code. Present only with <tt>requirement: "interaction"</tt>.</li>
<li><tt>clarification</tt> (OPTIONAL): A question from the user during consent.</li>
</ul>
</section>

<section anchor="polling-with-get"><name>Polling with GET</name>
<t>After receiving a <tt>202</tt>, the agent switches to <tt>GET</tt> for all subsequent requests to the <tt>Location</tt> URL. The agent does NOT resend the original request body. <strong>Exception</strong>: During clarification chat, the agent uses <tt>POST</tt> to deliver a clarification response.</t>
<t>The agent MUST respect <tt>Retry-After</tt> values. If a <tt>Retry-After</tt> header is not present, the default polling interval is 5 seconds. If the server responds with <tt>429 Too Many Requests</tt>, the agent MUST increase its polling interval by 5 seconds (linear backoff, following the pattern in <xref target="RFC8628"/>, Section 3.5). The <tt>Prefer: wait=N</tt> header (<xref target="RFC7240"/>) MAY be included on polling requests to signal the agent's willingness to wait for a long-poll response.</t>
</section>

<section anchor="terminal-responses"><name>Terminal Responses</name>
<t>A non-<tt>202</tt> response terminates polling. The following table covers responses to both the initial request and subsequent GET polling requests at any AAuth endpoint:</t>
<table>
<thead>
<tr>
<th>Status</th>
<th>Meaning</th>
<th>Agent Behavior</th>
</tr>
</thead>

<tbody>
<tr>
<td><tt>200</tt></td>
<td>Success</td>
<td>Process response body</td>
</tr>

<tr>
<td><tt>400</tt></td>
<td>Invalid request</td>
<td>Check <tt>error</tt> field; fix and retry</td>
</tr>

<tr>
<td><tt>401</tt></td>
<td>Invalid signature / auth token required</td>
<td>Check credentials; obtain auth token if resource challenge</td>
</tr>

<tr>
<td><tt>402</tt></td>
<td>Auth token + payment required</td>
<td>Obtain auth token and satisfy payment requirement</td>
</tr>

<tr>
<td><tt>403</tt></td>
<td>Denied or abandoned</td>
<td>Surface to user; check <tt>error</tt> field</td>
</tr>

<tr>
<td><tt>408</tt></td>
<td>Expired</td>
<td>MAY initiate a fresh request</td>
</tr>

<tr>
<td><tt>410</tt></td>
<td>Gone — permanently invalid</td>
<td>MUST NOT retry</td>
</tr>

<tr>
<td><tt>429</tt></td>
<td>Too many requests</td>
<td>Increase polling interval by 5 seconds</td>
</tr>

<tr>
<td><tt>500</tt></td>
<td>Internal server error</td>
<td>Start over</td>
</tr>

<tr>
<td><tt>503</tt></td>
<td>Temporarily unavailable</td>
<td>Back off per <tt>Retry-After</tt>, retry</td>
</tr>
</tbody>
</table></section>

<section anchor="deferred-response-state-machine"><name>Deferred Response State Machine</name>
<t>The following state machine applies to any AAuth endpoint that returns a <tt>202 Accepted</tt> response — including auth server token endpoints and resource endpoints during call chaining.</t>

<artwork><![CDATA[Initial request (with Prefer: wait=N)
    |
    +-- 200 --> done (process response)
    +-- 202 --> note Location URL, check require/code
    +-- 400 --> invalid request — check error field, fix and retry
    +-- 401 --> invalid signature — check credentials
    +-- 402 --> auth token + payment required (resource only)
    +-- 500 --> server error — start over
    +-- 503 --> back off (Retry-After), retry
               |
               GET Location (with Prefer: wait=N)
               |
               +-- 200 --> done (process response)
               +-- 202 --> continue polling (check status and clarification)
               |           status=interacting → stop prompting user
               +-- 403 --> denied or abandoned — surface to user
               +-- 408 --> expired — MAY retry with fresh request
               +-- 410 --> invalid_code — do not retry
               +-- 429 --> slow_down — increase interval by 5s
               +-- 500 --> server_error — start over
               +-- 503 --> temporarily_unavailable — back off (Retry-After)
]]></artwork>
</section>
</section>

<section anchor="token-endpoint"><name>Token Endpoint</name>
<t>The auth server's <tt>token_endpoint</tt> issues auth tokens to agents and to other auth servers during cross-domain federation.</t>

<section anchor="token-endpoint-modes"><name>Token Endpoint Modes</name>
<table>
<thead>
<tr>
<th>Mode</th>
<th>Key Parameters</th>
<th>Use Case</th>
</tr>
</thead>

<tbody>
<tr>
<td>Resource access</td>
<td><tt>resource_token</tt></td>
<td>Agent needs auth token for a resource</td>
</tr>

<tr>
<td>Self-access (SSO/1P)</td>
<td><tt>scope</tt> (no <tt>resource_token</tt>)</td>
<td>Agent needs auth token for itself</td>
</tr>

<tr>
<td>Call chaining</td>
<td><tt>resource_token</tt> + <tt>upstream_token</tt></td>
<td>Resource acting as agent</td>
</tr>

<tr>
<td>AS-to-AS federation</td>
<td><tt>resource_token</tt> + <tt>agent_token</tt> + optional <tt>upstream_token</tt></td>
<td>Auth server federating on behalf of agent (includes <tt>upstream_token</tt> when call chaining cross-domain)</td>
</tr>

<tr>
<td>Token refresh</td>
<td><tt>auth_token</tt> (expired)</td>
<td>Renew expired token</td>
</tr>
</tbody>
</table></section>

<section anchor="authorization-request"><name>Authorization Request</name>
<t>The agent MUST make a signed POST to the <tt>token_endpoint</tt>. The request MUST include HTTP Message Signatures and the agent MUST present its agent token via the <tt>Signature-Key</tt> header using <tt>scheme=jwt</tt>.</t>
<t><strong>Request parameters:</strong></t>

<ul spacing="compact">
<li><tt>resource_token</tt> (CONDITIONAL): The resource token. Required when requesting access to another resource.</li>
<li><tt>scope</tt> (CONDITIONAL): Space-separated scope values. Used when the agent requests authorization to itself.</li>
<li><tt>upstream_token</tt> (OPTIONAL): An auth token from an upstream authorization, used in call chaining.</li>
<li><tt>agent_token</tt> (OPTIONAL): The agent's agent token. Used in AS-to-AS federation.</li>
<li><tt>justification</tt> (OPTIONAL): A Markdown string declaring why access is being requested. The auth server SHOULD present this value to the user during consent. The auth server MUST sanitize the Markdown before rendering to users. The auth server MAY log the <tt>justification</tt> for audit and monitoring purposes. <strong>TODO:</strong> Define recommended sections.</li>
<li><tt>login_hint</tt> (OPTIONAL): Hint about who to authorize, per <xref target="OpenID.Core"/> Section 3.1.2.1.</li>
<li><tt>tenant</tt> (OPTIONAL): Tenant identifier, per OpenID Connect Enterprise Extensions 1.0 <xref target="OpenID.Enterprise"/>.</li>
<li><tt>domain_hint</tt> (OPTIONAL): Domain hint, per OpenID Connect Enterprise Extensions 1.0 <xref target="OpenID.Enterprise"/>.</li>
</ul>
<t><strong>Example request:</strong></t>

<sourcecode type="http"><![CDATA[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...",
  "justification": "Find available meeting times"
}
]]></sourcecode>
</section>

<section anchor="auth-server-response"><name>Auth Server Response</name>
<t><strong>Direct grant response</strong> (<tt>200</tt>):</t>

<sourcecode type="json"><![CDATA[{
  "auth_token": "eyJhbGc...",
  "expires_in": 3600
}
]]></sourcecode>
<t><strong>User interaction required response</strong> (<tt>202</tt>):</t>

<sourcecode type="http"><![CDATA[HTTP/1.1 202 Accepted
Location: /pending/abc123
Retry-After: 0
Cache-Control: no-store
AAuth-Requirement: requirement=interaction; code="ABCD1234"
Content-Type: application/json

{
  "status": "pending",
  "location": "/pending/abc123",
  "require": "interaction",
  "code": "ABCD1234"
}
]]></sourcecode>
</section>

<section anchor="clarification-chat"><name>Clarification Chat</name>
<t>During user consent, the user may ask questions about the agent's stated justification. The auth server delivers these questions to the agent, and the agent responds. This enables a consent dialog without requiring the agent to have a direct channel to the user.</t>
<t>Agents that support clarification chat SHOULD declare <tt>"clarification_supported": true</tt> in their agent server metadata. Individual requests MAY indicate clarification support by including <tt>"clarification_supported": true</tt> in the token endpoint request body.</t>

<section anchor="clarification-flow"><name>Clarification Flow</name>
<t>When the user asks a question during consent, the auth server includes a <tt>clarification</tt> field in the next polling response:</t>

<sourcecode type="json"><![CDATA[{
  "status": "pending",
  "clarification": "Why do you need write access to my calendar?",
  "timeout": 120
}
]]></sourcecode>

<ul spacing="compact">
<li><tt>clarification</tt> (String): The user's question.</li>
<li><tt>timeout</tt> (Integer, OPTIONAL): Seconds until the auth server times out the user interaction. The agent MUST respond before this deadline.</li>
</ul>
</section>

<section anchor="agent-response-to-clarification"><name>Agent Response to Clarification</name>
<t>The agent MUST respond to a clarification with one of:</t>

<ol spacing="compact">
<li><strong>Clarification response</strong>: POST a <tt>clarification_response</tt> to the pending URL.</li>
<li><strong>Updated request</strong>: POST a new <tt>resource_token</tt> to the pending URL, replacing the original request with updated scope or parameters.</li>
<li><strong>Cancel request</strong>: DELETE the pending URL to withdraw the request.</li>
</ol>

<section anchor="clarification-response"><name>Clarification Response</name>
<t>The agent responds by POSTing JSON with <tt>clarification_response</tt> to the pending URL:</t>

<sourcecode type="http"><![CDATA[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 create a meeting invite for the participants you listed."
}
]]></sourcecode>
<t>The <tt>clarification_response</tt> value is a Markdown string. <strong>TODO:</strong> Define recommended sections. After posting, the agent resumes polling with <tt>GET</tt>.</t>
</section>

<section anchor="updated-request"><name>Updated Request</name>
<t>The agent MAY obtain a new resource token from the resource (e.g., with reduced scope) and POST it to the pending URL:</t>

<sourcecode type="http"><![CDATA[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..."

{
  "resource_token": "eyJ...",
  "justification": "I've reduced my request to read-only access."
}
]]></sourcecode>
<t>The new resource token MUST have the same <tt>iss</tt>, <tt>agent</tt>, and <tt>agent_jkt</tt> as the original. The auth server presents the updated request to the user. A <tt>justification</tt> is OPTIONAL but RECOMMENDED to explain the change to the user.</t>
</section>

<section anchor="cancel-request"><name>Cancel Request</name>
<t>The agent MAY cancel the request by sending DELETE to the pending URL:</t>

<sourcecode type="http"><![CDATA[DELETE /pending/abc123 HTTP/1.1
Host: auth.example
Signature-Input: sig=("@method" "@authority" "@path" "signature-key");created=1730217600
Signature: sig=:...signature bytes...:
Signature-Key: sig=jwt;jwt="eyJhbGc..."
]]></sourcecode>
<t>The auth server terminates the consent session and informs the user that the agent withdrew its request. Subsequent requests to the pending URL return <tt>410 Gone</tt>.</t>
</section>
</section>

<section anchor="clarification-limits"><name>Clarification Limits</name>
<t>Auth servers SHOULD enforce limits on clarification rounds (recommended: 5 rounds maximum). Clarification responses from agents are untrusted input and MUST be sanitized before display to the user.</t>
</section>
</section>

<section anchor="user-interaction"><name>User Interaction</name>
<t>When a server responds with <tt>202</tt> and <tt>AAuth-Requirement: requirement=interaction</tt>, the <tt>url</tt> and <tt>code</tt> parameters in the header tell the agent where to send the user (<xref target="I-D.hardt-aauth-headers"/>). The agent constructs the user-facing URL as <tt>{url}?code={code}</tt> and directs the user using one of the methods defined in the header specification (browser redirect, QR code, or display code).</t>
<t>When the agent has a browser, it MAY append a <tt>callback</tt> parameter:</t>

<artwork><![CDATA[{url}?code={code}&callback={callback_url}
]]></artwork>
<t>The <tt>callback</tt> URL is constructed from the agent's <tt>callback_endpoint</tt> metadata. When present, the server redirects the user's browser to the <tt>callback</tt> URL after the user completes the action. If no <tt>callback</tt> parameter is provided, the server displays a completion page and the agent relies on polling to detect completion.</t>
<t>The <tt>code</tt> parameter is single-use: once the user arrives at the URL with a valid code, the code is consumed and cannot be reused.</t>
</section>

<section anchor="third-party-initiated-login"><name>Third-Party Initiated Login</name>
<t>When a third party directs a user to the agent's <tt>login_endpoint</tt>, the agent initiates a standard "agent as audience" login flow.</t>
<t><strong>Login endpoint parameters:</strong></t>

<ul spacing="compact">
<li><tt>issuer</tt> (REQUIRED): The auth server URL.</li>
<li><tt>domain_hint</tt> (OPTIONAL): Domain hint.</li>
<li><tt>tenant</tt> (OPTIONAL): Tenant identifier.</li>
<li><tt>start_path</tt> (OPTIONAL): Path on the agent's origin where the user should be directed after login completes.</li>
</ul>
</section>

<section anchor="token-refresh"><name>Token Refresh</name>
<t>When an auth token expires, the agent requests a new one by presenting the expired auth token:</t>

<sourcecode type="http"><![CDATA[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..."
}
]]></sourcecode>
<t>The auth server verifies the agent's HTTP signature, validates the expired auth token, and issues a new auth token. The refreshed auth token MUST have the same <tt>aud</tt>, <tt>scope</tt>, and <tt>sub</tt> claims as the original.</t>
<t>The agent's signing key MAY have rotated since the original auth token was issued. The auth server identifies the agent by the agent identifier in the agent token presented via <tt>Signature-Key</tt> on the refresh request.</t>
<t>The auth server determines the maximum time after expiration that a token may be refreshed. The refresh request MAY return a <tt>202</tt> deferred response if user interaction or approval is required to reauthorize.</t>
</section>
</section>

<section anchor="metadata-documents"><name>Metadata Documents</name>
<t>Participants publish metadata at well-known URLs (<xref target="RFC8615"/>) to enable discovery.</t>

<section anchor="agent-server-metadata"><name>Agent Server Metadata</name>
<t>Published at <tt>/.well-known/aauth-agent.json</tt>:</t>

<sourcecode type="json"><![CDATA[{
  "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",
  "login_endpoint": "https://agent.example/login",
  "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"
}
]]></sourcecode>
<t>Fields:</t>

<ul spacing="compact">
<li><tt>agent</tt> (REQUIRED): The agent server's HTTPS URL (the <tt>domain</tt> in agent identifiers it issues)</li>
<li><tt>jwks_uri</tt> (REQUIRED): URL to the agent server's JSON Web Key Set</li>
<li><tt>client_name</tt> (OPTIONAL): Human-readable agent name (per <xref target="RFC7591"/>)</li>
<li><tt>logo_uri</tt> (OPTIONAL): URL to agent logo (per <xref target="RFC7591"/>)</li>
<li><tt>logo_dark_uri</tt> (OPTIONAL): URL to agent logo for dark backgrounds</li>
<li><tt>login_endpoint</tt> (OPTIONAL): URL where third parties direct users to initiate a login flow</li>
<li><tt>callback_endpoint</tt> (OPTIONAL): The agent's HTTPS callback endpoint URL</li>
<li><tt>localhost_callback_allowed</tt> (OPTIONAL): Boolean. Default: <tt>false</tt>.</li>
<li><tt>clarification_supported</tt> (OPTIONAL): Boolean. Default: <tt>false</tt>.</li>
<li><tt>tos_uri</tt> (OPTIONAL): URL to terms of service (per <xref target="RFC7591"/>)</li>
<li><tt>policy_uri</tt> (OPTIONAL): URL to privacy policy (per <xref target="RFC7591"/>)</li>
</ul>
</section>

<section anchor="auth-server-metadata"><name>Auth Server Metadata</name>
<t>Published at <tt>/.well-known/aauth-issuer.json</tt>:</t>

<sourcecode type="json"><![CDATA[{
  "issuer": "https://auth.example",
  "token_endpoint": "https://auth.example/token",
  "jwks_uri": "https://auth.example/.well-known/jwks.json"
}
]]></sourcecode>
<t>Fields:</t>

<ul spacing="compact">
<li><tt>issuer</tt> (REQUIRED): The auth server's HTTPS URL</li>
<li><tt>token_endpoint</tt> (REQUIRED): Single endpoint for all agent-to-auth-server communication</li>
<li><tt>jwks_uri</tt> (REQUIRED): URL to the auth server's JSON Web Key Set</li>
</ul>
</section>

<section anchor="resource-metadata"><name>Resource Metadata</name>
<t>Published at <tt>/.well-known/aauth-resource.json</tt>:</t>

<sourcecode type="json"><![CDATA[{
  "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",
  "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"]
}
]]></sourcecode>
<t>Fields:</t>

<ul spacing="compact">
<li><tt>resource</tt> (REQUIRED): The resource's HTTPS URL</li>
<li><tt>jwks_uri</tt> (REQUIRED): URL to the resource's JSON Web Key Set</li>
<li><tt>client_name</tt> (OPTIONAL): Human-readable resource name (per <xref target="RFC7591"/>)</li>
<li><tt>logo_uri</tt> (OPTIONAL): URL to resource logo (per <xref target="RFC7591"/>)</li>
<li><tt>logo_dark_uri</tt> (OPTIONAL): URL to resource logo for dark backgrounds</li>
<li><tt>resource_token_endpoint</tt> (OPTIONAL): URL where agents can proactively request resource tokens <xref target="resource-token-endpoint"/></li>
<li><tt>scope_descriptions</tt> (OPTIONAL): Object mapping scope values to Markdown strings for consent display</li>
<li><tt>additional_signature_components</tt> (OPTIONAL): Array of HTTP message component identifiers (<xref target="RFC9421"/>) that agents MUST include in the <tt>Signature-Input</tt> covered components when signing requests to this resource, in addition to the base components required by the AAuth HTTP Message Signatures profile (<xref target="I-D.hardt-aauth-headers"/>)</li>
</ul>
</section>
</section>

<section anchor="request-verification"><name>Request Verification</name>

<section anchor="jwt-verification"><name>JWT Verification</name>
<t>When a request includes a JWT (agent token or auth token) via <tt>scheme=jwt</tt>, the server MUST verify the JWT per <xref target="RFC7515"/>, <xref target="RFC7519"/>, and the Signature-Key specification (<xref target="I-D.hardt-httpbis-signature-key"/>):</t>

<ol spacing="compact">
<li>Decode the JWT header. Verify <tt>typ</tt> matches the expected token type (<tt>agent+jwt</tt>, <tt>auth+jwt</tt>, or <tt>resource+jwt</tt>).</li>
<li>Extract the <tt>iss</tt> and <tt>dwk</tt> claims from the JWT payload. Fetch <tt>{iss}/.well-known/{dwk}</tt>, parse as JSON, and extract the <tt>jwks_uri</tt>. Fetch the JWKS and locate the key matching the JWT header <tt>kid</tt>.</li>
<li>Verify the JWT signature using the discovered issuer key.</li>
<li>Verify <tt>exp</tt> is in the future. Verify <tt>iat</tt> is not in the future.</li>
<li><strong>Key binding</strong>: Verify that the public key in the JWT's <tt>cnf</tt> claim matches the key used to sign the HTTP request.</li>
</ol>

<section anchor="agent-token-verification"><name>Agent Token Verification</name>

<ol spacing="compact">
<li>Perform JWT Verification. Verify <tt>dwk</tt> is <tt>aauth-agent.json</tt>.</li>
<li>Verify <tt>iss</tt> is a valid HTTPS URL conforming to the Server Identifier requirements.</li>
<li>Verify <tt>cnf.jwk</tt> matches the key used to sign the HTTP request.</li>
<li>If <tt>aud</tt> is present, verify that the server's own identifier is listed.</li>
</ol>
</section>

<section anchor="auth-token-verification"><name>Auth Token Verification</name>

<ol spacing="compact">
<li>Perform JWT Verification. Verify <tt>dwk</tt> is <tt>aauth-issuer.json</tt>.</li>
<li>Verify <tt>iss</tt> is a valid HTTPS URL.</li>
<li>Verify <tt>aud</tt> matches the resource's own identifier.</li>
<li>Verify <tt>agent</tt> matches the agent identifier from the request's signing context.</li>
<li>Verify <tt>cnf.jwk</tt> matches the key used to sign the HTTP request.</li>
<li>Verify that at least one of <tt>sub</tt> or <tt>scope</tt> is present.</li>
</ol>
</section>

<section anchor="resource-token-verification"><name>Resource Token Verification</name>

<ol spacing="compact">
<li>Perform JWT Verification. Verify <tt>dwk</tt> is <tt>aauth-resource.json</tt>.</li>
<li>Verify <tt>aud</tt> matches the auth server's own identifier.</li>
<li>Verify <tt>agent</tt> matches the requesting agent's identifier.</li>
<li>Verify <tt>agent_jkt</tt> matches the JWK Thumbprint of the key used to sign the HTTP request.</li>
<li>Verify <tt>exp</tt> is in the future.</li>
<li>Verify <tt>jti</tt> has not been seen before (replay detection).</li>
</ol>
</section>

<section anchor="jwks-discovery-and-caching"><name>JWKS Discovery and Caching</name>
<t>Implementations MUST cache JWKS responses and SHOULD respect HTTP cache headers (<tt>Cache-Control</tt>, <tt>Expires</tt>) returned by the JWKS endpoint. When an implementation encounters an unknown <tt>kid</tt> in a JWT header, it SHOULD refresh the cached JWKS for that issuer to support key rotation. To prevent abuse, implementations MUST NOT fetch a given issuer's JWKS more frequently than once per minute. If a JWKS fetch fails, implementations SHOULD use the cached JWKS if available and SHOULD retry with exponential backoff. Cached JWKS entries SHOULD be discarded after a maximum of 24 hours regardless of cache headers, to ensure removed keys are no longer trusted.</t>
</section>

<section anchor="upstream-token-verification"><name>Upstream Token Verification</name>
<t>When the auth server receives an <tt>upstream_token</tt> parameter in a call chaining request:</t>

<ol spacing="compact">
<li>Perform Auth Token Verification on the upstream token.</li>
<li>Verify <tt>iss</tt> is a trusted auth server (the auth server's own identifier, or a federated auth server).</li>
<li>Verify the <tt>aud</tt> in the upstream token matches the resource that is now acting as an agent (i.e., the upstream token was issued for the intermediary resource).</li>
<li>The auth server evaluates its own policy based on the upstream token's claims. The resulting downstream authorization is not required to be a subset of the upstream scopes.</li>
</ol>
</section>
</section>
</section>

<section anchor="response-verification"><name>Response Verification</name>

<section anchor="auth-token-response-verification"><name>Auth Token Response Verification</name>
<t>When an agent receives an auth token:</t>

<ol spacing="compact">
<li>Verify the auth token JWT using the auth server's JWKS.</li>
<li>Verify <tt>iss</tt> matches the auth server the agent sent the token request to.</li>
<li>Verify <tt>aud</tt> matches the resource the agent intends to access.</li>
<li>Verify <tt>cnf.jwk</tt> matches the agent's own signing key.</li>
<li>Verify <tt>agent</tt> matches the agent's own identifier.</li>
</ol>
</section>

<section anchor="resource-challenge-verification"><name>Resource Challenge Verification</name>
<t>When an agent receives a <tt>401</tt> response with <tt>AAuth-Requirement: requirement=auth-token</tt>:</t>

<ol spacing="compact">
<li>Extract the <tt>resource-token</tt> parameter.</li>
<li>Decode and verify the resource token JWT.</li>
<li>Verify <tt>iss</tt> matches the resource the agent sent the request to.</li>
<li>Send the resource token to the agent's own auth server's token endpoint.</li>
<li>Verify <tt>agent</tt> matches the agent's own identifier.</li>
<li>Verify <tt>agent_jkt</tt> matches the JWK Thumbprint of the agent's signing key.</li>
<li>Verify <tt>exp</tt> is in the future.</li>
</ol>
</section>
</section>

<section anchor="error-responses"><name>Error Responses</name>

<section anchor="authentication-errors"><name>Authentication Errors</name>
<t>A <tt>401</tt> response from any AAuth endpoint uses the <tt>AAuth-Error</tt> header as defined in (<xref target="I-D.hardt-aauth-headers"/>).</t>
</section>

<section anchor="error-response-format"><name>Token Endpoint Error Response Format</name>
<t>Token endpoint errors use <tt>Content-Type: application/json</tt> (<xref target="RFC8259"/>) with the following members:</t>

<ul spacing="compact">
<li><tt>error</tt> (REQUIRED): String. A single error code.</li>
<li><tt>error_description</tt> (OPTIONAL): String. A human-readable description.</li>
</ul>
</section>

<section anchor="token-endpoint-error-codes"><name>Token Endpoint Error Codes</name>
<table>
<thead>
<tr>
<th>Error</th>
<th>Status</th>
<th>Meaning</th>
</tr>
</thead>

<tbody>
<tr>
<td><tt>invalid_request</tt></td>
<td>400</td>
<td>Malformed JSON, missing required fields</td>
</tr>

<tr>
<td><tt>invalid_agent_token</tt></td>
<td>400</td>
<td>Agent token malformed or signature verification failed</td>
</tr>

<tr>
<td><tt>expired_agent_token</tt></td>
<td>400</td>
<td>Agent token has expired</td>
</tr>

<tr>
<td><tt>invalid_resource_token</tt></td>
<td>400</td>
<td>Resource token malformed or signature verification failed</td>
</tr>

<tr>
<td><tt>expired_resource_token</tt></td>
<td>400</td>
<td>Resource token has expired</td>
</tr>

<tr>
<td><tt>invalid_auth_token</tt></td>
<td>400</td>
<td>Auth token for refresh is malformed, signature verification failed, or beyond refresh window</td>
</tr>

<tr>
<td><tt>server_error</tt></td>
<td>500</td>
<td>Internal error</td>
</tr>
</tbody>
</table></section>

<section anchor="polling-error-codes"><name>Polling Error Codes</name>
<table>
<thead>
<tr>
<th>Error</th>
<th>Status</th>
<th>Meaning</th>
</tr>
</thead>

<tbody>
<tr>
<td><tt>denied</tt></td>
<td>403</td>
<td>User or approver explicitly denied the request</td>
</tr>

<tr>
<td><tt>abandoned</tt></td>
<td>403</td>
<td>Interaction code was used but user did not complete</td>
</tr>

<tr>
<td><tt>expired</tt></td>
<td>408</td>
<td>Timed out</td>
</tr>

<tr>
<td><tt>invalid_code</tt></td>
<td>410</td>
<td>Interaction code not recognized or already consumed</td>
</tr>

<tr>
<td><tt>slow_down</tt></td>
<td>429</td>
<td>Polling too frequently — increase interval by 5 seconds</td>
</tr>

<tr>
<td><tt>server_error</tt></td>
<td>500</td>
<td>Internal error</td>
</tr>
</tbody>
</table></section>
</section>

<section anchor="security-considerations"><name>Security Considerations</name>

<section anchor="proof-of-possession"><name>Proof-of-Possession</name>
<t>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 <tt>cnf</tt> claim.</t>
</section>

<section anchor="token-security"><name>Token Security</name>

<ul spacing="compact">
<li>Agent tokens bind agent keys to agent identity</li>
<li>Resource tokens bind access requests to resource identity, preventing confused deputy attacks</li>
<li>Auth tokens bind authorization grants to agent keys</li>
</ul>
</section>

<section anchor="pending-url-security"><name>Pending URL Security</name>

<ul spacing="compact">
<li>Pending URLs MUST be unguessable and SHOULD have limited lifetime</li>
<li>Pending URLs MUST be on the same origin as the server that issued them</li>
<li>Servers MUST verify the agent's identity on every poll</li>
<li>Once a terminal response is returned, the pending URL MUST return <tt>404</tt></li>
</ul>
</section>

<section anchor="clarification-chat-security"><name>Clarification Chat Security</name>

<ul spacing="compact">
<li>Auth servers MUST enforce a maximum number of clarification rounds</li>
<li>Clarification responses from agents are untrusted input and MUST be sanitized before display</li>
</ul>
</section>

<section anchor="auth-server-discovery"><name>Auth Server Discovery</name>
<t>The resource's auth server is identified by the <tt>aud</tt> claim in the resource token. Federation mechanics are described in <xref target="cross-domain-trust"/>.</t>
</section>

<section anchor="call-chaining-identity"><name>Call Chaining Identity</name>
<t>When a resource acts as an agent in call chaining, it uses its own signing key and presents its own credentials. The resource MUST publish agent metadata so downstream parties can verify its identity.</t>
</section>

<section anchor="token-revocation"><name>Token Revocation</name>
<t>This specification does not define a token revocation mechanism. Auth tokens are short-lived and bound to specific signing keys.</t>
</section>

<section anchor="third-party-initiated-login-security"><name>Third-Party Initiated Login Security</name>
<t>Agents MUST treat all login endpoint parameters as untrusted input. The agent MUST verify the <tt>issuer</tt> and MUST validate that <tt>start_path</tt> is a relative path on its own origin.</t>
</section>

<section anchor="tls-requirements"><name>TLS Requirements</name>
<t>All HTTPS connections MUST use TLS 1.2 or later, following the recommendations in BCP 195 <xref target="RFC9325"/>.</t>
</section>
</section>

<section anchor="iana-considerations"><name>IANA Considerations</name>

<section anchor="well-known-uri-registrations"><name>Well-Known URI Registrations</name>
<t>This specification registers the following well-known URIs per <xref target="RFC8615"/>:</t>
<table>
<thead>
<tr>
<th>URI Suffix</th>
<th>Change Controller</th>
<th>Reference</th>
</tr>
</thead>

<tbody>
<tr>
<td><tt>aauth-agent.json</tt></td>
<td>IETF</td>
<td>This document, <xref target="agent-server-metadata"/></td>
</tr>

<tr>
<td><tt>aauth-issuer.json</tt></td>
<td>IETF</td>
<td>This document, <xref target="auth-server-metadata"/></td>
</tr>

<tr>
<td><tt>aauth-resource.json</tt></td>
<td>IETF</td>
<td>This document, <xref target="resource-metadata"/></td>
</tr>
</tbody>
</table></section>

<section anchor="media-type-registrations"><name>Media Type Registrations</name>
<t>This specification registers the following media types:</t>

<section anchor="application-agent-jwt"><name>application/agent+jwt</name>

<ul spacing="compact">
<li>Type name: application</li>
<li>Subtype name: agent+jwt</li>
<li>Required parameters: N/A</li>
<li>Optional parameters: N/A</li>
<li>Encoding considerations: binary; a JWT is a sequence of Base64url-encoded parts separated by period characters</li>
<li>Security considerations: See <xref target="security-considerations"/></li>
<li>Interoperability considerations: N/A</li>
<li>Published specification: This document, <xref target="agent-tokens"/></li>
<li>Applications that use this media type: AAuth agents and auth servers</li>
<li>Fragment identifier considerations: N/A</li>
</ul>
</section>

<section anchor="application-auth-jwt"><name>application/auth+jwt</name>

<ul spacing="compact">
<li>Type name: application</li>
<li>Subtype name: auth+jwt</li>
<li>Required parameters: N/A</li>
<li>Optional parameters: N/A</li>
<li>Encoding considerations: binary; a JWT is a sequence of Base64url-encoded parts separated by period characters</li>
<li>Security considerations: See <xref target="security-considerations"/></li>
<li>Interoperability considerations: N/A</li>
<li>Published specification: This document, <xref target="auth-tokens"/></li>
<li>Applications that use this media type: AAuth auth servers, agents, and resources</li>
<li>Fragment identifier considerations: N/A</li>
</ul>
</section>

<section anchor="application-resource-jwt"><name>application/resource+jwt</name>

<ul spacing="compact">
<li>Type name: application</li>
<li>Subtype name: resource+jwt</li>
<li>Required parameters: N/A</li>
<li>Optional parameters: N/A</li>
<li>Encoding considerations: binary; a JWT is a sequence of Base64url-encoded parts separated by period characters</li>
<li>Security considerations: See <xref target="security-considerations"/></li>
<li>Interoperability considerations: N/A</li>
<li>Published specification: This document, <xref target="resource-tokens"/></li>
<li>Applications that use this media type: AAuth resources and auth servers</li>
<li>Fragment identifier considerations: N/A</li>
</ul>
</section>
</section>

<section anchor="jwt-type-registrations"><name>JWT Type Registrations</name>
<t>This specification registers the following JWT <tt>typ</tt> header parameter values in the "JSON Web Token Types" sub-registry:</t>
<table>
<thead>
<tr>
<th>Type Value</th>
<th>Reference</th>
</tr>
</thead>

<tbody>
<tr>
<td><tt>agent+jwt</tt></td>
<td>This document, <xref target="agent-tokens"/></td>
</tr>

<tr>
<td><tt>auth+jwt</tt></td>
<td>This document, <xref target="auth-tokens"/></td>
</tr>

<tr>
<td><tt>resource+jwt</tt></td>
<td>This document, <xref target="resource-tokens"/></td>
</tr>
</tbody>
</table></section>

<section anchor="jwt-claims-registrations"><name>JWT Claims Registrations</name>
<t>This specification registers the following claims in the IANA "JSON Web Token Claims" registry established by <xref target="RFC7519"/>:</t>
<table>
<thead>
<tr>
<th>Claim Name</th>
<th>Claim Description</th>
<th>Change Controller</th>
<th>Reference</th>
</tr>
</thead>

<tbody>
<tr>
<td><tt>dwk</tt></td>
<td>Discovery Well-Known document name</td>
<td>IETF</td>
<td>This document</td>
</tr>

<tr>
<td><tt>agent</tt></td>
<td>Agent identifier</td>
<td>IETF</td>
<td>This document</td>
</tr>

<tr>
<td><tt>agent_jkt</tt></td>
<td>JWK Thumbprint of the agent's signing key</td>
<td>IETF</td>
<td>This document</td>
</tr>
</tbody>
</table></section>

<section anchor="aauth-requirement-level-registry"><name>AAuth Requirement Level Registry</name>
<t>This specification registers the following additional entry in the AAuth Requirement Level Registry established by the AAuth-Requirement specification (<xref target="I-D.hardt-aauth-headers"/>):</t>
<table>
<thead>
<tr>
<th>Value</th>
<th>Reference</th>
</tr>
</thead>

<tbody>
<tr>
<td><tt>auth-token</tt></td>
<td>This document</td>
</tr>
</tbody>
</table></section>
</section>

<section anchor="design-rationale"><name>Design Rationale</name>

<section anchor="why-standard-http-async-pattern"><name>Why Standard HTTP Async Pattern</name>
<t>AAuth uses standard HTTP async semantics (<tt>202 Accepted</tt>, <tt>Location</tt>, <tt>Prefer: wait</tt>, <tt>Retry-After</tt>). This applies uniformly to all endpoints, aligns with RFC 7240, replaces OAuth device flow, supports headless agents, and enables clarification chat.</t>
</section>

<section anchor="why-no-authorization-code"><name>Why No Authorization Code</name>
<t>AAuth eliminates authorization codes entirely. The user redirect carries only the callback URL, which has no security value to an attacker. The auth token is delivered exclusively via polling, authenticated by the agent's HTTP Message Signature.</t>
</section>

<section anchor="why-every-agent-has-a-legal-person"><name>Why Every Agent Has a Legal Person</name>
<t>AAuth requires every agent to be associated with a legal person — a user or an organization. There are no truly autonomous agents. The auth server maintains this association. This ensures there is always an accountable party for an agent's actions, which is essential for authorization decisions, audit, and liability.</t>
</section>

<section anchor="why-https-based-agent-identity"><name>Why HTTPS-Based Agent Identity</name>
<t>HTTPS URLs as agent identifiers enable dynamic ecosystems without pre-registration.</t>
</section>

<section anchor="why-no-refresh-token"><name>Why No Refresh Token</name>
<t>Every AAuth request includes an HTTP Message Signature that proves the agent holds the private key. The expired auth token provides authorization context. A separate refresh token would be redundant.</t>
</section>

<section anchor="why-json-instead-of-form-encoded"><name>Why JSON Instead of Form-Encoded</name>
<t>JSON is the standard format for modern APIs. AAuth uses JSON for both request and response bodies.</t>
</section>

<section anchor="why-callback-url-has-no-security-role"><name>Why Callback URL Has No Security Role</name>
<t>Tokens never pass through the user's browser. The callback URL is purely a UX optimization.</t>
</section>

<section anchor="why-reuse-openid-connect-vocabulary"><name>Why Reuse OpenID Connect Vocabulary</name>
<t>AAuth reuses OpenID Connect scope values, identity claims, and enterprise parameters. This lowers the adoption barrier.</t>
</section>

<section anchor="why-not-mtls"><name>Why Not mTLS?</name>
<t>Mutual TLS (mTLS) authenticates the TLS connection, not individual HTTP requests. Different paths on the same resource may have different requirements — some paths may require no signature, others pseudonymous access, others verified identity, and others an auth token. AAuth's per-request signatures allow resources to vary requirements by path. Additionally, mTLS requires PKI infrastructure (CA, certificate provisioning, revocation), cannot express progressive requirements, and is stripped by TLS-terminating proxies and CDNs. mTLS remains the right choice for infrastructure-level mutual authentication (e.g., service mesh). AAuth addresses application-level identity where progressive requirements and intermediary compatibility are needed.</t>
</section>

<section anchor="why-not-dpop"><name>Why Not DPoP?</name>
<t>DPoP (<xref target="RFC9449"/>) binds an existing OAuth access token to a key, preventing token theft. AAuth differs in that agents can establish identity from zero — no pre-existing token, no pre-registration. At the <tt>pseudonym</tt> and <tt>identity</tt> levels, AAuth requires no tokens at all, only a signed request. DPoP has a single mode (prove you hold the key bound to this token), while AAuth supports progressive requirements from pseudonymous access through verified identity to authorized access with interactive consent. DPoP is the right choice for adding proof-of-possession to existing OAuth deployments.</t>
</section>

<section anchor="why-not-extend-gnap"><name>Why Not Extend GNAP</name>
<t>GNAP (RFC 9635) shares several motivations with AAuth — proof-of-possession by default, client identity without pre-registration, and async authorization. A natural question is whether AAuth's capabilities could be achieved as GNAP extensions rather than a new protocol. There are several reasons they cannot.</t>
<t><strong>Resource tokens require an architectural change, not an extension.</strong> In GNAP, as in OAuth, the resource server is a passive consumer of tokens — it verifies them but never produces signed artifacts that the authorization server consumes. AAuth's resource tokens invert this: the resource cryptographically asserts what is being requested, binding its own identity, the agent's key thumbprint, and the requested scope into a signed JWT. Adding this to GNAP would require changing its core architectural assumption about the role of the resource server.</t>
<t><strong>Interaction chaining requires a different continuation model.</strong> GNAP's continuation mechanism operates between a single client and a single authorization server. When a resource needs to access a downstream resource that requires user consent, GNAP has no mechanism for that consent requirement to propagate back through the call chain to the original user. Supporting this would require rethinking GNAP's continuation model to support multi-party propagation through intermediaries.</t>
<t><strong>The federation model is fundamentally different.</strong> In GNAP, the client must discover and interact with each authorization server directly. AAuth's "agents always go up" principle — where the agent only ever talks to its own auth server, and auth servers federate horizontally — is a different trust topology, not a configuration option. Retrofitting this into GNAP would produce a profile so constrained that it would be a distinct protocol in practice.</t>
<t><strong>GNAP's generality is a liability for this use case.</strong> GNAP is designed to be maximally flexible — interaction modes, key proofing methods, token formats, and access structures are all pluggable. This means implementers must make dozens of profiling decisions before arriving at an interoperable system. AAuth makes these decisions prescriptively: one token format (JWT), one key proofing method (HTTP Message Signatures), one interaction pattern (interaction codes with polling), and one identity model (<tt>local@domain</tt> with HTTPS metadata). For the agent-to-resource ecosystem, this prescriptiveness is a feature — it enables interoperability without bilateral agreements.</t>
<t>In summary, AAuth's core innovations — resource-signed challenges, interaction chaining through multi-hop calls, AS-to-AS federation, and clarification chat during consent — are architectural choices that would require changing GNAP's foundations rather than extending them. The result would be a heavily constrained GNAP profile that shares little with other GNAP deployments.</t>
</section>
</section>

<section anchor="implementation-status"><name>Implementation Status</name>
<t><em>Note: This section is to be removed before publishing as an RFC.</em></t>
<t>This section records the status of known implementations of the protocol defined by this specification at the time of posting of this Internet-Draft, and is based on a proposal described in <xref target="RFC7942"/>. The description of implementations in this section is intended to assist the IETF in its decision processes in progressing drafts to RFCs.</t>
<t>The following implementations are known at the time of writing:</t>

<ul>
<li><t><strong>Hellō</strong> (<eref target="https://hello.coop):">https://hello.coop):</eref> Auth server implementation of the AAuth protocol, including token endpoint, and deferred responses.</t>
</li>
<li><t><strong>@aauth npm packages</strong> (<eref target="https://www.npmjs.com/org/aauth):">https://www.npmjs.com/org/aauth):</eref> JavaScript/TypeScript libraries for AAuth agents and resources.</t>
</li>
<li><t><strong>aauth-implementation</strong> (<eref target="https://github.com/christian-posta/aauth-implementation):">https://github.com/christian-posta/aauth-implementation):</eref> Python library implementing key pair generation (Ed25519), HTTP Message Signatures, AAuth request signing, and Signature-Key header support. Author: Christian Posta.</t>
</li>
<li><t><strong>keycloak-aauth-extension</strong> (<eref target="https://github.com/christian-posta/keycloak-aauth-extension):">https://github.com/christian-posta/keycloak-aauth-extension):</eref> Java Keycloak SPI extension implementing the auth server role — HTTP Message Signature verification, AAuth token issuance with agent identity binding, consent flows, token refresh, token exchange for delegation chains, and <tt>/.well-known/aauth-issuer</tt> metadata. Author: Christian Posta.</t>
</li>
<li><t><strong>aauth-full-demo</strong> (<eref target="https://github.com/christian-posta/aauth-full-demo):">https://github.com/christian-posta/aauth-full-demo):</eref> End-to-end Python/JavaScript demo with multiple agents communicating via A2A protocol with AAuth authentication, including autonomous authorization, user-delegated consent via Keycloak, multi-hop token exchange, and JWKS discovery. Author: Christian Posta.</t>
</li>
</ul>
</section>

<section anchor="document-history"><name>Document History</name>
<t><em>Note: This section is to be removed before publishing as an RFC.</em></t>

<ul spacing="compact">
<li><t>draft-hardt-aauth-protocol-00</t>

<ul spacing="compact">
<li>Initial submission</li>
</ul></li>
</ul>
</section>

<section anchor="acknowledgments"><name>Acknowledgments</name>
<t>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.</t>
</section>

</middle>

<back>
<references><name>References</name>
<references><name>Normative References</name>
<reference anchor="I-D.hardt-aauth-headers" target="https://github.com/dickhardt/AAuth">
  <front>
    <title>HTTP AAuth Headers</title>
    <author fullname="Dick Hardt" initials="D." surname="Hardt">
      <organization>Hellō</organization>
    </author>
    <date year="2026"/>
  </front>
</reference>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml3/reference.I-D.hardt-httpbis-signature-key.xml"/>
<reference anchor="OpenID.Core" target="https://openid.net/specs/openid-connect-core-1_0.html">
  <front>
    <title>OpenID Connect Core 1.0</title>
    <author fullname="Nat Sakimura" initials="N." surname="Sakimura">
      <organization>NRI</organization>
    </author>
    <author fullname="John Bradley" initials="J." surname="Bradley">
      <organization>Ping Identity</organization>
    </author>
    <author fullname="Michael B. Jones" initials="M." surname="Jones">
      <organization>Microsoft</organization>
    </author>
    <author fullname="Breno de Medeiros" initials="B." surname="de Medeiros">
      <organization>Google</organization>
    </author>
    <author fullname="Chuck Mortimore" initials="C." surname="Mortimore">
      <organization>Salesforce</organization>
    </author>
    <date year="2014" month="November"/>
  </front>
</reference>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.5890.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.6749.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.7240.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.7515.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.7519.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.7638.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.7800.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8259.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8615.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9068.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9110.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9325.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9421.xml"/>
</references>
<references><name>Informative References</name>
<reference anchor="OpenID.Enterprise" target="https://openid.net/specs/openid-connect-enterprise-extensions-1_0.html">
  <front>
    <title>OpenID Connect Enterprise Extensions 1.0</title>
    <author fullname="Dick Hardt" initials="D." surname="Hardt">
      <organization>Hellō</organization>
    </author>
    <author fullname="Karl McGuinness" initials="K." surname="McGuinness">
      <organization>Okta</organization>
    </author>
    <date year="2025"/>
  </front>
</reference>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.7591.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.7942.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8628.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9449.xml"/>
</references>
</references>

<section anchor="agent-token-acquisition"><name>Agent Token Acquisition Patterns</name>
<t>This appendix describes common patterns for how agents obtain agent tokens from their agent server. In all patterns, the agent generates an ephemeral signing key pair, proves its identity to the agent server, and receives an agent token binding the ephemeral key to an agent identifier. Each pattern differs in how the agent proves its identity and what trust assumption the agent server relies on.</t>

<section anchor="server-workloads"><name>Server Workloads</name>

<ol spacing="compact">
<li>The agent generates an ephemeral signing key pair (e.g., Ed25519).</li>
<li>The agent obtains a platform attestation from its runtime environment — such as a SPIFFE SVID from a SPIRE agent, a WIMSE workload identity token, or a cloud provider instance identity document (AWS IMDSv2, GCP metadata, Azure IMDS).</li>
<li>The agent presents the attestation and its ephemeral public key to the agent server.</li>
<li>The agent server verifies the attestation against the platform's trust root and issues an agent token with the ephemeral key in the <tt>cnf</tt> claim.</li>
</ol>
<t>On managed infrastructure, the platform may additionally attest the software identity (container image hash, binary signature) alongside the workload identity, allowing the agent server to restrict tokens to known software.</t>
<t><strong>Trust assumption:</strong> The agent server trusts the platform's attestation that the workload is what it claims to be.</t>
</section>

<section anchor="mobile-applications"><name>Mobile Applications</name>

<ol spacing="compact">
<li>The app generates an ephemeral signing key pair, optionally backed by the device's secure enclave (iOS Secure Enclave, Android StrongBox).</li>
<li>The app obtains a platform attestation — iOS App Attest assertion or Android Play Integrity verdict — binding the app identity and the ephemeral public key.</li>
<li>The app sends the attestation and public key to the agent server.</li>
<li>The agent server verifies the attestation against Apple's or Google's attestation service and issues an agent token.</li>
</ol>
<t>The platform attestation proves the app is a genuine installation from the app store, running on a real device, and has not been tampered with. If the key is hardware-backed, the attestation also proves the key cannot be exported.</t>
<t><strong>Trust assumption:</strong> The agent server trusts the platform's attestation that the app is a genuine, untampered installation running on a real device.</t>
</section>

<section anchor="desktop-and-cli-applications"><name>Desktop and CLI Applications</name>
<t>Desktop platforms generally do not provide application-level attestation comparable to mobile platforms. Several patterns are available depending on the deployment context:</t>

<section anchor="user-login"><name>User Login</name>

<ol spacing="compact">
<li>The agent opens a browser and redirects the user to the agent server's web interface.</li>
<li>The user authenticates at the agent server.</li>
<li>The agent generates an ephemeral signing key pair, stores the private key in a platform vault (macOS Keychain, Windows TPM, Linux Secret Service), and sends the public key to the agent server.</li>
<li>The agent server issues an agent token binding the ephemeral key to an agent identifier and returns it to the agent (e.g., via localhost callback).</li>
</ol>
<t>This is the most common pattern for user-facing desktop and CLI tools.</t>
<t>The agent may also hold a stable key in hardware (TPM, secure enclave) or a platform keychain. During the initial user login flow, the agent server records the stable public key alongside the agent identity. When the agent token expires, the agent can renew it by sending its new ephemeral public key in a <tt>scheme=jkt-jwt</tt> request signed by the stable key, without requiring the user to log in again.</t>
<t><strong>Trust assumption:</strong> The agent server trusts the user's authentication but cannot verify which software is running — only that the user authorized the agent. For renewal via stable key, the agent server trusts that the key registered at enrollment continues to represent the same agent.</t>
</section>

<section anchor="managed-desktops"><name>Managed Desktops</name>
<t>On managed desktops (e.g., corporate MDM-enrolled devices), the management platform may provide device and software attestation similar to server workloads. The agent presents the platform attestation alongside its ephemeral key, and the agent server verifies the device is managed and the software is approved.</t>
<t><strong>Trust assumption:</strong> The agent server trusts the management platform's attestation that the device is managed and the software is approved.</t>
</section>

<section anchor="self-hosted-agent-metadata"><name>Self-Hosted Agent Metadata</name>
<t>A user publishes agent metadata and a JWKS at a domain they control (e.g., <tt>username.github.io/.well-known/aauth-agent.json</tt>) — no active server is required, only static files. The agent's public key is included in the published JWKS. The corresponding private key is held on the user's machine — potentially in a secure enclave or hardware token. Agents generate ephemeral signing keys and use <tt>scheme=jwt</tt> to obtain agent tokens signed by the private key. Auth servers verify agent tokens against the published JWKS.</t>
<t><strong>Trust assumption:</strong> The trust anchor is the published JWKS and the private key held by the user. No server-side logic is involved — verification relies entirely on the static metadata and key material.</t>
</section>
</section>

<section anchor="browser-based-applications"><name>Browser-Based Applications</name>

<ol spacing="compact">
<li>The web server — which acts as the agent server — authenticates the user. The recommended mechanism is WebAuthn, which binds authentication to the device and origin, preventing scripts or headless browsers from impersonating the web page to obtain an agent token.</li>
<li>The web app generates an ephemeral signing key pair using the Web Crypto API (non-extractable if supported) and sends it to the web server.</li>
<li>The web server issues an agent token binding the web app's ephemeral public key to an agent identifier and returns it.</li>
</ol>
<t>The key pair and agent token exist only for the lifetime of the browser session. The web server controls both the agent identity and the issuance.</t>
<t><strong>Trust assumption:</strong> The web server is the agent server and controls the entire lifecycle. The agent token lifetime is tied to the browser session. When WebAuthn is used, authentication is bound to the device and origin rather than relying solely on session credentials.</t>
</section>
</section>

<section anchor="detailed-flows"><name>Detailed Flows</name>
<t>This appendix provides complete end-to-end flows combining the key interactions defined in the Protocol Overview.</t>

<section anchor="autonomous-agent"><name>Autonomous Agent</name>
<t>A machine-to-machine agent obtains authorization directly without user interaction.</t>

<section anchor="resource-challenge"><name>Resource Challenge</name>
<t>The resource challenges the agent with a <tt>401</tt> response containing a resource token:</t>

<sourcecode type="ascii-art"><![CDATA[Agent                       Resource                    Auth Server
  |                            |                            |
  |  HTTPSig request           |                            |
  |--------------------------->|                            |
  |                            |                            |
  |  401 + resource_token      |                            |
  |<---------------------------|                            |
  |                            |                            |
  |  POST token_endpoint with resource_token                |
  |-------------------------------------------------------->|
  |                            |                            |
  |                            |  validate resource_token   |
  |                            |  evaluate policy           |
  |                            |                            |
  |  auth_token                                             |
  |<--------------------------------------------------------|
  |                            |                            |
  |  HTTPSig request           |                            |
  |  (with auth-token)         |                            |
  |--------------------------->|                            |
  |                            |                            |
  |                            |  verify auth_token         |
  |                            |                            |
  |  200 OK                    |                            |
  |<---------------------------|                            |
  |                            |                            |
]]></sourcecode>
</section>

<section anchor="proactive-token-request"><name>Proactive Token Request</name>
<t>When the agent knows the resource's requirements from metadata, it can request a resource token proactively via the <tt>resource_token_endpoint</tt>:</t>

<sourcecode type="ascii-art"><![CDATA[Agent                       Resource                    Auth Server
  |                            |                            |
  |  POST                      |                            |
  |  resource_token_endpoint   |                            |
  |--------------------------->|                            |
  |                            |                            |
  |  resource_token            |                            |
  |<---------------------------|                            |
  |                            |                            |
  |  POST token_endpoint with resource_token                |
  |-------------------------------------------------------->|
  |                            |                            |
  |                            |  validate resource_token   |
  |                            |  evaluate policy           |
  |                            |                            |
  |  auth_token                                             |
  |<--------------------------------------------------------|
  |                            |                            |
  |  HTTPSig request           |                            |
  |  (with auth-token)         |                            |
  |--------------------------->|                            |
  |                            |                            |
  |                            |  verify auth_token         |
  |                            |                            |
  |  200 OK                    |                            |
  |<---------------------------|                            |
  |                            |                            |
]]></sourcecode>
<t><strong>Use cases:</strong> Machine-to-machine API calls, automated pipelines, cron jobs, service-to-service communication where no user is involved.</t>
</section>
</section>

<section anchor="agent-as-audience"><name>Agent as Audience</name>
<t>An agent requests an auth token where it is the audience — either for SSO (obtaining user identity) or for first-party resource access. The agent calls the token endpoint with <tt>scope</tt> (and no <tt>resource_token</tt>), since the agent itself is the resource.</t>

<sourcecode type="ascii-art"><![CDATA[User             Agent                              Auth Server
  |                |                                    |
  |                |  POST token_endpoint               |
  |                |  scope (no resource_token)         |
  |                |  Prefer: wait=45                   |
  |                |----------------------------------->|
  |                |                                    |
  |                |  202 Accepted                      |
  |                |  Location: /pending/def            |
  |                |  AAuth-Requirement:                  |
  |                |    requirement=interaction;            |
  |                |    code="EFGH5678"                 |
  |                |<-----------------------------------|
  |                |                                    |
  |  direct to     |                                    |
  |  {url}?code={code}                                  |
  |<---------------|                                    |
  |                |                                    |
  |  authenticate and consent                           |
  |---------------------------------------------------->|
  |                |                                    |
  |  redirect to callback_url                           |
  |<----------------------------------------------------|
  |                |                                    |
  |                |  GET /pending/def                  |
  |                |----------------------------------->|
  |                |  200 OK, auth_token                |
  |                |<-----------------------------------|
  |                |                                    |
]]></sourcecode>
<t><strong>Use cases:</strong> Single sign-on, user login, enabling agent to access protected resources at the agent on behalf of the user.</t>
</section>

<section anchor="third-party-initiated-login-1"><name>Third-Party Initiated Login</name>
<t>A third party — such as an auth server, organization portal, app marketplace, or partner site — directs the user to the agent's <tt>login_endpoint</tt> with enough context to start a login flow. The agent then initiates a standard "agent as audience" flow.</t>

<sourcecode type="ascii-art"><![CDATA[User          Third Party       Agent                  Auth Server
  |               |               |                        |
  |  select agent |               |                        |
  |-------------->|               |                        |
  |               |               |                        |
  |  redirect to login_endpoint   |                        |
  |  (issuer, tenant, start_path) |                        |
  |<--------------|               |                        |
  |               |               |                        |
  |  login_endpoint               |                        |
  |------------------------------>|                        |
  |               |               |                        |
  |               |               |  POST token_endpoint   |
  |               |               |  scope, tenant         |
  |               |               |  Prefer: wait=45       |
  |               |               |----------------------->|
  |               |               |                        |
  |               |               |  202 Accepted          |
  |               |               |  Location: /pending/ghi|
  |               |               |  AAuth-Requirement:      |
  |               |               |    requirement=interaction;|
  |               |               |    code="JKLM9012"     |
  |               |               |<-----------------------|
  |               |               |                        |
  |  direct to {url}?code={code}                           |
  |<------------------------------|                        |
  |               |               |                        |
  |  auth server recognizes user                           |
  |  (existing session), auto-approves                     |
  |------------------------------------------------------->|
  |               |               |                        |
  |  redirect to callback_url                              |
  |<-------------------------------------------------------|
  |               |               |                        |
  |  callback     |               |                        |
  |------------------------------>|                        |
  |               |               |                        |
  |               |               |  GET /pending/ghi      |
  |               |               |----------------------->|
  |               |               |  200 OK, auth_token    |
  |               |               |<-----------------------|
  |               |               |                        |
  |  redirect to start_path       |                        |
  |<------------------------------|                        |
  |               |               |                        |
]]></sourcecode>
<t><strong>Use cases:</strong> Organization portal SSO, app marketplace "connect" buttons, partner site deep links, auth server dashboard launching an agent.</t>
</section>

<section anchor="user-authorization"><name>User Authorization</name>
<t>Full flow with user-authorized access. The agent obtains a resource token from the resource's <tt>resource_token_endpoint</tt>, then requests authorization from the auth server.</t>

<sourcecode type="ascii-art"><![CDATA[User           Agent                Resource             Auth Server
  |              |                     |                      |
  |              |  POST               |                      |
  |              |  resource_token_endpoint                   |
  |              |-------------------->|                      |
  |              |                     |                      |
  |              |  resource_token     |                      |
  |              |<--------------------|                      |
  |              |                     |                      |
  |              |  POST token_endpoint                       |
  |              |  resource_token, scope                     |
  |              |  Prefer: wait=45                           |
  |              |------------------------------------------->|
  |              |                     |                      |
  |              |  202 Accepted                              |
  |              |  Location: /pending/abc                    |
  |              |  AAuth-Requirement:                          |
  |              |    requirement=interaction;                    |
  |              |    code="ABCD1234"                         |
  |              |<-------------------------------------------|
  |              |                     |                      |
  |  direct to   |                     |                      |
  |  {url}?code={code}                |                      |
  |<-------------|                     |                      |
  |              |                     |                      |
  |  authenticate and consent                                 |
  |---------------------------------------------------------->|
  |              |                     |                      |
  |  redirect to callback_url                                 |
  |<----------------------------------------------------------|
  |              |                     |                      |
  |  callback    |                     |                      |
  |------------->|                     |                      |
  |              |                     |                      |
  |              |  GET /pending/abc   |                      |
  |              |------------------------------------------->|
  |              |  200 OK, auth_token |                      |
  |              |<-------------------------------------------|
  |              |                     |                      |
  |              |  HTTPSig request    |                      |
  |              |  (with auth-token)  |                      |
  |              |-------------------->|                      |
  |              |                     |                      |
  |              |  200 OK             |                      |
  |              |<--------------------|                      |
  |              |                     |                      |
]]></sourcecode>
</section>

<section anchor="direct-approval"><name>Direct Approval</name>
<t>The auth server obtains approval directly — from a user (e.g., push notification, existing session, email) — without the agent facilitating a redirect.</t>

<sourcecode type="ascii-art"><![CDATA[Agent               Resource          Auth Server            User
  |                    |                   |                    |
  |  POST              |                   |                    |
  |  resource_token_endpoint               |                    |
  |------------------->|                   |                    |
  |                    |                   |                    |
  |  resource_token    |                   |                    |
  |<-------------------|                   |                    |
  |                    |                   |                    |
  |  POST token_endpoint                   |                    |
  |  resource_token, scope                 |                    |
  |  Prefer: wait=45                       |                    |
  |--------------------------------------->|                    |
  |                    |                   |                    |
  |  202 Accepted                          |                    |
  |  Location: /pending/jkl                |                    |
  |  AAuth-Requirement: requirement=approval     |                    |
  |<---------------------------------------|                    |
  |                    |                   |                    |
  |                    |                   |  push / email /    |
  |                    |                   |  existing session  |
  |                    |                   |------------------->|
  |                    |                   |                    |
  |                    |                   |  approve           |
  |                    |                   |<-------------------|
  |                    |                   |                    |
  |  GET /pending/jkl  |                   |                    |
  |  Prefer: wait=45   |                   |                    |
  |--------------------------------------->|                    |
  |                    |                   |                    |
  |  200 OK, auth_token                    |                    |
  |<---------------------------------------|                    |
  |                    |                   |                    |
  |  HTTPSig request   |                   |                    |
  |  (with auth-token) |                   |                    |
  |------------------->|                   |                    |
  |                    |                   |                    |
  |  200 OK            |                   |                    |
  |<-------------------|                   |                    |
  |                    |                   |                    |
]]></sourcecode>
</section>

<section anchor="call-chaining"><name>Call Chaining</name>
<t>When a resource needs to access a downstream resource on behalf of the caller, it acts as an agent. Like any agent, it sends the downstream resource token to its own auth server along with the auth token it received from the original caller as the <tt>upstream_token</tt>.</t>
<t>The auth server evaluates its own policy based on both the upstream auth token and the downstream resource token. The resulting authorization is not necessarily a subset of the upstream scopes.</t>
<t>Because the resource acts as an agent, it MUST publish agent metadata at <tt>/.well-known/aauth-agent.json</tt> so that downstream resources and auth servers can verify its identity.</t>

<section anchor="same-auth-server"><name>Same Auth Server</name>
<t>When both resources share the same auth server:</t>

<sourcecode type="ascii-art"><![CDATA[Agent          Resource 1        Resource 2           AS
  |                |                 |                  |
  |  HTTPSig req   |                 |                  |
  |  (auth_token)  |                 |                  |
  |--------------->|                 |                  |
  |                |                 |                  |
  |                |  verify         |                  |
  |                |  auth_token     |                  |
  |                |                 |                  |
  |                |  HTTPSig req    |                  |
  |                |  (as agent)     |                  |
  |                |---------------->|                  |
  |                |                 |                  |
  |                |  401 + resource_token              |
  |                |<----------------|                  |
  |                |                 |                  |
  |                |  POST token_endpoint               |
  |                |  resource_token from R2            |
  |                |  upstream_token                    |
  |                |----------------------------------->|
  |                |                 |                  |
  |                |                 |  verify          |
  |                |                 |  upstream_token  |
  |                |                 |  evaluate policy |
  |                |                 |                  |
  |                |  auth_token for R2                 |
  |                |<-----------------------------------|
  |                |                 |                  |
  |                |  HTTPSig req    |                  |
  |                |  (auth_token)   |                  |
  |                |---------------->|                  |
  |                |                 |                  |
  |                |  200 OK         |                  |
  |                |<----------------|                  |
  |                |                 |                  |
  |  200 OK        |                 |                  |
  |<---------------|                 |                  |
  |                |                 |                  |
]]></sourcecode>
</section>

<section anchor="interaction-chaining"><name>Interaction Chaining</name>
<t>When the auth server requires user interaction for the downstream access, it returns a <tt>202</tt> with <tt>requirement=interaction</tt>. Resource 1 chains the interaction back to the original agent by returning its own <tt>202</tt>.</t>

<sourcecode type="ascii-art"><![CDATA[User         Agent          Resource 1        Resource 2       AS
  |            |                 |                 |             |
  |            |  HTTPSig req    |                 |             |
  |            |---------------->|                 |             |
  |            |                 |                 |             |
  |            |                 |  HTTPSig req    |             |
  |            |                 |  (as agent)     |             |
  |            |                 |---------------->|             |
  |            |                 |                 |             |
  |            |                 |  401 + resource_token         |
  |            |                 |<----------------|             |
  |            |                 |                 |             |
  |            |                 |  POST token_endpoint          |
  |            |                 |  resource_token,              |
  |            |                 |  upstream_token               |
  |            |                 |------------------------------>|
  |            |                 |                 |             |
  |            |                 |  202 Accepted                 |
  |            |                 |  requirement=interaction;         |
  |            |                 |  code=WXYZ                    |
  |            |                 |<------------------------------|
  |            |                 |                 |             |
  |            |  202 Accepted   |                 |             |
  |            |  Location: /pending/xyz           |             |
  |            |  AAuth-Requirement:                 |             |
  |            |    requirement=interaction;           |             |
  |            |    code="MNOP"  |                 |             |
  |            |<----------------|                 |             |
  |            |                 |                 |             |
  |  direct to R1                |                 |             |
  |  {url}?code={code}          |                 |             |
  |<-----------|                 |                 |             |
  |            |                 |                 |             |
  |  {url}?code={code}          |                 |             |
  |----------------------------->|                 |             |
  |            |                 |                 |             |
  |  redirect to AS {url}       |                 |             |
  |<-----------------------------|                 |             |
  |            |                 |                 |             |
  |  authenticate and consent    |                 |             |
  |------------------------------------------------------------->|
  |            |                 |                 |             |
  |  redirect to R1 callback     |                 |             |
  |<-------------------------------------------------------------|
  |            |                 |                 |             |
  |            |            [R1 polls AS pending URL,            |
  |            |             receives auth_token for R2]         |
  |            |                 |                 |             |
  |            |                 |  HTTPSig req    |             |
  |            |                 |  (auth_token)   |             |
  |            |                 |---------------->|             |
  |            |                 |                 |             |
  |            |                 |  200 OK         |             |
  |            |                 |<----------------|             |
  |            |                 |                 |             |
  |  redirect to agent callback_url                |             |
  |<-----------------------------|                 |             |
  |            |                 |                 |             |
  |  callback  |                 |                 |             |
  |----------->|                 |                 |             |
  |            |                 |                 |             |
  |            |  GET /pending/xyz                 |             |
  |            |---------------->|                 |             |
  |            |                 |                 |             |
  |            |  200 OK         |                 |             |
  |            |<----------------|                 |             |
  |            |                 |                 |             |
]]></sourcecode>
<t>When a resource acting as an agent receives a <tt>202 Accepted</tt> response with <tt>AAuth-Requirement: requirement=interaction</tt> from its auth server, and the resource needs to propagate this interaction requirement to its caller, it MUST return a <tt>202 Accepted</tt> response to the original agent with its own <tt>AAuth-Requirement</tt> header containing <tt>requirement=interaction</tt> and its own interaction code. The resource MUST provide its own <tt>Location</tt> URL for the original agent to poll. When the user completes interaction and the resource obtains the downstream auth token, the resource completes the original request and returns the result at its pending URL.</t>
<t>When call chaining crosses auth server domains, the agent's auth server (or the intermediary resource's auth server) federates with the downstream resource's auth server. See <xref target="cross-domain-trust"/>.</t>
</section>
</section>

<section anchor="cross-domain-trust"><name>Cross-Domain Trust</name>
<t>When an agent's auth server (AS1) receives a resource token whose <tt>aud</tt> identifies a different auth server (AS2), AS1 federates with AS2 to obtain the auth token. This enables agents and resources that operate within different trust domains to work together — the federation effort is at the auth server level, not at every agent and resource.</t>
<t>Cross-domain federation is required when the agent's AS and the resource's AS differ. Within a single AS domain, no federation is needed.</t>

<section anchor="as-to-as-federation"><name>AS-to-AS Federation</name>
<t>AS1 calls AS2's token endpoint with the resource token and the agent's agent token. AS2 uses the agent token to verify agent identity, confirm the agent's key matches the <tt>agent_jkt</tt> in the resource token, and include agent claims in the auth token it issues.</t>
<t>AS2 returns the auth token to AS1. AS1 passes the auth token through to the agent unchanged — AS1 MUST NOT re-sign or modify the auth token. The auth token's <tt>iss</tt> is AS2 and <tt>aud</tt> is the resource. The agent forwards it to the resource, which verifies the auth token against AS2's JWKS (its own auth server). The agent's response verification <xref target="auth-token-response-verification"/> is limited to checking that <tt>aud</tt>, <tt>cnf</tt>, and <tt>agent</tt> match its own values — the agent does not need to verify the auth token's signature.</t>
<t>AS2 MAY also return an <tt>assessment</tt>, a Markdown string describing AS2's evaluation of the request for AS1 to consider in its authorization decision. <strong>TODO:</strong> Define recommended sections. Any step may return a <tt>202</tt> deferred response — the standard AAuth deferred response protocol applies to AS-to-AS calls.</t>

<sourcecode type="ascii-art"><![CDATA[Agent              AS1              AS2            Resource
  |                 |                 |                |
  |  POST /token    |                 |                |
  |  resource_token |                 |                |
  |  (aud=AS2)      |                 |                |
  |---------------->|                 |                |
  |                 |                 |                |
  |                 |  aud≠self,      |                |
  |                 |  federate       |                |
  |                 |                 |                |
  |                 |  POST /token    |                |
  |                 |  resource_token |                |
  |                 |  agent_token    |                |
  |                 |---------------->|                |
  |                 |                 |                |
  |                 |                 |  verify resource_token,
  |                 |                 |  verify agent_token,
  |                 |                 |  evaluate policy
  |                 |                 |                |
  |                 |  auth_token     |                |
  |                 |<----------------|                |
  |                 |                 |                |
  |  auth_token     |                 |                |
  |<----------------|                 |                |
  |                 |                 |                |
  |  HTTPSig req    |                 |                |
  |  (auth_token)   |                 |                |
  |--------------------------------------------------->|
  |                 |                 |                |
  |  200 OK         |                 |                |
  |<---------------------------------------------------|
  |                 |                 |                |
]]></sourcecode>
</section>

<section anchor="organization-visibility"><name>Organization Visibility</name>
<t>Organizations benefit from the trust model <xref target="trust-model"/>: internal agents and resources share a single AS with zero federation overhead. Federation is only incurred at the boundary, when an internal agent accesses an external resource or vice versa.</t>
</section>

<section anchor="token-endpoint-parameters-for-federation"><name>Token Endpoint Parameters for Federation</name>
<t>When an auth server calls another auth server's token endpoint for federation, the following additional parameter is used:</t>

<ul spacing="compact">
<li><tt>agent_token</tt> (OPTIONAL): The agent's agent token. Sent by AS1 to AS2 so that AS2 can verify the agent's identity and signing key, and include agent claims in the issued auth token. AS2 verifies that the <tt>cnf</tt> claim in the agent token matches the <tt>agent_jkt</tt> in the resource token.</li>
</ul>
<t>The <tt>upstream_token</tt> parameter (used in call chaining) may also be present in federated calls to provide authorization chain provenance.</t>
</section>

<section anchor="relationship-to-aauth-mission-protocol"><name>Relationship to AAuth Mission Protocol</name>
<t>AAuth Mission extends cross-domain federation with mission-scoped authorization, centralized audit, and MA countersignatures. When a mission is active, the agent's auth server acts as the Mission Authority and adds mission context to federation calls. See the AAuth Mission specification for details.</t>
</section>
</section>
</section>

</back>

</rfc>
