<?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-oauth-aauth-protocol-11" submissionType="IETF" category="std" xml:lang="en" indexInclude="true">

<front>
<title abbrev="AAuth-Protocol">AAuth Protocol</title><seriesInfo value="draft-hardt-oauth-aauth-protocol-11" 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 for agent-to-resource authorization and identity claim retrieval. The protocol supports five resource access modes — agent identity, resource-managed (two-party), person identity, PS authorization (three-party), and federated authorization (four-party) — with agent governance as an orthogonal layer. It builds on the HTTP Signature Keys specification (<xref target="I-D.hardt-httpbis-signature-key"/>) for HTTP Message Signatures and key discovery.</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.
Related documents and open issues can be found at <eref target="https://github.com/dickhardt/AAuth">https://github.com/dickhardt/AAuth</eref>.
Raw markdown source is at <eref target="https://raw.githubusercontent.com/dickhardt/AAuth/refs/heads/main/draft-hardt-oauth-aauth-protocol.md">https://raw.githubusercontent.com/dickhardt/AAuth/refs/heads/main/draft-hardt-oauth-aauth-protocol.md</eref></t>
</note>

</front>

<middle>

<section anchor="introduction"><name>Introduction</name>

<section anchor="http-clients-need-their-own-identity"><name>HTTP Clients Need Their Own Identity</name>
<t>In OAuth 2.0 <xref target="RFC6749"/> and OpenID Connect <xref target="OpenID.Core"/>, the client has no independent identity. Client identifiers are issued by each authorization server or OpenID provider — a <tt>client_id</tt> at Google is meaningless at GitHub. The client's identity exists only in the context of each server it has pre-registered with. This made sense when the web had a manageable number of integrations and a human developer could visit each portal to register.</t>
<t>API keys are the same model pushed further: a shared secret issued by a service, copied to the client, and used as a bearer credential. The problem is that any secret that must be copied to where the workload runs will eventually be copied somewhere it shouldn't be.</t>
<t>SPIFFE and WIMSE brought workload identity to enterprise infrastructure — a workload can prove who it is without shared secrets. But these operate within a single enterprise's trust domain. They don't help an agent that needs to access resources across organizational boundaries, or a developer's tool that runs outside any enterprise platform.</t>
<t>AAuth starts from this premise: every agent has its own cryptographic identity. An agent identifier (<tt>aauth:local@domain</tt>) is bound to a signing key, published at a well-known URL, and verifiable by any party — no pre-registration, no shared secrets, no dependency on a particular server. At its simplest, an agent signs a request and a resource decides what to do based on who the agent is. This identity-based access replaces API keys and is the foundation that authorization, governance, and federation build on incrementally.</t>
</section>

<section anchor="agents-are-different"><name>Agents Are Different</name>
<t>Traditional software knows at build time what services it will call and what permissions it needs. Registration, key provisioning, and scope configuration happen before the first request. This works when the set of integrations is fixed and known in advance.</t>
<t>Agents don't work this way. They discover resources at runtime. They execute long-running tasks that span multiple services across trust domains. They need to explain what they're doing and why. They need authorization decisions mid-task, long after the user set them in motion. A protocol designed for pre-registered clients with fixed integrations cannot serve agents that discover their needs as they go.</t>
</section>

<section anchor="what-aauth-provides"><name>What AAuth Provides</name>
<t>Every agent instance has its own identifier and signing key, and every request it makes is bound to that key by an HTTP Message Signature (<xref target="RFC9421"/>): no credential is a bearer credential, and none needs pre-registration, so the first API call to a resource is the registration. A person server represents the person — asserting who the agent acts for, managing consent and missions, relaying interactions and payments, and recording what the agent did — and federates with the access servers that guard resources across trust domains. Each party adopts independently. Asynchronous event delivery to agents is defined in AAuth Events (<xref target="I-D.hardt-aauth-events"/>).</t>
<t>The HTTP Signature Keys specification (<xref target="I-D.hardt-httpbis-signature-key"/>) defines how signing keys are bound to JWTs and discovered via well-known metadata, and how agents present cryptographic identity using HTTP Message Signatures (<xref target="RFC9421"/>). This specification defines the <tt>AAuth-Requirement</tt>, <tt>AAuth-Access</tt>, and <tt>AAuth-Capabilities</tt> headers, and the authorization protocol across five resource access modes.</t>
<t>Because agent identity is independent and self-contained, AAuth is designed for incremental adoption: each party can add support independently, and rollout does not need to be coordinated. A resource that verifies an agent's signature can manage access by identity alone, with no other infrastructure; adding a person server and an access server is additive. The five resource access modes are introduced in <xref target="protocol-overview"/> and the adoption path in <xref target="incremental-adoption"/>.</t>
</section>

<section anchor="built-on"><name>Built On</name>
<t>AAuth builds on existing standards and design patterns:</t>

<ul spacing="compact">
<li><strong>OpenID Connect vocabulary</strong>: AAuth reuses OpenID Connect scope values, identity claims, and enterprise extensions (<xref target="OpenID.Enterprise"/>), lowering the adoption barrier for identity-aware resources.</li>
<li><strong>Well-known metadata and key discovery</strong>: Servers publish metadata at well-known URLs (<xref target="RFC8615"/>) and signing keys via JWKS endpoints, following the pattern established by OAuth Authorization Server Metadata (<xref target="RFC8414"/>) and OpenID Connect Discovery (<xref target="OpenID.Core"/>).</li>
<li><strong>HTTP Message Signatures</strong>: All requests are signed with HTTP Message Signatures (<xref target="RFC9421"/>) using keys bound to tokens conveyed via the Signature-Key header (<xref target="I-D.hardt-httpbis-signature-key"/>), providing proof-of-possession, identity, and message integrity on every call.</li>
</ul>
</section>
</section>

<section anchor="conventions-and-definitions"><name>Conventions and Definitions</name>
<t>{::boilerplate bcp14-tagged}</t>
<t>In HTTP examples throughout this document, line breaks and indentation are added for readability. Actual HTTP messages do not contain these extra line breaks. Examples of signed requests show the <tt>Signature-Key</tt> header and omit the <tt>Signature-Input</tt>, <tt>Signature</tt>, and <tt>Content-Digest</tt> headers that every signed request carries; the fully bound form is shown in <xref target="aauth-access"/> and <xref target="covered-components"/>.</t>
</section>

<section anchor="terminology"><name>Terminology</name>
<t>Parties:</t>

<ul spacing="compact">
<li><strong>Person</strong>: A user or organization — the legal person — on whose behalf an agent acts and who is accountable for the agent's actions.</li>
<li><strong>Agent</strong>: An HTTP client (<xref target="RFC9110"/>, Section 3.5) acting on behalf of a person. Identified by an agent identifier URI using the <tt>aauth</tt> scheme, of the form <tt>aauth:local@domain</tt> <xref target="agent-identifiers"/>. An agent MAY have a person server, declared via the <tt>ps</tt> claim in the agent token.</li>
<li><strong>Agent Provider (AP)</strong>: A server that manages agent identity and issues agent tokens to agents. Trusted by the person to issue agent tokens only to authorized agents.</li>
<li><strong>Resource</strong>: A server that requires authentication and/or authorization to protect access to its APIs and data. A resource MAY enforce access policy itself or delegate policy evaluation to an access server.</li>
<li><strong>Person Server (PS)</strong>: A server that represents the person to the rest of the protocol. The person chooses their PS; it is not imposed by any other party. The PS manages missions, handles consent, asserts user identity, and brokers authorization on behalf of agents.</li>
<li><strong>Access Server (AS)</strong>: A policy engine that evaluates token requests, applies resource policy, and issues auth tokens on behalf of a resource.</li>
<li><strong>Supervisor</strong>: The party that performs supervision — the Person by default, or a supervision server (SS) the PS delegates to <xref target="roles"/>.</li>
</ul>
<t>Each server role is identified by an HTTPS URL <xref target="server-identifiers"/> and publishes metadata at its well-known URL <xref target="metadata-documents"/>.</t>
<t>Tokens:</t>

<ul spacing="compact">
<li><strong>Agent Token</strong>: Issued by an agent provider to establish the agent's identity. MAY declare the agent's person server <xref target="agent-tokens"/>.</li>
<li><strong>Person Token</strong>: Issued by a PS to identify the person an agent acts for, to one resource, before any authorization exists. Carries identity and no authorization <xref target="person-tokens"/>.</li>
<li><strong>Resource Token</strong>: Issued by a resource to describe the access the agent needs <xref target="resource-tokens"/>.</li>
<li><strong>Auth Token</strong>: Issued by a PS or AS to grant an agent access to a resource, containing identity claims and/or authorized scopes <xref target="auth-tokens"/>.</li>
<li><strong>Session Token</strong>: Issued by a resource to an agent when the resource manages authorization itself. Opaque to the agent, carried in the <tt>AAuth-Access</tt> header and presented back via <tt>Authorization: AAuth</tt> <xref target="aauth-access"/>.</li>
</ul>
<t>Protocol concepts:</t>

<ul spacing="compact">
<li><strong>Mission</strong>: A scoped authorization context for agent governance <xref target="missions"/>. Required when the person's PS requires governance over the agent's actions. A mission is a JSON object containing structured fields (agent, approved_at, approved tools) and a Markdown description. Identified by the PS that approved it and the SHA-256 hash of the mission JSON (<tt>s256</tt>). Missions are proposed by agents and approved by the PS and person.</li>
<li><strong>Mission Log</strong>: The ordered record of all agent↔PS interactions within a mission — token requests, permission requests, audit records, interaction requests, and clarification chats. The PS maintains the log and uses it to evaluate whether each new request is consistent with the mission's intent <xref target="mission-log"/>.</li>
<li><strong>Supervision</strong>: The evaluation of one act — a token request, a permission request, a mission update — against the mission's intent, the prior log entries, and the person's policy <xref target="policy-evaluation-points"/>. Governance names the layer: missions plus permission, audit, and interaction relay. Supervision names the decision made within it, and the Supervisor makes it.</li>
<li><strong>HTTP Sig</strong>: An HTTP Message Signature (<xref target="RFC9421"/>) created per the AAuth HTTP Message Signatures profile defined in this specification <xref target="http-message-signatures-profile"/>, using a key conveyed via the <tt>Signature-Key</tt> header (<xref target="I-D.hardt-httpbis-signature-key"/>).</li>
<li><strong>Markdown</strong>: AAuth uses Markdown (<xref target="CommonMark"/>) as the human-readable content format for mission descriptions, justifications, clarifications, and scope descriptions. Implementations MUST sanitize Markdown before rendering to users. <strong>Editor's note:</strong> recommended section structures for the Markdown-valued parameters of this document (mission descriptions, updates, and completion summaries; justifications; clarifications and clarification responses; permission, audit, and interaction descriptions) are to be defined together in a later revision, so that they are consistent across parameters.</li>
<li><strong>Interaction</strong>: User authentication, consent, or other action at an interaction endpoint <xref target="user-interaction"/>. Triggered when a server returns <tt>202 Accepted</tt> with <tt>requirement=interaction</tt>.</li>
<li><strong>Justification</strong>: A Markdown string provided by the agent declaring why access is needed, presented to the user by the PS during consent <xref target="ps-token-endpoint"/>.</li>
<li><strong>Clarification</strong>: A Markdown string containing a question posed to the agent by the user during consent via the PS <xref target="clarification-chat"/>. The agent may respond with an explanation or an updated request.</li>
</ul>
</section>

<section anchor="protocol-overview"><name>Protocol Overview</name>
<t>An agent holds a signing key and an agent token that binds the key to its identifier. Every request the agent makes is signed with that key, and every other token it obtains is bound to the same key. All AAuth tokens are JWTs, verified with a key from the issuer's JWKS, which is discovered from the issuer's well-known metadata <xref target="aauth-tokens"/>. This section shows how the parties fit together; the sections that follow define each part.</t>

<section anchor="obtaining-an-agent-token"><name>Obtaining an Agent Token</name>
<t>The agent generates a signing key pair and proves its identity to its agent provider through a platform-specific mechanism (<xref target="I-D.hardt-aauth-bootstrap"/>). The agent provider issues an agent token binding the key to the agent's identifier <xref target="agent-tokens"/>. The agent token MAY carry a <tt>ps</tt> claim naming the agent's person server.</t>
</section>

<section anchor="resource-access-modes"><name>Resource Access Modes</name>
<t>AAuth supports five resource access modes. They differ in what the resource ends up knowing and which party established it, not in how much of the protocol they use. A resource MAY apply different modes to different endpoints.</t>
<table>
<thead>
<tr>
<th>Mode</th>
<th>Resource knows</th>
<th>Established by</th>
<th>Parties</th>
</tr>
</thead>

<tbody>
<tr>
<td>Agent identity</td>
<td>which agent</td>
<td>the agent provider</td>
<td>Agent <br/>
 Resource</td>
</tr>

<tr>
<td>Resource-managed <br/>
(two-party)</td>
<td>which person</td>
<td>the resource's own flow</td>
<td>Agent <br/>
 Resource</td>
</tr>

<tr>
<td>Person identity</td>
<td>which person</td>
<td>the person server</td>
<td>Agent <br/>
 Resource <br/>
 PS</td>
</tr>

<tr>
<td>PS authorization <br/>
(three-party)</td>
<td>person and consented scope</td>
<td>the person server</td>
<td>Agent <br/>
 Resource <br/>
 PS</td>
</tr>

<tr>
<td>Federated authorization <br/>
(four-party)</td>
<td>person and policy verdict</td>
<td>the access server</td>
<td>Agent <br/>
 Resource <br/>
 PS <br/>
 AS</td>
</tr>
</tbody>
</table><t>Resource-managed and person-identity access reach the same destination by different routes: in the first the resource runs its own login, in the second it accepts one the person server ran. The rest of the ladder adds what the resource is told beyond who the person is.</t>
<t>The following diagram shows all parties and their relationships. Not all parties or relationships are present in every mode.</t>
<figure anchor="fig-parties"><name>Protocol Parties and Relationships </name>
<sourcecode type="ascii-art"><![CDATA[                    /---------------\
                   |      Person     |
                    \---------------/
                      ^           ^
              mission |           | consent
                      v           v
                     +--------------+                   +--------------+
                     |              |    federation     |              |
                     |   Person     |------------------>|   Access     |
                     |   Server     |<------------------|   Server     |
                     |              |    auth token     |              |
                     +--------------+                   +--------------+
                      ^          ^ |
            mission   |   signed | | person token
                      |  request | | or auth token
                      v          | v
              agent  +--------------+  signed request   +--------------+
+-----------+ token  |              |------------------>|              |
|  Agent    |------->|    Agent     |<------------------|   Resource   |
|  Provider |        |              | resource response |              |
+-----------+        +--------------+ or resource token +--------------+

]]>
</sourcecode>
</figure>

<ul spacing="compact">
<li><strong>Agent Provider → Agent</strong>: Issues an agent token binding the agent's signing key to its identity <xref target="agent-tokens"/>.</li>
<li><strong>Agent ↔ Resource</strong>: Agent sends signed requests; the resource returns responses, or a resource token when authorization is needed <xref target="resource-tokens"/>.</li>
<li><strong>Agent ↔ PS</strong>: Agent obtains person tokens and auth tokens, and with governance creates missions and requests permissions <xref target="person-server"/>.</li>
<li><strong>PS ↔ AS</strong>: Federation (four-party only). The PS sends the resource token to the AS; the AS returns an auth token <xref target="access-server-federation"/>.</li>
<li><strong>Person ↔ PS</strong>: Mission approval and consent for resource access.</li>
</ul>
<t>Detailed end-to-end flows are in <xref target="detailed-flows"/>.</t>

<section anchor="overview-identity-access"><name>Agent Identity Access</name>
<t>The agent signs requests with its agent token. The resource verifies the agent's identity and applies its own access control, granting or denying based on who the agent is <xref target="requirement-agent-token"/>. This replaces API keys with cryptographic identity. No authorization flow, no tokens beyond the agent token.</t>
<figure anchor="fig-identity-access"><name>Identity-Based Access </name>
<sourcecode type="ascii-art"><![CDATA[Agent                                        Resource
  |                                             |
  | HTTP Sig w/ agent_token                     |
  |-------------------------------------------->|
  |                                             |
  | 200 OK                                      |
  |<--------------------------------------------|
]]>
</sourcecode>
</figure>
<t>The agent identifier reaches a resource only in this mode and in resource-managed access. In the other three, no token the resource reads carries one <xref target="why-no-agent-identifier"/>.</t>
</section>

<section anchor="overview-resource-managed"><name>Resource-Managed Access (Two-Party)</name>
<t>The resource handles authorization itself, via its own interaction, existing OAuth or OIDC infrastructure, or internal policy <xref target="resource-managed-auth"/>. After authorization, the resource MAY return an <tt>AAuth-Access</tt> header with a session token for subsequent calls <xref target="aauth-access"/>.</t>
<figure anchor="fig-resource-managed"><name>Resource-Managed Access (Two-Party) </name>
<sourcecode type="ascii-art"><![CDATA[Agent                                        Resource
  |                                             |
  | HTTP Sig w/ agent_token                     |
  |-------------------------------------------->|
  |                                             |
  | 202 (interaction required)                  |
  |<--------------------------------------------|
  |                                             |
  | [user completes interaction]                |
  |                                             |
  | GET pending URL                             |
  |-------------------------------------------->|
  |                                             |
  | 200 OK                                      |
  | AAuth-Access: session-token                 |
  |<--------------------------------------------|
  |                                             |
  | HTTP Sig w/ agent_token                     |
  | Authorization: AAuth session-token          |
  |-------------------------------------------->|
  |                                             |
  | 200 OK                                      |
  |<--------------------------------------------|
]]>
</sourcecode>
</figure>
</section>

<section anchor="overview-person-identity"><name>Person Identity Access</name>
<t>The agent obtains a person token for the resource from its PS <xref target="person-token-endpoint"/> and signs requests with it in place of its agent token. The resource verifies the token, learns which person the agent acts for, and applies its own access control on that identity. No resource token, no auth token, and the PS is not in the path of any call.</t>
<figure anchor="fig-person-identity"><name>Person Identity Access </name>
<sourcecode type="ascii-art"><![CDATA[Agent                                 Resource       PS
  |                                      |            |
  | HTTP Sig w/ agent_token              |            |
  | POST person_token_endpoint           |            |
  |-------------------------------------------------->|
  |                                      |            |
  | person_token (aud = resource)        |            |
  |<--------------------------------------------------|
  |                                      |            |
  | HTTP Sig w/ person_token             |            |
  |------------------------------------->|            |
  |                                      |            |
  | 200 OK                               |            |
  |<-------------------------------------|            |
]]>
</sourcecode>
</figure>
<t>This is federated login for agents. A resource that needs more than identity for a particular operation challenges for it there, with <tt>requirement=auth-token</tt> <xref target="requirement-auth-token"/>, while continuing to serve the rest on the person token.</t>
</section>

<section anchor="ps-authorization-access-three-party"><name>PS Authorization Access (Three-Party)</name>
<t>The resource has no access server. It accepts identity and consent asserted by whichever PS issued the person token it verified, and applies its own policy to the claims in the auth token the PS returns <xref target="trust-posture-in-ps-asserted-access"/>. Any PS can assert to any resource without bilateral setup.</t>
<figure anchor="fig-ps-asserted"><name>PS Authorization Access (Three-Party) </name>
<sourcecode type="ascii-art"><![CDATA[Agent                                 Resource       PS
  |                                      |            |
  | HTTP Sig w/ person_token             |            |
  | POST authorization_endpoint          |            |
  |------------------------------------->|            |
  |                                      |            |
  | resource_token (aud = PS URL)        |            |
  |<-------------------------------------|            |
  |                                      |            |
  | HTTP Sig w/ agent_token              |            |
  | POST auth_token_endpoint             |            |
  | w/ resource_token                    |            |
  | + presented_token                    |            |
  |-------------------------------------------------->|
  |                                      |            |
  | auth_token                           |            |
  |<--------------------------------------------------|
  |                                      |            |
  | HTTP Sig w/ auth_token               |            |
  | GET /api/documents                   |            |
  |------------------------------------->|            |
  |                                      |            |
  | 200 OK                               |            |
  |<-------------------------------------|            |
]]>
</sourcecode>
</figure>

<ol spacing="compact">
<li>Presenting its person token, the agent requests access at the resource's authorization endpoint, or calls the resource and receives a <tt>401</tt> challenge carrying a resource token <xref target="resource-tokens"/>.</li>
<li>The agent sends the resource token to its PS, which returns an auth token <xref target="ps-token-endpoint"/>.</li>
<li>The agent presents the auth token to the resource.</li>
</ol>
</section>

<section anchor="federated-authorization-access-four-party"><name>Federated Authorization Access (Four-Party)</name>
<t>The resource has its own access server. The resource token names the AS as its <tt>aud</tt>, and the PS federates with the AS to obtain the auth token <xref target="access-server-federation"/>.</t>
<figure anchor="fig-federated"><name>Federated Access (Four-Party) </name>
<sourcecode type="ascii-art"><![CDATA[Agent                                Resource   PS                    AS
  |                                     |       |                      |
  | HTTP Sig w/ person_token            |       |                      |
  | POST authorization_endpoint         |       |                      |
  |------------------------------------>|       |                      |
  |                                     |       |                      |
  | resource_token (aud = AS URL)       |       |                      |
  |<------------------------------------|       |                      |
  |                                     |       |                      |
  | HTTP Sig w/ agent_token             |       |                      |
  | POST auth_token_endpoint            |       |                      |
  | w/ resource_token                   |       |                      |
  | + presented_token                   |       |                      |
  |-------------------------------------------->|                      |
  |                                     |       |                      |
  |                                     |       | HTTP Sig w/ jwks_uri |
  |                                     |       | POST                 |
  |                                     |       | auth_token_endpoint  |
  |                                     |       | w/ resource_token    |
  |                                     |       | + presented_token    |
  |                                     |       |--------------------->|
  |                                     |       |                      |
  |                                     |       | auth_token           |
  |                                     |       |<---------------------|
  |                                     |       |                      |
  | auth_token                          |       |                      |
  |<--------------------------------------------|                      |
  |                                     |       |                      |
  | HTTP Sig w/ auth_token              |       |                      |
  | GET /api/documents                  |       |                      |
  |------------------------------------>|       |                      |
  |                                     |       |                      |
  | 200 OK                              |       |                      |
  |<------------------------------------|       |                      |
]]>
</sourcecode>
</figure>

<ol spacing="compact">
<li>Presenting its person token, the agent requests access at the resource's authorization endpoint, or calls the resource and receives a <tt>401</tt> challenge carrying a resource token <xref target="resource-tokens"/>.</li>
<li>The agent sends the resource token to its PS. The PS federates with the AS named by the resource token's <tt>aud</tt> <xref target="ps-as-federation"/>, which returns the auth token to the PS.</li>
<li>The PS returns the auth token to the agent, which presents it to the resource.</li>
</ol>
</section>
</section>

<section anchor="roles"><name>Roles</name>
<t>Agent, AP, Resource, PS, and AS are <strong>roles</strong>, not deployment units. Each role has its own protocol identity: the Agent by an <tt>aauth:local@domain</tt> URI attested by an agent token, and AP, Resource, PS, and AS each by an HTTPS URL with metadata at that role's well-known path. A single deployment unit MAY fill multiple roles. Server identifiers are scheme and host only <xref target="server-identifiers"/>, so roles hosted under a shared origin share one identifier and are distinguished by the well-known document (<tt>dwk</tt>). The protocol treats each role independently regardless of collocation.</t>
<t>Common collocations:</t>

<ul spacing="compact">
<li><strong>PS + AS</strong>: One server brokers user consent and evaluates resource policy. Federation collapses to a single internal evaluation <xref target="ps-as-collapse"/>.</li>
<li><strong>Resource + Agent + AP</strong>: A resource acts as an agent for downstream calls and is its own agent provider <xref target="intermediary-agent-identity"/>.</li>
<li><strong>AP + Resource</strong>: An agent provider exposes its own services to the agents it issues tokens to, publishing resource metadata and issuing resource tokens. How the agent obtains the resource token from the agent provider is out of scope.</li>
<li><strong>Agent + AP</strong>: A self-hosted agent is its own agent provider, self-issuing agent tokens signed by a key the user controls (<xref target="I-D.hardt-aauth-bootstrap"/>).</li>
<li><strong>Org-wide bundle</strong>: One organizational server operates AP + PS + AS for employees and internal resources, with federation only at the boundary when an internal agent reaches an external resource.</li>
</ul>
<t>The <strong>Supervisor</strong> performs supervision <xref target="policy-evaluation-points"/>: the Person by default, or a <strong>supervision server (SS)</strong> the PS MAY delegate to. Supervision is an independent protocol; a companion specification is TBD, and an implementation may treat supervision as internal to the PS. Nothing an agent, resource, or AS sees changes with who supervises.</t>
</section>

<section anchor="policy-evaluation-points"><name>Policy Evaluation Points</name>
<t>The Agent is the subject of every policy decision; the four server roles each evaluate the agent's activity from their own vantage point. No single party is the policy decision point.</t>

<ul spacing="compact">
<li><strong>Agent Provider</strong> decides whether to continue treating the agent as authorized, based on device posture, attestation freshness, account status, or any other AP-internal criteria, by issuing or refusing fresh agent tokens.</li>
<li><strong>Person Server</strong> decides whether to issue a person token or an auth token, based on user consent and, under a mission, the mission's intent and prior log entries. The Supervisor performs that evaluation.</li>
<li><strong>Access Server</strong> decides whether to issue an auth token on behalf of the resource, based on resource policy, the claims the PS has provided, and any further requirements it gathers.</li>
<li><strong>Resource</strong> decides what is required when it issues a resource token, and enforces the resulting auth token at the moment of access.</li>
</ul>
<t>Every token has a limited lifetime, so each issuance is a re-evaluation point for the party that issues it, and revocation <xref target="token-revocation"/> ends access between them.</t>
</section>

<section anchor="agent-governance"><name>Agent Governance</name>
<t>An agent with a person server can be governed by it: through missions, and through the PS's permission, audit, and interaction endpoints <xref target="person-server"/>. An agent that has a person server MUST carry the <tt>ps</tt> claim in its agent token <xref target="agent-token-structure"/>; it is how a resource learns that a person token can be asked for.</t>
<t>Governance of resource access rides on the person token. The agent names its mission when it obtains one, and <tt>mission_s256</tt> flows from there into the resource token and the auth token, so the PS evaluates every token request against the mission. That reaches the resource in the three modes where a person token is presented; in agent identity and resource-managed access the PS is not in the path. The permission and interaction endpoints do not depend on the mode, or on a mission.</t>

<section anchor="missions-overview"><name>Missions</name>
<t>A mission is a Markdown description of what the agent intends to accomplish, proposed by the agent and approved by the person at the PS. It is identified by the <tt>s256</tt> hash of the approved mission, accumulates context through the mission log, and ends when the person accepts the agent's completion proposal. Missions are OPTIONAL. Section <xref target="missions"/> defines them.</t>
</section>
</section>
</section>

<section anchor="agent-identity"><name>Agents</name>
<t>This section defines agents: the agent provider that issues their identity, the identifier it assigns, and the agent token that binds that identifier to a signing key. Agent identity is the foundation of AAuth: the agent token binds the agent's identifier to its signing key, and every other token the agent obtains (resource tokens, auth tokens) is issued in response to a request signed by that key. When an agent presents an auth token to a resource, the auth token's <tt>cnf</tt> claim binds it to the same key — so the agent's identity, established by the agent token, ultimately authorizes every signed request whether the <tt>Signature-Key</tt> header carries the agent token or an auth token.</t>

<section anchor="agent-provider"><name>Agent Provider</name>
<t>An agent provider (AP) is the server that issues an agent its identity. An AP is identified by an HTTPS URL <xref target="server-identifiers"/> and publishes metadata at <tt>/.well-known/aauth-agent.json</tt> <xref target="agent-provider-metadata"/>, where its <tt>jwks_uri</tt> holds the keys that agent tokens are verified against. A resource, PS, or AS trusts an AP's agents by fetching that one JWKS, rather than managing a key per agent <xref target="why-agents-are-under-an-agent-provider"/>.</t>
<t>An AP does four things in the protocol:</t>

<ul spacing="compact">
<li><strong>Issues agent tokens.</strong> An agent MUST obtain an agent token from its agent provider before participating in the AAuth protocol. The agent generates a signing key pair (Ed25519 is RECOMMENDED), proves its identity to the AP through a platform-specific mechanism, and the AP issues an agent token binding the agent's public key to its identifier <xref target="agent-token-structure"/>. The mechanism for proving identity is platform-dependent; see <xref target="I-D.hardt-aauth-bootstrap"/> for common patterns, including self-hosted agents, browser-based applications, and mobile applications, and for the key-refresh ceremony.</li>
<li><strong>Issues sub-agent tokens.</strong> An AP issues a sub-agent its own identifier and agent token, marked with <tt>parent_agent</tt>, under the rules in <xref target="sub-agents"/>.</li>
<li><strong>Evaluates policy.</strong> The AP decides whether to keep treating an agent as authorized, and enforces that decision by issuing or refusing fresh agent tokens <xref target="policy-evaluation-points"/>. Agent tokens SHOULD NOT live longer than 24 hours <xref target="agent-token-structure"/>, so the decision is revisited at least that often.</li>
<li><strong>Revokes agent tokens.</strong> When an agent can no longer be trusted, the AP revokes its agent token at the agent's PS <xref target="token-revocation"/>. The PS is the only recipient of an agent token revocation.</li>
</ul>
<t>An AP MAY be collocated with other roles: a self-hosted agent is its own AP, and a resource that acts as an agent for downstream calls MUST be its own AP <xref target="roles"/>. An AP that supports AAuth Events (<xref target="I-D.hardt-aauth-events"/>) also receives event tokens from resources on behalf of its agents.</t>
<t>An AP is named in both things it issues: the <tt>domain</tt> part of each agent identifier it assigns <xref target="agent-identifiers"/>, and the <tt>iss</tt> of each agent token it signs <xref target="agent-tokens"/>.</t>
</section>

<section anchor="agent-identifiers"><name>Agent Identifiers</name>
<t>An AP assigns each agent an identifier: a URI using the <tt>aauth</tt> scheme, of the form <tt>aauth:local@domain</tt>, where <tt>domain</tt> is the AP's domain. The <tt>local</tt> part MUST consist of ASCII letters (<tt>A-Za-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 <xref target="server-identifiers"/> (without scheme).</t>
<t>The plus character (<tt>+</tt>) is RESERVED as the sub-agent delimiter <xref target="sub-agents"/>. A top-level agent's <tt>local</tt> part MUST NOT contain <tt>+</tt>. A sub-agent's <tt>local</tt> part MUST be its parent's <tt>local</tt> part, followed by <tt>+</tt>, followed by a non-empty discriminator (for example, <tt>planner.7f3c+search1</tt>). This naming is for operational readability only — a sub-agent's identifier shows its parent at a glance in logs. Parties MUST NOT parse the <tt>local</tt> part for protocol decisions; the <tt>parent_agent</tt> claim <xref target="sub-agents"/> is the authoritative sub-agent marker and names the parent.</t>
<t>Valid agent identifiers:</t>

<ul spacing="compact">
<li><tt>aauth:assistant-v2@agent.example</tt></li>
<li><tt>aauth:planner.7f3c@vendor.example</tt> (top-level)</li>
<li><tt>aauth:planner.7f3c+search1@vendor.example</tt> (sub-agent of <tt>planner.7f3c</tt>)</li>
</ul>
<t>Invalid agent identifiers:</t>

<ul spacing="compact">
<li><tt>My Agent@agent.example</tt> (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): <tt>aauth:Agent@agent.example</tt> and <tt>aauth:agent@agent.example</tt> are different agents, and an implementation MUST NOT case-fold the <tt>local</tt> part.</t>
<t>An agent identifier is stable across key rotations. The agent token binds it to the agent's current signing key.</t>
</section>

<section anchor="agent-tokens"><name>Agent Token</name>

<section anchor="agent-token-structure"><name>Agent Token Structure</name>
<t>An agent token is a JWT with <tt>typ: aa-agent+jwt</tt>. Its header and the claims <tt>iss</tt>, <tt>dwk</tt>, <tt>jti</tt>, <tt>iat</tt>, <tt>exp</tt>, and <tt>cnf</tt> are as defined in <xref target="common-claims"/>, with:</t>

<ul spacing="compact">
<li><tt>iss</tt>: Agent provider URL</li>
<li><tt>dwk</tt>: <tt>aauth-agent.json</tt></li>
<li><tt>cnf</tt>: <tt>jwk</tt> is the agent's public key</li>
<li><tt>exp</tt>: Agent tokens SHOULD NOT have a lifetime exceeding 24 hours.</li>
</ul>
<t>Required payload claims specific to agent tokens:
- <tt>sub</tt>: Agent identifier (stable across key rotations)</t>
<t>Optional payload claims:
- <tt>ps</tt>: The HTTPS URL of the agent's person server. Configured per agent instance. When present, it tells a resource that the agent has a person server and which one, before the resource has verified a person token — enough to decide whether to challenge for one. The PS of an issued authorization is the <tt>iss</tt> of the person token the resource verified <xref target="person-token-structure"/>, not this claim. This claim is distinct from <tt>iss</tt> (which identifies the agent provider that issued the token).
- <tt>parent_agent</tt>: Sub-agent marker <xref target="sub-agents"/>. When present, the agent is a sub-agent and the value is the identifier of its parent agent. A sub-agent MUST NOT request authorization directly; its parent obtains auth tokens on its behalf <xref target="sub-agents"/>.</t>
<t>Agent providers MAY include additional claims in the agent token. Companion specifications may define additional claims for use by PSes or ASes in policy evaluation — for example, software attestation, platform integrity, secure enclave status, workload identity assertions, or software publisher identity. PSes and ASes 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"/>) under the <tt>jwt</tt> scheme:</t>

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

<section anchor="agent-token-verification"><name>Agent Token Verification</name>
<t>Verify the agent token per <xref target="common-verification"/>, with <tt>typ</tt> <tt>aa-agent+jwt</tt> and <tt>dwk</tt> <tt>aauth-agent.json</tt>, then:</t>

<ol spacing="compact">
<li>Verify <tt>cnf.jwk</tt> matches the key used to sign the HTTP request.</li>
<li>If <tt>ps</tt> is present, verify it is a valid HTTPS URL conforming to the Server Identifier requirements.</li>
<li>If <tt>parent_agent</tt> is present, verify it is a valid agent identifier — the parent agent. Its presence marks this as a sub-agent's token <xref target="sub-agents"/>; the PS additionally enforces the single-level rule <xref target="sub-agents"/> when such a token signs a request.</li>
</ol>
</section>
</section>
</section>

<section anchor="resource-tokens"><name>Resource Access</name>
<t>An agent calls a resource with a signed request. The resource serves it, or answers with an <tt>AAuth-Requirement</tt> header naming what it needs first <xref target="requirement-responses"/>. This section defines the four requirements a resource can raise, in the order they ask more of the agent, followed by the session token, the authorization endpoint, and the resource token.</t>
<table>
<thead>
<tr>
<th>Requirement</th>
<th>Status</th>
<th>The resource needs</th>
<th>The agent</th>
</tr>
</thead>

<tbody>
<tr>
<td><tt>agent-token</tt></td>
<td><tt>401</tt></td>
<td>the agent's identity</td>
<td>presents its agent token <xref target="requirement-agent-token"/></td>
</tr>

<tr>
<td><tt>interaction</tt></td>
<td><tt>202</tt></td>
<td>the person, at the resource's own page</td>
<td>directs the person there and polls <xref target="resource-managed-auth"/></td>
</tr>

<tr>
<td><tt>person-token</tt></td>
<td><tt>401</tt></td>
<td>the person's identity</td>
<td>obtains a person token from its PS <xref target="requirement-person-token"/></td>
</tr>

<tr>
<td><tt>auth-token</tt></td>
<td><tt>401</tt> or <tt>202</tt></td>
<td>consent or policy for a scope</td>
<td>takes the enclosed resource token to its PS <xref target="requirement-auth-token"/></td>
</tr>
</tbody>
</table><t>The first two involve no person server: the resource decides on the agent's identity, or runs its own consent and issues a session token <xref target="aauth-access"/>. The last two need the agent's PS. An agent with no PS cannot obtain a person token, so <tt>agent-token</tt> and <tt>interaction</tt> are the whole of what is available to it.</t>
<t>A resource MAY handle authorization itself for any request, regardless of whether the agent has a PS, and MAY apply different requirements to different endpoints.</t>

<section anchor="requirement-agent-token"><name>Agent Token Required</name>
<t>A resource that decides on the agent's identity alone answers a request that did not present an AAuth agent token with <tt>401</tt> and <tt>requirement=agent-token</tt>:</t>

<sourcecode type="http"><![CDATA[HTTP/1.1 401 Unauthorized
AAuth-Requirement: requirement=agent-token
]]>
</sourcecode>
<t>The header carries no parameters. The agent retries, presenting its agent token via the <tt>Signature-Key</tt> header under the <tt>jwt</tt> scheme <xref target="keying-material"/>.</t>
<t><tt>requirement=agent-token</tt> asks for an AAuth agent token (<tt>typ: aa-agent+jwt</tt>) in particular. An <tt>Accept-Signature-Scheme</tt> challenge (<xref target="I-D.hardt-httpbis-signature-key"/>) names schemes, and so would accept any key those schemes can convey; a resource challenging an AAuth agent uses <tt>requirement=agent-token</tt> instead <xref target="scheme-rejection"/>.</t>
</section>

<section anchor="resource-managed-auth"><name>Resource-Managed Authorization</name>
<t>A resource that runs its own consent, login, or existing OAuth flow answers with <tt>202 Accepted</tt> and <tt>requirement=interaction</tt>:</t>

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

{
  "status": "pending"
}
]]>
</sourcecode>
<t>The agent directs the user to the interaction URL <xref target="user-interaction"/> and polls the <tt>Location</tt> URL <xref target="deferred-responses"/>. When the interaction completes, the resource returns <tt>200 OK</tt> and MAY include an <tt>AAuth-Access</tt> header <xref target="aauth-access"/> with a session token for subsequent calls.</t>
<t>A resource MAY also authorize on the agent's identity alone, without any interaction, when the agent's key is already known or its domain is trusted.</t>
</section>

<section anchor="aauth-access"><name>AAuth-Access Response Header</name>
<t>The <tt>AAuth-Access</tt> response header carries a <strong>session token</strong> from a resource to an agent. The token is opaque to the agent: the resource wraps its own authorization state, which MAY be an existing OAuth access token or other credential. It is the one AAuth credential a resource issues for its own consumption. The agent returns it in the <tt>Authorization</tt> header on subsequent requests:</t>

<sourcecode type="http"><![CDATA[GET /api/data HTTP/1.1
Host: resource.example
Authorization: AAuth wrapped-session-token-value
Signature-Input: sig=("@method" "@authority" "@path" \
    "authorization" "signature-key");created=1730217600
Signature: sig=:...signature bytes...:
Signature-Key: sig=jwt;jwt="eyJhbGc..."
]]>
</sourcecode>
<t>The agent MUST include <tt>authorization</tt> in the covered components of its HTTP signature. The token MUST NOT be usable as a standalone bearer token: the resource wraps its state so that the value is meaningless without a valid signature from the agent.</t>
<t>A resource MAY return a new <tt>AAuth-Access</tt> header on any response, replacing the current session token. When the agent receives a new value, it MUST use it on subsequent requests. This is the refresh mechanism; there is no separate refresh flow.</t>
<t>The <tt>AAuth-Access</tt> value, and the credential carried in <tt>Authorization: AAuth</tt>, is a <tt>token68</tt> (<xref target="RFC9110"/>, Section 11.2). Recipients MUST reject empty values, values containing embedded whitespace or control characters, and responses carrying more than one credential.</t>
</section>

