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

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

<abstract>
<t>This document defines AAuth Bootstrap, an extension to the AAuth Protocol (<xref target="I-D.hardt-aauth-protocol"/>) that specifies how an agent server and an agent bootstrap the agent to be bound to a person at the person's Person Server (PS). Bootstrap establishes the binding between a user (as vouched for by the user's PS) and an agent identity (<tt>aauth:local@domain</tt>) hosted by an agent server. The specification covers self-hosted agents (where the agent and agent server are co-located under a domain the user controls), web apps, and mobile agents. The agent MAY pass account hints on the PS <tt>/bootstrap</tt> request to help the PS resolve the user when the user has more than one account at the PS or when the agent already knows something about the user. Identity claims and scoped authorization are obtained separately through the standard three-party flow defined in AAuth Protocol. This specification defines the <tt>bootstrap_token</tt>, the <tt>bootstrap_endpoint</tt> on the PS and agent server, the signature schemes used at each step, and renewal flows that bypass the PS after the initial binding is established.</t>
</abstract>

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

</front>

<middle>

<section anchor="introduction"><name>Introduction</name>
<t><strong>Status: Exploratory Draft</strong></t>
<t>The AAuth Protocol (<xref target="I-D.hardt-aauth-protocol"/>) defines agent identities of the form <tt>aauth:local@domain</tt>, each bound to a signing key and published under a well-known JWKS. Bootstrap is the ceremony by which an agent server and an agent bootstrap the agent to be bound to a person at the person's PS, so that subsequent flows defined in <xref target="I-D.hardt-aauth-protocol"/> can run with a <tt>(user, agent)</tt> binding the PS recognizes.</t>
<t>The same ceremony serves several deployment shapes:</t>

<ul spacing="compact">
<li><strong>Self-hosted agents</strong>, where the agent and the agent server are co-located under a domain the user controls. The agent self-issues its agent token. Binding to a PS is optional; an agent that does bind benefits from a PS that can prompt the user out of band on later <tt>auth_token</tt> requests instead of requiring an in-app interaction.</li>
<li><strong>Web apps</strong>, where the agent is a web application running in the user's browser and the agent server runs separately on the network. The agent server mints an agent identity for the user; the user's identity is vouched for by the user's PS, not by the agent server.</li>
<li><strong>Mobile agents</strong>, similar to web apps but the agent runs on the user's mobile device and uses platform attestation (App Attest, Play Integrity) to bind the agent to a hardware-protected key.</li>
</ul>
<t>The same ceremony works whether the PS is operated by a consumer identity provider or by the user's organization (B2B). When the agent has prior context about the user — an email address from a previous session, a known organizational tenant, a corporate domain — it can pass that context as an account hint on the PS <tt>/bootstrap</tt> request; see <xref target="account-hints"/>.</t>
<t>Bootstrap is intentionally limited to establishing the identity binding. It does not carry <tt>scope</tt> and does not request identity claims. Identity claims (e.g., email, profile, organizational membership) are obtained separately by the agent server running the standard three-party flow defined in <xref target="I-D.hardt-aauth-protocol"/>. This separation keeps bootstrap focused on the binding, reuses existing protocol machinery for claims release, and lets the agent request claims incrementally rather than up front.</t>
<t>This document specifies that ceremony. It defines:</t>

<ul spacing="compact">
<li>The <tt>bootstrap_token</tt>, a short-lived JWT issued by the PS and consumed by the agent server.</li>
<li>The <tt>/bootstrap</tt> endpoints on the PS and the agent server.</li>
<li>Which signature schemes from <xref target="I-D.hardt-httpbis-signature-key"/> are used at each step.</li>
<li>Renewal flows on the agent server that do not require re-involving the PS.</li>
<li>Account hints the agent MAY pass to the PS to help resolve the user.</li>
</ul>

<section anchor="in-scope"><name>In Scope</name>

