<?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-02" submissionType="IETF" category="info" xml:lang="en" indexInclude="true">

<front>
<title abbrev="AAuth-Bootstrap">AAuth Bootstrap Guidance</title><seriesInfo value="draft-hardt-aauth-bootstrap-02" stream="IETF" status="informational" 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 provides informational guidance for agent providers (APs) on enrolling agents and issuing AAuth agent tokens defined in <xref target="I-D.hardt-oauth-aauth-protocol"/>. It covers per-platform key handling, optional platform attestation, agent identifier strategies, and refresh patterns. The mechanisms described here are not normative protocol — they are common patterns that interoperable AP implementations can adopt or adapt.</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>The AAuth Protocol <xref target="I-D.hardt-oauth-aauth-protocol"/> establishes that every agent has its own cryptographic identity — an agent identifier of the form <tt>aauth:local@domain</tt>, bound to a signing key, and attested by an agent token issued by an agent provider (AP). The protocol defines the agent token format and where the agent presents it: always to its person server (PS), which passes it to an access server (AS) when it federates, and to a resource only in agent identity and resource-managed access (<xref target="I-D.hardt-oauth-aauth-protocol"/>, Keying Material). It does not specify how an agent comes to hold an agent token in the first place. That step is <strong>bootstrap</strong>, and it is the subject of this document.</t>

<section anchor="what-bootstrapping-is"><name>What Bootstrapping Is</name>
<t>Bootstrapping is the AP-side ceremony by which an instance of an agent acquires an agent token. The agent generates a signing key on the device or in the browser where it will run, presents whatever evidence the AP requires (a signed-in account, an attested device, a published JWKS, etc.), and receives an agent token whose <tt>cnf.jwk</tt> is bound to that key and whose <tt>sub</tt> is an <tt>aauth:local@domain</tt> identifier the AP has chosen.</t>
<t>After bootstrap the agent can participate in AAuth: it can sign HTTP messages per <xref target="I-D.hardt-httpbis-signature-key"/>, identify itself at resources, and present its agent token to a PS so the user can bind the agent to themselves on first interaction.</t>
</section>

<section anchor="what-bootstrapping-is-not"><name>What Bootstrapping Is Not</name>

<ul spacing="compact">
<li><strong>Not normative protocol.</strong> This document is informational. The AAuth Protocol does not mandate a specific bootstrap ceremony, and conformance does not depend on the patterns described here. APs are free to use other approaches that produce a valid agent token.</li>
<li><strong>Not the user-to-agent binding.</strong> Binding an agent to a person is performed by the PS, lazily, on the agent's first interaction with the PS (<xref target="I-D.hardt-oauth-aauth-protocol"/>, Agent-Person Binding). Bootstrap produces an agent identity; the PS attaches that identity to a user.</li>
<li><strong>Not authorization.</strong> Bootstrap conveys no scope, no resource permission, and no user identity claims. Those are obtained through the flows defined in the AAuth Protocol after bootstrap.</li>
<li><strong>Not one-size-fits-all.</strong> Web, mobile, and self-hosted agents have different threat models and different platform primitives available to them. This document offers patterns appropriate to each, not a single prescribed ceremony.</li>
</ul>
</section>

<section anchor="patterns-covered"><name>Patterns Covered</name>

<ul spacing="compact">
<li><strong>Per-platform key handling</strong> — where the agent's signing key lives and how strongly it is protected on web, mobile, and self-hosted deployments; desktop and workload coverage is TBD <xref target="per-platform-keys"/>.</li>
<li><strong>Optional platform attestation</strong> — when and why to require WebAuthn, App Attest, or Play Integrity <xref target="optional-attestation"/>.</li>
<li><strong>Agent identifier strategies</strong> — how to construct the <tt>sub</tt> claim's local part <xref target="identifier-strategies"/>.</li>
<li><strong>Refresh patterns</strong> — issuing fresh agent tokens for renewal <xref target="refresh-patterns"/>.</li>
<li><strong>Many agents, one operator</strong> — the key layout and custody for a self-hosted domain running several agents <xref target="many-agents-one-operator"/>.</li>
<li><strong>Sub-agent tokens</strong> — how a sub-agent comes to hold its token, self-hosted and under a hosted AP <xref target="sub-agent-tokens"/>.</li>
</ul>
<t>Throughout, when this document refers to "the durable key" and "the ephemeral key" it means the keys defined in <xref target="per-platform-keys"/>. The ephemeral key's public part appears in <tt>agent_token.cnf.jwk</tt> and signs HTTP messages from the agent per <xref target="I-D.hardt-httpbis-signature-key"/>; the durable key serves as the AP's stable enrollment anchor and signs only at refresh. APs that use a single durable key for all signatures <xref target="per-platform-keys"/> can read references to "the ephemeral key" as referring to that same durable key.</t>
</section>
</section>

<section anchor="conventions-and-definitions"><name>Conventions and Definitions</name>
<t>This document is informational guidance and uses no BCP 14 keywords. Normative requirements relevant to bootstrap are defined in <xref target="I-D.hardt-oauth-aauth-protocol"/>; this document references them where helpful but uses lowercase "should" / "must" in its own descriptive prose.</t>
</section>

<section anchor="terminology"><name>Terminology</name>
<t>Terms defined in <xref target="I-D.hardt-oauth-aauth-protocol"/> are used here with the same meaning. In particular:</t>

<ul spacing="compact">
<li><strong>Agent Provider (AP)</strong> — issues agent tokens.</li>
<li><strong>Agent token</strong> — JWT signed by the AP, binding the agent identifier in <tt>sub</tt> to the key in <tt>cnf.jwk</tt>. It carries <tt>ps</tt> when the agent has a person server (<xref target="I-D.hardt-oauth-aauth-protocol"/>, Agent Token Structure).</li>
<li><strong>Person Server (PS)</strong> — represents the person; binds agents to a person on first interaction.</li>
</ul>
<t>This document additionally uses:</t>

