From c7b1bf732307e1bb286d9eb2d1cff946e671fea8 Mon Sep 17 00:00:00 2001 From: Fabrice Rochette <45168870+mjfelis@users.noreply.github.com> Date: Fri, 26 Jun 2026 16:38:12 -0500 Subject: [PATCH] feat(spec): add trqp_v2 (ToIP TRQP v2.0) trusted_authorities type (#1) * feat(spec): add trqp_v2 (ToIP TRQP v2.0) trusted_authorities type Add a dereferenceable trqp_v2 entry type to the DCQL trusted_authorities enumeration in Credential Acquisition Guidance, alongside openid_federation, etsi_tl, and aki. The entry has a REQUIRED endpoint (TRQP v2.0 registry base URL) and an OPTIONAL values array of authority_ids; when values is omitted the authority is derived from a registry-unique resource. Verifiers MAY enforce issuer trust directly via POST /authorization (authorized: true). Also reference trqp_v2 in the overview, design-rationale, Agent/Verifier processing rules, and Issuer Trust sections. Payment Object untouched. * feat(spec): anchor trqp_v2 on DID-fragment service reference Encode each trqp_v2 value as a DID URL whose fragment references a service entry in the authority's DID Document (per DID v1.1 #fragment), e.g. did:example:health-authority#trqp. The client resolves the DID and dereferences the fragment to the service's serviceEndpoint (the TRQP v2.0 registry base), then POSTs /authorization with the bare DID as authority_id. This anchors trust on the authority's DID (verifiable, rotatable, self-published endpoint) rather than a bare URL, and avoids depending on a non-standardized DID service type by selecting the service by its id (fragment) per DID Core. endpoint is demoted to an OPTIONAL pin for authorities without a resolvable DID. Payment Object untouched. * refactor(spec): trqp_v2 is plain {type, values}, no endpoint member A trqp_v2 value is a self-contained DID URL (did:authority#trqp), so the type needs no type-specific member. Revert the trusted_authorities preamble to its original { type, values } wording and drop the OPTIONAL endpoint pin from the trqp_v2 bullet. Payment Object untouched. --- spec.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/spec.md b/spec.md index fbb889b..cb570c1 100644 --- a/spec.md +++ b/spec.md @@ -44,7 +44,7 @@ x401 uses: - the **W3C Digital Credentials API (DC API)** request shape as the carrier for the Verifier-composed presentation request, so the request can be executed by native credential wallet/handler methods or relayed to other wallets and remote services - **OpenID for Verifiable Presentations (OpenID4VP)** over the DC API for the composed presentation request, using **Digital Credentials Query Language (DCQL)** to describe the credential requirements - **OAuth 2.0** for optional exchange of a verified presentation for an access token -- the **DCQL `trusted_authorities`** the Verifier already places inside the request as the authoritative issuer constraint, and — for its dereferenceable types (`openid_federation`, `etsi_tl`) — as the acquisition and discovery hint an Agent can follow to find where qualifying credentials are issued +- the **DCQL `trusted_authorities`** the Verifier already places inside the request as the authoritative issuer constraint, and — for its dereferenceable types (`openid_federation`, `etsi_tl`, `trqp_v2`) — as the acquisition and discovery hint an Agent can follow to find where qualifying credentials are issued - **OpenID for Verifiable Credential Issuance (OpenID4VCI)** for resolving credential issuer metadata when the request's `trusted_authorities` resolve to OpenID4VCI issuers The x401 payload carries a composed, valid Digital Credentials request authored by the Verifier. The Verifier authors the request and, in the RECOMMENDED signed mode, is its relying party. The request is carried in the `digital` member of the payload's `credential_requirements` — itself a `CredentialRequestOptions` value usable directly as the argument to native credential wallet/handler methods (`navigator.credentials.get(payload.credential_requirements)`) — so it can be executed directly, relayed to another web wallet or remote presentation service, or handed off for fully remote generation. This version of x401 specifies only the `digital` member; the container leaves room for other `navigator.credentials.get()` request types in future versions. The payload's other top-level members — such as OAuth token exchange metadata and reusable requirement identifiers — carry x401-specific values that are not yet expressible inside a native Digital Credentials request. @@ -78,7 +78,7 @@ x401 fills that gap by defining an HTTP-native wrapper that: - recommends a signed OpenID4VP request for the DC API — making the Verifier the relying party and binding the request to the Verifier independently of which surface invokes it — while allowing an unsigned request when the request must be fulfilled at an invocation origin the Verifier cannot declare in advance - lets the [[ref: Agent]] execute the request through native credential methods, relay it to another wallet or remote service, or hand it off for fully remote generation and then acquire the result - includes OAuth token exchange metadata for Agents that want a reusable access token after proving -- lets Agents discover where to acquire qualifying credentials by following the dereferenceable issuer pointers (`openid_federation`, `etsi_tl`) the Verifier already places in the request's DCQL `trusted_authorities`, without adding an x401-specific issuer list +- lets Agents discover where to acquire qualifying credentials by following the dereferenceable issuer pointers (`openid_federation`, `etsi_tl`, `trqp_v2`) the Verifier already places in the request's DCQL `trusted_authorities`, without adding an x401-specific issuer list - composes with, but does not subsume, payment protocols In the typical flow, an [[ref: Agent]] receives an x401 proof requirement from a [[ref: Verifier]], obtains a presentation for the Verifier-composed [[ref: Presentation Request]] — by invoking the request through native credential methods, relaying it to a [[ref: Wallet]], or acquiring a remotely generated result — and retries the original protected route with the presentation result inline or by reference. The dereferenceable issuer pointers in the request's DCQL `trusted_authorities` can help the Agent discover where to acquire qualifying credentials, but the Verifier remains authoritative for issuer trust enforcement. @@ -552,11 +552,12 @@ Each `trusted_authorities` entry is a `{ "type": ..., "values": [...] }` object. - **`openid_federation`** — each value is an HTTPS Entity Identifier. The Agent resolves it as an OpenID Federation entity by fetching `/.well-known/openid-federation`. If the entity's `metadata` carries an `openid_credential_issuer` entry, that entity is itself a credential issuer and its OpenID4VCI metadata is published in-band. If the entity is a federation anchor or intermediate, the Agent walks the subordinate listing and trust chain to find subordinate entities that publish `openid_credential_issuer` metadata, validating the chain back to the anchor named in `values`. This is the fully resolvable path from issuer constraint to issuance endpoint. - **`etsi_tl`** — each value identifies an ETSI TS 119 612 Trusted List. The Agent MAY fetch and parse the list, enumerate the trust service providers and services whose service type matches the required credential, and follow a service's supply point to locate an issuer. Whether a listed service exposes an OpenID4VCI issuance endpoint is an ecosystem convention, not a guarantee of this format, so acquisition from `etsi_tl` is best-effort. +- **`trqp_v2`** — each value is a DID URL whose **fragment** references a service entry in the authority's DID Document (per [DID v1.1](https://www.w3.org/TR/did-1.1/#fragment)), e.g. `did:example:health-authority#trqp`. The client resolves the DID and dereferences the fragment to that service's `serviceEndpoint` — the base of a [ToIP TRQP v2.0](https://trustoverip.github.io/tswg-trust-registry-protocol/approved/) registry — then `POST`s `/authorization` (or `/recognition`) with the bare DID as `authority_id`, the issuer as `entity_id`, and `action`/`resource` in the JSON body, then drives OpenID4VCI against an authorized issuer. Anchoring on the authority's **DID** rather than a bare URL means the endpoint is self-published and key-controlled by the authority; the authority MAY instead be derived from the credential (issuer → schema credential → authority). Unlike the other types, TRQP returns an authoritative yes/no rather than only listing issuers; whether an authorized issuer exposes OpenID4VCI is an ecosystem convention, so acquisition is best-effort. - **`aki`** — each value is an Authority Key Identifier: an opaque issuer key identifier with no resolution mechanism. It constrains acceptable issuers but provides no acquisition pointer. For a request whose only issuer constraint is `aki`, x401 defines no native acquisition path; the Agent must already hold a qualifying credential or resolve the issuer out of band. When an entry resolves to one or more OpenID4VCI Credential Issuer Identifiers, the Agent resolves issuer metadata using the OpenID4VCI issuer metadata discovery rules and drives issuance against the discovered issuer. See OpenID4VCI Section 12.2.2: . -Acquisition guidance is advisory and never decides validity. The Verifier enforces issuer trust during proof validation against the `trusted_authorities` in the request it issued and its own policy, independently of how — or whether — the Agent used these pointers to acquire a credential. +Acquisition guidance is advisory and never decides validity. The Verifier enforces issuer trust during proof validation against the `trusted_authorities` in the request it issued and its own policy, independently of how — or whether — the Agent used these pointers to acquire a credential. For a `trqp_v2` entry, the Verifier MAY enforce directly: resolve the authority DID, dereference the value's fragment to the registry `serviceEndpoint`, `POST` `/authorization` (issuer as `entity_id`, the bare DID as `authority_id`, plus `action`/`resource`), and accept the issuer only on `authorized: true` — making `trqp_v2` the one `trusted_authorities` type that is authoritative for enforcement, not just an acquisition hint. ### Payment Object @@ -834,7 +835,7 @@ An Agent receiving an HTTP response with a `PROOF-REQUIRED` proof requirement: 4. MUST treat `credential_requirements` as the Verifier-composed credential request and MUST NOT modify any of its entries. 5. MUST obtain a [[ref: Presentation Result]] for `credential_requirements` by invoking it through a native credential method, relaying it to a Wallet or remote service, or acquiring a remotely generated result. 6. MAY select among multiple `credential_requirements.digital.requests` entries by protocol or supported credential format when more than one is present. -7. MAY use the dereferenceable issuer pointers in the request's DCQL `trusted_authorities` (`openid_federation`, `etsi_tl`) to filter candidate credentials or guide acquisition; see [Credential Acquisition Guidance](#credential-acquisition-guidance). +7. MAY use the dereferenceable issuer pointers in the request's DCQL `trusted_authorities` (`openid_federation`, `etsi_tl`, `trqp_v2`) to filter candidate credentials or guide acquisition; see [Credential Acquisition Guidance](#credential-acquisition-guidance). 8. MUST NOT treat any Agent-side interpretation of the request's `trusted_authorities` as proof of verifier acceptance. 9. MUST package the presentation result as a VP Artifact, inline or as a [[ref: Presentation Reference]]. 10. MUST retry the same route that produced the x401 proof requirement with one of: @@ -854,7 +855,7 @@ A Verifier implementing x401: 4. MUST include a `credential_requirements` whose `digital` member's entries are valid OpenID4VP requests for the DC API, using `openid4vp-v1-signed` (RECOMMENDED) or `openid4vp-v1-unsigned`. 5. SHOULD use signed requests and set their `client_id` and `expected_origins`; when using unsigned requests, MUST account for the weaker binding described in [Verifier Binding](#verifier-binding). 6. MUST include OAuth token exchange metadata in `oauth`. -7. SHOULD express its accepted issuers as DCQL `trusted_authorities` inside the request, preferring dereferenceable types (`openid_federation`, `etsi_tl`) when it wants Agents to be able to discover acquisition paths. +7. SHOULD express its accepted issuers as DCQL `trusted_authorities` inside the request, preferring dereferenceable types (`openid_federation`, `etsi_tl`, `trqp_v2`) when it wants Agents to be able to discover acquisition paths. 8. MUST NOT enumerate verifier-approved issuers inline as an x401-specific payload member. 9. MUST validate presentations according to the proof validation rules in this specification and the credential format rules it relies upon, dereferencing a [[ref: Presentation Reference]] when one is supplied. 10. MUST evaluate issuer trust, status, revocation, and policy constraints independently of any Agent-side interpretation of the request's `trusted_authorities`. @@ -1373,7 +1374,7 @@ A `presentation_uri` is a capability-style reference: possession of the URL is e Acquisition hints MUST NOT be treated as sufficient trust material. Verifiers MUST apply their own trusted issuer policy and validation logic. -The issuer constraints that must hold at presentation time belong in the request's DCQL `trusted_authorities`. An Agent MAY follow the dereferenceable entries (`openid_federation`, `etsi_tl`) to discover where to acquire a qualifying credential, but that resolution is an acquisition and discovery aid; it does not decide validity. The Verifier MUST independently validate that presented credentials were issued by parties approved under the request it issued and its own issuer policy, and MUST NOT rely on the Agent's interpretation of `trusted_authorities`. Because an `aki` entry is not dereferenceable, a request constrained only by `aki` offers no acquisition path; this does not weaken enforcement, which depends on the constraint, not on its resolvability. +The issuer constraints that must hold at presentation time belong in the request's DCQL `trusted_authorities`. An Agent MAY follow the dereferenceable entries (`openid_federation`, `etsi_tl`, `trqp_v2`) to discover where to acquire a qualifying credential, but that resolution is an acquisition and discovery aid; it does not decide validity. The Verifier MUST independently validate that presented credentials were issued by parties approved under the request it issued and its own issuer policy, and MUST NOT rely on the Agent's interpretation of `trusted_authorities`. Because an `aki` entry is not dereferenceable, a request constrained only by `aki` offers no acquisition path; this does not weaken enforcement, which depends on the constraint, not on its resolvability. ### Proof Presentation and Token Retry