Skip links
Home » Blog Articles » From Back-End to Wallet: OID4VP and OID4VCI in the Gaia-X Compliance Dispatcher

From Back-End to Wallet: OID4VP and OID4VCI in the Gaia-X Compliance Dispatcher

Delphine Claerhout, Software Engineer at Gaia-X

Published in the June edition of the Gaia-X Magazine

An upcoming release of the gx-compliance-dispatcher will add support for OID4VCI and OID4VP v1.0 — meaning Danube compliance credentials will be able to live in your wallet, without the extra fuss.

Overview

Up until now, the Gaia-X Danube Compliance Dispatcher has been a purely back-end affair: it receives a (HTTP/REST) request, routes it to the right extension, and returns a signed response. Works fine but remains quite technical and less interoperable.

That’s about to change. The dispatcher will soon be able to ask a wallet directly for the credentials it needs, pass them along to the right extension, and hand the resulting compliance credential back into that same wallet. One conversation, two protocols.

OID4VP (OpenID for Verifiable Presentations) is the “show me your credentials” half – it’s how the dispatcher asks the wallet for proof of who the user/organisation is, and how the wallet packages and signs that proof so the dispatcher can verify it came from a genuine, untampered credential.

OID4VCI (OpenID for Verifiable Credential Issuance) is the “here’s your new credential” half – it covers how a fresh credential is offered to a wallet, how the wallet authenticates itself (via the pre-authorised code and proof of possession), and how it pulls down and stores the result. In short: OID4VP moves credentials out of the wallet for verification, OID4VCI moves a credential into the wallet for safekeeping.

One Scan, Start to Finish

From the user’s side, it’s a simple and straightforward process. Just one quick scan (or deep link), selecting the requested credentials and waiting for the final credential to be saved in the wallet. No extra work, no extra app to open or form to fill in. Scan and go.

Behind the curtain, the dispatcher chains an OID4VP exchange (gathering the credentials it needs) straight into an OID4VCI issuance (handing back a fresh compliance credential).

It starts with the dispatcher figuring out what it needs from the user to run the compliance check for the selected trust scope. The QR code encodes an openid-vc:// URI, following the JAR pattern (JWT Authorisation Request by Reference): instead of cramming the full request into the QR code, it just contains a request_uri that the wallet fetches separately. That gives a signed JWT with a DCQL query, a nonce, and a response_uri to send the result to.

DCQL (Digital Credentials Query Language) is the bit doing the heavy lifting here. Rather than asking for “a credential” in vague terms, the request includes a DCQL query – a JSON structure where each entry names a credential type and the expected format. For a Gaia-X Participant Compliance check that means asking for proof of legal identity (gx:LegalPerson), VAT registration (gx:VatID), and acceptance of the Terms and Conditions (gx:Issuer). The wallet evaluates this query against whatever credentials it’s holding, picks out the ones that match, and returns them – a structured, machine-readable way of saying “show me exactly these, in exactly this shape,” with no back-and-forth needed.

The wallet picks out the matching credentials (with the user’s validation), builds a Verifiable Presentation, and posts it as a vp_token to a single /oid4vp/callback endpoint. The dispatcher verifies the presentation, forwards the gathered credentials to the relevant Danube compliance extension, and gets a compliance outcome back – already packaged as a signed VC.

And here’s the fun part: instead of just returning that outcome, the dispatcher immediately wraps it in an OID4VCI credential offer and sends it back as a redirect_uri, in the same response. The wallet follows it straight into the credential issuance flow: it exchanges the offer for an access token, then requests the credential with a proof of possession JWT signed by its own key. The dispatcher checks that proof, and hands back the compliance VC exactly as it received it from the compliance extension – untouched and unsigned by the dispatcher itself. Nothing gets tampered with along the way.

Result: a VC JWT lands in the wallet, self-contained and verifiable by anyone who can resolve the issuing extension’s DID. No need to contact a centralised authority. One scan in, one portable compliance credential out.

Same Dispatcher, New Tricks

Both legs of the flow lean on the dispatcher’s own did:web identity. That one identity covers a few different signing jobs, each with its own short lifespan: the issuer and verifier metadata, the authorisation request JWT the wallet fetches (a 5-minute JAR, typ: oauth-authz-req+jwt), and the verifiable presentation the dispatcher itself builds when talking to the extension. The authorisation request signature is what lets the wallet confirm it genuinely comes from the dispatcher and hasn’t been tampered with along the way.

The compliance credential itself, though, comes back already signed by the extension that issued it (Loire, Myrtus, IMX,…). The dispatcher just passes it through as-is rather than re-signing it. Meaning, the dispatcher manages its own keys for everything it puts its name to, but signing compliance outcomes stays firmly with the extension that did the actual evaluation – as it should.

There’s also a bit of state to keep track of between the initial QR scan and the vp_token landing later on – handled by Redis with a handful of short-lived, single-use, prefixed keys (nonces, pre-authorised codes, tokens), each consumed atomically so nothing can be replayed.

Trust scope routing itself hasn’t changed: the dispatcher reads the trustScope parameter, checks it against the configured TRUST_SCOPES list, and forwards results to the right extension endpoint. Have a look at the latest Architecture Document v3.1 for more details on the Gaia-X Trust Protocol (which is behind trust scopes) and how it solves the cross-ecosystem trust dilemma. There’s also an optional complianceLevel parameter, used by the Gaia-X Loire extension to indicate which label level the check should be run against. The OID4VCI and OID4VP layers just sit on top of that – GXDCH nodes can swap or extend their compliance backends without touching any of the wallet-facing protocol handling. Adding a new compliance extension remains a config change, not a code change.

Looking Ahead

Right now, the entry point into the whole flow is a single QR code encoding the openid-vc:// URI – a plain custom-scheme link. That already works well for mobile wallets that register the scheme, but it’s also the foundation for where this is headed next: that same URI can double as a deep link, letting web-based wallets expose it directly to their users and skip the QR code altogether. It also opens the door to more automation down the line – think a scripted or headless wallet that walks through the entire flow end to end via that link, making demos and regression testing far quicker than scanning codes by hand.

What This Opens Up

The real win here is interoperability. OID4VCI and OID4VP are open standards, so any conformant wallet can take part in the Danube dispatcher’s compliance flow. Compliance stops being something that happens quietly in a back-end pipeline and becomes something an organisation can actually carry around, share, and reuse across different contexts and verifiers. It’s a small piece of the puzzle, but it’s exactly the kind of portable, user-controlled trust Gaia-X has been aiming for all along.