<ul spacing="compact">
<li><strong>Durable key</strong> — a signing key whose lifetime is intended to span the agent install (typically the lifetime of an install or browser-storage entry). The durable key is the AP's stable enrollment anchor; it is presented only to the AP at refresh and is not used to sign requests to PSes, resources, or ASes.</li>
<li><strong>Ephemeral key</strong> — a signing key generated fresh per agent-token issuance. Its public part appears in <tt>agent_token.cnf.jwk</tt>. The agent uses it to sign HTTP messages for the agent token's lifetime, then discards it on the next refresh.</li>
<li><strong>Platform attestation</strong> — a mechanism by which the runtime platform attests to properties of the agent or its key (WebAuthn, Apple App Attest, Google Play Integrity, etc.).</li>
</ul>
</section>

<section anchor="per-platform-keys"><name>Per-Platform Key Handling</name>
<t>On web, mobile, and desktop, APs should use a two-key pattern: a <strong>durable key</strong> that serves as the AP's stable enrollment anchor and is presented only to the AP at refresh, plus an <strong>ephemeral key</strong> generated fresh per agent-token issuance whose public part appears in <tt>agent_token.cnf.jwk</tt> and which signs HTTP messages for the agent token's lifetime. Refresh chains the new ephemeral key to the durable key via the <tt>jkt-jwt</tt> scheme <xref target="I-D.hardt-httpbis-signature-key"/>; see <xref target="refresh-patterns"/>.</t>
<t>This pattern bounds the blast radius of an ephemeral-key leak to one agent token's lifetime, narrows the durable key's attack surface to the AP refresh path (it never signs requests to PSes, resources, or ASes), and accommodates hardware-backed durable keys on platforms that have them today and on platforms that may expose them in the future without protocol change. APs may use a single durable key for all signatures where simplicity outweighs these properties — receivers cannot distinguish the two patterns, since they only verify <tt>cnf.jwk</tt> against the HTTP signature.</t>
<t>A self-hosted deployment running one agent uses a single key — the JWKS-published key serves as both the AP signing key and the agent's signing key, since there is no separate AP to refresh against <xref target="self-hosted-agents"/>. One running several agents keeps the JWKS-published key as the AP key and gives each agent a key of its own <xref target="many-agents-one-operator"/>.</t>

<section anchor="web-apps"><name>Web Apps</name>
<t>The durable key is a non-extractable <xref target="WebCryptoAPI"/> key generated with <tt>extractable: false</tt> and stored in IndexedDB scoped to the AP's origin. The ephemeral key is also a non-extractable WebCrypto key, regenerated on each refresh and discarded when the next refresh produces its replacement.</t>
<t>Properties of the durable key:</t>

<ul spacing="compact">
<li>The private key cannot be read or exported by JavaScript, including by code injected via XSS or malicious browser extensions. JS can only ask the browser to sign with it.</li>
<li>The key is bound to the origin's IndexedDB storage. Clearing site data destroys it; a new enrollment is required.</li>
<li>No user-verification gesture is required to sign — operations are fast enough for routine signature use.</li>
</ul>
<t>Both keys are software-protected (browser sandbox) rather than hardware-protected. The two-key pattern still applies: the durable key signs only the periodic refresh (once per agent-token lifetime), while the ephemeral key signs every HTTP request and rotates on each refresh. APs that want the additional assurance of a WebAuthn ceremony at enrollment time can layer one on top; see <xref target="optional-attestation"/>. If browsers later expose hardware-backed credentials suitable for use as the durable key, the pattern accommodates them with no protocol change.</t>
</section>

<section anchor="mobile-ios-and-android"><name>Mobile (iOS and Android)</name>
<t>The durable key is generated and stored in the platform's hardware-backed keystore: the Secure Enclave on iOS or StrongBox on Android (or the Android Keystore where StrongBox is unavailable). The ephemeral key is a software key in app memory, regenerated on each refresh.</t>
<t>Properties of the durable key:</t>

<ul spacing="compact">
<li>The private key cannot be exported from the keystore. Cryptographic operations are performed by the keystore on the application's behalf.</li>
<li>The key is bound to the application install. Reinstalling the app generates a new key and requires re-enrollment.</li>
<li>The keystore can additionally enforce user authentication (biometric or device passcode) before sign operations, if the AP wants user-presence on each refresh.</li>
</ul>
<t>The AP typically also requires an attestation ceremony at enrollment to confirm the durable key is real keystore-bound material rather than software-generated. See <xref target="optional-attestation"/>. Because the durable key signs only at refresh, any per-op cost (keystore round-trip, optional user verification) is incurred at most once per agent-token lifetime, not on every request.</t>
</section>

<section anchor="self-hosted-agents"><name>Self-Hosted Agents</name>
<t>A self-hosted agent runs under a domain the user controls. The agent publishes its AP metadata document at <tt>/.well-known/aauth-agent.json</tt> per <xref target="I-D.hardt-oauth-aauth-protocol"/>; the JWKS itself is hosted at any HTTPS URL referenced by the metadata's <tt>jwks_uri</tt>. The corresponding private key should be hardware-bound where the platform supports it: macOS Keychain (Secure Enclave on supported hardware), Windows TPM, or Linux Secret Service.</t>
<t>Self-hosted agents act as their own AP — they self-issue agent tokens signed by the JWKS-published key. There is no separate AP to refresh against, so the two-key pattern does not apply: the JWKS-published key serves both as the AP signing key (signing self-issued agent tokens) and as the key whose public part appears in <tt>agent_token.cnf.jwk</tt> (signing HTTP messages). Because the trust anchor is a key the user controls and publishes, no platform attestation step exists. Other parties verify the agent token signature against the published JWKS, exactly as they would for any other AP.</t>
<t>A resource that calls downstream resources for its callers is self-hosted in this sense. The protocol requires it to be its own AP: it publishes <tt>/.well-known/aauth-agent.json</tt> on its own origin with the same <tt>issuer</tt> as its resource metadata, and signs downstream requests with an agent token it issued to itself (<xref target="I-D.hardt-oauth-aauth-protocol"/>, Intermediary Agent Identity). That agent token acts for every person whose requests the resource serves, so it is not bound to one person.</t>

