x401: HTTP Proof Requirement Protocol ================== Status: [[badge: Draft]] Version: 0.1.0 Editors: ~ [Daniel Buchner](https://www.linkedin.com/in/dbuchner) - [Proof](https://proof.com) ~ [Darren Louie](https://www.linkedin.com/in/darrenlouie) - [Proof](https://proof.com) Participate: ~ [GitHub repo](https://github.com/proof/x401) ~ [File an issue](https://github.com/proof/x401/issues) ~ [Commit history](https://github.com/proof/x401/commits/main) ------------------------------------ ## Abstract x401 defines an HTTP-based, route-scoped proof requirement protocol for requiring credential-based proof before access to a protected resource is granted. x401 uses: - the **`PROOF-REQUIRED` HTTP header field** to carry proof requirements - the **`PROOF-PRESENTATION` HTTP header field** to carry proof presentations or reusable proof-satisfaction tokens - the **`PROOF-RESPONSE` HTTP header field** to carry verifier response information, including x401 proof errors - **HTTP status codes** to express the overall response semantics independently of x401 proof state - **Digital Credentials Query Language (DCQL)**, directly or through OpenID4VP `scope` aliases, to describe the credential requirements for a protected route - **OpenID for Verifiable Presentations (OpenID4VP)** for the Agent-created presentation request - **OAuth 2.0** for optional exchange of a verified presentation for an access token - **DIF Credential Trust Establishment** for verifier-approved issuer policy and non-authoritative acquisition guidance - **OpenID for Verifiable Credential Issuance (OpenID4VCI)** for resolving credential issuer metadata when the referenced trust document identifies OpenID4VCI issuers The x401 payload is not a fully composed OpenID4VP Authorization Request. The Verifier returns the presentation protocol, credential query requirement, Verifier Challenge value, issuer trust reference, and OAuth token exchange metadata the Agent needs in order to create its own OpenID4VP Authorization Request for a Wallet. x401 is intentionally separate from payment protocols. When payment is required, it MUST be handled with **HTTP 402 Payment Required** and an appropriate payment protocol. x401 MUST NOT redefine payment semantics. This document defines the x401 payload, processing rules, interoperability requirements, and examples for proof requirements with optional payment handling. ::: note Protocol Boundary x401 defines proof requirement semantics only. When payment is required, implementations still use `402 Payment Required` and a separate payment protocol. ::: ## Introduction HTTP provides a standard challenge mechanism for authentication via `401 Unauthorized` and `WWW-Authenticate`, but it does not define a general-purpose, machine-readable protocol for route-scoped proof requirements such as: - proving personhood - proving country of residency - proving membership or accreditation - proving entitlement issued by a specific issuer class - proving organizational standing - proving workload identity attributes At the same time, OpenID4VP, DCQL, OAuth, and OpenID4VCI define interoperable mechanisms for requesting presentations, evaluating credential requirements, issuing access tokens, and issuing credentials, but they are not themselves an HTTP route proof requirement protocol. x401 fills that gap by defining an HTTP-native wrapper that: - signals proof requirements at the protected route - carries x401 proof objects as base64url values in dedicated proof header fields - carries a credential query requirement for the route, expressed as either a DCQL query or an OpenID4VP `scope` value that represents a DCQL query - carries a Verifier Challenge value composed from the Verifier's identifier and a cryptographic nonce - gives the Agent the information required to create a valid OpenID4VP Authorization Request for its Wallet - includes OAuth token exchange metadata for Agents that want a reusable access token after proving - optionally includes a DIF Credential Trust Establishment URL that can help Agents discover acceptable credential issuers - composes with, but does not subsume, payment protocols In the typical flow, an [[ref: Agent]] receives an x401 proof requirement from a [[ref: Verifier]], creates a wallet-facing [[ref: Presentation Request]] that includes the Verifier's [[ref: Credential Query Requirement]] and [[ref: Verifier Challenge]], receives a verifiable presentation from a [[ref: Wallet]], and retries the original protected route. An [[ref: Issuer Trust List]] can help the Agent discover credentials or issuers, but the Verifier remains authoritative for issuer trust enforcement. ## Design Goals The goals of x401 are: 1. Define a route-scoped proof requirement for HTTP resources. 2. Allow Verifiers to send proof requirements without composing the Agent's OpenID4VP Authorization Request. 3. Require the Agent to bind the wallet presentation to its own identifier while preserving a verifier-originated Verifier Challenge. 4. Reuse OpenID4VP credential query mechanisms, DCQL, OAuth, and OpenID4VCI. 5. Remain separate from payment semantics. 6. Allow issuer discovery by reference to verifier trust policy without listing issuers inline in the x401 payload. 7. Support stateless verifier deployments by allowing Verifier Challenge correlation context to be encoded in verifier-protected nonce values. 8. Allow additional caller authentication, request signing, and delegation artifacts to compose with x401 without making any one agent identity system mandatory. ## Non-Goals x401 does not: - define a new credential format - replace OpenID4VP - replace OpenID4VCI - define the transport used to deliver an OpenID4VP request to a Wallet - define a payment protocol - require all Verifiers to maintain server-side session state - define a universal agent authentication protocol ## Terminology The key words **MUST**, **MUST NOT**, **REQUIRED**, **SHALL**, **SHALL NOT**, **SHOULD**, **SHOULD NOT**, **RECOMMENDED**, **NOT RECOMMENDED**, **MAY**, and **OPTIONAL** in this document are to be interpreted as described in RFC 2119 and RFC 8174. [[def: Verifier]]: ~ The party protecting a resource or operation and requiring proof. [[def: Agent]]: ~ The HTTP caller that requests a protected route, receives an x401 proof requirement, creates a wallet-facing OpenID4VP presentation request, receives proof material from a Wallet, and retries the protected route. The Agent uses an [[ref: Agent Identifier]] for client binding. [[def: Agent Identifier]]: ~ An identifier for the Agent that the Verifier can bind to the HTTP caller. This specification does not register a single Agent Identifier scheme; a Verifier policy MUST define which schemes it accepts, including any accepted DID, HTTPS origin, domain-bound client identifier, or certificate-bound identifier schemes. [[def: Holder]]: ~ The subject or entity that possesses credentials and can authorize a Wallet to present proof. [[def: Wallet]]: ~ Software capable of receiving an OpenID4VP presentation request from an Agent and returning a verifiable presentation result authorized by a [[ref: Holder]]. [[def: Presentation Request]]: ~ An OpenID4VP Authorization Request created by the Agent. It includes the [[ref: Credential Query Requirement]] from the x401 payload as either `dcql_query` or `scope`, uses the [[ref: Verifier Challenge]] value as `nonce`, and identifies the Agent as the OpenID4VP client. [[def: Credential Query Requirement]]: ~ The verifier-supplied credential selection requirement in the x401 payload. It is expressed as exactly one of `proof.dcql_query` or `proof.scope`. [[def: DCQL Requirement]]: ~ A [[ref: Credential Query Requirement]] expressed directly as `proof.dcql_query`. [[def: Scope Requirement]]: ~ A [[ref: Credential Query Requirement]] expressed as `proof.scope`. A Scope Requirement is an OpenID4VP `scope` string whose scope value, or values, represent well-defined DCQL queries according to OpenID4VP and an applicable profile, ecosystem, Wallet configuration, or verifier policy recognized by the Wallet and Verifier. [[def: Verifier Challenge]]: ~ A nonce-bearing object generated by the Verifier. It contains a challenge value and expiry. The challenge value is composed from, or bound to, the Verifier's identifier and a fresh cryptographic nonce. The Agent uses this exact value as the OpenID4VP `nonce` in its wallet-facing Presentation Request. [[def: Issuer Trust List]]: ~ A verifier-controlled DIF Credential Trust Establishment document that identifies the issuers, authorities, roles, activities, credential schemas, or credential types the Verifier accepts for a proof requirement. The same document can also help Agents discover where qualifying credentials may be obtained. [[def: VP Artifact]]: ~ A retry artifact containing the Wallet's presentation result and the x401 metadata needed by the Verifier to validate proof fulfillment for an [[ref: Agent Identifier]], encoded for use in a `PROOF-PRESENTATION` request header or OAuth token exchange. [[def: Verification Token]]: ~ A verifier-issued, short-lived access token returned after successful proof verification and used by the [[ref: Agent]] on later protected-route requests so that the VP Artifact does not need to be repeated. A Verification Token can be used as the route's normal `Authorization` token when the deployment supports that model, or carried as an x401 Token Object in `PROOF-PRESENTATION` when the route already uses `Authorization` for existing application authentication. [[def: x401 Token Object]]: ~ A JSON object carried as a base64url value in `PROOF-PRESENTATION` to pass a Verification Token without replacing the route's existing `Authorization` credentials. [[def: x401 Payload]]: ~ The JSON object defined by this specification, UTF-8 encoded, and carried as a base64url value in the `PROOF-REQUIRED` header field. [[def: x401 Error Object]]: ~ A JSON object carried as a base64url value in the `PROOF-RESPONSE` header field to describe why a presented proof failed or could not be processed. ## Protocol Overview The x401 protocol is made up of four legs: a Verifier exposes identity proof requirements that gate access to a resource, the Agent turns the requirements into a wallet-facing OpenID4VP request, the Agent presents the Wallet's presentation result back to the Verifier, and the Agent may exchange that proof for a reusable Verification Token. The tabs below summarize each leg and link to the detailed section that defines the processing rules that pertain to them. ::: tabs :: 1. Gated Resource The Verifier declares a route-scoped proof requirement by returning a `PROOF-REQUIRED` header. The header carries the base64url-encoded x401 payload that defines the route's proof requirement; see [x401 Gated Resource Configuration](#x401-gated-resource-configuration) for details. The response status code describes the overall HTTP response and is not the x401 protocol carrier. ```http HTTP/1.1 401 Unauthorized PROOF-REQUIRED: Cache-Control: no-store ``` :: 2. Agent Presentation The Agent decodes the x401 payload and creates its own OpenID4VP Authorization Request for the Wallet. It preserves the Verifier's credential query requirement, as either `dcql_query` or `scope`, and uses the exact Verifier Challenge value as the OpenID4VP `nonce`; see [Agent-Generated Verifiable Presentation](#agent-generated-verifiable-presentation) for details. ```json { "response_type": "vp_token", "client_id": "decentralized_identifier:did:web:agent.example", "nonce": "x401:aHR0cHM6Ly9iYW5rLmV4YW1wbGUuY29t:uX7Vq3mZJH6MeN0qz2L7SQ", "dcql_query": { "credentials": [ { "id": "financial_customer", "format": "jwt_vc_json" } ] }, "response_uri": "https://agent.example/wallet/callback/7c9e" } ``` :: 3. VP Presentation After the Wallet returns a presentation result, the Agent packages it as a VP Artifact for protected-route retry. The VP Artifact carries the Agent Identifier, Verifier Challenge, and Wallet-returned proof material; see [Verifiable Presentation Delivery](#verifiable-presentation-delivery) for details. ```json { "agent_id": "did:web:agent.example", "challenge": "x401:aHR0cHM6Ly9iYW5rLmV4YW1wbGUuY29t:uX7Vq3mZJH6MeN0qz2L7SQ", "vp_token": "" } ``` :: 4. Token Acquisition The Agent can submit the same VP Artifact to the Verifier's OAuth token endpoint to obtain a reusable Verification Token. The token exchange uses fixed x401 token-exchange parameters and then the Agent retries protected routes with either an upgraded `Authorization` token or a `PROOF-PRESENTATION` proof-satisfaction token object; see [Access Token Acquisition](#access-token-acquisition) for details. ```http POST /oauth/token HTTP/1.1 Host: bank.example.com Content-Type: application/x-www-form-urlencoded grant_type=urn:ietf:params:oauth:grant-type:token-exchange& subject_token_type=urn:x401:params:oauth:token-type:vp_artifact& subject_token= ``` ::: ### Primary Flow In the primary x401 flow, the [[ref: Agent]] is the HTTP caller and is assumed to have access to a [[ref: Wallet]], keys, credentials, or local capabilities needed to fulfill the proof requirement. 1. The [[ref: Agent]] requests a protected route. 2. The [[ref: Verifier]] determines that proof is required. 3. The [[ref: Verifier]] returns an HTTP response with: - `PROOF-REQUIRED: ` 4. The [[ref: Agent]] decodes the x401 payload and extracts the OpenID4VP presentation protocol, Credential Query Requirement, Verifier Challenge, and OAuth token endpoint. 5. The [[ref: Agent]] creates a wallet-facing OpenID4VP Presentation Request. The request MUST use the x401 Verifier Challenge as its `nonce` value, MUST include the x401 Credential Query Requirement using the same OpenID4VP query parameter name supplied by the x401 payload, and MUST identify the Agent as the OpenID4VP client. 6. The [[ref: Wallet]] returns a presentation result to the [[ref: Agent]]. 7. The [[ref: Agent]] retries the same protected route that produced the x401 proof requirement with one of: - a [[ref: VP Artifact]] in a `PROOF-PRESENTATION` request header, - a [[ref: Verification Token]] carried as an x401 Token Object in a `PROOF-PRESENTATION` request header, or - a [[ref: Verification Token]] in the route's normal `Authorization` request header when the deployment uses x401 proof satisfaction to upgrade or replace the route's ordinary authorization credential. 8. The [[ref: Verifier]] validates the VP Artifact or Verification Token. 9. If proof is satisfied and payment is not required or is already satisfied, the [[ref: Verifier]] returns the protected resource. 10. If proof is satisfied but payment remains unsatisfied, the [[ref: Verifier]] returns `402 Payment Required` with payment protocol details. After satisfying payment, the [[ref: Agent]] retries the same route with proof or token material and the payment artifact required by the selected payment protocol. ```mermaid sequenceDiagram participant Agent participant Verifier participant Wallet participant Payment Agent->>Verifier: Request protected route Verifier-->>Agent: HTTP response + PROOF-REQUIRED ... Agent->>Wallet: OpenID4VP Presentation Request with Agent client_id, verifier nonce, and credential query Wallet-->>Agent: Presentation result Agent->>Verifier: Retry same route with PROOF-PRESENTATION or Authorization token opt Payment required after proof satisfaction Verifier-->>Agent: 402 Payment Required Agent->>Payment: Complete payment protocol Payment-->>Agent: Payment artifact Agent->>Verifier: Retry same route with proof or token + payment end Verifier-->>Agent: Protected resource ``` ### Optional OAuth Token Exchange An Agent MAY exchange a VP Artifact for a Verification Token before retrying the protected route. The token endpoint is supplied by the Verifier in the x401 payload. ```mermaid sequenceDiagram participant Agent participant Verifier participant Wallet Agent->>Verifier: Request protected route Verifier-->>Agent: HTTP response + PROOF-REQUIRED ... Agent->>Wallet: OpenID4VP Presentation Request with Agent client_id, verifier nonce, and credential query Wallet-->>Agent: Presentation result Agent->>Verifier: OAuth token request with VP artifact Verifier-->>Agent: Verification token Agent->>Verifier: Retry same route with Authorization token or PROOF-PRESENTATION token object Verifier-->>Agent: Protected resource ``` The technical sections that follow are organized by the four main legs of the protocol: x401 gated resource configuration, Agent-generated verifiable presentation, verifiable presentation delivery, and access token acquisition. ## x401 Gated Resource Configuration The initial leg of the protocol defines how a protected HTTP resource declares what proof is required. The Verifier responds to the original protected-route request with a `PROOF-REQUIRED` header whose value contains the base64url-encoded x401 payload. The requirement is route-scoped. If a representation includes gated and ungated material, this version of x401 does not define per-fragment requirements or separate requirement mapping inside the page. Fulfillment of the complete credential demand in the `PROOF-REQUIRED` payload satisfies the route's x401 gate. ```http POST /accounts/applications HTTP/1.1 Host: bank.example.com Accept: application/json HTTP/1.1 401 Unauthorized PROOF-REQUIRED: Cache-Control: no-store ``` ### HTTP Semantics HTTP status or condition | Meaning in a x401-capable deployment | Agent expectation ------------------------ | ------------------------------------ | ---------------- Any response with `PROOF-REQUIRED` | Proof is required, advertised, or not yet satisfied for the route | Decode the x401 payload from the `PROOF-REQUIRED` field value Any response with `PROOF-RESPONSE` containing an x401 Error Object | A presented x401 proof failed or could not be processed | Decode the x401 error object and treat the x401 proof branch as failed `2xx` with `PROOF-REQUIRED` | The HTTP response is otherwise successful, but the route includes x401-gated material or actions | Process the response normally and fulfill the route-scoped proof requirement when access to the gated material is needed `4xx` with `PROOF-REQUIRED` | The route cannot be completed without proof | Fulfill the route-scoped proof requirement before retrying `402 Payment Required` | Payment remains unsatisfied | Switch to the payment protocol The x401 proof header fields are the x401 protocol carriers. HTTP status codes describe the overall response and do not, by themselves, define x401 proof state. #### Status Code Independence A server that requires proof for access to a protected resource, route, representation, or operation whose response would be changed as a whole by fulfilling a single set of proof requirements MUST return `PROOF-REQUIRED: `. The `PROOF-REQUIRED` header is the authoritative carrier for an x401 proof requirement that gates the entire response. The response MAY use any HTTP status code appropriate for the whole response. A Verifier can use a successful status code when the response body is still useful without proof, or a client or server error status when the requested operation cannot proceed until proof is satisfied. Example: ```http HTTP/1.1 200 OK PROOF-REQUIRED: Cache-Control: no-store ``` An x401 proof requirement carried in `PROOF-REQUIRED` SHOULD NOT require the Agent to parse a response body in order to understand the proof requirement. This specification does not carry x401 proof requirements in `WWW-Authenticate`. A protected route may already use that header for an existing HTTP authentication scheme, and combining multiple schemes — whether through comma-separated field values or duplicate header lines — is not consistently or well handled by common HTTP servers, proxies, client libraries, and middleware. Carrying x401 proof requirements in a dedicated `PROOF-REQUIRED` field avoids those interoperability gaps and lets x401 compose with any existing `WWW-Authenticate`-based authentication without contention. #### 402 for Payment A server that requires payment MUST use `402 Payment Required` and MUST NOT overload x401 to represent payment as proof. Payment metadata MAY be declared in a x401 payload for informational purposes when payment may also be required, but payment satisfaction itself remains governed by the payment protocol used with `402`. #### x401 Error for Failed Policy Satisfaction If an Agent presents a proof artifact that is structurally valid but does not satisfy the Verifier's policy, the Verifier SHOULD return a `PROOF-RESPONSE: ` header. The x401 Error Object is the protocol-specific indication of failure for x401 exchanges. The HTTP response status remains independent and describes the overall response. Examples include: - credential from an untrusted issuer - credential does not satisfy predicates - expired or revoked credential - presentation audience does not match the Agent Identifier - challenge value does not match the expected verifier identifier and nonce - insufficient assurance level ### Proof Header Fields The proof header fields carry x401 protocol objects. This specification uses "x401 proof requirement" for the overall route-gating declaration and reserves [[ref: Verifier Challenge]] for the nonce-bearing `proof.challenge` object. #### Header Syntax Each x401 proof header field carries exactly one base64url-encoded UTF-8 JSON object. The header field name identifies the protocol leg, so x401 does not use an action keyword inside a shared carrier header. Header field names are shown in uppercase for consistency with the examples. As HTTP field names, they are case-insensitive. ```text PROOF-REQUIRED: PROOF-PRESENTATION: PROOF-RESPONSE: ``` The defined x401 proof header fields are: Header field | Direction | Decoded payload ------------ | --------- | --------------- `PROOF-REQUIRED` | Verifier to Agent | [[ref: x401 Payload]] `PROOF-PRESENTATION` | Agent to Verifier | [[ref: VP Artifact]] or [[ref: x401 Token Object]] `PROOF-RESPONSE` | Verifier to Agent | x401 response information, including [[ref: x401 Error Object]] The encoded value MUST be base64url-encoded UTF-8 JSON using the URL and filename safe alphabet defined by RFC 4648 Section 5 without padding. The decoded value MUST be a single JSON object. When `PROOF-PRESENTATION` carries a VP Artifact, the request is a direct proof presentation. When it carries an x401 Token Object, the request is presenting a reusable proof-satisfaction token issued after prior verification. `PROOF-RESPONSE` is the Verifier-to-Agent response information channel for x401-specific results and diagnostics. This specification defines the x401 Error Object for failed proof presentation or token processing. Deployments MAY define additional response objects for accepted proof state, token metadata, or other x401-specific return information. A sender MUST NOT generate more than one field line with the same x401 proof header name in the same HTTP message. A sender MUST NOT combine multiple x401 protocol objects in a single proof header using commas or other list syntax. If a recipient receives multiple field lines for the same proof header, or receives a proof header value containing a comma-separated list of messages, it MUST treat that proof header as invalid. A response SHOULD NOT contain both `PROOF-REQUIRED` and `PROOF-RESPONSE` unless the response both reports verifier response information and intentionally advertises a fresh proof requirement for a subsequent attempt. A request SHOULD NOT contain more than one `PROOF-PRESENTATION` value. When browser-based JavaScript needs to read `PROOF-REQUIRED` or `PROOF-RESPONSE` from a cross-origin response, the response needs CORS exposure for those fields. When browser-based JavaScript sends `PROOF-PRESENTATION` cross-origin, the server needs to allow the `PROOF-PRESENTATION` request header through CORS preflight. ### x401 Payload A x401 payload is a single JSON object encoded in the `PROOF-REQUIRED` header field. The payload SHOULD remain compact. Sensitive route state SHOULD be omitted, stored server-side, or carried only inside verifier-protected nonce state. #### Top-Level Members ```json { "scheme": "x401", "version": "0.1.0", "proof": {}, "payment": {} } ``` #### Member Definitions Name | Definition ---- | ---------- `scheme` | REQUIRED. Value MUST be the string `"x401"`. `version` | REQUIRED. The x401 payload version. `proof` | REQUIRED. Contains the presentation protocol, Credential Query Requirement, Verifier Challenge, issuer trust reference, OAuth token exchange metadata, and reusable requirement identifiers. `payment` | OPTIONAL. Describes that payment is additionally required, without replacing `402` semantics. ### Proof Object The proof object gives the Agent the verifier-supplied values it needs to create its own Presentation Request and later retry the protected route. #### General Structure ```json { "presentation_protocol": "openid4vp", "dcql_query": { "credentials": [ { "id": "financial_customer", "format": "jwt_vc_json", "meta": { "type_values": ["FinancialCustomerCredential"] }, "claims": [ { "path": ["credentialSubject", "assurance_level"], "values": ["VC-AL2", "VC-AL3"] } ] } ] }, "challenge": { "value": "x401:aHR0cHM6Ly9iYW5rLmV4YW1wbGUuY29t:uX7Vq3mZJH6MeN0qz2L7SQ", "expires_at": "2026-05-06T18:45:00Z" }, "oauth": { "token_endpoint": "https://bank.example.com/oauth/token" }, "issuers": { "trust_establishment_url": "https://bank.example.com/.well-known/x401/trust/financial-customer-v1" }, "request_id": "proof-template-financial-customer-v1", "satisfied_requirements": [ "urn:example:x401:satisfaction:financial-customer:v1" ] } ``` The same proof object can instead use an OpenID4VP `scope` value when an applicable profile, Wallet configuration, or verifier policy recognized by the Wallet and Verifier defines that scope as a credential query: ```json { "presentation_protocol": "openid4vp", "scope": "financial_customer", "challenge": { "value": "x401:aHR0cHM6Ly9iYW5rLmV4YW1wbGUuY29t:uX7Vq3mZJH6MeN0qz2L7SQ", "expires_at": "2026-05-06T18:45:00Z" }, "oauth": { "token_endpoint": "https://bank.example.com/oauth/token" }, "issuers": { "trust_establishment_url": "https://bank.example.com/.well-known/x401/trust/financial-customer-v1" }, "request_id": "proof-template-financial-customer-v1", "satisfied_requirements": [ "urn:example:x401:satisfaction:financial-customer:v1" ] } ``` #### Members Name | Definition ---- | ---------- `presentation_protocol` | REQUIRED. Identifies the wallet-facing presentation protocol the Agent MUST use. For this version of x401, the value MUST be `"openid4vp"`. `dcql_query` | OPTIONAL. The explicit [[ref: DCQL Requirement]] for the protected route. The Agent MUST include this value as the `dcql_query` in its Presentation Request when `dcql_query` is present. Exactly one of `dcql_query` or `scope` MUST be present. `scope` | OPTIONAL. The [[ref: Scope Requirement]] for the protected route. The Agent MUST include this value as the OpenID4VP `scope` in its Presentation Request when `scope` is present. Exactly one of `dcql_query` or `scope` MUST be present. `challenge` | REQUIRED. The Verifier Challenge object. The Agent MUST include `challenge.value` as the OpenID4VP `nonce` in its Presentation Request. `oauth` | REQUIRED. OAuth token exchange metadata. It gives the Agent the endpoint, and any optional resource or audience values, needed to exchange a VP Artifact for a Verification Token. `issuers` | OPTIONAL. References verifier-approved issuer trust policy for the proof requirement. When present, this object MUST identify an [[ref: Issuer Trust List]] and MUST NOT list approved issuers inline. `request_id` | OPTIONAL. A stable verifier-defined identifier for the proof template. This value can be reused across Verifier Challenge instances and routes when they ask for the same proof requirement. `satisfied_requirements` | OPTIONAL. An array of stable verifier-defined identifiers for the reusable proof requirements that will be marked satisfied if this proof is fulfilled. The proof object MUST contain exactly one Credential Query Requirement: either `dcql_query` or `scope`. It MUST NOT contain both, and it MUST NOT omit both. When `scope` is used, each scope value MUST be an alias for a well-defined DCQL query in the applicable OpenID4VP profile, ecosystem, Wallet configuration, or verifier policy recognized by the Wallet and Verifier. If a single `scope` string contains multiple scope values, the DCQL Credential Query `id` values and claim `id` values represented by those scope values MUST be unique across the combined query. The Verifier MUST be able to validate the returned credentials against the credential policy represented by the `scope` value and MUST NOT treat the returned `scope` string itself as proof satisfaction. #### Verifier Challenge Members Name | Definition ---- | ---------- `value` | REQUIRED. The exact value the Agent MUST use as the OpenID4VP `nonce` in its Presentation Request. `expires_at` | REQUIRED. The time after which the Verifier will reject the Verifier Challenge. The value MUST be an RFC 3339 timestamp. #### OAuth Members Name | Definition ---- | ---------- `token_endpoint` | REQUIRED. OAuth 2.0 token endpoint where the Agent can exchange a VP Artifact for a Verification Token. `audience` | OPTIONAL. OAuth token exchange `audience` value the Agent should request. `resource` | OPTIONAL. OAuth token exchange `resource` value the Agent should request. The x401 OAuth profile fixes `grant_type`, `subject_token_type`, and Bearer token usage. These values MUST NOT be repeated in the x401 payload. #### Issuers Members Name | Definition ---- | ---------- `trust_establishment_url` | REQUIRED when `issuers` is present. An HTTPS URL for a DIF Credential Trust Establishment document that describes the issuers, authorities, roles, activities, credential schemas, or credential types the Verifier approves for this proof requirement. Verifier-approved issuers MUST NOT be enumerated inline in the x401 payload. A Verifier that wants to expose issuer approval policy to Agents SHOULD publish a DIF Credential Trust Establishment document and provide its URL in `proof.issuers.trust_establishment_url`. The Agent MAY use the Issuer Trust List to select candidate credentials or guide acquisition. The Verifier MUST enforce issuer trust during proof validation and MUST NOT rely on the Agent's interpretation of the Issuer Trust List. #### Proof Object Example ::: example Proof Object Example ```json { "presentation_protocol": "openid4vp", "dcql_query": { "credentials": [ { "id": "financial_customer", "format": "jwt_vc_json", "meta": { "type_values": ["FinancialCustomerCredential"] } } ] }, "challenge": { "value": "x401:aHR0cHM6Ly9iYW5rLmV4YW1wbGUuY29t:uX7Vq3mZJH6MeN0qz2L7SQ", "expires_at": "2026-05-06T18:45:00Z" }, "oauth": { "token_endpoint": "https://bank.example.com/oauth/token" }, "request_id": "proof-template-financial-customer-v1", "satisfied_requirements": [ "urn:example:x401:satisfaction:financial-customer:v1" ] } ``` ::: ### Verifier Challenge Construction and Correlation When a Verifier creates a Verifier Challenge, it MUST bind the Verifier Challenge instance to the protected resource context needed to evaluate the retry. This context includes the requested method, route or resource identifier, Credential Query Requirement, Verifier identifier, cryptographic nonce, expiration time, expected Agent Identifier rules, and accepted retry mechanisms. This binding MAY be stored server-side, but x401 does not require that storage. A Verifier MAY instead encode the binding into the nonce segment as verifier-protected Verifier Challenge state. #### Verifier Challenge State and Verification A Verifier MUST be able to determine that a returned challenge value is one it issued for the protected route being retried. Matching the `x401` prefix and decoded Verifier identifier is not sufficient. The Verifier MUST support at least one of the following Verifier Challenge correlation models: 1. **Stored Verifier Challenge state.** The nonce segment is an opaque handle to a Verifier Challenge record stored by the Verifier. The record MUST include or reference the route, method, Credential Query Requirement, policy, Verifier identifier, nonce, expiration time, expected Agent Identifier rules, accepted retry mechanisms, and replay status needed to evaluate the retry. 2. **Verifier-protected nonce state.** The nonce segment is a self-contained or referencing value protected by the Verifier with a signature, MAC, or authenticated encryption. It MUST allow the Verifier to authenticate the returned challenge value and reconstruct the expected route, method, Credential Query Requirement, policy, Verifier identifier, nonce, expiration time, expected Agent Identifier rules, and replay requirements. In both models, the nonce segment MUST contain or be bound to at least 128 bits of cryptographic randomness. The Agent treats the nonce segment as opaque and MUST NOT parse it. When validating a returned VP Artifact or OAuth token exchange request, the Verifier MUST use the nonce segment to recover or authenticate the expected Verifier Challenge context. It MUST reject the returned artifact if the returned challenge value is not exactly the issued or reconstructed challenge value, if the Verifier Challenge is expired, if the nonce is unknown or fails verifier-protected authentication, if the route or method does not match, or if replay policy fails. #### Verifier Challenge Format The `proof.challenge` object carries the value the Agent gives to the Wallet and the time after which the Verifier will reject it: ```json { "value": "x401:aHR0cHM6Ly9iYW5rLmV4YW1wbGUuY29t:uX7Vq3mZJH6MeN0qz2L7SQ", "expires_at": "2026-05-06T18:45:00Z" } ``` The canonical `value` is: ```text x401:: ``` The Verifier Challenge value has three colon-delimited segments: Name | Segment | Definition ---- | ------- | ---------- Prefix | `x401` | REQUIRED. Domain-separates x401 Verifier Challenge values from other nonce or challenge values a Wallet may receive. Verifier identifier | `` | REQUIRED. The Verifier identifier encoded as UTF-8 and then base64url without padding. The decoded value is typically an HTTPS origin, domain-based identifier, or DID controlled by the Verifier. Nonce | `` | REQUIRED. An opaque verifier-generated nonce segment encoded as base64url without padding. It MUST either identify stored Verifier Challenge state or carry verifier-protected Verifier Challenge state. It MUST contain or be bound to at least 128 bits of cryptographic randomness. The Verifier constructs the entire `value`, including the `x401:` prefix. The encoded verifier identifier and nonce MUST omit padding. The nonce segment MUST let the Verifier triangulate back to the exact requirements presented to the Agent, including route, method, Credential Query Requirement, policy, expiration, expected Agent Identifier rules, accepted retry mechanisms, and replay status. The payload does not duplicate the verifier identifier or nonce as separate fields. The Verifier MUST reject a presentation if the challenge value, embedded or expected verifier identifier, nonce, or expiration does not match the Verifier Challenge expected for the protected route. The Agent MUST use `proof.challenge.value` exactly as the OpenID4VP `nonce` in its Presentation Request. The Agent MUST NOT add the `x401:` prefix, re-encode, hash, trim, normalize, or transform this value before giving it to the Wallet. #### Stateless Continuation x401 deployments MAY make the interaction between the protected resource and the token endpoint stateless by making the challenge value nonce-state-encapsulating. In a stateless deployment: 1. The x401 payload carried in `PROOF-REQUIRED` contains only information the Agent needs to continue, such as the Credential Query Requirement, Verifier Challenge, issuer trust reference, token exchange metadata, and payment hints. 2. The nonce segment of the Verifier Challenge MUST carry or reference the verifier-protected Verifier Challenge state needed to validate a VP Artifact or issue a Verification Token. 3. A [[ref: Verification Token]], when issued, MUST be verifier-protected and carry or reference the route, policy, Agent binding, expiration, and satisfied requirements needed for later protected-route evaluation. 4. The protected resource server and token endpoint MAY be separate components if they share the keys, policies, or verification services needed to validate these artifacts. Stateless processing does not remove every need for storage. Verifiers MAY still keep server-side state for replay detection, token revocation, audit, rate limiting, or one-time Verifier Challenge enforcement. If a deployment requires strict one-time-use Verifier Challenges, it generally needs replay state shared by the components that accept returned challenge values. ### Credential Acquisition Guidance When a Verifier wants to disclose which issuers, authorities, roles, activities, credential schemas, or credential types can satisfy a proof requirement, it provides a `proof.issuers.trust_establishment_url`. That URL points to a [[ref: Issuer Trust List]]. The Agent MAY use the referenced document to discover candidate credentials or credential issuers, and the Wallet MAY use it to help select credentials that are likely to satisfy the Verifier. If the referenced Issuer Trust List identifies OpenID4VCI Credential Issuer Identifiers, Agents resolve those issuer identifiers using the OpenID4VCI issuer metadata discovery rules. See OpenID4VCI Section 12.2.2: . ### Payment Object When payment may also be required, a x401 payload MAY declare the existence of that additional payment requirement. This is to help avoid situations where the user is not willing or able to pay, but does not find out about the payment requirement until after they have already disclosed their credential(s), resulting in needless sharing of identity information without achieving the outcome the user intended. The payment object is informational and orchestration-oriented only. It does not replace `402 Payment Required`. The presence of a `payment` member does not create a distinct x401 proof flow. The Agent completes the same proof steps described in the base flow. If the Verifier accepts the proof but payment remains unsatisfied, the Verifier uses `402 Payment Required` and the selected payment protocol to complete payment before granting access. #### Example ```json { "required": true, "scheme_hint": "x402", "notes": "Payment is required after proof is satisfied." } ``` ::: warning payment hint warning There is good reason to include hints about payment requirements, but because it could result in replication of nearly everything 402-related protocols define, the payment hint has been constrained to a boolean to allow the community to drive how much payment requirement information to include. ::: #### Members Name | Definition ---- | ---------- `required` | OPTIONAL. Boolean indicating whether payment is additionally required. `scheme_hint` | OPTIONAL. A hint naming the expected payment protocol. `notes` | OPTIONAL. Human-readable notes. If proof is accepted but payment is still unsatisfied, the Verifier responds with `402 Payment Required` using the payment protocol indicated by the verifier. ## Agent-Generated Verifiable Presentation This leg defines how the Agent turns the x401 payload into a wallet-facing OpenID4VP Authorization Request. The Agent keeps the Verifier's Credential Query Requirement and Verifier Challenge value intact, uses the Verifier Challenge value as the OpenID4VP `nonce`, and identifies itself as the OpenID4VP client. ```json { "response_type": "vp_token", "client_id": "decentralized_identifier:did:web:agent.example", "nonce": "x401:aHR0cHM6Ly9iYW5rLmV4YW1wbGUuY29t:uX7Vq3mZJH6MeN0qz2L7SQ", "dcql_query": { "credentials": [ { "id": "financial_customer", "format": "jwt_vc_json" } ] }, "response_uri": "https://agent.example/wallet/callback/7c9e" } ``` When the x401 payload contains `proof.scope` instead of `proof.dcql_query`, the Agent uses that value as the OpenID4VP `scope` and does not include `dcql_query`: ```json { "response_type": "vp_token", "client_id": "decentralized_identifier:did:web:agent.example", "nonce": "x401:aHR0cHM6Ly9iYW5rLmV4YW1wbGUuY29t:uX7Vq3mZJH6MeN0qz2L7SQ", "scope": "financial_customer", "response_uri": "https://agent.example/wallet/callback/7c9e" } ``` ### OpenID4VP Boundary and Reuse x401 stays intentionally narrow. It defines the HTTP proof requirement at the protected route and the payload that carries proof requirements, Verifier Challenge material, issuer trust references, token exchange metadata, and payment hints. It does not define the Wallet transport or require the Verifier to become the Wallet-facing presentation requester. The protocol boundary is: 1. x401 governs the protected-route exchange that carries the `PROOF-REQUIRED` header containing the x401 payload. 2. The x401 payload supplies a Credential Query Requirement and Verifier Challenge. It is not a complete OpenID4VP Authorization Request and MUST NOT be treated as one. 3. The Agent creates the wallet-facing Presentation Request as an OpenID4VP Authorization Request. 4. The Agent's Presentation Request MUST include the exact x401 Credential Query Requirement as either `dcql_query` or `scope`, matching the query member present in the x401 payload, and MUST use the exact x401 Verifier Challenge value as `nonce`. 5. The Agent's Presentation Request MUST identify the Agent as the OpenID4VP client. 6. x401 resumes when the Agent retries the original protected route with a VP Artifact or Verification Token. 7. `proof.issuers` can help Agents discover acceptable issuers, but it never delegates verification behavior to the Agent. When the referenced Issuer Trust List identifies OpenID4VCI issuers, Agents resolve those issuers using OpenID4VCI issuer metadata discovery. If a deployment uses a Wallet, browser, device, or application that is separate from the Agent, that deployment MUST use an OpenID4VP response delivery mechanism that returns the presentation result to the Agent. The response delivery endpoint, such as an OpenID4VP `redirect_uri` or `response_uri`, MUST be controlled by the Agent. #### Agent-Created Presentation Request The Agent MUST provide the Wallet with a valid OpenID4VP Authorization Request. The Agent-created request MUST include at least: 1. a valid `response_type` for returning a verifiable presentation; 2. a `client_id` that identifies the Agent Identifier used with the protected-route request; 3. the exact x401 `proof.challenge.value` as the OpenID4VP `nonce`; 4. the exact x401 Credential Query Requirement as the OpenID4VP `dcql_query` or `scope`, matching the query member present in the x401 payload; 5. a `response_uri` or `redirect_uri` that delivers the Wallet's presentation result to the Agent; 6. any required `client_metadata`, key material, supported VP formats, encryption preferences, or request signing information required by the Wallet or by the OpenID4VP profile in use; 7. any Agent-generated `state` value needed for Agent-side correlation. The x401 payload does not contain a separate `presentation_request` object. The fixed request construction rules above are part of the protocol and are derived from `proof.presentation_protocol`; repeating them in each x401 payload would be redundant. Verifier-specific proof requirements should be expressed through the Credential Query Requirement, Verifier Challenge, OAuth metadata, or the Verifier's normal validation policy. The Agent MAY add Wallet UX metadata, request signing, encryption parameters, or stricter local handling requirements, but it MUST NOT weaken or omit the x401 Credential Query Requirement and MUST NOT alter the x401 Verifier Challenge value. The Verifier's identifier appears inside the Verifier Challenge. The Agent MUST NOT use the Verifier's identifier as the Wallet-facing `client_id` unless the Agent and Verifier are the same entity for that presentation transaction. ### OpenID4VP Request Construction Rules x401 Agents: 1. MUST create an OpenID4VP Authorization Request that is valid under OpenID4VP. 2. MUST preserve the exact OpenID4VP parameter names and MUST NOT define x401 aliases for `response_uri`, `redirect_uri`, `response_mode`, `nonce`, `state`, `dcql_query`, `scope`, or `client_metadata`. 3. MUST include an OpenID4VP `client_id` for the Agent. 4. MUST include a valid OpenID4VP `response_type` for the chosen flow. 5. MUST include exactly one OpenID4VP credential query parameter: `dcql_query` containing the exact x401 `proof.dcql_query` when present, or `scope` containing the exact x401 `proof.scope` when present. 6. MUST use `nonce` containing the exact x401 `proof.challenge.value`. 7. MUST arrange for the Wallet response to return to the Agent. 8. SHOULD use short expiry windows when a signed request object is used. 9. MAY use `request_uri` or signed request objects for the Agent-created Presentation Request, but those objects are created by the Agent and are not supplied by the x401 payload. ## Verifiable Presentation Delivery This phase of the protocol defines how the Agent packages the Wallet's presentation result and presents it back to the Verifier. The Agent either retries the protected route directly with a VP Artifact or uses that same VP Artifact in the OAuth token exchange described in the next leg. ```http POST /accounts/applications HTTP/1.1 Host: bank.example.com PROOF-PRESENTATION: ``` ### VP Artifact After the Wallet returns a presentation result, the Agent packages the result as a VP Artifact for protected-route retry or OAuth token exchange. #### General Structure ```json { "agent_id": "did:web:agent.example", "challenge": "x401:aHR0cHM6Ly9iYW5rLmV4YW1wbGUuY29t:uX7Vq3mZJH6MeN0qz2L7SQ", "request_id": "proof-template-financial-customer-v1", "vp_token": "", "presentation_submission": {} } ``` #### Members Name | Definition ---- | ---------- `agent_id` | REQUIRED. The Agent Identifier represented by the OpenID4VP `client_id` in the Agent-created Presentation Request. `challenge` | REQUIRED. The exact `proof.challenge.value` from the x401 payload. `request_id` | OPTIONAL. The x401 `proof.request_id`, when present. `vp_token` | REQUIRED. The verifiable presentation material returned by the Wallet. `presentation_submission` | OPTIONAL. The OpenID4VP presentation submission metadata returned by the Wallet when applicable. `state` | OPTIONAL. Agent-generated correlation state returned by the Wallet when applicable. The Verifier MUST NOT treat the `agent_id`, `challenge`, or `request_id` values inside the VP Artifact as authoritative by themselves. They are carried so the Verifier can correlate the presentation with its expected Verifier Challenge context. The Verifier remains responsible for authenticating the Agent Identifier and validating the presentation binding. ### x401 Error Object When a Verifier reports that an x401 proof presentation failed or could not be processed, it returns a `PROOF-RESPONSE` header whose payload is a base64url-encoded x401 Error Object. #### General Structure ```json { "scheme": "x401", "version": "0.1.0", "error": "invalid_presentation", "error_description": "The presentation did not satisfy the route proof requirement.", "request_id": "proof-template-financial-customer-v1", "challenge": "x401:aHR0cHM6Ly9iYW5rLmV4YW1wbGUuY29t:uX7Vq3mZJH6MeN0qz2L7SQ" } ``` #### Members Name | Definition ---- | ---------- `scheme` | REQUIRED. Value MUST be the string `"x401"`. `version` | REQUIRED. The x401 error object version. `error` | REQUIRED. A verifier-defined error code. The value SHOULD be a short ASCII token suitable for logs and programmatic handling. `error_description` | OPTIONAL. Human-readable diagnostic text. `error_uri` | OPTIONAL. HTTPS URL identifying documentation for the error. `request_id` | OPTIONAL. The x401 `proof.request_id`, when the error can be correlated to a proof template. `challenge` | OPTIONAL. The Verifier Challenge value, when the error can be safely correlated to a challenge. The x401 Error Object describes the x401 proof branch only. The HTTP status code continues to describe the overall response. ### x401 Token Object When an Agent has a Verification Token and the protected route already uses the `Authorization` request header for existing application authentication, the Agent can pass the Verification Token as an x401 Token Object in a `PROOF-PRESENTATION` header. This allows existing application credentials and x401 proof satisfaction to travel on the same request without overloading `Authorization`. #### General Structure ```json { "scheme": "x401", "version": "0.1.0", "token_type": "Bearer", "access_token": "" } ``` #### Members Name | Definition ---- | ---------- `scheme` | REQUIRED. Value MUST be the string `"x401"`. `version` | REQUIRED. The x401 token object version. `token_type` | REQUIRED. The token type of the supplied Verification Token. For Verification Tokens defined by this specification, the value is `"Bearer"`. `access_token` | REQUIRED. The opaque or structured [[ref: Verification Token]] value issued to the [[ref: Agent]]. The x401 Token Object carries proof satisfaction only. It does not replace the route's ordinary authorization credentials unless the deployment explicitly defines the Verification Token as an upgraded or replacement application token. ### Route Retry Headers After receiving an x401 proof requirement and obtaining a presentation result, the Agent retries the protected route with either direct proof material or a reusable Verification Token. When retrying with proof material directly, the Agent uses `PROOF-PRESENTATION`: ```http PROOF-PRESENTATION: ``` The `PROOF-PRESENTATION` value is the base64url-encoded UTF-8 JSON serialization of the VP Artifact, using the same no-padding encoding as the x401 payload. When retrying with a [[ref: Verification Token]], the Agent either uses the route's normal `Authorization` request header or carries an x401 Token Object in `PROOF-PRESENTATION`. A deployment MAY issue a Verification Token that is also the route's ordinary application authorization credential, or it MAY upgrade the caller's existing application access token with x401-specific proof satisfaction metadata. In that case, the Agent sends the token using the normal token type for the route. For Verification Tokens defined by this specification, the token type is `Bearer`: ```http Authorization: Bearer ``` If the protected route already uses `Authorization` for existing application authentication and the x401 Verification Token is separate from that application credential, the Agent preserves the existing `Authorization` value and sends the x401 token separately: ```http Authorization: Bearer PROOF-PRESENTATION: ``` A protected route MUST process the supplied `PROOF-PRESENTATION` value as a proof presentation or proof-token satisfaction attempt. If verification succeeds, the Verifier MAY return the protected resource directly. ### Agent Processing Rules An Agent receiving an HTTP response with a `PROOF-REQUIRED` proof requirement: 1. MUST treat the response as a proof requirement. 2. MUST extract the `PROOF-REQUIRED` field value and base64url-decode it as a UTF-8 JSON [[ref: x401 Payload]]. 3. MUST validate the decoded payload structure and process the `proof` object. 4. MUST verify that `proof.presentation_protocol` is `openid4vp`. 5. MUST create a wallet-facing OpenID4VP Presentation Request that includes the x401 Credential Query Requirement as `dcql_query` when `proof.dcql_query` is present or as `scope` when `proof.scope` is present. 6. MUST use the exact x401 `proof.challenge.value` as the Presentation Request's `nonce`. 7. MUST identify itself to the Wallet using an Agent Identifier that can also be bound to the protected-route retry. 8. MAY use `proof.issuers.trust_establishment_url`, when present, to filter candidate credentials or guide acquisition. 9. MUST NOT treat any Agent-side interpretation of the Issuer Trust List as proof of verifier acceptance. 10. MUST send the OpenID4VP Presentation Request to a Wallet or local presentation subsystem to fulfill the proof requirement. 11. MUST package the Wallet's presentation result as a VP Artifact. 12. MUST retry the same route that produced the x401 proof requirement with one of: - a VP Artifact in a `PROOF-PRESENTATION` request header, - a Verification Token carried as an x401 Token Object in a `PROOF-PRESENTATION` request header, or - a Verification Token in the route's normal `Authorization` request header when the deployment specifies that the token is an upgraded or replacement application authorization credential. 13. MUST NOT replace an existing application `Authorization` credential with an x401 Verification Token unless the deployment explicitly defines the returned token as valid for that route's ordinary authorization processing. 14. MUST treat a `PROOF-RESPONSE` carrying an x401 Error Object as an x401 proof failure for the route-scoped proof attempt, regardless of the HTTP status code. ### Verifier Processing Rules A Verifier implementing x401: 1. MUST include `PROOF-REQUIRED: ` when proof is required or advertised. 2. MUST include a valid base64url-encoded x401 payload in `PROOF-REQUIRED`. 3. MUST use an HTTP status code appropriate for the overall response and MUST NOT rely on the status code alone to convey x401 proof state. 4. MUST set `proof.presentation_protocol` to `openid4vp`. 5. MUST include exactly one Credential Query Requirement: either `proof.dcql_query` or `proof.scope`. 6. MUST include a Verifier Challenge in `proof.challenge`. 7. MUST include OAuth token exchange metadata in `proof.oauth`. 8. SHOULD provide `proof.issuers.trust_establishment_url` when issuer approval policy is useful to disclose to Agents. 9. MUST NOT enumerate verifier-approved issuers inline in the x401 payload. 10. MUST bind the Verifier Challenge to the route, method, policy, expiration, and expected nonce, and MUST either store or reconstruct enough Verifier Challenge context to recognize the challenge value on return. 11. MUST validate VP Artifacts according to the proof validation rules in this specification and the credential format rules it relies upon. 12. MUST evaluate issuer trust, status, revocation, and policy constraints independently of any Agent-side interpretation of the Issuer Trust List. 13. MUST accept a VP Artifact in a `PROOF-PRESENTATION` request header for protected-route retry. 14. MAY issue a Verification Token through the OAuth token endpoint after validating the presented VP Artifact. 15. MUST validate Verification Tokens on protected-route retry according to token scope, audience, expiration, Agent binding, and satisfied requirement metadata, whether the token arrives in `Authorization` or as an x401 Token Object in `PROOF-PRESENTATION`. 16. MUST bind any Verification Token carried in `PROOF-PRESENTATION` to the existing application caller, credential, client, key, or Agent Identifier required by the protected route when an `Authorization` header is also present. 17. SHOULD return `PROOF-RESPONSE: ` if proof is presented but policy satisfaction fails or the presentation or token cannot be processed. 18. MUST use `402 Payment Required` separately if payment is required and remains unsatisfied. ### Proof Validation When a Verifier receives a VP Artifact directly on a protected-route retry or through the OAuth token endpoint, it MUST validate the presentation using the route's expected x401 Verifier Challenge context. The Verifier MUST: 1. recover the expected Verifier Challenge context, including route, method, policy, Credential Query Requirement, Verifier identifier, nonce, challenge value, expiration, Agent Identifier rules, and replay requirements; 2. determine the Agent Identifier for the HTTP caller using the route's accepted Agent Identifier policy; 3. reject the presentation if it cannot bind the HTTP caller to an Agent Identifier acceptable for the route; 4. verify that the VP Artifact's `challenge` exactly matches the issued or reconstructed Verifier Challenge; 5. verify that the Wallet's presentation proof is cryptographically protected according to the credential and presentation formats in use; 6. verify that the presentation is bound to the authenticated Agent Identifier as its audience, client, or intended target; 7. verify that the presentation's OpenID4VP `nonce` equals the exact `proof.challenge.value` from the x401 payload; 8. verify that the credentials and disclosed claims satisfy the route's Credential Query Requirement; when the requirement is a `scope`, the Verifier MUST validate against the credential policy represented by that scope, not merely the presence of the scope value; 9. verify issuer trust, credential status, revocation, expiration, assurance, and any additional route policy; 10. enforce replay, one-time-use, and expiration requirements for the Verifier Challenge. The Verifier MUST NOT accept a presentation that is bound only to the Verifier unless the Verifier is also the Agent for the wallet-facing presentation transaction. In the default x401 model, the Wallet presents to the Agent, and the Verifier authorizes resource access by checking that the presentation was intended for the same Agent that is retrying the protected route. ## Access Token Acquisition This optional leg of the protocol defines the optional exchange of a verified VP Artifact for a reusable Verification Token. The Agent submits the VP Artifact to the OAuth token endpoint from the x401 payload using OAuth 2.0 Token Exchange, then retries protected routes with the returned token either as the route's normal authorization credential or as an x401 proof-satisfaction token object in `PROOF-PRESENTATION`. ```http POST /oauth/token HTTP/1.1 Host: bank.example.com Content-Type: application/x-www-form-urlencoded grant_type=urn:ietf:params:oauth:grant-type:token-exchange& subject_token_type=urn:x401:params:oauth:token-type:vp_artifact& subject_token= ``` ### OAuth Token Exchange An Agent MAY exchange a VP Artifact for a [[ref: Verification Token]] at the OAuth token endpoint supplied in `proof.oauth.token_endpoint`. The token request uses OAuth 2.0 Token Exchange. The Agent MUST use: - `grant_type=urn:ietf:params:oauth:grant-type:token-exchange` - `subject_token_type=urn:x401:params:oauth:token-type:vp_artifact` - `subject_token=` The Agent SHOULD include the `resource` or `audience` value from `proof.oauth` when present. If neither value is present, the Agent MAY use the original protected resource URL as the OAuth `resource` value. ```http POST /oauth/token HTTP/1.1 Host: bank.example.com Content-Type: application/x-www-form-urlencoded grant_type=urn:ietf:params:oauth:grant-type:token-exchange& subject_token_type=urn:x401:params:oauth:token-type:vp_artifact& subject_token=& resource=https%3A%2F%2Fbank.example.com%2Faccounts%2Fapplications ``` The token endpoint MAY require normal OAuth client authentication. If client authentication is used, the authenticated OAuth client MUST bind to the same Agent Identifier that appears as the intended audience of the Wallet presentation. The token endpoint MUST process the submitted VP Artifact using the same proof validation rules that apply to direct protected-route retry. In particular, it MUST verify that: 1. the VP is cryptographically valid and bound to the authenticated Agent Identifier; 2. the credential material satisfies the Credential Query Requirement for the requested resource; 3. the Verifier Challenge value contains or resolves to the token endpoint's Verifier identifier and the expected cryptographic nonce; 4. the Verifier Challenge is not expired, replayed, or outside the route, method, policy, or resource context requested. If verification succeeds and the Verifier chooses token retry, the token endpoint returns an OAuth-compatible successful access token response: ```http HTTP/1.1 200 OK Content-Type: application/json Cache-Control: no-store Pragma: no-cache ``` For Verification Tokens defined by this specification, the token endpoint MUST return `token_type` with the value `Bearer`. ```json { "access_token": "eyJhbGciOi...", "issued_token_type": "urn:ietf:params:oauth:token-type:access_token", "token_type": "Bearer", "expires_in": 300, "scope": "accounts:open", "x401": { "agent_id": "did:web:agent.example", "verifier_id": "https://bank.example.com", "request_id": "proof-template-financial-customer-v1", "satisfied_requirements": [ "urn:example:x401:satisfaction:financial-customer:v1" ], "resource": "https://bank.example.com/accounts/applications", "method": "POST", "challenge": "x401:aHR0cHM6Ly9iYW5rLmV4YW1wbGUuY29t:uX7Vq3mZJH6MeN0qz2L7SQ", "expires_at": "2026-05-06T18:50:00Z" } } ``` Name | Definition ---- | ---------- `access_token` | REQUIRED. The opaque or structured [[ref: Verification Token]] value issued to the [[ref: Agent]]. `issued_token_type` | RECOMMENDED. Token type of the issued token. For Bearer access tokens, use `urn:ietf:params:oauth:token-type:access_token`. `token_type` | REQUIRED. The HTTP authorization scheme the Agent uses with the token. The value defined by this specification is `Bearer`. `expires_in` | RECOMMENDED. Lifetime of the token in seconds from the time the response is generated. `scope` | OPTIONAL. OAuth scope string representing access authorized by the token. `x401` | RECOMMENDED. Object containing x401-specific token metadata that helps the Agent and Verifier understand what proof requirements were satisfied. #### Verification Token Retry Placement A Verification Token can be placed in either `Authorization` or `PROOF-PRESENTATION`, depending on how the deployment composes x401 with existing application authorization. If the Verifier, authorization server, and protected application are integrated, the token endpoint MAY issue a token that is accepted as the route's normal application authorization credential. This can be a new token or an upgraded version of an existing application token that includes x401-specific proof satisfaction metadata. In that model, the Agent sends the returned token through the normal authorization mechanism for the route: ```http Authorization: Bearer ``` If the protected route already requires an application token in `Authorization`, and the x401 Verification Token is separate from that application token, the Agent SHOULD preserve the existing `Authorization` value and send the Verification Token as an x401 Token Object in `PROOF-PRESENTATION`: ```http Authorization: Bearer PROOF-PRESENTATION: ``` When processing a `PROOF-PRESENTATION` value that carries an x401 Token Object, the Verifier MUST decode the x401 Token Object, validate the contained Verification Token, and evaluate whether the token satisfies the route's x401 proof requirement. If an `Authorization` header is also present, the Verifier MUST ensure that the application credential and the x401 Verification Token are bound to the same Agent Identifier, client, subject, proof-of-possession key, certificate, or other verifier-accepted caller binding for the route. A Verifier MUST reject a request that combines an application credential and x401 Verification Token that cannot be safely bound to the same accepted caller context. #### Verification Token Contents A [[ref: Verification Token]] records the Verifier's decision that a presentation satisfied an x401 proof requirement. It is not a credential, payment artifact, or new issuer attestation about the credential subject. A Verifier MAY issue a Verification Token after accepting a VP Artifact. The token: 1. MUST be issued to the Agent Identifier that was bound during proof validation; 2. MUST NOT rely on the credential subject as the token holder identity unless the credential subject is also the Agent; 3. MUST be scoped to the Verifier audience and to the route, policy, action, resource, or resource class for which proof was accepted; 4. MUST expire, and SHOULD be short-lived; 5. SHOULD include a unique token identifier and support replay detection and revocation; 6. SHOULD identify the `request_id` and `satisfied_requirements` accepted by the Verifier; 7. SHOULD identify the Verifier Challenge, Verifier identifier, resource, method, and proof time used to issue the token. When a Verification Token is represented as a JWT, its exact claim set is deployment-specific. The token SHOULD include: - `iss` identifying the Verifier or authorization server; - `sub` identifying the Agent; - `aud` identifying the protected resource server or Verifier audience; - `exp`, `iat`, and `jti`; - `client_id` identifying the Agent when useful for OAuth infrastructure; - `scope`, `resource`, or method/action claims used for access decisions; - `x401_request_id`; - `x401_satisfied_requirements`; - `x401_challenge` or a digest of the challenge value; - `x401_query_hash` or a verifier-defined reference to the Credential Query Requirement that was satisfied. #### Reuse Across Routes OpenID4VP `state`, presentation `nonce`, DCQL Credential Query `id` values, and OpenID4VP `scope` values are useful for request-response correlation, holder binding, or wallet-facing query selection inside a single presentation transaction. They are not, by themselves, stable semantic identifiers for cross-route token reuse. x401 uses `proof.request_id` and `proof.satisfied_requirements` for reusable proof semantics. A Verifier MAY accept a Verification Token issued for one route on another route only when: 1. the token is valid for the Verifier audience and current protected resource; 2. the token has not expired or been revoked; 3. the token is issued to the current Agent Identifier; 4. the token's accepted proof requirements cover the later route's `proof.satisfied_requirements`; 5. any freshness, status, assurance, and policy constraints still hold. Agents MAY use the `x401.satisfied_requirements` metadata returned with a Verification Token to decide whether to try the token on a later route. The Verifier remains authoritative and SHOULD return a new x401 proof requirement when the token is valid but does not satisfy the later route. ## Examples ### Example 1: Proof Requirement #### Initial Request ```http POST /accounts/applications HTTP/1.1 Host: bank.example.com ``` #### Response ```http HTTP/1.1 401 Unauthorized PROOF-REQUIRED: Cache-Control: no-store ``` Decoded x401 payload, shown for readability: ```json { "scheme": "x401", "version": "0.1.0", "proof": { "presentation_protocol": "openid4vp", "dcql_query": { "credentials": [ { "id": "financial_customer", "format": "jwt_vc_json", "meta": { "type_values": ["FinancialCustomerCredential"] }, "claims": [ { "path": ["credentialSubject", "assurance_level"], "values": ["VC-AL2", "VC-AL3"] } ] } ] }, "challenge": { "value": "x401:aHR0cHM6Ly9iYW5rLmV4YW1wbGUuY29t:uX7Vq3mZJH6MeN0qz2L7SQ", "expires_at": "2026-05-06T18:45:00Z" }, "oauth": { "token_endpoint": "https://bank.example.com/oauth/token" }, "issuers": { "trust_establishment_url": "https://bank.example.com/.well-known/x401/trust/financial-customer-v1" }, "request_id": "proof-template-financial-customer-v1", "satisfied_requirements": [ "urn:example:x401:satisfaction:financial-customer:v1" ] } } ``` #### Agent-Created Presentation Request Fragment The Agent creates an OpenID4VP Authorization Request. The fragment below shows the x401-required OpenID4VP fields; deployments add the remaining fields required by the selected OpenID4VP response mode, client identifier scheme, and signing or encryption profile. ```json { "response_type": "vp_token", "client_id": "decentralized_identifier:did:web:agent.example", "nonce": "x401:aHR0cHM6Ly9iYW5rLmV4YW1wbGUuY29t:uX7Vq3mZJH6MeN0qz2L7SQ", "dcql_query": { "credentials": [ { "id": "financial_customer", "format": "jwt_vc_json", "meta": { "type_values": ["FinancialCustomerCredential"] }, "claims": [ { "path": ["credentialSubject", "assurance_level"], "values": ["VC-AL2", "VC-AL3"] } ] } ] }, "response_uri": "https://agent.example/wallet/callback/7c9e" } ``` #### Scope-Based Payload Variant The same route can express the credential query using `proof.scope` when the value is defined as an OpenID4VP scope query by an applicable profile, Wallet configuration, or verifier policy recognized by the Wallet and Verifier: ```json { "scheme": "x401", "version": "0.1.0", "proof": { "presentation_protocol": "openid4vp", "scope": "financial_customer", "challenge": { "value": "x401:aHR0cHM6Ly9iYW5rLmV4YW1wbGUuY29t:uX7Vq3mZJH6MeN0qz2L7SQ", "expires_at": "2026-05-06T18:45:00Z" }, "oauth": { "token_endpoint": "https://bank.example.com/oauth/token" }, "issuers": { "trust_establishment_url": "https://bank.example.com/.well-known/x401/trust/financial-customer-v1" }, "request_id": "proof-template-financial-customer-v1", "satisfied_requirements": [ "urn:example:x401:satisfaction:financial-customer:v1" ] } } ``` The Agent-created OpenID4VP Authorization Request carries the same `scope` value and omits `dcql_query`: ```json { "response_type": "vp_token", "client_id": "decentralized_identifier:did:web:agent.example", "nonce": "x401:aHR0cHM6Ly9iYW5rLmV4YW1wbGUuY29t:uX7Vq3mZJH6MeN0qz2L7SQ", "scope": "financial_customer", "response_uri": "https://agent.example/wallet/callback/7c9e" } ``` #### Successful Retry With VP Artifact ```http POST /accounts/applications HTTP/1.1 Host: bank.example.com PROOF-PRESENTATION: ``` The decoded VP Artifact contains the Wallet's `vp_token`, the Agent Identifier, and the x401 Verifier Challenge correlation values. #### Failed Retry With x401 Error ```http HTTP/1.1 401 Unauthorized PROOF-RESPONSE: Cache-Control: no-store ``` The decoded x401 Error Object describes the failed proof presentation. The `401 Unauthorized` status in this example means the route was not completed because the x401 proof branch failed. ### Example 2: OAuth Token Exchange After receiving the Wallet presentation result, the Agent may exchange the VP Artifact for a token. ```http POST /oauth/token HTTP/1.1 Host: bank.example.com Content-Type: application/x-www-form-urlencoded grant_type=urn:ietf:params:oauth:grant-type:token-exchange& subject_token_type=urn:x401:params:oauth:token-type:vp_artifact& subject_token=& resource=https%3A%2F%2Fbank.example.com%2Faccounts%2Fapplications ``` If verification succeeds, the token endpoint returns: ```json { "access_token": "eyJhbGciOi...", "issued_token_type": "urn:ietf:params:oauth:token-type:access_token", "token_type": "Bearer", "expires_in": 300, "scope": "accounts:open", "x401": { "agent_id": "did:web:agent.example", "verifier_id": "https://bank.example.com", "request_id": "proof-template-financial-customer-v1", "satisfied_requirements": [ "urn:example:x401:satisfaction:financial-customer:v1" ], "resource": "https://bank.example.com/accounts/applications", "method": "POST" } } ``` If the returned token is an upgraded or replacement application authorization credential, the Agent retries the original protected route with the token in `Authorization`: ```http POST /accounts/applications HTTP/1.1 Host: bank.example.com Authorization: Bearer eyJhbGciOi... ``` If the application already requires an existing `Authorization` token and the x401 Verification Token is separate, the Agent preserves the application token and carries the x401 Verification Token as an x401 Token Object in `PROOF-PRESENTATION`: ```http POST /accounts/applications HTTP/1.1 Host: bank.example.com Authorization: Bearer PROOF-PRESENTATION: ``` ## Composable Agent and Entity Identification x401 requires the Verifier to bind the Wallet presentation to the Agent Identifier used by the HTTP caller, but it does not define a single global agent identity system. Deployments MAY layer additional mechanisms over x401 to authenticate the calling agent, bind an entity identity to the HTTP request, sender-constrain a Verification Token, or carry delegation evidence from a user, organization, workload, or upstream agent. These mechanisms compose cleanly with x401 when they: 1. produce an authenticated caller identifier the Verifier can map to the route's accepted Agent Identifier policy; 2. bind that identifier to the HTTP request being evaluated, including the method, target URI or authority, freshness values, and relevant x401 retry material; 3. can be verified before or during x401 proof validation; 4. do not alter the x401 Credential Query Requirement, Verifier Challenge, VP Artifact, or `402 Payment Required` boundary; 5. allow the Verifier to reject mismatches between the authenticated caller, the OpenID4VP `client_id`, the VP Artifact `agent_id`, and any Verification Token holder identity. ### Web Bot Auth and HTTP Message Signatures Web Bot Auth is a natural option for adding request-bound identification to x401. A calling Agent can sign the initial protected-route request, the retry request carrying a VP Artifact or Verification Token, and the OAuth token exchange request using HTTP Message Signatures. The Agent can also use the `Signature-Agent` header to point the Verifier to an HTTP Message Signatures key directory. When layered over x401, a Web Bot Auth signature SHOULD cover the method and target, the authority or host, the `Signature-Agent` header when present, the `PROOF-PRESENTATION` header when retrying with direct proof or proof-token material, the `Authorization` header when retrying with application or upgraded token material, and `Content-Digest` when the request has a body. The signature SHOULD include short-lived freshness metadata such as `created`, `expires`, and a replay-resistant `nonce`. A Verifier MAY use the validated signing key, key directory authority, or derived service identity as the Agent Identifier, or as evidence that maps to an Agent Identifier. The Verifier MUST still validate the x401 Verifier Challenge, Wallet presentation binding, Credential Query Requirement satisfaction, issuer trust, token scope, and payment boundary. Web Bot Auth identifies the calling automation or service; it does not by itself prove the credential subject, satisfy the Credential Query Requirement, or prove end-user delegation. ### OAuth Proof-of-Possession and Client Authentication Deployments that use the OAuth token exchange leg MAY require additional OAuth client authentication at `proof.oauth.token_endpoint`. Mutual TLS client authentication and certificate-bound access tokens can bind the token request, and later token use, to a certificate controlled by the Agent. This works well when the Agent Identifier is a certificate-bound identifier, domain-bound client identifier, SPIFFE ID, or other identifier that the Verifier can map from the TLS client certificate. DPoP can bind OAuth token requests and resource requests to an Agent-controlled key at the application layer. Because this version of x401 defines Bearer Verification Tokens, a DPoP-bound Verification Token retry needs either a deployment-specific profile or a future x401 token retry profile that permits `token_type: DPoP` and the `DPoP` proof header. Even when DPoP is used only at the token endpoint, the endpoint MUST ensure the DPoP key maps to the same Agent Identifier that the Wallet presentation targets. ### Workload Identity In service-to-service deployments, the Agent may be a workload rather than a user-facing application. The Agent Identifier can be derived from a workload identity mechanism such as a SPIFFE ID carried in an X.509-SVID or JWT-SVID, or from WIMSE work on workload identity tokens and HTTP Message Signatures. These mechanisms are useful for infrastructure agents, internal tools, and managed compute environments where the Verifier needs to know which deployed workload made the request. Workload identity proves the caller's operational identity. It does not prove that the caller holds the requested credential, that the credential subject is authorized, or that an end user delegated authority to the Agent. Those remain x401 proof validation and policy questions. ### DID, Signed Request Objects, and Wallet-Facing Client Binding An Agent MAY use a DID, HTTPS origin, domain-bound client identifier, certificate-bound identifier, or other verifier-approved scheme as its OpenID4VP `client_id`. If the Agent signs its OpenID4VP request object, that signature helps the Wallet bind the presentation to the Agent's key and metadata. This wallet-facing binding is necessary but not always sufficient. The Verifier also needs an HTTP-layer or token-layer way to determine that the protected-route caller is the same Agent Identifier the Wallet presentation targeted. Deployments can accomplish this by using the same key, a linked key, or a verifier-recognized mapping across the OpenID4VP client identifier, HTTP Message Signature identity, mutual TLS certificate, DPoP key, workload identity, or Verification Token holder identity. ### Delegation and Actor Evidence Some deployments need to know not only which Agent made the request, but who or what authorized that Agent to act. Delegation evidence can be carried as an additional credential, a VP disclosed through the x401 Credential Query Requirement, an OAuth Token Exchange actor chain, a GNAP grant artifact, a Verifiable Intent credential, or another signed mandate or capability. Delegation evidence composes best when it is scoped, time-limited, replay-resistant, and bound to the Agent Identifier and requested resource or action. It does not replace caller authentication: the Verifier still needs to know which Agent is presenting the delegation evidence and whether that Agent is the one authorized by the evidence. ## Consumer Client Compatibility ::: note Experimental The mechanism in this section is an early experiment in adapting x401 to consumer AI clients and other body-only consumers of HTTP responses. The core x401 protocol — as defined in the preceding sections — is the standard, straightforward way to convey proof requirements: a Verifier returns a `PROOF-REQUIRED` header, and the Agent reads, decodes, and acts on it. The pattern described below is offered as a compatibility supplement for content-bearing responses where header-driven discovery is not viable, and the community is invited to contribute proposals, examples, and reference implementations that improve how x401 reaches these clients. ::: x401 is fundamentally an HTTP header protocol. A conforming Verifier signals proof requirements through `PROOF-REQUIRED`, and a conforming Agent processes that header. There is, however, a meaningful class of consumers that today cannot reliably access HTTP response headers on a successful (`2xx`) response with a body. A Verifier that wants its gating requirements to reach those consumers — most notably consumer-facing AI assistants — MAY emit a body-embedded form of the same x401 payload in addition to whatever it carries in `PROOF-REQUIRED`. ### Motivation Several common client classes do not surface `PROOF-REQUIRED` on a successful response with a body: - Consumer-facing AI assistants from major platforms typically fetch web content through summarization or browsing tools that surface the response body to the model but drop, hide, or do not propagate the HTTP headers of `2xx` responses. As a result, a proof requirement carried only in `PROOF-REQUIRED` on a successful HTML response will not reach the model. - HTML documents rendered in a browser do not expose response headers to inline content unless application code reads them through JavaScript and re-injects them into the DOM. - Archival, syndication, and feed-rendering tools often store or render the body without preserving headers. A Verifier that wants its gating requirements to be discoverable by these consumer AI flows or other body-only clients SHOULD strongly consider embedding the proof requirement in the response body using the form defined below, in addition to setting `PROOF-REQUIRED` on the same response. ### Embedded Proof Requirements in HTML Content On a non-`401` HTML response, the Verifier MAY emit each advertised x401 proof requirement as a single HTML `` element placed in the document body at, near, or wrapping the content to which the requirement applies. The element: 1. MUST use the tag name `data`. 2. MUST set the `value` attribute to the MIME-type expression `application/json;x401=proof-required`. The `x401` parameter identifies the embedded carrier and signals the role of the element's text content. 3. MUST set the `hidden` attribute so the element is not visually rendered. 4. MUST contain a single JSON object as its text content. The JSON object MUST be a valid x401 payload as defined in [x401 Payload](#x401-payload), and MUST include a `$schema` member whose value is the JSON Schema URL for the x401 request object, `https://x401.id/spec/schemas/request.json`. The `$schema` member is an informational marker that allows AI scrapers, content processors, and validators that retain only the JSON object to recognize it as an x401 proof requirement without prior knowledge of the surrounding HTML carrier. ```html ``` Unlike the `PROOF-REQUIRED` header value, the embedded form is the unencoded JSON object. The `` element is already a text container, and the `$schema` member is intended to be directly readable by content processors that retain the object. ### Placement and Scope A `` element placed at the document level applies to the page as a whole and SHOULD be used as a body-side mirror of the route-scoped `PROOF-REQUIRED` header so that header-blind clients can still discover the requirement. A response MAY include multiple `