<ul spacing="compact">
<li>Self-hosted agents (including self-hosted CLI), where the agent and agent server are co-located under a domain the user controls. Binding to a PS is optional.</li>
<li>Web apps (agent runs in the user's browser; agent server runs separately on the network).</li>
<li>Mobile agents (agent runs on the user's device with platform attestation; agent server runs separately on the network).</li>
</ul>
<t>B2B SaaS deployments use the same web app or mobile profile; what differs is which PS the agent talks to and which account hints it passes (see <xref target="account-hints"/>).</t>
</section>

<section anchor="out-of-scope"><name>Out of Scope</name>
<t>The following deployment shapes are not specified by this document and are deferred to future revisions. Each is described briefly so implementers can recognize the pattern and understand why it is treated separately.</t>

<section anchor="command-line-tools-with-a-user-independent-agent-server"><name>Command-line tools with a user-independent agent server</name>
<t>A CLI whose agent server is operated separately from the user — typically by a vendor — needs a way to enroll the CLI as an agent at the vendor without the user being present in a browser. Self-hosted CLIs are covered in <xref target="self-hosted-agents"/>; this exclusion applies only when the CLI's agent server is vendor-operated. Future work may define an out-of-band enrollment ceremony (for example, a one-time pairing code displayed on a vendor web page and entered into the CLI).</t>
</section>

<section anchor="server-workloads-and-headless-services"><name>Server workloads and headless services</name>
<t>Server-side agents — daemons, batch jobs, microservices — typically run in environments that already provide a workload identity. Examples include SPIFFE SVIDs from a SPIRE agent, WIMSE workload identity tokens, AWS IMDSv2 instance identity documents, GCP metadata service tokens, and Azure IMDS tokens. The agent presents the platform attestation and an ephemeral public key to the agent server, which verifies the attestation against the platform's trust root and issues an agent token. There is no end user, so no PS interaction; the trust anchor is the platform's attestation that the workload is what it claims to be. On managed infrastructure the platform may additionally attest the software identity (container image hash, binary signature), allowing the agent server to restrict tokens to known software.</t>
</section>

<section anchor="managed-desktops"><name>Managed desktops</name>
<t>On managed desktops (corporate MDM-enrolled devices), the management platform may provide device and software attestation comparable to mobile platforms. The agent presents the platform attestation alongside its ephemeral key, and the agent server verifies the device is managed and the software is approved. The trust anchor is the management platform's attestation rather than user interaction; consequently a different consent and enrollment model applies than for the web app or mobile profiles.</t>
</section>

<section anchor="organizational-agents-with-administrative-enrollment"><name>Organizational agents with administrative enrollment</name>
<t>In some enterprise deployments an organization administrator pre-authorizes agents on behalf of users — bindings exist before any user interaction. This requires a pre-established trust relationship between the organization's PS and the vendor's agent server, plus an administrative enrollment channel that this specification does not define. The user-driven case where each user bootstraps their own agent in an organizational context is covered by the in-scope profiles together with the account hints in <xref target="account-hints"/>; administrator-driven bulk enrollment is a separate problem.</t>
</section>
</section>
</section>

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

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

<section anchor="terms-reused-from-aauth-protocol"><name>Terms Reused From AAuth Protocol</name>
<t>The following terms are defined in <xref target="I-D.hardt-aauth-protocol"/> and are used in this document with the same meaning. Summaries are provided for convenience; the referenced specification is authoritative.</t>

<ul spacing="compact">
<li><strong>Person</strong>: The user or organization on whose behalf an agent acts. Referred to in this document as "the user".</li>
<li><strong>Agent</strong>: An HTTP client acting on behalf of a person, identified by an agent identifier URI of the form <tt>aauth:local@domain</tt> defined in <xref target="I-D.hardt-aauth-protocol"/>.</li>
<li><strong>Agent Server</strong>: A server that manages agent identity and issues agent tokens. In the self-hosted profile the agent server is co-located with the agent under a domain the user controls; in the web app and mobile profiles it runs separately on the network. Identified by an HTTPS URL.</li>
<li><strong>Person Server (PS)</strong>: A server that represents the person to the rest of the protocol. In the context of this document, the PS vouches for the user to the agent server during bootstrap. Identified by an HTTPS URL.</li>
<li><strong>Resource</strong>: A server that protects access to its APIs. Not involved in bootstrap.</li>
<li><strong>Agent Token</strong>: A JWT issued by an agent server to establish the agent's identity, defined in <xref target="I-D.hardt-aauth-protocol"/>.</li>
<li><strong>Auth Token</strong>: A JWT issued by a PS or AS granting an agent access to a resource, defined in <xref target="I-D.hardt-aauth-protocol"/>. Not issued during bootstrap.</li>
<li><strong>Interaction</strong>: User authentication or consent at an interaction endpoint, triggered when a server returns <tt>202 Accepted</tt> with <tt>requirement=interaction</tt>. Defined in <xref target="I-D.hardt-aauth-protocol"/>.</li>
</ul>
</section>

<section anchor="terms-reused-from-http-signature-keys"><name>Terms Reused From HTTP Signature Keys</name>
<t>The following signature schemes are defined in <xref target="I-D.hardt-httpbis-signature-key"/> and used in this document without modification:</t>

<ul spacing="compact">
<li><strong><tt>hwk</tt> scheme</strong>: Conveys a public key inline in the <tt>Signature-Key</tt> header.</li>
<li><strong><tt>jkt-jwt</tt> scheme</strong>: Chains a JWT signed by one key (e.g., a durable enclave key) to authorize a second key (e.g., an ephemeral HTTP signing key).</li>
<li><strong><tt>jwt</tt> scheme</strong>: Conveys a JWT (e.g., an agent token) that names the signing key.</li>
</ul>
</section>

<section anchor="terms-defined-in-this-document"><name>Terms Defined in This Document</name>

<ul spacing="compact">
<li><strong>Bootstrap</strong>: The PS-mediated ceremony defined in this document by which an agent becomes bound to a person at that person's PS. For self-hosted agents bootstrap is optional; for web app and mobile agents it is the way to obtain an initial agent token.</li>
<li><strong>Ephemeral Key</strong>: An HTTP signing key generated by the agent whose lifetime is bounded by the agent token. Typically hours to a day, not per-request.</li>
<li><strong>Durable Key</strong>: A platform-protected key whose lifetime is bounded by the application install (or, for self-hosted agents, by the install of the agent on the user's machine). Typically months to years. Examples include the iOS Secure Enclave, Android StrongBox, the macOS Keychain (Secure Enclave on supported hardware), Windows TPM, and Linux Secret Service. A durable key is identified by a JWK thumbprint (<tt>urn:jkt</tt>).</li>
<li><strong>bootstrap_token</strong>: A short-lived JWT issued by the PS, directed at an agent server, and bound to an agent's ephemeral key via a <tt>cnf</tt> claim. Defined in <xref target="bootstrap-token-issuance"/>.</li>
<li><strong>Binding</strong>: The one-to-one association <tt>(ps_url, user_sub) -&gt; aauth:local@&lt;agent-server-domain&gt;</tt> recorded by the agent server at the end of bootstrap, together with the device credentials associated with it.</li>
<li><strong>Device Credential</strong>: A credential held by the agent server and associated with a binding, used to authenticate renewal requests without re-involving the PS. A WebAuthn credential ID for web apps or an enclave-key thumbprint (<tt>urn:jkt:sha-256:&lt;thumbprint&gt;</tt>) for mobile.</li>
</ul>
</section>
</section>

<section anchor="parties-and-topology"><name>Parties and Topology</name>
<t>The bootstrap ceremony involves three parties:</t>

<ul spacing="compact">
<li>The <strong>agent</strong>, which generates key material and initiates the ceremony.</li>
<li>The <strong>PS</strong>, which authenticates the user, collects consent, issues the <tt>bootstrap_token</tt>, and records the <tt>aauth:local@domain</tt> identifier of each agent the user bootstraps.</li>
<li>The <strong>agent server</strong>, which verifies the <tt>bootstrap_token</tt>, performs attestation, records the <tt>(user, agent)</tt> binding, and issues the agent token.</li>
</ul>
<t>For self-hosted agents the agent and agent server are co-located under a domain the user controls. The agent self-issues its agent token; the agent server does not expose <tt>bootstrap_endpoint</tt> or <tt>refresh_endpoint</tt>, and platform attestation is replaced by a hardware-bound JWKS-published key. See <xref target="self-hosted-agents"/>.</t>
<t>Resources and access servers (AS) are not involved in bootstrap. After bootstrap, the agent interacts with them using the tokens returned by the agent server following the flows defined in <xref target="I-D.hardt-aauth-protocol"/>.</t>
</section>

<section anchor="bootstrap-overview"><name>Bootstrap Overview</name>
<t>The following sequence shows the bootstrap ceremony end to end. Attestation and renewal sub-flows are shown separately for clarity.</t>
<figure anchor="fig-bootstrap"><name>Bootstrap Ceremony </name>
<sourcecode type="ascii-art"><![CDATA[User/Browser    Agent                   PS                 Agent Server
     |            |                      |                       |
     |            | HTTPSig (hwk, eph)   |                       |
     |            | POST /bootstrap      |                       |
     |            | { agent_server }     |                       |
     |            |--------------------->|                       |
     |            |                      |                       |
     |            | 202 Accepted         |                       |
     |            | Location, url, code  |                       |
     |            |<---------------------|                       |
     |            |                      |                       |
     |            | (agent invokes browser at url?code)          |
     |<-----------|                      |                       |
     |            |                      |                       |
     | authenticate + consent            |                       |
     |---------------------------------->|                       |
     |            |                      |                       |
     |            | GET pending URL      |                       |
     |            | (poll)               |                       |
     |            |--------------------->|                       |
     |            |                      |                       |
     |            | 200 OK               |                       |
     |            | { bootstrap_token }  |                       |
     |            |<---------------------|                       |
     |            |                      |                       |
     |            | [attestation ceremony -- see (#attestation)] |
     |            |                      |                       |
     |            | HTTPSig (hwk|jkt-jwt)                        |
     |            | POST bootstrap_endpoint                      |
     |            | { bootstrap_token, attestation }             |
     |            |--------------------------------------------->|
     |            |                      |                       |
     |            | 200 OK               |                       |
     |            | { agent_token }      |                       |
     |            |<---------------------------------------------|
     |            |                      |                       |
     |            | HTTPSig (jwt, agent_token)                   |
     |            | POST /bootstrap (announcement, empty body)   |
     |            |--------------------->|                       |
     |            |                      |                       |
     |            | 204 No Content       |                       |
     |            |<---------------------|                       |
]]>
</sourcecode>
</figure>
<t>At this point the PS has bound the user to the agent identifier and the agent holds an <tt>agent_token</tt>.</t>
<t>The subsequent renewal flow skips the PS and uses the device credential recorded at bootstrap:</t>
<figure anchor="fig-renewal"><name>Renewal Ceremony </name>
<sourcecode type="ascii-art"><![CDATA[Agent                                          Agent Server
  |                                                  |
  | [renewal attestation -- see (#attestation)]      |
  |                                                  |
  | HTTPSig (hwk or jkt-jwt, new ephemeral)          |
  | POST refresh_endpoint                            |
  | { <attestation> }                                |
  |------------------------------------------------->|
  |                                                  |
  | { agent_token } (new ephemeral)                  |
  |<-------------------------------------------------|
]]>
</sourcecode>
</figure>
</section>

<section anchor="agent-server-metadata"><name>Agent Server Metadata Extensions</name>
<t>This specification extends the <tt>/.well-known/aauth-agent.json</tt> document defined in <xref target="I-D.hardt-aauth-protocol"/> with the following fields. These fields apply to agent servers that accept web app or mobile clients; self-hosted agent servers <xref target="self-hosted-agents"/> do not expose these endpoints.</t>
<t>Fields:</t>

<ul spacing="compact">
<li><strong><tt>bootstrap_endpoint</tt></strong> (REQUIRED for agent servers that accept web app or mobile clients). The HTTPS URL where the agent POSTs the <tt>bootstrap_token</tt> and attestation result per <xref target="request-to-agent-server-bootstrap"/>.</li>
<li><strong><tt>refresh_endpoint</tt></strong> (REQUIRED for agent servers that accept web app or mobile clients). The HTTPS URL where the agent POSTs renewal requests per <xref target="renewal"/>.</li>
<li><strong><tt>webauthn_endpoint</tt></strong> (REQUIRED for agent servers that accept web app clients). The HTTPS URL from which the agent fetches a WebAuthn challenge and, for registration, WebAuthn ceremony options. Defined in <xref target="webauthn-endpoint"/>.</li>
</ul>
<t>Agents MUST NOT assume a fixed path for these endpoints; the agent MUST discover them from agent-server metadata. Each endpoint URL's host MUST equal the host of the <tt>issuer</tt> field, or be a DNS subdomain of it. This allows operational deployments such as <tt>https://api.agent-server.example/bootstrap</tt> paired with <tt>issuer = https://agent-server.example</tt>, while still anchoring discovery to the metadata origin.</t>
<t>Example aauth-agent.json file:</t>

<sourcecode type="json"><![CDATA[{
  "issuer": "https://agent-server.example",
  "jwks_uri": "https://agent-server.example/.well-known/jwks.json",
  "client_name": "Example AI Assistant",
  "logo_uri": "https://agent-server.example/logo.png",
  "bootstrap_endpoint": "https://agent-server.example/bootstrap",
  "refresh_endpoint": "https://agent-server.example/refresh",
  "webauthn_endpoint": "https://agent-server.example/webauthn"
}
]]>
</sourcecode>
</section>

<section anchor="webauthn-endpoint"><name>WebAuthn Endpoint</name>
<t>The <tt>webauthn_endpoint</tt> issues WebAuthn challenges for both bootstrap (registration) and refresh (assertion). It is used by web app clients. The endpoint takes no user context on the request; the server tracks each challenge as an opaque single-use nonce and binds it to the user only when the ceremony result is submitted.</t>

<section anchor="request"><name>Request</name>
<t>The agent issues a GET request with the ceremony type as a query parameter:</t>

<artwork><![CDATA[GET /webauthn?type=create HTTP/1.1
Host: agent-server.example
]]>
</artwork>
<t>or</t>

<artwork><![CDATA[GET /webauthn?type=get HTTP/1.1
Host: agent-server.example
]]>
</artwork>
<t>Query parameters:</t>

<ul spacing="compact">
<li><strong><tt>type</tt></strong> (REQUIRED). Either <tt>create</tt> (for bootstrap registration) or <tt>get</tt> (for refresh assertion).</li>
</ul>
<t>The request is unsigned. The agent MUST NOT include user identifiers on the challenge request.</t>
</section>

<section anchor="response"><name>Response</name>

<artwork><![CDATA[HTTP/1.1 200 OK
Content-Type: application/json

{
  "challenge": "<base64url random bytes>",
  "creation_options": {
    "rp": { "id": "agent-server.example", "name": "Example AI Assistant" },
    "pubKeyCredParams": [{ "type": "public-key", "alg": -7 }, ...],
    "authenticatorSelection": { ... },
    "attestation": "none"
  }
}
]]>
</artwork>
<t>Response members:</t>

<ul spacing="compact">
<li><strong><tt>challenge</tt></strong> (REQUIRED). Server-generated random value of at least 16 bytes, base64url-encoded without padding. The agent server MUST store the challenge in a single-use, time-limited registry. The challenge SHOULD expire within 5 minutes.</li>
<li><strong><tt>creation_options</tt></strong> (REQUIRED when <tt>type=create</tt>). The non-user portions of a WebAuthn <tt>PublicKeyCredentialCreationOptions</tt> object (<xref target="WebAuthn"/>): <tt>rp</tt>, <tt>pubKeyCredParams</tt>, <tt>authenticatorSelection</tt>, <tt>attestation</tt>, <tt>timeout</tt>, and related server-decided fields. The agent fills in the <tt>challenge</tt> field from this response and fills in the <tt>user</tt> field from the <tt>bootstrap_token</tt>, deriving <tt>user.id</tt> from <tt>bootstrap_token.sub</tt>. WebAuthn (<xref target="WebAuthn"/>, Section 5.4.3) limits <tt>user.id</tt> to 64 bytes and requires that it not contain personally identifying information; the agent MUST encode <tt>bootstrap_token.sub</tt> accordingly (typically the raw 32-byte SHA-256 of <tt>sub</tt>, since <tt>sub</tt> is already a pairwise opaque identifier). MUST be absent when <tt>type=get</tt>.</li>
</ul>
<t>The response MUST NOT include user-identifying information. The agent server MUST NOT bind the challenge to a particular user in its issuing state; user context enters only when the ceremony result is submitted.</t>
</section>
</section>

<section anchor="bootstrap-flow"><name>Bootstrap Flow</name>
<t>The bootstrap flow consists of the following steps.</t>

<section anchor="key-generation"><name>Key Generation</name>
<t>The agent generates an ephemeral signing key.</t>
<t>On mobile platforms, the agent also has (or generates on first install) a durable enclave-protected key with a stable JWK thumbprint of the form <tt>urn:jkt:sha-256:&lt;thumbprint&gt;</tt>.</t>
<t>In the web app profile, only the ephemeral key exists. (The self-hosted profile uses a JWKS-published hardware-bound key in place of these; see <xref target="self-hosted-agents"/>.)</t>
</section>

<section anchor="request-to-ps-bootstrap"><name>Request to PS /bootstrap</name>
<t>The agent sends an HTTP request to the PS's <tt>/bootstrap</tt> endpoint signed under the <tt>hwk</tt> scheme (<xref target="I-D.hardt-httpbis-signature-key"/>) using the ephemeral key:</t>

<artwork><![CDATA[POST /bootstrap HTTP/1.1
Host: ps.example
Signature-Input: sig=...
Signature: sig=...
Signature-Key: sig=hwk;kty="OKP";crv="Ed25519";x="<ephemeral-pubkey>"
Content-Type: application/json

{
  "agent_server": "https://agent-server.example"
}
]]>
</artwork>
<t>The request body is a JSON object with the following members:</t>

<ul spacing="compact">
<li><strong><tt>agent_server</tt></strong> (REQUIRED). The HTTPS URL identifying the agent server that will mint the agent identity. This value is placed in the <tt>aud</tt> claim of the issued <tt>bootstrap_token</tt>.</li>
<li><strong><tt>tenant</tt></strong>, <strong><tt>domain_hint</tt></strong>, <strong><tt>login_hint</tt></strong> (OPTIONAL account hints). See <xref target="account-hints"/> for semantics and selection guidance.</li>
</ul>
</section>

<section anchor="interaction-response"><name>Interaction Response</name>
<t>The PS responds with an interaction requirement using the requirement-response and polling pattern defined in <xref target="I-D.hardt-aauth-protocol"/>:</t>

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

{
  "status": "pending"
}
]]>
</sourcecode>
<t>The agent constructs the user-facing URL from the <tt>url</tt> and <tt>code</tt> parameters and directs the user to it per the methods defined in <xref target="I-D.hardt-aauth-protocol"/> (browser redirect, QR code, or display code). The PS authenticates the user and presents a consent screen asking the user to allow the agent server to establish an account bound to the user at this PS. No identity claims are released at this step; claims flow through the standard three-party flow defined in <xref target="I-D.hardt-aauth-protocol"/> after bootstrap completes.</t>
<t>The consent screen SHOULD display the agent server's display name and logo as retrieved from the agent server's <tt>/.well-known/aauth-agent.json</tt> metadata document, alongside the agent server's host. The user approves or denies the request.</t>
<t>The agent polls the <tt>Location</tt> URL until the interaction completes, following the polling rules in <xref target="I-D.hardt-aauth-protocol"/>. While the interaction is pending the PS returns the same <tt>202</tt> envelope; on completion it returns the response defined in <xref target="bootstrap-token-issuance"/>.</t>
</section>

<section anchor="bootstrap-token-issuance"><name>bootstrap_token Issuance</name>
<t>On successful user approval, a poll of the pending URL returns the <tt>bootstrap_token</tt>:</t>

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

{
  "bootstrap_token": "eyJhbGc..."
}
]]>
</sourcecode>
<t>The <tt>bootstrap_token</tt> is a signed JWT <xref target="RFC7519"/> with the following structure.</t>

<section anchor="bootstrap-token-header"><name>bootstrap_token Header</name>

<ul spacing="compact">
<li><strong><tt>alg</tt></strong>: The signature algorithm (e.g., <tt>EdDSA</tt>).</li>
<li><strong><tt>typ</tt></strong>: <tt>aa-bootstrap+jwt</tt>.</li>
<li><strong><tt>kid</tt></strong>: Key identifier for the PS key used to sign the token.</li>
</ul>
</section>

<section anchor="bootstrap-token-payload"><name>bootstrap_token Payload</name>

<ul spacing="compact">
<li><strong><tt>iss</tt></strong> (REQUIRED). The PS URL.</li>
<li><strong><tt>dwk</tt></strong> (REQUIRED). The well-known document name used to discover the PS's JWKS, defined in <xref target="I-D.hardt-httpbis-signature-key"/>. For a PS this is <tt>aauth-person.json</tt>.</li>
<li><strong><tt>aud</tt></strong> (REQUIRED). The agent server URL (matches <tt>agent_server</tt> from the request).</li>
<li><strong><tt>sub</tt></strong> (REQUIRED). A pairwise user identifier, directed at <tt>aud</tt>, identifying the user to the agent server.</li>
<li><strong><tt>cnf</tt></strong> (REQUIRED). Confirmation claim (<xref target="RFC7800"/>) with <tt>jwk</tt> containing the agent's ephemeral public key. The key MUST match the <tt>hwk</tt> key used to sign the PS /bootstrap request.</li>
<li><strong><tt>jti</tt></strong> (REQUIRED). A unique token identifier per <xref target="RFC7519"/>.</li>
<li><strong><tt>iat</tt></strong> (REQUIRED). Issued-at time per <xref target="RFC7519"/>.</li>
<li><strong><tt>exp</tt></strong> (REQUIRED). Expiration time per <xref target="RFC7519"/>. SHOULD NOT exceed 5 minutes after <tt>iat</tt>.</li>
</ul>
<t>The <tt>bootstrap_token</tt> does not carry <tt>scope</tt>, <tt>agent</tt>, or any claim describing user attributes. Its sole purpose is to convey a directed user identifier (<tt>sub</tt>) bound to an agent-side ephemeral key (<tt>cnf</tt>). Identity claims are obtained separately via the three-party flow defined in <xref target="I-D.hardt-aauth-protocol"/> after bootstrap.</t>
<t>The <tt>bootstrap_token</tt> differs from the resource token defined in <xref target="I-D.hardt-aauth-protocol"/>:</t>

<ul spacing="compact">
<li>It omits the <tt>agent</tt> claim, because the agent identity does not yet exist at bootstrap time. The binding to the agent is carried solely by <tt>cnf</tt>.</li>
<li>It is directed at an agent server, not at a PS or AS.</li>
</ul>
</section>
</section>

<section anchor="request-to-agent-server-bootstrap"><name>Request to Agent Server /bootstrap</name>
<t>This step is omitted for self-hosted agents <xref target="self-hosted-agents"/>, where the agent and agent server are co-located and the agent self-issues the agent token instead of POSTing to a remote <tt>bootstrap_endpoint</tt>. The remainder of this section applies to web app and mobile profiles.</t>
<t>Before calling <tt>bootstrap_endpoint</tt>, the agent performs the attestation ceremony appropriate to its platform. See <xref target="attestation"/> for the ceremony details and for the logic by which the platform is chosen. The attestation result is carried in the POST body.</t>
<t>The agent sends a single POST to <tt>bootstrap_endpoint</tt>:</t>

<artwork><![CDATA[POST /bootstrap HTTP/1.1
Host: agent-server.example
Signature-Input: sig=...
Signature: sig=...
Signature-Key: sig=<scheme>
Content-Type: application/json

{
  "bootstrap_token": "<jwt>",
  "attestation": {
    "type": "<attestation type>",
    ...
  }
}
]]>
</artwork>
<t>The HTTP Message Signature scheme is determined by the platform:</t>

<ul spacing="compact">
<li><strong>Web app clients</strong> use <tt>hwk</tt> with the agent's ephemeral key.</li>
<li><strong>Mobile clients</strong> use <tt>jkt-jwt</tt> (<xref target="I-D.hardt-httpbis-signature-key"/>), where the JWT header references the enclave key and the JWT payload's <tt>cnf.jwk</tt> is the ephemeral public key. The ephemeral key signs the HTTP message.</li>
</ul>
<t>Request body members:</t>

<ul spacing="compact">
<li><strong><tt>bootstrap_token</tt></strong> (REQUIRED). The JWT received from the PS.</li>
<li><strong><tt>attestation</tt></strong> (REQUIRED). The attestation result, whose shape is defined by the ceremony described in <xref target="attestation"/>. The <tt>type</tt> field identifies the ceremony (e.g., <tt>webauthn</tt>, <tt>app-attest</tt>, <tt>play-integrity</tt>) and MUST match the scheme implied by the HTTP signature.</li>
</ul>
<t>On receiving the request, the agent server MUST:</t>

<ol spacing="compact">
<li>Verify the HTTP Message Signature per <xref target="RFC9421"/> and <xref target="I-D.hardt-httpbis-signature-key"/>.</li>
<li>Resolve and validate the <tt>bootstrap_token</tt> signature per <xref target="RFC7515"/> and <xref target="RFC7519"/> by fetching the PS's JWKS using <tt>iss</tt> and <tt>dwk</tt>.</li>
<li>Apply local policy on whether <tt>bootstrap_token.iss</tt> is an acceptable PS for this agent server. By default, consumer-profile agent servers SHOULD accept any PS that publishes valid metadata; B2B-profile agent servers SHOULD restrict acceptance to an explicit allowlist. Reject with <tt>403 Forbidden</tt> if policy denies.</li>
<li>Verify that <tt>bootstrap_token.aud</tt> equals the agent server's own URL.</li>
<li>Verify that <tt>bootstrap_token.cnf.jwk</tt> matches the ephemeral public key used to sign the HTTP request.</li>
<li>Verify <tt>iat</tt> is not in the future and <tt>exp</tt> is in the future per <xref target="RFC7519"/>; reject replays by <tt>jti</tt>.</li>
<li>Verify the <tt>attestation</tt> per the rules of the indicated ceremony in <xref target="attestation"/>, including that any ceremony challenge or nonce matches one issued by the agent server and has not expired or been consumed. Mark the ceremony challenge as consumed.</li>
<li>Create or look up the binding <xref target="binding-creation"/>.</li>
<li>Store the device credential derived from the attestation (a WebAuthn credential ID for web apps, or the enclave key thumbprint for mobile) on the binding.</li>
<li>Issue the agent token <xref target="token-response"/>.</li>
</ol>
<t>If <tt>bootstrap_token</tt> verification fails, the agent server MUST respond <tt>400 Bad Request</tt>. If issuer policy denies, the agent server MUST respond <tt>403 Forbidden</tt>. If attestation verification fails, the agent server MUST respond <tt>401 Unauthorized</tt>.</t>
</section>

<section anchor="binding-creation"><name>Binding Creation</name>
<t>The agent server looks up or creates a binding keyed by <tt>(bootstrap_token.iss, bootstrap_token.sub)</tt>:</t>

<artwork><![CDATA[(ps_url, user_sub) -> aauth:local@<agent-server-domain>
]]>
</artwork>
<t>The binding is one-to-one: the same user at the same PS at the same agent server MUST map to the same <tt>aauth:local@domain</tt> identity, regardless of device.</t>
<t>The agent server stores the device credential obtained during attestation (a WebAuthn credential ID, or a <tt>urn:jkt:sha-256:&lt;enclave&gt;</tt> thumbprint) against the binding. Multiple device credentials MAY be associated with a single binding.</t>
</section>

<section anchor="token-response"><name>Token Response</name>
<t>The agent server returns an agent token:</t>

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

{
  "agent_token": "eyJhbGc..."
}
]]>
</sourcecode>
<t>The <tt>agent_token</tt> is a JWT <xref target="RFC7519"/> as defined in <xref target="I-D.hardt-aauth-protocol"/> with:</t>

<ul spacing="compact">
<li><strong><tt>typ</tt></strong>: <tt>aa-agent+jwt</tt>.</li>
<li><strong><tt>iss</tt></strong>: The agent server URL.</li>
<li><strong><tt>dwk</tt></strong>: <tt>aauth-agent.json</tt>.</li>
<li><strong><tt>sub</tt></strong>: The <tt>aauth:local@&lt;agent-server-host&gt;</tt> identity from the binding.</li>
<li><strong><tt>ps</tt></strong>: The PS URL (from <tt>bootstrap_token.iss</tt>).</li>
<li><strong><tt>cnf.jwk</tt></strong>: The agent's ephemeral public key.</li>
</ul>
<t>The agent server does not issue a resource token at bootstrap. If the agent server needs identity claims about the user (e.g., to populate a profile page), it follows the standard three-party flow defined in <xref target="I-D.hardt-aauth-protocol"/> after bootstrap completes.</t>
</section>

<section anchor="bootstrap-completion"><name>Bootstrap Completion</name>
<t>Once the agent holds the <tt>agent_token</tt>, it SHOULD announce its new agent identity to the PS so the PS can bind the identity to the user within the PS's bootstrap record. The agent SHOULD perform this announcement immediately upon receiving the <tt>agent_token</tt> and in any case before <tt>bootstrap_token.exp</tt>, because the PS correlates the announcement to the bootstrap record by the ephemeral key's thumbprint and MAY discard records after expiry. The agent MUST perform the announcement before rotating its ephemeral key (that is, before any call to <tt>refresh_endpoint</tt>).</t>

<section anchor="announcement-request"><name>Announcement Request</name>
<t>The agent sends an empty POST to the PS's <tt>/bootstrap</tt> endpoint, signed under the <tt>jwt</tt> scheme (<xref target="I-D.hardt-httpbis-signature-key"/>) with the <tt>agent_token</tt> as the naming JWT:</t>

<sourcecode type="http"><![CDATA[POST /bootstrap HTTP/1.1
Host: ps.example
Signature-Input: sig=...
Signature: sig=...
Signature-Key: sig=jwt;jwt="<agent_token>"
Content-Length: 0
]]>
</sourcecode>
<t>The PS distinguishes the announcement from the initial bootstrap by the signature scheme and the empty body: the initial call uses <tt>hwk</tt> with a JSON body, the announcement uses <tt>jwt</tt> with an empty body.</t>
</section>

<section anchor="ps-processing"><name>PS Processing</name>
<t>On receiving the announcement, the PS MUST:</t>

<ol spacing="compact">
<li>Verify the HTTP Message Signature per <xref target="RFC9421"/> and <xref target="I-D.hardt-httpbis-signature-key"/> under the <tt>jwt</tt> scheme.</li>
<li>Verify the <tt>agent_token</tt> per <xref target="RFC7515"/> and <xref target="RFC7519"/> by resolving the agent server's JWKS via <tt>agent_token.iss</tt> and <tt>agent_token.dwk</tt> per <xref target="I-D.hardt-httpbis-signature-key"/>.</li>
<li>Verify that <tt>agent_token.ps</tt> equals this PS's URL.</li>
<li>Look up the bootstrap record by the thumbprint of <tt>agent_token.cnf.jwk</tt>.</li>
<li>If a matching bootstrap record exists, record the binding between <tt>agent_token.sub</tt> (the <tt>aauth:local@domain</tt> identifier) and the <tt>(user, agent_server)</tt> tuple already on file, then respond <tt>204 No Content</tt>.</li>
<li>If no matching record exists, respond <tt>404 Not Found</tt>.</li>
</ol>
<t>The announcement is idempotent: repeated calls for the same ephemeral thumbprint after a successful binding have no effect and respond <tt>204 No Content</tt>.</t>
<t>The PS MUST retain the bootstrap record at least until <tt>bootstrap_token.exp</tt>, plus a small allowance for clock skew (a few seconds is sufficient). After that time the PS MAY discard the record; an announcement that arrives later MAY fail with <tt>404</tt>. Even when the explicit announcement fails, the PS MAY learn the binding lazily from the <tt>agent</tt> claim of a resource token presented at the PS <tt>/token</tt> endpoint during the standard three-party flow.</t>
</section>

<section anchor="post-bootstrap-api-access"><name>Post-Bootstrap API Access</name>
<t>With the <tt>agent_token</tt> in hand and the announcement made, the agent uses the <tt>agent_token</tt> to call the agent server's APIs:</t>

<ul spacing="compact">
<li><strong>Identity-based calls</strong>, where the agent server authorizes based solely on the agent's identity, use the <tt>agent_token</tt> directly per the identity-based mode of <xref target="I-D.hardt-aauth-protocol"/>.</li>
<li><strong>Calls requiring user claims</strong> follow the standard three-party flow defined in <xref target="I-D.hardt-aauth-protocol"/>: the agent calls the agent server's <tt>authorization_endpoint</tt> to obtain a resource token, exchanges that resource token at the PS <tt>/token</tt> endpoint for an auth token carrying the required claims, then calls the agent server's API with the auth token.</li>
</ul>
<t>Each claim-bearing call follows the same pattern, which lets the agent request claims incrementally rather than up front. The PS applies its own policy (including any user consent) when issuing each auth token.</t>
</section>
</section>
</section>

<section anchor="attestation"><name>Attestation</name>
<t>Every call in this specification that mints tokens for an agent (at bootstrap or at renewal) carries a platform-specific attestation, except for self-hosted agents <xref target="self-hosted-agents"/> where the trust anchor is the user-controlled JWKS-published key. At bootstrap the attestation registers a device credential on the binding; at renewal it proves possession of that device credential. Without attestation an attacker who intercepted a <tt>bootstrap_token</tt> or replayed a refresh could obtain tokens from a machine the user does not control.</t>

<section anchor="ceremony-selection"><name>Ceremony Selection</name>
<t>The agent selects the ceremony from its runtime platform. The agent server determines which ceremony to expect from the HTTP signature scheme on the <tt>bootstrap_endpoint</tt> or <tt>refresh_endpoint</tt> POST.</t>
<table>
<thead>
<tr>
<th>Platform</th>
<th>Bootstrap ceremony</th>
<th>Renewal ceremony</th>
<th>HTTP signature scheme</th>
</tr>
</thead>

<tbody>
<tr>
<td>Self-hosted</td>
<td>None (hardware-bound JWKS key)</td>
<td>None (hardware-bound JWKS key)</td>
<td><tt>jwt</tt> (self-issued agent token)</td>
</tr>

<tr>
<td>Web app (browser-hosted)</td>
<td>WebAuthn registration (<xref target="WebAuthn"/>)</td>
<td>WebAuthn assertion (<xref target="WebAuthn"/>)</td>
<td><tt>hwk</tt></td>
</tr>

<tr>
<td>Mobile (iOS)</td>
<td>App Attest</td>
<td>Enclave <tt>jkt-jwt</tt></td>
<td><tt>jkt-jwt</tt></td>
</tr>

<tr>
<td>Mobile (Android)</td>
<td>Play Integrity</td>
<td>Enclave <tt>jkt-jwt</tt></td>
<td><tt>jkt-jwt</tt></td>
</tr>
</tbody>
</table><t>An agent server that accepts web app clients MUST publish <tt>webauthn_endpoint</tt> in its metadata <xref target="agent-server-metadata"/>.</t>
<t>For mobile clients, the agent server MUST nominate the nonce consumed by the platform attestation; the nonce MUST be at least 16 bytes of cryptographically random data, single-use, and MUST expire within 5 minutes of issuance. The transport by which the agent obtains the nonce is application-defined: most mobile agents already share an authenticated channel with their server and can carry the nonce on an existing endpoint. No metadata field for this transport is defined here. <xref target="mobile-nonce-example"/> gives a non-normative example shape for implementations that do not have an existing channel.</t>
<t>Self-hosted agents do not use <tt>bootstrap_endpoint</tt> or <tt>refresh_endpoint</tt> and therefore do not perform a ceremony at the agent server step; the trust anchor is the hardware-bound JWKS key described in <xref target="self-hosted-agents"/>.</t>
</section>

<section anchor="web-app-webauthn"><name>Web App: WebAuthn</name>
<t>Web app clients use the same <tt>webauthn_endpoint</tt> <xref target="webauthn-endpoint"/> for both bootstrap (<tt>type=create</tt>) and renewal (<tt>type=get</tt>). The ceremony result is carried in the POST body to <tt>bootstrap_endpoint</tt> or <tt>refresh_endpoint</tt>.</t>

<section anchor="registration-at-bootstrap"><name>Registration (at Bootstrap)</name>
<t>The agent first fetches a challenge and creation options from <tt>webauthn_endpoint</tt>:</t>

<artwork><![CDATA[GET /webauthn?type=create HTTP/1.1
Host: agent-server.example
]]>
</artwork>

<artwork><![CDATA[HTTP/1.1 200 OK
Content-Type: application/json

{
  "challenge": "<base64url random bytes>",
  "creation_options": {
    "rp": { "id": "agent-server.example", "name": "Example AI Assistant" },
    "pubKeyCredParams": [
      { "type": "public-key", "alg": -7 },
      { "type": "public-key", "alg": -257 }
    ],
    "authenticatorSelection": {
      "residentKey": "required",
      "userVerification": "preferred"
    },
    "attestation": "none"
  }
}
]]>
</artwork>
<t>The agent invokes <tt>navigator.credentials.create()</tt> using those options, filling <tt>user.id</tt> with <tt>bootstrap_token.sub</tt> and supplying <tt>user.name</tt> and <tt>user.displayName</tt> from agent-side data. The resulting <tt>PublicKeyCredential</tt> is included in the POST to <tt>bootstrap_endpoint</tt>:</t>

<artwork><![CDATA[POST /bootstrap HTTP/1.1
Host: agent-server.example
Signature-Input: sig=...
Signature: sig=...
Signature-Key: sig=hwk;kty="OKP";crv="Ed25519";x="<ephemeral-pubkey>"
Content-Type: application/json

{
  "bootstrap_token": "<jwt>",
  "attestation": {
    "type": "webauthn",
    "credential": {
      "id": "<base64url credential id>",
      "rawId": "<base64url>",
      "response": {
        "clientDataJSON": "<base64url>",
        "attestationObject": "<base64url>"
      },
      "type": "public-key"
    }
  }
}
]]>
</artwork>
<t>The agent server verifies the WebAuthn registration (<xref target="WebAuthn"/>) including that <tt>clientDataJSON.challenge</tt> matches a challenge it issued via <tt>webauthn_endpoint</tt>, and records the credential ID as a device credential on the binding.</t>
</section>

<section anchor="assertion-at-renewal"><name>Assertion (at Renewal)</name>
<t>The agent first fetches a challenge from <tt>webauthn_endpoint</tt>:</t>

<artwork><![CDATA[GET /webauthn?type=get HTTP/1.1
Host: agent-server.example
]]>
</artwork>

<artwork><![CDATA[HTTP/1.1 200 OK
Content-Type: application/json

{
  "challenge": "<base64url random bytes>"
}
]]>
</artwork>
<t>The agent invokes <tt>navigator.credentials.get()</tt> with that challenge and the agent server's <tt>rpId</tt>, using a discoverable credential (no <tt>allowCredentials</tt>). The resulting <tt>PublicKeyCredential</tt> is included in the POST to <tt>refresh_endpoint</tt>:</t>

<artwork><![CDATA[POST /refresh HTTP/1.1
Host: agent-server.example
Signature-Input: sig=...
Signature: sig=...
Signature-Key: sig=hwk;kty="OKP";crv="Ed25519";x="<new-ephemeral-pubkey>"
Content-Type: application/json

{
  "attestation": {
    "type": "webauthn",
    "credential": {
      "id": "<base64url credential id>",
      "rawId": "<base64url>",
      "response": {
        "clientDataJSON": "<base64url>",
        "authenticatorData": "<base64url>",
        "signature": "<base64url>",
        "userHandle": "<base64url>"
      },
      "type": "public-key"
    }
  }
}
]]>
</artwork>
<t>The agent server looks up the binding by the credential's <tt>rawId</tt> and verifies the WebAuthn assertion (<xref target="WebAuthn"/>) including that <tt>clientDataJSON.challenge</tt> matches a challenge it issued via <tt>webauthn_endpoint</tt>.</t>
</section>
</section>

<section anchor="mobile-platform-attestation-and-enclave-proof"><name>Mobile: Platform Attestation and Enclave Proof</name>
<t>Mobile clients use App Attest (<xref target="AppAttest"/>) on iOS or Play Integrity (<xref target="PlayIntegrity"/>) on Android at bootstrap to enroll the enclave key as the device credential. At renewal, the enclave key signature in the <tt>jkt-jwt</tt> HTTP Message Signature is itself sufficient proof; no additional ceremony is required.</t>

<section anchor="initial-attestation-at-bootstrap"><name>Initial Attestation (at Bootstrap)</name>
<t>The agent obtains a nonce from the agent server (subject to the constraints in <xref target="ceremony-selection"/>) and feeds it to the platform attestation API. On iOS the nonce is bound by computing <tt>clientDataHash</tt> over the nonce; on Android the nonce is supplied to the Play Integrity request and surfaced in <tt>requestDetails.nonce</tt> of the verdict.</t>
<t>Once the ceremony has produced an attestation, the agent POSTs to <tt>bootstrap_endpoint</tt>:</t>

<artwork><![CDATA[POST /bootstrap HTTP/1.1
Host: agent-server.example
Signature-Input: sig=...
Signature: sig=...
Signature-Key: sig=jkt-jwt;jwt="<jkt-jwt-token>"
Content-Type: application/json

{
  "bootstrap_token": "<jwt>",
  "attestation": {
    "type": "app-attest",
    "key_id": "<base64url>",
    "attestation_object": "<base64url>",
    "client_data_hash": "<base64url>"
  }
}
]]>
</artwork>
<t>For Play Integrity the <tt>attestation</tt> object has the form:</t>

<sourcecode type="json"><![CDATA[{
  "type": "play-integrity",
  "integrity_token": "<JWS-encoded attestation>"
}
]]>
</sourcecode>
<t>The agent server verifies the attestation per the platform's published rules, including that the nonce was one it nominated, and records the enclave key thumbprint (<tt>urn:jkt:sha-256:&lt;thumbprint&gt;</tt>) as the device credential on the binding.</t>
</section>

<section anchor="enclave-proof-at-renewal"><name>Enclave Proof (at Renewal)</name>
<t>The agent generates a new ephemeral key, has the enclave sign a <tt>jkt-jwt</tt> binding that key, and POSTs to <tt>refresh_endpoint</tt>:</t>

<artwork><![CDATA[POST /refresh HTTP/1.1
Host: agent-server.example
Signature-Input: sig=...
Signature: sig=...
Signature-Key: sig=jkt-jwt;jwt="<jkt-jwt-token>"
]]>
</artwork>
<t>The request body is empty. The agent server computes the enclave key thumbprint from the <tt>jkt-jwt</tt> header, looks up the binding, and issues a fresh <tt>agent_token</tt>. No <tt>attestation</tt> member appears in the body because the <tt>jkt-jwt</tt> signature itself is the proof.</t>
</section>

<section anchor="mobile-nonce-example"><name>Non-Normative Nonce Endpoint Example</name>
<t>This non-normative example shows one shape an agent server might expose if it does not already have an authenticated channel suitable for delivering the attestation nonce. It mirrors the <tt>webauthn_endpoint</tt> pattern: an unsigned GET that returns an opaque single-use nonce. Implementations are free to use any other transport that meets the constraints in <xref target="ceremony-selection"/>.</t>
<t>Request:</t>

<sourcecode type="http"><![CDATA[GET /attestation/nonce HTTP/1.1
Host: agent-server.example
]]>
</sourcecode>
<t>Response:</t>

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

{
  "nonce": "<base64url random bytes>"
}
]]>
</sourcecode>
<t>The agent feeds <tt>nonce</tt> to the platform attestation API per <xref target="initial-attestation-at-bootstrap"/>.</t>
</section>
</section>
</section>