<section anchor="many-agents-one-operator"><name>Many Agents, One Operator</name>
<t>The single-key description above assumes one agent per domain. The common self-hosted shape is one operator running several distinct agents under one domain — a planner, a researcher, one agent per lane of work — each with its own identity and its own signing key. The layout is:</t>

<ul spacing="compact">
<li><strong>One AP key, published.</strong> The JWKS at <tt>jwks_uri</tt> holds the AP signing key, and only it. It signs every agent token the domain issues.</li>
<li><strong>One agent token per agent, each with its own <tt>sub</tt> and its own <tt>cnf</tt> key.</strong> The AP self-issues a token for each agent, naming it <tt>aauth:planner@ops.example</tt>, <tt>aauth:research@ops.example</tt>, and so on, and binding each to a key generated where that agent runs.</li>
<li><strong>Agent keys are not published.</strong> An agent's key appears in exactly one place: the <tt>cnf.jwk</tt> of its agent token. Nothing about an agent key goes in the JWKS, and no party ever fetches an agent key; verifiers take it from the token, as they do for every AP. A reading of "each agent holds a signing key published at a well-known URL" is the one-agent case misapplied.</li>
</ul>
<t>This is the two-key pattern of <xref target="per-platform-keys"/> in another shape. The AP key is the domain's durable key: it signs agent tokens and the revocations the AP sends to a PS under the <tt>jwks_uri</tt> scheme (<xref target="I-D.hardt-oauth-aauth-protocol"/>, Keying Material), never an agent's request. Each agent key is that agent's ephemeral key, and it can be rotated as often as the operator likes, because rotating it costs one self-issued token.</t>
<t>Custody follows the blast radius. The AP key belongs in hardware (Secure Enclave, TPM, StrongBox) or a keystore that only the token-issuing process can reach; compromise of it mints identities for the whole domain. An agent key belongs with the agent — in a per-lane signing proxy that signs on the agent process's behalf, in a per-process keystore, or in the process's own memory when the token is short-lived — and compromise of it is contained to that one <tt>sub</tt> for the token's lifetime. An operator that keeps every agent key in one place has collapsed the layout back to a single key and should treat that place as it would treat the AP key.</t>
</section>
</section>

<section anchor="desktop-apps"><name>Desktop Apps</name>
<t>TBD. Future revisions will cover key handling for native desktop applications, where the durable key would live in a hardware-backed store (macOS Keychain with Secure Enclave on supported hardware, Windows TPM via CNG, Linux Secret Service / TPM2) and the ephemeral key in process memory, following the same pattern as mobile.</t>
</section>

<section anchor="workload"><name>Workload</name>
<t>TBD. Future revisions will cover headless workload identity (e.g., SPIFFE/SPIRE SVIDs, WIMSE workload identity, cloud-platform IMDS attestation), where the trust anchor is platform attestation rather than user interaction.</t>
</section>
</section>

<section anchor="optional-attestation"><name>Optional Platform Attestation</name>
<t>Platform attestation gives the AP cryptographic evidence about the runtime context in which the durable key was generated. It is optional — the AAuth Protocol does not require it. APs choose whether to require attestation based on their threat model.</t>
<t>Common reasons an AP might require attestation:</t>

<ul spacing="compact">
<li><strong>Anti-fraud at enrollment.</strong> Distinguishing real user devices from server-side automation.</li>
<li><strong>Hardware-binding evidence.</strong> Confirming the durable key is in a Secure Enclave or StrongBox rather than software.</li>
<li><strong>App-integrity evidence.</strong> Confirming the agent is the AP's published app, not a modified or repackaged binary.</li>
<li><strong>User-presence evidence.</strong> Confirming a real user gesture authorized this enrollment.</li>
</ul>
<t>Common reasons an AP might not require attestation:</t>

<ul spacing="compact">
<li>The AP serves a trust posture where AP-side fraud detection is not signal-driven (e.g., paid accounts, invitation-only enrollment).</li>
<li>The AP wants the broadest possible reach, including environments where attestation is unavailable.</li>
<li>The deployment is self-hosted, where the user is the trust anchor.</li>
</ul>

<section anchor="webauthn-web-apps"><name>WebAuthn (Web Apps)</name>
<t><xref target="WebAuthn"/> provides user-verification and a hardware-rooted credential on web. APs that require it perform a registration ceremony at enrollment and an assertion ceremony at later sensitive operations. The credential is stored in the user's authenticator (TPM, Secure Enclave, security key, or platform syncing fabric).</t>
<t>Tradeoffs:</t>

<ul spacing="compact">
<li>Provides user-verification (touch, face, PIN) the WebCrypto-only approach lacks.</li>
<li>Provides hardware-protected key material that cannot be lifted even by a fully compromised browser process.</li>
<li>UX failure modes exist around cross-Chrome-profile use, embedded webviews, syncing-fabric inconsistencies, and cross-device QR ceremonies. APs that adopt WebAuthn should plan for fallback paths when these fail.</li>
</ul>
</section>

<section anchor="app-attest-ios"><name>App Attest (iOS)</name>
<t><xref target="AppAttest"/> attests to two things: that the durable key was generated in the device's Secure Enclave, and that the request originates from the AP's published app on a genuine Apple device. The AP receives an attestation object at enrollment which it verifies against Apple's attestation root, then accepts subsequent assertions signed by the same enclave key.</t>
</section>

<section anchor="play-integrity-android"><name>Play Integrity (Android)</name>
<t><xref target="PlayIntegrity"/> provides a device, app, and account integrity verdict signed by Google. The AP nominates a nonce, the agent invokes the Play Integrity API, and the AP verifies the resulting integrity token. Subsequent assertions can be signed by an Android Keystore (or StrongBox) key that the AP enrolled at the same time.</t>
</section>

