We built an independent implementation of the x401 Verifier side and have it running publicly at
https://auth.solidus.network/v1/x401/demo. Opening that URL in a browser renders the requirement;
an agent reads the same requirement from the PROOF-REQUEST header. It is layered over an OpenID4VP
verifier we already ran, which is what made the specification quick to adopt.
Coverage is the Verifier conformance list except issuer expression: the header exchange, signed
request objects with client_id and expected_origins, credential result validation through the
OpenID4VP verifier, both inline and by-reference Result Artifacts, Verification Tokens with the
token exchange, caller binding, and the error object. Scope and gaps are stated on the page itself.
Three things from building it.
1. The protocol enum diverges from the W3C DC API table.
Section "Request Members" says the protocol value MUST be openid4vp-v1-signed (RECOMMENDED) or
openid4vp-v1-unsigned. The W3C Digital Credentials API now normatively enumerates a fixed table of
presentation protocols which additionally contains openid4vp-v1-multisigned, and states that user
agents must support every listed presentation protocol. As written, an x401 payload cannot express a
protocol that browsers are required to implement.
We kept our enum to the two values the specification allows rather than widening it. Is
openid4vp-v1-multisigned intended to be added, or deliberately excluded?
2. Issuer trust for DID-based issuers, which connects to #20.
Our issuers are DIDs with no X.509 chain, and none of DCQL's three trusted_authorities types can
name them: aki wants an RFC 5280 AuthorityKeyIdentifier, etsi_tl requires a certificate in the
trust chain, and openid_federation requires a federation entity. We therefore omit the member,
which is conformant because expressing it is a SHOULD.
The cost is specific to x401 rather than to OpenID4VP generally: because x401 reuses
trusted_authorities as the agent's acquisition and discovery surface, a DID-based Verifier has no
way to tell an agent where to obtain a credential it would accept. On #20 the note was that a general
stance on trusted authority list types would be helpful. There is an open OpenID4VP issue on exactly
this, openid/OpenID4VP#494, and we have added our implementer data to it.
3. Consumer Client Compatibility works, and it solved a real problem.
We implemented the body-embedded form, since that section invites contributions. The concrete
motivation turned out to be immediate rather than theoretical: our Verifier originally answered the
protected route with 401 and an empty body, which is protocol-honest and renders as a broken page to
any human who opens the link. Moving to 200 with an explanatory body plus the hidden
<data value="application/json;x401=proof-required" hidden> mirror fixed that in one change, and an
agent reading the header is unaffected. Content is negotiated, so a non-browser client gets compact
JSON instead of the page.
One implementation note in case it is worth a sentence in that section: we found it important that
the mirror be treated strictly as disclosure. A 200 status makes it easy to accidentally serve the
protected representation alongside the requirement, so we have a test asserting the challenge body is
never the representation.
Finally: is there a public list of implementations? We would like to be counted if so. We have pinned
Appendix A, both Result Artifact forms, the Token Object and the Error Object as test fixtures, and
we would rather they matched an agreed set than only our own reading of the text.
We built an independent implementation of the x401 Verifier side and have it running publicly at
https://auth.solidus.network/v1/x401/demo. Opening that URL in a browser renders the requirement;
an agent reads the same requirement from the
PROOF-REQUESTheader. It is layered over an OpenID4VPverifier we already ran, which is what made the specification quick to adopt.
Coverage is the Verifier conformance list except issuer expression: the header exchange, signed
request objects with
client_idandexpected_origins, credential result validation through theOpenID4VP verifier, both inline and by-reference Result Artifacts, Verification Tokens with the
token exchange, caller binding, and the error object. Scope and gaps are stated on the page itself.
Three things from building it.
1. The protocol enum diverges from the W3C DC API table.
Section "Request Members" says the
protocolvalue MUST beopenid4vp-v1-signed(RECOMMENDED) oropenid4vp-v1-unsigned. The W3C Digital Credentials API now normatively enumerates a fixed table ofpresentation protocols which additionally contains
openid4vp-v1-multisigned, and states that useragents must support every listed presentation protocol. As written, an x401 payload cannot express a
protocol that browsers are required to implement.
We kept our enum to the two values the specification allows rather than widening it. Is
openid4vp-v1-multisignedintended to be added, or deliberately excluded?2. Issuer trust for DID-based issuers, which connects to #20.
Our issuers are DIDs with no X.509 chain, and none of DCQL's three
trusted_authoritiestypes canname them:
akiwants an RFC 5280 AuthorityKeyIdentifier,etsi_tlrequires a certificate in thetrust chain, and
openid_federationrequires a federation entity. We therefore omit the member,which is conformant because expressing it is a SHOULD.
The cost is specific to x401 rather than to OpenID4VP generally: because x401 reuses
trusted_authoritiesas the agent's acquisition and discovery surface, a DID-based Verifier has noway to tell an agent where to obtain a credential it would accept. On #20 the note was that a general
stance on trusted authority list types would be helpful. There is an open OpenID4VP issue on exactly
this, openid/OpenID4VP#494, and we have added our implementer data to it.
3. Consumer Client Compatibility works, and it solved a real problem.
We implemented the body-embedded form, since that section invites contributions. The concrete
motivation turned out to be immediate rather than theoretical: our Verifier originally answered the
protected route with 401 and an empty body, which is protocol-honest and renders as a broken page to
any human who opens the link. Moving to 200 with an explanatory body plus the hidden
<data value="application/json;x401=proof-required" hidden>mirror fixed that in one change, and anagent reading the header is unaffected. Content is negotiated, so a non-browser client gets compact
JSON instead of the page.
One implementation note in case it is worth a sentence in that section: we found it important that
the mirror be treated strictly as disclosure. A 200 status makes it easy to accidentally serve the
protected representation alongside the requirement, so we have a test asserting the challenge body is
never the representation.
Finally: is there a public list of implementations? We would like to be counted if so. We have pinned
Appendix A, both Result Artifact forms, the Token Object and the Error Object as test fixtures, and
we would rather they matched an agreed set than only our own reading of the text.