<section anchor="signature-scheme-summary"><name>Signature Scheme Summary</name>
<t>The following signature schemes from <xref target="I-D.hardt-httpbis-signature-key"/> are used at each step of bootstrap and renewal:</t>
<table>
<thead>
<tr>
<th>Context</th>
<th>Scheme</th>
<th>Key material</th>
</tr>
</thead>

<tbody>
<tr>
<td>PS /bootstrap initial request</td>
<td><tt>hwk</tt></td>
<td>Ephemeral (inline)</td>
</tr>

<tr>
<td>PS /bootstrap announcement request</td>
<td><tt>jwt</tt></td>
<td>Ephemeral (via agent_token)</td>
</tr>

<tr>
<td>Agent Server webauthn_endpoint request (web app)</td>
<td>unsigned</td>
<td>(no signature)</td>
</tr>

<tr>
<td>Agent Server bootstrap_endpoint request (web app)</td>
<td><tt>hwk</tt></td>
<td>Ephemeral (same as PS call)</td>
</tr>

<tr>
<td>Agent Server bootstrap_endpoint request (mobile)</td>
<td><tt>jkt-jwt</tt></td>
<td>Enclave signs ephemeral</td>
</tr>

<tr>
<td>Agent Server refresh_endpoint request (web app)</td>
<td><tt>hwk</tt> + WebAuthn assertion in body</td>
<td>New ephemeral + user proof</td>
</tr>