<section anchor="when-to-require-attestation"><name>When to Require Attestation</name>
<t>A practical rule of thumb:</t>

<ul spacing="compact">
<li><strong>Consumer-grade APs</strong>: optional. Most consumer agent providers do not require attestation; the protocol-level binding at the PS is the security gate.</li>
<li><strong>Regulated or high-assurance APs</strong>: usually required. Financial services, healthcare, enterprise SSO providers benefit from attestation as part of their AML/KYC or device-management posture.</li>
<li><strong>Multi-tenant APs</strong>: often required at higher tiers. An AP may issue weak agent tokens to free tier users and require attestation for paid or enterprise tier users, surfacing the difference to receivers via AP-defined claims in the agent token.</li>
</ul>
</section>
</section>

<section anchor="identifier-strategies"><name>Agent Identifier Strategies</name>
<t>The agent token's <tt>sub</tt> is an <tt>aauth:local@domain</tt> identifier. The <tt>domain</tt> part is the AP. The <tt>local</tt> part identifies the agent install at the AP — it must be stable for the lifetime of the install so PSes and other parties can recognize a returning agent.</t>
<t>APs are free to choose any opaque scheme for the local part: a random string assigned at enrollment, a deterministic derivation from the durable key's thumbprint, a sequential identifier, or a human-readable handle. When deriving from a thumbprint, use the durable key's thumbprint — the ephemeral key rotates on each refresh and is not a stable identifier. Whatever the scheme, the local part must meet the protocol's character and length rules, which allow uppercase letters and reserve <tt>+</tt> for sub-agents (<xref target="I-D.hardt-oauth-aauth-protocol"/>, Agent Identifiers); a base64url-encoded thumbprint meets them as it stands. Receivers treat the identifier as opaque and compare it exactly, without case-folding.</t>

<section anchor="per-install-identity"><name>Per-Install Identity</name>
<t>Each install's durable key is the basis for one agent identity. A returning user on a new device is a new agent. The AP may know which of its accounts enrolled an install, but it issues each install its own identifier and does not join installs into one agent.</t>
<t>Multi-device users will see multiple agent entries in their PS dashboard. Grouping or merging those entries belongs at the PS, which already authenticates the user and is the correct layer for cross-device correlation. Rotation of the durable key produces a new agent identity; rotation of the ephemeral key (on every refresh) does not — the agent's <tt>sub</tt> is stable across ephemeral rotations. PS-side regrouping is the recovery path for durable-key changes.</t>
</section>
</section>

<section anchor="example-agent-token-claims"><name>Example Agent Token Claims</name>
<t>A typical agent token issued by an AP, illustrating the claims described in <xref target="I-D.hardt-oauth-aauth-protocol"/> and the identifier strategies in <xref target="identifier-strategies"/>.</t>
<t>JWT header:</t>

<sourcecode type="json"><![CDATA[{ "alg": "Ed25519", "typ": "aa-agent+jwt", "kid": "..." }
]]>
</sourcecode>
<t>JWT payload:</t>

<sourcecode type="json"><![CDATA[{
  "iss":  "https://ap.example",
  "dwk":  "aauth-agent.json",
  "sub":  "aauth:k7q3p9n2@ap.example",
  "ps":   "https://ps.example",
  "cnf":  { "jwk": { "kty": "OKP", "crv": "Ed25519",
                     "x": "...", "alg": "Ed25519" } },
  "iat":  1746316800,
  "exp":  1746320400,
  "jti":  "..."
}
]]>
</sourcecode>
</section>

<section anchor="refresh-patterns"><name>Refresh Patterns</name>
<t>Agent token lifetime is the AP's policy re-evaluation cadence — every refresh is the AP's chance to re-check device posture, attestation freshness, and account status before issuing a new token. A typical lifetime is <strong>1 hour</strong>, matching common practice for proof-of-possession-bound access tokens. APs may use shorter lifetimes (e.g., 15–30 minutes) for higher-assurance deployments where attestation must be refreshed often, or longer lifetimes up to the 24 hours the AAuth Protocol recommends as the maximum, for low-policy-churn deployments where refresh chattiness is undesirable.</t>
<t>The agent refreshes its agent token when fewer than five minutes remain, then the person tokens and auth tokens it obtained with it, which expire no later than the agent token (<xref target="I-D.hardt-oauth-aauth-protocol"/>, Expiry and the Refresh Margin). A lifetime has to leave room for that margin: an agent token of five minutes or less is inside it when issued.</t>

<section anchor="two-key-refresh"><name>Two-Key Refresh</name>
<t>On web, mobile, and desktop, refresh chains the new ephemeral key to the durable key via the <tt>jkt-jwt</tt> scheme <xref target="I-D.hardt-httpbis-signature-key"/>:</t>

<ol spacing="compact">
<li>The agent generates a fresh ephemeral key pair.</li>
<li>The agent constructs a JWT signed by the <strong>durable key</strong>, naming the new ephemeral public key. This is the "naming JWT" carried in the <tt>Signature-Key</tt> header under the <tt>jkt-jwt</tt> scheme.</li>
<li>The agent signs the refresh request with the <strong>ephemeral key</strong> under <xref target="RFC9421"/> HTTP Message Signatures.</li>
<li>The AP verifies the durable-key signature on the naming JWT, looks up the enrollment by the durable key's thumbprint, verifies the HTTP signature against the ephemeral public key, applies its policy (device posture, attestation freshness, account status), and returns a new agent token whose <tt>cnf.jwk</tt> is the ephemeral public key.</li>
<li>The agent uses the new agent token and ephemeral key for the agent token's lifetime, then discards the ephemeral key on the next refresh.</li>
</ol>
<t>Example refresh request:</t>

