OpenID4VP (verifier)
This module is the verifier counterpart to b.auth.oid4vci. The relying party (verifier) builds an authorization request asking the wallet for one or more verifiable presentations; the wallet replies with a vp_token carrying the SD-JWT VC presentations plus a presentation_submission (legacy Presentation Exchange 2.0) OR no submission when DCQL is used.
DCQL (OpenID4VP 1.0 §6) is the JSON-shaped query language that replaces Presentation Exchange's JSONPath-soup. Two top-level keys:
credentials: [ { id: "id-card", format: "vc+sd-jwt", meta: { vct_values: ["https://example.com/vct/identity"] }, claims: [ { path: ["given_name"] }, { path: ["birthdate"], values: ["1990-01-15"] }, ], }, ], credential_sets: [ { options: [["id-card"], ["passport"]], required: true }, ]
The verifier-side primitives:
b.auth.oid4vp.verifier.create({ ... }) .createRequest({ dcql, audience, nonce, responseUri }) .verifyResponse({ vpToken, dcql, audience, nonce }) .matchDcql(presentations, dcql) // structural-only check
verifyResponse composes b.auth.sdJwtVc.verify (with requireKeyBinding: true and the DCQL-claim filter applied to the disclosed-claim set), then runs matchDcql to confirm the wallet's selection satisfies the query.
b.auth.oid4vp.matchDcql(presentations, dcql) #
Structural matcher: confirms the wallet's selected presentations (each with its DCQL id + verified claims) satisfy the DCQL query. Returns { valid, matched, errors }. Operators wanting to implement their own verifier transport call this directly after SD-JWT VC verification.
Claim path pointers follow OpenID4VP 1.0 §7.1.1: a string segment selects an object property, a non-negative integer indexes an array, and a null segment matches any element of the array at that depth (e.g. ["degrees", null, "type"] matches the type claim of any element in the degrees array). A null segment applied to a non-array node is a non-match.
var match = b.auth.oid4vp.matchDcql([
{ id: "id-card", format: "vc+sd-jwt", claims: { vct: "...", given_name: "Alice" } }
], dcqlQuery);
if (!match.valid) throw new Error(match.errors.join(", "));
b.auth.oid4vp.verifier.create(opts) #
{
{
clientId: string, // required
responseUri: string, // where the wallet POSTs vp_token (RP-side)
issuerKeyResolver: fn(header)→keyOrJwk, // resolves SD-JWT VC issuer signing key
audience?: string, // override aud claim (defaults to clientId)
keyAttestationVerifier?: fn,
}
}
Build an OID4VP verifier. Returns helpers for emitting an authorization request with a DCQL query and parsing the wallet's signed vp_token response.
var verifier = b.auth.oid4vp.verifier.create({
clientId: "verifier-1",
responseUri: "https://verifier.example/vp",
issuerKeyResolver: async function (header) { return jwksByKid[header.kid]; },
});
b.auth.oid4vp.verifier.createRequest(opts) #
{
{
dcql: object, // DCQL query — required
responseMode?: string, // default "direct_post"
nonce?: string,
state?: string,
aud?: string,
}
}
Build the OID4VP authorization request body. Operators sign + post the JWT (via PAR) or render it as an openid4vp:// deep-link.
var rv = verifier.createRequest({
dcql: {
credentials: [{ id: "id-card", format: "vc+sd-jwt", claims: [{ path: ["given_name"] }] }],
},
});
// → { request, nonce, state }
b.auth.oid4vp.verifier.verifyResponse(opts) #
{
{
vpToken: object|string,
dcql: object,
nonce: string,
requireKeyAttestation?: boolean,
}
}
Parse the wallet's vp_token response, verify each SD-JWT VC presentation (signature + KB-JWT + nonce + audience), and run the DCQL matcher. Returns { valid, presentations: [{ id, claims, ... }], matched, errors }
The vp_token may be a single string OR an array of strings. The legacy Presentation Exchange presentation_submission is accepted but not consumed — DCQL is the canonical query path.
var result = await verifier.verifyResponse({
vpToken: req.body.vp_token,
dcql: storedDcql,
nonce: storedNonce,
});
// → { valid, presentations, matched, errors }
Last updated 2026-08-08T16:39:49.652Z by seeder.