<tr>
<td>Agent Server refresh_endpoint request (mobile)</td>
<td><tt>jkt-jwt</tt></td>
<td>Enclave signs new ephemeral</td>
</tr>

<tr>
<td>Self-hosted: agent self-issues agent_token</td>
<td>n/a (no agent server endpoint call)</td>
<td>JWKS-published hardware-bound key</td>
</tr>

<tr>
<td>Post-bootstrap resource calls</td>
<td><tt>jwt</tt></td>
<td>agent_token wrapping ephemeral</td>
</tr>
</tbody>
</table></section>

<section anchor="renewal"><name>Renewal</name>
<t>Agent tokens expire. Per <xref target="I-D.hardt-aauth-protocol"/> the maximum lifetime is 24 hours. Renewal bypasses the PS because the agent server already holds the <tt>(user, agent)</tt> binding and the device credential recorded at bootstrap.</t>
<t>Agent servers that accept web app or mobile clients MUST expose a <tt>refresh_endpoint</tt> as defined in <xref target="agent-server-metadata"/>. The endpoint issues a fresh <tt>agent_token</tt> bound to a new ephemeral key.</t>

<section anchor="renewal-flow"><name>Renewal Flow</name>
<t>Before calling <tt>refresh_endpoint</tt>, the agent performs the renewal attestation ceremony appropriate to its platform. See <xref target="attestation"/> for the ceremony details. The ceremony result, if any, is carried in the POST body.</t>