<sourcecode type="http"><![CDATA[POST /refresh HTTP/1.1
Host: ap.example
Content-Type: application/json
Content-Digest: sha-256=:...:
Signature-Input: sig=("@method" "@authority" "@path"
    "content-type" "content-digest"
    "signature-key");created=1746316800
Signature: sig=:...ephemeral-key signature bytes...:
Signature-Key: sig=jkt-jwt;jwt="eyJhbGc..."

{}
]]>
</sourcecode>
<t>A request with a body to an AP endpoint — enrollment, refresh, or sub-agent issuance — should cover <tt>content-digest</tt> and <tt>content-type</tt>, as the protocol requires of a request with a body to a PS or AS (<xref target="I-D.hardt-oauth-aauth-protocol"/>, Covered Components). A refresh body can carry attestation evidence, and an enrollment or sub-agent request carries a public key the AP binds into a token.</t>
<t>The <tt>jwt</tt> parameter value is a JWT signed by the durable key, carrying the ephemeral public key in <tt>cnf.jwk</tt> along with the other claims the <tt>jkt-jwt</tt> scheme requires <xref target="I-D.hardt-httpbis-signature-key"/>, and a <tt>jti</tt> for replay protection. The HTTP signature is produced by the ephemeral key. The AP correlates the two keys via the naming JWT's payload.</t>
</section>

<section anchor="single-key-refresh"><name>Single-Key Refresh</name>
<t>APs that opt for the single-durable pattern <xref target="per-platform-keys"/> sign the refresh request directly with the durable key under the <tt>hwk</tt> scheme <xref target="I-D.hardt-httpbis-signature-key"/>. The AP verifies the signature, looks up the enrollment by the key's thumbprint, and issues a fresh agent token with a new <tt>exp</tt>. The same <tt>cnf.jwk</tt> is carried through; the agent's key is unchanged.</t>
</section>

<section anchor="mobile-refresh-specifics"><name>Mobile Refresh Specifics</name>
<t>APs that required platform attestation at enrollment typically do not re-attest on every refresh — the durable-key signature on the naming JWT (or the durable-key HTTP signature in the single-key pattern) is sufficient proof that the same enclave-resident key is making the request. APs that want periodic re-attestation can require a fresh App Attest assertion or Play Integrity verdict on a schedule (e.g., every 30 days) by including a server nonce in the refresh challenge.</t>
</section>

<section anchor="self-hosted-refresh"><name>Self-Hosted Refresh</name>
<t>Self-hosted agents self-issue agent tokens. There is no separate refresh ceremony — the agent generates a new agent token signed by its JWKS-published key whenever needed. The two-key pattern does not apply <xref target="self-hosted-agents"/>. Where one operator runs several agents <xref target="many-agents-one-operator"/>, the token-issuing process re-issues each agent's token against that agent's current key; an agent key can change at every issuance without any ceremony, because the AP key that signs the token is the only key anyone verifies against a published document.</t>
</section>

<section anchor="key-rotation-vs-token-refresh"><name>Key Rotation vs Token Refresh</name>
<t>Refresh issues a new agent token bound to a fresh ephemeral key (or, in the single-key pattern, to the same durable key). The agent replaces its person tokens and auth tokens after every refresh in either pattern <xref target="refresh-patterns"/>, so the new ephemeral key adds no work. <strong>Durable key rotation</strong> generates a new durable key and is a separate, rare event. Under the per-install identity model <xref target="per-install-identity"/>, a new durable key is a new agent — the PS treats it as new on first interaction, and any cross-device or cross-rotation continuity is handled at the PS by the user.</t>
</section>
</section>

<section anchor="sub-agent-tokens"><name>Sub-Agent Tokens</name>
<t>The AAuth Protocol represents a sub-agent as an agent whose token carries a <tt>parent_agent</tt> claim naming its parent, with a <tt>local</tt> part formed from the parent's followed by <tt>+</tt> and a discriminator, its own <tt>cnf</tt> key, the same <tt>iss</tt> as its parent's token, and no sub-agents of its own; a PS rejects token requests signed by a sub-agent, and the parent obtains person tokens and auth tokens on its behalf (<xref target="I-D.hardt-oauth-aauth-protocol"/>, Sub-Agents). The protocol leaves how a sub-agent comes to hold that token to this document. Two cases.</t>

<section anchor="self-hosted-sub-agents"><name>Self-Hosted Sub-Agents</name>
<t>The operator's self-hosted AP is the parent's AP, and it issues the sub-agent token exactly as it issues the parent's: the sub-agent generates a key where it runs, and the token-issuing process self-issues a token signed by the JWKS-published key. The only differences are in the claims.</t>

<sourcecode type="json"><![CDATA[{
  "iss":  "https://ops.example",
  "dwk":  "aauth-agent.json",
  "sub":  "aauth:planner+search1@ops.example",
  "parent_agent": "aauth:planner@ops.example",
  "ps":   "https://ps.example",
  "cnf":  { "jwk": { "kty": "OKP", "crv": "Ed25519",
                     "x": "...", "alg": "Ed25519" } },
  "iat":  1746316800,
  "exp":  1746320400,
  "jti":  "..."
}
]]>
</sourcecode>

<ul spacing="compact">
<li><tt>sub</tt> is the parent's <tt>local</tt> part, <tt>+</tt>, and a discriminator the operator chooses — a lane name, a spawn counter, a short random string. It must be unique among that parent's sub-agents for as long as any party might hold a token naming it.</li>
<li><tt>parent_agent</tt> is the parent's identifier. The protocol requires that it name a top-level agent; the issuing process checks that the parent's own token carries no <tt>parent_agent</tt>.</li>
<li><tt>ps</tt> is copied from the parent's token. A sub-agent's person is its parent's person.</li>
<li><tt>exp</tt> should not exceed the parent's current agent token <tt>exp</tt>. A sub-agent that outlives its parent's token has nothing to be a sub-agent of; issue for the task's expected duration, and re-issue through the same process if the task runs longer.</li>
</ul>
<t>The parent plays no protocol role in issuance here. The operator spawns the sub-agent, and the process that holds the AP key mints its token. What the parent does afterwards — obtain a person token for the sub-agent with <tt>subagent_token</tt>, pass it to the sub-agent, and later exchange the resource token the sub-agent receives for an auth token — is defined by the protocol (<xref target="I-D.hardt-oauth-aauth-protocol"/>, Parent-Mediated Authorization).</t>
</section>

