2026-06-08 · OID4VP verifier · proof of work

EU Wallet Sign-In — Verified Proof of Work

CodeB Sovereign Communications shipped an OID4VP 1.0 / EUDI Wallet verifier. On 2026‑06‑08 a spec‑compliant test wallet round‑tripped end‑to‑end against phone.codeb.io, returning verified PID claims and a signed SSO assertion. This page records the receipts — for buyers, auditors, regulators, and search engines that want more than a marketing slide.

Why a mock wallet?

The EUDI Wallet ecosystem is mid‑rollout. National pilot apps exist, but spec‑compliant OID4VP 1.0 + SD‑JWT VC + DCQL support is still landing in real wallets. To prove the verifier works independently of any specific wallet vendor, an internal deterministic test client follows the OID4VP 1.0 happy path exactly as the spec requires — demonstrating that any conforming wallet will interoperate.

Real‑wallet interop scales as the ecosystem matures. The substrate is locked in today.

What ran end-to-end

Eleven steps, every cryptographic gate passed, HTTP 200 returned with verified claims. The verifier's exact response to a successful presentation:

[10] POST /oidc.ashx?action=vp-response&id=... HTTP 200 { "ok": true, "user": "eu_0123456789abcdef", "role": "guest", "redirect": "/account.html", "sso_assertion": "eyJ...", "sso_max_age": 14400, "vct": "urn:eu.europa.ec.eudi:pid:1", "issuer": "https://test-issuer.example", "disclosures_verified": 4 }

Four selective disclosures (given_name, family_name, birth_date, age_over_18) were verified against the SD‑JWT VC's _sd SHA‑256 hash array. The SSO assertion is a standard RS256 JWT with amr=["vc"] and acr="urn:codeb:acr:eudi-wallet" — every federated app in the tenant inherits verified identity through ordinary OIDC userinfo. One presentation, every reliant party benefits, no bespoke integration.

Full transcript — sanitised mock‑wallet run

Every protocol step recorded by the internal test client, with placeholder identity (Alice Tester, 1990‑01‑01) substituted for the real test subject. Cryptographic blobs are shown truncated (eyJ…) because they encode the placeholder data. The session ID, nonce, state, ephemeral keys and certificate are illustrative; they rotate per session.