<artwork><![CDATA[POST /refresh HTTP/1.1
Host: agent-server.example
Signature-Input: sig=...
Signature: sig=...
Signature-Key: sig=<scheme>
Content-Type: application/json

{
  "attestation": {
    "type": "<attestation type>",
    ...
  }
}
]]>
</artwork>
<t>The HTTP Message Signature scheme is determined by the platform:</t>

<ul spacing="compact">
<li><strong>Web app clients</strong> use <tt>hwk</tt> with a newly generated ephemeral key.</li>
<li><strong>Mobile clients</strong> use <tt>jkt-jwt</tt> (<xref target="I-D.hardt-httpbis-signature-key"/>), where the enclave key signs a <tt>jkt-jwt</tt> binding a newly generated ephemeral key; the ephemeral key signs the HTTP message. For mobile, the request body MAY be empty because the <tt>jkt-jwt</tt> signature itself proves possession of the device credential.</li>
</ul>
<t>Request body members:</t>

<ul spacing="compact">
<li><strong><tt>attestation</tt></strong> (REQUIRED for web app clients; omitted for mobile clients). The assertion result defined by the renewal ceremony in <xref target="attestation"/>.</li>
</ul>
<t>On receiving the request, the agent server MUST:</t>

<ol spacing="compact">
<li>Verify the HTTP Message Signature per <xref target="RFC9421"/> and <xref target="I-D.hardt-httpbis-signature-key"/>.</li>
<li><t>Look up the binding:</t>