<section anchor="sub-agents-under-a-hosted-ap"><name>Sub-Agents Under a Hosted AP</name>
<t>When the parent's tokens come from an AP the operator does not run, the parent requests the sub-agent's token from that AP; no other AP can issue it, since a PS rejects a sub-agent token whose <tt>iss</tt> differs from its parent's (<xref target="I-D.hardt-oauth-aauth-protocol"/>, Sub-Agent Identity). This document defines no endpoint for it; the shape below is what any AP offering sub-agent issuance needs to cover, and an AP publishes how it does so in its own documentation.</t>

<ol spacing="compact">
<li>The sub-agent generates its key pair where it will run, and gives its public key to the parent. Where the parent spawns the sub-agent in a runtime it controls, it may generate the pair on the sub-agent's behalf and hand over the private key at spawn; the point is that the private key ends up with the sub-agent and nowhere else.</li>
<li>The parent sends a signed request to the AP, signing with its own ephemeral key and presenting its own agent token under the <tt>jwt</tt> scheme (<xref target="I-D.hardt-httpbis-signature-key"/>). The body carries the sub-agent's public key and, if the parent wants to name it, a discriminator. The signature covers <tt>content-digest</tt> and <tt>content-type</tt>, as recommended in <xref target="two-key-refresh"/>, which binds that key to the parent's signature.</li>
<li>The AP verifies the parent's signature and token (<xref target="I-D.hardt-oauth-aauth-protocol"/>, Agent Token Verification), checks that the token carries no <tt>parent_agent</tt> (a sub-agent may not have sub-agents), and applies its policy: how many sub-agents this parent may have live, what lifetime they get, whether this parent may spawn at all.</li>
<li>The AP issues the sub-agent token: <tt>iss</tt> the same as in the parent's token, <tt>sub</tt> formed from the parent's <tt>local</tt> part and the discriminator (its own if the parent offered none, or if the parent's collides), <tt>parent_agent</tt> naming the parent, <tt>ps</tt> copied from the parent's token, <tt>cnf.jwk</tt> the sub-agent's public key, <tt>exp</tt> no later than the parent's token. The AP returns it to the parent, which passes it to the sub-agent.</li>
</ol>
<t>Example sub-agent request, with the parent's agent token in <tt>Signature-Key</tt> and the sub-agent's public key in the body. The path and member names are illustrative; each AP defines its own.</t>

<sourcecode type="http"><![CDATA[POST /subagent HTTP/1.1
Host: ap.example
Content-Type: application/json
Content-Digest: sha-256=:...:
Signature-Input: sig=("@method" "@authority" "@path"
    "content-type" "content-digest"
    "signature-key");created=1746316800
Signature: sig=:...parent ephemeral-key signature bytes...:
Signature-Key: sig=jwt;jwt="eyJhbGc..."

{
  "jwk": { "kty": "OKP", "crv": "Ed25519",
           "x": "...", "alg": "Ed25519" },
  "discriminator": "search1"
}
]]>
</sourcecode>
<t>The parent's durable key is not involved. Sub-agent issuance is a request the parent makes with its current ephemeral key and token, the same credentials it uses for every other signed request, and it needs nothing from the enrollment ceremony. Sub-agent tokens are not refreshed by the sub-agent: a sub-agent has no enrollment with the AP and no durable key. A sub-agent that needs a fresh token gets one through the parent, by the same request.</t>
<t>Two things the AP should record. Which parent requested each sub-agent token, since the <tt>parent_agent</tt> claim is the AP's assertion and a PS relies on it. And how many sub-agent tokens are live per parent, because a parent that spawns without bound is either misbehaving or compromised, and the AP is the only party positioned to notice before the PS does.</t>
</section>
</section>

<section anchor="per-platform-enrollment-sketches"><name>Per-Platform Enrollment Sketches</name>
<t>This section sketches a typical end-to-end enrollment for each platform. The sketches are illustrative; APs are free to vary them.</t>

<section anchor="web-app-enrollment"><name>Web App Enrollment</name>

<ol spacing="compact">
<li>User logs into the AP through the AP's normal login.</li>
<li>Agent (running in the AP's web origin) generates a non-extractable WebCrypto Ed25519 <strong>durable</strong> key and stores its handle in IndexedDB.</li>
<li>Agent posts the durable public key to an AP-internal enrollment endpoint, signed by the new key (<tt>hwk</tt> scheme).</li>
<li>AP optionally performs a WebAuthn registration and verifies it.</li>
<li>AP records <tt>(ap_user, durable_jkt)</tt> and is now ready to issue agent tokens.</li>
<li>When the agent needs an agent token, it generates a fresh <strong>ephemeral</strong> WebCrypto key and calls an AP-internal token-issuance endpoint, signed via <tt>jkt-jwt</tt> chaining the durable key to the ephemeral key <xref target="refresh-patterns"/>. The AP returns an agent token with <tt>sub</tt> derived per the AP's identifier strategy <xref target="identifier-strategies"/> (using the durable key's thumbprint when derivation is used), <tt>ps</tt> set to the agent's configured person server (an agent has exactly one PS and one person), <tt>cnf.jwk</tt> = the ephemeral public key, and any AP-attested claims.</li>
</ol>
</section>

<section anchor="mobile-app-enrollment"><name>Mobile App Enrollment</name>

<ol spacing="compact">
<li>User signs into the AP through the app's normal login.</li>
<li>App generates a <strong>durable</strong> key in the Secure Enclave (iOS) or StrongBox (Android).</li>
<li>App initiates platform attestation: App Attest on iOS, Play Integrity on Android. The AP nominates a nonce.</li>
<li>App posts the durable public key, the attestation result, and the nonce to an AP-internal enrollment endpoint.</li>
<li>AP verifies the attestation against the platform's trust root.</li>
<li>AP records <tt>(ap_user, durable_jkt, attestation)</tt> and is ready to issue agent tokens.</li>
<li>Token issuance proceeds as in the web app sketch — app generates a fresh ephemeral key per agent token and chains it to the durable key via <tt>jkt-jwt</tt>.</li>
</ol>
</section>

<section anchor="self-hosted-enrollment"><name>Self-Hosted Enrollment</name>

<ol spacing="compact">
<li>User generates a hardware-bound key on their machine.</li>
<li>User publishes an AP metadata document at <tt>/.well-known/aauth-agent.json</tt> per <xref target="I-D.hardt-oauth-aauth-protocol"/>, with <tt>jwks_uri</tt> pointing to a JWKS containing the public part of that key.</li>
<li>The agent self-issues an agent token signed by that key as needed.</li>
</ol>
<t>There is no separate enrollment step — publication of the JWKS is the enrollment.</t>
<t>With several agents under the domain <xref target="many-agents-one-operator"/>, step 1 produces the AP key, and each agent additionally generates a key where it runs; step 3 issues one token per agent, binding each to its own key. Sub-agents are issued the same way <xref target="sub-agent-tokens"/>.</t>
</section>
</section>

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

<section anchor="trust-in-the-ap"><name>Trust in the AP</name>
<t>Every AP-attested claim in the agent token is only as trustworthy as the AP that signed the token. Receivers should apply policy proportional to their trust in the AP. The receivers are the PS and, when the PS federates, the AS; a resource reads the agent token only in agent identity and resource-managed access (<xref target="I-D.hardt-oauth-aauth-protocol"/>, Why No Agent Identifier Reaches a Resource). An unfamiliar AP making strong attestation claims may warrant additional caution at the PS consent screen.</t>
</section>

<section anchor="ephemeral-key-compromise"><name>Ephemeral Key Compromise</name>
<t>An ephemeral-key leak — via memory disclosure, in-page attacker, side channel, or similar — exposes only the signatures the agent makes during the current agent token's lifetime. At the recommended 1-hour lifetime, the blast radius is bounded to roughly that window before natural expiry forces replacement. The bound holds for the person tokens and auth tokens obtained with the key, since none expires later than the agent token <xref target="refresh-patterns"/>. Agents that detect compromise can decline to refresh, aging out the ephemeral key without explicit revocation. This bounding is the primary security argument for the two-key pattern <xref target="per-platform-keys"/>.</t>
</section>

<section anchor="durable-key-compromise"><name>Durable Key Compromise</name>
<t>Compromise of the durable key compromises the install's agent identity for the durable key's lifetime. The durable key signs only at refresh and is presented only to the AP, so its attack surface is much narrower than the ephemeral key's — but a successful compromise lets the attacker mint refresh requests indefinitely until the AP revokes the enrollment. APs should detect anomalous refresh patterns and provide a way for users to revoke a durable enrollment.</t>
<t>Revoking the enrollment stops further agent tokens but not the current one. The AP ends the current one by revoking it at the agent's PS, which then revokes every person token and auth token it issued to the agent's <tt>sub</tt> (<xref target="I-D.hardt-oauth-aauth-protocol"/>, Token Revocation). A resource that accepted the agent token directly receives no revocation and honors the token until its <tt>exp</tt>.</t>
<t>A non-extractable WebCrypto durable key cannot be exfiltrated by page-level attackers, but it can be used by them while they hold execution in the page. APs should pair WebCrypto-only enrollment with normal web hygiene (CSP, subresource integrity, dependency review) and should not treat the non-extractable property as a substitute for keeping the page's JavaScript clean. A hardware-backed durable key (Secure Enclave, StrongBox, TPM) cannot be exfiltrated at all, only used in-place — narrowing the threat to malicious code running in the agent application itself.</t>
</section>

<section anchor="attestation-replay"><name>Attestation Replay</name>
<t>Platform attestation results (App Attest, Play Integrity, WebAuthn ceremonies) should be bound to a server-nominated nonce that is single-use and short-lived (5 minutes is a reasonable upper bound). The AP should verify that the attestation includes the nonce it issued; without this binding, a captured attestation can be replayed across enrollments.</t>
</section>

<section anchor="self-hosted-jwks-key-compromise"><name>Self-Hosted JWKS Key Compromise</name>
<t>Compromise of the self-hosted JWKS key allows the attacker to mint agent tokens for that user's domain. Users running self-hosted agents should use hardware-backed keys (Secure Enclave / TPM / StrongBox) and rotate the published JWKS if compromise is suspected.</t>
<t>Where the domain runs several agents <xref target="many-agents-one-operator"/>, the two kinds of key have different blast radii. Compromise of one agent's key lets the attacker sign as that one <tt>sub</tt> until its token expires, and nothing else: the key appears in no published document and cannot mint tokens. Compromise of the AP key mints identities for every agent the domain runs, and for agents it does not run. The layout is only as strong as that separation; an operator who stores agent keys where the AP key lives has given each of them the AP key's reach.</t>
</section>
</section>

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

<section anchor="identifier-stability-and-user-tracking"><name>Identifier Stability and User Tracking</name>
<t>An agent's <tt>sub</tt> is not a pairwise identifier. It is the same value at every party the agent token reaches: the agent's PS, any AS the PS federates with, and any resource the agent calls in agent identity or resource-managed access. A stable <tt>sub</tt> lets the PS reliably re-identify the agent across sessions — that is the intended property — but because an agent acts for one person, it is also a pseudonym for that person which any two of those resources can correlate. In the modes where the agent presents a person token, a resource sees a per-resource identifier instead (<xref target="I-D.hardt-oauth-aauth-protocol"/>, Directed Identifiers). Under per-install identity <xref target="per-install-identity"/>, durable-key rotation produces a new <tt>sub</tt>, giving users a natural "fresh start" capability.</t>
</section>

<section anchor="ap-visibility-into-agent-activity"><name>AP Visibility Into Agent Activity</name>
<t>The AP that issued an agent token does not see the agent's subsequent traffic to PSes, resources, or ASes (they verify against the AP's published JWKS, not by calling the AP). The AP's view is limited to the requests made to it, such as enrollment, refresh, and sub-agent issuance. When the AP revokes an agent token, the PS reports nothing of what it revoked downstream (<xref target="I-D.hardt-oauth-aauth-protocol"/>, Why a PS Reports Nothing to the Agent Provider). APs should document their data retention practices for those events.</t>
</section>
</section>

