This page is auto-synced from
AFAuthHQ/spec/spec/conformance.md. Do not edit here.core.md. The §C.1 through §C.6 test-vector corpus and the executable harness at ../harness/run.js ship in this repository; an implementation that passes every committed vector through its own verifier may claim v0.1 conformance for its declared role.
This document describes what an AFAuth implementation must support to call itself “AFAuth-conformant.” It is the authoritative reference for that claim; the protocol specification (core.md) provides the underlying normative requirements.
Scope
An AFAuth-conformant implementation is either an agent that signs requests according to the protocol, or a service that verifies them and operates the account lifecycle. Conformance is asserted separately for each role. This document does NOT specify:- What operations an agent may perform at any given account state beyond the §7.5 owner-binding floor (out of scope per §1.3).
- The user interface of the claim ceremony (service responsibility per §12.3).
- The treatment of pre-claim account state at claim time (service responsibility per §12.7).
Service conformance (planned probes)
A conforming service MUST:- Discovery. Serve a valid
/.well-known/afauthdocument per the schema, including allrequiredendpoints (accounts,owner_invitation,claim_page,claim_completion). - Signature verification. Accept the §5.2 covered components and parameters; reject requests with extra or missing components; honour
createdandexpires; maintain a(keyid, nonce)replay set covering at leastexpires - created + skew_tolerance. - State machine. Implement all and only the transitions in Appendix A. In particular, transition
UNCLAIMED → INVITED → CLAIMEDonly via the §7 flow; never bind an owner whose authenticated identity does not satisfy the match relation againstpending_recipientfor its registered type (§7.4, §7.7). - Two-step verify (§7.1). Reject any flow in which the agent’s signature alone binds ownership.
- Owner-binding floor (§7.5). Reject agent-signed owner-binding operations post-claim with
403 Forbiddenandowner_binding_blocked. - Invitation atomicity (§7.3). At most one pending invitation per account at any time; atomic replacement; invalidated tokens MUST fail with
410 Gone. attested_onlyhonouring (§9, §6.3). Services declaringunclaimed_mode = "attested_only"MUST reject implicit signup lacking attestation withattestation_required, without creating the account.- Error codes (§11). Use the reserved codes for the conditions they describe.
- Recipient types (§7.7). Accept at minimum the
emailrecipient type. Declare any additional supported types inrecipient_typesof the discovery doc. Reject unsupported types in invitation requests with400 Bad Requestandunsupported_recipient_type. - Owner re-key & revoke (§8.2, §8.4). A service that advertises
key_rekey/key_revocationMUST gate them on a fresh owner-authenticated session (§7.5) — never the agent signature — and MUST verify the session owns the target account: reject a stale session withowner_session_too_stale, a non-owner withowner_authentication_required(and no session with401), and a non-CLAIMEDtarget withnot_claimed. After revoke — and, for the old DID, after re-key — a request signed by the retired key MUST fail with401 revoked_key. - Attested sessions (§10.7). A service that advertises
attested_sessionMUST treat a presented attestation as valid only until itsexp(strict mode) — or, in extended mode, until a service-chosen windowTrefreshed on each presentation — and MUST challenge a request from an established account whose freshness window has lapsed with401 attestation_requiredrather than serving it on the agent signature alone. It MUST reject an already-expired attestation presented as a refresh (§10.2).
- Authentication challenge (§5.7). Emit a
WWW-Authenticate: AFAuth …challenge on401 Unauthorized, carrying at leastdiscovery, and — onattestation_required/invalid_attestation—attestors. Emission is RECOMMENDED, not required. When emitted: the challenge MUST be syntactically valid per [RFC9110]; itserror, if present, MUST equal the body’serror.codeand MUST be set only when the request attempted AFAuth (carriedSignature-InputorAFAuth-Attestation) — otherwise the challenge is a bare advertisement with noerror; and on a resource that also accepts other schemes the AFAuth challenge MUST be one of possibly severalWWW-Authenticatechallenges, never suppressing or reordering another scheme’s.
Agent conformance (planned probes)
A conforming agent MUST:- Discovery. Fetch and honour
/.well-known/afauthbefore signup; respect the declaredsignature_algorithmsandbilling.unclaimed_mode. - Identity. Generate a valid
did:keyaccount identifier per §3.1.1, including canonical multibase/multicodec encoding. - Signing. Produce signatures with all §5.2 covered components and parameters; bound
expires - created≤ 300 seconds; use a fresh nonce per request. - Key handling. Store private keys per §3.2 / §12.1 recommendations; rotate per §8.
- Claim flow. Initiate the two-step invitation per §7.2; treat
410 Goneon an invitation as a normal expiry condition. - Attestation refresh (§10.7). On
401 attestation_requiredfrom a service with which it has an established account, an agent SHOULD obtain a fresh attestation (§10) and retry the request. It MUST NOT treat a transient mint failure as terminal, and MUST stop and surface a revoked or paused binding (a refused mint) to the operator rather than retrying indefinitely.
- Authentication challenge (§5.7). Read the
WWW-Authenticate: AFAuth …challenge on a401to bootstrap discovery (discovery) and route recovery (error, andattestorsfor attestation failures). It MUST NOT require the challenge to be present — when absent, fall back to fetching/.well-known/afauthby convention (§4) — and MUST ignoreauth-params it does not recognize.
Versioning
This document tracks the protocol version it describes. Future revisions will use additive probes for non-breaking protocol changes and a new major version for breaking changes.Test vectors and harness
The Appendix C corpus ships under../vectors/:
- §C.1, §C.2 —
vectors/signatures/: canonical input + reference signatures. - §C.3 —
vectors/discovery/: well-formed, forward-compatible, and malformed discovery documents. - §C.4 —
vectors/recipients/: per-type normalisation rules with canonical forms. - §C.5 —
vectors/errors/: one envelope fixture per §11.3 reserved code. - §C.6 —
vectors/replay-window/: expired, future-dated, replay-invariant, and cross-keyid sequences. - §C.7 —
vectors/trust-attestation/:afauth-trustattestation JWTs — well-formed, runtime-only (nosub_h), and malformedsub_hcases (§10). Applies only to implementations that consume attestations.
../harness/run.js runs every fixture against a reference verifier and exits non-zero on any failure. It exports its primitives (buildCanonicalInput, verifySignature, checkDiscoveryDocument, normaliseRecipient, …) so independent implementations can reuse them. Contributions of additional vectors are welcome via the proposals process (see ../proposals/).