<ul spacing="compact">
<li>For web app clients, by the credential ID (<tt>rawId</tt>) in the WebAuthn assertion.</li>
<li>For mobile clients, by the enclave key thumbprint (<tt>urn:jkt:sha-256:&lt;thumbprint&gt;</tt>) derived from the <tt>jkt-jwt</tt> header.</li>
</ul></li>
<li>Verify the renewal attestation per the rules of the indicated ceremony in <xref target="attestation"/>, including that any ceremony challenge matches one it issued and has not expired or been consumed. Mark the challenge as consumed.</li>
<li>Issue a fresh agent token bound to the new ephemeral key.</li>
</ol>
</section>

<section anchor="refresh-response"><name>Refresh Response</name>
<t>On success, the agent server returns:</t>

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

{
  "agent_token": "eyJhbGc..."
}
]]>
</sourcecode>
<t>The <tt>agent_token</tt> has the same structure as in <xref target="token-response"/> but bound to the new ephemeral key.</t>
</section>

<section anchor="renewal-failure"><name>Renewal Failure</name>
<t>Renewal MUST fail in the following conditions. The agent MUST then repeat the full PS-mediated bootstrap flow from <xref target="bootstrap-flow"/>. The agent server distinguishes the failure mode by status code:</t>

<ul spacing="compact">
<li><strong><tt>404 Not Found</tt></strong> when no binding matches the credential identifier presented (e.g., the enclave key has been regenerated after mobile reinstall and the <tt>urn:jkt</tt> is unknown; the WebAuthn <tt>rawId</tt> is unknown; or the binding has been revoked at the agent server and removed from storage).</li>
<li><strong><tt>401 Unauthorized</tt></strong> when a binding is found but the proof fails (e.g., a WebAuthn assertion does not verify, the <tt>jkt-jwt</tt> signature does not verify, or the ceremony challenge has expired or been consumed).</li>
</ul>
<t>Independently, the PS MAY have stopped vouching for the user (for example, account closure or organizational deprovisioning). The agent server has no direct signal of this; it surfaces only through a subsequent PS <tt>/bootstrap</tt> call that fails at the interaction step. The agent will then be unable to re-establish the binding.</t>
</section>
</section>