<section anchor="requirement-person-token"><name>Person Token Required</name>
<t>A resource that needs to know which person the agent acts for, and has not verified a person token on the request, answers with <tt>401</tt> and <tt>requirement=person-token</tt>:</t>

<sourcecode type="http"><![CDATA[HTTP/1.1 401 Unauthorized
AAuth-Requirement: requirement=person-token
]]>
</sourcecode>
<t>The header carries no parameters. The agent obtains a person token for this resource from its PS <xref target="person-token-endpoint"/> and retries, presenting it via the <tt>Signature-Key</tt> header in place of its agent token <xref target="person-token-usage"/>. A resource MUST answer a request to its authorization endpoint that carries no person token this way <xref target="authorization-endpoint-request"/>, and MAY use it on any other endpoint where it requires the person's identity before serving a request.</t>
<t>An agent with no person server cannot satisfy this requirement and surfaces it as an error <xref target="requirement-values"/>.</t>
</section>

<section anchor="requirement-auth-token"><name>Auth Token Required</name>
<t>A resource that needs consent or policy for a scope answers with <tt>401</tt>, <tt>requirement=auth-token</tt>, and a <tt>resource-token</tt> parameter carrying a resource token JWT <xref target="resource-token-structure"/>:</t>

<sourcecode type="http"><![CDATA[HTTP/1.1 401 Unauthorized
AAuth-Requirement: requirement=auth-token; resource-token="eyJ..."
]]>
</sourcecode>
<t>A resource MUST use <tt>requirement=auth-token</tt> when an auth token is required, and the header MUST include the <tt>resource-token</tt> parameter. The agent MUST extract and verify the resource token <xref target="resource-challenge-verification"/> and present it to its PS's auth token endpoint <xref target="ps-token-endpoint"/> to obtain an auth token. It then retries, presenting the auth token via <tt>Signature-Key</tt> <xref target="auth-token-usage"/>.</t>
<t>A resource issues a resource token only after verifying a person token or an auth token on the request <xref target="resource-token"/>. A request that carried neither is answered with <tt>requirement=person-token</tt> instead. A resource MAY also send <tt>402 Payment Required</tt> with the same header when payment is additionally required <xref target="requirement-responses"/>.</t>
<t>A resource MAY return <tt>requirement=auth-token</tt> with a new resource token to a request that already carries an auth token, when the request needs more authorization than the token provides. Agents MUST be prepared for this step-up at any time.</t>

<section anchor="deferred-auth-token"><name>Deferred Delivery</name>
<t>A resource MAY instead deliver the same requirement as a <tt>202 Accepted</tt> deferred response <xref target="deferred-responses"/>, holding the invocation rather than requiring the agent to retry it:</t>

<sourcecode type="http"><![CDATA[HTTP/1.1 202 Accepted
Location: /pending/f7a3b9c
Retry-After: 5
Cache-Control: no-store
AAuth-Requirement: requirement=auth-token; resource-token="eyJ..."

{
  "status": "pending"
}
]]>
</sourcecode>
<t>The agent obtains an auth token exactly as in the <tt>401</tt> case, then polls the pending URL with signed <tt>GET</tt> requests, presenting the auth token via <tt>Signature-Key</tt> once it holds one. The resource executes the held invocation on the first poll that presents a valid auth token and answers with the invocation's response.</t>
<t>Completion consumes the pending record. The resource MUST retain the record, with the invocation's result, at least until the auth token's <tt>exp</tt>, and MUST answer a repeated presentation of the same auth token at the pending URL from that result rather than executing again: a response can be lost in transit, and the agent cannot otherwise tell "not executed" from "executed, response lost". The record is keyed by the auth token's <tt>jti</tt>. If the resource token expires before the agent obtains an auth token, the resource MAY include a fresh one in the <tt>AAuth-Requirement</tt> header of a later poll response.</t>
<t>Which delivery to use is the resource's choice, per invocation. The <tt>401</tt> needs no state and works on any transport; the <tt>202</tt> suits a resource that can hold the invocation. Agents MUST support both.</t>
</section>
</section>

<section anchor="authorization-endpoint-request"><name>Authorization Endpoint</name>
<t>A resource MAY publish an <tt>authorization_endpoint</tt> in its metadata <xref target="resource-metadata"/>. It lets an agent request access for a scope before calling the resource, instead of waiting for a challenge. The agent MUST present a person token <xref target="person-tokens"/> via the <tt>Signature-Key</tt> header, and the resource MUST verify it <xref target="person-token-verification"/>. A request without one is answered per <xref target="requirement-person-token"/>.</t>
<t><strong>Request parameters:</strong></t>

<ul spacing="compact">
<li><tt>scope</tt> (REQUIRED): A space-separated string of scope values the agent is requesting <xref target="scopes"/>.</li>
<li><tt>account</tt> (OPTIONAL): A string identifying which account at the resource the authorization is for, drawn from the resource's own account namespace <xref target="account-binding"/>.</li>
</ul>

<sourcecode type="http"><![CDATA[POST /authorize HTTP/1.1
Host: resource.example
Content-Type: application/json
Signature-Key: sig=jwt;jwt="eyJhbGc..."

{
  "scope": "data.read data.write"
}
]]>
</sourcecode>
<t>The resource answers in one of two ways: it handles authorization itself, or it issues a resource token.</t>

<section anchor="response-without-resource-token"><name>Response without Resource Token</name>
<t>The resource handles authorization itself. If user interaction is needed, it returns a <tt>202 Accepted</tt> deferred response with <tt>requirement=interaction</tt>, as in <xref target="resource-managed-auth"/>. When authorization is complete, or can be granted immediately, it returns <tt>200 OK</tt> and MAY include an <tt>AAuth-Access</tt> header <xref target="aauth-access"/>:</t>

<sourcecode type="http"><![CDATA[HTTP/1.1 200 OK
AAuth-Access: wrapped-session-token-value
Content-Type: application/json

{
  "status": "authorized",
  "scope": "data.read data.write"
}
]]>
</sourcecode>
</section>

<section anchor="response-with-resource-token"><name>Response with Resource Token</name>
<t>The resource returns a resource token <xref target="resource-token-structure"/>, with <tt>aud</tt> set to its AS or to the PS that issued the person token, and <tt>mission_s256</tt> copied from the person token when it carried one:</t>

<sourcecode type="json"><![CDATA[{
  "resource_token": "eyJhbGc..."
}
]]>
</sourcecode>
<t>The agent sends the resource token to its PS's auth token endpoint <xref target="ps-token-endpoint"/>.</t>
</section>

<section anchor="authorization-endpoint-error-responses"><name>Authorization 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_scope</tt></td>
<td>400</td>
<td>Requested scope not recognized by the resource</td>
</tr>

<tr>
<td><tt>invalid_account</tt></td>
<td>400</td>
<td>The <tt>account</tt> named is not held by the person the person token identifies</td>
</tr>

<tr>
<td><tt>server_error</tt></td>
<td>500</td>
<td>Internal error</td>
</tr>
</tbody>
</table><t>Errors use the error response format <xref target="error-response-format"/>. A person token that fails verification is answered with <tt>401</tt> and <tt>Signature-Error</tt> <xref target="verification"/>, not with a code from this table.</t>
</section>
</section>

<section anchor="resource-token"><name>Resource Token</name>
<t>A resource token is what a resource hands the agent to carry to the agent's PS. It binds the resource's identity, the person's identity, the agent's signing key, and the requested scope, so that the PS or AS issuing the auth token knows exactly what was asked, by whom, for whom.</t>
<t>A resource MUST verify a person token <xref target="person-token-verification"/> or an auth token <xref target="auth-token-verification"/> on the request before it issues a resource token: the token's <tt>ps</tt>, <tt>sub</tt>, and <tt>presented_jti</tt> are copied from the token the request carried. On the authorization endpoint that is the person token; on any other endpoint it is whichever the request carried. A resource that has verified neither MUST challenge with <tt>requirement=person-token</tt> <xref target="requirement-person-token"/> instead.</t>
<t>The resource sets <tt>aud</tt> to the party that will redeem the token:</t>

<ul spacing="compact">
<li><tt>aud</tt> = the AS URL when the resource has its own access server (four-party)</li>
<li><tt>aud</tt> = the <tt>iss</tt> of the person token the resource verified when it has none (three-party)</li>
</ul>

<section anchor="resource-token-structure"><name>Resource Token Structure</name>
<t>A resource token is a JWT with <tt>typ: aa-resource+jwt</tt>. Its header and the claims <tt>iss</tt>, <tt>dwk</tt>, <tt>jti</tt>, <tt>iat</tt>, and <tt>exp</tt> are as defined in <xref target="common-claims"/>, with <tt>iss</tt> the resource URL and <tt>dwk</tt> <tt>aauth-resource.json</tt>. A resource token carries no <tt>cnf</tt>; <tt>agent_jkt</tt> binds it to the agent's key.</t>
<t>Required payload claims specific to resource tokens:</t>

<ul spacing="compact">
<li><tt>aud</tt>: The PS URL or the AS URL, as above.</li>
<li><tt>ps</tt>: The person server whose namespace <tt>sub</tt> belongs to: the <tt>iss</tt> of the person token the request carried, or the <tt>ps</tt> of the auth token it carried.</li>
<li><tt>sub</tt>: The <tt>sub</tt> of the token the request carried.</li>
<li><tt>presented_jti</tt>: The <tt>jti</tt> of the token the request carried: the person token on the first challenge of a grant, or the auth token on a step-up or per-call challenge. The agent passes that token to the PS as <tt>presented_token</tt> <xref target="ps-token-endpoint"/>. Binding the resource token to one presented token is what makes mission stripping detectable <xref target="why-presented-jti"/>.</li>
<li><tt>agent_jkt</tt>: JWK Thumbprint (<xref target="RFC7638"/>) of the agent's current signing key.</li>
</ul>
<t>A resource token carries no agent identifier. The recipient learns the agent's identity from the agent token that signs the token request.</t>
<t>Optional payload claims:</t>

<ul spacing="compact">
<li><tt>scope</tt>: Requested scopes, as a space-separated string. Present unless a companion specification defines an authorization claim that replaces it, as R3 does (<xref target="I-D.hardt-aauth-r3"/>).</li>
<li><tt>account</tt>: Echoes the <tt>account</tt> parameter of the request that produced this token <xref target="account-binding"/>.</li>
<li><tt>login_hint</tt>: A hint about who the authorization is for, per <xref target="OpenID.Core"/> Section 3.1.2.1, for a resource that knows it. The agent passes the value to its PS as the <tt>login_hint</tt> parameter of the token request <xref target="ps-token-endpoint"/> and MUST NOT alter it. The PS MAY ignore it. A resource MUST check the claims in the auth token it receives against what it asked for rather than assuming the hint was honored.</li>
<li><tt>mission_s256</tt>: REQUIRED when the presented token carried one, copied unchanged.</li>
<li><tt>tenant</tt>: Copied from the presented token when it carried one.</li>
<li><tt>interaction</tt>: Present when the resource requires its own user-facing flow, such as an OAuth authorization at a third-party service, before the PS can issue an auth token <xref target="resource-initiated-interaction"/>. Contains <tt>url</tt>, the HTTPS URL of the resource's interaction endpoint, and <tt>code</tt>, the interaction code to present there.</li>
</ul>
<t>Resource tokens SHOULD NOT have a lifetime exceeding 5 minutes. A resource token's lifetime is independent of any mission it names: the PS verifies that the mission is active when it acts on the token. If a resource token expires before it is redeemed, the agent MUST obtain a fresh one from the resource and submit a new token request. The PS SHOULD remember prior consent decisions within a mission so the user is not re-prompted for the same resource and scope. ASes are not required to enforce replay detection on resource tokens.</t>
</section>

<section anchor="resource-token-verification"><name>Resource Token Verification</name>
<t>Verify the resource token per <xref target="common-verification"/>, with <tt>typ</tt> <tt>aa-resource+jwt</tt> and <tt>dwk</tt> <tt>aauth-resource.json</tt>, then:</t>

<ol spacing="compact">
<li>Verify <tt>aud</tt> matches the recipient's own identifier (the PS in three-party, or the AS in four-party).</li>
<li>Verify <tt>agent_jkt</tt> matches the JWK Thumbprint of the key used to sign the HTTP request. For a parent-mediated sub-agent authorization <xref target="sub-agents"/>, verify it against the <tt>subagent_token</tt>'s <tt>cnf.jwk</tt> instead, since the parent signs the request.</li>
<li>Verify the <tt>presented_token</tt> from the token request <xref target="ps-token-endpoint"/> and <xref target="ps-to-as-token-request"/> by its <tt>typ</tt>: a person token (<tt>aa-person+jwt</tt>) per <xref target="person-token-verification"/> or an auth token (<tt>aa-auth+jwt</tt>) per <xref target="auth-token-verification"/>, with two substitutions: <tt>aud</tt> MUST equal the resource token's <tt>iss</tt> rather than the verifier's own identifier, and <tt>cnf.jwk</tt> MUST match the resource token's <tt>agent_jkt</tt> rather than the key that signed the request. The resource's record check on <tt>sub</tt> does not apply. A token that fails is rejected with <tt>invalid_presented_token</tt>, or <tt>expired_presented_token</tt> when only <tt>exp</tt> fails. Then verify that the presented token's <tt>jti</tt> equals <tt>presented_jti</tt>, that its <tt>iss</tt> (person token) or <tt>ps</tt> (auth token) equals the resource token's <tt>ps</tt>, and that its <tt>sub</tt>, <tt>mission_s256</tt>, and <tt>tenant</tt> match the resource token's exactly, rejecting the resource token with <tt>invalid_resource_token</tt> on any mismatch or omission. A mismatch against a token that verifies is evidence of tampering and SHOULD be surfaced to operators. A PS MUST verify that <tt>ps</tt> names itself; an AS MUST verify that <tt>ps</tt> names the PS that sent the token request.</li>
<li>If <tt>mission_s256</tt> is present, a PS MUST verify the mission is active and that the current time precedes its <tt>expires_at</tt> where one is set.</li>
</ol>
</section>

<section anchor="resource-challenge-verification"><name>Resource Challenge Verification</name>
<t>When an agent receives <tt>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>Verify <tt>agent_jkt</tt> matches the JWK Thumbprint of the agent's signing key.</li>
<li>Verify <tt>ps</tt> matches the agent's own person server, <tt>sub</tt> the value in the token the agent presented, and <tt>presented_jti</tt> that token's <tt>jti</tt>.</li>
<li>Verify <tt>exp</tt> is in the future.</li>
<li>Send the resource token, with the token the agent presented as <tt>presented_token</tt>, to the agent's PS's auth token endpoint.</li>
</ol>
</section>
</section>
</section>

<section anchor="person-server"><name>Person Server</name>
<t>A person server represents the person to the rest of the protocol. This section defines what it serves to agents, in the order an agent meets them: the two token endpoints, the consent that issuing tokens may require, the endpoints an agent uses to reach the person or to be governed, and what an agent does when tokens expire.</t>
<t>Every PS endpoint is published in its metadata <xref target="ps-metadata"/> and authenticates callers by HTTP Sig with an agent token <xref target="http-message-signatures-profile"/>; all use the same requirement responses <xref target="requirement-responses"/> and deferred responses <xref target="deferred-responses"/>.</t>
<table>
<thead>
<tr>
<th>Endpoint</th>
<th>Metadata field</th>
<th>Purpose</th>
</tr>
</thead>

<tbody>
<tr>
<td>Person token <xref target="person-token-endpoint"/></td>
<td><tt>person_token_endpoint</tt> (REQUIRED)</td>
<td>issues a person token identifying the person to one resource</td>
</tr>

<tr>
<td>Auth token <xref target="ps-token-endpoint"/></td>
<td><tt>auth_token_endpoint</tt> (REQUIRED)</td>
<td>takes a resource token and returns an auth token, directly or by federating with the resource's AS</td>
</tr>

<tr>
<td>Interaction <xref target="interaction-endpoint"/></td>
<td><tt>interaction_endpoint</tt> (OPTIONAL)</td>
<td>the agent's channel to the person through the PS</td>
</tr>

<tr>
<td>Permission <xref target="permission-endpoint"/></td>
<td><tt>permission_endpoint</tt> (OPTIONAL)</td>
<td>permission for actions not governed by a remote resource</td>
</tr>

<tr>
<td>Audit <xref target="audit-endpoint"/></td>
<td><tt>audit_endpoint</tt> (OPTIONAL)</td>
<td>a record of actions performed</td>
</tr>

<tr>
<td>Mission <xref target="missions"/></td>
<td><tt>mission_endpoint</tt> (OPTIONAL)</td>
<td>where the agent proposes, updates, and completes its missions</td>
</tr>

<tr>
<td>Mission control <xref target="mission-management"/></td>
<td><tt>mission_control_endpoint</tt> (OPTIONAL)</td>
<td>the control plane for principals other than the owning agent; defined by a companion specification</td>
</tr>

<tr>
<td>Revocation <xref target="token-revocation"/></td>
<td><tt>revocation_endpoint</tt> (RECOMMENDED)</td>
<td>where an agent provider revokes an agent token, and a resource a resource token this PS holds</td>
</tr>
</tbody>
</table><t>The two REQUIRED endpoints, with <tt>issuer</tt> and <tt>jwks_uri</tt>, are the conformance floor <xref target="ps-metadata"/>. A PS MAY also maintain a direct channel to the person, such as email, push notification, or messaging, for out-of-band approvals, notifications, and revocation alerts. The PS evaluates every request against the mission when one is in force, handles consent when it is needed, and issues tokens bounded by what it has verified.</t>

<section anchor="person-token-endpoint"><name>Person Token Endpoint</name>
<t>The first thing an agent needs from its PS is a person token for a resource <xref target="person-tokens"/>. Every PS MUST publish a <tt>person_token_endpoint</tt> in its metadata and MUST issue person tokens from it.</t>
<t>The agent MUST make a signed POST with an HTTP Sig <xref target="http-message-signatures-profile"/>, presenting its agent token via the <tt>Signature-Key</tt> header under the <tt>jwt</tt> scheme.</t>
<t><strong>Request parameters:</strong></t>

<ul spacing="compact">
<li><tt>resource</tt> (REQUIRED): The HTTPS URL of the resource the person token is for, conforming to the server identifier requirements <xref target="server-identifiers"/>. Becomes the <tt>aud</tt> of the issued token. The PS MUST validate it against those requirements.</li>
<li><tt>mission_s256</tt> (OPTIONAL): The mission the agent is operating under <xref target="missions"/>. The PS MUST verify the mission exists, is active, and belongs to this agent, and MUST reject the request otherwise. When present, the PS includes it in the issued token. Not sent with <tt>upstream_token</tt>, which carries the mission itself.</li>
<li><tt>subagent_token</tt> (OPTIONAL): A sub-agent's agent token, present when a parent agent obtains a person token on behalf of one of its sub-agents <xref target="sub-agents"/>. The signing agent MUST be named by the <tt>subagent_token</tt>'s <tt>parent_agent</tt>. The issued token's <tt>cnf</tt> is the sub-agent's key.</li>
<li><tt>upstream_token</tt> (OPTIONAL): The person token or auth token the calling agent presented to the requester, present when a resource acting as an agent needs a person token for a downstream resource <xref target="call-chaining"/>. The PS MUST verify it per <xref target="upstream-token-verification"/>.</li>
</ul>
<t>The request also takes the OPTIONAL parameters of the auth token request <xref target="ps-token-endpoint"/>, with the same definitions: <tt>capabilities</tt>, <tt>login_hint</tt>, <tt>tenant</tt>, <tt>domain_hint</tt>, <tt>prompt</tt>, <tt>justification</tt>, <tt>platform</tt>, and <tt>device</tt>. This is where a PS first decides which person the agent acts for and whether it has to reach them, so these matter here first. <tt>capabilities</tt> tells the PS whether the agent can drive an interaction; without it, a PS that must reach the person answers <tt>user_unreachable</tt> <xref target="token-endpoint-error-codes"/>. Within a mission the PS uses the capabilities captured at approval <xref target="mission-approval"/> when <tt>capabilities</tt> is omitted.</t>

<sourcecode type="http"><![CDATA[POST /person HTTP/1.1
Host: ps.example
Content-Type: application/json
Signature-Key: sig=jwt;jwt="eyJhbGc..."

{
  "resource": "https://resource.example",
  "mission_s256": "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
  "capabilities": ["interaction"]
}
]]>
</sourcecode>
<t><strong>Response</strong> (<tt>200</tt>):</t>

<sourcecode type="json"><![CDATA[{
  "person_token": "eyJhbGc...",
  "expires_in": 3600
}
]]>
</sourcecode>
<t>The PS MAY require user interaction before issuing and return a <tt>202 Accepted</tt> deferred response with <tt>requirement=interaction</tt> <xref target="interaction-required"/>. Because a resource MAY serve requests on identity alone, the question put to the person is whether this agent may act at the resource as them, not merely whether it may name them. A PS SHOULD fetch the resource's metadata <xref target="resource-metadata"/> before issuing for a resource the person has not used, and present its <tt>name</tt>, <tt>description</tt>, and <tt>access_mode</tt>.</t>
<t>Errors use the token endpoint error codes <xref target="token-endpoint-error-codes"/>; <tt>invalid_request</tt> covers a missing or malformed <tt>resource</tt> or <tt>mission_s256</tt>.</t>
<t><strong>Which person.</strong> Without <tt>upstream_token</tt> the PS issues for the person bound to the requesting agent <xref target="agent-person-binding"/>. With it, the PS issues for the person the upstream token was issued for. The upstream token MUST name this PS (as <tt>iss</tt> in a person token, as <tt>ps</tt> in an auth token), and its <tt>sub</tt> is the directed identifier this PS minted for that person at the upstream token's <tt>aud</tt> <xref target="directed-identifiers"/>. The PS resolves the person from its own record for that resource and <tt>sub</tt>; a PS that holds no such record MUST reject the request. When the upstream token carries <tt>mission_s256</tt>, the PS evaluates the request against that mission <xref target="call-chaining"/> and copies <tt>mission_s256</tt> into the person token it issues, so the mission's <tt>expires_at</tt> and termination reach the chain. The intermediary does not send <tt>mission_s256</tt> of its own.</t>
<t><strong>Retention.</strong> A PS MUST record, for each person token it issues, the <tt>jti</tt>, the <tt>aud</tt>, and the <tt>exp</tt>, and, once it has presented the token to an access server <xref target="ps-to-as-token-request"/>, which one, and MUST keep the record until the token's <tt>exp</tt> plus clock skew. The record serves revocation <xref target="token-revocation"/>, not verification. A PS SHOULD rate-limit the number of distinct <tt>resource</tt> values it accepts from one agent, since each obliges it to derive and retain a directed <tt>sub</tt>.</t>
<t><strong>Caching.</strong> An agent SHOULD cache a person token for a resource until it expires rather than requesting one per call. A person token is scoped to one resource and, when it carries <tt>mission_s256</tt>, to one mission, so an agent holds one per combination. Rotating the signing key invalidates all of them, since each binds the key through <tt>cnf</tt>; the agent SHOULD re-request lazily, on next use of each resource.</t>

<section anchor="person-tokens"><name>Person Token</name>
<t>A person token is a PS-issued JWT that identifies the person an agent acts for to a single resource. It is not a bearer credential — <tt>cnf</tt> binds it to the agent's signing key — its <tt>aud</tt> is one resource, and it lives at most one hour. It carries no authorization from the PS: no scope, no account, no permission. Whether identity alone is sufficient to serve a request is the resource's decision, and a resource that decides it is <xref target="overview-person-identity"/> serves whatever it serves on identity — so holding a person token is, at such a resource, effectively access. What a person token MUST NOT do is stand in for an auth token where one is required <xref target="person-token-not-authorization"/>.</t>
<t>A person token asserts that its issuer recognizes this person and that this agent acts for them. It carries no statement about how the person server established the person's identity, and a resource MUST NOT treat it as evidence of identity proofing, of legal identity, or of any assurance level. What it guarantees is continuity: the same <tt>(iss, sub)</tt> is the same person at this resource over time <xref target="continuity-not-proofing"/>.</t>
<t>The agent presents it via the <tt>Signature-Key</tt> header in place of its agent token <xref target="keying-material"/>. A resource MUST have verified a person token before it issues a resource token <xref target="resource-tokens"/>, so the identity and mission a resource records are PS-asserted rather than agent-asserted.</t>
</section>

<section anchor="person-token-structure"><name>Person Token Structure</name>
<t>A person token is a JWT with <tt>typ: aa-person+jwt</tt>. Its header and the claims <tt>iss</tt>, <tt>dwk</tt>, <tt>jti</tt>, <tt>iat</tt>, <tt>exp</tt>, and <tt>cnf</tt> are as defined in <xref target="common-claims"/>, with:</t>

<ul spacing="compact">
<li><tt>iss</tt>: PS URL</li>
<li><tt>dwk</tt>: <tt>aauth-person.json</tt></li>
<li><tt>cnf</tt>: <tt>jwk</tt> is the agent's public key</li>
<li><tt>exp</tt>: Person tokens MUST NOT have a lifetime exceeding 1 hour, and MUST NOT outlive the agent token presented when the token was requested, the <tt>upstream_token</tt> when the request carried one <xref target="call-chaining"/>, or, when <tt>mission_s256</tt> is present, the mission's <tt>expires_at</tt> <xref target="mission-approval"/>.</li>
</ul>
<t>Required payload claims specific to person tokens:</t>

<ul spacing="compact">
<li><tt>aud</tt>: The URL of the resource this token identifies the person to</li>
<li><tt>sub</tt>: Directed user identifier, with the same value the PS uses in the <tt>sub</tt> claim of auth tokens it issues for this <tt>aud</tt> <xref target="auth-token-structure"/></li>
</ul>
<t>Optional payload claims:</t>

<ul spacing="compact">
<li><tt>mission_s256</tt>: The mission the agent is operating under <xref target="missions"/>, when the request named one. The base64url-encoded SHA-256 hash of the approved mission JSON, without padding.</li>
<li><tt>tenant</tt>: Tenant identifier per OpenID Connect Enterprise Extensions 1.0 <xref target="OpenID.Enterprise"/>, declaring the organization the person belongs to. It lets a resource apply organizational policy before it issues anything <xref target="person-token-org-policy"/>. It is not part of the identifier; see <xref target="directed-identifiers"/>.</li>
</ul>

<sourcecode type="json"><![CDATA[{
  "typ": "aa-person+jwt",
  "alg": "Ed25519",
  "kid": "ps-key-1"
}
]]>
</sourcecode>

<sourcecode type="json"><![CDATA[{
  "iss": "https://ps.example",
  "dwk": "aauth-person.json",
  "aud": "https://resource.example",
  "sub": "8f14e45fceea167a5a36dedd4bea2543",
  "cnf": { "jwk": { "kty": "OKP", "crv": "Ed25519",
                    "x": "NzbLsXh8uDCcd...", "alg": "Ed25519" } },
  "mission_s256": "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
  "jti": "pt-3ab910",
  "iat": 1730217600,
  "exp": 1730221200
}
]]>
</sourcecode>
<t>A person token MUST NOT contain <tt>scope</tt> or <tt>account</tt>.</t>
</section>

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

<sourcecode type="http"><![CDATA[Signature-Key: sig=jwt;
    jwt="eyJhbGciOiJFZDI1NTE5IiwidHlwIjoiYWEtcGVyc29uK2p3dCJ9..."
]]>
</sourcecode>
<t>The person token's <tt>cnf.jwk</tt> is the same key that signed the request, so HTTP Message Signature verification proceeds identically to the agent-token case. Once an auth token has been issued for a resource, the agent presents the auth token on subsequent requests to that resource <xref target="auth-token-usage"/>.</t>
<t>An agent refreshes a person token before it expires, within the margin of <xref target="refresh-margin"/>, and re-obtains resource tokens and auth tokens against it, so that no token in the chain lapses mid-task. The refresh runs through the resource's authorization endpoint <xref target="authorization-endpoint-request"/> presenting the fresh person token, which is also what restores a full-length chain: a resource token issued on a step-up names the auth token the request carried, and the auth token issued against it inherits that token's <tt>exp</tt> <xref target="auth-token-structure"/>. This parallels the agent-token guidance in <xref target="re-authorization"/>.</t>
</section>

<section anchor="person-token-verification"><name>Person Token Verification</name>
<t>Verify the person token per <xref target="common-verification"/>, with <tt>typ</tt> <tt>aa-person+jwt</tt> and <tt>dwk</tt> <tt>aauth-person.json</tt>, then:</t>

<ol spacing="compact">
<li>Verify <tt>aud</tt> matches the resource's own identifier.</li>
<li><tt>cnf.jwk</tt> is REQUIRED. Verify it matches the key used to sign the HTTP request, applying the same structural checks as auth token verification <xref target="request-context-binding"/>.</li>
</ol>
<t>A recipient MUST reject an <tt>aa-person+jwt</tt> wherever an auth token is required. Only <tt>typ</tt> distinguishes the two <xref target="person-token-not-authorization"/>.</t>
<t><tt>sub</tt> is unique within the issuer, not globally. A resource MUST treat <tt>(iss, sub)</tt> as the identifier, MUST treat the value as opaque, and MUST NOT match a <tt>sub</tt> received from one issuer against a record established under another, however the values compare.</t>
</section>
</section>

<section anchor="ps-token-endpoint"><name>Auth Token Endpoint</name>
<t>Once a resource has issued a resource token, the agent brings it here. The PS evaluates the request, handles user consent if needed, and either issues the auth token itself or federates with the resource's AS <xref target="ps-as-federation"/>. The resource token's <tt>aud</tt> decides which.</t>
<table>
<thead>
<tr>
<th>Mode</th>
<th>Key Parameters</th>
<th>Use Case</th>
</tr>
</thead>

<tbody>
<tr>
<td>PS authorization</td>
<td><tt>resource_token</tt> (<tt>aud</tt> = PS)</td>
<td>PS asserts identity and consent; resource applies its own policy (three-party)</td>
</tr>

<tr>
<td>AS-federated</td>
<td><tt>resource_token</tt> (<tt>aud</tt> = AS)</td>
<td>PS federates with the resource's AS, which evaluates resource policy (four-party)</td>
</tr>

<tr>
<td>Call chaining</td>
<td><tt>resource_token</tt> + <tt>upstream_token</tt></td>
<td>Resource acting as agent <xref target="call-chaining"/></td>
</tr>
</tbody>
</table>
<section anchor="auth-token-request"><name>Auth Token Request</name>
<t>The agent MUST make a signed POST to the PS's <tt>auth_token_endpoint</tt> with an HTTP Sig <xref target="http-message-signatures-profile"/>, presenting its agent token via the <tt>Signature-Key</tt> header under the <tt>jwt</tt> scheme.</t>
<t><strong>Request parameters:</strong></t>

<ul spacing="compact">
<li><tt>resource_token</tt> (REQUIRED): The resource token.</li>
<li><tt>presented_token</tt> (REQUIRED): The token the agent presented to the resource that issued <tt>resource_token</tt>, whose <tt>jti</tt> the resource token's <tt>presented_jti</tt> names <xref target="resource-token-structure"/>: the person token on the first challenge of a grant, or the auth token on a step-up or per-call challenge. The PS verifies it against the resource token <xref target="resource-token-verification"/> and, in four-party, passes it to the AS <xref target="ps-to-as-token-request"/>. Its <tt>exp</tt> bounds the auth token issued <xref target="auth-token-structure"/>.</li>
<li><tt>upstream_token</tt> (OPTIONAL): The person token or auth token the calling agent presented to the requester, used in call chaining <xref target="call-chaining"/>. The PS MUST verify it per <xref target="upstream-token-verification"/>.</li>
<li><tt>subagent_token</tt> (OPTIONAL): A sub-agent's agent token, present when a parent agent requests authorization on behalf of one of its sub-agents <xref target="sub-agents"/>. The signing agent (the parent) MUST be named by the <tt>subagent_token</tt>'s <tt>parent_agent</tt>.</li>
<li><tt>justification</tt> (OPTIONAL): A Markdown string declaring why access is being requested. The PS SHOULD present it to the user during consent, MUST present it as agent-asserted content <xref target="consent-presentation"/>, and MUST sanitize it before rendering. The PS MAY log it. It is also the text the user's clarification questions are asked about <xref target="clarification-chat"/>. This document does not yet define a section structure for the value <xref target="terminology"/>.</li>
<li><tt>login_hint</tt> (OPTIONAL): Hint about who to authorize, per <xref target="OpenID.Core"/> Section 3.1.2.1. When the resource token carries a <tt>login_hint</tt> <xref target="resource-token-structure"/> the agent sends that value unchanged.</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>
<li><tt>prompt</tt> (OPTIONAL): Space-delimited, case-sensitive list of values specifying whether the PS prompts the user for reauthentication and consent, per <xref target="OpenID.Core"/> Section 3.1.2.1. Defined values: <tt>none</tt>, <tt>login</tt>, <tt>consent</tt>, <tt>select_account</tt>.</li>
<li><tt>platform</tt> (OPTIONAL): Identifier for the runtime platform the agent runs on. The value MUST be from the AAuth Platform Value Registry <xref target="aauth-platform-value-registry"/>. Describes where the agent runs, not what security measures apply there. For display at the consent screen and the connected-agents dashboard. Agent-attested.</li>
<li><tt>device</tt> (OPTIONAL): Short human-readable string identifying the device or browser, for display so users can distinguish entries in their connected-agents dashboard (e.g., <tt>Chrome on macOS</tt>, <tt>Pixel 8 (App)</tt>). Opaque to receivers. MUST consist of UTF-8 printable characters only and MUST NOT exceed 64 characters. Agents MUST NOT include personally identifying information beyond what the user has chosen. Agent-attested.</li>
<li><tt>capabilities</tt> (OPTIONAL): An array of capability values <xref target="aauth-capabilities"/> the agent can handle for this request, the body equivalent of the <tt>AAuth-Capabilities</tt> header, which is not used on PS endpoints. Within a mission, if omitted, the PS uses the values captured at approval <xref target="mission-approval"/>; if present, it refreshes them for this request.</li>
</ul>
<t><strong>Example request:</strong></t>

<sourcecode type="http"><![CDATA[POST /token HTTP/1.1
Host: ps.example
Content-Type: application/json
Prefer: wait=45
Signature-Key: sig=jwt;jwt="eyJhbGc..."

{
  "resource_token": "eyJhbGc...",
  "presented_token": "eyJhbGc...",
  "justification": "Find available meeting times"
}
]]>
</sourcecode>
</section>

<section anchor="ps-response"><name>PS Response</name>
<t>The PS returns one of:</t>
<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</strong> (<tt>202</tt>): a deferred response with <tt>requirement=interaction</tt> <xref target="interaction-required"/>, of the same shape as <xref target="resource-managed-auth"/>. In four-party mode the PS may also pass a clarification from the AS through to the agent this way <xref target="as-token-endpoint"/>.</t>
<t>An agent MAY have several token requests pending at the PS at once, for example when a mission needs several resources. Each has its own pending URL and lifecycle, and the PS MUST handle them independently. How the PS manages concurrent user interactions, by batching consent prompts or serializing them, is its own choice.</t>
</section>

<section anchor="resource-initiated-interaction"><name>Resource-Initiated Interaction</name>
<t>When the resource token carries an <tt>interaction</tt> claim <xref target="resource-token-structure"/>, the resource needs its own user-facing flow, typically an OAuth authorization at a third-party service, before the PS can issue an auth token. The PS resolves the resource's interaction before presenting its own consent: if the user declines at the resource, PS consent is moot.</t>

<ol spacing="compact">
<li>The PS returns <tt>202</tt> to the agent with its own interaction URL, as for any consent interaction.</li>
<li>The user arrives at the PS's interaction page. The PS shows an interstitial explaining that the resource requires additional permissions.</li>
<li>The PS redirects the user to the resource's interaction endpoint using the standard callback pattern, where <tt>ps_callback_url</tt> is a PS-generated, per-flow URL: <tt>{interaction.url}?code={interaction.code}&amp;callback={ps_callback_url}</tt></li>
<li>The resource completes its own flow. The resource MUST redirect the user to the <tt>callback</tt> URL when its flow completes, successfully or with an error per <xref target="interaction-callback-errors"/>.</li>
<li>If the callback carries an <tt>error</tt> parameter, the PS abandons the authorization and returns the mapped polling error to the agent. Otherwise it continues with its own consent step.</li>
<li>On user approval, the PS issues the auth token and resolves the agent's pending request.</li>
</ol>
<t>A resource's interaction endpoint MUST support the <tt>?code=...&amp;callback=...</tt> pattern whether the redirect comes from an agent or from a PS; it need not distinguish the two. The <tt>interaction.url</tt> MUST be an HTTPS URL; the PS MUST validate this before redirecting and MUST apply its egress admission policy to it.</t>
</section>
</section>

<section anchor="user-interaction"><name>User Interaction</name>
<t>Issuing a token may require the person. When a server responds with <tt>202</tt> and <tt>requirement=interaction</tt>, the agent directs the user to the interaction <tt>url</tt> with the <tt>code</tt>, optionally relaying through its PS first, using the mechanics defined in <xref target="interaction-required"/> and <xref target="interaction-relay"/>. Two details apply when the agent directs the user itself.</t>
<t>When the agent has a browser, it MAY append a <tt>callback</tt> parameter, constructed from its <tt>callback_endpoint</tt> metadata:</t>

<artwork><![CDATA[{url}?code={code}&callback={callback_url}
]]>
</artwork>
<t>When present, the server redirects the user's browser to the <tt>callback</tt> URL after the user completes the action. Without it, the server displays a completion page and the agent relies on polling.</t>
<t>The <tt>code</tt> is single-use: once the user arrives with a valid code, it is consumed. The server hosting the interaction URL MAY instead complete the interaction over a channel it already controls, such as a notification the person taps, without the person visiting <tt>url</tt> or presenting <tt>code</tt>; the code is consumed at that completion, and the pending URL returns the terminal response <xref target="deferred-responses"/>. Only the host of <tt>url</tt> can complete an interaction this way.</t>

<section anchor="interaction-callback-errors"><name>Interaction Callback Errors</name>
<t>When an interaction cannot be completed, the server MUST redirect to the <tt>callback</tt> URL with an <tt>error</tt> query parameter:</t>

<artwork><![CDATA[{callback_url}?error={error_code}
]]>
</artwork>
<table>
<thead>
<tr>
<th>Error</th>
<th>Meaning</th>
</tr>
</thead>

<tbody>
<tr>
<td><tt>access_denied</tt></td>
<td>The user explicitly declined the interaction.</td>
</tr>

<tr>
<td><tt>user_abandoned</tt></td>
<td>The user opened the interaction but did not complete it.</td>
</tr>

<tr>
<td><tt>server_error</tt></td>
<td>The party handling the interaction encountered an internal failure.</td>
</tr>

<tr>
<td><tt>temporarily_unavailable</tt></td>
<td>The interaction service is temporarily unavailable; the caller MAY retry.</td>
</tr>

