Local SD-JWT and OpenID4VP verification for EUDI Wallet
Tech Article

Local SD-JWT and OpenID4VP verification for EUDI Wallet

July 31, 2026
3 min read

There are already decent SD-JWT decoders. Paste a token, get a JSON tree. That solves half the problem. When I started wiring EU Digital Identity presentations for EUDI × Solana, the questions I actua

privacyWeb Developmentsd-jwtopenidOpen SourceEUDI

There are already decent SD-JWT decoders. Paste a token, get a JSON tree. That solves half the problem.

When I started wiring EU Digital Identity presentations for EUDI × Solana, the questions I actually had were different:

  1. Would this presentation verify? Decoding is the easy part. I cared about signatures, disclosure binding, validity window, audience, and nonce.

  2. Is the request asking for more than it needs? Exact birth date when an age-over-18 assertion would do. A portrait in a remote flow. The whole credential instead of named claims.

I could not find a paste-in tool that answered both, locally. So I built one.

EUDI Inspector is open source (Apache-2.0), a static SPA. Nothing you paste leaves the browser.

What it does

Paste an SD-JWT VC (with or without a Key-Binding JWT) or an OpenID4VP presentation request. It:

  • Decodes the artifact and reconstructs the resolved claim set: every disclosure woven back into the issuer payload, _sd machinery stripped.

  • Verifies over WebCrypto: issuer and holder signatures, the disclosure seal (sd_hash), the validity window, audience/nonce binding, and the disclosure↔issuer binding (each disclosure's digest must appear in the issuer-signed _sd). That last check catches a claim injected after issuance. The issuer signature alone does not cover the disclosures.

  • Flags over-asking in a request against an editable, advisory rule set. Each hit comes with a rationale. Never a verdict.

  • Checks the issuer against a small trust snapshot or anchors you paste. Labeled informational, not authoritative.

A check is pass, fail, or skip. Skip when there is no input to decide; never a silent fail. Leave the issuer key blank and the signature check skips. Paste it and it runs.

Why "local" is the point

Presentation artifacts are PII. Shipping them to a hosted debugger is an easy habit and a bad one.

The inspector is a static site with a strict CSP (connect-src 'self'). No backend, no telemetry, no network calls with your data. Key resolution is paste-only on purpose: if the tool cannot fetch metadata or follow a request_uri, it cannot leak your token either. Fetch the request yourself, paste the result.

That also keeps the threat model simple. You can self-host it. You can read the ADRs for the design trade-offs.

What is different from a decoder

Decoders are crowded. Paradym, sdjwt.co, a handful of SPAs. They stop at structure.

The EU reference verifier and full OIDF stacks do real verification, but they are Docker services and conformance harnesses, not a URL you open with a paste box.

What I wanted was all of this in one client-side tool:

CapabilityTypical decoderHeavyweight verifierEUDI Inspector
Decode SD-JWTyesyesyes
Local sig + binding checksno / partialyes (server)yes (browser)
OpenID4VP request inspectrareyesyes
Over-asking flagsnonoyes
Zero backendoftennoyes

Over-asking is subjective, so the rules live as editable JSON with a rationale per rule. You can disagree with a rule; you can change it. The tool does not pretend to be a compliance oracle.

Honest gaps

No mdoc/mDL yet. The EUDI PID also ships as ISO 18013-5 (CBOR/COSE). That is the biggest usefulness cap and the next feature, not a nice-to-have.

Trust is thin in v1: informational membership against pasted JWKS and one reference issuer. It will never issue a legal standing verdict. That is intentional.

IETF spec vectors are still on the backlog. The good/bad fixture matrix plus an independent ES256 vector already gate the checks.

Try it

Load good-presentation for a clean pass, then bad-forged-disclosure to see why the binding check exists. If you are building the relying-party side of an EUDI pilot, I want the feedback, especially on the over-asking rules and on real request shapes from the wild.