<section anchor="agent-person-binding-invariant"><name>Agent-Person Binding Invariant</name>
<t>Per <xref target="I-D.hardt-aauth-protocol"/>, each agent is bound to exactly one person. In bootstrap:</t>

<ul spacing="compact">
<li><tt>(ps_url, user_sub) -&gt; aauth:local@&lt;agent-server-domain&gt;</tt> is one-to-one.</li>
<li>The same user at the same PS maps to the same <tt>aauth:local@&lt;agent-server-domain&gt;</tt> regardless of device.</li>
<li>Multiple devices per binding MAY exist through multiple associated device credentials.</li>
<li>Revoking a binding at the agent server revokes access for all devices bound to it.</li>
</ul>
<t>This supports:</t>

<ul spacing="compact">
<li>Account switching, where an agent holds multiple agent tokens for multiple <tt>(user, agent)</tt> bindings and selects among them at runtime.</li>
<li>Multi-device use, where a single <tt>aauth:local</tt> identity has device credentials on several devices.</li>
<li>Immediate cross-device revocation, where disabling the binding at the agent server invalidates all device credentials simultaneously.</li>
</ul>
</section>

<section anchor="out-of-band-user-channel"><name>Out-of-Band User Channel</name>
<t>After first bootstrap, the PS knows the <tt>(user, agent_server)</tt> binding and MAY establish a communication channel with the user (push, email, or an authenticated session). PSes that establish such a channel at first bootstrap can use it to prompt the user for consent on subsequent <tt>auth_token</tt> requests from the agent server without requiring an in-app interaction.</t>
<t>This does not change the bootstrap protocol itself; bootstrap produces only the binding. It enables a smoother user experience for the standard three-party flow by letting the PS reach the user out of band when a token request requires consent.</t>
<t>PS implementations that wish to support out-of-band consent SHOULD establish a direct user communication channel at first bootstrap.</t>
</section>

<section anchor="self-hosted-agents"><name>Self-Hosted Agents</name>
<t>In a self-hosted deployment the agent and agent server are co-located under a domain the user controls. The user publishes the agent server's <tt>/.well-known/aauth-agent.json</tt> metadata document and JWKS at that domain per <xref target="I-D.hardt-aauth-protocol"/>. The corresponding private signing key is held on the user's machine. Self-issued agent tokens are signed by that key and verified by any party against the published JWKS.</t>
<t>JWKS publication, key handling, and key rotation for the agent server role are unchanged from <xref target="I-D.hardt-aauth-protocol"/>. The JWKS-published key used to sign agent tokens SHOULD be hardware-bound where the platform supports it: the macOS Keychain (Secure Enclave on supported hardware), Windows TPM, or Linux Secret Service. This mirrors the durable-key guidance for mobile enclaves elsewhere in this document and is the trust anchor that takes the place of platform attestation in the self-hosted profile.</t>
<t>A self-hosted agent operates in one of two modes:</t>

<section anchor="without-a-ps"><name>Without a PS</name>
<t>The agent self-issues an <tt>agent_token</tt> with no <tt>ps</tt> claim. The agent does not call the PS <tt>/bootstrap</tt> endpoint and does not call the agent server <tt>/bootstrap</tt> endpoint (it would be calling itself). The agent uses its self-issued <tt>agent_token</tt> directly with resources that support identity-based or resource-managed access per <xref target="I-D.hardt-aauth-protocol"/>. Resources requiring PS-issued auth tokens are not reachable in this mode.</t>
</section>

<section anchor="with-a-ps"><name>With a PS</name>
<t>The agent calls the PS <tt>/bootstrap</tt> endpoint to establish a <tt>(user, agent)</tt> binding at the PS, then self-issues an <tt>agent_token</tt> carrying a <tt>ps</tt> claim that names the bound PS. The PS-side flow is unchanged from <xref target="bootstrap-flow"/>:</t>

<ol spacing="compact">
<li>The agent generates an ephemeral key and POSTs to the PS <tt>/bootstrap</tt> endpoint signed under the <tt>hwk</tt> scheme, with <tt>agent_server</tt> set to the agent server's URL (the user's domain rooted at <tt>https://</tt>).</li>
<li>The PS authenticates the user, collects consent, and returns a <tt>bootstrap_token</tt> with <tt>aud</tt> set to the agent server's URL and <tt>sub</tt> set to a directed user identifier per <xref target="bootstrap-token-issuance"/>.</li>
<li>The agent does not POST to its own <tt>bootstrap_endpoint</tt>. Instead, the agent self-issues an <tt>agent_token</tt> per <xref target="token-response"/> with <tt>iss</tt> set to the agent server's URL, <tt>cnf.jwk</tt> set to the same ephemeral key bound in <tt>bootstrap_token.cnf.jwk</tt>, and <tt>ps</tt> set to the PS URL from <tt>bootstrap_token.iss</tt>. The <tt>aauth:local@&lt;agent-server-host&gt;</tt> <tt>sub</tt> of the <tt>agent_token</tt> is chosen by the agent under its own host.</li>
<li>The agent performs the bootstrap completion announcement to the PS per <xref target="bootstrap-completion"/>. The PS verifies the self-issued <tt>agent_token</tt> by resolving the agent server's JWKS at <tt>agent_token.iss</tt>, exactly as it would for an agent server operated separately from the agent.</li>
</ol>
<t>After the announcement, the PS holds the <tt>(user, agent)</tt> binding. When a resource later requires a PS-issued auth token and the PS needs the user's consent, the PS MAY prompt the user out of band over a channel established at first bootstrap <xref target="out-of-band-user-channel"/> instead of requiring an in-app interaction.</t>
</section>
</section>

<section anchor="account-hints"><name>Account Hints</name>
<t>When the agent has prior context about who the user is, it MAY pass an account hint on the PS <tt>/bootstrap</tt> request to help the PS resolve the user without prompting them to choose:</t>

<ul spacing="compact">
<li><strong><tt>login_hint</tt></strong> (<xref target="OpenID.Core"/>, Section 3.1.2.1) — an identifier for the user, typically an email address. Useful in both consumer (B2C) and organizational (B2B) contexts when the agent has learned the user's identifier from a previous session, a sign-up form, or another out-of-band source.</li>
<li><strong><tt>domain_hint</tt></strong> (<xref target="OpenID.Enterprise"/>) — a DNS domain associated with the user. Typically used in B2B contexts where the agent knows the user's company domain but not the specific user.</li>
<li><strong><tt>tenant</tt></strong> (<xref target="OpenID.Enterprise"/>) — an organizational tenant identifier known to the PS. Used in B2B contexts where the agent has been invoked in an organization-specific context (e.g., from a workspace launcher).</li>
</ul>
<t>The PS handles these hints with the same semantics an OpenID Provider applies to the corresponding OpenID Connect parameters: a hint is advisory and the PS MAY override or ignore it based on local policy. The agent SHOULD send only the most specific hint it has.</t>
<t>A request might look like:</t>

<sourcecode type="json"><![CDATA[{
  "agent_server": "https://vendor.example",
  "login_hint": "user@example.com"
}
]]>
</sourcecode>
<t>When the user has more than one account at the PS (e.g., a personal account and a work account), the hint tells the PS which to select. The selected account affects which pairwise <tt>sub</tt> the PS issues and which account subsequent token requests resolve against. When no hint is sent and more than one account is available, the PS MAY prompt the user to choose.</t>
<t>The <tt>bootstrap_token</tt> carries no identity or organization-related claims; those are released in the auth token issued by the PS on the standard three-party flow per <xref target="I-D.hardt-aauth-protocol"/>. This lets the agent server apply user- or organization-based authorization without running a per-customer SAML or OIDC integration.</t>
</section>

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

<section anchor="bootstrap-token-replay"><name>bootstrap_token Replay</name>
<t>The <tt>bootstrap_token</tt> carries a <tt>cnf</tt> claim binding it to the agent's ephemeral public key. Possession of the token alone is insufficient; the holder MUST also control the corresponding private key. Agent servers MUST reject <tt>bootstrap_token</tt>s that are not accompanied by an HTTP request signed with the key named in <tt>cnf.jwk</tt>, and MUST reject replay by <tt>jti</tt>.</t>
</section>