<tr>
<td><tt>interaction_expired</tt></td>
<td>The interaction session expired before the user completed the flow.</td>
</tr>
</tbody>
</table><t>Recipients of a callback with an <tt>error</tt> parameter MUST NOT treat the pending request as completable and MUST surface the error to the caller. In the resource-initiated interaction flow <xref target="resource-initiated-interaction"/>, the PS maps the callback error to a polling error <xref target="polling-error-codes"/>: <tt>access_denied</tt> to <tt>denied</tt>, <tt>user_abandoned</tt> to <tt>abandoned</tt>, <tt>interaction_expired</tt> to <tt>expired</tt>, and <tt>server_error</tt> and <tt>temporarily_unavailable</tt> to <tt>server_error</tt>.</t>
</section>
</section>

<section anchor="consent-presentation"><name>Consent Presentation</name>
<t>A consent surface carries content from two sources, and the person deciding needs to know which is which.</t>
<t><strong>Resource-asserted</strong> content comes from the party that will carry out the access: the <tt>name</tt>, <tt>description</tt>, <tt>logo_uri</tt>, and <tt>scope_descriptions</tt> in the resource's metadata <xref target="resource-metadata"/>, any claim in the resource token <xref target="resource-tokens"/>, and the <tt>display</tt> section of an R3 document (<xref target="I-D.hardt-aauth-r3"/>).</t>
<t><strong>Agent-asserted</strong> content comes from the party asking for the access: the <tt>justification</tt>, <tt>platform</tt>, and <tt>device</tt> parameters of the token request <xref target="ps-token-endpoint"/>, and the agent's clarification responses <xref target="clarification-chat"/>. The agent chooses the words and gains from being believed.</t>
<t>A PS MUST visually distinguish resource-asserted content from agent-asserted content when rendering a consent surface, and MUST attribute agent-asserted content to the agent. A PS MUST NOT base an authorization decision solely on agent-asserted content where resource-asserted content covering the same operation is available.</t>
<t>Neither requirement suppresses the justification: it is presented, as the agent's claim. Where the Supervisor <xref target="roles"/> is a supervision server rather than a person reading a screen, the PS MUST convey the same distinction in whatever form that context takes.</t>
</section>

<section anchor="clarification-chat"><name>Clarification Chat</name>
<t>During consent, the user may ask questions about the agent's stated justification. The PS delivers the question to the agent and the agent responds, giving the person a consent dialog without the agent needing a direct channel to them.</t>
<t>Agents that support clarification chat declare it with the <tt>clarification</tt> capability <xref target="aauth-capabilities"/>.</t>

<section anchor="requirement-clarification"><name>Clarification Required</name>
<t>A server MUST use <tt>requirement=clarification</tt> with a <tt>202 Accepted</tt> response when it needs the recipient to answer a question before proceeding. The body MUST include a <tt>clarification</tt> field containing the question and MAY include <tt>timeout</tt> and <tt>options</tt>.</t>

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

{
  "status": "pending",
  "clarification": "Why do you need write access to my calendar?",
  "timeout": 120
}
]]>
</sourcecode>
<t>Body fields:</t>

<ul spacing="compact">
<li><tt>clarification</tt> (REQUIRED): A Markdown string containing the question.</li>
<li><tt>timeout</tt> (OPTIONAL): Seconds until the server times out the request. The recipient MUST respond before this deadline.</li>
<li><tt>options</tt> (OPTIONAL): An array of string values when the question has discrete choices.</li>
</ul>
<t>The recipient MUST respond with one of the actions in <xref target="agent-response-to-clarification"/>. This requirement is used by PSes (delivering user questions to agents) and by ASes (requesting clarification from PSes).</t>
</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 an <tt>action</tt> of <tt>clarification_response</tt> to the pending URL.</li>
<li><strong>Updated request</strong>: POST an <tt>action</tt> of <tt>updated_request</tt> with a new <tt>resource_token</tt> to the pending URL.</li>
<li><strong>Cancel request</strong>: DELETE the pending URL.</li>
</ol>
<t>A POST body MUST include an <tt>action</tt> member. A server MUST reject a POST with a missing or unrecognized <tt>action</tt> with <tt>400 Bad Request</tt>.</t>

<section anchor="clarification-response"><name>Clarification Response</name>

