OpenID4VP / SD-JWT VC verifier (v6.6)

SdJwtVcVerifier validates the dc+sd-jwt presentations: the issuer signature and trust, the selective disclosures, the holder binding and the credential status. It is registered by default, but trusts no issuer until you configure trusted issuers.

See also:

  ▸ OpenID4VP overview

  ▸ Response validation and verifiers

  ▸ mdoc verifier

  ▸ DCQL queries, user profiles and logging


1) Dependencies

The verifier relies on the EUDI SD-JWT library, which is not included by default:

<dependency>
    <groupId>eu.europa.ec.eudi</groupId>
    <artifactId>eudi-lib-jvm-sdjwt-kt</artifactId>
    <version>0.20.1</version>
</dependency>

Without it, SdJwtVcVerifier throws an OpenId4VpException when validating a credential, with a message identifying the dependency to add. Applications replacing it with their own CredentialVerifier do not need EUDI.

If you also use the mdoc verifier, align the Kotlin dependencies as explained in Kotlin alignment.

2) Quick start

// trustedIssuers: see "Trusted issuers" in Response validation and verifiers
config.addCredentialVerifier(new SdJwtVcVerifier().setTrustedIssuers(trustedIssuers));
config.setDcqlQuery(EudiPidQuery.sdJwtVc(GIVEN_NAME, AGE_OVER_18));

3) What is verified

4) Holder binding

The key-binding JWT must match the transaction’s saved request:

Check URL / QR code flow Digital Credentials API
nonce the request nonce the request nonce
aud the full client_id origin: followed by one of the saved expected_origins
iat between the transaction creation and now same

5) Supported presentations

Supported Not supported (rejected)
SD-JWT compact serialization, with or without key-binding JWT other serializations
asymmetric signature algorithms: ES256 by default, configurable with issuerAlgorithms and holderAlgorithms symmetric algorithms
issuer keys from the trusted issuers: configured keys or x5c chain issuer metadata, DID
nested objects and array elements disclosures  

6) Claims and profile identifier

The claims are the reconstructed JSON payload: nested objects and arrays keep their structure, digests are removed, and an undisclosed claim is absent, which is not an error:

{
  "iss": "https://issuer.example",
  "vct": "urn:eudi:pid:1",
  "given_name": "Erika",
  "address": {"locality": "Berlin"}
}

The issuer is the configured iss or, with an x5c chain, the leaf certificate subject (RFC 2253): the iss claim is then kept as a mere claim.

The default profile identifier, ProfileIdResolver.issuerAndClaim("sub"), reads a top-level claim and combines it with the issuer; it checks at initialization that the DCQL query can return that claim. EudiWalletClient has no default: choose a stable claim, such as ProfileIdResolver.issuerAndClaim(PERSONAL_ADMINISTRATIVE_NUMBER) where the PID provider issues it.

7) Credential status

Credential status is not checked automatically, and no status list is fetched. A credential containing a status claim is rejected unless setStatusChecker(...) is configured:

A credential without a status claim does not invoke the checker.

8) Scope and limits

This is a minimal SD-JWT VC integration, not a complete EUDI trust-list or HAIP validation implementation. EUDI provides the parsing, the disclosure verification and the key-binding JWT signature checks; pac4j supplies the transaction binding, the issuer trust and the profile mapping.