<section anchor="iana-considerations"><name>IANA Considerations</name>
<t>This document is informational and registers no new media types, JWT claim names, or metadata fields.</t>
</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 patterns described in this document 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>TBD</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>
<li><t>draft-hardt-aauth-bootstrap-02</t>

<ul spacing="compact">
<li>Consistency pass against AAuth Protocol -11. Dropped the BCP 14 boilerplate, which an informational document that uses no keywords does not need. The web enrollment sketch no longer issues agent tokens "directed at" a PS: an agent has one person server, carried in <tt>ps</tt>. The agent-token lifetime bound is the protocol's recommended maximum, not a ceiling. Scheme names are given as prose.</li>
<li>Added Many Agents, One Operator under Self-Hosted Agents: one published AP key, one self-issued token per agent with its own <tt>sub</tt> and <tt>cnf</tt> key, agent keys never published, custody by blast radius. The single-key description assumed one agent per domain, and a deployment read it as "each agent holds a key published at a well-known URL". Refresh, enrollment, and Security Considerations gained the several-agent case.</li>
<li>Added Sub-Agent Tokens: the self-hosted case, where the operator's AP self-issues the sub-agent token with <tt>parent_agent</tt>, a <tt>+</tt> discriminator, the parent's <tt>ps</tt>, and a fresh <tt>cnf</tt> key; and the hosted-AP case, where the parent requests it with its own ephemeral key and token, and the AP checks the parent is top-level, applies policy, and returns the token. The protocol deferred acquisition here and nothing covered it.</li>
<li>Referenced the AAuth Protocol by its datatracker document URL, which tracks the latest revision.</li>
<li>Algorithm identifiers: <tt>Ed25519</tt> rather than the deprecated polymorphic <tt>EdDSA</tt>; the <tt>cnf.jwk</tt> example carries the <tt>alg</tt> member now required of every conveyed key.</li>
<li>The Introduction, Trust in the AP, and Identifier Stability name where the agent token goes: always to the PS, to an AS through the PS, and to a resource only in agent identity and resource-managed access. An agent has one PS, so the <tt>sub</tt> correlation concern is at resources, not across PSes.</li>
<li>The agent token carries <tt>ps</tt> whenever the agent has a person server.</li>
<li>A resource that calls downstream is its own AP and self-issues its agent token, which is not bound to one person.</li>
<li>The self-hosted AP key also signs the AP's revocations, under the <tt>jwks_uri</tt> scheme.</li>
<li>The agent identifier's local part follows the protocol's rules, which allow uppercase, and is compared exactly.</li>
<li>Refresh: the agent refreshes with five minutes left, then replaces its person tokens and auth tokens, so an agent-token lifetime must exceed that margin. The high-assurance example is 15–30 minutes, not 5–15.</li>
<li>The <tt>jkt-jwt</tt> naming JWT carries the ephemeral key in <tt>cnf.jwk</tt>, which the scheme requires.</li>
<li>A sub-agent token's <tt>iss</tt> equals its parent's, so only the parent's AP can issue it.</li>
<li>An AP revokes a compromised agent's current token at the PS, which revokes what it issued to that agent's <tt>sub</tt>. The ephemeral-key bound covers the person tokens and auth tokens obtained with the key. The PS reports nothing of a revocation back to the AP.</li>
<li>Protocol rules are cited by section name.</li>
<li>A request with a body to an AP endpoint should cover <tt>content-digest</tt> and <tt>content-type</tt>, as the protocol requires at a PS or AS. The refresh example shows it, and a new example shows the hosted sub-agent request, whose body carries the sub-agent's public key.</li>
<li>Per-Install Identity: the AP may know which of its accounts enrolled an install, but issues each install its own identifier and does not join installs into one agent. It no longer claims the AP keeps no account mappings, which the enrollment sketches contradicted.</li>
</ul></li>
<li><t>draft-hardt-aauth-bootstrap-01</t>