<sourcecode type="http"><![CDATA[POST /pending/abc123 HTTP/1.1
Host: ps.example
Content-Type: application/json
Signature-Key: sig=jwt;jwt="eyJhbGc..."

{
  "action": "clarification_response",
  "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, presented as agent-asserted content <xref target="consent-presentation"/>. 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, for example with reduced scope, and POST it to the pending URL together with the <tt>presented_token</tt> it used at the resource to obtain it:</t>

<sourcecode type="json"><![CDATA[{
  "action": "updated_request",
  "resource_token": "eyJ...",
  "presented_token": "eyJ...",
  "justification": "I've reduced my request to read-only access."
}
]]>
</sourcecode>
<t><tt>presented_token</tt> is REQUIRED. The PS verifies the pair per <xref target="resource-token-verification"/>, including step 3, before replacing the pending request, with the errors of that section. The new resource token MUST have the same <tt>iss</tt>, <tt>ps</tt>, <tt>sub</tt>, <tt>agent_jkt</tt>, <tt>mission_s256</tt>, and <tt>tenant</tt> as the original; its <tt>presented_jti</tt> MAY differ, and MUST equal the <tt>jti</tt> of the <tt>presented_token</tt> sent with it. The PS presents the updated request to the user. A PS answering an AS clarification with <tt>updated_request</tt> sends the same body to the AS pending URL, and the AS verifies the pair the same way <xref target="ps-to-as-token-request"/>. A <tt>justification</tt> is OPTIONAL but RECOMMENDED.</t>
</section>

<section anchor="cancel-request"><name>Cancel Request</name>
<t>The agent MAY cancel by sending a signed <tt>DELETE</tt> to the pending URL. The PS 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>PSes MUST enforce a maximum number of clarification rounds; five is RECOMMENDED. Clarification responses are untrusted input and MUST be sanitized before display <xref target="untrusted-input"/>.</t>
</section>
</section>

<section anchor="interaction-endpoint"><name>Interaction Endpoint</name>
<t>The interaction endpoint lets the agent reach the user through the PS, which may have a better channel to them (an active session, a registered app) than the agent has. The agent uses it to relay interaction requirements from resources <xref target="interaction-relay"/>, to relay payment approvals, and to ask the user questions. Proposing mission completion is not among these; it belongs at the <tt>mission_endpoint</tt> <xref target="mission-completion"/>. The endpoint MAY be used with or without a mission.</t>

<section anchor="interaction-request"><name>Interaction Request</name>
<t>The agent MUST make a signed POST to the PS's <tt>interaction_endpoint</tt> with an HTTP Sig <xref target="http-message-signatures-profile"/>, presenting its agent token via the <tt>Signature-Key</tt> header.</t>
<t><strong>Request parameters:</strong></t>

<ul spacing="compact">
<li><tt>type</tt> (REQUIRED): One of <tt>interaction</tt>, <tt>payment</tt>, or <tt>question</tt>.</li>
<li><tt>description</tt> (OPTIONAL): A Markdown string providing context for the user.</li>
<li><tt>url</tt> (OPTIONAL): The interaction URL to relay to the user (<tt>interaction</tt> and <tt>payment</tt> types).</li>
<li><tt>code</tt> (OPTIONAL): The interaction code associated with the URL.</li>
<li><tt>max_wait</tt> (OPTIONAL): Maximum seconds the PS SHOULD hold the relay's deferred response before resolving it (<tt>interaction</tt> and <tt>payment</tt> types). When the interaction URL is resource-hosted, the PS resolves once the user has engaged or this window elapses, whichever comes first <xref target="interaction-response-poll-authority"/>. Absent <tt>max_wait</tt>, the PS resolves when the user has engaged or it can make no further progress.</li>
<li><tt>question</tt> (OPTIONAL): A Markdown string containing a question for the user (<tt>question</tt> type).</li>
<li><tt>mission_s256</tt> (OPTIONAL): The mission this request belongs to.</li>
</ul>

<sourcecode type="http"><![CDATA[POST /interaction HTTP/1.1
Host: ps.example
Content-Type: application/json
Signature-Key: sig=jwt;jwt="eyJhbGc..."

{
  "type": "interaction",
  "description": "The booking service needs you to confirm payment",
  "url": "https://booking.example/confirm",
  "code": "X7K2-M9P4",
  "mission_s256": "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
}
]]>
</sourcecode>
</section>

<section anchor="interaction-response-poll-authority"><name>Interaction Response</name>
<t>For <tt>interaction</tt> and <tt>payment</tt> types, the PS relays the interaction to the user and returns a deferred response <xref target="deferred-responses"/>.</t>
<t>When the interaction URL is hosted by the <strong>PS itself</strong>, the PS's deferred response is authoritative: the agent polls it until the user completes the interaction.</t>
<t>When the interaction URL is hosted by a <strong>resource</strong>, the user completes the interaction at the resource, and the agent holds two pending URLs: the resource's original <tt>Location</tt> and the PS's relay <tt>Location</tt>. The <strong>resource's</strong> pending URL is authoritative. The PS's relay reports only that the relay reached the user: it returns <tt>status: "interacting"</tt> once the user has engaged, and a terminal response when the PS has done all it can. The agent MUST treat the resource's pending URL as the signal that the interaction is complete, and continues polling it after the PS relay resolves.</t>
<t>If the PS has no channel available to relay this interaction, it returns <tt>interaction_unavailable</tt> <xref target="interaction-endpoint-errors"/>, and the agent falls back to directing the user itself <xref target="interaction-relay"/>. If the PS cannot reach the user and the agent did not declare the <tt>interaction</tt> capability, it returns <tt>user_unreachable</tt> <xref target="token-endpoint-error-codes"/>, which is terminal.</t>
<t>For <tt>question</tt> type, the PS delivers the question to the user and returns the answer:</t>

<sourcecode type="http"><![CDATA[HTTP/1.1 200 OK
Content-Type: application/json

{
  "answer": "Yes, go ahead with the refundable option."
}
]]>
</sourcecode>
<t>If the mission is no longer active, the PS returns a mission status error <xref target="mission-status-errors"/>. The PS SHOULD record all interaction requests and responses; within a mission it records them in the mission log.</t>
</section>

<section anchor="interaction-endpoint-errors"><name>Interaction Endpoint Errors</name>
<t>Errors use the error response format <xref target="error-response-format"/>.</t>
<table>
<thead>
<tr>
<th>Error</th>
<th>Status</th>
<th>Meaning</th>
</tr>
</thead>

<tbody>
<tr>
<td><tt>interaction_unavailable</tt></td>
<td>424</td>
<td>The PS has no channel available to relay this <tt>interaction</tt> or <tt>payment</tt> to the user. Non-terminal: the agent directs the user to the <tt>url</tt>/<tt>code</tt> itself <xref target="interaction-relay"/>. Distinct from the terminal <tt>user_unreachable</tt> <xref target="token-endpoint-error-codes"/>.</td>
</tr>
</tbody>
</table></section>
</section>

<section anchor="permission-endpoint"><name>Permission Endpoint</name>
<t>The permission endpoint lets an agent ask the PS before an action no remote resource governs: a tool call, a file write, a message sent on the user's behalf. It gives the person governance over the agent before any resource supports AAuth. It MAY be used with or without a mission. When a mission is active, its approval MAY list pre-approved tools in <tt>approved_tools</tt> <xref target="mission-approval"/>; the agent calls the permission endpoint only for actions not covered by them.</t>

<section anchor="permission-request"><name>Permission Request</name>
<t>The agent MUST make a signed POST to the PS's <tt>permission_endpoint</tt> with an HTTP Sig <xref target="http-message-signatures-profile"/>, presenting its agent token via the <tt>Signature-Key</tt> header.</t>
<t><strong>Request parameters:</strong></t>

<ul spacing="compact">
<li><tt>action</tt> (REQUIRED): A string identifying the action the agent wants to perform (e.g., a tool name).</li>
<li><tt>description</tt> (OPTIONAL): A Markdown string describing what the action will do and why.</li>
<li><tt>parameters</tt> (OPTIONAL): A JSON object containing the parameters the agent intends to pass to the action.</li>
<li><tt>mission_s256</tt> (OPTIONAL): The mission this request belongs to. When present, the PS evaluates the request against the mission and its log.</li>
</ul>

<sourcecode type="http"><![CDATA[POST /permission HTTP/1.1
Host: ps.example
Content-Type: application/json
Signature-Key: sig=jwt;jwt="eyJhbGc..."

{
  "action": "SendEmail",
  "description": "Send the proposed itinerary to the user",
  "parameters": {
    "to": "user@example.com",
    "subject": "Japan trip itinerary"
  },
  "mission_s256": "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
}
]]>
</sourcecode>
</section>

<section anchor="permission-response"><name>Permission Response</name>
<t>If the PS can decide immediately, it returns <tt>200 OK</tt>:</t>

<sourcecode type="http"><![CDATA[HTTP/1.1 200 OK
Content-Type: application/json

{
  "permission": "granted"
}
]]>
</sourcecode>
<t>The <tt>permission</tt> field is one of:</t>

<ul spacing="compact">
<li><tt>granted</tt>: The agent MAY proceed with the action.</li>
<li><tt>denied</tt>: The agent MUST NOT proceed. The response MAY include a <tt>reason</tt> field with a Markdown string.</li>
</ul>
<t>If the PS requires user input, it returns a deferred response <xref target="deferred-responses"/> and the agent polls until a final response. If the mission is no longer active, the PS returns a mission status error <xref target="mission-status-errors"/>. The PS SHOULD record all permission requests and responses; within a mission it records them in the mission log.</t>
</section>
</section>

<section anchor="audit-endpoint"><name>Audit Endpoint</name>
<t>The audit endpoint lets an agent log an action after performing it, so the PS has a complete record of the mission. It requires a mission; there is no audit outside a mission context.</t>

<section anchor="audit-request"><name>Audit Request</name>
<t>The agent MUST make a signed POST to the PS's <tt>audit_endpoint</tt> with an HTTP Sig <xref target="http-message-signatures-profile"/>, presenting its agent token via the <tt>Signature-Key</tt> header.</t>
<t><strong>Request parameters:</strong></t>

<ul spacing="compact">
<li><tt>mission_s256</tt> (REQUIRED): The mission this record belongs to.</li>
<li><tt>action</tt> (REQUIRED): A string identifying the action that was performed.</li>
<li><tt>description</tt> (OPTIONAL): A Markdown string describing what was done and the outcome.</li>
<li><tt>parameters</tt> (OPTIONAL): A JSON object containing the parameters that were used.</li>
<li><tt>result</tt> (OPTIONAL): A JSON object containing the result or outcome of the action.</li>
</ul>

<sourcecode type="http"><![CDATA[POST /audit HTTP/1.1
Host: ps.example
Content-Type: application/json
Signature-Key: sig=jwt;jwt="eyJhbGc..."

{
  "mission_s256": "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
  "action": "WebSearch",
  "description": "Searched for flights to Tokyo in May",
  "parameters": {
    "query": "flights to Tokyo May 2026"
  },
  "result": {
    "status": "completed",
    "summary": "Found 12 flight options"
  }
}
]]>
</sourcecode>
</section>

<section anchor="audit-response"><name>Audit Response</name>
<t>The PS returns <tt>201 Created</tt> to acknowledge the record:</t>

<sourcecode type="http"><![CDATA[HTTP/1.1 201 Created
]]>
</sourcecode>
<t>The audit endpoint is fire-and-forget; the agent SHOULD NOT block on the response. The PS records the entry in the mission log and MAY use audit records to detect anomalous behavior, alert the user, or revoke the mission. If the mission is no longer active, the PS returns a mission status error <xref target="mission-status-errors"/>.</t>
</section>
</section>

<section anchor="re-authorization"><name>Re-authorization</name>
<t>AAuth has no refresh token. When an auth token expires, the agent obtains a fresh resource token from the resource and submits it to the PS, the same flow as the initial authorization. This gives the resource a voice in every re-authorization: it can adjust scope, require step-up, or deny on current policy.</t>
<t>When an agent rotates its signing key, every auth token bound to the old key stops working. The agent MUST re-authorize by obtaining fresh resource tokens and submitting them to the PS.</t>
<t>Auth tokens MUST NOT have an <tt>exp</tt> later than the agent token used to obtain them. An agent refreshes its agent token, and the tokens obtained with it, within the margin below.</t>

<section anchor="refresh-margin"><name>Expiry and the Refresh Margin</name>
<t>A token is expired the moment a verifier's clock passes its <tt>exp</tt>, at every party that verifies it <xref target="common-verification"/>. A person token or auth token that a resource names in <tt>presented_jti</tt> is verified by the resource, then by the PS, and in four-party by the AS, with a possible interaction in between, so a token valid at the resource can be expired by the time the AS sees it. And expiry propagates downward: a person token cannot outlive the agent token presented when it was requested, and an auth token cannot outlive the presented token <xref target="auth-token-structure"/>, so a token presented with thirty seconds left buys a thirty-second token.</t>
<t>The agent is the party to absorb this: it holds every token in the chain. An agent SHOULD refresh an agent, person, or auth token when fewer than five minutes remain before its <tt>exp</tt>, and SHOULD NOT present one inside that margin. The margin does not apply to a resource token, whose recommended lifetime is five minutes or less <xref target="resource-token-structure"/> and which the agent redeems at once. Five minutes is RECOMMENDED because it equals the recommended maximum lifetime of a resource token: a presented token with five minutes left is still valid whenever a resource token issued against it is redeemed.</t>
<t>Refresh runs from the top of the chain: the agent token first <xref target="agent-tokens"/>, then the person token <xref target="person-token-endpoint"/>, then the resource token and auth token against it. Refreshing in the other order produces a token capped by one about to expire.</t>
<t>Refresh is not required when the agent will present the token no further. An agent MAY also renew reactively, presenting an auth token until the resource answers <tt>401</tt> with <tt>expired_jwt</tt> (<xref target="I-D.hardt-httpbis-signature-key"/>) and then re-authorizing, for a request that is idempotent and can bear the extra round trip. The margin matters most for a token another party will name and pass on: presenting one inside the margin risks <tt>expired_presented_token</tt> downstream <xref target="token-endpoint-error-codes"/> after the resource has already accepted it.</t>
</section>
</section>
</section>

<section anchor="missions"><name>Mission</name>
<t>Missions are OPTIONAL. The protocol operates in all modes without missions. When used, missions provide scoped authorization contexts that guide an agent's work across multiple resource accesses — enabling scope pre-approval, reduced consent fatigue, and centralized audit. A mission is a natural-language description of what the agent intends to accomplish, proposed by the agent and approved by the PS. The PS uses the mission to evaluate every subsequent request in context — it is the only party with the mission content, the user relationship, and the full history of the agent's actions. Once approved, the agent names the mission's <tt>s256</tt> when it obtains person tokens <xref target="person-token-endpoint"/>, from where it flows into resource tokens and auth tokens.</t>
<t>The <tt>mission_endpoint</tt> is the agent's surface for the missions it owns. Parties other than the owning agent — the person, an administrator, a management service — read and manage missions at the <tt>mission_control_endpoint</tt> <xref target="ps-metadata"/> instead, under a different authentication model.</t>
<t>The agent has three operations, all of the same shape: it proposes, the person decides, and the PS returns a deferred response <xref target="deferred-responses"/> with clarification chat available <xref target="clarification-chat"/> whenever the person must be asked.</t>
<table>
<thead>
<tr>
<th>Request</th>
<th>Operation</th>
</tr>
</thead>

<tbody>
<tr>
<td><tt>POST {mission_endpoint}</tt></td>
<td>Propose a mission <xref target="mission-creation"/></td>
</tr>

<tr>
<td><tt>POST {mission_endpoint}/{mission_s256}</tt> with <tt>action: update</tt></td>
<td>Record a change in the work <xref target="mission-update"/></td>
</tr>

<tr>
<td><tt>POST {mission_endpoint}/{mission_s256}</tt> with <tt>action: completion</tt></td>
<td>Propose that the mission is finished <xref target="mission-completion"/></td>
</tr>
</tbody>
</table><t>The <tt>action</tt> member is REQUIRED on requests to a mission's own URL, and a PS MUST reject a request with a missing or unrecognized <tt>action</tt> with <tt>400 Bad Request</tt>. This is the same discriminator the pending route uses <xref target="agent-response-to-clarification"/>, for the same reason: it makes each POST self-describing and leaves the route extensible. Errors at these requests are defined in <xref target="mission-endpoint-errors"/>.</t>

<section anchor="mission-creation"><name>Mission Creation</name>
<t>The agent creates a mission by sending a proposal to the PS's <tt>mission_endpoint</tt>. The agent MUST make a signed POST with an HTTP Sig <xref target="http-message-signatures-profile"/>, presenting its agent token via the <tt>Signature-Key</tt> header under the <tt>jwt</tt> scheme.</t>
<t>The proposal includes a Markdown description of what the agent intends to accomplish, and MAY include a list of tools the agent wants to use and a list of resources it expects to access:</t>

<sourcecode type="json"><![CDATA[{
  "description": "# Plan Japan Vacation\n\n
    Plan and book a trip to Japan next month
    for 2 adults. Budget around $5k.
    Propose an itinerary before booking.",
  "tools": [
    {
      "name": "WebSearch",
      "description": "Search the web"
    },
    {
      "name": "BookFlight",
      "description": "Book flights"
    },
    {
      "name": "BookHotel",
      "description": "Book hotels"
    }
  ],
  "resources": [
    "https://flights.example",
    "https://hotels.example"
  ]
}
]]>
</sourcecode>
<t><strong><tt>resources</tt></strong> (OPTIONAL). An array of HTTPS URLs conforming to the Server Identifier requirements <xref target="server-identifiers"/>. The PS presents them to the person alongside the description, and issues a person token for each it approves in the approval response <xref target="mission-approval"/>, sparing the agent a separate request per resource. An agent MAY still obtain person tokens for other resources later <xref target="person-token-endpoint"/>, subject to the PS's policy; the list is not a limit on the mission.</t>
<t>The PS MAY return a <tt>202 Accepted</tt> deferred response <xref target="deferred-responses"/> if human review, clarification, or approval is needed. During this phase, the PS and user may engage in clarification chat <xref target="clarification-chat"/> with the agent to refine the mission scope, ask questions about the agent's intent, or negotiate which tools are needed. The PS or user may also modify the description — the approved mission MAY differ from the original proposal.</t>
</section>

<section anchor="mission-approval"><name>Mission Approval</name>
<t>When the PS approves the mission, it returns the approved mission — the <strong>mission blob</strong> — together with the mission's <tt>s256</tt> and a person token for each approved resource:</t>

<sourcecode type="http"><![CDATA[HTTP/1.1 200 OK
Content-Type: application/json

{
  "s256": "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
  "mission": "eyJhZ2VudCI6ImFhdXRoOmFzc2lzdGFudEBhZ2VudC5leGFtcGxlIiwiYXBwcm92...",
  "capabilities": [
    "interaction",
    "payment"
  ],
  "person_tokens": {
    "https://flights.example": "eyJhbGc...",
    "https://hotels.example": "eyJhbGc..."
  }
}
]]>
</sourcecode>
<t>The <tt>mission</tt> member decodes to the mission blob:</t>

<sourcecode type="json"><![CDATA[{
  "agent": "aauth:assistant@agent.example",
  "approved_at": "2026-04-07T14:30:00Z",
  "expires_at": "2026-05-07T14:30:00Z",
  "description": "# Plan Japan Vacation\n\n
    Plan and book a trip to Japan next month
    for 2 adults. Budget around $5k.
    Propose an itinerary before booking.",
  "approved_tools": [
    {
      "name": "WebSearch",
      "description": "Search the web"
    },
    {
      "name": "Read",
      "description": "Read files and web pages"
    }
  ],
  "approved_resources": [
    "https://flights.example",
    "https://hotels.example"
  ]
}
]]>
</sourcecode>
<t>Response members:</t>

<ul spacing="compact">
<li><tt>s256</tt> (REQUIRED): The mission identifier — the unpadded base64url encoding of the SHA-256 digest of the bytes <tt>mission</tt> decodes to.</li>
<li><tt>mission</tt> (REQUIRED): The mission blob, base64url-encoded without padding. An agent SHOULD verify <tt>s256</tt> against the decoded bytes before first use of the mission, and MAY skip verification where it trusts the PS unconditionally.</li>
<li><tt>capabilities</tt> (OPTIONAL): Array of capability strings (e.g., <tt>interaction</tt>, <tt>payment</tt>) that the PS can provide on behalf of the person for this session. The PS determines these based on whether it can currently reach the person — for example, via push notification, email, or an active session. The agent unions them with its own when constructing the <tt>AAuth-Capabilities</tt> request header <xref target="aauth-capabilities"/>. They describe this moment rather than a term of the mission, and are neither part of the blob nor covered by the digest.</li>
<li><tt>person_tokens</tt> (OPTIONAL): An object mapping resource identifiers to person tokens <xref target="person-tokens"/>, each carrying <tt>mission_s256</tt> set to <tt>s256</tt>. Present when the proposal named <tt>resources</tt>. A PS MAY omit a resource it declines to issue for; the agent MAY request one for it later and be refused individually. Each token carries its own <tt>exp</tt>, so no separate expiry is returned.</li>
</ul>
<t>The mission blob MUST include:</t>

<ul spacing="compact">
<li><tt>agent</tt>: The agent identifier (<tt>aauth:local@domain</tt>).</li>
<li><tt>approved_at</tt>: ISO 8601 timestamp of when the mission was approved. Ensures the <tt>s256</tt> is globally unique.</li>
<li><tt>description</tt>: Markdown string describing the approved mission scope.</li>
</ul>
<t>The mission blob MAY include:</t>

<ul spacing="compact">
<li><tt>expires_at</tt>: ISO 8601 timestamp after which the PS treats the mission as terminated. When absent, the mission runs until it is completed or revoked. Every PS decision path that acts on a mission MUST compare the current time to <tt>expires_at</tt> and MUST treat a mission past it as terminated <xref target="mission-status-errors"/>. The PS caps the person tokens and auth tokens it issues at <tt>expires_at</tt> <xref target="person-token-structure"/> and <xref target="auth-token-structure"/>, and the presented token carries that bound to an AS <xref target="ps-to-as-token-request"/>; a resource token's lifetime is independent of it <xref target="resource-token-structure"/>.</li>
<li><tt>approved_tools</tt>: Array of tool objects (each with <tt>name</tt> and <tt>description</tt>) that the agent may use without per-call permission at the PS's permission endpoint <xref target="permission-endpoint"/>. Nothing in the protocol enforces this list; see <xref target="why-tools-are-not-enforced"/>.</li>
<li><tt>approved_resources</tt>: Array of resource identifiers the person approved for this mission, drawn from the <tt>resources</tt> the proposal named. It records which resources were pre-approved, so an audit of the mission shows what the person agreed to before the agent began. It is not a limit: the agent MAY obtain person tokens for other resources during the mission, subject to the PS's policy, and those accesses appear in the mission log rather than in the blob.</li>
</ul>
<t>The member lists above are a floor, not a closed set. A PS MAY include additional members, and a companion specification MAY define them; a reader MUST ignore members it does not recognize <xref target="aauth-capabilities"/>. Because <tt>s256</tt> covers the bytes the PS persists, a blob carrying an additional member has a different identifier from one without — which is correct, since they are different missions. Member names in the mission blob are governed by this specification; a companion specification defining one SHOULD coordinate the name to avoid collision.</t>

<section anchor="mission-identifier"><name>Mission Identifier</name>
<t><tt>s256</tt> identifies the mission everywhere it appears — as the <tt>mission_s256</tt> claim of person, resource, and auth tokens, and as the <tt>mission_s256</tt> parameter of PS requests. The PS that approved the mission is named beside it: by the <tt>iss</tt> of a person token, the <tt>ps</tt> claim of a resource or auth token, and the PS a request is made to. The pair is the mission's identity; the blob carries no approver member, since the approver is always the PS.</t>
<t>It is a hash rather than an opaque identifier so that it is provable. An opaque identifier would name the mission but leave the PS free to attach it to any text afterwards. A digest binds every token carrying <tt>mission_s256</tt> to one specific mission, so the mission in the PS's log and the mission those tokens authorized are demonstrably the same. Verification is available to the agent at approval and to anyone holding the blob later.</t>
<t>The PS MUST compute <tt>s256</tt> over the exact bytes it persists as the mission blob, MUST return those same bytes as <tt>mission</tt>, and MUST serve them wherever it later exposes the mission for audit.</t>
<t>The approved description MAY differ from the proposal — the PS or user may refine, constrain, or expand the mission during review. The approved tools MAY be a subset of the proposed tools. The agent uses <tt>s256</tt> as <tt>mission_s256</tt> when requesting further person tokens <xref target="person-token-endpoint"/>.</t>
</section>
</section>

<section anchor="mission-log"><name>Mission Log</name>
<t>The approved mission description is immutable — the <tt>s256</tt> hash binds it permanently. Missions do not change; they accumulate context.</t>
<t>All agent interactions with the PS within a mission context form the <strong>mission log</strong>: token requests (with justifications), accepted updates <xref target="mission-update"/>, permission requests and responses, audit records, interaction requests, and clarification chats. The PS maintains this log as an ordered record of the agent's actions and the supervision decisions made. The mission log gives the PS the full history it needs to evaluate whether each new request is consistent with the mission's intent.</t>
<t>The agent names the mission when it requests a person token <xref target="person-token-endpoint"/>; the PS validates it and stamps <tt>mission_s256</tt> into the token, from where it flows into the resource token and the auth token. When the agent sends a resource token to its PS, the PS evaluates the request against the mission context and log history before federating with the resource's AS.</t>
</section>

<section anchor="mission-update"><name>Mission Update</name>
<t>Work changes. When what the agent is doing no longer matches the description the person approved, it records the change rather than proceeding silently:</t>

<sourcecode type="http"><![CDATA[POST /mission/dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk HTTP/1.1
Host: ps.example
Content-Type: application/json
Signature-Key: sig=jwt;jwt="eyJhbGc..."

{
  "action": "update",
  "description": "# Hotel unavailable\n\n
    The hotel in the itinerary has no availability.
    Proposing a comparable property two blocks away
    at a similar rate."
}
]]>
</sourcecode>

<ul spacing="compact">
<li><tt>action</tt> (REQUIRED): <tt>update</tt>.</li>
<li><tt>description</tt> (REQUIRED): A Markdown string describing what changed.</li>
</ul>
<t>The PS MAY accept the update on its own, or return a <tt>202 Accepted</tt> deferred response while the person reviews it. On acceptance it appends the update to the mission log and returns its <tt>s256</tt> — the unpadded base64url SHA-256 digest of the update's bytes as the PS persists them — so the sequence of accepted updates is verifiable, not merely stored:</t>

<sourcecode type="json"><![CDATA[{
  "s256": "Q2h1Y2sgSW50ZWdyaXR5IENoZWNr..."
}
]]>
</sourcecode>
<t>An update does not change the mission. The blob is immutable, <tt>mission_s256</tt> is unchanged, and every token carrying it remains valid — which is the point: the agent keeps working while the record catches up.</t>
<t>What the update changes is the context the PS evaluates against. From acceptance onward, the mission's meaning is the approved blob <strong>plus its accepted updates</strong>, and a party auditing the mission MUST read both. The blob alone records what the person approved at the outset, not what they approved in total.</t>
<t>An update may narrow or broaden the work. Both require the person's acceptance when the PS decides the change warrants it, and the PS applies the same judgment to an update that it applied to the proposal. When the work has changed enough that the original description no longer describes it, that is a new mission rather than an update, and the old one is terminated as <tt>superseded</tt> <xref target="mission-management"/>.</t>
</section>

<section anchor="mission-completion"><name>Mission Completion</name>
<t>When the agent believes the mission is complete, it proposes completion with a summary of what was accomplished:</t>

<sourcecode type="http"><![CDATA[POST /mission/dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk HTTP/1.1
Host: ps.example
Content-Type: application/json
Signature-Key: sig=jwt;jwt="eyJhbGc..."

{
  "action": "completion",
  "summary": "Booked flights and hotel for 7-14 May.
    Itinerary sent. Total $4,180."
}
]]>
</sourcecode>

<ul spacing="compact">
<li><tt>action</tt> (REQUIRED): <tt>completion</tt>.</li>
<li><tt>summary</tt> (REQUIRED): A Markdown string summarizing what the agent accomplished.</li>
</ul>
<t>The PS presents the summary to the person, returning a deferred response while they review. The person either accepts — the PS terminates the mission with reason <tt>completed</tt> and returns <tt>200 OK</tt> — or responds with follow-up questions via clarification chat <xref target="clarification-chat"/>, leaving the mission active. This is the most common mission lifecycle path.</t>
<t>The agent proposes completion; it does not declare it. Only the person's acceptance terminates the mission.</t>
</section>

<section anchor="mission-management"><name>Mission Management</name>
<t>A mission has one of two states:</t>

<ul spacing="compact">
<li><strong>active</strong>: The mission is in progress. The agent can make requests against it.</li>
<li><strong>terminated</strong>: The mission is permanently ended. The PS MUST reject requests with <tt>mission_terminated</tt>.</li>
</ul>
<t>A terminated mission MUST NOT return to <tt>active</tt>. A caller that needs to continue the work proposes a new mission.</t>
<t>The PS records why a mission terminated, alongside the mission rather than inside the immutable blob. This document defines the following reasons; the list is open, and a recipient that does not recognize a reason MUST retain the <tt>terminated</tt> state and treat the reason as an opaque audit value.</t>
<table>
<thead>
<tr>
<th>Reason</th>
<th>Meaning</th>
</tr>
</thead>

<tbody>
<tr>
<td><tt>completed</tt></td>
<td>The person accepted the agent's completion proposal <xref target="mission-completion"/></td>
</tr>

<tr>
<td><tt>revoked</tt></td>
<td>The person, the owning agent, or an authorized administrator withdrew the mission</td>
</tr>

<tr>
<td><tt>expired</tt></td>
<td>The mission reached its <tt>expires_at</tt> <xref target="mission-approval"/></td>
</tr>

<tr>
<td><tt>superseded</tt></td>
<td>The mission was replaced by another approved mission</td>
</tr>

<tr>
<td><tt>administrative</tt></td>
<td>An authorized administrator ended the mission under local policy</td>
</tr>
</tbody>
</table><t>A termination reason MUST NOT be exposed as a mission state or used to permit a later transition.</t>
<t>Reading a mission's status, terminating one, and querying delegation are operations for parties other than the owning agent, and belong at the <tt>mission_control_endpoint</tt> <xref target="ps-metadata"/>. They will be defined in a companion specification, along with the administrative principals that invoke them.</t>
</section>

<section anchor="mission-endpoint-errors"><name>Mission Endpoint Errors</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>The <tt>{mission_s256}</tt> path segment is malformed, or <tt>action</tt> is missing or unrecognized</td>
</tr>

<tr>
<td><tt>mission_not_found</tt></td>
<td>404</td>
<td>No such mission, or it is not this agent's</td>
</tr>

<tr>
<td><tt>mission_terminated</tt></td>
<td>403</td>
<td>This agent's mission, permanently ended <xref target="mission-status-errors"/></td>
</tr>
</tbody>
</table><t>A PS MUST return the same status, error, body, header set, and observably equivalent timing whether the mission does not exist or the authenticated agent does not own it, and MUST NOT disclose anything about a mission before authorization succeeds. Note that the natural arrangement — checking ownership only after a successful lookup — leaks the difference in timing.</t>
<t>The distinction matters because <tt>mission_s256</tt> travels in auth tokens to resources. Without this rule, a resource operator running an agent could POST a mission reference it had observed and learn from the response whether that mission was still live, reading mission status through a side channel instead of through the control plane, where the read would be authorized.</t>
<t>A terminated mission is deliberately distinguishable: the agent that owns it already knows it exists, and needs <tt>termination_reason</tt> to decide whether to propose a new mission.</t>
<t>A PS SHOULD rate-limit and security-log repeated failures, and SHOULD NOT retain raw mission references from failed requests longer than abuse correlation requires.</t>
</section>

<section anchor="mission-status-errors"><name>Mission Status Errors</name>
<t>When an agent makes a request to any PS endpoint with a <tt>mission_s256</tt> parameter referencing a mission that is no longer active, the PS MUST return an error:</t>

<sourcecode type="http"><![CDATA[HTTP/1.1 403 Forbidden
Content-Type: application/problem+json

{
  "error": "mission_terminated",
  "mission_status": "terminated",
  "termination_reason": "expired"
}
]]>
</sourcecode>
<table>
<thead>
<tr>
<th>Error</th>
<th>Mission Status</th>
<th>Meaning</th>
</tr>
</thead>

<tbody>
<tr>
<td><tt>mission_terminated</tt></td>
<td><tt>terminated</tt></td>
<td>The mission is permanently ended. The agent MUST stop acting on this mission.</td>
</tr>
</tbody>
</table><t><tt>termination_reason</tt> is OPTIONAL and carries a value from <xref target="mission-management"/>. It is one error rather than one per reason because the reason set is open: an agent keys its behaviour on <tt>mission_terminated</tt> and reads the reason for context — <tt>expired</tt> invites proposing a new mission, <tt>revoked</tt> does not.</t>
</section>
</section>

<section anchor="access-server-federation"><name>Access Server Federation</name>
<t>This section defines auth tokens and the mechanisms by which they are issued. The auth token is the end result of the authorization flow — a JWT issued by an access server, or by a PS in three-party access, that grants an agent access to a specific resource. This section covers the AS token endpoint, PS-AS federation, and the auth token structure.</t>

<section anchor="as-token-endpoint"><name>AS Token Endpoint</name>
<t>The AS evaluates resource policy and issues auth tokens. It accepts JSON POST requests.</t>

<section anchor="ps-to-as-token-request"><name>PS-to-AS Token Request</name>
<t>The PS MUST make a signed POST to the AS's <tt>auth_token_endpoint</tt>. The PS authenticates via an HTTP Sig <xref target="http-message-signatures-profile"/>.</t>
<t><strong>Request parameters:</strong></t>

<ul spacing="compact">
<li><tt>resource_token</tt> (REQUIRED): The resource token issued by the resource.</li>
<li><tt>agent_token</tt> (REQUIRED): The agent's agent token. For a parent-mediated sub-agent authorization, this is the parent (top-level) agent's token.</li>
<li><tt>presented_token</tt> (REQUIRED): The token named by the resource token's <tt>presented_jti</tt>, passed through from the agent's token request <xref target="ps-token-endpoint"/>. A person token carries the identity the resource saw — <tt>sub</tt>, <tt>tenant</tt>, and <tt>mission_s256</tt> when present — under the PS's signature; an auth token carries it under the signature of the server that issued it, which on a step-up in four-party is this AS. Its <tt>exp</tt> bounds the auth token the AS issues <xref target="auth-token-structure"/>. A PS MUST NOT present an expired token; it rejects the agent's token request with <tt>expired_presented_token</tt> <xref target="token-endpoint-error-codes"/>, and the agent obtains a fresh person token and a fresh resource token.</li>
<li><tt>subagent_token</tt> (OPTIONAL): A sub-agent's agent token, present when the PS federates a parent-mediated sub-agent authorization <xref target="sub-agents"/>. When present, the AS binds the issued auth token to the sub-agent, verifying <tt>resource_token</tt>'s <tt>agent_jkt</tt> against the <tt>subagent_token</tt>'s <tt>cnf.jwk</tt>.</li>
<li><tt>upstream_token</tt> (OPTIONAL): The <tt>upstream_token</tt> of the agent's token request, passed through <xref target="call-chaining"/>. The AS MUST verify it per <xref target="upstream-token-verification"/>. <tt>agent_token</tt> is then the intermediary's agent token.</li>
</ul>
<t>The resource token carries the person's identity as <tt>ps</tt> and <tt>sub</tt> <xref target="resource-token-structure"/>, and the presented token carries the same identity under its issuer's signature, so the AS needs no separate identity parameter and <tt>requirement=claims</tt> <xref target="requirement-claims"/> is reserved for claims beyond it.</t>
<t>The AS MUST verify <tt>presented_token</tt> against the resource token per step 3 of <xref target="resource-token-verification"/>, which is the same check the PS made. A person token's <tt>iss</tt> and an auth token's <tt>ps</tt> MUST be the PS that signed this request. The errors are those of that step: <tt>invalid_presented_token</tt>, <tt>expired_presented_token</tt>, and <tt>invalid_resource_token</tt> for a mismatch.</t>
<t><tt>agent_token</tt> remains REQUIRED even though the resource never sees an agent identifier. A resource deploys an AS because it wants policy evaluated, and an agent token MAY carry claims bearing on that decision — software attestation, platform integrity, secure enclave status, workload identity <xref target="agent-token-structure"/>. The resource enforces; the AS evaluates; posture goes to the evaluator.</t>
<t><strong>Example request:</strong></t>

<sourcecode type="http"><![CDATA[POST /token HTTP/1.1
Host: as.resource.example
Content-Type: application/json
Signature-Key: sig=jwks_uri;id="https://ps.example";
    dwk="aauth-person.json";kid="key-1"

{
  "resource_token": "eyJhbGc...",
  "agent_token": "eyJhbGc...",
  "presented_token": "eyJhbGc..."
}
]]>
</sourcecode>
</section>

<section anchor="as-response"><name>AS Response</name>
<t>The PS calls the AS token endpoint and follows the standard deferred response loop <xref target="deferred-responses"/>: it handles <tt>202</tt> and <tt>402</tt> responses and continues until it receives a <tt>200</tt> with an auth token or a terminal error.</t>
<t><strong>Direct grant response</strong> (<tt>200</tt>):</t>

<sourcecode type="json"><![CDATA[{
  "auth_token": "eyJhbGc...",
  "expires_in": 3600
}
]]>
</sourcecode>
<t>The AS MAY return <tt>202 Accepted</tt> with an <tt>AAuth-Requirement</tt> header indicating what is needed before it can issue an auth token:</t>

<ul spacing="compact">
<li><strong><tt>requirement=claims</tt></strong> <xref target="requirement-claims"/>: The AS needs identity claims. The body includes <tt>required_claims</tt>. The PS MUST provide the requested claims by POSTing to the <tt>Location</tt> URL. The AS cannot know what claims it needs until it has processed the resource token.</li>
<li><strong><tt>requirement=clarification</tt></strong> <xref target="requirement-clarification"/>: The AS needs a question answered. The PS triages who answers: itself (if mission context has the answer), the user, or the agent. The PS MAY pass the clarification down to the agent via a <tt>202</tt> response.</li>
<li><strong><tt>requirement=interaction</tt></strong> <xref target="requirement-responses"/>: The AS requires user interaction — for example, the user must authenticate at the AS to bind their PS, or the resource owner must approve access. The PS directs the user to the AS's interaction URL, or passes the interaction requirement back to the agent.</li>
<li><strong><tt>requirement=approval</tt></strong> <xref target="requirement-responses"/>: The AS is obtaining approval without requiring user direction.</li>
</ul>
<t><strong>Payment required</strong> (<tt>402</tt>):</t>
<t>The AS MAY return <tt>402 Payment Required</tt> when a billing relationship is required before it will issue auth tokens. The <tt>402</tt> response includes payment details per an applicable payment protocol such as x402 <xref target="x402"/> or the Payment scheme (<xref target="I-D.ryan-httpauth-payment"/>). The response MUST include a <tt>Location</tt> header for the PS to poll after payment is settled.</t>

<sourcecode type="http"><![CDATA[HTTP/1.1 402 Payment Required
Location: https://as.resource.example/token/pending/xyz
WWW-Authenticate: Payment id="x7Tg2pLq", method="stripe",
    request="eyJhbW91bnQiOiIxMDAw..."
]]>
</sourcecode>
<t>The PS settles payment per the indicated protocol and polls the <tt>Location</tt> URL. When payment is confirmed, the AS continues processing the token request — which may result in a <tt>200</tt> with an auth token, or a further <tt>202</tt> requiring claims, interaction, or approval.</t>
<t>The PS caches the billing relationship per AS. Future token requests from the same PS to the same AS skip the billing step. The payment protocol, settlement mechanism, and billing terms are out of scope for this specification.</t>
</section>

<section anchor="auth-token-delivery"><name>Auth Token Delivery</name>
<t>When the AS issues an auth token (<tt>200</tt> response), the PS MUST verify the auth token before returning it to the agent:</t>

<ol spacing="compact">
<li>Verify the auth token JWT signature using the AS's JWKS <xref target="jwks-discovery"/>.</li>
<li>Verify <tt>iss</tt> matches the AS the PS sent the token request to.</li>
<li>Verify <tt>aud</tt> matches the resource identified by the resource token's <tt>iss</tt>.</li>
<li>Verify <tt>cnf.jwk</tt> matches the agent's signing key.</li>
<li>Verify <tt>sub</tt> matches the directed identifier the PS issues for this person at this resource.</li>
<li>Verify <tt>scope</tt> is consistent with what was requested — not broader than the scope in the resource token.</li>
<li>Verify <tt>exp</tt> does not exceed the <tt>exp</tt> of the <tt>presented_token</tt> the PS presented <xref target="ps-to-as-token-request"/>.</li>
</ol>
<t>After verification, the PS returns the auth token to the agent. The agent presents the auth token to the resource via the <tt>Signature-Key</tt> header <xref target="auth-token-usage"/>. The resource verifies the auth token against the AS's JWKS <xref target="auth-token-verification"/>.</t>
<t>When the AS answers with a well-formed terminal error, the PS relays it: the response to the agent carries the AS's <tt>error</tt> value and status in the PS's own problem+json body <xref target="error-response-format"/>, so that an AS denial and a federation failure are distinguishable. When the PS cannot obtain a verifiable auth token at all — the AS is unreachable, times out, returns a malformed response, or returns an auth token that fails the verification above — the PS returns <tt>as_unreachable</tt> <xref target="token-endpoint-error-codes"/>. Either outcome reaches the agent on its pending request when the token request was deferred.</t>
<t>The agent receives the auth token from its trusted PS, so signature verification is not strictly required. However, agents SHOULD verify the auth token's signature to detect errors early. Agents MUST verify that <tt>aud</tt> and <tt>cnf</tt> match their own values.</t>
</section>
</section>

<section anchor="requirement-claims"><name>Claims Required</name>
<t>A server MUST use <tt>requirement=claims</tt> with a <tt>202 Accepted</tt> response when it needs identity claims to process a request. The response body MUST include a <tt>required_claims</tt> field containing an array of claim names.</t>

<sourcecode type="http"><![CDATA[HTTP/1.1 202 Accepted
Location: https://as.resource.example/token/pending/xyz
Retry-After: 0
Cache-Control: no-store
AAuth-Requirement: requirement=claims
Content-Type: application/json

{
  "status": "pending",
  "required_claims": ["email", "tenant"]
}
]]>
</sourcecode>
<t>The recipient MUST provide the requested claims by POSTing to the <tt>Location</tt> URL. The person is already identified by the presented token <xref target="ps-to-as-token-request"/>: <tt>sub</tt> is a claim of every person token and auth token, never a requested one. An AS MUST NOT request it, and a PS MUST NOT include it in the response. The recipient MUST include an HTTP Sig <xref target="http-message-signatures-profile"/> on the POST. Claims not recognized by the recipient SHOULD be ignored. This requirement is used by ASes to request identity claims from PSes during token issuance.</t>
</section>

<section anchor="ps-as-federation"><name>PS-AS Federation</name>
<t>The PS is the only entity that calls AS token endpoints. When the PS receives a resource token from an agent, the resource token's <tt>aud</tt> claim identifies where to send the token request. If <tt>aud</tt> matches the PS's own identifier, the PS issues an auth token asserting identity and consent for the requested scope (three-party). If <tt>aud</tt> identifies a different server (an AS), the PS discovers the AS's metadata at <tt>{aud}/.well-known/aauth-access.json</tt> <xref target="access-server-metadata"/> and calls the AS's <tt>auth_token_endpoint</tt> <xref target="as-token-endpoint"/> (four-party).</t>

<section anchor="ps-as-trust-establishment"><name>PS-AS Trust Establishment</name>
<t>Trust between the PS and AS may be pre-established out of band or emerge dynamically from the AS's response to the PS's first token request — AAuth does not require a separate registration step before the protocol can be used. The AS evaluates the token request and responds based on its current policy:</t>

<ul spacing="compact">
<li><strong>Pre-established</strong>: A business relationship configured between the PS and AS, potentially including payment terms, SLA, and compliance requirements. The AS recognizes the PS and processes the token request directly.</li>
<li><strong>Interaction</strong>: The AS returns <tt>202</tt> with <tt>requirement=interaction</tt>, directing the user to authenticate at the AS and confirm their PS. After this one-time binding, the AS trusts future requests from that PS for that user. This is the primary mechanism for establishing trust dynamically.</li>
<li><strong>Payment</strong>: The AS returns <tt>402</tt>, requiring the PS to establish a billing relationship before tokens will be issued. The PS settles payment per the indicated protocol and polls for completion. After billing is established, the AS trusts future requests from that PS.</li>
<li><strong>Claims only</strong>: The AS may trust any PS that can provide sufficient identity claims for a policy decision, without requiring a prior relationship.</li>
</ul>
<t>These mechanisms may compose: for example, the AS may first require payment (<tt>402</tt>), then interaction for user binding (<tt>202</tt>), then claims (<tt>202</tt>) before issuing an auth token. Each step uses the same <tt>Location</tt> URL for polling.</t>

<sourcecode type="ascii-art"><![CDATA[PS                        User                    AS
  |                         |                       |
  |  POST /token            |                       |
  |  resource_token,        |                       |
  |  agent_token,           |                       |
  |  presented_token        |                       |
  |------------------------------------------------>|
  |                         |                       |
  |  402 Payment Required   |                       |
  |  Location: /token/pending/xyz                   |
  |<------------------------------------------------|
  |                         |                       |
  |  [PS settles payment per indicated protocol]    |
  |                         |                       |
  |  GET /token/pending/xyz |                       |
  |------------------------------------------------>|
  |                         |                       |
  |  202 Accepted           |                       |
  |  requirement=interaction|                       |
  |  url=".../authorize/abc"|                       |
  |<------------------------------------------------|
  |                         |                       |
  |  direct user to URL     |                       |
  |------------------------>|                       |
  |                         |  authenticate, bind PS|
  |                         |---------------------->|
  |                         |                       |
  |  GET /token/pending/xyz |                       |
  |------------------------------------------------>|
  |                         |                       |
  |  202 Accepted           |                       |
  |  requirement=claims     |                       |
  |<------------------------------------------------|
  |                         |                       |
  |  POST /token/pending/xyz|                       |
  |  {email, tenant}        |                       |
  |------------------------------------------------>|
  |                         |                       |
  |  200 OK (auth_token)    |                       |
  |<------------------------------------------------|
  |                         |                       |
]]>
</sourcecode>
<t>{: #fig-mm-as-trust title="PS-AS Trust Establishment (all steps shown — most requests skip some)"}</t>
</section>

<section anchor="as-decision-logic"><name>AS Decision Logic (Non-Normative)</name>
<t>The following is a non-normative description of how an AS might evaluate a token request:</t>

<ol spacing="compact">
<li><strong>PS = AS (same entity)</strong>: Grant directly. The federation call is internal and trust is implicit. See <xref target="ps-as-collapse"/>.</li>
<li><strong>User has bound this PS at the AS</strong>: Apply the user's configured policy for this PS.</li>
<li><strong>PS is pre-established (enterprise agreement)</strong>: Apply the organization's configured policy.</li>
<li><strong>Resource is open or has a free tier</strong>: Grant with restricted scope or rate limits.</li>
<li><strong>Resource requires billing</strong>: Return <tt>402</tt> with payment details.</li>
<li><strong>Resource requires user binding</strong>: Return <tt>202</tt> with <tt>requirement=interaction</tt>.</li>
<li><strong>AS needs identity claims to decide</strong>: Return <tt>202</tt> with <tt>requirement=claims</tt>.</li>
<li><strong>Insufficient trust for requested scope</strong>: Return <tt>403</tt>.</li>
</ol>
<t>The AS is not required to follow this order. The decision logic is entirely at the AS's discretion based on resource policy.</t>
</section>

<section anchor="ps-as-collapse"><name>PS-AS Collapse</name>
<t>When the agent's PS and the resource's chosen AS are the same server (an instance of role collocation, see <xref target="roles"/>), federation collapses to a single internal evaluation. This is operationally similar to three-party access — no cross-server hop — but structurally different:</t>

<ul spacing="compact">
<li><strong>Three-party (PS authorization)</strong>: the resource has no AS; the resource token's <tt>aud</tt> is the PS, and the auth token has <tt>dwk: aauth-person.json</tt>. The resource trusts identity claims and applies its own policy.</li>
<li><strong>PS-AS collapse</strong>: the resource has chosen an AS that also operates as the agent's PS; the resource token's <tt>aud</tt> is the AS, and the auth token has <tt>dwk: aauth-access.json</tt>. The resource trusts the AS's policy verdict.</li>
</ul>
<t>The server applies user consent (its PS responsibility) and resource policy (its AS responsibility) in a single evaluation. Trust between PS and AS is implicit because they are the same entity. This is the common shape for an organization: its agents share one PS and its internal resources one AS, the PS gives centralized audit across every agent and mission, and federation is incurred only at the boundary, when an internal agent reaches an external resource.</t>
</section>
</section>

<section anchor="auth-tokens"><name>Auth Token</name>

<section anchor="auth-token-structure"><name>Auth Token Structure</name>
<t>An auth token is a JWT with <tt>typ: aa-auth+jwt</tt>. Its header and the claims <tt>iss</tt>, <tt>dwk</tt>, <tt>jti</tt>, <tt>iat</tt>, <tt>exp</tt>, and <tt>cnf</tt> are as defined in <xref target="common-claims"/>, with:</t>

<ul spacing="compact">
<li><tt>iss</tt>: The URL of the server that issued the auth token — an AS (four-party) or a PS (three-party)</li>
<li><tt>dwk</tt>: <tt>aauth-access.json</tt> when issued by an AS, <tt>aauth-person.json</tt> when issued by a PS</li>
<li><tt>cnf</tt>: <tt>jwk</tt> is the agent's public key</li>
<li><tt>exp</tt>: Auth tokens MUST NOT have a lifetime exceeding 1 hour, MUST NOT expire later than the agent token used to obtain them <xref target="re-authorization"/>, and MUST NOT expire later than the <tt>presented_token</tt> of the token request <xref target="ps-token-endpoint"/> and <xref target="ps-to-as-token-request"/> — a person token, which the PS capped at the mission's <tt>expires_at</tt> when <tt>mission_s256</tt> is present <xref target="person-token-structure"/>, or an auth token bounded the same way in its turn. When the token request carried <tt>upstream_token</tt> <xref target="call-chaining"/>, the auth token MUST NOT expire later than that token either. A PS-issued auth token carrying <tt>mission_s256</tt> MUST NOT expire later than the mission's <tt>expires_at</tt> <xref target="mission-approval"/>.</li>
</ul>
<t>Required payload claims specific to auth tokens:
- <tt>aud</tt>: The URL of the resource the agent is authorized to access.
- <tt>ps</tt>: The person server the person is represented by. Equal to <tt>iss</tt> when a PS issued the token. An intermediary acting as an agent routes its downstream token request here <xref target="call-chaining"/>.
- <tt>sub</tt>: Directed user identifier, copied from the resource token. An opaque string, unique within <tt>iss</tt>, that identifies the person. The PS SHOULD derive a pairwise pseudonymous value per resource (<tt>aud</tt>), so different resources see different values for the same person <xref target="directed-identifiers"/>.
An auth token carries no agent identifier and no delegation chain. <tt>cnf</tt> binds it to one key, and the resource enforces against <tt>sub</tt> and <tt>scope</tt>.</t>
<t>Optional payload claims:
- <tt>scope</tt>: Authorized scopes, as a space-separated string of scope values consistent with <xref target="RFC9068"/> Section 2.2.3
- <tt>account</tt>: The account the authorization is for, copied from the resource token <xref target="account-binding"/>.
- <tt>mission_s256</tt>: Copied from the resource token when it carried one. Present when the auth token was issued in the context of a mission.
- <tt>tenant</tt>: Tenant identifier per OpenID Connect Enterprise Extensions 1.0 <xref target="OpenID.Enterprise"/>, declaring the organization the person belongs to. <tt>(iss, tenant)</tt> identifies the organization. It is not part of the person's identifier, which is <tt>(iss, sub)</tt>.</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"/>) under the <tt>jwt</tt> scheme:</t>

<sourcecode type="http"><![CDATA[Signature-Key: sig=jwt;
    jwt="eyJhbGciOiJFZDI1NTE5IiwidHlwIjoiYWEtYXV0aCtqd3QiLCJraWQiOiJhcy1rZXktMSJ9..."
]]>
</sourcecode>
<t>Once an auth token has been issued for a resource, the agent presents the auth token (not the agent token) via <tt>Signature-Key</tt> on subsequent requests to that resource. The auth token's <tt>cnf.jwk</tt> is the same key that signed the request, so HTTP Message Signature verification proceeds identically to the agent-token case.</t>
</section>

<section anchor="auth-token-verification"><name>Auth Token Verification</name>
<t>A valid JWT signature alone is not a complete AAuth authorization check — both JWT trust and request-context binding must pass.</t>

<section anchor="jwt-trust-verification"><name>JWT Trust Verification</name>

<ol spacing="compact">
<li>Verify the token per <xref target="common-verification"/>, with <tt>typ</tt> <tt>aa-auth+jwt</tt> and <tt>dwk</tt> <tt>aauth-access.json</tt> (auth token from an AS) or <tt>aauth-person.json</tt> (auth token from a PS).</li>
</ol>
</section>

<section anchor="request-context-binding"><name>Request-Context Binding</name>

<ol spacing="compact" start="2">
<li>Verify <tt>aud</tt> matches the resource's own identifier.</li>
<li><tt>cnf.jwk</tt> is REQUIRED. If it is absent, or if its JWK is missing <tt>kty</tt> or the members required for that key type (e.g., <tt>crv</tt> and <tt>x</tt> for OKP keys; <tt>crv</tt>, <tt>x</tt>, and <tt>y</tt> for EC keys; <tt>n</tt> and <tt>e</tt> for RSA keys), reject the token as structurally incomplete before attempting key decoding. If present but not parseable as a supported public key, reject it as invalid key material. Otherwise verify <tt>cnf.jwk</tt> matches the key used to sign the HTTP request.</li>
<li>Verify <tt>sub</tt> is present, and that <tt>(iss, sub)</tt> matches or establishes the resource's record for this person <xref target="trust-posture-in-ps-asserted-access"/>.</li>
</ol>
</section>
</section>

<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>SHOULD verify the auth token JWT signature using the issuer's JWKS (the AS in four-party, or the PS in three-party). The agent trusts its PS, so signature verification is not required but is RECOMMENDED to detect errors early.</li>
<li>Verify <tt>iss</tt> matches the resource token's <tt>aud</tt> claim.</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>sub</tt> matches the value in the token it presented to that resource.</li>
</ol>
</section>

<section anchor="upstream-token-verification"><name>Upstream Token Verification</name>
<t>An <tt>upstream_token</tt> is a person token or an auth token. The recipient reads <tt>typ</tt> to tell which, and rejects any other <tt>typ</tt> with <tt>invalid_upstream_token</tt>. Accepting a person token here does not stand it in for an auth token <xref target="person-token-not-authorization"/>: the parameter is evidence of who the intermediary is acting for, and grants nothing by itself.</t>
<t>The intermediary's agent token is the one that signed the request at the PS, presented in the <tt>Signature-Key</tt> header, and the <tt>agent_token</tt> parameter at the AS, where the PS signed the request <xref target="ps-to-as-token-request"/>. When the PS or AS receives an <tt>upstream_token</tt> parameter in a call chaining request:</t>

<ol spacing="compact">
<li>Verify the upstream token per Person Token Verification <xref target="person-token-verification"/> or Auth Token Verification <xref target="auth-token-verification"/>, with these substitutions: <tt>aud</tt> MUST equal the intermediary's identifier rather than the verifier's own; <tt>cnf.jwk</tt> is the calling agent's key and is not compared with the key that signed this request, which is the intermediary's; and for an auth token the resource's record check on <tt>sub</tt> does not apply. A token that fails is rejected with <tt>invalid_upstream_token</tt>, <tt>expired_upstream_token</tt> when only <tt>exp</tt> fails, or <tt>revoked_upstream_token</tt> when the recipient holds a revocation for it <xref target="token-revocation"/>.</li>
<li>Verify the issuer. At the PS: a person token's <tt>iss</tt> MUST be this PS; an auth token's <tt>ps</tt> MUST be this PS, and its <tt>iss</tt> MUST be this PS or an AS this PS presented a person token to for that token's <tt>aud</tt> and <tt>sub</tt> <xref target="ps-to-as-token-request"/>. At the AS: a person token's <tt>iss</tt>, or an auth token's <tt>ps</tt>, MUST be the PS that signed the request. The AS does not check an auth token's <tt>iss</tt> beyond verifying its signature; the PS has already done so.</li>
<li>Verify the upstream token's <tt>aud</tt> equals the <tt>iss</tt> of the intermediary's agent token. The intermediary is its own agent provider <xref target="intermediary-agent-identity"/>, so this is the one comparison that ties the token the calling agent presented to the party now making the downstream request. A mismatch is rejected with <tt>invalid_upstream_token</tt>.</li>
<li>At the PS, identify the calling agent from its own records: the agent it issued the upstream person token to, or the one it issued the person token to that the upstream auth token was obtained with. If the PS has revoked that agent's agent token or its binding to the person <xref target="agent-person-binding"/>, it MUST reject the request with <tt>revoked_upstream_token</tt>, whether or not it has revoked the upstream token itself. A PS that cannot identify the calling agent MUST reject the request with <tt>invalid_upstream_token</tt>.</li>
<li>The PS evaluates the request against the mission and its supervision policy, based on the upstream token's claims and mission context. The resulting downstream authorization is not required to be a subset of any upstream authorization — see <xref target="call-chaining"/>.</li>
</ol>
</section>
</section>
</section>

<section anchor="agent-delegation"><name>Agent Delegation</name>
<t>Agent delegation covers the scenarios where more than one agent is involved in fulfilling a request: a resource that acts as an agent to call a downstream resource (call chaining), and an orchestrating agent that spawns sub-agents.</t>

<section anchor="multi-hop"><name>Multi-Hop Resource Access</name>
<t>This section defines how resources act as agents (an instance of role collocation, see <xref target="roles"/>) to access downstream resources on behalf of the original caller. In multi-hop scenarios, a resource that receives an authorized request needs to access another resource to fulfill that request. The resource acts as an agent — it has its own agent identity and signing key — and routes the downstream authorization to obtain an auth token for the downstream resource.</t>

<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 — the intermediary. The upstream token is a token the calling agent presented in the <tt>Signature-Key</tt> header of a request the intermediary served: a person token when the intermediary served on the person's identity <xref target="overview-person-identity"/>, an auth token when it required authorization. A person token the intermediary answered with a challenge for an auth token is not one: the request it came on was not served <xref target="person-token-not-authorization"/>.</t>
<t>An intermediary MAY present the same upstream token for any number of downstream requests until it expires. Downstream access does not outlive it: a person token issued with <tt>upstream_token</tt> expires no later than the upstream token <xref target="person-token-structure"/>, and so does an auth token issued on a request carrying one <xref target="auth-token-structure"/>. Once the upstream token has expired the intermediary uses a later token from the calling agent, which it receives on the agent's next request. A pending downstream request <xref target="deferred-responses"/> whose upstream token expires before it completes ends with <tt>expired</tt> <xref target="polling-error-codes"/>, and the intermediary starts over with a later upstream token.</t>
<t>The intermediary routes the downstream token requests to the person server the upstream token names: the <tt>iss</tt> of a person token, the <tt>ps</tt> of an auth token. The <tt>ps</tt> claim in the intermediary's own agent token, if it has one, is NOT used for this routing — it names the intermediary's person server, not the person's.</t>
<t>The intermediary first obtains a person token for the downstream resource, presenting the upstream token as <tt>upstream_token</tt> <xref target="person-token-endpoint"/>. It then presents that person token at the downstream resource, receives a resource token, and sends it to the same person server's auth token endpoint, along with the person token as <tt>presented_token</tt> and the upstream token as <tt>upstream_token</tt> <xref target="ps-token-endpoint"/>. The PS evaluates the downstream request against the mission context when the upstream token carries <tt>mission_s256</tt>.</t>
<t>In every case the intermediary signs the downstream token request with its <strong>own</strong> key, presenting its own agent token via the <tt>Signature-Key</tt> header <xref target="http-message-signatures-profile"/>. The <tt>upstream_token</tt> is a body parameter — it is neither presented via <tt>Signature-Key</tt> nor used as the signing key. Its <tt>aud</tt> is the intermediary and its <tt>cnf</tt> is the calling agent's key, not the intermediary's, and it serves only as evidence of who the intermediary is acting for. The signature the recipient verifies is therefore always the intermediary's, over its own key.</t>
<t>The recipient evaluates the downstream request per <xref target="upstream-token-verification"/>.</t>

<section anchor="intermediary-agent-identity"><name>Intermediary Agent Identity</name>
<t>An intermediary MUST be its own agent provider. It MUST publish agent metadata at <tt>/.well-known/aauth-agent.json</tt> on its own origin, with <tt>issuer</tt> equal to the <tt>issuer</tt> of its resource metadata <xref target="resource-metadata"/>, and MUST sign downstream token requests with an agent token it issued to itself. That agent token's <tt>iss</tt> is therefore the intermediary's resource identifier, and its <tt>sub</tt> is an agent identifier whose <tt>domain</tt> is the intermediary's host <xref target="agent-identifiers"/>.</t>
<t>This is how a recipient knows the intermediary is the party the calling agent presented the upstream token to. The upstream token's <tt>aud</tt> is a resource identifier, and the only resource-scoped identifier a signed downstream request carries is the <tt>iss</tt> of the agent token that signed it; step 3 of <xref target="upstream-token-verification"/> compares the two. Resolving the agent token's signing key from <tt>{iss}/.well-known/aauth-agent.json</tt> proves that the key belongs to that origin. An agent token issued by any other agent provider names the provider, not the resource, and nothing in it ties the signer to the upstream token's <tt>aud</tt>, so a recipient rejects the request with <tt>invalid_upstream_token</tt>.</t>
<t>An intermediary acts for every person whose requests it fulfills, so its agent token is not bound to one person <xref target="agent-person-binding"/>. The PS issues for the person the upstream token identifies <xref target="person-token-endpoint"/>, never for a person bound to the intermediary. An intermediary MAY use one agent identifier for all the requests it chains; the PS does not key person resolution or policy on it.</t>
</section>

<section anchor="directed-sub-chaining"><name>Directed Identifiers Across a Chain</name>
<t>The <tt>sub</tt> of an auth token is a directed identifier: a PS SHOULD issue a pairwise pseudonymous value per resource, so that two resources serving the same person cannot correlate them by comparing tokens <xref target="auth-tokens"/> and <xref target="directed-identifiers"/>. Identity is the pair <tt>(iss, sub)</tt> — a <tt>sub</tt> minted by one issuer for one audience carries no meaning under a different issuer for a different audience.</t>
<t>A downstream issuer sees the upstream <tt>sub</tt> in the <tt>upstream_token</tt> it is handed. It MUST NOT carry that value forward:</t>

<ol spacing="compact">
<li>An issuer MUST NOT copy a directed <tt>sub</tt> from an upstream token into a token it issues.</li>
<li>The <tt>sub</tt> it issues is the directed identifier for the person at the downstream resource, taken from the downstream resource token, which the resource copied from the person token the PS issued for that resource.</li>
</ol>
<t>Because the intermediary obtains a person token for the downstream resource before calling it <xref target="person-token-endpoint"/>, the PS has already minted a downstream-directed identifier by the time the resource token exists. The chain never needs to carry a <tt>sub</tt> forward, and never leaves a downstream token without one.</t>
<t>Copying instead would fail in both directions at once. The value would be meaningless under the new issuer, so the downstream resource would either misidentify the person or key state to an identifier no one can resolve; and the same string appearing at two resources is exactly the correlation handle pairwise identifiers exist to prevent, handed to a party the user never consented to share it with.</t>
<t>Note that downstream authorization is not required to be a subset of the upstream scopes. A downstream resource may have capabilities that are orthogonal to the upstream resource — for example, a flight booking API that calls a payment processor needs the payment processor to charge a card, an operation the user and original agent could never perform directly. The downstream resource's scope is constrained by its own AS policy and the PS's evaluation of the mission context, not by the upstream token's scope. The PS provides the supervision constraint — it evaluates each hop independently and can deny requests that fall outside the mission or the user's intent — where a formal subset rule would prevent legitimate delegation chains.</t>
</section>
</section>

<section anchor="interaction-chaining"><name>Interaction Chaining</name>
<t>When the PS or AS 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>
<t>When a resource acting as an agent receives a <tt>202 Accepted</tt> response with <tt>AAuth-Requirement: requirement=interaction</tt>, 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>
</section>
</section>

<section anchor="sub-agents"><name>Sub-Agents</name>
<t>Agent platforms increasingly spawn short-lived sub-agents — workers or tool-specific helpers — under an orchestrating parent agent. AAuth represents a sub-agent as an agent whose agent token carries a <tt>parent_agent</tt> claim identifying its parent. The user consents to the parent; sub-agents operate under that consent without per-spawn re-prompting, while remaining individually identifiable for audit and revocation.</t>

<section anchor="sub-agent-identity"><name>Sub-Agent Identity</name>
<t>A sub-agent has its own agent identity — its own <tt>aauth:local@domain</tt> identifier and signing key, issued by its parent's agent provider, exactly like a top-level agent. The <tt>iss</tt> of a sub-agent's agent token MUST equal the <tt>iss</tt> of its parent's, and a PS MUST reject a <tt>subagent_token</tt> whose <tt>iss</tt> differs from that of the signing agent's token with <tt>invalid_subagent_token</tt>. Two things distinguish it:</t>

<ul spacing="compact">
<li><strong><tt>parent_agent</tt> claim</strong>: the sub-agent's agent token includes <tt>parent_agent</tt> set to the parent agent's identifier. Its presence is the authoritative marker of sub-agent status.</li>
<li><strong>Local-part naming</strong>: the sub-agent's <tt>local</tt> part MUST be the parent's <tt>local</tt> part followed by <tt>+</tt> and a non-empty discriminator <xref target="agent-identifiers"/> — for example <tt>aauth:planner.7f3c+search1@vendor.example</tt>. For protocol decisions, verifiers rely on <tt>parent_agent</tt>, not on parsing the local part; the naming is for operational readability (e.g., logs).</li>
</ul>

<sourcecode type="json"><![CDATA[{
  "iss": "https://vendor.example",
  "dwk": "aauth-agent.json",
  "sub": "aauth:planner.7f3c+search1@vendor.example",
  "cnf": { "jwk": { "kty": "OKP", "crv": "Ed25519",
                    "x": "...", "alg": "Ed25519" } },
  "ps":  "https://ps.example",
  "parent_agent": "aauth:planner.7f3c@vendor.example"
}
]]>
</sourcecode>
<t>Acquisition of a sub-agent token from the agent provider is platform-dependent and is described in <xref target="I-D.hardt-aauth-bootstrap"/>, parallel to top-level agent token acquisition.</t>
</section>

<section anchor="single-level-depth"><name>Single-Level Depth</name>
<t>Delegation is at most one level deep: a top-level agent may have sub-agents, but a sub-agent MUST NOT have sub-agents of its own. Two rules enforce this:</t>

<ul spacing="compact">
<li>A PS MUST reject a token request signed by an agent whose agent token has a <tt>parent_agent</tt> claim — a sub-agent cannot request authorization on its own behalf or on behalf of a further sub-agent.</li>
<li>An agent provider MUST NOT issue a sub-agent token whose parent (<tt>parent_agent</tt>) is itself a sub-agent.</li>
</ul>
<t>For genuinely deeper workflows, AAuth already provides chained top-level agents <xref target="call-chaining"/>: each hop is an independent principal with its own grant, rather than recursive sub-agent spawning.</t>
</section>

<section anchor="parent-mediated-authorization"><name>Parent-Mediated Authorization</name>
<t>A sub-agent MUST NOT call the PS directly. Instead, the parent obtains auth tokens on the sub-agent's behalf:</t>

<ol spacing="compact">
<li>The parent obtains a person token for the sub-agent by POSTing to the PS's <tt>person_token_endpoint</tt> with <tt>subagent_token</tt> <xref target="person-token-endpoint"/>; the issued token's <tt>cnf</tt> is the sub-agent's key. It passes the person token to the sub-agent out of band (for example, via IPC).</li>
<li>The sub-agent presents that person token at the resource and obtains a resource token bound to its own key <xref target="resource-tokens"/>, exactly as a top-level agent would. It passes the resource token back to its parent.</li>
<li>The parent POSTs to the PS's <tt>auth_token_endpoint</tt>, signing the request with its own key and presenting its own agent token via the <tt>Signature-Key</tt> header. The request body includes <tt>resource_token</tt> (the sub-agent's resource token), <tt>presented_token</tt> (the token the sub-agent presented to the resource: the person token from step 1, or the sub-agent's auth token on a step-up), and <tt>subagent_token</tt> (the sub-agent's agent token).</li>
<li><t>The PS processes this as an authorization request from the parent <xref target="ps-token-endpoint"/>:</t>

<ul spacing="compact">
<li>It verifies the HTTP Message Signature against the parent's <tt>cnf.jwk</tt>.</li>
<li>It verifies the <tt>subagent_token</tt> <xref target="agent-token-verification"/> and that its <tt>parent_agent</tt> names the parent — the agent that signed the request.</li>
<li>It verifies the <tt>resource_token</tt> is bound to the sub-agent's key: <tt>agent_jkt</tt> matches the <tt>subagent_token</tt>'s <tt>cnf.jwk</tt>, not the signing key <xref target="resource-token-verification"/>.</li>
<li>It evaluates the parent's grant for the requested scope, exactly as for a direct request from the parent. If the user has already consented, the response is immediate; otherwise consent surfaces for the parent as usual.</li>
</ul></li>
<li>On success the issuer — the PS in three-party, or the AS in four-party — issues an auth token bound to the sub-agent's key (<tt>cnf</tt> = the sub-agent's <tt>jwk</tt>). In four-party, the PS federates by passing the parent as <tt>agent_token</tt> and the sub-agent as <tt>subagent_token</tt> to the AS <xref target="as-token-endpoint"/>, so the AS records the parent authoritatively from those tokens. The parent passes the auth token to the sub-agent, which presents it to the resource signing with its own key.</li>
</ol>
<t>The sub-agent relationship is recorded by the PS, which issued both tokens and holds the <tt>parent_agent</tt> binding. It does not appear in the tokens the resource sees.</t>
<t>Because every sub-agent authorization passes through the parent, the parent retains control — it can refuse, attenuate, or rate-limit — and revocation propagates naturally: revoking the parent's grant causes the next sub-agent authorization to fail, while existing auth tokens expire normally (≤1 hour).</t>
</section>
</section>
</section>

<section anchor="protocol-primitives"><name>Protocol Primitives</name>
<t>This section is the normative reference for the mechanisms the preceding sections use: identifiers, metadata documents, HTTP message signatures, key discovery, the common JWT profile, requirement responses, capabilities, deferred responses, error responses, scopes, account binding, and token revocation. Context for each is given where it is first used.</t>

<section anchor="identifiers-and-discovery"><name>Identifiers</name>

<section anchor="server-identifiers"><name>Server Identifiers</name>
<t>The <tt>issuer</tt> values in metadata documents that identify agent providers, resources, access servers, and person 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="endpoint-and-other-urls"><name>Endpoint and Other URLs</name>
<t>The <tt>auth_token_endpoint</tt>, <tt>person_token_endpoint</tt>, <tt>authorization_endpoint</tt>, <tt>mission_endpoint</tt>, and <tt>callback_endpoint</tt> values MUST use the <tt>https</tt> scheme and MUST NOT contain a query string or a fragment. 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>
<t>One exception: when <tt>localhost_callback_allowed</tt> is <tt>true</tt> in the agent's metadata, the agent MAY use a loopback callback URL with the <tt>http</tt> scheme (<tt>http://localhost</tt> or <tt>http://127.0.0.1</tt>, any port) as the <tt>callback</tt> parameter to the interaction endpoint, in place of its <tt>callback_endpoint</tt>.</t>
</section>
</section>

<section anchor="metadata-documents"><name>Metadata Documents</name>
<t>Participants publish metadata at well-known URLs (<xref target="RFC8615"/>).</t>
<t>When fetching a metadata document, implementations MUST verify that it contains an <tt>issuer</tt> member, and that the <tt>issuer</tt> value matches the URL the document was retrieved from (the URL minus the <tt>/.well-known/{dwk}</tt> suffix), compared by byte equality. A document with no <tt>issuer</tt> MUST be rejected with <tt>issuer_missing</tt>; one whose <tt>issuer</tt> does not match MUST be rejected with <tt>issuer_mismatch</tt> (<xref target="I-D.hardt-httpbis-signature-key"/>). This is the check <xref target="RFC8414"/>, Section 3.3 requires of authorization server metadata, and it prevents a document hosted at one domain from claiming the <tt>issuer</tt> of another.</t>
<t>The following fields are defined identically across all four metadata documents (<tt>aauth-agent.json</tt>, <tt>aauth-resource.json</tt>, <tt>aauth-person.json</tt>, <tt>aauth-access.json</tt>):</t>
<table>
<thead>
<tr>
<th>Field</th>
<th>Requirement</th>
<th>Description</th>
</tr>
</thead>

<tbody>
<tr>
<td><tt>issuer</tt></td>
<td>REQUIRED</td>
<td>The server's HTTPS URL. MUST match the URL the document was fetched from. Placed in the <tt>iss</tt> claim of JWTs issued by this server.</td>
</tr>

<tr>
<td><tt>jwks_uri</tt></td>
<td>REQUIRED (see per-role)</td>
<td>URL to the server's JSON Web Key Set.</td>
</tr>

<tr>
<td><tt>accept_signature_algs</tt></td>
<td>OPTIONAL</td>
<td>JSON array of fully-specified JWS algorithm identifiers the server's verifier accepts, exactly the set. Same semantics as the <tt>Accept-Signature-Alg</tt> response header (<xref target="I-D.hardt-httpbis-signature-key"/>). One list per server, covering every endpoint. A server MAY omit it.</td>
</tr>

<tr>
<td><tt>name</tt></td>
<td>OPTIONAL</td>
<td>Human-readable display name.</td>
</tr>

<tr>
<td><tt>description</tt></td>
<td>OPTIONAL</td>
<td>Markdown string describing the server, for display at consent screens or dashboards. Implementations MUST sanitize before rendering.</td>
</tr>

<tr>
<td><tt>logo_uri</tt></td>
<td>OPTIONAL</td>
<td>URL to the server's logo. MUST use <tt>https</tt>.</td>
</tr>

<tr>
<td><tt>logo_dark_uri</tt></td>
<td>OPTIONAL</td>
<td>URL to the server's logo for dark backgrounds. MUST use <tt>https</tt>.</td>
</tr>

<tr>
<td><tt>documentation_uri</tt></td>
<td>OPTIONAL</td>
<td>URL with developer documentation. MUST use <tt>https</tt>.</td>
</tr>

<tr>
<td><tt>tos_uri</tt></td>
<td>OPTIONAL</td>
<td>URL to terms of service. MUST use <tt>https</tt>.</td>
</tr>

<tr>
<td><tt>policy_uri</tt></td>
<td>OPTIONAL</td>
<td>URL to privacy policy. MUST use <tt>https</tt>.</td>
</tr>
</tbody>
</table><t>AAuth uses <tt>issuer</tt> rather than the <tt>resource</tt> field of RFC 9728, and unprefixed field names rather than RFC 9728's <tt>resource_</tt>-prefixed forms <xref target="why-issuer-not-resource"/>.</t>
<t>The per-role sections below list role-specific fields and role-specific requirement differences.</t>

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

<sourcecode type="json"><![CDATA[{
  "issuer": "https://agent.example",
  "jwks_uri": "https://agent.example/.well-known/jwks.json",
  "name": "Example AI Assistant",
  "description": "**Example AI Assistant** drafts and sends email on your behalf.",
  "logo_uri": "https://agent.example/logo.png",
  "logo_dark_uri": "https://agent.example/logo-dark.png",
  "documentation_uri": "https://agent.example/docs",
  "callback_endpoint": "https://agent.example/callback",
  "event_endpoint": "https://agent.example/events",
  "localhost_callback_allowed": true,
  "tos_uri": "https://agent.example/tos",
  "policy_uri": "https://agent.example/privacy"
}
]]>
</sourcecode>
<t>Role-specific fields:</t>

<ul spacing="compact">
<li><tt>issuer</tt> (REQUIRED): The agent provider's HTTPS URL (the <tt>domain</tt> in agent identifiers it issues). Placed in the <tt>iss</tt> claim of agent tokens.</li>
<li><tt>jwks_uri</tt> (REQUIRED): URL to the agent provider's JSON Web Key Set.</li>
<li><tt>callback_endpoint</tt> (OPTIONAL): The agent's HTTPS callback endpoint URL <xref target="user-interaction"/>.</li>
<li><tt>event_endpoint</tt> (OPTIONAL): HTTPS URL at which the AP receives event tokens from resources. Required if the AP supports AAuth Events (<xref target="I-D.hardt-aauth-events"/>).</li>
<li><tt>localhost_callback_allowed</tt> (OPTIONAL): Boolean. Default: <tt>false</tt>.</li>
</ul>
</section>

<section anchor="ps-metadata"><name>Person Server Metadata</name>
<t>Published at <tt>/.well-known/aauth-person.json</tt>:</t>

<sourcecode type="json"><![CDATA[{
  "issuer": "https://ps.example",
  "name": "Example Person Server",
  "description": "**Example Person Server** — manage which agents act for you and review what they do.",
  "logo_uri": "https://ps.example/logo.png",
  "logo_dark_uri": "https://ps.example/logo-dark.png",
  "documentation_uri": "https://ps.example/docs",
  "tos_uri": "https://ps.example/tos",
  "policy_uri": "https://ps.example/privacy",
  "auth_token_endpoint": "https://ps.example/token",
  "person_token_endpoint": "https://ps.example/person",
  "mission_endpoint": "https://ps.example/mission",
  "permission_endpoint": "https://ps.example/permission",
  "audit_endpoint": "https://ps.example/audit",
  "interaction_endpoint": "https://ps.example/interaction",
  "mission_control_endpoint": "https://ps.example/mission-control",
  "jwks_uri": "https://ps.example/.well-known/jwks.json"
}
]]>
</sourcecode>
<t>Role-specific fields:</t>

<ul spacing="compact">
<li><tt>issuer</tt> (REQUIRED): The PS's HTTPS URL. Placed in the <tt>iss</tt> claim of JWTs issued by the PS.</li>
<li><tt>jwks_uri</tt> (REQUIRED): URL to the PS's JSON Web Key Set.</li>
<li><tt>auth_token_endpoint</tt> (REQUIRED): URL where agents send token requests <xref target="ps-token-endpoint"/>.</li>
<li><tt>person_token_endpoint</tt> (REQUIRED): URL where agents request a person token for a resource <xref target="person-token-endpoint"/>.</li>
<li><tt>mission_endpoint</tt> (OPTIONAL): URL where an agent proposes, updates, and completes the missions it owns <xref target="missions"/>. A mission's own URL is <tt>{mission_endpoint}/{mission_s256}</tt>.</li>
<li><tt>permission_endpoint</tt> (OPTIONAL): URL where agents request permission for actions not governed by a remote resource <xref target="permission-endpoint"/>.</li>
<li><tt>audit_endpoint</tt> (OPTIONAL): URL where agents log actions performed <xref target="audit-endpoint"/>.</li>
<li><tt>interaction_endpoint</tt> (OPTIONAL): URL where agents relay interactions to the user through the PS <xref target="interaction-endpoint"/>.</li>
<li><tt>mission_control_endpoint</tt> (OPTIONAL): URL of the PS's mission control plane, where parties other than the owning agent read and manage missions <xref target="mission-management"/>. Its authentication model, operations, and responses are out of scope for this document. A PS MAY also use it for a deployment's human-facing administrative interface. <strong>Editor's note:</strong> a mission control companion specification is TBD.</li>
<li><tt>revocation_endpoint</tt> (RECOMMENDED): URL where an agent provider revokes an agent token it issued, and where a resource revokes a resource token this PS holds <xref target="token-revocation"/>.</li>
<li><tt>scopes_supported</tt> (RECOMMENDED): Array of scope values the PS supports, including identity scopes (e.g., <tt>openid</tt>, <tt>profile</tt>, <tt>email</tt>) and enterprise scopes (e.g., <tt>tenant</tt>, <tt>groups</tt>, <tt>roles</tt>).</li>
<li><tt>claims_supported</tt> (RECOMMENDED): Array of identity claim names the PS can provide (e.g., <tt>sub</tt>, <tt>email</tt>, <tt>name</tt>, <tt>tenant</tt>).</li>
</ul>
<t>The four REQUIRED fields (<tt>issuer</tt>, <tt>jwks_uri</tt>, <tt>auth_token_endpoint</tt>, <tt>person_token_endpoint</tt>) are the whole of what a conformant PS publishes. The OPTIONAL endpoints add missions, permission checks, audit, and the relay channel to the person; they do not add conformance.</t>
</section>

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

<sourcecode type="json"><![CDATA[{
  "issuer": "https://as.resource.example",
  "name": "Example Access Server",
  "description": "**Example Access Server** — issues access for the Example resource.",
  "logo_uri": "https://as.resource.example/logo.png",
  "logo_dark_uri": "https://as.resource.example/logo-dark.png",
  "documentation_uri": "https://as.resource.example/docs",
  "tos_uri": "https://as.resource.example/tos",
  "policy_uri": "https://as.resource.example/privacy",
  "auth_token_endpoint": "https://as.resource.example/token",
  "jwks_uri": "https://as.resource.example/.well-known/jwks.json"
}
]]>
</sourcecode>
<t>Role-specific fields:</t>

<ul spacing="compact">
<li><tt>issuer</tt> (REQUIRED): The AS's HTTPS URL. Placed in the <tt>iss</tt> claim of auth tokens.</li>
<li><tt>jwks_uri</tt> (REQUIRED): URL to the AS's JSON Web Key Set.</li>
<li><tt>auth_token_endpoint</tt> (REQUIRED): URL where PSes send token requests <xref target="as-token-endpoint"/>.</li>
<li><tt>revocation_endpoint</tt> (RECOMMENDED): URL where a PS revokes a person token it presented to this AS, and where a resource revokes a resource token whose <tt>aud</tt> is this AS <xref target="token-revocation"/>.</li>
</ul>
</section>

<section anchor="resource-metadata"><name>Resource Metadata</name>
<t>Published at <tt>/.well-known/aauth-resource.json</tt>. A resource MAY publish this document, and SHOULD point agents at it from pages they reach first <xref target="resource-metadata-link"/>. A resource that publishes none can still verify identity-based access and issue resource tokens and interaction requirements via <tt>401</tt> responses.</t>

<sourcecode type="json"><![CDATA[{
  "issuer": "https://resource.example",
  "jwks_uri": "https://resource.example/.well-known/jwks.json",
  "access_mode": "auth-token",
  "name": "Example Data Service",
  "description": "**Example Data Service** stores and serves your documents.",
  "logo_uri": "https://resource.example/logo.png",
  "logo_dark_uri": "https://resource.example/logo-dark.png",
  "documentation_uri": "https://resource.example/docs",
  "tos_uri": "https://resource.example/tos",
  "policy_uri": "https://resource.example/privacy",
  "authorization_endpoint": "https://resource.example/authorize",
  "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>Role-specific fields:</t>

<ul spacing="compact">
<li><tt>issuer</tt> (REQUIRED): The resource's HTTPS URL. Placed in the <tt>iss</tt> claim of resource tokens.</li>
<li><tt>jwks_uri</tt> (REQUIRED when the resource issues resource tokens or makes signed calls): URL to the resource's JSON Web Key Set. A resource that only verifies agent signatures has no keys to publish and MAY omit it.</li>
<li><tt>access_mode</tt> (OPTIONAL): The credential flow the resource expects, so an agent can plan its first call. Values defined by this document: <tt>agent-token</tt> (the agent signs with its agent token), <tt>person-token</tt> (the agent signs with a person token), <tt>session-token</tt> (the agent completes the resource's interaction flow and receives a session token via <tt>AAuth-Access</tt>), and <tt>auth-token</tt> (the agent obtains an auth token from its PS using a resource token; the initial call MUST present a person token). Default: <tt>agent-token</tt>. Extensions MAY define further values, recorded in the AAuth Access Mode Value Registry <xref target="aauth-access-mode-value-registry"/>; R3 (<xref target="I-D.hardt-aauth-r3"/>) defines <tt>per-call</tt>. An agent that does not recognize a value proceeds as with no declaration. The declaration is advisory: a resource MAY return any <tt>AAuth-Requirement</tt> at runtime <xref target="requirement-responses"/> and MAY apply different modes to different endpoints. An agent MAY use <tt>access_mode</tt> to skip resources its setup cannot satisfy, for example a PS-less agent and <tt>auth-token</tt>.</li>
<li><tt>authorization_endpoint</tt> (OPTIONAL): URL where agents request authorization <xref target="authorization-endpoint-request"/>. When absent, the resource issues resource tokens and interaction requirements via <tt>401</tt> responses.</li>
<li><tt>scope_descriptions</tt> (OPTIONAL): Object mapping scope values to Markdown strings for consent display <xref target="scopes"/>.</li>
<li><tt>signature_window</tt> (OPTIONAL): Integer. The signature validity window in seconds for the <tt>created</tt> timestamp <xref target="verification"/>. Default: 60. A resource MAY advertise a larger value for agents with poor clock synchronization, or a smaller one.</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 <xref target="covered-components"/>.</li>
<li><tt>revocation_endpoint</tt> (RECOMMENDED for a resource that accepts person tokens): URL where the issuer of an auth token or person token for this resource revokes it <xref target="token-revocation"/>. A resource that accepts only agent tokens receives no revocations and need not publish one.</li>
</ul>
</section>

<section anchor="resource-metadata-link"><name>Resource Metadata Link Relation</name>
<t>The <tt>aauth-resource</tt> link relation (<xref target="RFC8288"/>) lets any HTTP response, and any HTML page, name the resource metadata document that governs what the response describes. It serves an agent that has reached a developer portal or an API host and does not yet have the resource identifier to append <tt>/.well-known/aauth-resource.json</tt> to.</t>
<t>A server MAY include a <tt>Link</tt> header field in any response:</t>

<sourcecode type="http"><![CDATA[Link: <https://api.example/.well-known/aauth-resource.json>;
    rel="aauth-resource"
]]>
</sourcecode>
<t>An HTML document MAY carry the same relation as a <tt>link</tt> element in its <tt>head</tt>:</t>

<sourcecode type="html"><![CDATA[<link rel="aauth-resource"
      href="https://api.example/.well-known/aauth-resource.json">
]]>
</sourcecode>
<t>The target MUST be a server identifier <xref target="server-identifiers"/> followed by <tt>/.well-known/aauth-resource.json</tt>. An agent MUST NOT fetch a target of any other form. Having fetched it, the agent verifies the document as any metadata document <xref target="metadata-documents"/>: its <tt>issuer</tt> MUST equal the target minus the well-known suffix.</t>
<t>A resource SHOULD include the relation on the page at its <tt>documentation_uri</tt>. A response MAY carry more than one <tt>aauth-resource</tt> link when it describes several resources, each a resource identifier of its own. The relation says nothing about the response that carries it: a <tt>401</tt> from a resource endpoint still carries its requirement in <tt>AAuth-Requirement</tt>, and an agent MUST NOT treat the link as a substitute for it.</t>
<t>Verifiers do not use this relation. A party verifying a token or a signature discovers keys from the signer's <tt>iss</tt> and <tt>dwk</tt> (<xref target="I-D.hardt-httpbis-signature-key"/>), never from a link in content <xref target="link-relation-security"/>.</t>
</section>
</section>

<section anchor="http-message-signatures-profile"><name>HTTP Message Signatures Profile</name>
<t>This section profiles HTTP Message Signatures (<xref target="RFC9421"/>) for AAuth. Signing requirements (the agent's) and verification requirements (the server's) are specified separately.</t>

<section anchor="signature-algorithms"><name>Signature Algorithms</name>
<t>Every party MUST support <tt>Ed25519</tt> (<xref target="RFC8032"/>) and SHOULD support <tt>ES256</tt>. Algorithm identifiers are values from the IANA "JSON Web Signature and Encryption Algorithms" registry <xref target="IANA.JOSE.Algorithms"/>, carried in the <tt>alg</tt> member of the JWK (<xref target="RFC7517"/>).</t>
<t>Every key AAuth conveys or references is subject to the Algorithm Determination rules of the HTTP Signature Keys specification (<xref target="I-D.hardt-httpbis-signature-key"/>). In particular:</t>

<ul spacing="compact">
<li>The <tt>alg</tt> member MUST be present and MUST be a fully-specified identifier, one that determines the signature operation completely, including curve and hash. A verifier MUST reject a key whose <tt>alg</tt> is absent.</li>
<li>The polymorphic <tt>EdDSA</tt> identifier MUST NOT be used. Use <tt>Ed25519</tt> (or <tt>Ed448</tt>), which <xref target="RFC9864"/> registered as its fully-specified replacements.</li>
<li><tt>none</tt>, any algorithm whose JOSE Implementation Requirement is <tt>Prohibited</tt>, and symmetric algorithms (the <tt>oct</tt> key type and the <tt>HS256</tt>, <tt>HS384</tt>, and <tt>HS512</tt> identifiers) MUST NOT be used.</li>
<li>A verifier MUST reject a key whose <tt>kty</tt> or, where present, <tt>crv</tt> disagrees with its <tt>alg</tt>.</li>
</ul>
<t><tt>ES256</tt> is RECOMMENDED where a platform's keys are ECDSA on P-256, such as hardware-backed keys whose secure enclave does not offer Ed25519. The ML-DSA identifiers registered by <xref target="RFC9964"/> are fully specified and are used directly as the <tt>alg</tt> value.</t>
</section>

<section anchor="keying-material"><name>Keying Material</name>
<t>The signing key is conveyed in the <tt>Signature-Key</tt> header (<xref target="I-D.hardt-httpbis-signature-key"/>). Agents MUST use the <tt>jwt</tt> scheme, presenting a token that carries their public key in <tt>cnf</tt>; agents MUST NOT use the <tt>jwks_uri</tt> or <tt>hwk</tt> scheme for AAuth resource, PS, or AS requests. Which token the agent presents depends on what the recipient needs to know. All three carry the same key in <tt>cnf</tt>, so signature verification is identical.</t>
<table>
<thead>
<tr>
<th>Token</th>
<th>Presented to</th>
<th>Asserts</th>
</tr>
</thead>

<tbody>
<tr>
<td>Agent token <xref target="agent-tokens"/></td>
<td>the PS and the AP always; a resource for agent identity and resource-managed access</td>
<td>which agent</td>
</tr>

<tr>
<td>Person token <xref target="person-tokens"/></td>
<td>a resource, at its authorization endpoint or where it requires the person's identity</td>
<td>which person</td>
</tr>

<tr>
<td>Auth token <xref target="auth-tokens"/></td>
<td>a resource, once it has authorized the agent</td>
<td>what is authorized</td>
</tr>
</tbody>
</table><t>A PS, AS, AP, or resource making a signed AAuth request in its own right, such as a PS-to-AS token request <xref target="ps-to-as-token-request"/> or a revocation <xref target="token-revocation"/>, MUST use the <tt>jwks_uri</tt> scheme. The <tt>id</tt> parameter MUST be the server's <tt>issuer</tt> as published in its metadata <xref target="metadata-documents"/>, and <tt>dwk</tt> MUST be that metadata document's well-known name: <tt>aauth-person.json</tt>, <tt>aauth-access.json</tt>, <tt>aauth-agent.json</tt>, or <tt>aauth-resource.json</tt>.</t>

<sourcecode type="http"><![CDATA[Signature-Key: sig=jwks_uri;id="https://ps.example";
    dwk="aauth-person.json";kid="key-1"
]]>
</sourcecode>
<t>The recipient resolves <tt>id</tt> to the caller's identity, which is the <tt>iss</tt> of every token that server mints. A resource that acts as an agent to reach a downstream resource <xref target="multi-hop"/> signs as an agent: it presents its own agent token under the <tt>jwt</tt> scheme.</t>
<t>AAuth does not use the <tt>hwk</tt> scheme; the agent token is the minimum AAuth credential. The <tt>jkt-jwt</tt> scheme is used only in the agent provider's key-refresh ceremony (<xref target="I-D.hardt-aauth-bootstrap"/>).</t>
</section>

<section anchor="signing-agent"><name>Signing (Agent)</name>
<t>The agent creates an HTTP Message Signature (<xref target="RFC9421"/>) on each request, including the <tt>Signature-Key</tt>, <tt>Signature-Input</tt>, and <tt>Signature</tt> headers.</t>

<section anchor="covered-components"><name>Covered Components</name>
<t>The signature MUST cover the following derived components and header fields:</t>

<ul spacing="compact">
<li><tt>@method</tt>: The HTTP request method (<xref target="RFC9421"/>, Section 2.2.1)</li>
<li><tt>@authority</tt>: The target host (<xref target="RFC9421"/>, Section 2.2.3)</li>
<li><tt>@path</tt>: The request path (<xref target="RFC9421"/>, Section 2.2.6)</li>
<li><tt>signature-key</tt>: The Signature-Key header value</li>
</ul>
<t>On a request carrying a body to a PS or AS endpoint, or to any revocation endpoint <xref target="token-revocation"/>, the signature MUST additionally cover:</t>

<ul spacing="compact">
<li><tt>content-digest</tt>: The Content-Digest header value (<xref target="RFC9530"/>)</li>
<li><tt>content-type</tt>: The Content-Type header value</li>
</ul>
<t>A resource declares any further components it requires through <tt>additional_signature_components</tt> <xref target="resource-metadata"/>. Servers MAY require further covered components; the agent learns of them from server metadata or from an <tt>invalid_input</tt> error response that includes <tt>required_input</tt>. See <xref target="why-covered-components"/>.</t>
<t>The following example shows a fully bound request carrying a session token. Token and key values are placeholders.</t>

<sourcecode type="http"><![CDATA[GET /api/documents HTTP/1.1
Host: resource.example
Authorization: AAuth session-token-placeholder
Signature-Input: sig=("@method" "@authority" "@path"
    "authorization" "signature-key");created=1730217600
Signature: sig=:BASE64URL-SIGNATURE-PLACEHOLDER:
Signature-Key: sig=jwt;jwt="eyJhbGciOiJFZDI1NTE5IiwidHlwIjoiYWEtYWdlbnQrand0Iiwia2lkIjoiYXAta2V5LTEifQ.PLACEHOLDER.PLACEHOLDER"
]]>
</sourcecode>
</section>

<section anchor="signature-parameters"><name>Signature Parameters</name>
<t>The <tt>Signature-Input</tt> header (<xref target="RFC9421"/>, Section 4.1) MUST include:</t>

<ul spacing="compact">
<li><tt>created</tt>: Signature creation timestamp as an Integer (Unix time). The agent MUST set this to the current time.</li>
</ul>
<t>Agents MUST NOT include the <tt>alg</tt> signature parameter, and verifiers MUST ignore it if present, per <xref target="RFC9421"/>, Section 3.3.7; the algorithm is determined from the key <xref target="signature-algorithms"/>.</t>
<t>Agents SHOULD NOT include the <tt>keyid</tt> parameter (<xref target="RFC9421"/>, Section 5.1). If <tt>keyid</tt> is present for a label that also appears in <tt>Signature-Key</tt>, the two MUST identify the same key, and the verifier MUST take the key from <tt>Signature-Key</tt>.</t>
</section>
</section>

<section anchor="verification"><name>Verification (Server)</name>
<t>When a server receives a signed request, it MUST perform the following steps. Any failure MUST result in a <tt>401</tt> response with the appropriate <tt>Signature-Error</tt> header (<xref target="I-D.hardt-httpbis-signature-key"/>).</t>

<ol spacing="compact">
<li>Extract the <tt>Signature</tt>, <tt>Signature-Input</tt>, and <tt>Signature-Key</tt> headers. If any are missing, return <tt>invalid_signature</tt>.</li>
<li>Verify that the <tt>Signature-Input</tt> covers the required components <xref target="covered-components"/> and any additional components the server requires. If not, return <tt>invalid_input</tt> with <tt>required_input</tt>.</li>
<li>Verify the <tt>created</tt> parameter is present and within the server's signature validity window of the server's current time. The default window is 60 seconds; servers MAY advertise a different window via metadata (<tt>signature_window</tt> in resource metadata). Return <tt>invalid_signature</tt> if <tt>created</tt> is older than the window, and <tt>clock_skew</tt> if it is further ahead of the server's clock than the window. Servers and agents SHOULD synchronize their clocks using NTP (<xref target="RFC5905"/>).</li>
<li>Select the <tt>Signature-Key</tt> dictionary member for the label being verified and read its scheme. If the scheme is not one the server implements, including any scheme this profile does not use <xref target="keying-material"/> and any unregistered value, return <tt>unsupported_scheme</tt> with an <tt>Accept-Signature-Scheme</tt> header naming the schemes the server accepts. A server MUST NOT fail in a scheme-specific or undefined manner on an unrecognized scheme.</li>
<li>Obtain the public key from the <tt>Signature-Key</tt> header according to the scheme (<xref target="I-D.hardt-httpbis-signature-key"/>). Return <tt>invalid_key</tt> if the key cannot be parsed, <tt>unknown_key</tt> if the key is not found at the <tt>jwks_uri</tt>, <tt>invalid_jwt</tt> if a JWT scheme fails verification, <tt>expired_jwt</tt> if the JWT has expired, <tt>clock_skew</tt> if the server applies the OPTIONAL bound on <tt>iat</tt> <xref target="common-verification"/> and the JWT's <tt>iat</tt> is further ahead of the server's clock than the validity window, <tt>revoked_jwt</tt> if the JWT verifies and is unexpired but the server holds a revocation for it <xref target="token-revocation"/>, or <tt>issuer_missing</tt> / <tt>issuer_mismatch</tt> if the issuer's metadata document fails the checks in <xref target="metadata-documents"/>.</li>
<li>Determine the signature algorithm from the <tt>alg</tt> member of the obtained key <xref target="signature-algorithms"/>. Return <tt>unsupported_algorithm</tt> if <tt>alg</tt> is absent, is a polymorphic identifier, or names an algorithm or key type the server does not implement, and include an <tt>Accept-Signature-Alg</tt> header naming the algorithms the server accepts. Return <tt>invalid_key</tt> if the key's <tt>kty</tt> or <tt>crv</tt> disagrees with its <tt>alg</tt>.</li>
<li>Verify the HTTP Message Signature (<xref target="RFC9421"/>) using the obtained public key and determined algorithm. Return <tt>invalid_signature</tt> if verification fails.</li>
</ol>
<t>An <tt>Accept-Signature-Alg</tt> header names exactly the algorithms the server accepts. A server MAY omit either <tt>Accept-Signature-*</tt> header where enumerating what it accepts to an unauthenticated caller is judged a disclosure risk.</t>
<t>This profile uses <tt>401</tt> for every signature failure, where the HTTP Signature Keys specification uses <tt>400</tt> for most of them <xref target="why-401-signature-failures"/>. A <tt>403</tt> response denies access after the signature verified. Per (<xref target="I-D.hardt-httpbis-signature-key"/>), such a response MUST NOT include a <tt>Signature-Error</tt>, <tt>Accept-Signature-Scheme</tt>, or <tt>Accept-Signature-Alg</tt> header. This applies to the AAuth errors returned with <tt>403</tt> <xref target="token-endpoint-error-codes"/> and <xref target="polling-error-codes"/>.</t>

<section anchor="scheme-rejection"><name>Signature-Key Scheme Rejection</name>
<t>AAuth requires the <tt>jwt</tt> scheme of agents <xref target="keying-material"/>, so a request presenting any other scheme is rejected under step 4:</t>

<sourcecode type="http"><![CDATA[HTTP/1.1 401 Unauthorized
Signature-Error: error=unsupported_scheme
Accept-Signature-Scheme: jwt
]]>
</sourcecode>
<t>A resource that also serves clients outside AAuth MAY accept further schemes and MUST then list all of them. <tt>Accept-Signature-Scheme</tt> states what the server accepts from any caller; <tt>AAuth-Requirement: requirement=agent-token</tt> <xref target="requirement-agent-token"/> states that an AAuth agent token in particular is required.</t>
</section>

<section anchor="freshness-and-replay"><name>Freshness and Replay</name>
<t>The <tt>created</tt> parameter is the primary replay defense: a captured signature becomes unusable once the validity window closes. <tt>expires</tt> is OPTIONAL; servers MUST honor it when present and MUST reject requests where <tt>expires</tt> is in the past.</t>
<t>Within the validity window, a verifier MAY maintain a short-lived cache keyed by <tt>(signing-key-thumbprint, created, @method, @authority, @path)</tt> for the duration of the window, rejecting duplicate tuples. PSes and ASes are NOT required to maintain replay caches for resource tokens <xref target="resource-tokens"/>, which are consumed in a single token request. This profile defines no nonce mechanism.</t>
<t>A verifier that first sees a signed artifact after a delay, such as a batch pipeline or store-and-forward, uses the signed <tt>created</tt> as the signing-time anchor: it verifies that the presented token was valid at <tt>created</tt>, and applies its own policy for how much <tt>created</tt>-to-verification skew it accepts. A replay cache at such a verifier MUST span the skew it accepts.</t>
</section>
</section>
</section>

<section anchor="jwks-discovery"><name>JWKS Discovery and Caching</name>
<t>All AAuth token verification requires discovering the issuer's signing keys via the <tt>{iss}/.well-known/{dwk}</tt> pattern defined in the HTTP Signature Keys specification (<xref target="I-D.hardt-httpbis-signature-key"/>).</t>
<t>Every key an AAuth server publishes at its <tt>jwks_uri</tt> MUST carry a fully-specified <tt>alg</tt> member <xref target="signature-algorithms"/>, even though <xref target="RFC7517"/> makes the member OPTIONAL; the <tt>Signature-Key</tt> header identifies a key without describing it, so the JWKS is the only channel for the algorithm. Deployments reusing an existing JWKS need only ensure that the keys AAuth selects by <tt>kid</tt> carry <tt>alg</tt>.</t>
<t>A verifier MUST select the key matching <tt>kid</tt> without requiring any other member of the JWKS to be usable, and MUST NOT fail because an unselected member names a key type or algorithm it does not implement.</t>
<t>Implementations MUST cache JWKS responses and SHOULD respect HTTP cache headers (<tt>Cache-Control</tt>, <tt>Expires</tt>). On an unknown <tt>kid</tt> in a JWT header, an implementation SHOULD refresh the cached JWKS for that issuer. 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.</t>
<t>If a cached key matching the JWT <tt>kid</tt> fails signature verification, the verifier SHOULD refresh the issuer's JWKS once and retry before returning <tt>unknown_key</tt> (if the key is then absent) or <tt>invalid_jwt</tt> (if verification still fails), subject to the once-per-minute floor.</t>
<t>Before fetching any issuer metadata or <tt>jwks_uri</tt>, verifiers MUST apply egress admission per (<xref target="I-D.hardt-httpbis-signature-key"/>).</t>
</section>

<section anchor="aauth-tokens"><name>AAuth Tokens</name>
<t>Agent tokens <xref target="agent-tokens"/>, person tokens <xref target="person-tokens"/>, resource tokens <xref target="resource-tokens"/>, and auth tokens <xref target="auth-tokens"/> are JWTs (<xref target="RFC7519"/>) that share the header and claims below. Each token's own section states the values these take for it and the claims specific to it.</t>

<section anchor="common-claims"><name>Common JWT Claims</name>
<t>Header:</t>

<ul spacing="compact">
<li><tt>alg</tt>: Signing algorithm, per <xref target="signature-algorithms"/>. A fully-specified identifier is REQUIRED; <tt>Ed25519</tt> is RECOMMENDED. Implementations MUST NOT accept <tt>none</tt>, the polymorphic <tt>EdDSA</tt> identifier, or any symmetric algorithm.</li>
<li><tt>typ</tt>: The token type, <tt>aa-&lt;type&gt;+jwt</tt>. A recipient MUST check <tt>typ</tt> before acting on any AAuth JWT <xref target="person-token-not-authorization"/>.</li>
<li><tt>kid</tt>: Key identifier of the issuer's signing key in its JWKS.</li>
</ul>
<t>Payload:</t>

<ul spacing="compact">
<li><tt>iss</tt>: The issuer's server identifier <xref target="server-identifiers"/>.</li>
<li><tt>dwk</tt>: The issuer's well-known metadata document name, for key discovery (<xref target="I-D.hardt-httpbis-signature-key"/>).</li>
<li><tt>jti</tt>: Unique token identifier for replay detection, audit, and revocation <xref target="token-revocation"/>.</li>
<li><tt>iat</tt>: Issued-at timestamp. REQUIRED. Not a validity check <xref target="common-verification"/>.</li>
<li><tt>exp</tt>: Expiration timestamp. Lifetime limits are stated per token type.</li>
<li><tt>cnf</tt>: Confirmation claim (<xref target="RFC7800"/>) with <tt>jwk</tt> containing the public key the token is bound to. The JWK MUST carry a fully-specified <tt>alg</tt> member <xref target="signature-algorithms"/>. A resource token carries <tt>agent_jkt</tt> in place of <tt>cnf</tt> <xref target="resource-token-structure"/>.</li>
</ul>
</section>

<section anchor="common-verification"><name>Common JWT Verification</name>
<t>Verify per <xref target="RFC7515"/> and <xref target="RFC7519"/>:</t>

<ol spacing="compact">
<li>Decode the JWT header. Verify <tt>typ</tt> is the expected type.</li>
<li>Verify <tt>dwk</tt> is the expected metadata document name. Discover the issuer's JWKS via <tt>{iss}/.well-known/{dwk}</tt> per the HTTP Signature Keys specification (<xref target="I-D.hardt-httpbis-signature-key"/>) and <xref target="jwks-discovery"/>. Locate the key matching the JWT header <tt>kid</tt> and verify the JWT signature.</li>
<li>Verify <tt>exp</tt> is in the future, judged by the verifier's own clock. This document defines no tolerance for clock skew on <tt>exp</tt>.</li>
<li>Verify <tt>iss</tt> is a valid server identifier <xref target="server-identifiers"/>.</li>
</ol>
<t><tt>iat</tt> is not a validity check. A verifier MAY refuse a token whose <tt>iat</tt> is ahead of its clock by its own policy; the bound SHOULD be its signature validity window <xref target="verification"/>, 60 seconds by default. A verifier that refuses answers <tt>clock_skew</tt>: in the body for a token carried as a request parameter <xref target="token-endpoint-error-codes"/>, and as <tt>Signature-Error: error=clock_skew</tt> (<xref target="I-D.hardt-httpbis-signature-key"/>) for the token in the <tt>Signature-Key</tt> header. A verifier MAY use <tt>iat</tt> to bound the age of a token by its own policy; this document defines no such bound. <tt>exp</tt> minus <tt>iat</tt> MUST NOT exceed the one-hour ceiling of a person token <xref target="person-token-structure"/> or an auth token <xref target="auth-token-structure"/>, and SHOULD NOT exceed the recommended lifetime of an agent token <xref target="agent-tokens"/> or a resource token <xref target="resource-token-structure"/>.</t>
<t>The token's own section adds the checks specific to it. A token presented in the <tt>Signature-Key</tt> header that fails any of these steps is a signature failure and is answered per <xref target="verification"/>; a token carried as a request parameter that fails is answered with the parameter's own error code <xref target="token-endpoint-error-codes"/>.</t>
</section>
</section>

<section anchor="requirement-responses"><name>Requirement Responses</name>
<t>Servers use the <tt>AAuth-Requirement</tt> response header to tell an agent what the request needs before it can be served. The header MAY be sent with <tt>401 Unauthorized</tt>, <tt>202 Accepted</tt>, or <tt>402 Payment Required</tt>. A <tt>401</tt> says authorization is required. A <tt>202</tt> says the request is pending and further action is required. A <tt>402</tt> says authorization and payment are both required; the payment requirement is conveyed separately, by x402 <xref target="x402"/> or the Payment scheme (<xref target="I-D.ryan-httpauth-payment"/>).</t>
<t><tt>AAuth-Requirement</tt> and <tt>WWW-Authenticate</tt> are independent header fields; a response MAY include both, and neither invalidates the other. AAuth never conveys its own requirements via <tt>WWW-Authenticate</tt>.</t>

<section anchor="aauth-requirement-header-structure"><name>AAuth-Requirement Header Structure</name>
<t>The <tt>AAuth-Requirement</tt> header field is a Dictionary (<xref target="RFC9651"/>, Section 3.2). It MUST contain the following member:</t>

<ul spacing="compact">
<li><tt>requirement</tt>: A Token (<xref target="RFC9651"/>, Section 3.3.4) indicating the requirement type.</li>
</ul>
<t>Requirement-specific data are conveyed as parameters on the <tt>requirement</tt> member (for example, <tt>resource-token</tt>, <tt>url</tt>, <tt>code</tt>). Recipients MUST ignore unknown parameters.</t>

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

<section anchor="requirement-values"><name>Requirement Values</name>
<t>The <tt>requirement</tt> value is an extension point. This document defines the following values:</t>
<table>
<thead>
<tr>
<th>Value</th>
<th>Status Code</th>
<th>Meaning</th>
<th align="center">Resource</th>
<th align="center">PS</th>
<th align="center">AS</th>
</tr>
</thead>

<tbody>
<tr>
<td><tt>agent-token</tt></td>
<td><tt>401</tt></td>
<td>AAuth agent token required <xref target="requirement-agent-token"/></td>
<td align="center">Y</td>
<td align="center"/>
<td align="center"/>
</tr>

<tr>
<td><tt>person-token</tt></td>
<td><tt>401</tt></td>
<td>Person token required <xref target="requirement-person-token"/></td>
<td align="center">Y</td>
<td align="center"/>
<td align="center"/>
</tr>

<tr>
<td><tt>auth-token</tt></td>
<td><tt>401</tt></td>
<td>Auth token required <xref target="requirement-auth-token"/></td>
<td align="center">Y</td>
<td align="center"/>
<td align="center"/>
</tr>

<tr>
<td><tt>interaction</tt></td>
<td><tt>202</tt></td>
<td>User action required at an interaction URL <xref target="interaction-required"/></td>
<td align="center">Y</td>
<td align="center">Y</td>
<td align="center">Y</td>
</tr>

<tr>
<td><tt>approval</tt></td>
<td><tt>202</tt></td>
<td>Approval pending, poll for result <xref target="approval-pending"/></td>
<td align="center">Y</td>
<td align="center">Y</td>
<td align="center">Y</td>
</tr>

<tr>
<td><tt>clarification</tt></td>
<td><tt>202</tt></td>
<td>Question posed to the recipient <xref target="requirement-clarification"/></td>
<td align="center">Y</td>
<td align="center">Y</td>
<td align="center">Y</td>
</tr>

<tr>
<td><tt>claims</tt></td>
<td><tt>202</tt></td>
<td>Identity claims required <xref target="requirement-claims"/></td>
<td align="center"/>
<td align="center"/>
<td align="center">Y</td>
</tr>
</tbody>
</table><t>An agent that does not recognize the <tt>requirement</tt> value MUST NOT treat the response as satisfiable, and surfaces the unsupported requirement to the caller as an error. For a <tt>202</tt> response with an unrecognized <tt>requirement</tt>, the agent MAY continue polling the <tt>Location</tt> URL in case a later response carries a requirement it does understand.</t>
</section>

<section anchor="interaction-required"><name>Interaction Required</name>
<t>When a server requires user action, such as authentication, consent, or payment approval, it returns a <tt>202 Accepted</tt> response:</t>

<sourcecode type="http"><![CDATA[HTTP/1.1 202 Accepted
AAuth-Requirement:
    requirement=interaction;
    url="https://example.com/interact";
    code="A1B2-C3D4"
Location: /pending/f7a3b9c
Retry-After: 0
]]>
</sourcecode>
<t>The <tt>AAuth-Requirement</tt> header MUST include the following parameters:</t>

<ul spacing="compact">
<li><tt>url</tt> (String): The interaction URL where the user completes the required action. MUST use the <tt>https</tt> scheme and MUST NOT contain query or fragment components.</li>
<li><tt>code</tt> (String): An interaction code that links the agent's pending request to the user's session at the interaction URL. Generated and compared per <xref target="interaction-code-format"/>.</li>
</ul>
<t>The response MUST also include:</t>

<ul spacing="compact">
<li><tt>Location</tt>: A URL the agent polls (with GET) for a terminal response.</li>
<li><tt>Retry-After</tt>: Recommended polling interval in seconds.</li>
</ul>

<section anchor="interaction-code-format"><name>Interaction Code Format</name>
<t>The <tt>code</tt> is a Structured Field String (<xref target="RFC9651"/>, Section 3.3.3). The user reads it out of band and compares it against the code shown on the interaction page, so it MUST be both unguessable and unambiguous to a human.</t>
<t><strong>Alphabet.</strong> The code MUST be generated from Crockford base32 (<xref target="I-D.crockford-davis-base32-for-humans"/>), the symbol set <tt>0123456789ABCDEFGHJKMNPQRSTVWXYZ</tt>. Every symbol is URL-safe, so the code requires no escaping when appended as <tt>{url}?code={code}</tt>. Servers MUST NOT emit codes containing characters outside this set, other than the optional grouping hyphen.</t>
<t><strong>Entropy and length.</strong> A code MUST carry at least 40 bits of entropy, at least 8 Crockford base32 symbols, drawn from a cryptographically secure random source. Servers MAY use longer codes for higher-value interactions.</t>
<t><strong>Hyphens.</strong> A server MAY insert hyphen (<tt>-</tt>) characters into the displayed code for visual grouping (for example, <tt>A1B2-C3D4</tt>). The hyphen carries no entropy and is not part of the code's value. Before comparison, both the server and any party validating the code MUST strip all hyphens.</t>
<t><strong>Case.</strong> Comparison MUST be case-insensitive. A server MUST accept the code regardless of the case the user enters, and on input MUST fold the Crockford decode aliases (<tt>I</tt>/<tt>L</tt> → <tt>1</tt>, <tt>O</tt> → <tt>0</tt>) before comparison.</t>
<t><strong>Correlation only.</strong> The code is a correlation identifier that ties the user's browser session to the pending interaction. It is NOT an authorization credential. The person's approve/deny decision MUST be recorded via an authenticated channel at the PS; how the PS authenticates the person is out of scope. Because the agent relays the code to the user, the code is visible to the agent, and the code alone MUST NOT authorize the decision.</t>
<t><strong>Single use.</strong> A code MUST be single-use. Once the user arrives at the interaction URL with a valid code and the code is consumed, the server MUST reject any later presentation of the same code with <tt>invalid_code</tt> <xref target="polling-error-codes"/>. A code consumed by out-of-band completion <xref target="user-interaction"/> is rejected the same way.</t>
<t><strong>Rate-limiting.</strong> The server MUST rate-limit code-validation attempts at the interaction URL. After a small number of failed attempts the server MUST treat the pending interaction as terminally failed and return <tt>invalid_code</tt> <xref target="polling-error-codes"/> on subsequent attempts.</t>
<t><strong>Lifetime.</strong> A code MUST expire no later than the pending interaction it is bound to <xref target="deferred-responses"/>. Once the pending request has expired, presenting the code MUST fail with <tt>expired</tt> <xref target="polling-error-codes"/>; the agent MAY initiate a fresh request to obtain a new code.</t>
</section>

<section anchor="interaction-relay"><name>Relaying Through the Person Server</name>
<t>When the agent has a PS, it SHOULD relay the interaction to the PS's <tt>interaction_endpoint</tt> <xref target="interaction-endpoint"/> before directing the user itself. To relay, the agent POSTs <tt>{ "type": "interaction", "url": "...", "code": "..." }</tt> to the interaction endpoint. The PS responds:</t>

<ul spacing="compact">
<li><strong>PS can relay</strong>: it returns a <tt>202</tt> deferred response, and the agent polls for completion as described in <xref target="interaction-endpoint"/>.</li>
<li><strong>PS cannot relay</strong>: it returns <tt>interaction_unavailable</tt> <xref target="interaction-endpoint-errors"/>. This is non-terminal; the agent falls back to directing the user itself.</li>
</ul>
<t>The agent directs the user itself when it has no PS, or when the PS returns <tt>interaction_unavailable</tt>. It constructs a user-facing URL by appending the code as a query parameter, <tt>{url}?code={code}</tt>, and directs the user to it by one of:</t>

<ul spacing="compact">
<li><strong>Browser redirect</strong>: The agent opens the URL in the user's browser.</li>
<li><strong>Display code</strong>: The agent displays the <tt>url</tt> and <tt>code</tt> for the user to enter manually. The agent MAY also render the constructed URL as a QR code.</li>
</ul>
<t>After directing the user, the agent polls the <tt>Location</tt> URL with GET requests, respecting the <tt>Retry-After</tt> interval. A <tt>202</tt> response means the request is still pending. A non-<tt>202</tt> response is terminal: <tt>200</tt> indicates success, <tt>403</tt> denial, and <tt>408</tt> timeout.</t>

<sourcecode type="ascii-art"><![CDATA[Agent                        User                         Server
  |                            |                             |
  |  202 Accepted                                            |
  |  AAuth-Requirement:                                      |
  |    requirement=interaction;                              |
  |    url="..."; code="..."                                 |
  |  Location: /pending/...                                  |
  |<---------------------------------------------------------|
  |                            |                             |
  |  open {url}?code={code}    |                             |
  |  (or display code / QR)    |                             |
  |--------------------------->|                             |
  |                            |                             |
  |                            |  {url}?code={code}          |
  |                            |---------------------------->|
  |                            |                             |
  |                            |  user completes action      |
  |                            |<----------------------------|
  |                            |                             |
  |  GET /pending/...                                        |
  |--------------------------------------------------------->|
  |                            |                             |
  |  200 OK                                                  |
  |<---------------------------------------------------------|
]]>
</sourcecode>
</section>
</section>

<section anchor="approval-pending"><name>Approval Pending</name>
<t>When a server is obtaining approval from another party without requiring the agent to direct a user, for example via push notification, email, or administrator review:</t>

<sourcecode type="http"><![CDATA[HTTP/1.1 202 Accepted
AAuth-Requirement: requirement=approval
Location: /pending/f7a3b9c
Retry-After: 30
]]>
</sourcecode>
<t>The response MUST include <tt>Location</tt> and <tt>Retry-After</tt>. The agent polls the <tt>Location</tt> URL with GET requests until a terminal response is received. No user action is required at the agent side. The same terminal response codes apply as for <tt>interaction</tt>.</t>
</section>
</section>

<section anchor="aauth-capabilities"><name>AAuth-Capabilities Request Header</name>
<t>Agents use the <tt>AAuth-Capabilities</tt> request header to declare which protocol capabilities they can handle, so a resource knows which requirements it can raise. The header field is a List (<xref target="RFC9651"/>, Section 3.1) of Tokens.</t>

<sourcecode type="http"><![CDATA[AAuth-Capabilities: interaction, clarification, payment
]]>
</sourcecode>
<t>This specification defines the following capability values:</t>
<table>
<thead>
<tr>
<th>Value</th>
<th>Meaning</th>
</tr>
</thead>

<tbody>
<tr>
<td><tt>interaction</tt></td>
<td>Agent can get a user to a URL, either directly or via its PS's interaction endpoint</td>
</tr>

<tr>
<td><tt>clarification</tt></td>
<td>Agent can engage in clarification chat <xref target="clarification-chat"/></td>
</tr>

<tr>
<td><tt>payment</tt></td>
<td>Agent can handle <tt>402</tt> payment flows, either directly or via its PS's interaction endpoint</td>
</tr>
</tbody>
</table><t>The agent's capabilities are the union of what it can do directly and what its PS can do on its behalf. When a mission approval response includes a <tt>capabilities</tt> array <xref target="mission-approval"/>, the agent unions those with its own.</t>
<t>Agents SHOULD include the <tt>AAuth-Capabilities</tt> header on signed requests to resources. The header is not used on requests to PS endpoints, which take a <tt>capabilities</tt> request parameter instead <xref target="person-token-endpoint"/> and <xref target="ps-token-endpoint"/>. Recipients MUST ignore unrecognized capability values. When the header is absent, recipients MUST NOT assume any capabilities.</t>
<t>Capability values are Tokens and currently carry no parameters. A future capability value MAY define parameters; recipients MUST ignore parameters they do not recognize rather than rejecting the header. Recipients ignore what they do not recognize throughout AAuth, and no document carries a version or schema identifier. An extension that defines a member a recipient must understand states that requirement, and the behaviour on failure, itself.</t>
</section>

<section anchor="deferred-responses"><name>Deferred Responses</name>
<t>Any AAuth endpoint MAY return a <tt>202 Accepted</tt> response (<xref target="RFC9110"/>) when it cannot immediately resolve a request. 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-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"
}
]]>
</sourcecode>
<t>Headers:</t>

<ul spacing="compact">
<li><tt>Location</tt> (REQUIRED): The pending 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).</li>
<li><tt>AAuth-Requirement</tt> (OPTIONAL): Present when user interaction or approval is required <xref target="requirement-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>
</ul>
<t>Additional body fields may be present depending on the <tt>AAuth-Requirement</tt> value, for example <tt>clarification</tt> and <tt>timeout</tt> with <tt>requirement=clarification</tt>, or <tt>required_claims</tt> with <tt>requirement=claims</tt>.</t>
</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 and does not resend the original request body. <strong>Exception</strong>: during clarification chat, the agent uses <tt>POST</tt> to deliver a clarification response <xref target="agent-response-to-clarification"/>.</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 <xref target="RFC8628"/>, Section 3.5). The <tt>Prefer: wait=N</tt> header (<xref target="RFC7240"/>) MAY be included on polling requests.</t>
</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 <tt>202 Accepted</tt>. A non-<tt>202</tt> response terminates polling.</t>

<artwork><![CDATA[Initial request (with Prefer: wait=N)
    |
    +-- 200 --> done — process response body
    +-- 202 --> note Location URL, check requirement/code
    +-- 400 --> invalid request — check error field, fix and retry
    +-- 401 --> invalid signature — check credentials;
    |           obtain auth token if resource challenge
    +-- 402 --> payment required (settle payment, poll Location)
    +-- 500 --> server error — start over
    +-- 502 --> as_unreachable — fresh resource token, retry after backoff
    +-- 503 --> back off per Retry-After, retry
               |
               GET Location (with Prefer: wait=N)
               |
               +-- 200 --> done — process response body
               +-- 202 --> continue polling (check status/clarification)
               |           status=interacting → stop prompting user
               +-- 403 --> denied or abandoned — surface to user
               +-- 408 --> expired — MAY initiate a fresh request
               +-- 410 --> gone — MUST NOT retry
               +-- 429 --> slow down — increase interval by 5s
               +-- 500 --> server error — start over
               +-- 502 --> as_unreachable — fresh resource token, retry after backoff
               +-- 503 --> temporarily unavailable
                           back off per Retry-After
]]>
</artwork>
</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>Signature-Error</tt> header as defined in (<xref target="I-D.hardt-httpbis-signature-key"/>). The header, not the response body, is the machine-readable carrier; agents MUST NOT depend on the body for signature error handling. A server returning <tt>unsupported_scheme</tt> SHOULD include <tt>Accept-Signature-Scheme</tt>, and one returning <tt>unsupported_algorithm</tt> SHOULD include <tt>Accept-Signature-Alg</tt> <xref target="verification"/>.</t>
</section>

<section anchor="error-response-format"><name>Error Response Format</name>
<t>Error response bodies use the HTTP problem details format (<xref target="RFC9457"/>) with <tt>Content-Type: application/problem+json</tt>. The body is a JSON object with the following members:</t>

<ul spacing="compact">
<li><tt>error</tt> (REQUIRED): String. A single error code, as defined by the endpoint returning the error. This is an RFC 9457 extension member; receivers MUST determine how to proceed from this member.</li>
<li><tt>detail</tt> (OPTIONAL): String. A human-readable explanation specific to this occurrence of the error.</li>
</ul>
<t>Other RFC 9457 members (<tt>type</tt>, <tt>title</tt>, <tt>status</tt>, <tt>instance</tt>) MAY be present with their RFC 9457 semantics. AAuth does not define problem type URIs; receivers MUST NOT rely on <tt>type</tt> to identify AAuth errors.</t>
</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_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>revoked_resource_token</tt></td>
<td>400</td>
<td>The resource that issued the resource token has withdrawn it <xref target="token-revocation"/>. Terminal for that token: the agent MUST NOT resubmit it, and MAY call the resource again.</td>
</tr>

<tr>
<td><tt>invalid_presented_token</tt></td>
<td>400</td>
<td>Presented token malformed, signature verification failed, or its <tt>typ</tt> is neither a person token nor an auth token <xref target="resource-token-verification"/></td>
</tr>

<tr>
<td><tt>expired_presented_token</tt></td>
<td>400</td>
<td>The presented token has expired. The agent obtains a fresh person token, then a fresh resource token.</td>
</tr>

<tr>
<td><tt>revoked_presented_token</tt></td>
<td>400</td>
<td>The presented token has been revoked by the server that issued it <xref target="token-revocation"/>. The agent obtains a fresh person token, then a fresh resource token.</td>
</tr>

<tr>
<td><tt>invalid_upstream_token</tt></td>
<td>400</td>
<td>Upstream token malformed, signature verification failed, or its <tt>aud</tt> is not the requesting intermediary <xref target="upstream-token-verification"/></td>
</tr>

<tr>
<td><tt>expired_upstream_token</tt></td>
<td>400</td>
<td>The upstream token has expired. The calling agent must re-authorize at the intermediary.</td>
</tr>

<tr>
<td><tt>revoked_upstream_token</tt></td>
<td>400</td>
<td>The upstream token has been revoked, or the PS has revoked the calling agent's agent token or its binding to the person <xref target="upstream-token-verification"/>. Terminal for that token.</td>
</tr>

<tr>
<td><tt>invalid_subagent_token</tt></td>
<td>400</td>
<td>Sub-agent token malformed, signature verification failed, its <tt>parent_agent</tt> does not name the signing agent, or its <tt>iss</tt> is not the signing agent's <xref target="sub-agents"/></td>
</tr>

<tr>
<td><tt>expired_subagent_token</tt></td>
<td>400</td>
<td>The sub-agent token has expired. The parent obtains a fresh one.</td>
</tr>

<tr>
<td><tt>revoked_subagent_token</tt></td>
<td>400</td>
<td>The sub-agent token has been revoked by its agent provider <xref target="token-revocation"/>. Terminal for that token.</td>
</tr>

<tr>
<td><tt>clock_skew</tt></td>
<td>400</td>
<td>A token carried as a request parameter has an <tt>iat</tt> further ahead of the verifier's clock than the signature validity window <xref target="common-verification"/>. A fresh token from the same issuer carries the same skew, so the agent does not refresh; it MAY present the same token again once its <tt>iat</tt> is within the window, using the response's <tt>Date</tt> header to judge, or surfaces the error.</td>
</tr>

<tr>
<td><tt>user_unreachable</tt></td>
<td>403</td>
<td>Terminal. The PS has no channel to reach the user and the agent did not declare the <tt>interaction</tt> capability.</td>
</tr>

<tr>
<td><tt>as_unreachable</tt></td>
<td>502</td>
<td>PS token endpoint only. The PS could not obtain an auth token from the access server named by the resource token's <tt>aud</tt>: connection failure, timeout, a malformed response, or an auth token that fails delivery verification <xref target="auth-token-delivery"/>. The agent MAY retry with a fresh resource token after a backoff. Distinct from an AS denial, which the PS relays.</td>
</tr>

<tr>
<td><tt>server_error</tt></td>
<td>500</td>
<td>Internal error</td>
</tr>
</tbody>
</table><t>Token-specific codes exist only for tokens carried as request parameters, and follow the pattern <tt>&lt;invalid|expired|revoked&gt;_&lt;parameter&gt;_token</tt>. The token in the <tt>Signature-Key</tt> header has no codes in this table: when it fails, the response is <tt>401</tt> with <tt>Signature-Error</tt> <xref target="verification"/>.</t>
<t>Example:</t>

<sourcecode type="http"><![CDATA[HTTP/1.1 400 Bad Request
Content-Type: application/problem+json

{
  "error": "expired_resource_token",
  "detail": "The resource token expired; obtain a new
    resource token from the resource and retry."
}
]]>
</sourcecode>
</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>revoked</tt></td>
<td>403</td>
<td>A token the pending request depends on was revoked <xref target="token-revocation"/>: the resource token it was started for, the upstream token of a chained request <xref target="call-chaining"/>, or the agent token of the agent that started it. <tt>detail</tt> SHOULD say which</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><t>Example:</t>

<sourcecode type="http"><![CDATA[HTTP/1.1 403 Forbidden
Content-Type: application/problem+json

{
  "error": "denied",
  "detail": "The user declined the request."
}
]]>
</sourcecode>
</section>
</section>

<section anchor="scopes"><name>Scopes</name>
<t>Scopes define what an agent is authorized to do at a resource. AAuth uses two categories of scope values:</t>

<ul spacing="compact">
<li><strong>Resource scopes</strong>: Resource-specific authorization grants (e.g., <tt>data.read</tt>, <tt>data.write</tt>, <tt>data.delete</tt>). Each resource defines its own scope values and publishes human-readable descriptions in its metadata (<tt>scope_descriptions</tt>). Resources that already define OAuth scopes SHOULD use the same scope values in AAuth.</li>
<li><strong>Identity scopes</strong>: Requests for user identity claims following <xref target="OpenID.Core"/> (e.g., <tt>openid</tt>, <tt>profile</tt>, <tt>email</tt>, <tt>address</tt>, <tt>phone</tt>). When identity scopes are present, the auth token includes the corresponding identity claims. Enterprise extensions include the <tt>tenant</tt> claim from <xref target="OpenID.Enterprise"/> and the <tt>groups</tt> and <tt>roles</tt> claims from <xref target="RFC9068"/> (originally defined by SCIM <xref target="RFC7643"/>).</li>
</ul>
<t>A resource token MUST only include resource scopes that the resource has defined in its <tt>scope_descriptions</tt> metadata, and identity scopes that the PS has declared in its <tt>scopes_supported</tt> metadata.</t>
<t>Scopes appear in three places:</t>

<ol spacing="compact">
<li><strong>Authorization endpoint request</strong> (<tt>scope</tt>): The scope the agent is requesting from the resource.</li>
<li><strong>Resource token</strong> (<tt>scope</tt>): The scope the resource is willing to grant.</li>
<li><strong>Auth token</strong> (<tt>scope</tt>): The scope actually granted. MUST NOT be broader than the resource token's scope.</li>
</ol>
<t>The PS evaluates requested scopes against mission context (if present) and user consent. The AS evaluates scopes against resource policy. Either party may narrow the granted scope.</t>
</section>

<section anchor="account-binding"><name>Account Binding</name>
<t>A resource may hold more than one account for the same person: an AAuth-to-OAuth proxy where a user has connected several accounts, a SaaS product where someone belongs to several workspaces. Scope says what the agent may do; <tt>account</tt> says which account it may do it to.</t>
<t>The OPTIONAL <tt>account</tt> parameter of the authorization endpoint request <xref target="authorization-endpoint-request"/> binds an authorization to one account. Its value is a string from the resource's own account namespace, such as an email address, a workspace identifier, or a tenant id. This specification gives it no structure and no meaning; only the resource interprets it.</t>
<t>When the request carried <tt>account</tt>, the resource echoes it as the <tt>account</tt> claim of the resource token, the PS or AS copies it into the auth token, and the resource enforces per-account access from the token it receives. Different accounts yield different auth tokens. <tt>account</tt> is not <tt>login_hint</tt> <xref target="why-account-not-login-hint"/>.</t>
</section>

<section anchor="token-revocation"><name>Token Revocation</name>
<t>A PS and an AS SHOULD provide a revocation endpoint, and so SHOULD a resource that accepts person tokens. A resource that accepts only agent tokens receives no revocations. Revocation endpoints are advertised in server metadata as <tt>revocation_endpoint</tt>. A server without one honors a revoked token until its <tt>exp</tt>.</t>

<section anchor="revocation-request"><name>Revocation Request</name>
<t>The endpoint accepts a signed POST. The caller signs as a server <xref target="keying-material"/>, and recipients MUST verify the caller's identity via HTTP Message Signatures. The signature MUST cover <tt>content-digest</tt> and <tt>content-type</tt> along with the base components <xref target="covered-components"/>, at a resource's revocation endpoint as much as a PS's or an AS's.</t>

<sourcecode type="http"><![CDATA[POST /revoke HTTP/1.1
Host: resource.example
Content-Type: application/json
Signature-Key: sig=jwks_uri;id="https://ps.example";
    dwk="aauth-person.json";kid="key-1"

{
  "jti": "unique-token-identifier",
  "exp": 1788727813
}
]]>
</sourcecode>

<ul spacing="compact">
<li><tt>jti</tt> (REQUIRED): The token to revoke, within the caller's namespace.</li>
<li><tt>exp</tt> (REQUIRED): The revoked token's own expiration, which bounds how long the recipient has to remember the revocation.</li>
</ul>
<t>The issuer is not a request parameter. The recipient takes it from the identity it verified on the signature: a caller revokes only its own tokens. Recipients maintaining revocation state MUST key it by <tt>(iss, jti)</tt>, where <tt>iss</tt> is the verified identity of the caller. A recipient MAY discard an entry once the current time is past <tt>exp</tt> plus its clock skew tolerance, and MAY reject a revocation whose <tt>exp</tt> is further in the future than the longest lifetime it accepts for any token.</t>
</section>

<section anchor="who-revokes-what"><name>Who Revokes What</name>
<table>
<thead>
<tr>
<th>Token</th>
<th>Revoked by</th>
<th>At</th>
</tr>
</thead>

<tbody>
<tr>
<td>Agent token</td>
<td>the agent provider that issued it</td>
<td>the PS only. A resource that accepts an agent token directly has no revocation path; that access is bounded by the agent token's lifetime, which is why it SHOULD NOT exceed 24 hours <xref target="agent-tokens"/>.</td>
</tr>

<tr>
<td>Person token</td>
<td>the PS that issued it</td>
<td>the resource named in its <tt>aud</tt>, and each AS the PS presented it to <xref target="ps-to-as-token-request"/></td>
</tr>

<tr>
<td>Auth token</td>
<td>the PS (three-party) or AS (four-party) that issued it</td>
<td>the resource it was issued for. A PS that federated to an AS revokes the person token at the AS instead, and the AS revokes what it issued.</td>
</tr>

<tr>
<td>Resource token</td>
<td>the resource that issued it</td>
<td>the party named in its <tt>aud</tt>, and in four-party the <tt>ps</tt> as well</td>
</tr>
</tbody>
</table></section>

<section anchor="revocation-response"><name>Revocation Response</name>
<t>The recipient records the revocation before it calls anyone, then makes every downstream revocation it is going to make <xref target="revocation-cascade"/>, and answers <tt>200 OK</tt> once each has a terminal outcome. A <tt>200</tt> says the cascade is finished, not that it was started. Whether the recipient holds a record of the token does not enter into it: a recipient that verifies tokens statelessly still answers <tt>200 OK</tt>, having recorded the pair. There is no "not found" response <xref target="why-revocation-no-not-found"/>. A recipient with nothing downstream, such as a resource, or a PS or AS receiving a resource token revocation, answers at once.</t>
<t>A downstream revocation is terminal when it was recorded there, or when it ended in one of two ways:</t>

<ul spacing="compact">
<li><tt>revocation_unsupported</tt>: the downstream party publishes no <tt>revocation_endpoint</tt>, or answered <tt>unsupported_iss</tt>. That party honors the tokens until their <tt>exp</tt>.</li>
<li><tt>revocation_unavailable</tt>: the downstream party did not respond, timed out, or returned a <tt>5xx</tt> or a malformed response. The caller MAY revoke again later.</li>
</ul>
<t><strong>Body.</strong> An AS reports to the PS: its <tt>200</tt> carries a <tt>downstream</tt> array with one entry per resource it revoked at, each with <tt>recipient</tt> (the resource identifier) and <tt>error</tt> (one of the two values above when the revocation there did not succeed, absent when it did). A PS does not report to the agent provider: its <tt>200</tt> to an AP carries an empty body <xref target="why-ps-reports-nothing-to-ap"/>. A recipient with nothing downstream answers with an empty body. A PS MAY report the outcome of a revocation the person initiated to that person, per recipient.</t>

<sourcecode type="http"><![CDATA[HTTP/1.1 200 OK
Content-Type: application/json

{
  "downstream": [
    {
      "recipient": "https://resource.example",
      "error": "revocation_unavailable"
    }
  ]
}
]]>
</sourcecode>
<t>A <tt>200</tt> MAY carry no body, and then carries no <tt>Content-Type</tt>. A non-empty body MUST be <tt>application/json</tt> and MUST be a JSON object, whose <tt>downstream</tt> member is OPTIONAL. A caller reads an absent body as the revocation recorded with nothing reported, and a non-empty body it cannot parse as <tt>revocation_unavailable</tt>.</t>
<t><strong>Deferred completion.</strong> If the cascade takes longer than the recipient will hold the connection, or a downstream recipient itself answered <tt>202</tt>, the recipient returns a <tt>202 Accepted</tt> deferred response <xref target="deferred-responses"/> and the caller polls the pending URL for the terminal response. The caller MAY say how long it will wait with <tt>Prefer: wait</tt> (<xref target="RFC7240"/>). Absent <tt>Prefer: wait</tt>, the recipient SHOULD hold at least long enough for one round trip to each downstream party it will call, on the order of 20 seconds for a PS or an AS. A recipient MAY hold for less than the caller asked for and answer <tt>202</tt> at its own cap.</t>
<t>The poller is a server: it polls the pending URL with a signed <tt>GET</tt>, signing under the same identity that made the revocation, and sends no body. The recipient MUST verify that identity and MUST answer <tt>404</tt> to a poll from any other. <tt>Retry-After</tt> and <tt>Prefer: wait</tt> apply as on any other pending URL. The terminal response is what the synchronous path would have returned, or an error from the table below. No <tt>AAuth-Requirement</tt> is used. A recipient with nothing downstream MUST NOT answer <tt>202</tt>.</t>
<t><strong>Idempotence.</strong> A recipient answering a repeated revocation for the same <tt>(iss, jti)</tt> records nothing new, re-attempts each downstream revocation that did not succeed, and reports the current outcome. A PS that received <tt>revocation_unavailable</tt> MAY therefore revoke the person token at the AS again later.</t>
<t><strong>Errors</strong> use the format in <xref target="error-response-format"/>:</t>
<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, or a missing or malformed <tt>jti</tt> or <tt>exp</tt></td>
</tr>

<tr>
<td><tt>unsupported_iss</tt></td>
<td>403</td>
<td>The recipient does not accept revocations from this caller</td>
</tr>

<tr>
<td><tt>rate_limited</tt></td>
<td>429</td>
<td>The caller has sent more revocations than the recipient will accept from it for now. <tt>Retry-After</tt> is REQUIRED. Distinct from polling <tt>slow_down</tt> <xref target="polling-error-codes"/></td>
</tr>

<tr>
<td><tt>server_error</tt></td>
<td>500</td>
<td>Internal error</td>
</tr>
</tbody>
</table><t>A request whose signature does not verify is answered with <tt>401</tt> and the <tt>Signature-Error</tt> header <xref target="error-responses"/>.</t>
<t><strong>What a recipient will accept.</strong> A recipient MAY restrict revocations to issuers it holds tokens from, or has a record of exchanging tokens with, and answer <tt>unsupported_iss</tt> to the rest. A recipient that accepts any verified issuer SHOULD bound what one issuer can hold, in entries and in rate, and answer <tt>rate_limited</tt> beyond either.</t>
</section>

<section anchor="revocation-cascade"><name>Revocation Cascade</name>

<ul spacing="compact">
<li><strong>PS revokes an auth token it issued</strong> (three-party): The PS calls the resource's revocation endpoint.</li>
<li><strong>AS revokes an auth token it issued</strong>: The AS calls the resource's revocation endpoint.</li>
<li><strong>PS terminates access it federated</strong> (four-party): The PS revokes the person token it presented with the token request <xref target="ps-to-as-token-request"/>, and the AS cascades to the auth tokens it issued against that person token.</li>
<li><strong>PS revokes a person token it issued</strong>: The PS calls the revocation endpoint of the resource named in the token's <tt>aud</tt>, and of every AS it presented that person token to. The resource MUST refuse subsequent requests presenting the person token and MUST NOT issue a resource token naming it. The AS MUST NOT issue further auth tokens against it, and MUST revoke the auth tokens it already issued against it by calling the revocation endpoint of the resource each names in <tt>aud</tt>. The PS MUST NOT present a revoked person token to an AS, and rejects a token request whose <tt>presented_token</tt> is one with <tt>revoked_presented_token</tt> <xref target="token-endpoint-error-codes"/>. Where the revoked person token, or an auth token issued against it, was later presented as an <tt>upstream_token</tt> <xref target="call-chaining"/>, the PS SHOULD revoke the person tokens it issued from it in the same cascade, at their resources and at the ASes it presented them to.</li>
<li><strong>Resource revokes a resource token it issued</strong>: The resource calls the revocation endpoint of the party named in the token's <tt>aud</tt>, and in four-party that of the <tt>ps</tt> as well. The recipient MUST NOT issue an auth token against that resource token, rejects a token request naming it with <tt>revoked_resource_token</tt> <xref target="token-endpoint-error-codes"/>, and SHOULD terminate a pending request it started for it, which the agent reads as <tt>revoked</tt> <xref target="polling-error-codes"/>. The recipient records the <tt>(iss, jti)</tt> whether or not it has seen the token, and MAY discard the entry once the current time is past the <tt>exp</tt> the revocation named plus clock skew.</li>
<li><strong>PS revokes a mission</strong>: The PS marks the mission as revoked. All subsequent token requests referencing that mission's <tt>s256</tt> are denied. The PS SHOULD revoke outstanding auth tokens issued under the mission.</li>
<li><strong>Agent provider revokes an agent token it issued</strong>: The agent provider calls the PS's revocation endpoint. The PS MUST deny subsequent requests presenting that agent token, and SHOULD revoke the person tokens and auth tokens it issued for that agent, and terminate what it federated by the four-party path above. The cascade is by agent identity: the PS revokes every person token and auth token it issued to that agent's <tt>sub</tt>, whichever agent token the agent presented when it asked. The revocation does not alter the agent-person binding <xref target="agent-person-binding"/>. An agent token the same provider issues later is verified on its own terms. The PS answers the agent provider with an empty <tt>200</tt> once its cascade is terminal.</li>
<li><strong>Agent provider stops issuing agent tokens</strong>: Existing agent tokens expire naturally.</li>
</ul>
<t><strong>Records.</strong> The parties that cascade retain what they issued. A PS records, for each agent token it accepts, that token's <tt>(iss, jti)</tt> and the <tt>sub</tt> it carried, until the agent token's <tt>exp</tt> plus clock skew. A PS records each person token it issues <xref target="person-token-endpoint"/>, and for each auth token it issued or federated against that person token, the <tt>jti</tt>, the resource it was for, and the <tt>exp</tt>. An AS records the same for each auth token it issues, along with the <tt>presented_jti</tt> it was issued against. A person token issued with an <tt>upstream_token</tt> is recorded with the upstream token's <tt>(iss, jti)</tt>, so a revocation of the earlier person token walks the chain from the PS's own records; an AS records nothing of the chain. An entry MAY be discarded once the current time is past the token's <tt>exp</tt> plus clock skew.</t>
</section>

<section anchor="presenting-a-revoked-token"><name>Presenting a Revoked Token</name>
<t>A recipient MUST say that a revoked token was revoked, not that it is malformed or expired. Where it says so depends on how the token was carried:</t>

<ul spacing="compact">
<li><strong>Agent, person, or auth token in the <tt>Signature-Key</tt> header</strong>: <tt>401</tt> with <tt>Signature-Error: error=revoked_jwt</tt> (<xref target="I-D.hardt-httpbis-signature-key"/>). A resource refusing a revoked auth token SHOULD also include <tt>AAuth-Requirement: requirement=person-token</tt> <xref target="requirement-person-token"/>. It MUST NOT answer with <tt>requirement=auth-token</tt> and a resource token, since the PS or AS would reject a resource token naming a revoked token with <tt>revoked_presented_token</tt>. For a revoked agent token no requirement repairs it; the agent obtains a fresh one from its provider.</li>
<li><strong>A token carried as a request parameter</strong>: the signature verified, so this is not a <tt>401</tt>. The recipient returns <tt>revoked_&lt;parameter&gt;_token</tt> in the response body <xref target="token-endpoint-error-codes"/>. A pending request already started against a withdrawn resource token, or whose upstream token or agent's agent token is revoked while it waits, terminates with <tt>revoked</tt> <xref target="polling-error-codes"/>, with <tt>detail</tt> saying which.</li>
</ul>
<t>Verifying a token does not ask the issuer about that token, so a resource learns of a revocation only when one reaches its revocation endpoint. A party that no revocation reaches is bounded by token lifetime alone: at most one hour for an auth token or a person token, five minutes for a resource token, and 24 hours for an agent token presented directly to a resource. Deployments requiring immediate termination should issue shorter-lived tokens rather than rely on revocation reaching every holder.</t>
</section>
</section>
</section>

<section anchor="incremental-adoption"><name>Incremental Adoption</name>
<t>AAuth is designed for incremental adoption. Each party — agent, resource, PS, AS — can independently add support. The system works at every partial adoption state. No coordination is required between parties.</t>

<section anchor="drop-in-migration"><name>Drop-In Replacement for API Keys and OAuth</name>
<t>The first two resource steps require neither a person server nor an access server. They map directly onto what resources already do today:</t>

<ul spacing="compact">
<li><strong>Agent identity access drops in where you use API keys.</strong> A resource that verifies the agent's HTTP Message Signature gets a cryptographic, per-agent identity in place of a shared secret — nothing to copy and leak, no pre-registration, no authorization flow. The agent signs, the resource recognizes who it is and applies its existing access control. This is identity-based access <xref target="overview-identity-access"/>; it involves no PS and no AS.</li>
<li><strong>Resource-managed access drops in where you use OAuth.</strong> A resource keeps its existing authorization — consent screens, OAuth access tokens, or session tokens — and wraps it: it returns its existing token opaquely via the <tt>AAuth-Access</tt> header <xref target="aauth-access"/>, bound to the agent's signature so it cannot be stolen and replayed as a standalone bearer token. The resource talks directly to the agent. This is resource-managed (two-party) access <xref target="overview-resource-managed"/>; it too involves no PS and no AS.</li>
</ul>
<t>Both modes are complete and useful on their own. Adding a PS (PS authorization, three-party) and an AS (federated authorization, four-party) is additive — it brings cross-domain identity assertion and policy federation — but neither is a prerequisite for the value a resource gets from the first two steps.</t>

<section anchor="consuming-a-resource"><name>Consuming a Resource End to End</name>
<t>A resource that wants agents to discover and use it with no prior integration SHOULD publish two things in its <tt>aauth-resource.json</tt> <xref target="resource-metadata"/>:</t>

<ul spacing="compact">
<li><strong><tt>access_mode</tt></strong> — the credential flow the agent should expect: <tt>agent-token</tt>, <tt>person-token</tt>, <tt>session-token</tt>, or <tt>auth-token</tt>.</li>
<li><strong>An R3 vocabulary.</strong> Resources SHOULD advertise an R3 vocabulary (<tt>r3_vocabularies</tt>, <xref target="I-D.hardt-aauth-r3"/>) describing their operations, so that an agent that knows only the resource's hostname can learn the API and begin using it. The R3 document itself is fetched only by the AS and PS, not the agent; the vocabulary (an OpenAPI, MCP, gRPC, or similar API description) is the agent-facing surface.</li>
</ul>
<t>An agent onboards as follows:</t>

<ol spacing="compact">
<li>Fetch <tt>aauth-resource.json</tt> — from the resource identifier's well-known URL, or from an <tt>aauth-resource</tt> link on the page the agent reached first <xref target="resource-metadata-link"/>; read <tt>access_mode</tt> and the advertised vocabulary.</li>
<li>Fetch the vocabulary to learn the resource's operations, then construct calls.</li>
<li>If <tt>access_mode</tt> is <tt>auth-token</tt> and the agent has no PS, it cannot complete that flow and SHOULD skip the resource.</li>
<li><t>Make the call and satisfy whatever the resource requires, bringing the user in only where the mode calls for it:</t>

<ul spacing="compact">
<li><strong><tt>agent-token</tt></strong> — the agent signs with its agent token and calls. If the resource needs to bind the agent to a user account (the equivalent of associating an API key with an account), it returns a <tt>202</tt> with <tt>requirement=interaction</tt> <xref target="requirement-responses"/> pointing at a login or account-link page; the agent brings the user there, directly or via the PS's interaction endpoint <xref target="interaction-endpoint"/>. Once bound, subsequent calls with the same agent token are recognized with no further interaction. No token is issued — the account-bound agent token is the durable credential.</li>
<li><strong><tt>session-token</tt></strong> — the agent's call, or a request to the <tt>authorization_endpoint</tt>, triggers a <tt>202</tt> with <tt>requirement=interaction</tt> pointing at the resource's existing consent or login flow. After the user completes it, the resource returns an opaque token via the <tt>AAuth-Access</tt> header <xref target="aauth-access"/>; the agent presents that token in <tt>Authorization: AAuth ...</tt>, bound to its signature, on subsequent calls.</li>
<li><strong><tt>auth-token</tt></strong> — the resource issues a resource token via the <tt>authorization_endpoint</tt> or a <tt>401</tt> <xref target="requirement-auth-token"/>. The agent sends it to its PS, which runs consent — bringing the user in at the PS, not the resource — and returns an auth token the agent signs with. Whether the PS asserts identity directly (three-party) or federates with the resource's AS (four-party) is invisible to the agent.</li>
</ul></li>
</ol>
<t>Throughout, the agent runs a single loop: make the request, read any <tt>AAuth-Requirement</tt>, satisfy it — bringing in the user where the requirement directs — and retry. The <tt>access_mode</tt> declaration lets the agent anticipate the flow; the runtime <tt>AAuth-Requirement</tt> remains authoritative, so a resource can mix modes across endpoints or escalate at any time.</t>
</section>
</section>

<section anchor="agent-adoption-path"><name>Agent Adoption Path</name>
<t>Each step builds on the previous one. An agent that adopts any step gains immediate value.</t>

<ol spacing="compact">
<li><strong>Obtain an agent token and sign requests</strong> (the <tt>jwt</tt> scheme, <tt>typ: aa-agent+jwt</tt>): The agent has a full AAuth identity with an <tt>aauth:local@domain</tt> identifier issued by an agent provider. It signs requests using HTTP Message Signatures (<xref target="RFC9421"/>) per the Signature-Key specification (<xref target="I-D.hardt-httpbis-signature-key"/>) and presents its agent token via the <tt>Signature-Key</tt> header under the <tt>jwt</tt> scheme. Resources that recognize signatures can verify the agent's identity and apply access control. Resources that don't ignore the signature and <tt>Signature-Key</tt> headers — existing auth mechanisms continue to work. This enables identity-based access.</li>
<li><strong>Add a person server</strong> (include <tt>ps</tt> claim in agent token): The agent can obtain auth tokens from its PS directly. Resources in three-party and four-party modes can issue resource tokens targeting the PS. Enables PS-issued auth tokens with user identity, <tt>tenant</tt>, <tt>groups</tt>, and <tt>roles</tt> claims.</li>
<li><strong>Add governance</strong> (create a mission): The agent creates a mission at its PS, gaining permissions, audit, PS-relayed interactions, and consent-managed resource access. The mission can be as simple as the user's prompt.</li>
</ol>
</section>

<section anchor="resource-adoption-path"><name>Resource Adoption Path</name>
<t>Each step builds on the previous one. A resource that adopts any step works with agents at all identity levels.</t>

<ol spacing="compact">
<li><strong>Recognize AAuth signatures</strong>: Verify HTTP Message Signatures and respond with <tt>Accept-Signature-Scheme</tt> headers (<xref target="I-D.hardt-httpbis-signature-key"/>). Resources that don't recognize AAuth ignore the signature headers — existing auth mechanisms continue to work. This is identity-based access.</li>
<li><strong>Manage authorization</strong>: Handle authorization with interaction, consent, or existing infrastructure — via <tt>401</tt> responses, an authorization endpoint, or both. Return <tt>AAuth-Access</tt> headers <xref target="aauth-access"/> for subsequent calls. This is resource-managed access (two-party).</li>
<li><strong>Accept identity claims from any PS</strong>: Verify person tokens and issue resource tokens with <tt>aud</tt> = the <tt>iss</tt> of the person token verified. The agent's PS returns an auth token asserting identity claims about the user and consent for the requested scope; the resource applies its own policy. This is PS authorization access (three-party).</li>
<li><strong>Deploy an access server</strong>: Issue resource tokens with <tt>aud</tt> = AS URL. The PS federates with the AS. This is federated access (four-party).</li>
</ol>
</section>

<section anchor="adoption-matrix"><name>Adoption Matrix</name>
<table>
<thead>
<tr>
<th>Agent</th>
<th>Resource</th>
<th>Mode</th>
<th>What Works</th>
</tr>
</thead>

<tbody>
<tr>
<td>Agent token</td>
<td>Recognizes signatures</td>
<td>Agent identity</td>
<td>Identity verification, access control by agent identity</td>
</tr>

<tr>
<td>Agent token</td>
<td>Manages authorization</td>
<td>Resource-managed</td>
<td>Resource-handled auth, interaction, <tt>AAuth-Access</tt></td>
</tr>

<tr>
<td>Person token</td>
<td>Accepts person tokens</td>
<td>Person identity</td>
<td>Resource knows the person and any mission; applies its own access control</td>
</tr>

<tr>
<td>Auth token</td>
<td>Issues resource tokens</td>
<td>PS authorization</td>
<td>PS asserts identity and consent for a scope; resource applies its own policy</td>
</tr>

<tr>
<td>Auth token</td>
<td>AS deployed</td>
<td>Federated authorization</td>
<td>Full federation, AS policy enforcement</td>
</tr>

<tr>
<td>Agent token + <tt>ps</tt> + mission</td>
<td>Any or none</td>
<td>+ governance</td>
<td>Tool-call permissions, audit, PS-relayed interaction, consent-managed access</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. Agent tokens bind that key to an agent identity and auth tokens bind an authorization grant to it; resource tokens bind the request to the resource's own identity, which is what prevents one resource being substituted for another in the authorization flow.</t>
</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 are on the same origin as the server that issued them <xref target="deferred-responses"/></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>410 Gone</tt>, except where a flow requires a repeated presentation of the same token to be answered from a retained result — deferred auth-token delivery <xref target="deferred-auth-token"/> and per-call grants (<xref target="I-D.hardt-aauth-r3"/>) — in which case the URL answers from the record until its retention ends, and returns <tt>410 Gone</tt> after</li>
</ul>
</section>

<section anchor="untrusted-input"><name>Untrusted Input</name>
<t>All protocol inputs — JSON request bodies, clarification responses, justification strings, mission descriptions, and token claims — are untrusted input from potentially adversarial parties. This is consistent with standard web security practice where HTTP request bodies, headers, and query parameters are always treated as untrusted. Implementations MUST sanitize all values before rendering to users and MUST validate all values before processing. Markdown fields MUST be sanitized before rendering to prevent script injection.</t>
</section>

<section anchor="agent-consent-surface-control"><name>Agent Control of the Consent Surface</name>
<t>The <tt>justification</tt> is written by the party requesting the access and rendered on the surface where the person decides whether to grant it. Sanitizing it prevents script injection; it does not prevent the agent from describing the access as something other than what the resource says it is, understating what an operation touches, or asserting a purpose the resource's own <tt>description</tt> and <tt>scope_descriptions</tt> contradict. The same holds for <tt>platform</tt>, <tt>device</tt>, and clarification responses, all of which are agent-attested.</t>
<t>The mitigation is attribution rather than filtering. A PS MUST distinguish resource-asserted from agent-asserted content, and MUST NOT decide on agent-asserted content alone where resource-asserted content covering the same operation is available <xref target="consent-presentation"/>. A PS that renders the agent's text and the resource's text with the same weight has given the agent an equal voice in describing its own request.</t>
</section>

<section anchor="interaction-code-misdirection"><name>Interaction Code Misdirection</name>
<t>An attacker could attempt to trick a user into approving an authorization request by directing them to an interaction URL with the attacker's code. The PS mitigates this by displaying the full request context — the agent's identity, the resource being accessed, and the requested scope — so the user can recognize requests they did not initiate. A stronger mitigation is for the PS to interact directly with the user via a pre-established channel (push notification, email, or existing session) using <tt>requirement=approval</tt>, which eliminates the possibility of misdirection through attacker-supplied links entirely.</t>
<t>The reverse threat — an attacker who knows a pending request's interaction URL but not its <tt>code</tt> and tries to guess it to drive the interaction — is bounded by the code-format rules in <xref target="interaction-code-format"/>. The minimum 40 bits of entropy make a single guess overwhelmingly likely to fail, and the mandatory rate-limit terminates the pending interaction after a few failed attempts, capping total guesses far below the entropy bound. These entropy and rate-limit requirements are the brute-force defense; they complement the user-recognition and pre-established-channel defenses above, which address misdirection of a legitimate code rather than recovery of an unknown one.</t>
</section>

<section anchor="link-relation-security"><name>Link Relation Security</name>
<t>An <tt>aauth-resource</tt> link <xref target="resource-metadata-link"/> is a statement by whoever controls the response that carries it, not by the resource it names. Two limits keep that harmless. The target is constrained to a well-known URL and the fetched document is verified against the URL it came from, so a link cannot cause an agent to accept metadata the resource did not publish; and the relation plays no part in key discovery, so it cannot affect what any verifier trusts.</t>
<t>What a link can do is steer. A page an attacker controls can point an agent at a resource the person did not intend, and the agent will then request a person token naming that resource and present it there. The answer is the one the protocol already gives for any resource an agent meets for the first time: the person token endpoint puts the question to the person, presenting the resource's own <tt>name</tt> and <tt>description</tt> <xref target="person-token-endpoint"/>, and a person token carries no authorization <xref target="person-token-not-authorization"/>. An agent SHOULD record where it found a link, so that a resource introduced by a third-party page is distinguishable from one the person named. An agent that parses HTML to find the relation is reading untrusted input <xref target="untrusted-input"/>.</t>
</section>

<section anchor="trust-posture-in-ps-asserted-access"><name>Trust Posture in PS Authorization Access</name>
<t>In three-party mode, the resource has no AS of its own — it accepts identity claims and consent from whichever PS issued the person token it verified. This is a deliberate trust posture: the resource externalizes identity claim issuance while retaining policy enforcement. Resources MUST apply their own policy on the resulting claims rather than treating the PS-issued auth token as a bearer authorization. Resources that need policy decisions made externally (per-resource scope enforcement, organizational gating, billing) should deploy an AS and use four-party mode.</t>
<t>Because identity assertion does not require pre-registration, the resource follows the same protocol flow whether it is meeting the user for the first time or recognizing a returning one. The auth token's <tt>(iss, sub)</tt> pair is a stable identifier per user per PS — the resource looks up the tuple and creates a new user record on a miss, matches an existing one on a hit. As in many OIDC deployments, registration and login are the same flow; the resource's own logic distinguishes the two outcomes. In multi-tenant deployments the auth token MAY also carry a <tt>tenant</tt> claim (<xref target="OpenID.Enterprise"/>); <tt>(iss, tenant, sub)</tt> identifies a user within an organization, and <tt>(iss, tenant)</tt> identifies the organization itself — useful for grouping users from the same employer or account.</t>
<t>The PS MUST protect its signing keys with appropriate rigor — compromise of a PS's signing key allows forgery of identity claims for every resource that accepts that PS.</t>
</section>

<section anchor="person-token-exposure"><name>Person Token Exposure</name>
<t>A person token identifies the person to a resource before any authorization decision, on every request that carries one including those the resource refuses. A resource therefore learns of people whose agents it never serves.</t>
<t>The directed <tt>sub</tt> bounds the exposure to one <tt>(PS, resource)</tt> pair. The PS bounds it further: it decides whether to issue at all, and SHOULD treat the first person token for a given resource as requiring the person's approval <xref target="person-token-endpoint"/>.</t>
<t>A person token grants nothing from the PS. Disclosure to a party without the signing key leaks a directed identifier and nothing more: <tt>cnf</tt> prevents that party from presenting it. The party that holds the key can present it at the one resource in <tt>aud</tt>, and a resource that serves on identity alone <xref target="overview-person-identity"/> serves it; that exposure is bounded by the token's single audience and its one-hour lifetime, not by <tt>cnf</tt>.</t>
</section>

<section anchor="continuity-not-proofing"><name>Continuity, Not Identity Proofing</name>
<t>A person token proves "the same entity again", not a verified legal identity. The protocol does not state what standard, if any, a PS applied before recognizing a person, and a resource MUST NOT infer one <xref target="person-tokens"/>. Continuity is sufficient for most resource access: authorizing an agent, keeping an account stable across agents and sessions, applying per-person policy. A resource requiring more — an age check, a residency check, regulated onboarding — obtains it out of band or through claims a person server explicitly asserts.</t>
</section>

<section anchor="person-token-org-policy"><name>Organization Identification</name>
<t>The OPTIONAL <tt>tenant</tt> claim declares the organization the person belongs to, and unlike <tt>sub</tt> it is not directed — the same value appears at every resource the organization's agents reach.</t>
<t>That is deliberate. It lets a resource apply organizational policy before it issues anything: recognise a contracted customer, or rate-limit and refuse an organization whose agents are abusing it. Without it a resource's only lever at first contact is the person server, which is far too coarse — refusing one would refuse every organization that uses it — and pairwise <tt>sub</tt> otherwise makes one organization's agents indistinguishable from many unrelated people.</t>
<t><tt>tenant</tt> is organizational context and MUST NOT be treated as part of the person's identifier, which is <tt>(iss, sub)</tt> <xref target="directed-identifiers"/>.</t>
</section>

<section anchor="person-token-not-authorization"><name>Person Token Is Not Authorization</name>
<t>A PS-issued auth token and a person token carry the same <tt>iss</tt>, <tt>dwk</tt>, <tt>aud</tt>, <tt>sub</tt>, and <tt>cnf</tt>. Only <tt>typ</tt> distinguishes them. A resource that verifies the signature and reads <tt>sub</tt> without checking <tt>typ</tt> accepts a person token wherever it accepts an auth token.</t>
<t>Implementations MUST check <tt>typ</tt> before acting on any AAuth JWT, and MUST reject <tt>aa-person+jwt</tt> where an auth token is required <xref target="person-token-verification"/>. Deployments SHOULD test this case explicitly; it fails open. <tt>upstream_token</tt> is not such a place: it accepts either type, and the recipient reads <tt>typ</tt> to choose the verification <xref target="upstream-token-verification"/>.</t>
</section>

<section anchor="incremental-consent"><name>Incremental Consent</name>
<t>A mission can be updated <xref target="mission-update"/>, and an update may broaden the work as well as narrow it. An agent could therefore propose a modest mission, obtain easy approval, and broaden it in steps each small enough to wave through, arriving somewhere the person would have refused had it been proposed at the outset.</t>
<t>The person's acceptance is required at every step, so no single step is unauthorized. What erodes is the person's sense of the whole. A PS SHOULD present the accumulated picture — the approved description together with the updates already accepted — when asking the person to accept another, rather than the increment alone.</t>
<t>The same erosion is available through a series of separate missions, so this is a property of incremental approval rather than of the update mechanism. It is stated here because the update mechanism makes it cheap.</t>
</section>

<section anchor="ps-approval-endpoint-auth"><name>PS Approval Endpoint Authentication</name>
<t>When the PS approval/consent endpoint is reachable beyond a single-user local deployment, the PS MUST authenticate the approving party before acting on a consent or denial decision. Acceptable mechanisms include an operator session cookie, a signed request from an authenticated operator, or an equivalent out-of-band channel.</t>
<t>An unauthenticated approval endpoint allows a remote party to consent on the user's behalf — a privilege escalation that breaks the agent-person binding invariant <xref target="agent-person-binding"/>. A locally-trusted PS (loopback only, no external network reachability) is exempt from this requirement provided it enforces OS-level access controls on the loopback interface.</t>
</section>

<section anchor="agent-person-binding"><name>Agent-Person Binding</name>
<t>The PS MUST ensure that each agent is associated with exactly one person. This one-to-one binding is a trust invariant — it ensures that every action an agent takes is attributable to a single accountable party.</t>
<t>The binding is typically established lazily — when the person first authorizes the agent at the PS via the interaction flow. The PS recognizes a returning agent by <tt>(agent_token.iss, agent_token.sub)</tt>; on first interaction with a new tuple for a person, the PS SHOULD treat it as a new-agent enrollment and surface this clearly at the consent screen, displaying the agent provider's name and logo (from agent provider metadata) alongside any agent-supplied display values (<tt>platform</tt>, <tt>device</tt>) provided in the request. An organization administrator may pre-authorize agents for the organization. Once established, the PS MUST NOT allow a different person to claim the same agent. If an agent's association needs to change (e.g., an employee leaves an organization), the existing binding MUST be revoked and a new binding established.</t>
<t>A request carrying <tt>upstream_token</tt> <xref target="call-chaining"/> neither uses nor establishes a binding. The intermediary acts for each person whose upstream token it presents, and the PS issues for the person that token identifies <xref target="intermediary-agent-identity"/>. The binding the invariant protects is the calling agent's, which the PS applied when it issued the person token the chain began with.</t>
<t>This invariant enables:</t>

<ul spacing="compact">
<li><strong>Accountability</strong>: Every authorization decision traces to a single person.</li>
<li><strong>Consent integrity</strong>: Consent granted by one person cannot be exercised by a different person through the same agent.</li>
<li><strong>Audit</strong>: The PS can provide a complete record of an agent's actions on behalf of its person.</li>
<li><strong>Revocation</strong>: Revoking an agent's association with its person immediately prevents the agent from obtaining new auth tokens.</li>
</ul>
</section>

<section anchor="ps-as-high-value-target"><name>PS as High-Value Target</name>
<t>The PS is a centralized authority that sees every authorization in a mission. PS implementations MUST apply appropriate security controls including access control, audit logging, and monitoring. Compromise of a PS could affect all agents and missions it manages.</t>
<t>Several architectural properties mitigate this centralization risk. The person chooses their PS — no other party in the protocol imposes a PS, and the person can migrate to a different PS at any time. The PS MAY delegate authentication to an identity provider chosen by the person or organization (e.g., an enterprise IdP via OIDC federation), reducing the PS's role in credential management. The PS MAY also delegate policy evaluation to external services selected by the person, so that consent and authorization decisions are not solely determined by the PS operator. To the rest of the protocol, the PS presents a single interface regardless of how it is composed internally.</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 anchor="non-repudiation-and-audit-after-key-rotation"><name>Non-Repudiation and Audit After Key Rotation</name>
<t>AAuth signatures prove authenticity at request time: a valid HTTP Message Signature shows that the signer held the private key bound to the presented identity when the request was made (proof-of-possession). This is request-time authentication, not long-term non-repudiation. Agent keys are short-lived and agent providers rotate their JWKS; once a key is removed from the issuer's JWKS, a signature made with it can no longer be verified by re-fetching the JWKS later. The persistent identifiers — the agent token's <tt>sub</tt> and the person's directed <tt>sub</tt> — do not by themselves cryptographically prove that a specific key signed a specific request at a specific time once that key is gone.</t>
<t>This is partly by design — short-lived keys and directed identifiers <xref target="directed-identifiers"/> limit long-term linkability. Deployments that require durable audit or non-repudiation beyond a key's lifetime SHOULD capture the evidence at verification time, while the key is still discoverable, rather than relying on re-verification later:</t>

<ul spacing="compact">
<li><strong>Archive the verified artifacts.</strong> At verification time, record the signed request (covered components and signature), the <tt>Signature-Key</tt> value (the presented key or JWT), the verification result, and a trusted timestamp. Optionally snapshot the issuer's JWKS entry (<tt>kid</tt> + JWK) so the key binding can be re-checked independently of later rotation.</li>
<li><strong>Use external timestamping or transparency logs</strong> where stronger non-repudiation is needed — for example, RFC 3161 <xref target="RFC3161"/> timestamps over the signed request, or appending verification records to a tamper-evident log.</li>
<li><strong>Bind audit records to durable identifiers.</strong> Index archived records by <tt>(iss, sub)</tt> for agents and by <tt>jti</tt> for tokens, so later review can attribute activity even though the signing key is no longer live.</li>
</ul>
<t>These measures trade privacy for durability: archived signatures and keys are correlatable, so deployments MUST balance audit retention against the privacy-preserving properties of short-lived keys and directed identifiers <xref target="privacy-considerations"/>, and apply appropriate retention limits and access controls.</t>
<t>A related case is the verifier that is the first to see the artifact, minutes or hours after it was signed. There the question is not whether evidence survives key rotation but whether the artifact was valid when it was signed, and the signed <tt>created</tt> parameter answers it <xref target="freshness-and-replay"/>.</t>
</section>
</section>

<section anchor="privacy-considerations"><name>Privacy Considerations</name>

<section anchor="directed-identifiers"><name>Directed Identifiers</name>
<t>The PS SHOULD provide a pairwise pseudonymous user identifier (<tt>sub</tt>) per resource, preventing resources from correlating users across trust domains. Each resource sees a different <tt>sub</tt> for the same user, preserving user privacy.</t>
<t>A <tt>sub</tt> MUST be unique within the issuer, so <tt>(iss, sub)</tt> identifies the person unambiguously and <tt>tenant</tt> is never part of the identifier. The same value MUST be used in the person token <xref target="person-token-structure"/>, in the resource token the resource derives from it, and in every auth token issued for that resource, and MUST NOT vary with the agent or its key.</t>
<t>Directed identifiers limit correlation between resources. They do not make a person's activity at one resource unlinkable across their agents: the agent signs with one key everywhere and that key appears in <tt>cnf</tt> in every token, so parties able to compare thumbprints correlate regardless of <tt>sub</tt>.</t>
<t>The person token reduces what a resource learns before authorization rather than increasing it. Presenting an agent token discloses the agent provider's domain and an agent identifier that is the same at every resource the agent visits — and because each agent belongs to exactly one person <xref target="agent-person-binding"/>, that identifier is a globally correlatable pseudonym for the person. A person token replaces both with an identifier scoped to the one resource receiving it.</t>
</section>

<section anchor="ps-visibility"><name>PS Visibility</name>
<t>In three-party and four-party modes, the PS sees every authorization request made by its agents — including the resource being accessed, the requested scope, and the mission context. This centralized visibility enables governance and audit, but it also means the PS is a sensitive data aggregation point. The person chooses to trust their PS with this visibility — no other party imposes the choice. PS implementations MUST apply appropriate access controls and data retention policies.</t>
<t>In two-party mode, no PS is involved and there is no centralized visibility — the resource handles authorization directly with the agent.</t>
</section>

<section anchor="mission-content-exposure"><name>Mission Content Exposure</name>
<t>The mission JSON is visible to the PS and, when included in resource tokens and auth tokens via the <tt>s256</tt> hash, its integrity is verifiable by any party that holds it. The approved mission JSON is shared between the agent and PS. Resources and ASes see only the <tt>s256</tt> hash and the PS that approved it, not the full mission content.</t>
</section>
</section>

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

<section anchor="http-header-field-registration"><name>HTTP Header Field Registration</name>
<t>This specification registers the following HTTP header fields in the "Hypertext Transfer Protocol (HTTP) Field Name Registry" established by <xref target="RFC9110"/>:</t>

<ul>
<li>Header Field Name: <tt>AAuth-Requirement</tt></li>
<li>Status: permanent</li>
<li>Structured Type: Dictionary</li>
<li><t>Reference: This document, <xref target="requirement-responses"/></t>
</li>
<li><t>Header Field Name: <tt>AAuth-Access</tt></t>
</li>
<li><t>Status: permanent</t>
</li>
<li><t>Reference: This document, <xref target="aauth-access"/></t>
</li>
<li><t>Header Field Name: <tt>AAuth-Capabilities</tt></t>
</li>
<li><t>Status: permanent</t>
</li>
<li><t>Structured Type: List</t>
</li>
<li><t>Reference: This document, <xref target="aauth-capabilities"/></t>
</li>
</ul>
</section>

<section anchor="http-authentication-scheme-registration"><name>HTTP Authentication Scheme Registration</name>
<t>This specification registers the following HTTP authentication scheme in the "Hypertext Transfer Protocol (HTTP) Authentication Scheme Registry" established by <xref target="RFC9110"/>:</t>

<ul spacing="compact">
<li>Authentication Scheme Name: <tt>AAuth</tt></li>
<li>Reference: This document, <xref target="aauth-access"/></li>
<li>Notes: Used with session tokens returned via the <tt>AAuth-Access</tt> header. The token MUST be bound to an HTTP Message Signature — the <tt>authorization</tt> field MUST be included in the signature's covered components.</li>
</ul>
</section>

<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-provider-metadata"/></td>
</tr>

<tr>
<td><tt>aauth-person.json</tt></td>
<td>IETF</td>
<td>This document, <xref target="ps-metadata"/></td>
</tr>

<tr>
<td><tt>aauth-access.json</tt></td>
<td>IETF</td>
<td>This document, <xref target="access-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="link-relation-type-registration"><name>Link Relation Type Registration</name>
<t>This specification registers the following link relation type in the IANA Link Relation Types registry per <xref target="RFC8288"/>, Section 4.2:</t>

<ul spacing="compact">
<li>Relation Name: <tt>aauth-resource</tt></li>
<li>Description: Refers to the AAuth resource metadata document for the resource that the link context belongs to or describes.</li>
<li>Reference: This document, <xref target="resource-metadata-link"/></li>
<li>Notes: The target MUST be a server identifier followed by <tt>/.well-known/aauth-resource.json</tt>; recipients verify the document against the URL it was fetched from and do not use the relation for key discovery.</li>
</ul>
</section>

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

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

<ul spacing="compact">
<li>Type name: application</li>
<li>Subtype name: aa-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, PSes, and ASes</li>
<li>Fragment identifier considerations: N/A</li>
</ul>
</section>

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

<ul spacing="compact">
<li>Type name: application</li>
<li>Subtype name: aa-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 ASes, agents, and resources</li>
<li>Fragment identifier considerations: N/A</li>
</ul>
</section>

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

<ul spacing="compact">
<li>Type name: application</li>
<li>Subtype name: aa-person+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="person-tokens"/></li>
<li>Applications that use this media type: AAuth PSes, agents, and resources</li>
<li>Fragment identifier considerations: N/A</li>
</ul>
</section>

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

<ul spacing="compact">
<li>Type name: application</li>
<li>Subtype name: aa-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 ASes</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>aa-agent+jwt</tt></td>
<td>This document, <xref target="agent-tokens"/></td>
</tr>

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

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

<tr>
<td><tt>aa-resource+jwt</tt></td>
<td>This document, <xref target="resource-tokens"/></td>
</tr>
</tbody>
</table><t>The following JWT <tt>typ</tt> values are registered by AAuth Events (<xref target="I-D.hardt-aauth-events"/>):</t>
<table>
<thead>
<tr>
<th>Type Value</th>
<th>Reference</th>
</tr>
</thead>

<tbody>
<tr>
<td><tt>aa-subscribe+jwt</tt></td>
<td><xref target="I-D.hardt-aauth-events"/></td>
</tr>

<tr>
<td><tt>aa-event+jwt</tt></td>
<td><xref target="I-D.hardt-aauth-events"/></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>ps</tt></td>
<td>Person server URL — the agent's person server in an agent token, and the person server whose namespace <tt>sub</tt> belongs to in a resource or auth token</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, in a resource token</td>
<td>IETF</td>
<td>This document</td>
</tr>

<tr>
<td><tt>parent_agent</tt></td>
<td>Parent agent identifier in a sub-agent's agent token</td>
<td>IETF</td>
<td>This document</td>
</tr>

<tr>
<td><tt>presented_jti</tt></td>
<td>The <tt>jti</tt> of the person token or auth token a resource token is bound to</td>
<td>IETF</td>
<td>This document</td>
</tr>

<tr>
<td><tt>mission_s256</tt></td>
<td>SHA-256 hash of the approved mission JSON, in person, resource, and auth tokens</td>
<td>IETF</td>
<td>This document</td>
</tr>

<tr>
<td><tt>account</tt></td>
<td>Account the authorization is for, in resource and auth tokens</td>
<td>IETF</td>
<td>This document</td>
</tr>

<tr>
<td><tt>interaction</tt></td>
<td>Resource interaction step required before authorization, an object with <tt>url</tt> and <tt>code</tt>, in a resource token</td>
<td>IETF</td>
<td>This document</td>
</tr>
</tbody>
</table></section>

<section anchor="aauth-requirement-value-registry"><name>AAuth Requirement Value Registry</name>
<t>This specification establishes the AAuth Requirement Value Registry. The registry policy is Specification Required (<xref target="RFC8126"/>, Section 4.6). See <xref target="designated-expert-instructions"/> for instructions to the designated expert.</t>
<table>
<thead>
<tr>
<th>Value</th>
<th>Reference</th>
</tr>
</thead>

<tbody>
<tr>
<td><tt>agent-token</tt></td>
<td>This document</td>
</tr>

<tr>
<td><tt>person-token</tt></td>
<td>This document</td>
</tr>

<tr>
<td><tt>interaction</tt></td>
<td>This document</td>
</tr>

<tr>
<td><tt>approval</tt></td>
<td>This document</td>
</tr>

<tr>
<td><tt>auth-token</tt></td>
<td>This document</td>
</tr>

<tr>
<td><tt>clarification</tt></td>
<td>This document</td>
</tr>

<tr>
<td><tt>claims</tt></td>
<td>This document</td>
</tr>
</tbody>
</table></section>

<section anchor="aauth-capability-value-registry"><name>AAuth Capability Value Registry</name>
<t>This specification establishes the AAuth Capability Value Registry. The registry policy is Specification Required (<xref target="RFC8126"/>, Section 4.6). See <xref target="designated-expert-instructions"/> for instructions to the designated expert.</t>
<table>
<thead>
<tr>
<th>Value</th>
<th>Reference</th>
</tr>
</thead>

<tbody>
<tr>
<td><tt>interaction</tt></td>
<td>This document</td>
</tr>

<tr>
<td><tt>clarification</tt></td>
<td>This document</td>
</tr>

<tr>
<td><tt>payment</tt></td>
<td>This document</td>
</tr>
</tbody>
</table></section>

<section anchor="aauth-platform-value-registry"><name>AAuth Platform Value Registry</name>
<t>This specification establishes the AAuth Platform Value Registry, used as values of the <tt>platform</tt> request parameter sent to the PS token endpoint <xref target="ps-token-endpoint"/>. The registry policy is Specification Required (<xref target="RFC8126"/>, Section 4.6). See <xref target="designated-expert-instructions"/> for instructions to the designated expert.</t>
<table>
<thead>
<tr>
<th>Value</th>
<th>Description</th>
<th>Reference</th>
</tr>
</thead>

<tbody>
<tr>
<td><tt>web</tt></td>
<td>Browser-hosted web application</td>
<td>This document</td>
</tr>

<tr>
<td><tt>mobile</tt></td>
<td>Native mobile application (iOS, Android)</td>
<td>This document</td>
</tr>

<tr>
<td><tt>desktop</tt></td>
<td>Native desktop application (macOS, Windows, Linux)</td>
<td>This document</td>
</tr>

<tr>
<td><tt>workload</tt></td>
<td>Headless server-class workload (backend service, CI runner, scheduled job, edge function)</td>
<td>This document</td>
</tr>

<tr>
<td><tt>self-hosted</tt></td>
<td>User-controlled deployment under a domain the user controls</td>
<td>This document</td>
</tr>
</tbody>
</table></section>

<section anchor="aauth-access-mode-value-registry"><name>AAuth Access Mode Value Registry</name>
<t>This specification establishes the AAuth Access Mode Value Registry, used as values of the <tt>access_mode</tt> field in resource metadata <xref target="resource-metadata"/>. The registry policy is Specification Required (<xref target="RFC8126"/>, Section 4.6). See <xref target="designated-expert-instructions"/> for instructions to the designated expert.</t>
<table>
<thead>
<tr>
<th>Value</th>
<th>Description</th>
<th>Reference</th>
</tr>
</thead>

<tbody>
<tr>
<td><tt>agent-token</tt></td>
<td>The resource authorizes on the agent's identity alone</td>
<td>This document</td>
</tr>

<tr>
<td><tt>person-token</tt></td>
<td>The resource authorizes on the person's identity alone</td>
<td>This document</td>
</tr>

<tr>
<td><tt>session-token</tt></td>
<td>The resource manages authorization itself and issues a session token</td>
<td>This document</td>
</tr>

<tr>
<td><tt>auth-token</tt></td>
<td>The agent obtains an auth token from its PS using a resource token</td>
<td>This document</td>
</tr>
</tbody>
</table></section>

<section anchor="designated-expert-instructions"><name>Designated Expert Instructions</name>
<t>Registration requests for the AAuth Requirement Value, AAuth Capability Value, AAuth Platform Value, and AAuth Access Mode Value registries are evaluated by a designated expert appointed by the IESG, using the Specification Required policy (<xref target="RFC8126"/>, Section 4.6).</t>
<t>Registration requests should be sent to IANA, which will forward them to the designated expert. The expert is expected to respond within two weeks. Denials should include an explanation and, if applicable, suggestions for how the request could be revised to be successful.</t>
<t>A registration request must include the proposed value, a brief description of its meaning, and a reference to the specification defining it. The designated expert should verify that:</t>

<ul spacing="compact">
<li>The referenced specification is stable and freely available, and describes the value's semantics in sufficient detail that interoperable, independent implementations are possible.</li>
<li>The proposed value is a lowercase token using only lowercase letters and hyphen, consistent with the registries' existing entries, and is not confusingly similar to an existing entry.</li>
<li>The registration does not duplicate the semantics of an existing entry without clear justification.</li>
<li>For the Requirement Value and Capability Value registries, the specification defines the protocol behavior expected of a party that declares or encounters the value, including how a party that does not understand the value behaves.</li>
<li>For the Platform Value registry, the value describes a distinct runtime context that is meaningful for display to a person at a consent screen or dashboard, and the description does not overstate the security properties the value conveys.</li>
<li>For the Access Mode Value registry, the value names a credential flow an agent can carry out, the specification defines what the agent presents and how it obtains it, and an agent that does not understand the value can still fall back to the runtime <tt>AAuth-Requirement</tt>.</li>
</ul>
</section>

<section anchor="uri-scheme-registration"><name>URI Scheme Registration</name>
<t>This specification registers the <tt>aauth</tt> URI scheme in the "Uniform Resource Identifier (URI) Schemes" registry (<xref target="RFC7595"/>):</t>

<ul spacing="compact">
<li>Scheme name: <tt>aauth</tt></li>
<li>Status: Permanent</li>
<li>Applications/protocols that use this scheme: AAuth Protocol</li>
<li>Contact: IETF</li>
<li>Change controller: IETF</li>
<li>Reference: This document, <xref target="agent-identifiers"/></li>
</ul>
<t>The <tt>aauth</tt> URI scheme follows the pattern established by the <tt>acct</tt> scheme (<xref target="RFC7565"/>). An <tt>aauth</tt> URI identifies an agent instance and has the syntax <tt>aauth:local@domain</tt>, where <tt>local</tt> is the agent-specific part and <tt>domain</tt> is the agent provider's domain name. The <tt>aauth</tt> URI is used in the <tt>sub</tt> and <tt>parent_agent</tt> claims of agent tokens and in the <tt>agent</tt> field of the mission blob.</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:</t>

<ul spacing="compact">
<li><strong>TypeScript</strong> — <eref target="https://github.com/aauth-dev/packages-js">github.com/aauth-dev/packages-js</eref>. Organization: Hellō. Coverage: agent token issuance, HTTP Message Signatures, resource token exchange, PS token endpoint. Level of maturity: exploratory.</li>
<li><strong>.NET</strong> — <eref target="https://github.com/aauth-dev/dotnet-samples">github.com/aauth-dev/dotnet-samples</eref> (NuGet: <tt>AAuth</tt>). Contact: Dasith Wijesiriwardena. Coverage: SDK spanning the access modes, the three-party challenge/exchange flow (autonomous and deferred consent), signature verification middleware, resource and auth token builders, and JWKS/metadata discovery, plus Blazor sample apps. Level of maturity: exploratory.</li>
<li><strong>Python</strong> — <eref target="https://github.com/christian-posta/aauth-full-demo">github.com/christian-posta/aauth-full-demo</eref>. Contact: Christian Posta. Coverage: agent-to-resource flows with Keycloak as AS. Level of maturity: exploratory.</li>
<li><strong>Java (Keycloak SPI)</strong> — <eref target="https://github.com/christian-posta/keycloak-aauth-extension">github.com/christian-posta/keycloak-aauth-extension</eref>. Contact: Christian Posta. Coverage: AAuth access server extension for Keycloak 26.2.5. Level of maturity: exploratory.</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>
<li><t>draft-hardt-oauth-aauth-protocol-11</t>

<ul spacing="compact">
<li>Restructured for readability: sections follow the order an implementer meets them, each normative statement is made once, and rationale moved from Protocol Primitives to the Design Rationale appendix. Agent Identity is now Agents, with an Agent Provider subsection; Person Token moved under the Person Token Endpoint.</li>
<li>A revocation request's signature MUST cover <tt>content-digest</tt> and <tt>content-type</tt> at every recipient. Issue #165.</li>
<li>Token Revocation: a PS cascades an agent token revocation by agent identity; a recipient records revoked resource tokens it has not seen; added <tt>rate_limited</tt>; the polling <tt>revoked</tt> code covers any token a pending request depends on. Issues #178, #179, #180, #182, #185.</li>
<li>Token Revocation: defined polling of a <tt>202</tt>, the <tt>200</tt> body, and how long a recipient holds the connection. Issues #181, #183, #184.</li>
<li>A resource token MAY carry <tt>login_hint</tt>, which the agent passes to its PS. Issue #163.</li>
<li>Adoption Matrix: the Agent column names the auth token in the PS authorization and federated authorization rows. Issue #161.</li>
<li>The person token request takes the OPTIONAL parameters of the auth token request. Issues #175, #177.</li>
<li>Call chaining accepts a person token as <tt>upstream_token</tt>; downstream tokens expire no later than the upstream token and carry its <tt>mission_s256</tt>. A sub-agent's agent token <tt>iss</tt> MUST equal its parent's.</li>
<li>An intermediary MUST be its own agent provider.</li>
<li>A revocation recipient answers once its cascade is terminal, and an AS reports the outcome to the PS in <tt>downstream</tt>. A person token revocation reaches call chains. Issue #173.</li>
<li>The agent identifier <tt>local</tt> part accepts uppercase letters. Issue #164.</li>
<li>Added the person token (<tt>aa-person+jwt</tt>), <tt>person_token_endpoint</tt>, and <tt>requirement=person-token</tt>. Issues #87, #97.</li>
<li>Five resource access modes and the AAuth Access Mode Value Registry. Renamed <tt>token_endpoint</tt> to <tt>auth_token_endpoint</tt>; the resource-managed credential is the session token.</li>
<li>A resource MUST verify a person token or auth token before issuing a resource token. No token a resource reads carries an agent identifier; <tt>act</tt> removed. Auth tokens carry <tt>ps</tt> and a directed <tt>sub</tt>.</li>
<li>Added <tt>presented_jti</tt> and the <tt>presented_token</tt> parameter. Issues #95, #152.</li>
<li>Missions: <tt>mission_s256</tt> replaces the <tt>mission</tt> object; <tt>AAuth-Mission</tt> and <tt>approver</tt> removed; the mission endpoint takes propose, update, and completion; added <tt>mission_terminated</tt>.</li>
<li>Call chaining routes on the auth token's <tt>ps</tt> claim.</li>
<li>Token Revocation reworked: the request is <tt>jti</tt> and <tt>exp</tt>, each token type has one recipient, and a revoked token is answered <tt>revoked_jwt</tt> or <tt>revoked_&lt;parameter&gt;_token</tt>. Issues #146, #154.</li>
<li>Expiry: <tt>exp</tt> has no tolerance, <tt>iat</tt> is REQUIRED, and the agent refreshes with five minutes left.</li>
<li>A server signing in its own right uses the <tt>jwks_uri</tt> scheme. Requests with a body to a PS or AS MUST cover <tt>content-digest</tt> and <tt>content-type</tt>. Added <tt>accept_signature_algs</tt>. Issue #94.</li>
<li>Consent Presentation: resource-asserted and agent-asserted content MUST be visually distinguished.</li>
<li>Added the Supervisor role, the PS conformance floor, and the Minimal Person Server appendix. Removed Third-Party Login and <tt>login_endpoint</tt>. Issue #155.</li>
<li><tt>requirement=auth-token</tt> MAY be delivered as a <tt>202</tt> deferred response. Added <tt>as_unreachable</tt> and the <tt>aauth-resource</tt> link relation. Issue #92.</li>
<li>Consistency pass: common JWT claims and verification stated once, typed error codes only for tokens passed as parameters, and every party MUST support <tt>Ed25519</tt>.</li>
</ul></li>
<li><t>draft-hardt-oauth-aauth-protocol-10</t>

<ul spacing="compact">
<li>Adopted <tt>Ed25519</tt> (<xref target="RFC9864"/>) in place of <tt>EdDSA</tt>; <tt>alg</tt> is REQUIRED and fully specified. Issue #57.</li>
<li>A <tt>cnf</tt> JWK and every key at an AAuth server's <tt>jwks_uri</tt> MUST carry <tt>alg</tt>.</li>
<li>Aligned verification and error codes with <xref target="I-D.hardt-httpbis-signature-key"/>.</li>
<li>Revocation identifies a token by <tt>(iss, jti)</tt>; an agent provider revokes an agent token at the PS. Issues #59, #60.</li>
<li>A downstream issuer MUST NOT copy a directed <tt>sub</tt> from an upstream token. Issue #41.</li>
<li>Added the OPTIONAL <tt>account</tt> authorization endpoint parameter. Issue #52.</li>
</ul></li>
<li><t>draft-hardt-oauth-aauth-protocol-09</t>

<ul spacing="compact">
<li>Clarification chat: added the <tt>action</tt> discriminator.</li>
<li>Errors use RFC 9457 problem details.</li>
</ul></li>
<li><t>draft-hardt-oauth-aauth-protocol-08</t>

<ul spacing="compact">
<li>Call chaining: upstream token <tt>aud</tt> MUST equal the intermediary's agent token <tt>iss</tt>; routing follows the upstream auth token.</li>
<li>The interaction code is a correlation identifier, not a credential.</li>
</ul></li>
<li><t>draft-hardt-oauth-aauth-protocol-07</t>

<ul spacing="compact">
<li>Added Interaction Callback Errors. Added Joshua Gay to Acknowledgments.</li>
</ul></li>
<li><t>draft-hardt-oauth-aauth-protocol-06</t>

<ul spacing="compact">
<li>Interoperability clarifications from Joshua Gay's feedback. The interoperability demo profile moved to a separate document.</li>
</ul></li>
<li><t>draft-hardt-oauth-aauth-protocol-05</t>

<ul spacing="compact">
<li><tt>act</tt> is OPTIONAL; <tt>act.agent</tt> names the immediate upstream agent.</li>
</ul></li>
<li><t>draft-hardt-oauth-aauth-protocol-04</t>

<ul spacing="compact">
<li>Replaced <tt>act.sub</tt> with <tt>act.agent</tt>. Issue #47.</li>
</ul></li>
<li><t>draft-hardt-oauth-aauth-protocol-03</t>

<ul spacing="compact">
<li>Metadata: added a common-fields table and documented the RFC 9728 divergences.</li>
<li>Metadata: added <tt>documentation_uri</tt>.</li>
<li>Updated the Crockford base32 citation.</li>
</ul></li>
<li><t>draft-hardt-oauth-aauth-protocol-02</t>

<ul spacing="compact">
<li>Added sub-agents.</li>
<li>Renamed <tt>interaction_required</tt> to <tt>user_unreachable</tt>; added <tt>interaction_unavailable</tt> and <tt>max_wait</tt>.</li>
<li>Added <tt>capabilities</tt> and <tt>prompt</tt> to the PS token endpoint.</li>
<li>Added <tt>requirement=agent-token</tt>.</li>
<li>Added the <tt>access_mode</tt> resource metadata field and two walkthroughs.</li>
<li>Added a Markdown <tt>description</tt> to each metadata document.</li>
<li>The returned <tt>issuer</tt> MUST match the metadata URL.</li>
<li>Call chaining: the intermediary signs with its own key.</li>
<li>Added rationale for the mandated covered components.</li>
<li>Added a Security Consideration on non-repudiation after key rotation.</li>
<li>Added a pointer to AAuth Bootstrap.</li>
<li>Diagrams use snake_case token names.</li>
<li>Named the mission reference.</li>
<li>AAuth never uses <tt>WWW-Authenticate</tt>.</li>
<li>Specified the interaction <tt>code</tt> format.</li>
<li>Editorial consistency pass.</li>
</ul></li>
<li><t>draft-hardt-oauth-aauth-protocol-01</t>

<ul spacing="compact">
<li>Renamed PS-managed access to PS-asserted access.</li>
<li>Renamed Agent Server to Agent Provider.</li>
<li>Added Roles.</li>
<li>Added Policy Evaluation Points.</li>
<li>Added PS-AS Collapse.</li>
<li>Added Trust Posture in PS-Asserted Access.</li>
<li>Added the <tt>platform</tt> and <tt>device</tt> request parameters and the AAuth Platform Value Registry.</li>
<li>Replaced <tt>org</tt> with the <tt>tenant</tt> claim.</li>
<li>Consistency pass.</li>
<li>The AAuth Bootstrap reference is informative.</li>
</ul></li>
<li><t>draft-hardt-oauth-aauth-protocol-00</t>

<ul spacing="compact">
<li>Initial draft. Replaces <eref target="https://datatracker.ietf.org/doc/draft-hardt-aauth-protocol/02/">draft-hardt-aauth-protocol-02</eref>; no technical changes.</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, and contributors who raised issues and pull requests: Aaron Parecki, Ben McAdams, Christian Posta, Danny Fuhriman, Dasith Wijesiriwardena, David Brossard, Frederik Krogsdal Jacobsen, He Gu, Jared Hanson, Jeoffrey Haeyaert, João André Marques, Joi Ito, Joshua Gay, Karl McGuinness, Ken Huang, Lukas Friman, Mark Hendrickson, Mayur Agnihotri, Nate Barbettini, Nick Gamb, Paul Carleton, Rohan Harikumar, Sanjay Dalal, Scott Motte, Wils Dawson, Yolanda Cao, Zeeshan Khan.</t>
</section>

</middle>

<back>
<references><name>References</name>
<references><name>Normative References</name>
<reference anchor="I-D.hardt-httpbis-signature-key" target="https://datatracker.ietf.org/doc/draft-hardt-httpbis-signature-key">
  <front>
    <title>HTTP Signature Keys</title>
    <author fullname="Dick Hardt" initials="D." surname="Hardt">
      <organization>Hellō</organization>
    </author>
    <author fullname="Thibault Meunier" initials="T." surname="Meunier">
      <organization>Cloudflare</organization>
    </author>
    <date year="2026"/>
  </front>
</reference>
<reference anchor="IANA.JOSE.Algorithms" target="https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms">
  <front>
    <title>JSON Web Signature and Encryption Algorithms</title>
    <author>
      <organization>IANA</organization>
    </author>
  </front>
</reference>
<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.7517.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.7595.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.8032.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8126.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8288.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8414.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"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9457.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9530.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9651.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9864.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9964.xml"/>
</references>
<references><name>Informative References</name>
<reference anchor="CommonMark" target="https://spec.commonmark.org/0.31.2/">
  <front>
    <title>CommonMark Spec</title>
    <author fullname="John MacFarlane" initials="J." surname="MacFarlane"/>
    <date year="2024"/>
  </front>
</reference>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml3/reference.I-D.crockford-davis-base32-for-humans.xml"/>
<reference anchor="I-D.hardt-aauth-bootstrap" target="https://datatracker.ietf.org/doc/draft-hardt-aauth-bootstrap">
  <front>
    <title>AAuth Bootstrap Guidance</title>
    <author fullname="Dick Hardt" initials="D." surname="Hardt">
      <organization>Hellō</organization>
    </author>
    <date year="2026"/>
  </front>
</reference>
<reference anchor="I-D.hardt-aauth-events" target="https://github.com/dickhardt/AAuth">
  <front>
    <title>AAuth Events</title>
    <author fullname="Dick Hardt" initials="D." surname="Hardt">
      <organization>Hellō</organization>
    </author>
    <date year="2026"/>
  </front>
</reference>
<reference anchor="I-D.hardt-aauth-r3" target="https://github.com/dickhardt/AAuth">
  <front>
    <title>AAuth Rich Resource Requests (R3)</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.ryan-httpauth-payment.xml"/>
<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.3161.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.5905.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.7565.xml"/>
<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.7636.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.7643.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"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9635.xml"/>
<reference anchor="x402" target="https://docs.x402.org">
  <front>
    <title>x402: HTTP 402 Payment Protocol</title>
    <author>
      <organization>x402 Foundation</organization>
    </author>
    <date year="2025"/>
  </front>
</reference>
</references>
</references>

<section anchor="detailed-flows"><name>Detailed Flows</name>
<t>This appendix provides flow diagrams for the chaining patterns defined in the main specification, where the choreography is hard to follow from prose alone.</t>

<section anchor="flow-call-chaining"><name>Four-Party: Call Chaining</name>
<t>See <xref target="call-chaining"/> for normative requirements. Resource 1 acts as an agent, sending the downstream resource token, the person token it presented to Resource 2, its own agent token, and the upstream token to the PS. The flow shows an agent that presented an auth token to Resource 1; one that presented a person token, to a Resource 1 serving on identity, is the same with the person token as <tt>upstream_token</tt>.</t>

<sourcecode type="ascii-art"><![CDATA[Agent        Resource 1       Resource 2          PS
  |              |                |                 |
  | HTTP Sig w/  |                |                 |
  | auth_token   |                |                 |
  |------------->|                |                 |
  |              |                |                 |
  |              | HTTP Sig w/    |                 |
  |              | R1 person_token|                 |
  |              |--------------->|                 |
  |              |                |                 |
  |              | 401            |                 |
  |              | + resource_tok |                 |
  |              |<---------------|                 |
  |              |                |                 |
  |              | POST auth_token_endpoint         |
  |              | resource_token from R2           |
  |              | presented_token                  |
  |              | upstream_token                   |
  |              | agent_token (R1's)               |
  |              |--------------------------------->|
  |              |                |                 |
  |              |                | [PS federates   |
  |              |                |  with R2's AS]  |
  |              |                |                 |
  |              | auth_token for R2                |
  |              |<---------------------------------|
  |              |                |                 |
  |              | HTTP Sig w/    |                 |
  |              | auth_token     |                 |
  |              |--------------->|                 |
  |              |                |                 |
  |              | 200 OK         |                 |
  |              |<---------------|                 |
  |              |                |                 |
  | 200 OK       |                |                 |
  |<-------------|                |                 |
]]>
</sourcecode>
</section>

<section anchor="flow-interaction-chaining"><name>Interaction Chaining</name>
<t>See <xref target="interaction-chaining"/> for normative requirements. When the PS requires user interaction for the downstream access, Resource 1 chains the interaction back to the original agent.</t>

<sourcecode type="ascii-art"><![CDATA[User      Agent       Resource 1      Resource 2    PS
  |         |              |               |          |
  |         | HTTP Sig req |               |          |
  |         |------------->|               |          |
  |         |              |               |          |
  |         |              | HTTP Sig w/   |          |
  |         |              | R1 person_tok |          |
  |         |              |-------------->|          |
  |         |              |               |          |
  |         |              | 401           |          |
  |         |              | + resource_tok|          |
  |         |              |<--------------|          |
  |         |              |               |          |
  |         |              | POST token_ep |          |
  |         |              | resource_tok, |          |
  |         |              | presented_tok,|          |
  |         |              | upstream_tok, |          |
  |         |              | agent_tok     |          |
  |         |              |------------------------->|
  |         |              |               |          |
  |         |              | 202 Accepted  |          |
  |         |              | interaction   |          |
  |         |              |<-------------------------|
  |         |              |               |          |
  |         | 202 Accepted |               |          |
  |         | interaction  |               |          |
  |         | code="MNOP"  |               |          |
  |         |<-------------|               |          |
  |         |              |               |          |
  | direct to R1 {url}     |               |          |
  |<--------|              |               |          |
  |         |              |               |          |
  | R1 redirects to PS     |               |          |
  |----------------------->|               |          |
  | PS {url}?code={code}   |               |          |
  |<-----------------------|               |          |
  |         |              |               |          |
  | authenticate and consent               |          |
  |-------------------------------------------------->|
  |         |              |               |          |
  | redirect to R1 callback                |          |
  |<--------------------------------------------------|
  |         |              |               |          |
  |         |         [R1 polls PS,        |          |
  |         |          gets auth_token]    |          |
  |         |              |               |          |
  |         |              | HTTP Sig w/   |          |
  |         |              | auth_token    |          |
  |         |              |-------------->|          |
  |         |              |               |          |
  |         |              | 200 OK        |          |
  |         |              |<--------------|          |
  |         |              |               |          |
  | redirect to agent callback             |          |
  |<-----------------------|               |          |
  |         |              |               |          |
  | callback|              |               |          |
  |-------->|              |               |          |
  |         |              |               |          |
  |         | GET /pending |               |          |
  |         |------------->|               |          |
  |         |              |               |          |
  |         | 200 OK       |               |          |
  |         |<-------------|               |          |
]]>
</sourcecode>
</section>
</section>

<section anchor="minimal-ps"><name>A Minimal Person Server</name>
<t>This appendix is informative. It describes how a person server serving one person — self-hosted, or a small service — composes from what this document already defines, and points at the sections that govern each step. It adds no requirement.</t>
<t><strong>Metadata.</strong> The floor is the four REQUIRED fields <xref target="ps-metadata"/>: <tt>issuer</tt>, <tt>jwks_uri</tt>, <tt>person_token_endpoint</tt>, and <tt>auth_token_endpoint</tt>. A minimal PS publishes those and nothing else. It has no <tt>mission_endpoint</tt>, so agents cannot propose missions to it and every request is evaluated on its own; no <tt>permission_endpoint</tt> or <tt>audit_endpoint</tt>; no <tt>interaction_endpoint</tt>, so an agent that would have relayed an interaction directs the person to it itself <xref target="interaction-relay"/>; and no <tt>mission_control_endpoint</tt>. A <tt>revocation_endpoint</tt> is RECOMMENDED, since it is how the person's agent provider tells the PS to stop honoring an agent <xref target="token-revocation"/>.</t>
<t><strong>One person.</strong> Every agent that reaches the PS acts for the same person, so agent-person binding <xref target="agent-person-binding"/> reduces to the first approval: the PS records the agent's <tt>(iss, sub)</tt> on the first token it issues for it, and thereafter recognizes it. The approving party still has to be authenticated <xref target="ps-approval-endpoint-auth"/>: a loopback-only PS relies on the operating system, and one reachable from a network authenticates the person before acting on a tap or a reply.</t>
<t><strong>Person tokens.</strong> The person token endpoint <xref target="person-token-endpoint"/> derives one directed <tt>sub</tt> per resource <xref target="directed-identifiers"/> — a keyed hash of the resource identifier is enough, provided the key is kept — issues the token bound to the agent's key, and records its <tt>jti</tt>, <tt>aud</tt>, and <tt>exp</tt> for revocation <xref target="person-token-endpoint"/>; the agent presents the token itself when it requests an auth token. The first token for a resource the person has not used is the moment to ask them <xref target="person-token-exposure"/>; later ones for the same resource need not be.</t>
<t><strong>Auth tokens.</strong> At the auth token endpoint <xref target="ps-token-endpoint"/>, a resource token whose <tt>aud</tt> is the PS is answered directly: the PS decides on consent and issues the auth token itself. One whose <tt>aud</tt> is an access server is federated <xref target="ps-as-federation"/>, presenting the resource token, the agent token, and the presented token the agent supplied <xref target="ps-to-as-token-request"/>. A minimal PS that never expects four-party access can decline the second case; one that supports it needs nothing beyond an HTTP client and its own signing key.</t>
<t><strong>Consent without a consent page.</strong> The PS answers any request that needs the person with a <tt>202</tt> deferred response carrying <tt>requirement=interaction</tt>, a <tt>url</tt>, and a <tt>code</tt> <xref target="requirement-responses"/> and <xref target="deferred-responses"/>. The <tt>url</tt> can be a page the PS serves, but it need not be visited: the PS MAY complete the interaction over a channel it already has — a notification the person taps, a message they reply to — and the code is consumed at that completion <xref target="user-interaction"/>. The pending URL then returns the terminal response on the agent's next poll. A queue of pending decisions, each resolved by one tap, is the whole of the consent surface; the Consent Presentation rules <xref target="consent-presentation"/> apply to what the tap shows.</t>
<t><strong>Long waits.</strong> The person may not answer for hours. The pending record lives as long as the PS chooses <xref target="pending-url-security"/>; the resource token the request carried will have expired by then, and the agent obtains a fresh one and resubmits <xref target="resource-tokens"/>. The PS remembers the decision it already has and applies it to the resubmission without asking again <xref target="resource-tokens"/>.</t>
<t><strong>Supervision.</strong> The person is the Supervisor <xref target="roles"/>. Every decision the PS cannot make from what it already recorded waits on them, which is the right default for one person and a handful of agents. A PS that wants to answer routine requests without waking the person applies a standing policy on their behalf; how it consults a supervision server for that is left to a companion specification <xref target="roles"/>, and is the one thing a minimal PS grows into rather than starts with.</t>
</section>

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

<section anchor="identity-and-foundation"><name>Identity and Foundation</name>

<section anchor="why-per-instance-agent-identity"><name>Why Per-Instance Agent Identity</name>
<t>OAuth's <tt>client_id</tt> identifies an application — every instance of the same app shares a single identifier and typically a single set of credentials. AAuth's <tt>aauth:local@domain</tt> agent identifier identifies a specific instance with its own signing key. This enables per-instance authorization (grant access to this specific agent process, not all instances of the app), per-instance revocation (revoke one compromised instance without affecting others), and per-instance audit (trace every action to the specific instance that performed it). The agent provider controls which instances receive agent tokens, providing centralized control over a distributed agent fleet.</t>
</section>

<section anchor="why-agents-are-under-an-agent-provider"><name>Why Agents Are Under an Agent Provider</name>
<t>Placing agents under an agent provider rather than allowing each agent to self-certify its own identity serves two purposes. First, <strong>scale</strong>: a single agent provider can issue, rotate, and revoke agent tokens across a fleet of thousands of instances. Resources and PSes verify agent tokens by fetching the AP's JWKS — one trust anchor for all agents from that provider — rather than performing individual key management with each instance. Second, <strong>policy enforcement</strong>: the AP is a natural PEP for agents. It controls which agent instances receive tokens, what identity claims they carry, and when tokens are denied or revoked. An agent that is also its own AP would bypass this layer entirely, eliminating the enforcement point without gaining anything: the protocol complexity increases while the security properties weaken. AAuth therefore requires every agent to hold a token issued by a distinct AP, not self-signed.</t>
</section>

<section anchor="why-every-agent-has-a-person"><name>Why Every Agent Has a Person</name>
<t>Every agent acts on behalf of a person — the entity accountable for the agent's actions. AAuth enables a person server to maintain this link, making it visible and enforceable across the protocol. When present, the PS ensures there is always an accountable party for authorization decisions, audit, and liability.</t>
</section>

<section anchor="why-person-tokens"><name>Why Person Tokens</name>
<t>An agent identifier embeds its agent provider's domain, so a person moving to another provider necessarily arrives at a resource as someone new, losing whatever state the resource held for them. The person is the party the resource has a relationship with — the account, the history, and any standing limits are theirs — and keying on <tt>(iss, sub)</tt> makes that relationship survive the change. It also keeps agent providers from becoming gatekeepers: a resource that never learns which agent product is calling cannot condition access on it.</t>
<t>Identifying the person at the authorization endpoint rather than after authorization also lets the resource decide before it commits. Account selection <xref target="account-binding"/>, standing policy for that person, and any per-person limit are all evaluable when the request arrives, instead of after a resource token has been issued and taken to a PS.</t>
</section>

<section anchor="why-a-mission-belongs-to-an-agent"><name>Why a Mission Belongs to an Agent</name>
<t>A person's relationship with a resource survives their changing agents, but a mission does not: it names one agent, and moving to another means proposing a new mission. The two are different things with different lifetimes. <tt>sub</tt> identifies the person, durably, because the resource's account and history are theirs. A mission is a grant of latitude to one agent to pursue one piece of work, and the person server evaluates every request against the record of what that agent has already done under it. Carrying that record across a change of agent would attribute one agent's history to another.</t>
<t>The practical effect is that anything done under a mission identifier was done by the agent the mission names, which is what makes the mission log worth reading.</t>
</section>

<section anchor="why-no-agent-identifier"><name>Why No Agent Identifier Reaches a Resource</name>
<t>Naming the agent, or its provider, in a token the resource reads would restore exactly the coupling the person token exists to remove: a resource able to see either can pin policy to it, and the person's relationship stops surviving a change of agent. So neither the person token, the resource token, nor the auth token carries one. <tt>agent_jkt</tt> and <tt>cnf</tt> still bind every request to one key. The consequence for resource policy is stated in <xref target="resource-access-modes"/>: a decision keyed on the agent identifier holds in the two-party modes and nowhere else.</t>
<t>The agent token still reaches the AS, because a resource deploys an AS to have policy evaluated and an agent token MAY carry claims bearing on that — attestation, platform integrity, workload identity. The resource enforces; the AS evaluates; posture goes to the evaluator. The consequence is that agent-provider independence is complete in three-party and partial in four-party, where the resource has explicitly delegated policy to an AS.</t>
</section>

<section anchor="why-identity-alone-can-authorize"><name>Why Identity Alone Can Authorize</name>
<t>A person token carries no authorization, yet a resource in person-identity mode <xref target="overview-person-identity"/> serves requests on it. That is not a contradiction: the person server has authorized nothing, and the resource has decided that knowing the person is enough — the same decision it makes after a login it ran itself. Signing in to a site with an identity provider gets whatever that site gives signed-in people, and no one describes the identity assertion as an authorization.</t>
<t>What follows is that issuing a person token is consequential even though it grants nothing. The person server is deciding that this agent may act at this resource as this person, bounded by whatever that resource does on identity. That is why the question put to the person at first issuance is about acting, not naming, and why a person server should know what the resource does with identity before it asks.</t>
</section>

<section anchor="why-the-mission-is-encoded-rather-than-nested"><name>Why the Mission Is Encoded Rather Than Nested</name>
<t>The approval response carries the mission blob base64url-encoded rather than as a JSON object so that <tt>s256</tt> has an unambiguous byte sequence to cover. A nested object has no defined serialization once it is inside an envelope — the receiver would have to re-serialize it to hash it, and any difference in key order, whitespace, or escaping produces a different digest. Encoding makes the string itself the bytes, so the agent decodes, hashes, and compares, which is the operation it already performs on a JWT payload.</t>
<t>An earlier revision avoided the problem by making the response body the mission and putting <tt>s256</tt> in a header, which worked but left no room in the response for anything else. The encoded member restores that room without giving up verifiability.</t>
</section>

<section anchor="why-presented-jti"><name>Why a Resource Token Names the Person Token</name>
<t>Binding by <tt>presented_jti</tt> rather than by comparing claims is what makes mission stripping detectable. A resource cannot drop <tt>mission_s256</tt> and present the result as an unscoped request, because the agent hands the person server the token the resource verified, under its issuer's signature, and the person server compares. Comparing claims alone cannot work: an agent running concurrent missions holds several person tokens for the same resource, so "the person token issued for this agent and resource" does not identify one, and a resource that omitted <tt>mission_s256</tt> could not be caught. Naming the token pins which one, and the <tt>jti</tt> check rejects a substitute.</t>
<t>The same reasoning is why the agent carries the token rather than the person server looking it up. The person server issued the person token and could retain it, but on a step-up the resource token names an auth token, which in four-party the access server issued and the person server never held. The resource, for its part, has nothing to look up by: an auth token carries no reference to the person token or the resource token it followed from, and <tt>(ps, sub, agent key)</tt> is the tuple just shown not to identify one. So the resource names the token it just verified, the agent passes that token along, and the person server and the access server run one verification with no record on the request path.</t>
</section>

<section anchor="why-tools-are-not-enforced"><name>Why Tool Pre-Approval Is Not Enforced</name>
<t>Tool use is local to the agent. No party the protocol can hold to account observes it, so <tt>approved_tools</tt> is a record of what the person agreed to rather than a control that stops anything. Its value is that a departure from it is visible afterwards, in the mission log and in what the agent reports to the audit endpoint.</t>
<t>The enforcement that does exist sits outside the protocol and is worth naming: the runtime that decides whether to call a tool is built by the agent provider, and the agent provider attests the agent. A person's leverage over local actions is therefore their choice of agent provider, not the tool list. The auth token is the only hard control AAuth offers, and it covers remote resources.</t>
</section>

<section anchor="why-there-is-no-delegation-chain-claim"><name>Why There Is No Delegation Chain Claim</name>
<t>Earlier revisions recorded the upstream chain in an <tt>act</tt> claim. It served no reader. The immediate caller in a chain signs the request with its own key and presents its own credentials, so a downstream resource already knows who is calling; <tt>act</tt> named the parties one and two hops further up, which the downstream has no relationship with and cannot evaluate. Those parties are also the person's tooling, disclosed to a resource that did not need them.</t>
<t>The chain is held by the person server, which authorizes every hop and holds the mission log. The same reasoning that made the resource stop attributing missions makes it stop recording delegation: the resource enforces, the person server attributes.</t>
</section>

<section anchor="why-the-ps-claim-in-agent-tokens"><name>Why the <tt>ps</tt> Claim in Agent Tokens</name>
<t>A resource learns the agent's PS from the person token it verifies, but it needs to know the agent has one before that — to decide whether to challenge for a person token at all, and to know that the <tt>auth-token</tt> flow is available. The <tt>ps</tt> claim in the agent token provides that, separately from mission supervision.</t>
</section>
</section>

<section anchor="protocol-mechanics"><name>Protocol Mechanics</name>

<section anchor="why-json-in-well-known-uris"><name>Why <tt>.json</tt> in Well-Known URIs</name>
<t>AAuth well-known metadata URIs use the <tt>.json</tt> extension (e.g., <tt>/.well-known/aauth-agent.json</tt>) rather than the extensionless convention used by OAuth and OpenID Connect. The <tt>.json</tt> extension makes the content type immediately obvious — no content negotiation is needed. More importantly, it enables static file hosting: a <tt>.json</tt> file served from GitHub Pages, S3, or a CDN works without server-side configuration. This aligns with AAuth's self-hosted agent model (see <xref target="I-D.hardt-aauth-bootstrap"/>), where an agent's metadata can be published as static files with no active server.</t>
</section>

<section anchor="why-no-authorization-code"><name>Why No Authorization Code</name>
<t>AAuth eliminates authorization codes entirely. OAuth authorization codes require PKCE (<xref target="RFC7636"/>) to prevent interception attacks, adding complexity for both clients and servers. AAuth avoids the problem: 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-issuer-not-resource"><name>Why <tt>issuer</tt> Rather Than <tt>resource</tt> in Metadata</name>
<t>AAuth diverges from RFC 9728 on two points. It uses <tt>issuer</tt> as the primary identifier field in every metadata document so that a generic Signature-Key verifier can extract the signer identity uniformly from any <tt>dwk</tt> document without knowing which role it represents. And it uses unprefixed field names (<tt>name</tt>, <tt>tos_uri</tt>, <tt>policy_uri</tt>, <tt>documentation_uri</tt>) rather than the <tt>resource_</tt>-prefixed forms, for consistency across all four roles.</t>
</section>

<section anchor="why-covered-components"><name>Why These Covered Components</name>
<t>The four mandated components each close a request-substitution attack, and all four are derivable by the agent at signing time on every platform, including browsers. <tt>@method</tt> prevents a captured signature from being replayed with a different method; <tt>@authority</tt> prevents cross-host replay; <tt>@path</tt> binds it to the endpoint; <tt>signature-key</tt> prevents key substitution.</t>
<t><tt>content-digest</tt> and <tt>content-type</tt> are mandated at PS and AS endpoints because their request bodies carry members that decide what is authorized (<tt>justification</tt>, <tt>mission_s256</tt>, <tt>resource</tt>, the mission proposal itself), and every such endpoint takes a JSON body of known shape, so a digest costs the sender nothing. Resources serve arbitrary APIs, including bodyless requests and streamed uploads, so they declare what they need through <tt>additional_signature_components</tt> instead. A resource's revocation endpoint is the exception because it is defined by this document, not by the resource's API, and its body selects a token and adds to retained state.</t>
</section>

<section anchor="why-401-signature-failures"><name>Why <tt>401</tt> for Every Signature Failure</name>
<t>The HTTP Signature Keys specification uses <tt>400</tt> for most signature failures and permits <tt>401</tt> for the recoverable ones. AAuth requests are authenticated by their signature, so a signature that does not verify is an authentication failure rather than a malformed request, and a single status keeps agent retry logic uniform.</t>
</section>

<section anchor="why-account-not-login-hint"><name>Why <tt>account</tt> Is Not <tt>login_hint</tt></name>
<t><tt>account</tt> selects; it does not hint. <tt>login_hint</tt> (<xref target="OpenID.Core"/>, Section 3.1.2.1) is a hint about who to authenticate at the party receiving it, and is consumed during a login. Nobody is being authenticated by <tt>account</tt>: the account is already connected at the resource, and the value has to survive into the issued tokens as a claim. Overloading <tt>login_hint</tt> would also conflate the person logging in at their PS with the account being acted on at the resource, which may belong to different namespaces entirely.</t>
</section>

<section anchor="why-revocation-no-not-found"><name>Why a Revocation Has No "Not Found"</name>
<t>A recipient cannot distinguish a token it never saw from one it saw and no longer holds, and an answer that varied with what it holds would disclose that. A <tt>200</tt> says the revocation is recorded and the token will be refused; that is true whether or not the recipient ever held it.</t>
</section>

<section anchor="why-ps-reports-nothing-to-ap"><name>Why a PS Reports Nothing to the Agent Provider</name>
<t>Any report to the agent provider, even a count of downstream revocations, would tell it which resources the person uses through that agent, which is what the PS exists to keep from it. The AS's report to the PS discloses nothing, since the PS already knows the resource as the person token's <tt>aud</tt>. The person is the party the rule protects, so a PS MAY report the per-recipient outcome to them.</t>
</section>

<section anchor="in-brief"><name>In Brief</name>

<ul spacing="compact">
<li><strong>URL-based server identity</strong>: HTTPS URLs as server identifiers, and an agent identifier that names its provider's domain, enable dynamic ecosystems without pre-registration.</li>
<li><strong>Standard HTTP async pattern</strong>: <tt>202 Accepted</tt>, <tt>Location</tt>, <tt>Prefer: wait</tt>, and <tt>Retry-After</tt> apply uniformly to every endpoint, align with RFC 7240, replace the OAuth device flow, support headless agents, and carry clarification chat.</li>
<li><strong>JSON rather than form encoding</strong>: JSON is the standard format for modern APIs, for request and response bodies alike.</li>
<li><strong>The callback URL has no security role</strong>: tokens never pass through the user's browser; the callback is a UX optimization.</li>
<li><strong>OpenID Connect vocabulary</strong>: reusing its scope values, identity claims, and enterprise parameters lowers the adoption barrier.</li>
</ul>
</section>
</section>

<section anchor="architecture"><name>Architecture</name>

<section anchor="why-a-separate-person-server"><name>Why a Separate Person Server</name>
<t>The PS is distinct from the AS because they serve different parties with different concerns. The PS represents the person — it handles consent, identity, mission supervision, and audit. The AS represents the resource — it evaluates policy and issues tokens. Combining these into a single entity would conflate the interests of the requesting party with the interests of the resource owner, which is the same conflation that makes OAuth insufficient for cross-domain agent ecosystems.</t>
</section>

<section anchor="why-five-resource-access-modes"><name>Why Five Resource Access Modes</name>
<t>The modes are not levels of protocol adoption but answers to one question: what does the resource need to know before it serves a request? A resource that only verifies agent signatures can start using AAuth today without deploying a PS or AS. One that needs the person can take a person token and decide for itself. One that wants a scope agreed with the person takes an auth token, and one that wants policy evaluated takes it from its own access server. Each mode is self-contained and useful — not a stepping stone to the "real" protocol — and a resource may use different modes on different endpoints, which is the common case: most calls need only identity, and a few sensitive operations warrant an authorization decision.</t>
<t>Resource-managed and person-identity access are kept separate because the difference is who established the person's identity, and that determines what the resource can rely on. In resource-managed access the resource ran its own flow, so it knows the person on its own terms and needs nothing from a person server. In person-identity access it accepts an identity a person server asserted, which is federated login — cheaper for the resource, and dependent on trusting that person server.</t>
<t>Agent governance (missions plus permission, audit, and interaction relay) works independently of all five.</t>
</section>

<section anchor="why-resource-tokens"><name>Why Resource Tokens</name>
<t>In GNAP and OAuth, the resource server is a passive consumer of tokens — it verifies them but never produces signed artifacts. AAuth inverts this: the resource cryptographically asserts what is being requested by issuing a resource token that binds the resource's own identity, the agent's key thumbprint, the requested scope, and the mission context into a single signed JWT. This prevents confused deputy attacks — an attacker cannot substitute a different resource in the authorization flow because the resource token is signed by the resource. It also gives the resource a voice in every authorization and re-authorization, and provides a complete audit artifact linking the request to a specific resource, agent, scope, and mission.</t>
</section>

<section anchor="why-session-tokens-are-opaque"><name>Why Session Tokens Are Opaque</name>
<t>In two-party mode, the resource returns an opaque wrapped token via the <tt>AAuth-Access</tt> header rather than a JWT auth token. This allows the resource to wrap its existing authorization infrastructure (OAuth access tokens, session tokens, etc.) without exposing internal structure. The token is bound to the AAuth signature — the agent includes it in the <tt>Authorization</tt> header as a covered component — so it cannot be stolen and replayed as a standalone bearer token.</t>
</section>

<section anchor="why-missions-are-not-a-policy-language"><name>Why Missions Are Not a Policy Language</name>
<t>Missions are intentionally not a machine-evaluable policy language. AAuth separates two kinds of authorization decisions:</t>

<ul>
<li><t><strong>Deterministic policy</strong> is handled by scopes, resource tokens, and AS policy evaluation. These are mechanically evaluable — "does this agent have <tt>data.read</tt> scope for this resource?" A policy engine (Cedar, OPA/Rego, or any other) can answer this question consistently and automatically.</t>
</li>
<li><t><strong>Contextual supervision</strong> is handled by missions, justifications, and clarification at the PS. These are the contextual decisions that policy engines cannot answer — "is booking a $10,000 flight reasonable for planning a weekend trip?" or "should this agent access the HR database given what it's trying to accomplish?" The mission description, the agent's justification for each resource access, and the clarification dialog between user and agent provide the context for these decisions.</t>
</li>
</ul>
<t>Prior attempts to make authorization semantics machine-evaluable across domains have not scaled. OAuth Rich Authorization Requests (RAR) require clients and servers to agree on domain-specific <tt>type</tt> values and JSON structures — workable within a single API but combinatorially explosive across arbitrary services. UMA attempted cross-domain resource sharing with machine-readable permission tickets, but adoption stalled because resource owners, requesting parties, and authorization servers could not converge on shared semantics for what permissions meant across organizational boundaries. The fundamental problem is that the meaning of "appropriate access" is contextual, evolving, and domain-specific — it cannot be captured in a predefined vocabulary that all parties share.</t>
<t>Missions solve this differently. Rather than requiring all parties to agree on machine-evaluable semantics, AAuth concentrates supervision at the PS — the only party with full context. The PS has the mission description, the user's identity and organizational context, the agent's justification for each request, the history of what the agent has done so far, and a channel to the user for clarification. No other party in the protocol has this context, and no predefined policy language can substitute for it.</t>
<t>This context can be presented to humans or to agents acting as decision-makers. The PS does not need to evaluate missions deterministically — it presents the mission context, the justification, and the resource request to the Supervisor <xref target="roles"/>: the person at a consent screen, or a supervision server deciding on their behalf — an AI agent applying an organization's policy, or an automated system applying heuristics. As AI decision-making matures, supervision can shift from human review to agent evaluation — without changing the protocol. AAuth standardizes how context is conveyed to the decision-maker; it does not prescribe how the decision is made.</t>
<t>The mission's <tt>description</tt> is Markdown because it represents human intent, not machine policy. The <tt>approved_tools</tt> array provides structured machine-evaluable elements where appropriate. Resources and access servers do not need the mission content — they enforce their own deterministic policies independently. The mission is a further restriction applied by the PS, and only the PS has sufficient context to evaluate it. Distributing mission semantics to other parties would be both a privacy leak and a false promise of enforcement, since those parties lack the context to evaluate the mission meaningfully.</t>
</section>

<section anchor="why-missions-have-only-two-states"><name>Why Missions Have Only Two States</name>
<t>Missions are either <strong>active</strong> or <strong>terminated</strong>. There is no suspended state. An <tt>expires_at</tt> in the mission blob does not add a state — it declares in advance when the PS will treat the mission as terminated, which the person can see at approval time. A suspended state would require the agent to learn that the mission has resumed, but AAuth has no push channel from the PS to the agent — the agent can only poll. For short pauses (minutes), the deferred response mechanism already provides natural waiting via <tt>202</tt> polling. For long pauses (hours or more), the agent would need to poll indefinitely with no indication of when to stop, making suspension operationally equivalent to termination. Terminating the mission and creating a new one is cleaner — the PS retains the old mission's log for audit, and the new mission can be scoped appropriately for the changed circumstances that prompted the pause. This keeps mission lifecycle simple: a mission is alive until it is done.</t>
</section>
</section>

<section anchor="comparisons-with-alternatives"><name>Comparisons with Alternatives</name>

<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 a signed request, others verified identity, and others an auth token. 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. The agent signs with its own agent token <xref target="agent-tokens"/>, which it obtains from its agent provider without any resource-side registration; no resource- or AS-issued token is needed to make the first identified call. DPoP has a single mode (prove you hold the key bound to this token), while AAuth supports progressive requirements from verified agent identity through 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 (<xref target="RFC9635"/>) 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; a resource that signs what is being requested <xref target="why-resource-tokens"/> changes that core assumption rather than extending it.</t>
<t><strong>Interaction chaining requires a different continuation model.</strong> GNAP's continuation mechanism operates between a single client and a single access 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 access server directly. AAuth's model — where the agent only ever talks to its PS, and the PS federates with resource ASes — 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, PS-to-AS federation, mission-scoped authorization, 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 anchor="why-not-extend-www-authenticate"><name>Why Not Extend WWW-Authenticate?</name>
<t><tt>WWW-Authenticate</tt> (<xref target="RFC9110"/>, Section 11.6.1) tells the client which authentication scheme to use. Its challenge model is "present credentials" — it cannot express progressive requirements, authorization, or deferred approval, and it cannot appear in a <tt>202 Accepted</tt> response.</t>
<t><tt>AAuth-Requirement</tt> and the <tt>Accept-Signature-*</tt> headers (<xref target="I-D.hardt-httpbis-signature-key"/>) coexist with <tt>WWW-Authenticate</tt>. A <tt>401</tt> response MAY include multiple headers, and the client uses whichever it understands:</t>

<sourcecode type="http"><![CDATA[HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="api"
Accept-Signature-Scheme: jwt
]]>
</sourcecode>
<t>A <tt>402</tt> response MAY include <tt>WWW-Authenticate</tt> for payment (e.g., the Payment scheme (<xref target="I-D.ryan-httpauth-payment"/>)) alongside <tt>Accept-Signature-Scheme</tt> for authentication or <tt>AAuth-Requirement</tt> for authorization:</t>

<sourcecode type="http"><![CDATA[HTTP/1.1 402 Payment Required
WWW-Authenticate: Payment id="x7Tg2pLq", method="example",
    request="eyJhbW91bnQiOiIxMDAw..."
Accept-Signature-Scheme: jwt
]]>
</sourcecode>
</section>

<section anchor="why-not-extend-oauth"><name>Why Not Extend OAuth?</name>
<t>OAuth 2.0 (<xref target="RFC6749"/>) was designed for delegated access — a user authorizes a pre-registered client to act on their behalf at a specific server. Extending OAuth for agent-to-resource authorization would require changing its foundational assumptions:</t>

<ul spacing="compact">
<li><strong>Client identity</strong>: OAuth clients have no independent identity. A <tt>client_id</tt> is issued by each authorization server — it is meaningless outside that relationship. AAuth agents have self-sovereign identity (<tt>aauth:local@domain</tt>) verifiable by any party.</li>
<li><strong>Pre-registration</strong>: OAuth requires clients to register with each authorization server before use. AAuth agents call resources they have never contacted before — the first API call is the registration.</li>
<li><strong>Bearer tokens</strong>: OAuth access tokens are bearer credentials — anyone who holds the token can use it. AAuth binds every token to a signing key via HTTP Message Signatures — a stolen token is useless without the private key.</li>
<li><strong>No resource identity</strong>: OAuth does not cryptographically identify the resource. AAuth resources sign resource tokens, binding their identity to the authorization flow.</li>
<li><strong>No governance layer</strong>: OAuth has no concept of missions, permission endpoints, audit logging, or interaction relay. These would need to be built on top as extensions, losing the coherence of a protocol designed around them.</li>
<li><strong>No federation model</strong>: OAuth's authorization server is always the resource owner's server. AAuth separates the person server (user's choice) from the access server (resource's choice) and defines how they federate.</li>
</ul>
<t>The Model Context Protocol (MCP) illustrates these limitations. MCP adopted OAuth 2.1 for agent-to-server authorization and immediately needed Dynamic Client Registration (<xref target="RFC7591"/>) because agents cannot pre-register with every server. But Dynamic Client Registration gives the agent a different <tt>client_id</tt> at each server — the agent still has no portable identity. Tokens are bearer credentials, so a stolen token grants full access. There is no resource identity — the server does not cryptographically prove who it is. There is no governance layer — no missions, no permission management, no audit trail. And the entire authorization model is per-server: each MCP server has its own authorization server, and the agent must discover and register with each one independently. MCP's experience demonstrates that OAuth can be made to work for the first API call, but it cannot provide the identity, governance, and federation that agents need as they operate across trust domains.</t>
<t>Rather than layer these changes onto OAuth — which would break backward compatibility and produce something unrecognizable — AAuth is a new protocol designed for the agent model from the ground up. AAuth complements OAuth: resources can wrap existing OAuth infrastructure behind the AAuth-Access token, and PSes can delegate user authentication to OpenID Connect providers.</t>
</section>
</section>
</section>

</back>

</rfc>