<section anchor="consent-phishing"><name>Consent Phishing</name>
<t>The user sees the agent server's domain, name, and logo at the PS consent screen. This relies on user recognition of the agent server. The PS SHOULD retrieve the agent server's display metadata from its <tt>/.well-known/aauth-agent.json</tt> document and present it at the consent screen.</t>
</section>

<section anchor="key-to-user-binding-without-attestation"><name>Key-to-User Binding Without Attestation</name>
<t>For the web app and mobile profiles, the bootstrap flow relies on platform attestation at the agent server step. Environments without a platform attestation mechanism cannot safely use this flow because a remote process may impersonate a local user by relaying ephemeral keys through them. This is the reason vendor-operated command-line tools are out of scope; self-hosted agents <xref target="self-hosted-agents"/> avoid the problem because the trust anchor is a hardware-bound JWKS-published key controlled by the user.</t>
</section>

<section anchor="enclave-key-compromise"><name>Enclave Key Compromise</name>
<t>On mobile, compromise of the enclave key breaks the chain of delegated ephemeral keys. Implementations SHOULD use the shortest practical <tt>jkt-jwt</tt> lifetimes.</t>
</section>

<section anchor="agent-server-compromise"><name>Agent Server Compromise</name>
<t>Compromise of an agent server breaks all agent identities minted by that server. Bootstrap does not introduce this risk, but centralizes it at the agent server.</t>
</section>

<section anchor="ps-compromise"><name>PS Compromise</name>
<t>The PS is already a high-value target in <xref target="I-D.hardt-aauth-protocol"/>. Bootstrap does not change the risk profile, but makes the PS load-bearing for agent identity creation.</t>
</section>

<section anchor="bootstrap-completion-announcement"><name>Bootstrap Completion Announcement</name>
<t>The announcement POST to the PS <xref target="bootstrap-completion"/> is bound to possession of the agent's ephemeral key plus a signed <tt>agent_token</tt> issued to that key. An attacker would need both the ephemeral private key and a valid <tt>agent_token</tt> for the target <tt>aauth</tt> identifier. These are the same credentials that protect the rest of bootstrap; the announcement introduces no new attack surface.</t>
</section>

<section anchor="resource-exhaustion-at-unauthenticated-endpoints"><name>Resource Exhaustion at Unauthenticated Endpoints</name>
<t>Three endpoints accept unauthenticated requests and allocate server-side state:</t>

<ul spacing="compact">
<li>The PS <tt>/bootstrap</tt> endpoint creates a pending bootstrap record and an interaction code.</li>
<li>The agent server <tt>webauthn_endpoint</tt> issues a single-use challenge.</li>
<li>The non-normative mobile nonce endpoint of <xref target="mobile-nonce-example"/>, if exposed, issues a single-use nonce.</li>
</ul>
<t>An attacker can flood any of these to exhaust storage or CPU. Implementations MUST rate-limit these endpoints (per source IP and globally), keep the issued state small (an opaque random value plus expiry), and apply short TTLs (≤5 minutes is RECOMMENDED) so that orphaned state is reclaimed quickly. Implementations SHOULD also apply rate limiting to <tt>bootstrap_endpoint</tt> and <tt>refresh_endpoint</tt> even though those are signed, since signature verification is itself non-trivial work.</t>
</section>
</section>

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

<section anchor="directed-user-identifiers"><name>Directed User Identifiers</name>
<t>The <tt>bootstrap_token.sub</tt> claim MUST be a pairwise user identifier directed at the agent server. This prevents cross-vendor correlation of users across different agent servers.</t>
</section>

<section anchor="mobile-enclave-identity"><name>Mobile Enclave Identity</name>
<t>The PS sees only the ephemeral <tt>hwk</tt> key. The agent server sees the <tt>jkt-jwt</tt> carrying the enclave identity. The PS therefore cannot track a device across bootstraps at different agent servers.</t>
</section>

<section anchor="out-of-band-consent-channel"><name>Out-of-Band Consent Channel</name>
<t>The user communication channel held by the PS after first bootstrap is privacy-sensitive. PS implementations SHOULD document their user communication practices.</t>
</section>

<section anchor="agent-identifier-registry-at-the-ps"><name>Agent Identifier Registry at the PS</name>
<t>After bootstrap completion <xref target="bootstrap-completion"/>, the PS knows the <tt>aauth:local@domain</tt> identifier of each agent the user has bootstrapped. This supports user-facing features such as a dashboard of connected agents and targeted revocation ("disconnect from agent X"). PS implementations SHOULD make this list visible to the user.</t>
</section>

<section anchor="announcement-metadata-exposure"><name>Announcement Metadata Exposure</name>
<t>The bootstrap completion announcement <xref target="bootstrap-completion"/> is sent over TLS, but its fact-of-existence and timing reveal the (PS, agent_server, agent_id) triple to any on-path observer that can correlate the connection's destination with the <tt>agent_token</tt> carried in <tt>Signature-Key</tt>. Operators of either side SHOULD treat the announcement timing with the same care as any other consent-related event, and SHOULD avoid logging the <tt>agent_token</tt> value in shared infrastructure.</t>
</section>
</section>

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

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

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

<ul spacing="compact">
<li>Type name: application</li>
<li>Subtype name: aa-bootstrap+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="bootstrap-token-issuance"/></li>
<li>Applications that use this media type: AAuth Person Servers and agent servers</li>
<li>Fragment identifier considerations: N/A</li>
</ul>
</section>
</section>

<section anchor="jwt-type-registrations"><name>JWT Type Registrations</name>
<t>This specification registers the following JWT <tt>typ</tt> header parameter value 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-bootstrap+jwt</tt></td>
<td>This document, <xref target="bootstrap-token-issuance"/></td>
</tr>
</tbody>
</table></section>

<section anchor="jwt-claims"><name>JWT Claims</name>
<t>This document defines no new JWT claims. The <tt>bootstrap_token</tt> uses claims registered in the IANA "JSON Web Token Claims" registry (<xref target="RFC7519"/>, <xref target="RFC7800"/>) together with <tt>dwk</tt>, which is registered by <xref target="I-D.hardt-aauth-protocol"/>.</t>
</section>

<section anchor="aauth-agent-server-metadata-registration"><name>AAuth Agent Server Metadata Registration</name>
<t>This document registers the following parameters in the AAuth Agent Server Metadata registry established by <xref target="I-D.hardt-aauth-protocol"/>:</t>
<table>
<thead>
<tr>
<th>Parameter</th>
<th>Description</th>
<th>Reference</th>
</tr>
</thead>

<tbody>
<tr>
<td><tt>bootstrap_endpoint</tt></td>
<td>URL of the agent server's bootstrap endpoint</td>
<td>This document, <xref target="agent-server-metadata"/></td>
</tr>

<tr>
<td><tt>refresh_endpoint</tt></td>
<td>URL of the agent server's refresh endpoint</td>
<td>This document, <xref target="agent-server-metadata"/></td>
</tr>

<tr>
<td><tt>webauthn_endpoint</tt></td>
<td>URL of the agent server's WebAuthn challenge endpoint</td>
<td>This document, <xref target="webauthn-endpoint"/></td>
</tr>
</tbody>
</table></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>There are currently no known implementations.</t>
</section>

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

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

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

<section anchor="acknowledgments"><name>Acknowledgments</name>
<t>TBD.</t>
</section>

</middle>

<back>
<references><name>References</name>
<references><name>Normative References</name>
<reference anchor="I-D.hardt-aauth-protocol" target="https://github.com/dickhardt/AAuth">
  <front>
    <title>AAuth Protocol</title>
    <author fullname="Dick Hardt" initials="D." surname="Hardt">
      <organization>Hellō</organization>
    </author>
    <date year="2026"/>
  </front>
</reference>
<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>
    <date year="2026"/>
  </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>
<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.7515.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.7519.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.7800.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9421.xml"/>
</references>
<references><name>Informative References</name>
<reference anchor="AppAttest" target="https://developer.apple.com/documentation/devicecheck/establishing-your-app-s-integrity">
  <front>
    <title>Establishing your app's integrity (App Attest)</title>
    <author>
      <organization>Apple</organization>
    </author>
    <date year="2024"/>
  </front>
</reference>
<reference anchor="PlayIntegrity" target="https://developer.android.com/google/play/integrity/overview">
  <front>
    <title>Play Integrity API</title>
    <author>
      <organization>Google</organization>
    </author>
    <date year="2024"/>
  </front>
</reference>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.7942.xml"/>
<reference anchor="WebAuthn" target="https://www.w3.org/TR/webauthn-3/">
  <front>
    <title>Web Authentication: An API for accessing Public Key Credentials - Level 3</title>
    <author>
      <organization>W3C</organization>
    </author>
    <date year="2024"/>
  </front>
</reference>
</references>
</references>

</back>

</rfc>