[1] GET https://phone.codeb.io/oidc.ashx?action=vp-start HTTP 200 { "id": "SESSION_ID", "request_uri": "https://phone.codeb.io/oidc.ashx?action=vp-request&id=SESSION_ID", "deep_link": "openid4vp://?client_id=x509_hash%3A4Ns4_AZgRtUzCbECSdEGgIdz_EG0iVqO8INWyufppZw&request_uri=…", "client_id": "x509_hash:4Ns4_AZgRtUzCbECSdEGgIdz_EG0iVqO8INWyufppZw", "expires_in": 300, "status": "pending" } [2] Parsing openid4vp:// deep link request_uri = https://phone.codeb.io/oidc.ashx?action=vp-request&id=SESSION_ID client_id = x509_hash:4Ns4_AZgRtUzCbECSdEGgIdz_EG0iVqO8INWyufppZw [3] GET .../vp-request?id=SESSION_ID — Authorization Request JAR JAR length = 2259 bytes --- JAR header --- { "alg":"ES256", "typ":"oauth-authz-req+jwt", "x5c":["MIIBoDCCAUegAwIBAgIIVkv7…"] } --- JAR payload --- { "iss": "x509_hash:4Ns4_AZgRtUzCbECSdEGgIdz_EG0iVqO8INWyufppZw", "client_id": "x509_hash:4Ns4_AZgRtUzCbECSdEGgIdz_EG0iVqO8INWyufppZw", "client_id_scheme": "x509_hash", "response_type": "vp_token", "response_mode": "direct_post.jwt", "response_uri": "https://phone.codeb.io/oidc.ashx?action=vp-response&id=SESSION_ID", "nonce": "NONCE_43_CHAR_BASE64URL", "state": "STATE_22_CHAR_BASE64URL", "dcql_query": { "credentials": [{ "id": "eudi_pid", "format": "dc+sd-jwt", "meta": { "vct_values": ["urn:eu.europa.ec.eudi:pid:1"] }, "claims": [ { "path": ["given_name"] }, { "path": ["family_name"] }, { "path": ["age_over_18"] } ] }] }, "client_metadata": { "jwks": { "keys": [{ "kty":"EC", "crv":"P-256", "use":"enc", "alg":"ECDH-ES", "x":"VERIFIER_EPK_X", "y":"VERIFIER_EPK_Y", "kid":"VERIFIER_EPK_KID" }]}, "authorization_encrypted_response_alg": "ECDH-ES", "authorization_encrypted_response_enc": "A128GCM", "vp_formats_supported": { "dc+sd-jwt": { "sd-jwt_alg_values":["ES256"], "kb-jwt_alg_values":["ES256"] } } }, "iat": <unix-ts>, "exp": <unix-ts + 300> } + JAR signature verified against x5c[0] subject: CN=CodeB Verifier (phone.codeb.io) [4] Extracting response parameters nonce = NONCE_43_CHAR_BASE64URL aud = x509_hash:4Ns4_AZgRtUzCbECSdEGgIdz_EG0iVqO8INWyufppZw response_uri = .../vp-response?id=SESSION_ID response_mode = direct_post.jwt query type = DCQL DCQL cred id = eudi_pid + verifier enc JWK: kty=EC crv=P-256 kid=VERIFIER_EPK_KID [5] Generating ephemeral holder P-256 key { "kty":"EC", "crv":"P-256", "x":"HOLDER_EPK_X", "y":"HOLDER_EPK_Y" } [6] Building SD-JWT VC with PID claims disclosed: given_name = "Alice" // placeholder disclosed: family_name = "Tester" // placeholder disclosed: birth_date = "1990-01-01" // placeholder disclosed: age_over_18 = true --- SD-JWT header --- { "alg":"ES256", "typ":"vc+sd-jwt" } --- SD-JWT payload --- { "iss": "https://mock-issuer.codeb.test", "iat": <unix-ts>, "exp": <unix-ts + 86400>, "vct": "urn:eu.europa.ec.eudi:pid:1", "_sd": ["DISCLOSURE_HASH_1","DISCLOSURE_HASH_2","DISCLOSURE_HASH_3","DISCLOSURE_HASH_4"], "_sd_alg": "sha-256", "cnf": { "jwk": { "kty":"EC", "crv":"P-256", "x":"HOLDER_EPK_X", "y":"HOLDER_EPK_Y" } } } [7] Building KB-JWT (key binding) --- KB-JWT header --- { "alg":"ES256", "typ":"kb+jwt" } --- KB-JWT payload --- { "iat": <unix-ts>, "nonce": "NONCE_43_CHAR_BASE64URL", "aud": "x509_hash:4Ns4_AZgRtUzCbECSdEGgIdz_EG0iVqO8INWyufppZw", "sd_hash": "SD_HASH_BASE64URL" } vp_token length = 1366 bytes [8] Composing response payload (pre-encryption) { "vp_token": { "eudi_pid": "eyJ….eyJ….SIG~DISC_1~DISC_2~DISC_3~DISC_4~eyJ….eyJ….SIG" }, "state": "STATE_22_CHAR_BASE64URL" } [9] JWE-encrypting response (alg=ECDH-ES, enc=A128GCM) JWE length = 2193 bytes [10] POST .../vp-response?id=SESSION_ID HTTP 200 { "ok": true, "user": "eu_0123456789abcdef", "role": "guest", "redirect": "/account.html", "sso_assertion": "eyJ….eyJpc3MiOiJodHRwczovL3Bob25lLmNvZGViLmlvIiwic3ViIjoiZXVfMDEyMy….SIG", "sso_max_age": 14400, "vct": "urn:eu.europa.ec.eudi:pid:1", "issuer": "https://mock-issuer.codeb.test", "disclosures_verified": 4 } [11] GET .../vp-response?id=SESSION_ID — status poll HTTP 200 { "ok": true, "user": "eu_0123456789abcdef", "status": "completed" } *** SUCCESS — verifier accepted the mock presentation. ***

Decode the sso_assertion JWT and you find amr=["vc"] and acr="urn:codeb:acr:eudi-wallet": every reliant party that trusts CodeB's OIDC layer now knows this session was authenticated by an EU Wallet credential, not a password. That signal is the whole point.

The crypto stack we shipped

What is still ahead (iteration 2)

The current iter‑1 substrate verifies structural integrity and holder binding. Future work, transparently flagged:

Until iter‑2 ships, the verifier accepts well‑formed SD‑JWT VCs for use cases where the tenant trusts the issuer at presentation time — pilot deployments, identity‑proofing demos, member‑organisation logins. Production high‑assurance identity proofing should wait for iter‑2 + a notified national wallet.

Why this matters

eIDAS 2.0 mandates that every EU member state issue a national Digital Identity Wallet by 2026 and that public‑service relying parties accept it. The OID4VP 1.0 / SD‑JWT VC / DCQL specs are the wire format. A working verifier is the precondition for accepting wallet sign‑in — without it, "EU Wallet ready" is a sticker on a roadmap.

CodeB tenants get the verifier today. Self‑hosted, EU‑sovereign, NIS2 / DORA aligned, with the verified‑claim relay wired through the OIDC layer the rest of the platform already uses.

Free EU Wallet training. Free EU Wallet training (dial 1844) — a dedicated AI trainer that walks you through OID4VP, SD‑JWT VC, eIDAS 2.0 and the December 2027 mandate. Free, no signup, ends when you do.

Hear the AI receptionist. Talk to vnum 1234 in your browser — the live CodeB Sovereign Communications virtual assistant. Ask it about EU Wallet, features, the build chain, anything.

Try the wallet flow. logineu.html — the live EU Wallet sign‑in flow.

Read the feature card. features.html#eu-wallet

Public verifier API. Integrators — the full endpoint reference is at eu-wallet-api.html (DE: de/eu-wallet-api.html).

Deutsche Fassung. de/eu-wallet-proof.html

Engineered by Aloaha Limited — 🇲🇹 proudly made in Malta, shipped from the smallest EU member state to the whole single market. www.codeb.io