<ul spacing="compact">
<li>Major rewrite. The document is now informational guidance for AP implementers. The previously-normative PS bootstrap protocol (PS <tt>/bootstrap</tt> endpoint, <tt>bootstrap_token</tt>, bootstrap announcement, agent server [now Agent Provider] <tt>bootstrap_endpoint</tt> / <tt>refresh_endpoint</tt> / <tt>webauthn_endpoint</tt>) has been removed. PS-side binding to a person now happens lazily on the agent's first interaction with the PS per the AAuth Protocol; the bootstrap document covers AP-side enrollment patterns only.</li>
<li>Removed Agent-Attested Display Values section; the <tt>platform</tt> and <tt>device</tt> parameters are defined and described in the AAuth Protocol.</li>
</ul></li>
<li><t>draft-hardt-aauth-bootstrap-00</t>

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

<section anchor="acknowledgments"><name>Acknowledgments</name>
<t>The author thanks Abay Aubakirov (Regent Protocol) for feedback from a production sub-agent deployment.</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>
    <date year="2026"/>
  </front>
</reference>
<reference anchor="I-D.hardt-oauth-aauth-protocol" target="https://datatracker.ietf.org/doc/draft-hardt-oauth-aauth-protocol">
  <front>
    <title>AAuth Protocol</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/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>
<reference anchor="WebCryptoAPI" target="https://www.w3.org/TR/WebCryptoAPI/">
  <front>
    <title>Web Cryptography API</title>
    <author>
      <organization>W3C</organization>
    </author>
    <date year="2017"/>
  </front>
</reference>
</references>
</references>

</back>

</rfc>
