# EUDI DE Documentation > German EUDI Wallet Ecosystem Documentation This file contains all documentation content in a single document following the llmstxt.org standard. ## Access Certificate Certificates and X.509 tend to sound like a purely technical detail you can leave to your developers. For the Access Certificate, that is a mistake — it represents something your organization has to *do*, not just something your software has to *implement*. This page explains what it is and why, in plain terms, without the cryptographic detail. ## Trust has to run in both directions Most people's mental model of the EUDI Wallet starts with the citizen: "the person proves who they are to a service." That is only half the picture. Think of it as the mirror image of an ID check: just as a bank needs to know its customer is really who they claim to be, the wallet — acting on behalf of its holder — needs to know that the organization asking for data is really who it claims to be, and not an impersonator trying to phish personal information. The Access Certificate is what makes that possible: it is a digital proof for your *organization*, presented automatically by your systems every time you ask a wallet for data. :::info[The core idea] Before a wallet will hand over any of its holder's data, **the Relying Party has to prove who *it* is to the wallet app** — not the other way around. The Access Certificate is how a Relying Party authenticates itself to the wallet. ::: ## Who issues it, and why you cannot just generate one yourself You cannot simply create your own Access Certificate the way you might generate a password. It is issued by the **Registrar** — the body responsible for keeping the EUDI Wallet Ecosystem trustworthy by making sure that only genuine, verified organizations can ask wallets for personal data. Before issuing a certificate, the Registrar has to actually establish that your organization is who it claims to be. In Germany, this legitimation is done through a **Know Your Business (KYB) process using your organization's ELSTER certificate** — the official digital business identity issued by the German tax authorities, already used to authenticate your organization with government systems. Because ELSTER certificates are themselves issued only after verifying a company's legal registration, they let the Registrar confirm your organization's identity without inventing a new, separate proofing process from scratch. In practice, this means three things have to happen before your organization can request any data from a wallet: 1. **Register** with the Registrar as an organization. 2. **Authenticate and legitimize** your organization using your ELSTER certificate, so the Registrar can confirm you are a real, registered legal entity and not an impersonator. 3. Once legitimation succeeds, the Registrar **issues your Access Certificate** — the certificate your systems will present to every wallet you interact with going forward. This certificate is bound to a cryptographic key pair that you generate and whose public key you provide during the process. :::info[Sandbox note] This describes the legitimation process for production access to the ecosystem. The Sandbox environment offers a lighter-weight onboarding path to obtain test certificates — see [Joining the Ecosystem](../sandbox/onboarding/joining.md) and the [Onboarding Process](../sandbox/onboarding/onboarding.md) for how that works. ::: --- ## Credentials An **EUDI Wallet credential** is a digitally issued, verifiable statement of fact about a natural or legal person — such as their identity, a qualification, an entitlement, or a status — that is: - **issued** by an accountable authority (public or private), - **held and controlled** by the individual in their EU Digital Identity Wallet, and - **presented** by that individual, at their own discretion, to any party that needs to verify the fact. A credential is the digital counterpart of a physical document of proof (ID card, driver's licence, diploma, membership card). It serves the same purpose and carries legal recognition under the eIDAS 2.0 Regulation, but can be verified instantly and reliably without contacting the issuer. ## Key Characteristics | Characteristic | Meaning | |---------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------| | **Verifiable** | Any receiving party can confirm the credential is genuine, unaltered, and issued by a legitimate authority — on their own, at the moment of use. | | **Holder-controlled** | Only the wallet holder decides when a credential is presented and to whom. Nothing is shared automatically. | | **Selectively shareable** | The holder can reveal only the specific facts required (e.g. *"over 18"*) while withholding everything else (name, birthdate, address). | | **Legally recognized** | Depending on its category (see below), a credential carries defined legal effect across all EU Member States. | | **Issuer-accountable** | Every credential is traceable to an identifiable issuer who vouches for its content. | --- ## The Three Roles Every credential exists within a triangle of trust: | Role | Who | Responsibility | |------------------------------|--------------------------------------------------------|--------------------------------------------------------------------------------------| | **Issuer** | An authority or organization entitled to attest a fact | Issues the credential based on an authentic source and stands behind its correctness | | **Holder** | The person the credential is about | Stores the credential in their wallet and decides on every presentation | | **Verifier** (Relying Party) | Any party that needs proof of a fact about a person | Requests and checks the credential — without contacting the issuer | ## Legal Categories eIDAS 2.0 distinguishes credential categories by *who* may issue them and *how much legal weight* they carry: | Category | Full Name | Issuer | Legal Significance | Analog Equivalent | |-------------|--------------------------------------|-------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------|-------------------| | **PID** | Person Identification Data | Designated national authority | The foundational identity credential (name, date of birth, etc.); anchors trust for everything else | | | **EAA** | Electronic Attestation of Attributes | Any qualified public or private attestation provider | Legally recognized proof of any attribute (diploma, membership, entitlement) | | | **QEAA** | Qualified EAA | Qualified Trust Service Provider (QTSP), under strict supervision | Highest evidentiary strength; comparable to a qualified electronic signature | | | **PUB-EAA** | EAA issued by a public sector body | Government body | Recognized EU-wide without additional qualification | | > **Note:** A mobile driving licence (mDL) is a prominent real-world example. It follows its own international rulebook > but conceptually belongs to the same family of attestations. --- ## What a Credential Is *Not* - **Not a copy of a database record.** It is a self-contained proof; the verifier does not query the issuer's systems. - **Not an account or login.** It is a portable statement of fact, independent of any single service. - **Not automatically visible to anyone.** No party — including the issuer — learns when or where the holder uses it. - **Not tied to one technical format.** Different technical envelopes exist (e.g. W3C Verifiable Credentials, ISO mdoc, SD-JWT VC). These are interoperable implementation choices and do not change what a credential *is* or *means* legally. --- ## The paradigm shift from paper-based proofs and IDs :::info[Information] The trust triangle has long existed in the analog world: issuer (authority) → holder (citizen) → verifier (case worker, checkout). But credential are not just "a scanned physical card as a PDF". They are equivalents and digitize this whole model. It is a new primitive that preserves analog properties (offline presentation, unobservability) and adds digital ones ( data minimization, cryptographic verifiability, revocability). ::: #### Analog equivalents National ID card ≈ PID Driver's license, official certificate from a register ≈ PuB-EAA Membership/loyalty card ≈ EAA Notarized/qualified certified document ≈ QEAA #### Where they differ – and why that makes the difference: | Property | Physical proof | Credential | |----------------------|---------------------------------------------------------------------------|---------------------------------------------------------------| | Selective disclosure | No – an ID card reveals name, address, and date of birth when proving age | Yes – only "≥18" without the date of birth | | Unobservability | Issuer does not learn about the presentation | Same (unlike federated login) | | Forgery/copying | Physical security features, copyable | Cryptographic signature + key binding against replay/transfer | | Revocation | Hard to invalidate mid-life | Status/revocation mechanisms (e.g. Token Status List) | | Verification | Visual (hologram, features) | Signature chain to the trust anchor | | Correlatability | Card number as a tracking feature | Reducible (batch issuance, salted disclosures) | ## The paradigm shift away from centralized ID The break with previous identity models in the digital space lies in the decoupling of issuance and presentation. | Dimension | Federated model (SAML / OIDC) | Wallet/attestation model | |-------------------------|---------------------------------------|----------------------------------------------------------| | Parties per transaction | IdP online in the loop | Issuer offline, not involved | | Trust establishment | Bilateral federation, per transaction | Signature verification against trust anchor / trust list | | Data sovereignty | With the IdP / directory | With the Holder (wallet) | | "Phone-home" | Yes – IdP learns of every login | No – Issuer does not learn of the presentation | | Data minimization | Hard (IdP delivers attribute bundles) | Built in (selective disclosure, predicates like "≥18") | :::info[Information] In the federated model, the identity provider is involved in every login and learns where the user authenticates. In the wallet model, the Issuer issues once; afterwards the Verifier verifies cryptographically and without contacting the Issuer. This restores the privacy property of the physical card (no one learns where I present it) in the digital world – and adds data minimization, which is not possible at all in the analog world. The shift is thus: from account-/directory-centric to proof-centric, and from "ask the issuer every time" to "verify the signature". ::: --- ## Level of Assurance **Level of Assurance (LoA)** is a formal, legally defined measure of how much confidence a relying party can have that a person really is who they claim to be. It is not a technical implementation detail — it is a legal and risk classification that then *drives* your technical requirements. ## The three levels eIDAS defines three levels of assurance: | Level | What it means, in plain terms | |---|---| | **Low** | Some confidence in the claimed identity. Reduces the risk of misuse or alteration, but with limited safeguards. Suitable for low-risk interactions. | | **Substantial** | Substantial confidence. Meant to reduce identity-related risk considerably, with stronger technical and procedural safeguards than Low. | | **High** | The highest confidence available. Designed to prevent misuse or alteration of identity even against attackers with significant capabilities. Required wherever identity fraud would cause serious harm. | Think of it as a sliding scale of "how sure do I need to be, and how hard would it be for someone to fake it?" — the higher the level, the stronger the safeguards required to reach it. ## Where is this defined? LoA is not an EUDI-specific invention — it comes directly from EU law: - **[eIDAS Regulation (EU) No 910/2014](https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:32014R0910)** — the original regulation that introduced the concept of assurance levels for electronic identification. - **[Commission Implementing Regulation (EU) 2015/1502](https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:32015R1502)** — sets out the precise minimum technical specifications and procedures that define what *Low*, *Substantial*, and *High* actually require in practice. - **[Regulation (EU) 2024/1183 ("eIDAS 2.0")](https://eur-lex.europa.eu/eli/reg/2024/1183/oj/eng)** — amends the original eIDAS Regulation to establish the European Digital Identity Framework, including the EUDI Wallet. It carries the same LoA concept forward into the wallet-based ecosystem. ## Choosing the right LoA for your use case As a Relying Party, the LoA you require is primarily a **legal and risk-driven decision**, not a technical preference. Ask: *if the claimed identity turned out to be false or manipulated, how serious would the consequences be — for the person, for you, or for others?* - **Low-risk interactions** (for example, a newsletter sign-up, a low-value loyalty program) rarely need more than Low or Substantial. - **Higher-risk interactions** — anything involving money, legal obligations, regulated services, or sensitive personal data — typically require **High**. Opening a bank account is a concrete example: it must be done at LoA High, as shown in the [Berlin Digital Bank example](../rp/onboarding/example_bdb.md). Often the level is not actually yours to choose freely: sector-specific regulation (for example, anti-money-laundering rules in finance, or requirements for public-sector services) may already mandate a specific LoA for your use case. If you are unsure which level applies to you, this is a question for your legal or compliance function, not a purely technical decision. ## Does this apply beyond PID? Yes. LoA is a general eIDAS concept about the strength of **identification**, not something that only concerns the PID (Person Identification Data) credential. - The **PID** is the foundational identity credential in the EUDI Wallet, and it is always issued and used at **LoA High** — it anchors trust for everything else. - Other attestation types — **EAA**, **QEAA**, **PUB-EAA** (see [What is a Credential?](credentials.md)) — are not identity credentials themselves, but presenting *any* attestation still relies on the wallet correctly authenticating its holder. So the assurance question — "how confident am I that the right person is presenting this?" — remains relevant whenever strong assurance matters, even outside the PID itself. :::info[Going deeper] This page explains LoA conceptually. For the architectural and technical consequences of each level — required credential formats, certificate requirements, key binding, and audit obligations — see [Understanding the basics](understanding-basics.md#level-of-assurance-loa). ::: --- ## Registration Certificate The [Access Certificate](access-certificate.md) proves *who* you are. The Registration Certificate is easy to misread as proof that you're *allowed* to ask for data — it is not. Its actual job is narrower and, in some ways, more important: it makes your reason for asking **transparent and publicly attributable**. ## Not an authorization — a transparency mechanism :::info[The core idea] The Registration Certificate is **not permission**. The Registrar does not judge, vet, or approve whether your stated purpose is legitimate. What it does is **sign your public declaration** of why you intend to interact with a wallet — so that this declaration is on record, attributable to your organization, and checkable by others. ::: Before you can ask a wallet for data, you must declare, in the Registrar, what you intend to use the interaction for. That declaration becomes part of a queryable registry entry tied to your organization. The Registration Certificate is simply the Registrar's signature over that declaration — confirmation that *this specific reason was made transparent*, not an endorsement that the reason is good, proportionate, or lawful. That judgment is left to others. ## Why this matters: accountability, not gatekeeping Because these declarations are recorded and queryable, they can be checked after the fact — by supervisory bodies, journalists, or civil-society organizations such as NGOs — against what an organization is actually doing in practice. If a Relying Party's real-world data requests do not match what it declared, that mismatch becomes visible and can be challenged. The Registration Certificate is what makes that kind of external scrutiny possible at all: without a signed, public declaration of intent, there would be nothing to check requests against. This is also why skipping or falsifying it is not a minor technicality. The Registrar signing your declaration is what allows the wallet to know that *the reason you gave was actually put on the record* — not invented on the spot, purely for that one request, with no accountability attached. ## Who issues it, and how you get one The Registration Certificate is issued by the same **Registrar** that issues your [Access Certificate](access-certificate.md), as part of the same registration process — there is no separate Know Your Business check to complete for it. Declaring your purpose to the Registrar and legitimizing your organization happen together; you receive both certificates as a pair. ## What you receive Both certificates from the Registrar work together, and your systems need both: - The **Access Certificate** — proves who you are. See [What is the Access Certificate?](access-certificate.md) - The **Registration Certificate** — proves your stated purpose was transparently declared and signed Neither is optional or interchangeable with the other. Leaving out the Registration Certificate does not make your request "unaccountable but harmless" — it removes the one mechanism that lets anyone outside your organization verify what you said you'd use the data for. :::info[Going deeper] This page explains what the Registration Certificate is and the role it plays. For the technical detail — how it's structured and how to embed it in a presentation request — see [Using Registrar Certificates in Presentation Requests](../rp/onboarding/registrar_certificate_usage.md). ::: --- ## Binding Three distinct "binding" guarantees run through OpenID4VP, each closing off a different class of attack. Getting all three right is what separates "the wallet showed a valid credential" from "the wallet showed a valid credential, bound to this holder, for this session, for this exact transaction" — the actual security property you need. ## Holder Binding **Holder binding** proves that the entity presenting the credential is the same entity to whom it was issued. The wallet holds a private key tied to the credential (via the `cnf` claim in SD-JWT-VC, or `DeviceKey` in mdoc) and produces a proof-of-possession signature over the presentation. Your response validation must verify this signature against the bound public key — verifying only the issuer's signature on the credential is not enough. ## Session Binding **Session binding** ties the presentation to *this specific* OpenID4VP exchange, not just to the holder in the abstract. It is enforced through the `nonce` you generate per request and require the wallet to echo inside its cryptographic proof — this is what stops a genuine presentation from being captured and replayed against a different verifier or a later session. ## Transaction Binding **Transaction binding** (via the `transaction_data` parameter) cryptographically ties the presentation to the specific business transaction the user consented to — e.g. "I consent to share my age to complete this exact €50 payment." The wallet signs a hash of the transaction data alongside the credential proof, so your backend can detect a presentation being reused for a transaction the user never actually saw or approved. ## Further Reading Key topics: `cnf` claim / key-binding JWT (SD-JWT-VC), `DeviceSigned` structure (mdoc), nonce-based replay protection, `transaction_data` parameter, proof-of-possession validation. [OpenID4VP — Terminology (Holder Binding / Key Binding)](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#name-terminology) · [OpenID4VP — Transaction Data](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#name-transaction_data) --- ## Online vs. Proximity When a Relying Party asks a person to present a credential from their EUDI Wallet, the request happens in one of **two settings**. Everything else — which app you use, which data format arrives, how you check the result — follows from which of the two you are in. The mental model is simple: **if it happens over the web, it is Online; if it happens in person, it is Proximity.** ## When do I choose which? You usually do not choose freely — the use case decides for you. Match the setting to where the person actually is. **Online — the person is somewhere else:** - Opening a bank account from home - Signing up for a service or verifying your age on a website - Logging in to a public-sector portal - Renting a car through an app before you arrive **Proximity — the person is right in front of you:** - Proving your age at a store checkout or a bar - Showing a driving licence during a roadside check - Checking in at a hotel reception - Boarding or entering a venue with a ticket or entitlement - You cannot guarantee access to the internet (offline) ## Credentials must be issued the right way To cover both use cases, the EUDI Wallet relies on **two different technical credential formats** — not because one is better than the other, but because Online and Proximity grew out of two different standards worlds that each already had a proven way of doing things: - **SD-JWT VC** comes from the web-identity world. It builds on formats already used for online authentication (JSON, JWTs), extended with selective disclosure so a person can reveal only some attributes. It is the format used for **Online** verification. - **ISO mdoc** (mobile document, secured with an MSO — Mobile Security Object) comes from the world of physical, standardized ID documents such as the mobile driving licence. It is built to work reliably in person, including offline, over a local connection like a scanned QR code or NFC. It is the format used for **Proximity** verification. Rather than forcing everyone onto a single new format, the ecosystem supports both — each optimized for the setting it is used in. **What this means for a credential:** a single credential is issued *in one specific format*. A PID issued as SD-JWT VC and a PID issued as ISO mdoc contain the same information, but are not interchangeable files — a wallet must hold the format your use case needs, and your Relying Party integration must request and read that same format. :::info[Tip] As a Relying Party, all you really need to decide is how you want to access this data — Online or Proximity — and use the matching format. But keep this constraint in mind: the credential issuer must have issued the credential in that specific format. If a wallet only holds a credential as SD-JWT VC, it cannot be used in a Proximity flow that requires ISO mdoc, and vice versa. ::: --- ## Core Concepts Overview You're building this ecosystem together with us, the ecosystem orchestrator. Getting started from scratch can feel overwhelming, so this guide gives you a research path: the sources you need to build your knowledge, and a foundation to design your own use case on. ## Before you start: OpenID4VP fundamentals **OpenID4VP** sits at the intersection of several mature but independently deep domains. An architect who wants to own the full stack — not just wire up a library — needs fluency across all of them. The list below maps each domain to the specific role it plays in an OpenID4VP implementation. Work through any gaps before diving into the protocol specification; trying to read the spec without this foundation will leave you guessing at every other sentence. ### Verifiable Credentials and Verifiable Presentations The payload the wallet returns is a **Verifiable Presentation** wrapping one or more **Verifiable Credentials**. You need to understand the VC data model — issuer, subject, claims, proof — and the relationship between credential and presentation. This is the domain model your response-parsing logic operates on, and it informs which fields you extract and trust. Key topics: VC data model, credential vs. presentation distinction, issuer key binding, holder binding, selective disclosure. [Credentials — Overview](credentials.md) ### Public Key Infrastructure (PKI) and X.509 Certificates OpenID4VP in the EUDI context requires your Relying Party backend to present an **Access Certificate**. You must understand certificate chains, how a verifier walks the chain to a trust anchor, certificate fields relevant to identity (`CN`, `SAN`, `OID` extensions), and revocation mechanisms (CRL, OCSP). You will also need to embed and serve these certificates correctly inside the signed request object. Key topics: X.509 v3 structure, certificate chains, CA hierarchy, PEM vs. DER encoding. [IETF RFC 5280 — X.509 PKI Certificate Profile](https://datatracker.ietf.org/doc/html/rfc5280) ### Level of Assurance (LoA) **Level of Assurance** defines how much confidence a system can have in a claimed identity. In eIDAS 2.0 and the EUDI Wallet context, three levels are defined: *Low*, *Substantial*, and *High*. The level you must request is a legal and risk-driven decision — but it has direct, concrete consequences for your software architecture. From an architectural perspective, LoA shapes the following decisions: - **Which credential format and profile you must accept.** LoA High mandates stricter cryptographic requirements (e.g. the [OpenID4VC High Assurance Interoperability Profile](https://openid.net/specs/openid4vc-high-assurance-interoperability-profile-1_0-final.html)), narrowing the set of acceptable algorithms and binding mechanisms your verification logic must enforce. - **Certificate and trust chain requirements.** Higher assurance levels require your Access Certificate to be issued by a qualified trust service provider, which affects procurement, renewal cycles, and your certificate management component. - **Key binding and holder authentication.** At LoA High, the wallet must cryptographically prove that the credential was presented by the legitimate holder (device binding / key binding). Your response validation must verify this proof, not just the credential signature. - **Audit and non-repudiation.** Higher assurance levels often imply stricter logging requirements — what you must record, how long, and in what tamper-evident form — which affects your persistence layer and operational setup. Understanding which LoA your use case requires before you design the system avoids costly late-stage rework. For a plain-language explanation of what the three levels mean and how to choose one for your use case, see [What is Level of Assurance (LoA)?](../concepts/level-of-assurance.md). ### RESTful HTTP APIs Both your request endpoint and response endpoint are plain HTTP resources. You need a solid grip on HTTP semantics: methods, status codes, content negotiation, caching headers, and CORS policy — because the wallet is a native mobile app making cross-origin requests to your backend. Misconfigured CORS or wrong status codes are among the most common causes of silent failures in integration testing. Key topics: HTTP methods and status codes, `Content-Type`, CORS headers (`Access-Control-Allow-Origin`, preflight), redirect chains (`302`), query-string encoding. [MDN — HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP) · [MDN — CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS) ### Session and State Management OpenID4VP is a redirect-based flow that spans multiple HTTP round-trips: the user lands on your frontend, a link opens the wallet, the wallet calls your backend asynchronously, and your frontend polls for completion. You must design a session model that correlates the `nonce` and `state` parameters across these hops, survives the user switching between browser and native app, and is resistant to fixation and replay attacks. This is the HTTP-layer session that correlates a user's browser/app interactions — distinct from the cryptographic binding of the presentation itself to a specific holder and exchange. This will be covered in the upcoming guide 'Types of Binding'. Key topics: server-side session stores vs. signed cookies, correlation by nonce/state, session lifecycle (creation, expiry, invalidation), polling vs. push notification patterns, CSRF protection. [OWASP — Session Management Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html) ### Credential Formats (SD-JWT-VC and mdoc) Credentials in the EUDI ecosystem are encoded in one of two formats: **SD-JWT-VC** (a JWT-based format with selective disclosure) or **mdoc** (ISO/IEC 18013-5, a CBOR-based format). Your parsing and verification logic must handle whichever format the wallet presents. The two formats have fundamentally different encodings, trust validation paths, and library ecosystems — choosing the right one for your use case, or supporting both, is an early architectural decision. For a detailed comparison and guidance on which format applies to your scenario, see the dedicated format reference. #### JSON Object Signing and Encryption (JOSE) SD-JWT-VC is built entirely on the JOSE family of standards. You must be comfortable with all three layers before you can read, verify, or produce any SD-JWT-VC artefact: - **JWT / JWS (RFC 7519 / RFC 7515)** — Almost every artefact in the flow is a JWT: the signed request object your backend serves to the wallet, the `id_token` variant of a response, and the credential claims themselves. Understand the three-part `header.payload.signature` structure, the standard claim set (`iss`, `aud`, `exp`, `iat`, `nonce`), how the signing algorithm is negotiated via `alg`, and what end-to-end signature verification actually involves. - **JWE (RFC 7516)** — The wallet encrypts its Verifiable Presentation response using the Relying Party's public key before posting it to the response endpoint. Understand how JWE differs from JWS (confidentiality vs. integrity), key-wrapping algorithms (`ECDH-ES+A256KW`), content encryption algorithms (`A256GCM`), and the fact that an ephemeral encryption key pair must be generated fresh per session. - **JWK / JWKS (RFC 7517)** — Key material is exchanged as JSON Web Key Sets. The wallet fetches your JWKS to encrypt its response; you fetch the issuer's JWKS to verify credential signatures. Know key types (`EC`, `RSA`), usage fields (`use`, `key_ops`), key IDs (`kid`), and how to rotate keys without breaking in-flight sessions. [IETF RFC 7519 — JWT](https://datatracker.ietf.org/doc/html/rfc7519) · [RFC 7515 — JWS](https://datatracker.ietf.org/doc/html/rfc7515) · [RFC 7516 — JWE](https://datatracker.ietf.org/doc/html/rfc7516) · [RFC 7517 — JWK](https://datatracker.ietf.org/doc/html/rfc7517) #### mdoc, CBOR and ISO/IEC 18013-5 The mdoc format uses an entirely different encoding stack from SD-JWT-VC. Instead of JSON and JWTs, it is based on **CBOR** (Concise Binary Object Representation) for encoding and **COSE** (CBOR Object Signing and Encryption) for signatures and encryption. The credential structure, namespace model, and trust validation path are defined in **ISO/IEC 18013-5**, the international standard for mobile driving licences — which EUDI adopts as the vehicle for mDL and other mdoc-typed credentials. Key topics: CBOR encoding (RFC 8949), COSE signing and encryption (RFC 9052), mdoc namespace and element structure, `DeviceResponse` and `DeviceSigned` objects, mdoc-specific trust chain validation. [RFC 8949 — CBOR](https://datatracker.ietf.org/doc/html/rfc8949) · [RFC 9052 — COSE](https://datatracker.ietf.org/doc/html/rfc9052) · [ISO/IEC 18013-5 (paid standard)](https://www.iso.org/standard/69084.html) ### Digital Credential Query Language (DCQL) OpenID4VP uses **DCQL** to express which credential attributes the Relying Party requests from the wallet. You must understand how to model a query (credential type, claim paths, optional vs. required), how alternative credential formats are expressed as alternatives within one query, and how the wallet matches a query against its stored credentials. Your DCQL implementation is the direct translation of your business requirements into protocol-level attribute requests. Key topics: DCQL query structure, `credential_sets`, claim paths, format identifiers (`vc+sd-jwt`, `mso_mdoc`), optional claims. [OpenID4VP — DCQL section](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#name-digital-credentials-query-l) ### Trust Frameworks, Trusted Lists, and Governance (LoTL / TSL) Verifying that a presented credential was issued by a legitimate issuer requires checking against a **Trusted List** — in the EUDI context, the List of Trusted Lists (LoTL) and national Trust Service Lists (TSL). You need to understand how trust anchors are published, how to fetch and cache them, and how to walk the certificate chain from the credential's issuer key all the way to a trusted root. This is non-trivial operational work, not just a library call. Trust anchors are not static: they exist within a **governance model** you need to design around, not just a data feed you consume. The European Commission operates the LoTL itself, but each Member State's national supervisory body governs its own TSL — deciding which Trust Service Providers are accredited, suspended, or withdrawn. An entry's status can change (a TSP loses accreditation, a root key is rotated or revoked) independently of any action on your side, so your architecture must treat trust anchors as a **live, revocable data set**: define a re-fetch/cache-invalidation policy, decide how to behave when the LoTL endpoint is unreachable (fail closed vs. serve a stale cache with a bounded TTL), and log which trust anchor version was active at the time of each verification for auditability. Key topics: ETSI TS 119 612 (TSL format), LoTL structure, trust anchor discovery and governance, TSP accreditation/withdrawal lifecycle, certificate chain validation, CRL fetching and caching, cache invalidation strategy. [European Commission Trust List Browser](https://eidas.ec.europa.eu/efts/browser) · [ETSI TS 119 612](https://www.etsi.org/deliver/etsi_ts/119600_119699/119612/02.02.01_60/ts_119612v020201p.pdf) ### Deep Linking and App-to-App Invocation The wallet is a native mobile application. Your Relying Party frontend must construct a correctly formed `openid4vp://` URI (or a universal/app link variant) that opens the wallet, passes the request URI, and survives the platform-specific redirect back to your frontend after the presentation is complete. The mechanics differ between iOS (Universal Links) and Android (App Links / Intent Filters), and a broken link scheme will silently fail on one platform. Key topics: custom URI schemes, iOS Universal Links, Android App Links, `openid4vp://` scheme, response URI callback, same-device vs. cross-device flow. [OpenID4VP — Invocation section](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#name-wallet-invocation) · [Apple — Universal Links](https://developer.apple.com/documentation/xcode/allowing-apps-and-websites-to-link-to-your-content) · [Android — App Links](https://developer.android.com/training/app-links) ### Key Management and Secrets Hygiene Every cryptographic operation in the stack depends on the secure lifecycle of private keys: generation, storage, rotation, and destruction. A mismanaged key undermines every other security measure. You must choose an appropriate secret store (HSM, cloud KMS, or software keystore) that matches your threat model, and you must ensure that ephemeral per-session keys (used for JWE response encryption) are isolated from long-lived signing keys. Key topics: symmetric vs. asymmetric key storage, HSM vs. software keystore trade-offs, key rotation without downtime, ephemeral key isolation, secret scanning in CI. [OWASP — Key Management Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Key_Management_Cheat_Sheet.html) --- ## OpenID4VP — The core of credential presentation The domains above are the ground the protocol stands on. This section explains the protocol itself. **OpenID4VP** — OpenID for Verifiable Presentations — answers a single question: how does an application ask a wallet for verified data about its user, and receive an answer it can trust? It is the protocol every Relying Party in the German EUDI Wallet ecosystem implements, and it is the only protocol that runs between your Relying Party backend and the wallet. ### Who takes part Three parties are active in an OpenID4VP exchange: - **The Relying Party** — your application, acting as verifier. It asks for attributes and validates what comes back. - **The Wallet** — the user's app. It holds the credentials, shows the request to the user, and builds the response. - **The User** — decides whether to share, and what to share. The **Issuer** is not part of the exchange. It signed the credential at some earlier point in time. You verify that signature against a trust anchor, so you never have to contact the issuer during a presentation. ### How an exchange runs 1. **You build a request.** It states who you are (`client_id`), which attributes you need (a DCQL query), and a fresh random value that ties the answer to this one exchange (`nonce`). You publish the request as a signed document and keep the matching session in your backend. 2. **You hand the request to the wallet.** The user either scans a QR code (the wallet runs on a second device) or taps a link that opens the wallet on the same device. Either way the wallet only receives a URL (`request_uri`) pointing at your signed request — the request itself stays on your server. 3. **The wallet checks who is asking.** It fetches the request, verifies your signature, and validates your Access Certificate against the trusted lists. A verifier that cannot prove its identity never reaches the user. 4. **The user decides.** The wallet shows which attributes you asked for and who is asking. The user confirms or cancels. Thanks to selective disclosure, the wallet reveals only the requested attributes — asking for a date of birth does not expose an address. 5. **The wallet sends the response.** It posts a **Verifiable Presentation** to your `response_uri`, encrypted with your public key. The presentation contains the disclosed attributes, the issuer's signature over the credential, and a proof of possession from the user's device key covering your `nonce`. 6. **You validate and continue.** You decrypt the response, check the issuer signature against a trust anchor, check the holder's proof of possession, confirm the `nonce` matches the session you started, and only then release the result to your business logic. ### What this design buys you - **You learn nothing you did not ask for.** The DCQL query is the full extent of what you receive. - **The user is never silently profiled.** Every presentation requires a visible, explicit confirmation in the wallet. - **Trust is verifiable offline.** Signatures and trusted lists carry the trust; no live call to the issuer is needed. - **Both parties authenticate.** The wallet proves the credential belongs to its holder, and your Access Certificate proves to the wallet that you are an authorised verifier. ### Where the work sits for you The protocol assigns you four concrete responsibilities. Everything else in this guide serves one of them: | Responsibility | What you build | |----------------------------|---------------------------------------------------------------------------------------------| | Express what you need | A DCQL query derived from your actual business requirement — nothing more | | Prove who you are | Access Certificate handling and a signed request object served from your `request_uri` | | Receive the answer | A `response_uri` endpoint that decrypts and parses the presentation | | Decide whether to trust it | Signature, trust chain, holder binding, and `nonce` validation before any business decision | Key topics: authorisation request and signed request object, `request_uri` and `response_uri`, `client_id` schemes, `nonce`, encrypted responses, same-device vs. cross-device flow. [OpenID4VP 1.0](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html) · [OpenID4VC High Assurance Interoperability Profile](https://openid.net/specs/openid4vc-high-assurance-interoperability-profile-1_0-final.html) ## Further Reading | Documentation | Read this, when ... | |------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------| | [OpenID4VP](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html) | ... you want to deeply understand the technical protocol between verifier and wallet app | | [OpenID4VC High Assurance Interoperability Profile](https://openid.net/specs/openid4vc-high-assurance-interoperability-profile-1_0-final.html) | ... you need to authenticate on level of assurance high | | [ARF](https://eudi.dev/latest/) | ... you want to understand the big picture of the whole european ecosystem | | [Blueprint for the EUDI Wallet Ecosystem in Germany](https://bmi.usercontent.opencode.de/eudi-wallet/eidas-2.0-architekturkonzept/) | ... you want to understand the german approach to this architecture | | [wallet-development-documentation](https://bmi.usercontent.opencode.de/eudi-wallet/wallet-development-documentation-public/latest/) | ... you want to understand the issuance of the pid in depth | --- ## Validation When someone presents a PID (Person Identification Data) from their EUDI Wallet, you cannot simply trust it at face value — just as you would not accept a photocopied ID card without checking it. **Validation** is the process of confirming, step by step, that a presented PID can actually be trusted before you rely on it. A presentation could, in principle, go wrong in several ways: it could come from an untrustworthy issuer, be intercepted and replayed by someone else, be presented by a person it was never issued to, contain more data than you asked for, or simply be expired or revoked. Validation is what rules each of these out — one check at a time. ## What gets checked A verifier works through a series of checks, roughly in this order. Each one answers a plain question: 1. **Is this from a trustworthy source?** The verifier confirms the PID issuer and the wallet provider are part of the recognized, official trust framework — not an unknown or fraudulent party. 2. **Is this response really meant for me?** The verifier makes sure the presentation was generated specifically in response to *its own* request, right now — not intercepted, reused from an earlier session, or redirected from somewhere else. 3. **Is this being presented by the right person?** Even a completely genuine PID must be proven to belong to the person presenting it, using a cryptographic proof tied to their specific wallet. This stops a copied or stolen credential from being reused elsewhere. 4. **Is the credential itself genuine and current?** The verifier checks that the PID was really issued by the claimed authority, has not been altered, is still valid, and has not been revoked. 5. **Did I only receive what I actually need?** The verifier confirms that no more personal data was shared than was requested — for example, receiving proof of "over 18" without receiving a full birth date. 6. **Does it meet my own requirements?** Only once all the above are confirmed does the verifier apply its own business rules, such as checking that a required attribute has an acceptable value. ## Why the order matters These checks are performed **in sequence, and stop immediately at the first failure**. A verifier does not, for example, apply business rules to data whose authenticity has not yet been confirmed. This "fail fast" approach avoids wasted effort and — more importantly — avoids ever acting on data that cannot yet be trusted. --- ## Credential Styling Guidance for EAA Providers This guidance is about the wallet-facing presentation of the credential, particularly how metadata controls attribute order and logo rendering. > **Note on Accessibility:** When styling credentials, it's important to consider accessibility requirements. For high-level guidance on designing accessible solutions, see the [Accessibility Design Guidelines](../resources/accessibility_design_guidelines.md). ## 1. Attribute order is defined by `credential_metadata` Many wallets order credential attributes according to the issuer metadata, not the raw schema order. - Use `credential_metadata.display` in your OpenID4VCI issuer metadata to define the display order of attributes. - The wallet will typically render attributes in the order listed in `credential_metadata`, so make sure the sequence reflects the user experience you want. - Place the most important or easy-to-read attributes first, such as name, status, and validity dates. - Avoid relying on arbitrary schema ordering; explicit metadata gives you predictable wallet presentation. ## 2. Supply logo images without padding Logos should be provided in the exact form the wallet expects. - Supply logos with no surrounding padding. - Wallet renderers often add their own spacing; pre-padding can make the logo appear too small or misaligned. - Use simple, centrally aligned icons or images that scale cleanly on mobile screens. ## 3. Use clear, human-friendly attribute labels Wallets can display friendly labels from issuer metadata. - Provide explicit display labels for each attribute in your credential metadata. - Prefer plain-language labels over internal field names or schema paths. - If you support multiple languages, include localized labels in the issuer metadata. - Keep labels concise enough to fit on small screens. ## 4. Test rendering in the target wallet Verify credential appearance with real wallet behavior. - Issue test credentials and inspect them in the EUDI Wallet. - Confirm that attribute order matches the `credential_metadata` sequence. - Check that the issuer logo renders correctly without unexpected margins. --- ## Implementer guide — implementing EAA issuance with OpenID4VCI > Audience: EAA Providers implementing issuance flows with OpenID4VCI in the German EUDI Wallet Ecosystem. --- ## Contents 1. Introduction 2. EAA issuance architecture and infrastructure 3. OpenID4VCI option space and rulebook choices 4. Implementing OpenID4VCI for your EAA configuration 5. Protocol templates (parameterized) 6. Implementation checklists This guide covers building the issuer and getting credentials into wallets. Post-issuance topics — validity, refresh, revocation, key rotation, incident response, and deferred issuance — live on the separate [Credential Lifecycle & Operations](./eaa_lifecycle.md) page. --- ## 1. Introduction This guide targets EAA Providers who want to issue EAAs into German EUDI Wallets using the [OpenID for Verifiable Credential Issuance](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html) protocol (OID4VCI). This guide is not intended to re-document the OID4VCI protocol, but attempts to provide additional conceptual explanation and EUDI-specific considerations. For implementers, it will always be advisable to study the [specification](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html) alongside this document. This document also complements the rulebook guidance: while the rulebook (the human-readable technical and governance specification) defines what your credential is and which level of end-to-end assurance you target, this document explains how to turn those decisions into a concrete OpenID4VCI-based issuance flow. :::info[Understanding Key Terminology] - Catalog of attestations: A registry where you can look up what attestations are issued in the ecosystem - Schema metadata: A machine-consumable entry in the catalog that references the rulebook, schema, and trust list for authorized issuers - Rulebook: The human-readable specification covering technical requirements and governance for a credential type - Trust list: A list of authorized issuers and their certificates for a given credential type ::: :::info[Sandbox: Do I need to start with a rulebook?] No, if you are a prospective EAA Provider who wants to start testing a solution in the Sandbox, you don't need to have a rulebook defined out of the gate. You can start with the implementation of your issuance infrastructure and write/publish your rulebook, trust list and schema metadata when you are ready. ::: ### Known Limitations Before starting implementation, be aware of these current ecosystem limitations: Authorization Code Flow with PID for identification: Using the Authorization Code Flow with the PID for identification depends on extensions to the Authorization Code Flow that still require standardization and is only expected to become available in Q2 2026. There are alternative, though less elegant, ways of combining a PID Presentation with an OpenID4VCI issuance flow. Which solution fits best for you likely depends on your context. See [Section 3.3.1](#331-identification-and-grant-type) for details. ### Document Structure Chapter 2 gives a high-level overview of the components and infrastructure needed to run an issuance service. Chapter 3 explains the OpenID4VCI option space (grant types, proof mechanisms, formats) and how these relate to choices made in your rulebook, in particular around identification. Chapter 4 provides a practical implementation path once you have selected your EAA configuration (format, assurance target, and flow). Once your issuer is live, the [Credential Lifecycle & Operations](./eaa_lifecycle.md) page covers the post-issuance topics: validity, refresh, revocation, key rotation, incident response, and deferred issuance. Where relevant, we call out aspects that are particularly important for the German EUDI Sandbox. This is a living document and guidance may change as technical details are further standardized and clarified. ### 1.1 Quick start — how do I get my credential into the wallet? Annoyingly, the answer is: it depends — mostly on the end-to-end assurance target set by your rulebook. Choosing the grant type and flow is how you implement **phase 3** of the [EAA Provider roadmap](./onboarding/overview.md#your-roadmap-as-an-eaa-provider), once your rulebook (phase 2) is settled. In this guide, we can't cover all possible configurations of OpenID4VCI, so to give implementers something concrete to work with, we lay out two profiles that should apply for many use cases. Although your use case may not fit neatly in one of these two categories, that does not mean that you can't achieve your use case with the appropriate assurance level and user experience. In this guide, we cover the following profiles: - **High fidelity (LoA High or Substantial)** — the credential has legal, regulatory, or safety implications, or the rulebook requires robust identification. Expect strong online identification (PID or equivalent), strict attribute verification at issuance time, and strong holder-binding proofs. → Use the Authorization Code flow (HAIP-like). For a complete, step-by-step walkthrough, see the **[HAIP example flow](./example_flow_haip.md)**. - **Low fidelity (LoA Low)** — event tickets, simple membership attestations, or low-risk attributes. Identification may be prior or optional, attribute verification can be lightweight, and attacker impact is limited. → Pre-authorized codes or QR/deep-link offers are usually sufficient; focus on short-lived, one-time codes. For a complete, step-by-step walkthrough, see the **[low-fidelity example flow](./example_flow_low_fidelity.md)**. Whichever you choose, always reflect the rulebook's stated assurance target in your implementation decisions (identification, token lifetime, refresh policies, and auditing). HAIP is presented as an example high-assurance profile; it is not mandatory to implement. --- ## 2. EAA issuance architecture and infrastructure This chapter describes the main technical building blocks required for EAA issuance and how they relate to your rulebook. ### 2.1 Core components At a minimum, an EAA Provider needs the following components: **Issuance backend** Application logic that determines which users are eligible for which credentials and when. It connects to source systems, implements business rules from your rulebook, and orchestrates issuance flows. **OpenID4VCI credential issuer** An HTTP API exposing the OpenID4VCI endpoints: - Issuer metadata (`/.well-known/openid-credential-issuer`) - Credential offer generation (for example, a QR code or deep link) - Token endpoint (if the authorization server is co-located — see §2.2) - Credential endpoint This component implements the OpenID4VCI protocol and translates between wallet-facing messages and your internal data. **Trust and signing subsystem** Infrastructure to manage signing keys and certificates for EAAs, connection to the trust framework (your EAA Provider registration and trust anchors), integration with the relevant trust lists in the German ecosystem, and key rotation, backup, and revocation. This subsystem must meet the end-to-end assurance target described in your rulebook. **Authentic source integration** The systems that hold the authoritative data for your credential attributes — registries, core databases, or line-of-business systems. Your issuance backend must retrieve and validate data from these systems at issuance time, according to the attribute verification rules in your rulebook. **Logging, monitoring, and audit trail** Logging of issuance events, errors, and security-relevant actions, with retention and access control aligned to your governance requirements. ### 2.2 Optional components Depending on your use case and rulebook decisions, you may additionally run: - User self-service portal Allows holders to request credentials, view status, or revoke/renew credentials (where supported). - Back-office operations console For manual review, exception handling, and audit. - Separate authorization server OpenID4VCI distinguishes between the **authorization server** (which authenticates the user and issues access tokens) and the **credential issuer** (which constructs and signs credentials). These roles can be deployed together or separately. A separate AS is useful when: - Your organization already operates an OAuth 2.0/OIDC server for other services and wants to reuse it for credential issuance - You want to separate authentication from credential construction for security or organizational reasons - Multiple credential issuers share a single authorization server When the AS is separate, your issuer metadata's `authorization_servers` field points to it. The wallet fetches the AS metadata independently and uses its token endpoint. The AS must issue tokens scoped and audience-restricted to your credential issuer. ### 2.3 Security Considerations For each of these components, you should follow best practice for Oauth 2.0 Security. All endpoints involved in the issuance process should be TLS protected. The details are stipulated in the [FAPI2 Security Profile](https://openid.net/specs/fapi-security-profile-2_0.html). ### 2.4 Relation to the rulebook Your rulebook and your issuance infrastructure are tightly coupled: The rulebook defines: - Credential semantics (attributes, formats, identifiers) - End-to-end assurance target - Identification and attribute verification methods - Revocation and update strategies The infrastructure must enforce: - Identification flows compatible with the stated assurance target - Attribute retrieval and checks from the source systems named in the rulebook - Secure operation of the signing and status mechanisms The OpenID4VCI profile you choose (Chapter 3) is one of the key implementation levers to achieve the rulebook’s assurance target. --- ## 3. OpenID4VCI option space and rulebook choices This chapter explains the main OpenID4VCI choices an EAA Provider must make and how they relate to the rulebook. ### 3.1 OpenID4VCI in brief [OpenID4VCI](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html) specifies how wallets obtain verifiable credentials from an issuer. At a high level, the flow is: 1. The EAA Provider creates a credential offer and delivers it to the wallet (for example via QR code or deep link). 2. The wallet and issuer perform an authorization and token exchange (using either authorization code or pre-authorized code). 3. The wallet calls the credential endpoint with proof-of-possession and receives the credential. Your rulebook does not describe protocol details but does define the assurance and governance requirements that your OpenID4VCI deployment must satisfy. OID4VCI flows vary along two key dimensions: who initiates the issuance, and on which device the offer is received. §3.2 covers these; the grant type and other choices follow from them. ### 3.2 Issuance flow variations Two orthogonal dimensions — who initiates the flow, and on which device — shape how you generate and deliver the credential offer. Both are described in [OID4VCI Section 3.3.3](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-issuance-flow-variations). #### Issuer-initiated vs wallet-initiated **Issuer-initiated** (most common): The issuer generates a credential offer — a URI containing the `credential_issuer` URL and the list of offered credential configurations — and delivers it to the user, typically as a QR code or a deep link. The user's wallet scans the QR code or follows the link to start the flow. This is the pattern described throughout the example flows in this guide. **Wallet-initiated**: The wallet starts the flow without a credential offer from the issuer. The wallet may already know what credentials a given issuer provides (for example, from prior configuration or ecosystem discovery), and the user selects a credential to request directly from the wallet UI. In the Authorization Code flow, the wallet sends an authorization request without an `issuer_state` parameter — the issuer has not pre-generated a credential offer. The German EUDI Wallet currently supports the issuer-initiated pattern. If you are designing a wallet-initiated flow, check current wallet documentation for supported variations. #### Same-device vs cross-device **Same-device**: The user is on the same device as their wallet (typically their phone). If you present a credential offer as a deep link (`openid-credential-offer://...`), the OS opens the wallet app directly. This gives a seamless, one-tap experience. **Cross-device**: The user is on a different device from their wallet — for example, on a desktop browser completing a form or an enrollment step. The credential offer is displayed as a QR code that the user scans with their wallet on their phone. The wallet then independently contacts your issuer, while the desktop session waits for the issuance to complete (typically via polling or a server-sent event). Both patterns use the same credential offer structure. The difference is purely in how the offer is presented to the user and which device processes it. Your credential offer endpoint should support both by providing a URI that can be rendered as a QR code for cross-device use and as a deep link for same-device use. See [OID4VCI Appendix H.1](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#appendix-H.1) (same-device) and [Appendix H.2](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#appendix-H.2) (cross-device) for worked examples. ### 3.3 Grant types: authorization code vs pre-authorized code OID4VCI supports two main issuance patterns (see [Section 3.4](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#section-3.4) and [Section 3.5](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#section-3.5)): - Authorization code The holder is redirected to the issuer’s authorization server, performs an interactive authentication and consent step, and the wallet exchanges the authorization code for an access token. - Pre-authorized code The issuer creates a credential offer containing a pre-authorized code that is already bound to a specific user or account. The wallet uses this code directly at the token endpoint (optionally with a one-time user code or PIN) to obtain an access token, without an interactive redirect. #### 3.3.1 Identification and grant type Identification is defined by your rulebook and by the surrounding trust framework, not by the grant type itself. However, there are typical pairings: Authorization code with strong online identification: - Suitable when you identify the user at issuance time via PID or another high-assurance process defined by your rulebook. - The authorization server handles user authentication; the result is linked to issuance. - The rulebook should state that identification happens at issuance and specify the method (for example PID-based online identification or a sector-specific process). Pre-authorized code with prior identification - Suitable when identification is not needed, or has already taken place in a different channel (for example, in-person onboarding at a branch or a fully compliant remote onboarding process). - The issuance flow itself relies on that prior identification and on secure code delivery to the same person. - The rulebook should state that identification is performed before issuance, how it is verified, and how the pre-authorized code is securely delivered and bound to that identified person. In both cases, your end-to-end assurance target from the rulebook must consider: - Strength of user identification (PID, sector process, account history) - Security of the issuance infrastructure (including authorization server, credential issuer, and source systems) - Security of the pre-authorized code lifecycle (generation, delivery, replay prevention) The choice between authorization code and pre-authorized code does not change the assurance target, but it changes where and how you must provide evidence that the target is met. :::info[Retrieving additional information during identification] Implementers should keep in mind that the presentation of a PID does not automatically give you a unique identifier to match against your own records. Consequently, the identification process may require you to retrieve additional data to allow you to make a unique match against internal source information. ::: ### 3.4 Credential formats and rulebook decisions From the rulebook you choose: - Supported credential formats (dc+sd-jwt, mso_mdoc, or both) - Required attributes and their semantics - Requirements on selective disclosure and data minimization From an OpenID4VCI perspective, this leads to: - Definition of credential configuration identifiers (vct values or document types) - Supported proof types and algorithms for holder binding and signing - Mapping between internal attribute names and the technical schemas (JSON Schema, CDDL) defined for this credential type These choices should be consistent with the EUDI and German ecosystem profiles you are targeting. **Example: credential offers reflecting format decisions** If your rulebook specifies SD-JWT VC, the credential offer references the corresponding configuration identifier: ```json { "credential_issuer": "https://city.example/issuer", "credential_configuration_ids": ["city_pass_sd_jwt"], "grants": { "authorization_code": { "issuer_state": "abc123" } } } ``` If your rulebook supports both formats, the offer can list both — the wallet picks the one it supports: ```json { "credential_issuer": "https://city.example/issuer", "credential_configuration_ids": ["city_pass_sd_jwt", "city_pass_mdoc"], "grants": { "urn:ietf:params:oauth:grant-type:pre-authorized_code": { "pre-authorized_code": "xyz789" } } } ``` The values in `credential_configuration_ids` must match exactly the keys you declare under `credential_configurations_supported` in your issuer metadata. See §4.2 and §5.1 for the full metadata structure. ### 3.5 Other relevant options #### Proof-of-possession (PoP) mechanisms The wallet proves it controls the private key that will be bound to the issued credential by sending a proof-of-possession with its credential request. The [OID4VCI spec](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#appendix-F) defines three proof types: **`jwt` (Appendix F.1)** The wallet creates a signed JWT containing the issuer as audience and a `c_nonce` to prevent replay. This is the most widely supported proof type and the one used in the German ecosystem today. **`attestation` (Appendix F.3)** A key attestation-based proof, where the device's secure hardware (e.g., TEE or secure enclave) attests that the key is held in protected hardware. This provides stronger assurance about key security than a plain JWT proof. Support for this proof type is expected in a future version of the ecosystem. **`di_vp` (Appendix F.2)** Data Integrity Verifiable Presentation-based proofs are NOT used in the EUDI Ecosystem. Relevant for credential ecosystems using W3C Data Integrity instead of JOSE. For the German EUDI ecosystem, use `jwt`. Declare this in your credential configuration under `proof_types_supported`: ```json "proof_types_supported": { "jwt": { "proof_signing_alg_values_supported": ["ES256"] } } ``` #### Credential response encryption Credential responses are already protected by HTTPS (TLS) — the same encryption that secures all web traffic. The [OID4VCI spec](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#section-10) also defines an optional additional layer: the wallet can ask the issuer to encrypt the credential response with the wallet's own public key, on top of TLS. This means the credential can only be decrypted by the wallet, even if someone were somehow able to read the TLS traffic. Standard HTTPS is sufficient for the majority of deployments. Adding response encryption requires both the issuer and wallet to implement extra cryptographic logic, and the security benefit is narrow. The cases where you might consider it: - Your infrastructure routes traffic through a TLS-terminating proxy or load balancer that you do not fully control, and you need to ensure credential contents are protected beyond that point - A specific data protection or security policy explicitly requires application-layer encryption of credential data Otherwise, focus on getting TLS right (strong cipher suites, valid certificates) and skip this option. #### Other options - **Multiple credentials in one flow** — The `batch_credential_issuance` capability lets you issue multiple credential instances in a single flow. Declare `batch_credential_issuance.batch_size` in your issuer metadata if you support this (relevant for anti-tracking via multiple unlinkable instances). - **Error handling and retry policies** — For high-assurance flows, define how the wallet should behave on errors at the token and credential endpoints, including appropriate rate limiting and backoff. --- ## 4. Implementing OpenID4VCI for your EAA configuration This chapter outlines a practical path to implement OpenID4VCI issuance once your rulebook and EAA profile are defined. It is the reference for *why* each step exists; for an end-to-end worked walkthrough with concrete payloads, read it alongside the [HAIP](./example_flow_haip.md) and [low-fidelity](./example_flow_low_fidelity.md) example flows. ### 4.1 Step 1 — Derive your issuance configuration from the rulebook From your rulebook and the associated schema metadata entry in the catalog of attestations, extract the following decisions into a concrete OpenID4VCI configuration: - Credential types and formats you will issue (credential identifiers, technical schema references, versions) - End-to-end assurance target and associated controls - Identification method (at issuance time or prior) and how it maps to: - Authorization code flows, or - Pre-authorized code flows - Requirements for attribute verification (which source systems must be queried at issuance time) - Revocation and update behavior (whether the credential supports updates, and how revocation status is exposed) Document this configuration as a separate technical specification for your development team and integrators. ### 4.2 Step 2 — Publish issuer metadata Issuer metadata is the first thing a wallet reads. Before any credential changes hands, the wallet fetches your metadata to discover what you issue, in which formats, which authorization server to use, where your endpoints are, and which proof types and algorithms you accept. If a required field is missing or inconsistent with what you actually implement, the wallet either cannot start the flow or completes it and then rejects the credential. Treat the metadata document as the public contract for your issuer: it must reflect your rulebook decisions about formats, assurance, and supported flows, and it must match what your endpoints actually do. #### How metadata discovery works When a wallet receives a credential offer containing your `credential_issuer` URL, it fetches your issuer metadata from a well-known URI derived from that URL. It then uses your metadata to: 1. Confirm you support the credential configuration(s) listed in the offer 2. Discover your authorization server (from `authorization_servers`), fetch its metadata, and determine the token endpoint 3. Determine which proof types and algorithms you require 4. Fetch a nonce (if you expose a nonce endpoint) before calling the credential endpoint The metadata therefore drives the entire flow. Incorrect or incomplete metadata is one of the most common integration issues. #### Where the wallet looks Per [OID4VCI Section 12.2](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#section-12.2), issuer metadata is served from a well-known URI derived from your `credential_issuer` identifier. The well-known path segment is **inserted between the host and the issuer's path** (following RFC 8414) — it is *not* appended to the end. For an issuer identifier of `https://issuer.example/issuers/playground`, the metadata URL is: ``` https://issuer.example/.well-known/openid-credential-issuer/issuers/playground ``` Appending `/.well-known/openid-credential-issuer` to the end of a path-bearing issuer identifier is a common source of 404s. #### Issuer-level fields These describe the issuer as a whole and where its endpoints live. Each should be derived from a rulebook or infrastructure decision: | Field | Purpose | Driven by | |-------|---------|-----------| | `credential_issuer` | Your issuer identifier (a URL). | Infrastructure | | `authorization_servers` | Array of the authorization server(s) that issue access tokens. May be co-located with the issuer or a separate/chained AS. | Flow & identification design (§3.3) | | `credential_endpoint` | Where the wallet exchanges a token + proof for a credential. | Infrastructure | | `nonce_endpoint` | Where the wallet fetches a fresh proof nonce (see [Proof nonces](#proof-nonces) below). | Infrastructure | | `deferred_credential_endpoint` | Present only if you support deferred issuance. Note the ecosystem wallet-support caveat in [Lifecycle §6](./eaa_lifecycle.md#6-deferred-issuance). | Rulebook (issuance timing) | | `notification_endpoint` | Where the wallet reports issuance outcomes (credential stored, deleted, etc.) per [OID4VCI §10.3](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#section-10.3). Not yet supported by the EUDI Wallet reference implementation — omit until support is confirmed. | Infrastructure | | `status_list_aggregation_endpoint` | Where status information for revocation is published/aggregated. | Rulebook revocation policy ([§vi](./rulebook.md#vi-revocation)) | | `batch_credential_issuance` | Declares support (and `batch_size`) for issuing multiple instances in one flow. | Rulebook (anti-tracking / multiple instances) | | `display` | Issuer name and logo, per locale — the issuer identity the wallet shows the user. | Branding | #### Per-credential configuration `credential_configurations_supported` maps a **configuration id** to the technical definition of one issuable credential. That id is the value wallets place in a credential offer's `credential_configuration_ids`. For each entry: | Field | Purpose | Driven by | |-------|---------|-----------| | `format` | `dc+sd-jwt` or `mso_mdoc`. | Rulebook format decision (§3.4) | | `vct` (SD-JWT) / `doctype` (mdoc) | The credential type identifier. | Rulebook semantic identity | | `scope` | Optional OAuth scope a wallet can request to obtain this configuration. | Flow design | | `cryptographic_binding_methods_supported` | How the credential is bound to the holder's key (e.g. `jwk`). | Rulebook holder-binding policy | | `credential_signing_alg_values_supported` | Algorithms you sign credentials with (e.g. `ES256`). | Rulebook signature requirements ([§v](./rulebook.md#v-credential-signature)) | | `proof_types_supported` | The proof mechanism(s) and algorithms the wallet must use for proof-of-possession (today `jwt`; an `attestation` proof type is expected in future). | Ecosystem profile | | `credential_metadata.display` | Per-credential card rendering (name, logo, colors, background image). | See styling guidance below | #### Proof nonces Proof-of-possession JWTs sent by the wallet to the credential endpoint must include a `c_nonce` to prevent replay. [OID4VCI §7](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#section-7) defines two ways to provide this nonce: **Token or credential response** (current EUDI Wallet behavior): Include `c_nonce` (and optionally `c_nonce_expires_in`) directly in the token response or credential response. The EUDI reference wallet expects the nonce here. ```json { "access_token": "...", "token_type": "Bearer", "expires_in": 3600, "c_nonce": "1a105ffb-0c70-4a83-b86b-8c803de3009d", "c_nonce_expires_in": 86400 } ``` **Nonce endpoint** (optional): Advertise a `nonce_endpoint` in your issuer metadata. The wallet sends an empty `POST` and receives a fresh nonce before each credential request. Implement this if you need to support wallets that use it, but it is not required for the EUDI reference wallet. For compatibility with the EUDI reference wallet, returning `c_nonce` in the token response is sufficient. #### Authorization server metadata If your AS is separate from the issuer (or chained), the wallet discovers it from `authorization_servers` and then fetches the AS's own metadata from its `.well-known/oauth-authorization-server` URI (again using the inserted-path form). A high-assurance authorization server in this ecosystem typically advertises the Authorization Code flow with PAR (`pushed_authorization_request_endpoint`), PKCE (`code_challenge_methods_supported: ["S256"]`), and DPoP (`dpop_signing_alg_values_supported`). These align with the high-fidelity path in §3.2. #### Display and rendering Note the two distinct display layers: issuer-level `display` (your organization's name and logo) and per-configuration `credential_metadata.display` (how the credential card itself renders). For guidance on attribute ordering, labels, and logos, see [Credential Styling Guidance](./credential_styling_guidance.md) rather than duplicating it here. #### A worked example For concrete, current metadata you can inspect a live issuer. [EUDIPLO](https://eudiplo.eudi-wallet.org) is an open-source issuer implementation operated as a public playground; the credential offer for its `university-diploma` configuration points at the issuer `https://eudiplo.eudi-wallet.org/issuers/playground`, whose metadata you can fetch at: ``` https://eudiplo.eudi-wallet.org/.well-known/openid-credential-issuer/issuers/playground ``` :::warning[Illustrative, not normative] EUDIPLO is one example implementation ::: A trimmed view of one credential configuration from that document: ```json "university-diploma": { "format": "dc+sd-jwt", "scope": "diploma", "vct": "urn:eudi:eaa:university-diploma:1", "cryptographic_binding_methods_supported": ["jwk"], "credential_signing_alg_values_supported": ["ES256"], "proof_types_supported": { "jwt": { "proof_signing_alg_values_supported": ["ES256"] } }, "credential_metadata": { "display": [ { "name": "University Diploma", "locale": "en-US", "description": "Academic Degree Certificate", "background_color": "#1e3a5f", "text_color": "#FFFFFF" } ] } } ``` See §5.1 for a parameterized template you can adapt. ### 4.3 Step 3 — Implement authorization and token handling For authorization code flows: - Integrate with your authorization server (or embedded authorization component) that supports: - Strong user authentication according to your rulebook - User consent for issuance - Binding of the authenticated user to the resulting authorization code - Implement the token endpoint logic: - Validate the authorization code - Issue an access token with claims that identify the user and the intended credential configuration - Enforce audience and scope restrictions suitable for issuance For pre-authorized code flows: - Implement secure generation and storage of pre-authorized codes bound to identified users or accounts. - Decide how codes are delivered to users (out-of-band channel, secure portal, etc.), in a way that maintains the rulebook’s assurance requirements. - Implement the token endpoint logic: - Validate pre-authorized codes and optional user codes - Enforce one-time use and expiry - Issue access tokens that carry enough information to drive issuance while respecting data minimization. In both cases, treat the token endpoint as part of your high-assurance infrastructure, with proper rate limiting, logging, and monitoring. ### 4.4 Step 4 — Implement the credential endpoint The credential endpoint is where the wallet exchanges proof-of-possession and an access token for an actual credential. A typical implementation performs the following steps: 1. Validate the access token: - Signature and issuer - Audience and intended use (issuance) - Expiry and freshness 2. Determine the credential configuration: - From the request or from the access token - Check that the requested configuration is allowed for this token and user 3. Validate the proof-of-possession from the wallet: - Check signature validity - Check that the proof is bound to the credential issuer and to the current credential request 4. Fetch and verify attributes from source systems: - Retrieve required data - Apply the attribute verification rules from the rulebook 5. Construct the credential: - Populate all required attributes - Apply the correct technical schema (JSON Schema or CDDL) and credential identifiers (vct or doctype) - Enforce data minimization and selective disclosure requirements 6. Sign and return the credential: - Use the signing keys from your trust and signing subsystem - Apply algorithms and formats that match your credential configuration - Return the credential in the format expected by the wallet. --- ## 5. Protocol templates (parameterized) The templates in this chapter are structurally realistic but parameterized and non-normative. Replace placeholders with the values required by the German ecosystem profile you are targeting. ### 5.1 Credential issuer metadata (template) ```json { "credential_issuer": "https://issuer.example/issuers/eaa", "authorization_servers": ["https://issuer.example/issuers/eaa"], "credential_endpoint": "https://issuer.example/issuers/eaa/vci/credential", "nonce_endpoint": "https://issuer.example/issuers/eaa/vci/nonce", "notification_endpoint": "https://issuer.example/issuers/eaa/vci/notification", "display": [ { "name": "Example EAA Provider", "locale": "en-US", "logo": { "uri": "https://issuer.example/assets/logo.png" } } ], "credential_configurations_supported": { "eaa_example_sd_jwt": { "format": "dc+sd-jwt", "vct": "urn:example:eaa:1", "scope": "eaa_example", "cryptographic_binding_methods_supported": ["jwk"], "credential_signing_alg_values_supported": ["ES256"], "proof_types_supported": { "jwt": { "proof_signing_alg_values_supported": ["ES256"] } }, "credential_metadata": { "display": [ { "name": "Example Credential", "locale": "en-US", "background_color": "#1e3a5f", "text_color": "#FFFFFF" } ] } }, "eaa_example_mdoc": { "format": "mso_mdoc", "doctype": "org.example.eaa.1", "cryptographic_binding_methods_supported": ["cose_key"], "credential_signing_alg_values_supported": ["ES256"], "proof_types_supported": { "jwt": { "proof_signing_alg_values_supported": ["ES256"] } } } } } ``` The token and authorization endpoints are not listed in issuer metadata: wallets discover them from the authorization server metadata at the URL(s) in `authorization_servers` (see §4.2). If your issuer requires `c_nonce` in proof-of-possession proofs — which is recommended — return it in the token response; the EUDI reference wallet expects it there. A `nonce_endpoint` is an optional alternative for wallets that support it. See §4.2 for both approaches. The nonce endpoint is optional. The EUDI Wallet still expects the dpop nonce to be included in the access token. ### 5.2 Credential offers (templates) Authorization code grant: ```json { "credential_issuer": "https://issuer.example", "credential_configuration_ids": ["eaa_example_sd_jwt"], "grants": { "authorization_code": { "issuer_state": "" } } } ``` Pre-authorized code grant: ```json { "credential_issuer": "https://issuer.example", "credential_configuration_ids": ["eaa_example_sd_jwt"], "grants": { "urn:ietf:params:oauth:grant-type:pre-authorized_code": { "pre-authorized_code": "", "tx_code": { "input_mode": "numeric", "length": 6, "description": "Enter the PIN sent to your registered phone number." } } } } ``` Security note: - Treat the pre-authorized code as a bearer secret. Anyone who obtains it may be able to redeem it. - Prefer delivering pre-authorized codes via an authenticated or out-of-band channel (for example, a logged-in portal, in-person handover, or a second channel you already trust for the identified holder). - Use the pre-authorized code in combination with a PIN if needed. - Use compensating controls: short expiry, one-time use, rate limiting, and require a user PIN where appropriate. ### 5.3 Token requests (templates) Authorization code token request: ```http POST /token HTTP/1.1 Content-Type: application/x-www-form-urlencoded grant_type=authorization_code& code=& redirect_uri=& client_id= ``` Pre-authorized code token request: ```http POST /token HTTP/1.1 Content-Type: application/x-www-form-urlencoded grant_type=urn:ietf:params:oauth:grant-type:pre-authorized_code& pre-authorized_code=& tx_code= ``` ### 5.4 Credential request (template) ```http POST /credential HTTP/1.1 Authorization: Bearer Content-Type: application/json { "credential_configuration_id": "eaa_example_sd_jwt", "proof": { "proof_type": "jwt", "jwt": "" } } ``` In the German ecosystem, proof-of-possession at the OpenID4VCI layer uses a JWT proof (`proof_type: "jwt"`). This proof mechanism is independent of the credential format you issue (dc+sd-jwt or mso_mdoc) and is used to bind issuance to a wallet-held key. In the future, the 'attestation' proof_type will also be supported. --- ## 6. Implementation checklists ### 6.1 Decision checklist (rulebook-to-protocol mapping) Before building, ensure your rulebook answers: - When is the holder identified: during issuance (online) or prior to issuance (re-used)? - Which grant type do you support: authorization code, pre-authorized code, or both? - Which credential formats do you issue: dc+sd-jwt, mso_mdoc, or both? - Which source systems must be queried at issuance time? - What is your end-to-end assurance target and which operational controls are mandatory? ### 6.2 Metadata checklist - Metadata is reachable over HTTPS and returns correct content type. - Metadata declares the same capabilities you actually implement: - supported formats and configuration identifiers - supported grant types - token and credential endpoints - supported algorithms and proof types required by the ecosystem profile ### 6.3 Token endpoint checklist - Validate grant type and required parameters. - Enforce expiry and one-time use semantics for codes. - Ensure tokens intended for issuance are not accepted at other APIs and vice versa (audience scoping). - Bind tokens to: - intended credential configuration(s) - intended subject / eligibility record - intended audience (issuance) - Rate limit and log suspicious activity. - Log token grants using pseudonymous identifiers where possible to support audits without violating data minimization. ### 6.4 Credential endpoint checklist - Validate access token (signature, expiry, audience, scope/claims). - Validate wallet proof-of-possession according to the ecosystem profile. - Perform source system lookups and apply the rulebook's attribute verification rules. - Construct credential using the correct technical schemas and credential identifiers (vct for SD-JWT, doctype for mDoc), and enforce data minimization. - Ensure status / revocation information required by the rulebook is correctly attached or referenced. - Sign using protected keys and return the correct format (dc+sd-jwt or mso_mdoc). - Ensure error responses do not leak sensitive information but are specific enough for wallet implementers to debug. --- ## Credential Lifecycle & Operations > Audience: EAA Providers running an issuance service in production. This is **phase 4** of the [EAA Provider roadmap](./onboarding/overview.md#your-roadmap-as-an-eaa-provider) — what happens after you have built and tested your issuer. --- ## Overview Once your issuer is live and credentials are in holders' wallets, you still have ongoing operational responsibilities: keeping credentials current, revoking them when something changes, rotating your signing keys over time, and responding to incidents. Your [rulebook](./rulebook.md) defines the policy — validity periods, refresh conditions, revocation requirements, and signature rules. The [EAA Issuance guide](./eaa_issuance.md) covers building the issuer. This page is about running it. --- ## 1. Credential validity periods Every credential has a validity period. Your rulebook defines the default validity period for your credential type; this section covers the operational aspects of implementing that policy. Implementation considerations: Encode validity dates correctly in the credential format. SD-JWT VC uses `iat` and `exp` claims; mDoc uses `validFrom` and `validUntil` fields. Ensure your issuance logic calculates expiry dates according to your rulebook's policy. Store issuance and expiry dates in your backend systems for tracking and renewal purposes. Use UTC timestamps to avoid ambiguity across time zones. User experience considerations: If your rulebook specifies relatively short validity periods, consider implementing proactive user notifications. Alert holders at meaningful intervals before expiry (for example, 7 days before expiration, adjusted based on your validity period). Direct users to renewal or refresh flows with clear instructions and minimal friction. ## 2. Credential refresh Credential refresh allows holders to obtain an updated version of a credential without repeating the full identification and verification process. Your rulebook defines whether refresh is supported and under what conditions. :::note[Refresh spans build and operate] Refresh is triggered as a credential nears expiry (an operational concern), but it is implemented at the token endpoint you built during issuance (see [EAA Issuance §4.3](./eaa_issuance.md#43-step-3--implement-authorization-and-token-handling)). It is documented here because it is fundamentally a lifecycle concern. ::: ### 2.1 Using refresh tokens (recommended) Refresh tokens are the protocol-native mechanism for credential refresh in OpenID4VCI and are particularly emphasized in the HAIP (High Assurance Interoperability Profile). How refresh tokens work: 1. At initial issuance, when the wallet exchanges an authorization code or pre-authorized code at the token endpoint, the issuer returns both an `access_token` (for immediate credential issuance) and a `refresh_token` (for future credential refresh). 2. When refresh is needed, the wallet presents the `refresh_token` to the token endpoint using the `refresh_token` grant type. 3. The issuer validates the refresh token and returns a new `access_token` (and optionally a new `refresh_token`). 4. The wallet uses the new access token at the credential endpoint to obtain a refreshed credential. 5. The holder does not need to repeat the authorization flow or provide identification again. This approach is protocol-standard, aligned with OAuth 2.0 and OpenID4VCI specifications. It provides minimal user friction since no re-authentication or manual action is required. The wallet can manage refresh automatically as credential expiry approaches, and the issuer retains control through refresh token lifetime and rotation policies. Implementing refresh tokens: Set refresh token expiry based on your rulebook's refresh policy. For example, if credentials are valid for 24 months and can be refreshed without re-identification, the refresh token should remain valid for at least 24 months. Consider implementing refresh token rotation where each refresh operation returns a new refresh token and invalidates the old one. This limits exposure if a refresh token is compromised. If implementing rotation, ensure old refresh tokens cannot be reused after rotation. Provide a mechanism to revoke refresh tokens when a holder's eligibility ends, when the holder requests it, or in case of security incidents. Refresh tokens are long-lived bearer credentials. Ensure wallets store them securely using encrypted storage or hardware-backed keystores where available. Token endpoint implementation for refresh: When the token endpoint receives a refresh token grant: ```http POST /token HTTP/1.1 Content-Type: application/x-www-form-urlencoded grant_type=refresh_token& refresh_token= ``` Your token endpoint should: 1. Validate the refresh token (signature, expiry, revocation status, issuer) 2. Verify the holder's eligibility remains valid (query your internal records or source systems) 3. Check whether attributes have changed since the last issuance 4. Generate a new access token bound to the same credential configuration as the original issuance, the current holder eligibility state, and the credential endpoint audience 5. Optionally return a new refresh token (if implementing rotation) 6. Log the refresh event for audit purposes Handling attribute changes during refresh: When processing a refresh token request, check whether the underlying data has changed (for example, a professional license was suspended, a specialization was added). If attributes have changed, the new credential should reflect current values, not stale data from the original issuance. If attributes changed in a way that invalidates the old credential (for example, license suspended), revoke the old credential in addition to declining refresh. Example refresh token response: ```json { "access_token": "", "token_type": "Bearer", "expires_in": 86400, "refresh_token": "" } ``` The wallet uses the `c_nonce` from the new token response to build its proof-of-possession, then calls the credential endpoint with the new `access_token` to obtain the refreshed credential — exactly as in the original issuance flow. ### 2.2 Alternative refresh approaches If refresh tokens are not suitable for your use case (for example, if your rulebook requires re-authentication or manual approval for renewal), consider these alternative approaches: Self-service portal with on-demand generation: Provide an authenticated portal where holders can request refresh. Verify eligibility and generate a fresh credential offer. This is suitable when refresh requires holder-initiated action or manual approval workflows. Authorization code flow with reduced authentication: If your rulebook permits re-using recent identification evidence (within a defined time window), implement an authorization flow that requires only account authentication (not full re-identification). This is useful for credentials where identification was strong at initial issuance but can be relied upon for a limited time. ### 2.3 Refresh implementation checklist Regardless of which approach you use, you should consider the following topics: - Eligibility verification: Confirm the holder's underlying eligibility remains valid by querying internal records or source systems. - Attribute freshness: Query source systems at refresh time to ensure attribute values are current. Never blindly re-issue stale data. - Attribute change handling: If attributes have changed, issue an updated credential with new values and log the change for audit purposes. - Key binding policy: Determine whether refreshed credentials maintain holder binding to the same key or allow key rotation. Your rulebook should specify this. - Audit logging: Log refresh events with sufficient detail to support audits. - User notifications: If implementing proactive refresh, notify holders when a refresh is available or has occurred. - Error handling: Handle cases where refresh fails (eligibility ended, attributes cannot be verified, etc.) with clear user-facing messages. ## 3. Credential revocation Revocation invalidates a credential before its natural expiry. Your [rulebook defines the revocation policy](./rulebook.md#vi-revocation) including when revocation is required, which mechanism to use, and who can request it. This section covers the technical implementation. Implementing status lists (recommended): If your rulebook specifies status lists (for example, StatusList2021), implement the following components: Status list infrastructure: Maintain a bit array representing credential status (0 = valid, 1 = revoked). Publish the status list at a stable, publicly accessible HTTPS URL. Update the list according to your rulebook's latency requirement (for example, within 4 hours). Credential referencing: Include a status list reference in each credential at issuance time. Specify the status list URL and the credential's index position. For SD-JWT VC, include this in the `status` claim. For mDoc, include it in the appropriate namespace or element. Revocation workflow: Provide an API or back-office interface for authorized parties to submit revocation requests. Validate the requestor's authority based on your rulebook policy. Update the status list by flipping the appropriate bit. Publish the updated list within the latency window defined in your rulebook. Log the revocation event with timestamp, requestor identity, and reason. Privacy considerations: Status lists are fetched by relying parties, not by the issuer on each verification. Holders are not tracked when their credentials are verified. Use sufficiently large status lists to prevent correlation attacks. Operational implementation: Regardless of which revocation mechanism you use, implement the following operational capabilities: Revocation request interface: Provide a web portal, API, or back-office system for authorized parties to request revocation. Authorization checks: Verify that the requestor is authorized according to your rulebook policy (holder via authenticated portal, regulatory authority via trusted channel, etc.). Notification system: Send notifications to affected holders when their credentials are revoked using email, wallet notification, or SMS. Audit logging: Log all revocation requests and actions with sufficient detail to support compliance audits. Monitoring: Monitor revocation infrastructure availability and latency to ensure compliance with your rulebook's SLA. ## 4. Issuer key and certificate rotation EAA Providers sign credentials using cryptographic keys associated with a certificate issued under a trust framework. Over time, you will need to rotate these keys and certificates. Your [rulebook defines signature requirements](./rulebook.md#v-credential-signature); this section covers the operational process of key and certificate rotation. Routine rotation: Key rotation is a normal part of secure operations. Certificates have a defined validity period and must be renewed before expiry. Periodic key rotation reduces risk exposure and is often required by security policies. During rotation, you should support a transition period where both old and new keys are trusted. Implementation approach: Obtain a new certificate from the trust framework before the current certificate expires. Publish the new certificate in the trust registry and update your issuer metadata. Begin signing new credentials with the new key. Continue to maintain the old certificate in the trust registry for the duration of its validity (or longer if credentials signed with it are still in circulation). Wallets and verifiers can validate credentials signed with either key during the transition period. Impact on existing credentials: Credentials issued and signed with the old key remain valid and verifiable as long as the old certificate is still within its validity period, or the trust registry maintains historical certificate information and verifiers are configured to accept credentials signed by expired-but-once-valid issuer certificates (depending on ecosystem policy). Your rulebook and operational documentation should clarify the expected lifetime of credentials relative to issuer certificate lifetimes. ## 5. Security incident response As an EAA Provider, you should prepare for security incidents that may affect the integrity of your issuance infrastructure or the credentials you've issued. While incidents vary in nature and severity, having a response plan helps minimize impact on holders and verifiers. Types of security incidents to consider: Key compromise: Your signing key is exposed or stolen, potentially allowing unauthorized credential issuance. Infrastructure breach: Unauthorized access to your issuance systems, source systems, or holder data. Process failure: Credentials issued with incorrect attributes or to wrong holders. Trust framework changes: Changes in trust framework policy that affect your credentials' validity. Certificate issues: Discovery that your issuer certificate was issued improperly or should be revoked. Example response to key compromise: If your signing key is compromised, you would typically follow these steps. First, contain the incident by securing your infrastructure and preventing further unauthorized issuance. Second, revoke the compromised certificate by submitting a revocation request to the trust framework authority. Third, assess the impact by determining which credentials may be affected and what the timeline of compromise was. Fourth, obtain a new certificate by completing the trust framework registration process with a new key pair. Fifth, notify affected parties by communicating with holders, verifiers, and the ecosystem operator about the incident, timeline, and next steps. Sixth, re-issue credentials by providing a clear process for holders to obtain new credentials signed with the new key. Finally, review and improve security by conducting a post-incident review and implementing measures to prevent recurrence. Impact of certificate revocation: Revoking your issuer certificate has significant consequences. All credentials signed with that key may be considered invalid by verifiers, depending on trust framework policy. Holders will need to obtain new credentials signed with a new, trusted key. You will need to communicate the incident and remediation steps clearly and quickly. Operational planning: Consider including the following in your operational documentation: an incident response plan covering various security scenarios, contact information and escalation paths for trust framework authorities, communication templates for notifying holders and relying parties, processes for mass re-issuance if necessary, roles and responsibilities during an incident, and procedures for testing and exercising your incident response capabilities. ## 6. Deferred issuance Deferred issuance is an OpenID4VCI feature that allows an issuer to accept a credential request but defer the actual issuance until a later time (for example, pending manual review, background checks, or asynchronous data retrieval). Current status in the EUDI ecosystem: Deferred issuance is not yet supported by the EUDI Wallet reference implementation. If your use case requires deferred issuance, you should monitor ecosystem updates for support timelines and consider alternative approaches in the interim (for example, notify the holder out-of-band when the credential is ready and issue a new credential offer). Use cases for deferred issuance: - Manual approval workflows where a credential requires human review. - Credentials that depend on slow or batch-processed source systems. - Credentials with complex eligibility rules that cannot be evaluated in real-time. Planning considerations: If you anticipate needing deferred issuance, document this requirement in your rulebook and integration plans. Design your issuance workflows to be adaptable when deferred issuance becomes available. --- ## Example flow — High-assurance issuance with HAIP > Audience: Developers new to the EUDI Wallet Ecosystem who need to understand how high-assurance credential issuance works in practice. --- ## Overview This document walks through a complete high-assurance credential issuance flow using the [HAIP (High Assurance Interoperability Profile)](https://openid.net/specs/openid4vc-high-assurance-interoperability-profile-1_0.html) pattern, built on [OpenID4VCI](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html). HAIP is designed for credentials that require strong identification and security guarantees — professional licenses, health credentials, government-issued attestations, or other legally significant credentials. This guide covers what HAIP is and when to use it, how the Authorization Code flow works in the context of OpenID4VCI, the complete technical journey from user initiation to credential delivery, and key security considerations. --- ## What is HAIP? [HAIP (High Assurance Interoperability Profile)](https://openid.net/specs/openid4vc-high-assurance-interoperability-profile-1_0.html) is a security profile for OpenID4VCI that specifies: - Strong holder binding using DPoP (Demonstrating Proof of Possession) — tokens are bound to a specific key held by the wallet - Sender-constrained access tokens that cannot be replayed by an attacker who intercepts them - High-assurance identification, typically using PID (Person Identification Data) or an equivalent strong authentication process - Refresh token support for credential lifecycle management Use HAIP when your credential has legal, regulatory, or safety implications and when your rulebook requires robust identification and strong security controls. --- ## When to use this flow Use a HAIP-style flow when: - Your credential requires high assurance (as defined in your rulebook) - Strong online identification is required at issuance time (e.g., PID-based identification) - Holder binding must be cryptographically strong - Long-term credential management is needed (refresh, revocation) - Your credential has legal or regulatory significance Examples: professional licenses, medical credentials, education certificates, employment attestations with legal weight, municipal proof-of-residence (Meldebestätigung), social benefit entitlements, local government permits. --- ## Complete flow walkthrough ### Prerequisites Before issuance can begin: - The EAA Provider has published issuer metadata at a well-known URL - The holder has a EUDI Wallet installed and operational - The EAA Provider has determined the holder is eligible for the credential - The provider has access to authoritative source data for credential attributes --- ### Step 1: Holder identification and eligibility determination The holder completes a high-assurance identification process as required by your credential's rulebook. This typically involves: - PID-based identification: The holder presents their PID (Person Identification Data) credential from their wallet to prove their identity - Alternative strong identification: Depending on your rulebook, this could be sector-specific authentication, eID, or another high-assurance method - Eligibility verification: The EAA Provider queries internal or external source systems to confirm the holder is eligible for the credential PID presentation typically happens via OpenID4VP (Verifiable Presentations). The verifier (EAA Provider) receives authenticated attributes from the PID, maps them to an internal user record or eligibility database, and confirms eligibility (e.g., "this person holds a valid professional license"). **Example (professional credential):** A healthcare professional wants to obtain a digital medical license credential. They visit the licensing authority's portal, which requests their PID to verify their identity. The authority confirms they hold an active medical license in their database. **Example (government credential):** A resident wants a digital proof-of-residence credential. They visit their municipality's portal (Einwohnermeldeamt), which requests their PID to verify their identity. The system confirms their registered address from the civil registry and triggers credential issuance. --- ### Step 2: Credential offer generation Once the holder is identified and eligible, the EAA Provider generates a credential offer and delivers it to the holder. The credential offer is a JSON object that tells the wallet: - Which issuer is offering the credential - Which credential type(s) are available - How to obtain the credential (grant type and parameters) Example credential offer (Authorization Code flow): ```json { "credential_issuer": "https://medical-board.example", "credential_configuration_ids": ["medical_license_sd_jwt"], "grants": { "authorization_code": { "issuer_state": "eyJhbGc...state_token" } } } ``` Delivery methods: - QR code displayed on a web portal (cross-device flow) - Deep link on the same device (same-device flow) The credential offer is encoded and presented to the holder. When scanned or clicked, it triggers the wallet to begin the issuance flow. --- ### Step 3: Wallet initiates authorization The wallet receives the credential offer and begins the [Authorization Code flow](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#section-3.4) by redirecting the holder to the issuer's authorization endpoint: ```http GET /authorize? response_type=code& client_id=wallet.example& redirect_uri=https://wallet.example/callback& state=wallet_state_abc123& code_challenge=sha256_challenge& code_challenge_method=S256& scope=openid credential& issuer_state=eyJhbGc...state_token HTTP/1.1 Host: as.medical-board.example ``` - `code_challenge`: PKCE challenge — prevents an attacker from redeeming a stolen authorization code - `issuer_state`: Links this authorization request to the specific credential offer generated in Step 2 - `redirect_uri`: Where the authorization server returns the result The wallet redirects the holder to the authorization server's authentication page — typically a web page or in-app authentication flow. --- ### Step 4: Holder authentication and consent The holder authenticates at the authorization server and consents to credential issuance. Authentication uses a strong method per the rulebook (eID, username/password + MFA, or re-using recent PID identification). The authorization server binds this authentication session to the authorization request parameters. The holder is then shown what will be issued and explicitly consents: ``` Medical License Board Authorization You are about to receive: - Medical License Credential - Contains: Name, License Number, Specialization, Expiry Date This credential will be issued to your EUDI Wallet. [Approve] [Cancel] ``` In high-assurance flows, the authorization server must verify that the authenticated identity matches the eligibility determination from Step 1, that authentication strength meets the rulebook's requirements, and log the consent event for audit purposes. --- ### Step 5: Authorization code issuance After successful authentication and consent, the authorization server generates an authorization code and redirects the holder back to the wallet ([OID4VCI §3.4](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#section-3.4)): ```http HTTP/1.1 302 Found Location: https://wallet.example/callback? code=auth_code_xyz789& state=wallet_state_abc123 ``` - `code`: The authorization code — short-lived (typically 60–300 seconds), single-use, and PKCE-bound - `state`: The wallet's state parameter, returned unchanged so the wallet can validate the response --- ### Step 6: Token exchange The wallet exchanges the authorization code for an access token and refresh token at the token endpoint ([OID4VCI §6](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#section-6), [RFC 9449 DPoP](https://www.rfc-editor.org/rfc/rfc9449)): ```http POST /token HTTP/1.1 Host: as.medical-board.example Content-Type: application/x-www-form-urlencoded DPoP: eyJhbGc...dpop_proof_jwt grant_type=authorization_code& code=auth_code_xyz789& redirect_uri=https://wallet.example/callback& client_id=wallet.example& code_verifier=original_pkce_verifier ``` **DPoP proof** In HAIP, the wallet attaches a DPoP proof to the token request. A DPoP proof is a short-lived, request-bound JWT that the wallet signs with its private key. It proves the wallet controls that key at this specific endpoint, binding the resulting access token to the wallet's key — so even if the token were intercepted, it cannot be used without the private key. The `DPoP` HTTP header contains this JWT in compact serialization (`base64url(header).base64url(payload).signature`). Below is the decoded structure before signing: ```json // DPoP proof — header { "typ": "dpop+jwt", "alg": "ES256", "jwk": { "kty": "EC", "crv": "P-256", "x": "...", "y": "..." } } ``` ```json // DPoP proof — payload { "jti": "unique-request-id", "htm": "POST", "htu": "https://as.medical-board.example/token", "iat": 1234567890 } ``` `htm` and `htu` bind this proof to the specific HTTP method and URL, preventing replay at any other endpoint. After the wallet signs it, the three parts are base64url-encoded and concatenated with `.` separators to form the compact JWT value sent in the header. **Token response:** ```json { "access_token": "eyJhbGc...access_token_jwt", "token_type": "DPoP", "expires_in": 86400, "refresh_token": "eyJhbGc...refresh_token_jwt", "c_nonce": "1a105ffb-0c70-4a83-b86b-8c803de3009d", "c_nonce_expires_in": 86400 } ``` - `access_token`: DPoP-bound access token for credential issuance - `token_type: "DPoP"`: Signals this is a sender-constrained token requiring a DPoP proof on each use - `refresh_token`: Long-lived token for credential refresh without re-authentication - `c_nonce`: Single-use nonce the wallet must include in its proof-of-possession at the credential endpoint Security characteristics: - The access token is bound to the wallet's DPoP key - The refresh token allows future credential updates without re-authentication - The authorization code is immediately invalidated after exchange --- ### Step 7: Credential request with proof-of-possession Using the `c_nonce` received in the token response, the wallet constructs a proof-of-possession ([OID4VCI Appendix F.1](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#appendix-F.1)) and requests the credential from the credential endpoint ([OID4VCI §8](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#section-8)). ```http POST /credential HTTP/1.1 Host: issuer.medical-board.example Authorization: DPoP eyJhbGc...access_token_jwt DPoP: eyJhbGc...dpop_proof_for_credential_endpoint Content-Type: application/json { "credential_configuration_id": "medical_license_sd_jwt", "proof": { "proof_type": "jwt", "jwt": "eyJhbGc...credential_proof_jwt" } } ``` **Proof-of-possession JWT** ([OID4VCI Appendix F.1](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#appendix-F.1)) The wallet creates a signed JWT proving it controls the private key that will be bound to the credential. Below is the decoded structure before signing: ```json // Proof JWT — header { "alg": "ES256", "typ": "openid4vci-proof+jwt", "jwk": { "kty": "EC", "crv": "P-256", "x": "...", "y": "..." } } ``` ```json // Proof JWT — payload { "iss": "wallet.example", "aud": "https://issuer.medical-board.example", "iat": 1234567890, "nonce": "1a105ffb-0c70-4a83-b86b-8c803de3009d" } ``` The `nonce` ties this proof to this specific issuance request and prevents replay. The `jwk` in the header is the wallet's public key — the issuer will embed this key in the issued credential, creating the cryptographic binding between credential and holder. --- ### Step 8: Credential issuance The issuer validates all proofs and tokens, retrieves authoritative data, constructs the credential, signs it, and returns it to the wallet. 1. Validate access token: - Check signature and issuer - Verify it's intended for this credential endpoint (audience) - Check expiry - Verify it's bound to the DPoP key in the request 2. Validate DPoP proof: - Check signature matches the public key in the access token binding - Verify HTTP method and URL match the request - Check freshness (iat timestamp) 3. Validate credential proof: - Check signature is valid - Verify the nonce matches a `c_nonce` issued by the issuer (unused and unexpired) - Verify audience matches the issuer - Extract the public key that will be bound to the credential 4. Retrieve and verify attributes: - Query source systems for current, authoritative data - Apply attribute verification rules from the rulebook - Ensure the authenticated user matches the credential subject 5. Construct the credential: - Populate all required attributes from source data - Include the holder's public key (from the credential proof) - Add credential metadata (issuer, issuance date, expiry, status reference) - Apply selective disclosure configuration (for SD-JWT) 6. Sign the credential: - Use the issuer's trusted signing key - Apply the appropriate signature algorithm (e.g., ES256) - Ensure the signing key is registered in the trust framework Example credential response (SD-JWT VC): ```json { "credential": "eyJhbGc...signed_credential_jwt" } ``` The credential itself is a signed JWT (for SD-JWT format) or a CBOR-encoded signed structure (for mDoc format). If the credential response also includes a `c_nonce`, the wallet uses it for any subsequent credential request. SD-JWT credential structure (decoded for illustration): ```json Header: { "alg": "ES256", "typ": "vc+sd-jwt", "kid": "issuer-pubkey-1" } Payload: { "vct": "https://medical-board.example/credentials/medical-license/1.0", "iss": "https://issuer.medical-board.example", "iat": 1234567890, "exp": 1297987890, "cnf": { "jwk": { /* holder's public key */ } }, "_sd": [ /* selective disclosure hashes */ ], "name": "Dr. Jane Smith", "license_number": "MD-123456", "specialization": "Cardiology", "status": { "status_list": { "uri": "https://medical-board.example/status/1", "idx": 42 } } } ``` - `vct`: Verifiable credential type identifier - `iss`: Issuer identifier - `exp`: Expiry timestamp - `cnf`: Confirmation claim containing the holder's public key — this is what binds the credential to the wallet - `_sd`: Selective disclosure hashes — the holder can reveal individual claims at presentation without disclosing the rest - `status`: Revocation status reference --- ### Step 9: Wallet stores the credential The wallet receives the credential, validates it, and stores it securely. The wallet checks that the credential is signed by a trusted issuer (verified against the trust framework), that it is currently valid (not yet expired), that the public key in the `cnf` claim matches the wallet's own key, and that all required fields are present. The credential and associated private key are stored in secure storage (hardware-backed where available). The refresh token is stored for future credential renewal. The holder sees a confirmation: ``` ✓ Credential Added Medical License Medical License Board Valid until: 31 Dec 2026 [View Details] ``` --- ## Credential lifecycle: Refresh flow One of the key benefits of HAIP is built-in support for credential refresh using the refresh token. ### When to refresh Credentials should be refreshed: - Before expiry (proactively) - When attributes have changed - When the issuer requests refresh (e.g., via push notification) ### Refresh protocol Step 1: Wallet initiates refresh ```http POST /token HTTP/1.1 Host: as.medical-board.example Content-Type: application/x-www-form-urlencoded DPoP: eyJhbGc...dpop_proof_jwt grant_type=refresh_token& refresh_token=eyJhbGc...refresh_token_jwt ``` Step 2: Authorization server validates and issues new tokens The authorization server: - Validates the refresh token (signature, expiry, revocation status) - Checks the holder's eligibility is still valid - Issues a new access token and optionally a new refresh token ```json { "access_token": "eyJhbGc...new_access_token", "token_type": "DPoP", "expires_in": 86400, "refresh_token": "eyJhbGc...new_refresh_token" } ``` Step 3: Wallet requests refreshed credential The wallet follows the same credential request flow as in Steps 7–8: it uses the `c_nonce` from the new token response, builds the proof, and uses the new access token at the credential endpoint. The holder does not need to re-authenticate or provide identification again. --- ## Security considerations ### Token security DPoP binding: - Access tokens are bound to a specific wallet key via DPoP - Even if an access token is intercepted, it cannot be used without the corresponding private key - This provides sender-constrained token security Short-lived access tokens: - Access tokens should have short lifetimes (typically hours to 1 day) - Reduces window of exposure if compromised Long-lived refresh tokens: - Refresh tokens can be valid for the credential's full lifecycle (months to years) - Must be stored securely and protected against theft - Should support revocation in case of compromise ### Holder binding Cryptographic binding: - The credential includes the holder's public key in the `cnf` claim - During presentation, the holder proves possession of the corresponding private key - Prevents credential theft and replay attacks Key protection: - Private keys should be stored in hardware-backed secure storage - Keys should never leave the secure element - Use device attestation where available to prove key security ### Identification assurance PID-based identification: - Using PID for identification provides high assurance of holder identity - The issuer receives government-verified attributes - The issuer must still map PID attributes to internal records Identification binding: - The authorization server must ensure the authenticated user matches the identified subject - The time gap between identification and issuance should be minimized - Consider re-verification if significant time has passed ### Audit and compliance Logging requirements: - Log all authorization requests, token exchanges, and credential issuances - Include sufficient detail for audit and incident response - Use pseudonymous identifiers to minimize privacy impact - Implement retention policies aligned with regulatory requirements Non-repudiation: - Maintain evidence of holder consent for credential issuance - Log proof-of-possession validation results - Store credential issuance records for the required retention period --- ## Common implementation challenges ### Challenge 1: Refresh token lifecycle management Problem: Long-lived refresh tokens require careful lifecycle management. Solution: - Implement refresh token rotation (issue new refresh token on each refresh) - Provide holder-initiated revocation through a self-service portal - Monitor for suspicious refresh patterns (unusual frequency, location, etc.) - Plan for refresh token expiry and holder re-onboarding ### Challenge 2: Attribute freshness at refresh Problem: Source data may change between issuance and refresh. Solution: - Always query source systems during refresh, not just at initial issuance - Handle cases where eligibility has changed (deny refresh, revoke old credential) - Implement change detection and notify holders when attributes update - Consider versioning credentials if significant changes occur --- ## Implementation checklist ### Before you start - [ ] Rulebook defines high-assurance requirements and identification method - [ ] Trust framework registration is complete (issuer certificate obtained) - [ ] Source systems are accessible and provide authoritative data - [ ] Decision made on PID or alternative identification mechanism ### Authorization server - [ ] Supports Authorization Code flow with PKCE - [ ] Implements strong authentication per rulebook requirements - [ ] Provides user consent interface - [ ] Issues DPoP-bound tokens (if implementing full HAIP) - [ ] Supports refresh token grant - [ ] Logs authentication and authorization events ### Token endpoint - [ ] Validates authorization codes (signature, expiry, PKCE, single-use) - [ ] Validates DPoP proofs - [ ] Issues access tokens bound to DPoP key - [ ] Issues refresh tokens with appropriate expiry - [ ] Implements rate limiting and monitoring ### Credential endpoint - [ ] Validates access tokens (signature, audience, expiry, DPoP binding) - [ ] Validates credential proof-of-possession (signature, nonce, audience) - [ ] Queries source systems for fresh attribute data - [ ] Constructs credentials per technical schema - [ ] Includes holder's public key in credential (cnf claim) - [ ] Signs credentials with registered issuer key - [ ] Includes revocation status reference - [ ] Rejects proofs with missing, reused, or expired nonces ### Metadata - [ ] Publishes credential issuer metadata at the .well-known endpoint (inserted-path form) - [ ] Declares supported credential configurations - [ ] Lists supported proof types and signing algorithms - [ ] Specifies the credential endpoint and references the authorization server(s) (token and authorization endpoints come from the AS metadata); `nonce_endpoint` is optional ### Lifecycle management - [ ] Implements refresh token flow - [ ] Queries source systems during refresh - [ ] Handles attribute changes during refresh - [ ] Implements revocation mechanism (status list) - [ ] Provides holder self-service portal for revocation - [ ] Monitors for expired or expiring credentials ### Security and compliance - [ ] All endpoints use TLS - [ ] Audit logging implemented - [ ] Security incident response plan in place - [ ] Key rotation procedure documented - [ ] Complies with FAPI 2.0 security profile (or equivalent) --- ## Next steps After implementing a HAIP-based high-assurance flow: 1. Test in the sandbox: Use the German EUDI Wallet sandbox to validate your implementation 2. Security review: Conduct security testing and penetration testing 3. Performance testing: Ensure your source systems and issuance infrastructure can handle expected load 4. User acceptance testing: Test the end-to-end user experience with real users 5. Prepare for production: Complete trust framework registration, publish metadata, and prepare operational runbooks --- ## Further reading - [EAA Issuance Implementation Guide](./eaa_issuance.md) - Comprehensive implementation guidance - [OpenID4VCI Specification](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html) - Official protocol specification - [HAIP Specification](https://openid.net/specs/openid4vc-high-assurance-interoperability-profile-1_0.html) - High Assurance Interoperability Profile - [FAPI 2.0 Security Profile](https://openid.net/specs/fapi-security-profile-2_0.html) - Security requirements and best practices --- ## Example flow — Low-fidelity event ticket issuance > Audience: Developers new to the EUDI Wallet Ecosystem who need to understand how low-assurance credential issuance works in practice. --- ## Overview This document walks through a complete low-fidelity credential issuance flow using the Pre-Authorized Code pattern, designed for credentials that don't require strong online identification — event tickets, membership cards, loyalty credentials, or other low-risk attestations. --- ## What is low-fidelity issuance? Low-fidelity (low-assurance) issuance is appropriate for credentials where: - The risk of fraud or misuse is limited and acceptable - Strong online identification is not required by the rulebook - Identification may have happened prior to issuance (e.g., at purchase, registration, or in-person) - The credential's purpose is convenience rather than legal or regulatory compliance The Pre-Authorized Code flow simplifies the issuance process by eliminating the interactive authorization step, making it faster and more user-friendly for low-risk use cases. --- ## When to use this flow Use a low-fidelity Pre-Authorized Code flow when: - Your credential has low risk if misused (limited financial, legal, or safety impact) - Identification happened before issuance (e.g., ticket purchase, membership registration) - You can deliver the pre-authorized code securely to the intended holder (e.g., via email, SMS, authenticated portal) - Your rulebook does not require strong online identification at issuance time - User experience is a priority (quick, frictionless issuance) Examples: event tickets, conference badges, gym memberships, loyalty cards, promotional credentials, low-value vouchers. --- ## Complete flow walkthrough ### Use case example Scenario: A concert ticket issued as a verifiable credential. 1. User purchases a concert ticket online 2. Payment is confirmed 3. Ticket issuer generates a verifiable credential offer 4. User receives a QR code or deep link via email 5. User opens the link in their EUDI Wallet 6. Wallet automatically retrieves the ticket credential --- ### Prerequisites Before issuance can begin, the issuer must publish two metadata documents that the wallet fetches automatically when it receives a credential offer. **Issuer metadata** ([OID4VCI §12.2](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#section-12.2)) is served from a well-known URI. Per the spec, the `.well-known` path segment is inserted between the host and the issuer path — it is not appended. For `https://tickets.festival.example`, the URL is: ``` https://tickets.festival.example/.well-known/openid-credential-issuer ``` Minimal issuer metadata for this flow: ```json { "credential_issuer": "https://tickets.festival.example", "authorization_servers": ["https://tickets.festival.example"], "credential_endpoint": "https://tickets.festival.example/credential", "display": [ { "name": "Festival Tickets GmbH", "locale": "de-DE" } ], "credential_configurations_supported": { "concert_ticket_sd_jwt": { "format": "dc+sd-jwt", "vct": "https://tickets.festival.example/concert-ticket/1.0", "cryptographic_binding_methods_supported": ["jwk"], "credential_signing_alg_values_supported": ["ES256"], "proof_types_supported": { "jwt": { "proof_signing_alg_values_supported": ["ES256"] } }, "credential_metadata": { "display": [ { "name": "Konzertticket", "locale": "de-DE", "background_color": "#1a1a2e", "text_color": "#FFFFFF" } ] } } } } ``` `authorization_servers` points to the issuer's own URL because, in this pre-auth flow, the token endpoint is co-located with the credential issuer rather than on a separate authorization server. The wallet discovers the token endpoint from the **authorization server metadata**, which must be published separately at: ``` https://tickets.festival.example/.well-known/oauth-authorization-server ``` ```json { "issuer": "https://tickets.festival.example", "token_endpoint": "https://tickets.festival.example/token", "grant_types_supported": [ "urn:ietf:params:oauth:grant-type:pre-authorized_code" ] } ``` See [EAA Issuance §4.2](./eaa_issuance.md#42-step-2--publish-issuer-metadata) for the full issuer metadata reference. The `concert_ticket_sd_jwt` key in `credential_configurations_supported` is the identifier wallets place in `credential_configuration_ids` in the offer — they must match exactly. --- ### Step 1: User completes a transaction or registration The holder completes an action that makes them eligible for the credential. This could be purchasing a ticket, registering for an event, signing up for a membership, or completing a form. The issuer's source system records the transaction (order ID, user email, ticket details) and has enough information to generate a credential (name, ticket type, event details, seat number, etc.) and a delivery channel to the holder. Example: ``` Bestellbestätigung Vielen Dank für Ihren Kauf! Veranstaltung: Lollapalooza Berlin 2026 Datum: 12. September 2026 Ticketart: Tagesticket Bestellnummer: ORD-123456 Ticket zum EUDI Wallet hinzufügen: [Zum Wallet hinzufügen] ``` In this flow, identification is not performed during issuance. The issuer relies on the identification that happened during purchase (e.g., the user provided their email and payment details). The pre-authorized code is delivered to that same email, creating a reasonable assurance that the right person receives the credential. --- ### Step 2: Pre-authorized code generation The issuer generates a pre-authorized code bound to this specific transaction and credential. A pre-authorized code must be: - A one-time-use secret token with high entropy (minimum 128 bits; a 32-character base64url value gives ~192 bits) - Short-lived — set expiry based on the use case (hours for urgent redemption, days or weeks for advance purchases) - Bound to a specific credential and holder via an internal order or user ID, so it cannot be redeemed for a different credential - Marked as used atomically upon successful token exchange to prevent replay; use database transactions to prevent race conditions - Optionally protected with a user PIN (`tx_code`) for an additional layer of security --- ### Step 3: Credential offer generation and delivery The issuer creates a credential offer ([OID4VCI §4](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#section-4)) containing the pre-authorized code and delivers it to the holder. ```json { "credential_issuer": "https://tickets.festival.example", "credential_configuration_ids": ["concert_ticket_sd_jwt"], "grants": { "urn:ietf:params:oauth:grant-type:pre-authorized_code": { "pre-authorized_code": "q8fM2xK9pL3wN7vR5tY8jH4bV6nC1mZ0", "tx_code": { "input_mode": "numeric", "length": 4, "description": "PIN sent to your email" } } } } ``` - `credential_configuration_ids` must match a key in your issuer metadata's `credential_configurations_supported` exactly - `pre-authorized_code` is the one-time code for this issuance - `tx_code` signals that the wallet must prompt the user for a PIN before calling the token endpoint To deliver this to the wallet, serialize the JSON offer, URL-encode it, and embed it in the `openid-credential-offer://` URI scheme: ``` openid-credential-offer://?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Ftickets.festival.example%22%2C...%7D ``` For same-device flows (user directly on their phone), render this as a deep link — the OS will open the wallet app: ```html Add to Wallet ``` For cross-device flows (wallet on phone, user browsing on desktop), render the URI as a QR code that the user scans with their wallet. To keep QR codes small and scannable, you can instead host the offer at a URL and reference it with `credential_offer_uri`: ``` openid-credential-offer://?credential_offer_uri=https%3A%2F%2Ftickets.festival.example%2Foffer%2Fabc123 ``` The wallet fetches the offer JSON from that URL. This avoids embedding a large JSON blob in the QR code. Example email delivery: ``` Betreff: Ihr Lollapalooza Berlin 2026 Ticket Ihr Ticket ist bereit! Ticket zum Wallet hinzufügen: Option 1: QR-Code mit Ihrem EUDI Wallet scannen [QR-Code-Bild] Option 2: Auf Ihrem Mobilgerät auf diesen Link klicken [Zum Wallet hinzufügen] Ihr PIN: 8274 (Wird beim Hinzufügen des Tickets benötigt) Dieser Link läuft ab am: 30. August 2026 ``` The pre-authorized code is delivered via the same channel used during purchase (email, SMS, portal), creating a binding between the purchaser and the credential recipient. For low-fidelity credentials, the limited attack surface and bounded misuse impact make this acceptable, even though these channels are not highly secure. --- ### Step 4: Wallet receives the credential offer The holder scans the QR code or clicks the deep link, and the wallet receives the credential offer. The wallet parses the offer, fetches issuer metadata from the `.well-known` endpoint, confirms the offered credential types are supported and the issuer is trusted, and presents the offer to the holder for acceptance. ``` Neues Credential-Angebot Von: Festival Tickets GmbH Typ: Konzertticket Veranstaltung: Lollapalooza Berlin 2026 Datum: 12. September 2026 [Akzeptieren] [Ablehnen] ``` If a PIN is required: ``` Enter PIN The issuer requires a PIN to add this credential. Check your email or purchase confirmation. PIN: [____] [Continue] ``` --- ### Step 5: Token request with pre-authorized code The wallet exchanges the pre-authorized code (and optional PIN) for an access token at the issuer's token endpoint ([OID4VCI §6](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#section-6), [§3.5](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#section-3.5)). ```http POST /token HTTP/1.1 Host: tickets.festival.example Content-Type: application/x-www-form-urlencoded grant_type=urn:ietf:params:oauth:grant-type:pre-authorized_code& pre-authorized_code=q8fM2xK9pL3wN7vR5tY8jH4bV6nC1mZ0& tx_code=8274 ``` The token endpoint: 1. Validates the pre-authorized code: checks it exists, hasn't been used, hasn't expired, and that the PIN matches (if required) 2. Marks the code as used to prevent replay 3. Looks up the order/transaction associated with this code and loads the credential attributes 4. Issues a short-lived access token bound to the credential configuration and order ID (The `c_nonce` for the wallet's proof-of-possession is included in this token response — the wallet uses it in Step 6.) ```python def handle_preauth_token_request(code: str, tx_code: str = None): # Validate code preauth = get_preauth_code(code) if not preauth: return error("invalid_grant", "Code not found") if preauth.used: return error("invalid_grant", "Code already used") if preauth.expires_at < time.time(): return error("invalid_grant", "Code expired") if preauth.tx_code and preauth.tx_code != tx_code: return error("invalid_grant", "Invalid transaction code") # Mark as used mark_code_as_used(code) # Generate access token access_token = create_access_token( subject=preauth.order_id, credential_config=preauth.credential_type, audience="https://tickets.festival.example/credential", expires_in=3600 # 1 hour ) return { "access_token": access_token, "token_type": "Bearer", "expires_in": 3600 } ``` Token response: ```json { "access_token": "eyJhbGc...access_token_jwt", "token_type": "Bearer", "expires_in": 3600, "c_nonce": "nonce_for_credential_proof_xyz789", "c_nonce_expires_in": 3600 } ``` Low-fidelity credentials typically don't include a refresh token — the ticket is single-use or time-limited, so refresh is not needed. The access token is short-lived and the pre-authorized code is one-time use only. --- ### Step 6: Credential request with proof-of-possession Using the `c_nonce` received in the token response, the wallet constructs a proof-of-possession ([OID4VCI Appendix F.1](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#appendix-F.1)) and requests the credential from the credential endpoint ([OID4VCI §8](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#section-8)). ```http POST /credential HTTP/1.1 Host: tickets.festival.example Authorization: Bearer eyJhbGc...access_token_jwt Content-Type: application/json { "credential_configuration_id": "concert_ticket_sd_jwt", "proof": { "proof_type": "jwt", "jwt": "eyJhbGc...credential_proof_jwt" } } ``` **Proof-of-possession JWT** ([OID4VCI Appendix F.1](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#appendix-F.1)) The wallet creates a signed JWT proving it controls the private key that will be bound to the credential. Below is the decoded structure before signing: ```json // Proof JWT — header { "alg": "ES256", "typ": "openid4vci-proof+jwt", "jwk": { "kty": "EC", "crv": "P-256", "x": "...", "y": "..." } } ``` ```json // Proof JWT — payload { "iss": "wallet-client-id", "aud": "https://tickets.festival.example", "iat": 1234567890, "nonce": "nonce_for_credential_proof_xyz789" } ``` The `jwk` in the header is the wallet's public key — this is what will be bound into the issued credential. The `nonce` from the token response ties this proof to this specific issuance request and prevents replay. --- ### Step 7: Credential issuance The issuer validates the request, retrieves ticket data, constructs the credential, signs it, and returns it to the wallet. 1. Validate the access token: verify signature and issuer, check audience (should be the credential endpoint), check expiry, and extract the order ID 2. Validate the credential proof: verify the JWT signature, check that the nonce matches the `c_nonce` issued in the token response and is unused and unexpired, verify the audience matches the issuer, and extract the public key from the `jwk` claim 3. Retrieve credential attributes from the order database using the order ID in the access token 4. Construct and sign the credential using the correct schema, the holder's public key from the proof, and the issuer's registered signing key ```json // Header { "alg": "ES256", "typ": "vc+sd-jwt", "kid": "issuer-signing-key-1" } // Payload { "vct": "https://tickets.festival.example/concert-ticket/1.0", "iss": "https://tickets.festival.example", "iat": 1234567890, "exp": 1720915200, "cnf": { "jwk": { /* holder's public key from proof */ } }, "event_name": "Lollapalooza Berlin 2026", "event_date": "2026-09-12", "event_location": "Olympiagelände, Berlin", "ticket_type": "Tagesticket", "order_id": "ORD-123456", "holder_name": "Anna Müller", "holder_email": "anna.mueller@beispiel.de", "barcode_data": "LOLLA2026-TAG-123456-MUELLERA" } ``` ```json { "credential": "eyJhbGc...signed_credential_jwt" } ``` --- ### Step 8: Wallet stores and displays the credential The wallet receives the credential, verifies the issuer signature, checks expiry, confirms the public key matches the wallet's key, and stores it. ``` ✓ Ticket hinzugefügt Lollapalooza Berlin 2026 Festival Tickets GmbH Datum: 12. September 2026 Ticketart: Tagesticket Ort: Olympiagelände, Berlin Gültig bis: 13. September 2026 [Details anzeigen] [Ticket vorzeigen] ``` --- ## Security considerations for low-fidelity flows ### Pre-authorized code security Use a cryptographically secure random number generator with at least 128 bits of entropy (32 characters base64url gives ~192 bits). Never use predictable patterns or sequential codes. Set expiry based on the use case — hours for urgent redemption, days or weeks for advance ticket purchases. Clean up expired codes from the database. Mark codes as used immediately upon successful token exchange, using database transactions to prevent race conditions under concurrent requests. Log all redemption attempts, successful and failed. ### User PIN protection Consider adding a PIN (`tx_code`) when the delivery channel is less secure (email, SMS), when additional friction is acceptable, or for higher-value low-fidelity credentials such as valuable vouchers. Use 4–8 digit numeric PINs for usability, generated randomly. Where possible, deliver the PIN via a different channel from the QR code (e.g., PIN via SMS, QR via email). Implement rate limiting on PIN attempts (3–5 attempts before lockout). ### Delivery channel security Email: use TLS for transport, include expiry information, and warn users not to forward the email. SMS: be aware of SIM-swapping risk; consider using SMS only for PIN delivery, not the full credential offer. Authenticated portal: delivering via a logged-in portal requires the user to authenticate before retrieving the offer, which provides stronger binding than email or SMS. ### Rate limiting and abuse prevention Limit token endpoint requests per IP address (e.g., 10 attempts per hour) and per pre-authorized code (e.g., 5 attempts total). Monitor for unusual redemption patterns such as bulk redemptions or high failure rates that may indicate brute-force attempts. Log all token requests with timestamps, IP addresses, and user agents. ### Fraud detection Watch for a single user redeeming many codes in a short time (possible account compromise), codes redeemed from unusual locations, or a high volume of failed PIN attempts. Responses can include temporary code lockout, requiring additional verification, or notifying the legitimate holder of suspicious activity. --- ## When NOT to use low-fidelity flows Pre-authorized code flows are not appropriate when: - High assurance is required: legal, regulatory, or high-value credentials - Strong identification must happen at issuance time: rulebook mandates PID or equivalent - Credential refresh is needed: pre-auth codes don't naturally support refresh - Risk of misuse is high: financial credentials, health credentials, identity credentials - Accountability is critical: strong non-repudiation of issuance is required For these cases, use the [Authorization Code flow with HAIP](./example_flow_haip.md) instead. --- ## Comparison: Pre-Authorized Code vs Authorization Code | Aspect | Pre-Authorized Code | Authorization Code | |--------|---------------------|-------------------| | User experience | Fast, minimal interaction | Requires redirect and authentication | | Identification | Prior or not required | At issuance time | | Security | Lower (relies on channel security) | Higher (interactive authentication) | | Complexity | Simple to implement | More complex (authorization server) | | Use case | Low-risk credentials | High-assurance credentials | | Refresh support | Not typically supported | Native refresh token support | | Best for | Tickets, memberships, vouchers | Licenses, health, legal credentials | --- ## Implementation checklist ### Before you start - [ ] Rulebook permits low-fidelity issuance for your credential type - [ ] Identified appropriate use case (low risk, prior identification acceptable) - [ ] Chosen delivery channel (QR code, email, SMS, portal) - [ ] Decided whether to require user PIN ### Issuer metadata - [ ] Issuer metadata published at `/.well-known/openid-credential-issuer` (or path-inserted form per §12.2) - [ ] `credential_configurations_supported` key matches the `credential_configuration_ids` in your offer - [ ] AS metadata published at `/.well-known/oauth-authorization-server` with `token_endpoint` declared ### Pre-authorized code generation - [ ] Use cryptographically secure random number generator - [ ] Generate codes with sufficient entropy (min 128 bits) - [ ] Bind codes to specific orders/transactions - [ ] Set appropriate expiry times - [ ] Store codes securely in database ### Credential offer delivery - [ ] Implement chosen delivery method (QR code, email, SMS, etc.) - [ ] Include user-friendly instructions - [ ] Include PIN (if required) via appropriate channel - [ ] Include expiry information - [ ] Test delivery reliability ### Token endpoint - [ ] Validate pre-authorized code (existence, expiry, not-used) - [ ] Validate user PIN (if required) - [ ] Mark code as used immediately after validation - [ ] Generate short-lived access token, including `c_nonce` and `c_nonce_expires_in` in the response - [ ] Implement rate limiting (per-IP, per-code) - [ ] Log all requests (successful and failed) ### Credential endpoint - [ ] Validate access token (signature, audience, expiry) - [ ] Validate proof-of-possession (signature, nonce, audience) - [ ] Retrieve credential attributes from source systems - [ ] Construct credential with correct schema - [ ] Include holder's public key (cnf claim) - [ ] Sign credential with registered issuer key - [ ] Set appropriate expiry date ### Security measures - [ ] Implement rate limiting on token endpoint - [ ] Monitor for suspicious redemption patterns - [ ] Log all issuance events with sufficient detail - [ ] Implement PIN retry limits (if using PINs) - [ ] Clean up expired codes from database - [ ] Set up alerts for unusual activity ### Testing - [ ] Test successful issuance flow end-to-end - [ ] Verify wallet fetches both metadata documents correctly before the flow starts - [ ] Test pre-authorized code expiry handling - [ ] Test pre-authorized code replay prevention - [ ] Test incorrect PIN handling (if applicable) - [ ] Test rate limiting enforcement - [ ] Test delivery channel reliability (deep link and QR code) --- ## Next steps After implementing a low-fidelity Pre-Authorized Code flow: 1. Test end-to-end: test the complete flow from purchase to credential delivery, starting with verifying the wallet can resolve your metadata 2. Security testing: test rate limiting, code expiry, PIN validation, replay prevention 3. User testing: test with real users to identify friction points in delivery and redemption 4. Monitor and optimize: track redemption rates and optimize expiry windows 5. Support readiness: train support team and document common issues --- ## Further reading - [EAA Issuance Implementation Guide](./eaa_issuance.md) - Comprehensive implementation guidance - [High-Assurance HAIP Flow](./example_flow_haip.md) - For credentials requiring strong identification - [OpenID4VCI Specification](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html) - Official protocol specification --- ## EAA Provider Onboarding This guide provides EAA-specific onboarding requirements and technical integration steps for organizations that want to issue Electronic Attestations of Attributes (EAAs) in the German EUDI Wallet Ecosystem. :::info[Start Here] This guide assumes you have already reviewed the [general onboarding process](../../sandbox/onboarding/joining.md). If you haven't completed the Plan stage and the Kick-off Call, please start there first. ::: --- ## Roles in the Ecosystem Before diving further into the onboarding process, it helps to understand the distinct roles involved in EAA issuance and presentation. Depending on your situation, you may take on one or more of these roles. **EAA Provider (Issuer)** An EAA Provider is an organization that issues EAAs to holders. Importantly, an EAA Provider does not need to write their own Rulebook. If a publicly available Rulebook already covers your credential type, you can adopt it directly and focus your effort on the technical integration. You only need to develop a new Rulebook if no suitable one exists. **Relying Party (Verifier)** A Relying Party is an organization or service that requests and validates credentials from holders. RPs rely on published Rulebooks to understand what a credential means and how to verify it. **Schema Owner** A Schema Owner defines the technical and governance specification for a credential type — what attributes it contains, how it is issued, how it is verified, and what trust model it follows. A Schema Owner may be any organization (a standards body, sector authority, industry association, or any EAA Provider) that has created a credential type and made its credential schema, rulebook, and trust list publicly available for others to adopt. A Schema Owner does this by publishing an entry in the catalog of attestations. The catalog of attestations contains a list of schema metadata entries. The schema metadata contains references to the rulebook, credential schema, and trust list as represented in the figure below. ![Catalog of Attestations](../assets/catalogofattestations.png) --- ## EAA-Specific Requirements ### Organizational Requirements | Requirement | Reason | |-------|------| | Document credential type and use case | The use case should be achievable within the bounds of the sandbox.| | Designated operational contact | The Orchestrator requires a single point of contact with respect to EAA Provider operations.| | Designated technical contact | The Orchestrator requires a single point of contact for technical communication | | Authority to issue the credential type | You must have legal or organizational authority to issue the credential type you're proposing | ### Technical Requirements | Requirement | Reason | |-------|------| | Implement an Issuer Component using OpenID4VCI | You are expected to bring your own software solution to the Sandbox. You can either develop your own solution, integrate an open source component, or employ a third party service provider.| | Run secure HTTPS network services | All issuance endpoints need to be secured using HTTPS.| | Access to authentic data sources | You must have access to authoritative sources for the attributes you plan to issue | ### Security & Compliance Requirements | Requirement | Reason | |-------|------| | Implement data minimization and purpose binding | EAA Providers must have a clear purpose for each credential and implement appropriate privacy protections | | Follow the ecosystem policies | Aside from the technical requirements, Sandbox participation involves behavioral rules that support an open and collaborative ecosystem | --- ## Your Roadmap as an EAA Provider Each phase below links to the page that owns the detail. You do not need to read everything at once — follow the roadmap and branch at phase 2. | Phase | What you do | Where | |-------|-------------|-------| | **1. Plan & confirm readiness** | Define your use case and confirm you meet the requirements above. | This page · [Joining the Ecosystem](../../sandbox/onboarding/joining.md) | | **2. Choose your Rulebook** | Decide whether to **adopt** an existing Rulebook or **create** a new one, then act on that choice. | [Rulebooks: Adopt or Create](../rulebook.md) | | **3. Implement issuance** | Build your OpenID4VCI issuer according to your Rulebook, and test it end to end. | [EAA Issuance](../eaa_issuance.md) · worked examples: [low-fidelity](../example_flow_low_fidelity.md), [high-assurance (HAIP)](../example_flow_haip.md) | | **4. Test & operate** | Validate in the sandbox, then run the credential lifecycle: refresh, revocation, key rotation, and incident response. | [Credential Lifecycle & Operations](../eaa_lifecycle.md) · [Joining the Ecosystem — Operate](../../sandbox/onboarding/joining.md#operate) | :::note[Where this fits in the general onboarding] Choosing a Rulebook and implementing issuance are the EAA-specific part of the **Integrate** stage of the general onboarding process; planning sits in **Plan**, and testing and operations in **Operate**. See the [general onboarding guide](../../sandbox/onboarding/joining.md) for what each stage involves across all roles. ::: --- ## Additional Resources - **Choosing & creating a Rulebook:** Start at [Rulebooks: Adopt or Create](../rulebook.md) to make the adopt-vs-create decision and, if needed, author a new Rulebook. - **Technical implementation:** For detailed issuance specifications and protocol templates, see the [EAA Issuance implementer guide](../eaa_issuance.md). - **Worked examples:** See the [low-fidelity issuance flow](../example_flow_low_fidelity.md) and the [high-assurance HAIP flow](../example_flow_haip.md) for complete, step-by-step walkthroughs. - **Lifecycle & operations:** Once your issuer is live, see [Credential Lifecycle & Operations](../eaa_lifecycle.md) for refresh, revocation, key rotation, and incident response. - **Credential styling:** For the appearance and wallet presentation of your credential, see [Credential Styling Guidance](../credential_styling_guidance.md). - **Operating in the sandbox:** Once integration is complete, return to the [general onboarding guide](../../sandbox/onboarding/joining.md) to learn about the Operate stage. --- ## Rulebooks: Adopt or Create A **rulebook** is the definitive, human-readable, technical, and governance specification for a credential type. It ensures that relying parties understand exactly what a credential contains, how it's issued, and how to verify it. This is **phase 2** of your [EAA Provider roadmap](./onboarding/overview.md#your-roadmap-as-an-eaa-provider). Before you write anything, make the key decision: **can you adopt an existing rulebook, or do you need to create a new one?** As an EAA Provider you do *not* need to author a rulebook if one already covers your credential type — adopting is faster and improves interoperability. :::info[Understanding Key Terminology] - **Rulebook**: Human-readable specification covering technical requirements and governance for a credential type (this document) - **Schema metadata**: Machine-consumable entry in the catalog of attestations that references the rulebook, technical schemas, and trust list for authorized issuers - **Catalog of attestations**: Registry where you can look up what attestations are issued in the ecosystem - **Trust list**: List of authorized issuers and their certificates for a given credential type ::: --- ## Make the decision: adopt or create? Before investing effort in creating a new rulebook, check the [catalog of attestations](https://sandbox.eudi-wallet.org/catalog) for a published rulebook that already covers your credential type. An existing rulebook is **suitable to adopt** if all of the following hold: - It covers the same credential type you intend to issue - Its trust model, attribute structure, and end-to-end assurance target fit your use case - You are willing and able to meet its EAA Provider requirements ```mermaid flowchart TD Q{"Does a suitable rulebook already exist?"} -->|"Yes"| A["Adopt it (see below)"] Q -->|"No / none suitable"| C["Create a new rulebook (see Creating a Rulebook)"] A --> I["Implement issuance (OpenID4VCI)"] C --> I ``` ### If you adopt an existing rulebook This is the lighter path. Your responsibilities are: - Review the rulebook in full and confirm it fits your use case - Confirm you meet the EAA Provider requirements defined in the rulebook - Request to be added as a provider by the rulebook author — this results in your addition to the trust list for authorized issuers - Reference the schema metadata entry from the catalog of attestations in your credential issuer configuration - Implement your issuance service according to the rulebook's technical specification and governance expectations Once you have been added to the trust list, you are done with this phase — continue to [implementing issuance](./eaa_issuance.md). :::info[Credentials that are good candidates for adoption] Not every organization that wants to issue a city transit pass should define their own credential schema — if multiple cities each publish their own pass credential with different attribute names and schemas, relying parties (e.g., apps that accept transit passes) would need to support each one separately. A shared rulebook avoids that fragmentation. Similar logic applies to any credential type that multiple organizations issue for the same purpose: student IDs, professional membership cards, event passes, library cards, or login/authentication credentials. Before starting a new rulebook, check the catalog for existing schemas that cover your need. ::: ### If you create a new rulebook If no suitable rulebook exists, you will need to develop one. This is the more involved path and becomes the most critical part of your EAA Provider journey. Before setting off, it is beneficial to coordinate with other organisations that have similar needs and/or ambitions, and with consumers of your envisaged credential (relying parties). The rest of this page is your guide. --- ## Creating a Rulebook ### 1. Purpose of the Credential **What to include:** - Clear description of what the credential attests to - Intended use cases and scenarios - Users who should receive this credential - Benefits for users and relying parties **Example:** ``` Purpose: This credential attests that the holder is a certified professional engineer licensed to practice in Germany. It enables the holder to prove their professional qualifications to employers, clients, and regulatory bodies without sharing unnecessary personal information. ``` --- ### 2. Applicable Regulations **What to include:** - Specific laws, regulations, or statutes that govern this credential - Industry-specific requirements - Sector regulations that influence issuance or use - Required trust levels **What NOT to include:** - Generic laws that apply to all digital services (GDPR, NIS2, baseline eIDAS requirements) - These provide no additional value and clutter the rulebook **Example:** ``` Applicable Regulations: - Engineers Act (Ingenieurgesetz) § 12-15 - Professional Chambers Act § 8 - EU Professional Qualifications Directive 2005/36/EC - End-to-end assurance target: High ``` --- ### 3. Supported Credential Formats The two credential formats available in the German EUDI ecosystem are SD-JWT VC and ISO mDoc. They differ in encoding, signing technology, presentation context, and implementation complexity. **SD-JWT VC (`dc+sd-jwt`)** SD-JWT VC is based on JSON and JOSE (JWS/JWK) — the same technology stack used for standard OAuth tokens and JWTs. Selective disclosure is implemented via the SD-JWT `_sd` claim mechanism, where individual claims are hashed and the holder can reveal a subset at presentation time. - Presentation typically happens online over HTTP, via redirect or API call - Well-supported in standard OAuth and identity tooling - Simpler to implement than mDoc if your team already works with JWTs **ISO mDoc (`mso_mdoc`)** mDoc (ISO/IEC 18013-5, also known as "mobile document") is based on CBOR and COSE — a binary encoding designed for constrained and offline environments. mDoc supports both online presentation and device-to-device proximity presentation over Bluetooth LE and NFC, without requiring a network connection. - Supports offline/proximity presentation (physical device-to-device) - The dominant format for driving licences and similar in-person credentials - More complex to implement than SD-JWT VC; requires CBOR/COSE tooling **Choosing a format** Your choice should be driven by how your credential will be presented: - If presentation is online (web or app) and no proximity use case exists, SD-JWT VC is the straightforward choice - If proximity presentation (physical, device-to-device without connectivity) is a primary requirement, mDoc is the appropriate format - Supporting both is possible and improves interoperability across presentation contexts, but adds implementation and maintenance complexity **Document in rulebook:** ``` Supported Formats: - Primary: SD-JWT VC (dc+sd-jwt) - Secondary: ISO mDoc (mso_mdoc) — planned for Q3 2026 ``` --- ### 4. Semantic Structure Define all attributes that may appear in your credential. **What to include in your rulebook:** Your rulebook should document all credential attributes and their semantic meaning. **Your credential-specific attributes:** For each attribute, specify: - **Attribute name** (aligned with standards where possible) - **Data type** (string, number, date, boolean, image, etc.) - **Whether mandatory or optional** - **Description and purpose** - **Format/encoding** (if applicable) - **Acceptable values** (if constrained) **Design principles:** - Use existing standards (ISO, W3C, etc.) where available - Align terminology with regulations - Minimize data collection (data minimization) - Harmonize with other EAA Providers of similar credentials - Enable selective disclosure (users should be able to share subsets) **Example:** ```json { "credential_type": "professional_engineer_license", "attributes": { "license_number": { "type": "string", "required": true, "description": "Unique license identifier", "format": "DE-ENG-[0-9]{8}" }, "given_name": { "type": "string", "required": true, "description": "First name(s) of license holder" }, "family_name": { "type": "string", "required": true, "description": "Surname of license holder" }, "date_of_birth": { "type": "date", "required": true, "description": "Birth date (YYYY-MM-DD)" }, "specializations": { "type": "array", "required": false, "description": "Engineering specializations", "values": ["civil", "mechanical", "electrical", "software", "chemical"] }, "issue_date": { "type": "date", "required": true, "description": "Date license was issued" }, "expiry_date": { "type": "date", "required": true, "description": "License expiration date" }, "issuing_chamber": { "type": "string", "required": true, "description": "Name of issuing professional chamber" } } } ``` Be aware that when the updates are made to the attributes in the rulebook, you shall also need to update the credential schema and update the schema metadata entry in the catalog of attestations. When you later create the schema metadata entry for the catalog of attestations, you will include machine-readable references to: - `id` - Unique identifier for this credential type - `version` - Version of the credential schema - `rulebookURI` - URL where this rulebook is published - `supportedFormats` - List of supported credential formats - `schemaURIs` - Links to technical schema definitions - `trustedAuthorities` - Trust anchors for signature verification - `attestationLoS` - Level of Assurance (eIDAS: Low, Substantial, High) - `bindingType` - How credential is bound to user (wallet/claim/biometric) --- ### 5. Trust & Governance Model This section defines the core security design of your EAA. It's the most important part of your rulebook. #### i. Validity Period Balance security risk with user convenience. **Considerations:** - Longer validity = attestation may be revoked before it expires, but better UX - Shorter validity = reduced risk, but more frequent renewal burden - Match underlying evidence validity where possible - Enable credential updates (you must store underlying data) - Best practice: Automate update processes **Guidance:** - Treat EAAs as strong digital representations of the original credential - For long-term storage needs, ensure long-term validation capability like available status lists - Consider the nature of the attributes (how often do they change that would require a reissuance?) **Your rulebook must specify:** - Default validity period for the credential type - Rationale for the chosen period based on attribute volatility, regulatory requirements, and user needs - Whether validity can be extended through refresh mechanisms (see below) **Example:** ``` Validity Period: 24 months Rationale: Professional licenses in our jurisdiction are valid for 2 years. Credential validity matches license validity. Refresh: Supported via automated renewal process. Holders can refresh credentials starting 30 days before expiration without re-identification if their license remains valid. Users notified via wallet notification. ``` #### ii. User Identification How you identify the person requesting the credential. **Options:** - **Not required** - For low-security, non-personal credentials (e.g., event tickets) - **High-assurance** - Using PID or equivalent eIDAS Article 24 process - **Reuse of prior identification** - From account creation, onboarding with 2FA - **Sector-specific methods** - Industry-standard identification processes **Document:** - The method used - How this identification contributes to the overall E2E assurance target - When identification occurs (at issuance, renewal, etc.) **Example:** ``` User Identification: High-assurance via national PID Process: Users must present their national Person Identification Data (PID) at initial license registration. Subsequent renewals use authenticated account access with 2FA. Trust Level: High (eIDAS) ``` #### iii. Attribute Verification How you ensure attributes are accurate and trustworthy. **Specify:** - Whether you are the appropriate source for these data attributes - If not, where data originates and how you verify or trust the source. - Verification methods and evidence sources - Trust level of the verification process - Date of verification (especially if reusing prior verification) **Example:** ``` Attribute Verification: - Source: Chamber Registry Database - Verification: Cross-reference with official examination records and professional training certificates - Process: Automated check against registry, manual review for discrepancies - Trust Level: High - Frequency: Attributes verified at issuance and each renewal ``` #### iv. EAA Provider Requirements Define what it takes to be a legitimate EAA Provider for this credential type. **Document:** - EAA Provider status (authority, chamber membership, license, accreditation) - Registration requirements - Oversight or audit mechanisms **Example:** ``` EAA Provider Requirements: - Status: Accredited Professional Engineering Chamber recognized under the Engineers Act - Trust Anchor: German Professional Chambers Trust List, maintained by Federal Ministry - Registration: Chamber must be registered with State authority and appear on official chambers registry - Audit: Annual review by State oversight body ``` #### v. Credential Signature EAAs must be electronically signed. **Requirements:** - Use certificates linked to your trust anchor - Use signature containers appropriate to the credential format: - Follow BSI/ENISA recommendations - If long-term validity needed, provide long-term storage instructions **Example:** ``` Signature: - Type: Qualified Electronic Seal (QSeal) - Format: JOSE/JWS for SD-JWT VC - Certificate: Chamber QSeal certificate issued by qualified TSP - Algorithm: RSA-PSS with SHA-256 - Long-term validation: Provide verifier guidance for long-term validation needs (e.g., key/cert rollover, archival of status information) when credentials must remain verifiable for many years. ``` #### vi. Revocation Whether and how credentials can be revoked before their natural expiry. **When revocation is necessary:** - Attributes may change (license suspended, privileges revoked) - Credential misuse poses significant risk - Regulatory requirements mandate revocation capability **When revocation may NOT be needed:** - Very short-lived credentials (hours or days) - Low-risk credentials with minimal misuse potential - No mechanism for attributes to become invalid before expiry **Revocation mechanisms:** The revocation mechanism you use is related to your credential format. If you are issuing SD-JWTs, you are required to use a Token Status List. When using mDocs, you should use CRLs. **Your rulebook must specify:** - Whether revocation is supported - Which revocation mechanism(s) are used - Expected maximum latency between revocation event and status update (for example, "within 4 hours") - Who can request revocation (holder, issuer, third party with appropriate authority) - Trigger events that require revocation - How revocation is communicated to affected parties **Revocation best practices:** - High-trust EAAs should support revocation within 24 hours - Use status lists for privacy (no tracking of credential usage) - Avoid OCSP-style lookups that track when/where credentials are used - Provide user notification of revocations **Example:** ``` Revocation: - Supported: Yes - Method: Privacy-preserving status list (Status List 2021) - Timeline: Revocation effective within 4 hours - Trigger events: - License suspension or revocation by regulatory authority - Holder request (lost device, security concern) - Credential issued in error - Security incident affecting credential integrity - Who can request: Holder (via self-service portal), issuing chamber (via back-office system), regulatory authority (via official channel) - User Notification: Email and wallet notification within 1 hour - Relying Party requirement: Must check status list before accepting credential ``` #### vii. Anti-Tracking Measures Prevent correlation of credential usage across relying parties. **Techniques:** - Data minimization by default (issue only what is needed; support selective disclosure where the format allows it) - Avoid stable, globally unique identifiers that allow cross-RP correlation (only include them when legally/operationally required) - Prefer privacy-preserving status checking (avoid per-credential, per-presentation online callbacks that reveal where/when a credential is used) - If you support issuing multiple credential instances, document the operational trade-offs clearly (revocation, renewal, user UX, and relying party expectations) **Example:** ``` Anti-Tracking: - Selective disclosure: Enabled (SD-JWT VC) with clear relying party guidance on requesting only necessary claims - Identifier policy: No globally unique holder identifier included unless required. - Status privacy: Status checking uses a privacy-preserving mechanism and does not reveal presentation events - Relying party policy: Requests must be minimized and proportional to the use case ``` --- ### 6. Registration in the Catalog of Attestations Once you have created your rulebook, you can register your credential type in the **catalog of attestations** by creating a **schema metadata** entry. **What is schema metadata?** Schema metadata is a machine-consumable entry in the catalog of attestations that includes: - Reference to your published rulebook (human-readable) - Links to technical schema definitions (JSON Schema, CDDL, etc.) - Reference to the trust list containing authorized issuers for this credential type - Machine-readable summary of key properties **Benefits of registration:** - Enables discovery of your credential type in the ecosystem - Provides machine-readable information for wallets and relying parties - Links verification methods, signatures, and revocation rules - Allows relying parties to understand and selectively accept specific attributes - Facilitates trust establishment through trust list references Note: The **trust list** referenced in the schema metadata contains the authorized EAA Providers (issuers) and their certificates. This is separate from the catalog of attestations, which is a registry of credential types. --- ## Rulebook Documentation Format Your rulebook should be: - **Machine-readable** (JSON schema or similar) - **Human-readable** (clear documentation) - **Publicly accessible** (published at stable URI) - **Versioned** (clear version control) - **Maintained** (updated as requirements evolve) --- ## Welcome to the EUDI Wallet Developer Guide Welcome to the **German EUDI Wallet Ecosystem Developer Guide**. This resource is designed to help organizations join and participate in the German EUDI Wallet Ecosystem. ## Important Notes :::warning[Sandbox Environment] The ecosystem is under constant development. Solutions developed in the sandbox today may require modifications before entering the production environment when it becomes available. ::: :::info[No Real Data] Only test data can be used in the sandbox environment, usage of real eIDs is not supported. ::: --- **Upcoming Kick-off Calls** Kick-off calls are held on a monthly schedule. Kick-off calls are planned for the following dates: - **Thursday August 13th, 2026** - **Thursday September 17th, 2026** - **Thursday October 15th, 2026** The schedule will continue throughout 2026, but further dates are still to be confirmed. --- ## About the Ecosystem The **German EUDI Wallet Ecosystem** is part of the European Digital Identity initiative under eIDAS 2.0. It enables secure, privacy-preserving digital identity verification across public and private services. **Current Status:** The **Sandbox environment** of the ecosystem is available, with the production environment to follow at a later stage. In the Sandbox, the PID (Person Identification Data) functionality is available for testing and integration. Additionally, participants can define EAA's and their trust corresponding trust frameworks and issue them into the EUDI Wallet. ### Who Should Use This Guide? - **Providers of digital services** seeking secure user verification - **Public sector organizations** modernizing citizen services - **Private sector companies** requiring identity or attribute verification - **Technical teams** responsible for integration and implementation ## Getting Support The ecosystem follows a **self-service-first support model** with comprehensive documentation, shared communication channels, and structured guidance. For broad information on how the ecosystem is meant to operate, visit our [BluePrint](https://bmi.usercontent.opencode.de/eudi-wallet/eidas-2.0-architekturkonzept/) For detailed information about available support resources, see the [Sandbox Support Resources Overview](sandbox/resources/sandbox_support_resources_overview.md). --- ## Next Steps 1. **Explore the integration guides** for [Relying Parties](rp/onboarding/rp_highlevel_onboarding.md) and/or [EEA Issuers](eaa/onboarding/overview.md) to understand the journey ahead 2. **Review the [Readiness Checklist](sandbox/onboarding/sandbox_readiness_checklist.md)** to assess your organization's preparation 3. **Familiarize yourself with [Support Resources](sandbox/resources/sandbox_support_resources_overview.md)** available to you --- **Ready to begin?** See the [Getting Started Guide](sandbox/onboarding/joining.md) → --- ## Accessibility Design Guidelines for RPs and EAA Providers ## Overview When designing solutions for the EUDI Wallet Ecosystem, both Relying Parties (RPs) and European Accessibility Act. Providers must consider accessibility requirements that ensure their services are usable by all citizens, including those with disabilities. This document outlines the high-level accessibility frameworks and key considerations for designing accessible solutions. ## Germany's Accessibility Framework Germany's accessibility framework establishes mandatory requirements for both public and private sector organizations. Understanding these frameworks is essential for organizations operating within the EUDI Wallet Ecosystem. ### Public Sector Accessibility #### Legal Foundation The **Behindertengleichstellungsgesetz (BGG)** (Act on Equal Opportunities of Persons with Disabilities) has been in force since May 1, 2002. It regulates equality for people with disabilities in the area of public law at the federal level and implements the prohibition of discrimination under Article 3, paragraph 3, sentence 2 of the Basic Law: "No person shall be disadvantaged because of their disability." #### Technical Requirements **BITV 2.0** (Barrierefreie-Informationstechnik-Verordnung - Barrier-Free Information Technology Ordinance) implements Section 12b of the BGG and ensures the accessibility of public sector products and services. BITV 2.0 provides the national legal framework while implementing: - The **EU Web Accessibility Directive (EU Directive 2016/2102)** - The **EN 301 549 standard** - The international **WCAG 2.1 (Web Content Accessibility Guidelines) Level AA** criteria ### Private Sector Accessibility #### Legal Foundation The **Barrierefreiheitsstärkungsgesetz (BFSG)** (Accessibility Reinforcement Act) aims to make private sector products and services more accessible for people with disabilities and to strengthen equal participation. #### Technical Requirements The **BFSGV** (Verordnung zum Barrierefreiheitsstärkungsgesetz - Ordinance on the Accessibility Improvement Act) sets out detailed provisions for implementing accessibility requirements for private sector products and services, including websites and online shops. Key accessibility requirements include: **Functional Accessibility** - Identification, authentication, security, and payment functions must be designed to be perceivable, operable, understandable, and robust **Multi-Sensory Communication** - Information must be provided through more than one sensory channel **Visual Presentation** - Appropriate font type, size, and form - Sufficient contrast and spacing between lines, letters, and paragraphs **Alternative Content** - Alternative representations for images and other non-text content ## Key Considerations for EUDI Wallet Solutions ### For Relying Parties When integrating with the EUDI Wallet, RPs should: - Ensure presentation request flows are accessible to users with varying abilities - Provide clear, understandable authentication and data verification flows - Test credential presentation and verification processes with accessibility tools ### For EAA Providers When issuing credentials within the EUDI Ecosystem, EAA Providers should: - Ensure credential data is structured to be machine-readable and accessible - Consider accessibility in credential styling and presentation - Design issuing flows that accommodate users with disabilities - Follow WCAG 2.1 Level AA guidelines for any user-facing interfaces --- ## Changelog This page aggregates relevant changes in the German EUDI ecosystem for Relying Parties and EAA Issuers. Where possible, upcoming changes are listed in advance so that integrators can prepare. --- ## PID Provider :::note Below are only excerpted entries, the full change log is available from the [PID Provider release page](https://preprod.pid-provider.bundesdruckerei.de/releases). ::: ### 1.27.0 — May 27, 2026 - Removed support for the `proof` parameter in the credential request as it is no longer part of the [OpenID4VCI 1.0 credential request](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-credential-request). ### 1.26.1 — May 20, 2026 - Fixed a regression in `DPoP-Nonce` handling introduced in 1.26.0 ### 1.25.0 — April 29, 2026 - Added lifetime parameters for the refresh token in the token endpoint response ### 1.24.0 — April 16, 2026 - Adjusted PID in credential configuration `pid-mso-mdoc_2-beta`: - `expiry_date` changed from `timestamp` to type `full-date` - `issuance_date` removed - `validFrom`, `signed`, and `validUntil` are set to 00:00 in the Berlin time zone and represented in UTC (e.g. `2003-12-12T23:00:00Z`) - Adjusted PID in credential configuration `pid-sd-jwt_2-beta`: - `date_of_expiry` added as a string with format `YYYY-MM-DD` - `nbf` added; `iat` removed - `nbf` and `exp` are set to 00:00 in the Berlin time zone and represented in UTC ### 1.23.0 — April 1, 2026 - Replaced the access token with an [RFC 9068](https://datatracker.ietf.org/doc/html/rfc9068)-compliant JWT referencing the seed credential - Replaced the refresh token to be self-contained, including the seed credential - Introduced a [changelog of PID credential configurations](https://preprod.pid-provider.bundesdruckerei.de/credential-configuration-changelog) ### 1.22.0 — March 18, 2026 - Added `also_known_as` and `academic_title` claims to `mdoc` - Added `also_known_as` and `title` claims to `sd-jwt` - Removed the trust anchor from the Status List token certificate chain (`x5c` header in JWT/CWT) ### 1.21.0 — March 4, 2026 - Upcoming schema changes to credential configurations: - `birth_date` now supports partially unknown dates (missing day or month) - `birth_name` updated to use uniform representation - `place_of_birth` updated to use uniform representation, including a `no_place_info` flag ### 1.20.0 - February 18, 2026 - mdoc doctype and namespace corrections - The mdoc PID doctype will change to `eu.europa.ec.eudi.pid.1`. - Claims defined in the ARF Rulebook will be placed in the namespace `eu.europa.ec.eudi.pid.1`. - Claims defined in the German PID Rulebook but not in the ARF will be placed in the namespace `eu.europa.ec.eudi.pid.de.1`. - Note that no claims are added or removed, only the doctype and namespace assignments change. - Age-over claims aligned with ARF and German PID Rulebook - **mdoc**: PID will include `age_over_12`, `age_over_14`, `age_over_16`, `age_over_18`, `age_over_21`, and `age_over_65` claims (`bool`) under the namespace `eu.europa.ec.eudi.pid.de.1`. Each claim is selectively disclosable. - **SD-JWT VC**: PID will include an `age_equal_or_over` object with members `12`, `14`, `16`, `18`, `21`, and `65` (`bool`). Both the top-level object and each member are selectively disclosable. - Values reflect whether the holder had reached age *n* on the date of issuance (`birth_date + n years ≥ validFrom` / `nbf`). If the birth date is partial (day or month is unknown), the latest possible date is used, meaning a `true` value guarantees the holder is at least that age. - Claims are recalculated on every new PID issuance. ### 1.19.0 — February 04, 2026 - Stores personal eID data as encrypted and signed JWT ### 1.18.0 — January 21, 2026 - Adds `/health` endpoint for external health check - Adjusts values for `source_document_type` in PID - Removes `age_in_years` and `age_birth_year` from PID --- ## German EUDI Wallet ### Android The Android wallet app changelog is maintained [here](https://github.com/german-national-wallet/issues-tracker-android#changelog). --- ### iOS The iOS wallet app changelog is maintained [here](https://github.com/german-national-wallet/issues-tracker-ios#changelog). --- ## Glossary This page defines terms, abbreviations, and acronyms used across the German EUDI Wallet Ecosystem documentation. Entries are drawn from the [Blueprint](https://bmi.usercontent.opencode.de/eudi-wallet/eidas-2.0-architekturkonzept/), the [Wallet Architecture Documentation](https://bmi.usercontent.opencode.de/eudi-wallet/wallet-development-documentation-public/latest/), and this [Developer Guide](https://bmi.usercontent.opencode.de/eudi-wallet/developer-guide/). Each entry links to the primary source specification or regulation where applicable. --- ## A **Access Certificate** An X.509 certificate issued to a Relying Party by a national Registrar that authenticates the RP's legal identity to the EUDI Wallet during a presentation request. It is distinct from the Registration Certificate and must be included directly in OID4VP requests. RPs may hold multiple Access Certificates for key rotation purposes, and revoked certificates must remain available via the Registrar for ten years. Defined under [ETSI TS 119 411-8](https://www.etsi.org/deliver/etsi_ts/119400_119499/11941108/01.01.01_60/ts_11941108v010101p.pdf). See also: [Registration Certificate](#registration-certificate), [Registrar](#registrar), [Overasking Protection](#overasking). **ARF – Architecture and Reference Framework** The European Commission's toolbox for EUDI Wallet implementations across all member states, comprising technical architecture specifications, common standards, guidelines, and best practices. The German ecosystem implementation is derived from and aligned with the ARF. Source: [ARF on GitHub](https://github.com/eu-digital-identity-wallet/eudi-doc-architecture-and-reference-framework). **Attestation Catalog (also: Catalog of Attestations)** A public registry maintained by the Orchestrator listing all EAA types available within the German EUDI Wallet ecosystem. Each entry references the corresponding Rulebook, JSON schema (Schema Metadata), and Trust List for authorised issuers. EAA Providers publish their schema metadata to the Catalog to make their credential type discoverable by wallets and verifiers. See the [Catalog of Attestations](https://bmi.usercontent.opencode.de/eudi-wallet/eidas-2.0-architekturkonzept/content/features/EAA/catalog-of-attestations/) section of the Blueprint. **Attribute** A single piece of identity or qualification data within a credential, for example `family_name`, `birthdate`, or `driving_privileges`. Attributes can be selectively disclosed to Relying Parties using the SD-JWT or mDoc selective disclosure mechanisms. **Authentication** The process of verifying that a claimed identity is valid — proving that the user is who they claim to be. Distinct from *Identification* (claiming an identity) and *Authorization* (granting access rights). Source: [eIDAS 2.0, Article 3](https://www.europarl.europa.eu/doceo/document/TA-9-2024-0117_EN.html). **Authorization** In the OID4VCI context, Authorization refers to the OAuth 2.0 grant flows that allow a wallet to obtain a credential from an issuer. Source: [eIDAS 2.0](https://www.europarl.europa.eu/doceo/document/TA-9-2024-0117_EN.html); [OAuth 2.0 (RFC 6749)](https://datatracker.ietf.org/doc/html/rfc6749). **Authorization Code Flow** An OID4VCI grant type following standard OAuth 2.0 authorization code semantics, where the wallet initiates an authentication step with the issuer's authorization server before obtaining a credential. Supports PID-based identification during EAA issuance. See [OID4VCI](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html). --- ## B **Blueprint (BMI Blueprint)** The public architecture and governance reference document for the German EUDI Wallet ecosystem, published on OpenCoDE. It proposes architectural, governance, and operational models aligned with eIDAS 2.0 and the ARF. Not a final binding specification, but the authoritative design basis for the German ecosystem. Published at [bmi.usercontent.opencode.de/eudi-wallet/eidas-2.0-architekturkonzept/](https://bmi.usercontent.opencode.de/eudi-wallet/eidas-2.0-architekturkonzept/). **Bundesdruckerei (BDR)** Designated PID Provider for the German EUDI Wallet. Bundesdruckerei issues PID credentials derived from chip data of the Personalausweis (German national identity card) or electronic residence permit. --- ## C **CBOR – Concise Binary Object Representation** A compact binary data encoding format used as the serialisation basis for ISO mDoc credentials. CBOR is the mDoc equivalent of JSON for SD-JWT VC. Defined in [RFC 7049](https://datatracker.ietf.org/doc/html/rfc7049) / [RFC 8949](https://datatracker.ietf.org/doc/html/rfc8949). **CIR 2024/2977** Commission Implementing Regulation (EU) 2024/2977. The EU Commission's implementing act specifying the mandatory and optional attribute set for the PID, along with technical format requirements applying to all member states. This is the normative source for the German PID attribute catalogue. **CIR 2024/2979** Commission Implementing Regulation (EU) 2024/2979. Specifies the requirements for electronic identification means at each Level of Assurance. Sets out security requirements, including two-factor authentication and resistance to cloning, that the German wallet's authentication mechanism must satisfy, particularly for PID issuance at LoA High. **Credential** A digitally signed, verifiable data structure issued into a wallet that represents a set of attributes about the holder. Credentials in the German EUDI Wallet ecosystem are issued in SD-JWT VC or ISO mDoc format. Also referred to as a *Verifiable Credential*. See [OID4VCI](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html). **Credential Offer** An OID4VCI mechanism through which an issuer initiates a credential issuance flow by presenting a URI (`openid-credential-offer://`) or a reference URL (`credential_offer_uri`) to the wallet. The wallet fetches the offer object, which contains the issuer URL and credential configuration identifiers. Source: [OID4VCI spec](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html). **CSC API – Cloud Signature Consortium API** A standardised API for interoperable remote digital signing operations. Version 2 (CSC API v2) is referenced in the German ecosystem for future QES integration. Source: [CSC API v2.2.0.0](https://cloudsignatureconsortium.org/wp-content/uploads/2025/11/csc-api.pdf). --- ## D **DCQL – Digital Credentials Query Language** A structured query language used within OID4VP presentation requests to specify exactly which credential types and attributes a Relying Party requires from the wallet. DCQL is the query format used in HAIP-compliant flows, superseding the earlier Presentation Exchange format for this profile. Specified in the [OID4VP spec](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html). **DPoP – Demonstrating Proof of Possession** An OAuth 2.0 mechanism that cryptographically binds an access token to the client that requested it, preventing misuse by a third party in possession of the token. Used in OID4VCI and OID4VP flows to bind tokens to the wallet's key material. Source: [DPoP draft (IETF)](https://datatracker.ietf.org/doc/draft-ietf-oauth-dpop/). --- ## E **EAA – Electronic Attestation of Attributes** A digitally signed credential issued into the EUDI Wallet by an authorised EAA Provider, attesting to one or more attributes of the holder beyond the core identity (PID). Examples include driver's licences, academic degrees, professional qualifications, and health records. EAAs are legally equivalent to their paper counterparts across the EU when issued by a recognised provider. Defined in [eIDAS 2.0, Article 3(44)](https://www.europarl.europa.eu/doceo/document/TA-9-2024-0117_EN.html). **EAA Provider (also: EAA Issuer)** An organisation authorised to issue EAAs into a user's EUDI Wallet. EAA Providers define and publish a Rulebook for each credential type, register with the Orchestrator, and implement an OID4VCI-compliant issuance endpoint. Both public sector bodies and private organisations may act as EAA Providers. **eID (German eID system)** Germany's electronic identity system based on the chip in the Personalausweis (national ID card), electronic Residence Permit, and eID Card for Union Citizens. The eID chip and its Online-Ausweis functionality are the trust anchor for PID issuance in the German EUDI Wallet. Architecture specified in [BSI TR-03127](https://www.bsi.bund.de/DE/Themen/Unternehmen-und-Organisationen/Standards-und-Zertifizierung/Technische-Richtlinien/TR-nach-Thema-sortiert/tr03127/TR-03127_node.html). **eID-Server** A server implementing Online Authentication based on Extended Access Control Version 2 (EAC2) between an eService and an eIDAS token (e.g. the German national ID card). Used in the PID issuance flow to authenticate users via their Personalausweis chip. Specified in [BSI TR-03130](https://www.bsi.bund.de/DE/Themen/Unternehmen-und-Organisationen/Standards-und-Zertifizierung/Technische-Richtlinien/TR-nach-Thema-sortiert/tr03130/TR-03130_node.html). **eIDAS 2.0** Regulation (EU) 2024/1183 of the European Parliament and of the Council, amending eIDAS (Regulation (EU) No 910/2014). The legal foundation for the EUDI Wallet, requiring all member states to make a wallet available to citizens and mandating acceptance by public sector bodies, Very Large Online Platforms, and certain regulated private sector entities. Source: [eIDAS 2.0 full text](https://www.europarl.europa.eu/doceo/document/TA-9-2024-0117_EN.html). **ERICA** An open-source debug and testing tool for the German EUDI Wallet ecosystem. Allows developers to inspect credential issuance and presentation flows, simulate wallet interactions, and diagnose protocol-level issues. Hosted at [gitlab.opencode.de/bmi/eudi-wallet/erica](https://gitlab.opencode.de/bmi/eudi-wallet/erica). Documentation in the [Developer Guide](https://bmi.usercontent.opencode.de/eudi-wallet/developer-guide/sandbox/resources/erica/). **ETSI – European Telecommunications Standards Institute** The European standards body responsible for defining technical specifications for trust services, electronic signatures, and the EUDI Wallet ecosystem. Key ETSI standards referenced in the German ecosystem include the TS 119 4xx series (trust service requirements), TS 119 472 series (EAA/PID attestation profiles), and TS 119 612 (Trusted Lists). See the full [standards list in the Blueprint](https://bmi.usercontent.opencode.de/eudi-wallet/eidas-2.0-architekturkonzept/content/ecosystem-vision-and-fundamentals/standards/#etsi-standards). **EUDI Wallet (EUDIW) – European Digital Identity Wallet** An electronic identification means that allows a user to securely store, manage, and share identity data and Electronic Attestations of Attributes, to provide them to Relying Parties, and to create Qualified Electronic Signatures or Seals. Member states are required to make wallets available to their citizens under eIDAS 2.0. Defined in [eIDAS 2.0](https://www.europarl.europa.eu/doceo/document/TA-9-2024-0117_EN.html). --- ## H **HAIP – High Assurance Interoperability Profile** A profile of the OpenID for Verifiable Credentials specifications (OID4VCI + OID4VP) that selects a specific, constrained set of protocol options to ensure interoperability at high assurance levels. HAIP is the primary normative profile for the German EUDI Wallet ecosystem. Specifies use of SD-JWT VC and ISO mDoc formats, DCQL for presentation queries, and FAPI 2.0-aligned security mechanisms. Source: [OpenID4VC High Assurance Interoperability Profile 1.0-draft 04](https://openid.net/specs/openid4vc-high-assurance-interoperability-profile-1_0-04.html). **HSM – Hardware Security Module** A physical device providing cryptographic functions where the lifecycle of cryptographic keys and the execution of cryptographic operations occur within a highly protected hardware environment. The Wallet Backend uses an HSM to sign Wallet Instance Attestations (WIA) and manage long-term signing keys. See [Wallet Architecture Documentation, Section 4.3](https://bmi.usercontent.opencode.de/eudi-wallet/wallet-development-documentation-public/latest/architecture-concept/04-wallet-backend/02-wia-issuance.html). **Holder (also: User, Identity Owner)** A natural or legal person who receives credentials from issuers, stores them in a EUDI Wallet, and controls their presentation to Relying Parties. Also referred to as the *User* in eIDAS 2.0 and the *Identity Owner* in ARF terminology. Source: [ARF 1.2.0](https://github.com/eu-digital-identity-wallet/eudi-doc-architecture-and-reference-framework). --- ## I **Identification** The process of claiming or presenting an identity, typically involving presentation of an identifier. Distinct from *Authentication* (proving the identity) and *Authorization* (granting rights). Source: [eIDAS 2.0](https://www.europarl.europa.eu/doceo/document/TA-9-2024-0117_EN.html). **ISO/IEC 18013-5** The international standard specifying the interface for a mobile driving licence (mDL), including the credential format (mDoc/CBOR), device engagement mechanisms, and the interface between the mDL and reader. Adopted in the EUDI Wallet ecosystem as one of the two primary credential formats for PID and EAAs. Source: [ISO/IEC 18013-5:2021](https://www.iso.org/standard/69084.html). **Issuer** An entity that issues a Verifiable Credential to a Holder. In the EUDI Wallet context: the PID Provider issues PID; EAA Providers issue EAAs. Sometimes referred to as *Provider*. Source: [OID4VCI](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html). --- ## J **JOSE – JSON Object Signing and Encryption** The IETF framework ([RFC 7515–7520](https://datatracker.ietf.org/doc/html/rfc7515)) covering JSON Web Signatures (JWS), JSON Web Encryption (JWE), JSON Web Keys (JWK), and JSON Web Algorithms (JWA). SD-JWT VC credentials and WIA tokens are built on JOSE primitives. **JWT – JSON Web Token** A compact, URL-safe format for representing claims as a JSON object, signed and optionally encrypted. The base format for SD-JWT VC credentials and Wallet Instance Attestations in the German EUDI Wallet ecosystem. Source: [RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519). --- ## K **KB-JWT – Key Binding JWT** An optional component of an SD-JWT presentation that cryptographically proves the presenter is the legitimate holder of the credential. The Key Binding JWT is signed with the holder's private key and binds the presentation to a specific audience and nonce, preventing replay attacks. Part of the [SD-JWT (RFC 9901)](https://www.rfc-editor.org/rfc/rfc9901.html) specification. --- ## L **LoA – Level of Assurance** A classification of the trustworthiness of an identity verification or authentication process, defined by eIDAS as Low, Substantial, or High. The German PID is issued at LoA High, based on the eID chip authentication of the Personalausweis. CIR 2024/2979 sets the security requirements for each LoA. Sources: [eIDAS Levels of Assurance](https://ec.europa.eu/digital-building-blocks/sites/display/DIGITAL/eIDAS+Levels+of+Assurance); [BSI TR-03107](https://www.bsi.bund.de/DE/Themen/Unternehmen-und-Organisationen/Standards-und-Zertifizierung/Technische-Richtlinien/TR-nach-Thema-sortiert/tr03107/TR-03107_node.html). --- ## M **mDoc / ISO mDoc** The binary credential format defined by ISO/IEC 18013-5, encoded in CBOR. Used as one of the two primary credential formats in the German EUDI Wallet ecosystem alongside SD-JWT VC. In the mDoc format, the German PID uses `docType: eu.europa.ec.eudi.pid.1`; Germany-specific extension attributes are in the namespace `eu.europa.ec.eudi.pid.de.1`. See [ISO/IEC 18013-5](https://www.iso.org/standard/69084.html). **MDVM – Mobile Device Vulnerability Management** A component of the German EUDI Wallet architecture that continuously monitors identified vulnerabilities in the hardware key store (HKS) and operating system of user devices. The MDVM issues `mdvm_token` attestations that the Wallet Backend uses to assess device security posture before issuing Wallet Instance Attestations (WIA). If an exploitable vulnerability is known for a device, the use of RWSCD keys is prevented. See [Wallet Architecture Documentation, Section 6](https://bmi.usercontent.opencode.de/eudi-wallet/wallet-development-documentation-public/latest/architecture-concept/06-mdvm/01-mdvm-concept.html). --- ## O **OID4VC – OpenID for Verifiable Credentials** The collective name for the OpenID Foundation specification family covering credential issuance (OID4VCI) and presentation (OID4VP). The core protocol family used throughout the German EUDI Wallet ecosystem. **OID4VCI – OpenID for Verifiable Credential Issuance** The OpenID Foundation protocol specification defining an API for issuers to issue Verifiable Credentials into a wallet. Defines the Credential Offer, Authorization Endpoint, Token Endpoint, and Credential Endpoint flows. The issuance protocol used by all EAA Providers in the German EUDI Wallet ecosystem. Source: [OID4VCI v1.0](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html). **OID4VP – OpenID for Verifiable Presentations** The OpenID Foundation protocol specification defining a mechanism on top of OAuth 2.0 for Relying Parties to request and receive Verifiable Credentials from a wallet. Defines the authorisation request, presentation exchange using DCQL, and response flows. The presentation protocol used by all Relying Parties in the German EUDI Wallet ecosystem. Source: [OID4VP v1.0](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html). **Orchestrator** The entity responsible for operating the German EUDI Wallet ecosystem infrastructure, including the Trusted List, the Attestation Catalog, the sandbox environment, and ecosystem governance. Currently operated by SPRIND (Bundesagentur für Sprunginnovationen) on behalf of BMDS. See [Blueprint – Governance & Operation Model](https://bmi.usercontent.opencode.de/eudi-wallet/eidas-2.0-architekturkonzept/content/ecosystem-vision-and-fundamentals/governance-and-operation-model/). **Overasking** A situation where a Relying Party requests more attributes from a user than is necessary for the declared purpose of the transaction, potentially violating GDPR's data minimisation principle. The wallet performs an over-asking check by comparing the requested attributes against those declared in the RP's Registration Certificate, and displays a warning to the user if the request exceeds the registered scope. See [Blueprint – Overasking Protection](https://bmi.usercontent.opencode.de/eudi-wallet/eidas-2.0-architekturkonzept/content/ecosystem-concepts/trust/overasking-protection/). --- ## P **Personalausweis** The German national identity card. The chip in the Personalausweis (and the electronic Residence Permit) is the trust anchor for PID issuance in the German EUDI Wallet. The chip data is read via the eID Online-Ausweis function to derive PID attributes. **PID – Person Identification Data** A set of data, issued in accordance with Union or national law, enabling the identity of a natural or legal person to be established. In the German EUDI Wallet, PID is issued by Bundesdruckerei at LoA High, based on the Personalausweis chip data. Available in SD-JWT VC (`vct: urn:eudi:pid:de:1`) and mDoc (`docType: eu.europa.ec.eudi.pid.1`) formats. PID does not include a persistent unique identifier across sessions. Defined in [eIDAS 2.0, Article 3(3)](https://www.europarl.europa.eu/doceo/document/TA-9-2024-0117_EN.html); see also [CIR 2024/2977] and the [German PID Rulebook](https://bmi.usercontent.opencode.de/eudi-wallet/eidas-2.0-architekturkonzept/content/features/PID/german-pid-rulebook/). **PID Provider** A Member State or other legal entity that issues Person Identification Data to users for later use in a EUDI Wallet. In Germany, the designated PID Provider is Bundesdruckerei. Source: [ARF 1.2.0](https://github.com/eu-digital-identity-wallet/eudi-doc-architecture-and-reference-framework). **PKI – Public Key Infrastructure** Systems, software, and protocols used to distribute, manage, and control public keys and certificates, establishing trust within the EUDI Wallet ecosystem. The ecosystem PKI underpins certificate chains for Access Certificates, Registration Certificates, Trusted Lists, and Wallet Instance Attestations. Source: [ARF 1.2.0](https://github.com/eu-digital-identity-wallet/eudi-doc-architecture-and-reference-framework). **PoP – Proof of Possession** Evidence provided by the wallet demonstrating control of specific key material. In the German wallet architecture, the Wallet Instance generates PoP JWTs (`wi_wb_auth_pop`, `wi_wia_pop`) to prove possession of hardware-bound private keys when requesting Wallet Instance Attestations. See [Wallet Architecture Documentation, Section 4.2](https://bmi.usercontent.opencode.de/eudi-wallet/wallet-development-documentation-public/latest/architecture-concept/04-wallet-backend/02-wia-issuance.html). **Pre-Authorized Code Flow** An OID4VCI grant type (`urn:ietf:params:oauth:grant-type:pre-authorized_code`) in which the issuer pre-generates an authorization code and delivers it to the wallet via the Credential Offer, without requiring a separate OAuth authorization step. Used when the issuer has already authenticated the user through an out-of-band process. Source: [OID4VCI](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html). **PubEAA – Public Electronic Attestation of Attributes** An EAA issued by or on behalf of a public sector body (e.g. a government ministry or agency), carrying equivalent legal status to official documents. Subject to specific trust and governance requirements under eIDAS 2.0. Trust model described in [Blueprint – QEAA/PubEAA Trust](https://bmi.usercontent.opencode.de/eudi-wallet/eidas-2.0-architekturkonzept/content/ecosystem-concepts/trust/trust_q_pub_eaa_issuance_presentation/). --- ## Q **QEAA – Qualified Electronic Attestation of Attributes** An EAA issued by a Qualified Trust Service Provider (QTSP), meeting the highest assurance requirements under eIDAS 2.0 Annex V. QEAAs carry enhanced legal standing and are required for certain regulated use cases. Defined in [eIDAS 2.0, Article 3(45)](https://www.europarl.europa.eu/doceo/document/TA-9-2024-0117_EN.html). **QES – Qualified Electronic Signature** An advanced electronic signature created using a Qualified Electronic Signature Creation Device, based on a qualified certificate. Legally equivalent to a handwritten signature across the EU. A planned upcoming feature for the German EUDI Wallet, see [Blueprint – QES (Upcoming Features)](https://bmi.usercontent.opencode.de/eudi-wallet/eidas-2.0-architekturkonzept/content/upcoming-features/qes/). Defined in [eIDAS 2.0](https://www.europarl.europa.eu/doceo/document/TA-9-2024-0117_EN.html). **QTSP – Qualified Trust Service Provider** A Trust Service Provider that has been granted qualified status by a national supervisory body and provides one or more Qualified Trust Services (such as QEAA issuance or QES). Source: [ARF 1.2.0](https://github.com/eu-digital-identity-wallet/eudi-doc-architecture-and-reference-framework). --- ## R **RASP – Runtime Application Self-Protection** A security mechanism that continuously and dynamically monitors a wallet application and its host device at runtime for integrity and authenticity threats, including app hooking, repackaging, debugging, rooting, and emulation. Used in the German wallet's MDVM architecture on both Android and iOS as a platform-independent detection layer. See [Wallet Architecture Documentation, Section 6.1](https://bmi.usercontent.opencode.de/eudi-wallet/wallet-development-documentation-public/latest/architecture-concept/06-mdvm/01-mdvm-concept.html). **Registration Certificate** An X.509 certificate issued to a Relying Party by a national Registrar that declares the RP's intended use, specifying which attributes may be requested, for what stated purpose, and including a human-readable service description. Included in OID4VP requests to allow the wallet to perform over-asking checks and inform the user. Defined under [ETSI TS 119 475](https://www.etsi.org/deliver/etsi_ts/119400_119499/119475/01.02.01_60/ts_119475v010201p.pdf). See also: [Access Certificate](#access-certificate), [Overasking](#overasking). **Registrar** The national entity responsible for registering Relying Parties, validating their identity and intended use declarations, and issuing Access Certificates and Registration Certificates. The Registrar also maintains a public lookup of registered RPs and certificate history. In the German ecosystem, Registrar functions are operated by the Orchestrator. See [Blueprint – Wallet-Relying Party Authentication](https://bmi.usercontent.opencode.de/eudi-wallet/eidas-2.0-architekturkonzept/content/ecosystem-concepts/trust/wallet-relying-party-authentication/). **Relying Party (RP)** A natural or legal person that relies upon an EUDI Wallet or electronic identification means to verify a user's identity or attributes for a service. RPs must register with the Orchestrator, obtain Access and Registration Certificates, and implement an OID4VP-compliant verification flow. Defined in [eIDAS 2.0, Article 3(6)](https://www.europarl.europa.eu/doceo/document/TA-9-2024-0117_EN.html). See also: [RP Onboarding – Developer Guide](https://bmi.usercontent.opencode.de/eudi-wallet/developer-guide/rp/onboarding/rp_highlevel_onboarding/). **Rulebook** The human-readable technical and governance specification for a specific EAA type. Defines the credential schema, attribute semantics, issuance assurance level, acceptable credential formats, trust list governance, and credential lifecycle rules. EAA Providers either adopt an existing Rulebook or author and publish a new one. A published and registered Rulebook is required at production; in sandbox it is not required to begin testing. See [Developer Guide – Rulebooks](https://bmi.usercontent.opencode.de/eudi-wallet/developer-guide/eaa/rulebook/). **RWSCD / RWSCA – Remote Wallet Secure Cryptographic Device / Application** A remote Hardware Security Module (HSM) operated by the Wallet Backend that stores and manages long-term cryptographic keys on behalf of the Wallet Instance. The RWSCA provides the user authentication and key protection assurances required for LoA High, independent of the security of the user's mobile device. See [Wallet Architecture Documentation, Section 5](https://bmi.usercontent.opencode.de/eudi-wallet/wallet-development-documentation-public/latest/architecture-concept/05-remote-wsca/01-remote-wsca.html). --- ## S **Sandbox** The non-production testing environment for the German EUDI Wallet ecosystem. The Sandbox allows developers to integrate and test credential issuance and presentation flows using test credentials, without the full governance requirements that apply at production. Real eID usage is not supported; only test credentials may be used. See [Developer Guide – Sandbox](https://bmi.usercontent.opencode.de/eudi-wallet/developer-guide/sandbox/onboarding/joining/). **Schema Metadata** A machine-consumable entry in the Attestation Catalog linking an EAA type to its Rulebook, JSON schema definition, and Trust List. Schema metadata enables wallets and verifiers to understand and validate a credential type without out-of-band configuration. See [Blueprint – Catalog of Attestations](https://bmi.usercontent.opencode.de/eudi-wallet/eidas-2.0-architekturkonzept/content/features/EAA/catalog-of-attestations/). **SD-JWT – Selective Disclosure JSON Web Token** An IETF specification defining a mechanism for selective disclosure of individual claims within a JWT. An SD-JWT consists of an issuer-signed JWT, one or more Disclosure objects (salted hash-encoded claims), and an optional Key Binding JWT. Holders can reveal only a chosen subset of claims to a verifier. Source: [RFC 9901](https://www.rfc-editor.org/rfc/rfc9901.html). **SD-JWT VC – SD-JWT-based Verifiable Credential** A credential format combining SD-JWT with the Verifiable Credential data model. The primary credential format for the German EUDI Wallet. For the German PID, the `vct` claim is `urn:eudi:pid:de:1`. Source: [draft-ietf-oauth-sd-jwt-vc](https://www.ietf.org/archive/id/draft-ietf-oauth-sd-jwt-vc-15.html). **SE – Secure Element** A tamper-resistant hardware component in a mobile device that securely stores and processes sensitive data and cryptographic keys. Examples include the embedded Secure Enclave (iOS) and StrongBox / hardware-backed KeyStore (Android). The German wallet architecture prioritises the most secure hardware key store available on a device for credential key material. Source: [BSI on Secure Elements](https://www.bsi.bund.de/EN/Themen/Unternehmen-und-Organisationen/Standards-und-Zertifizierung/Secure-Elements/secure-elements_node.html). **Seed Credential** A credential derived from the electronic identity card (Personalausweis chip) that is used to request a fresh PID credential on-demand from the PID Provider. The Seed Credential acts as a renewable bridge between the physical eID and the wallet's PID, enabling PID refresh without requiring the user to re-tap their card. Described in the [Wallet Architecture Documentation](https://bmi.usercontent.opencode.de/eudi-wallet/wallet-development-documentation-public/latest/). **Selective Disclosure** The capability for a credential holder to reveal only a chosen subset of attributes from a credential during a presentation, without exposing the full credential. Implemented in SD-JWT VC via the SD-JWT Disclosure mechanism and in mDoc via the ISO 18013-5 selective disclosure mechanism. A core privacy feature of the German EUDI Wallet. **Status List** A compact, bit-array-based mechanism for publishing the revocation or suspension status of credentials or Wallet Instance Attestations, allowing verifiers to check status without querying a central server per credential. Used by the Wallet Backend to express the security status of Wallet Instances in the WIA. Source: [IETF draft-ietf-oauth-status-list](https://datatracker.ietf.org/doc/draft-ietf-oauth-status-list/). --- ## T **TEE – Trusted Execution Environment** A secure area of the main processor in a mobile device that runs code and processes data in isolation from the standard operating system. Used as a hardware-backed key store (HKS) in the German wallet's mobile device security architecture when a dedicated Secure Element is not available. **Trust List (Trusted List)** A machine-readable, cryptographically signed list of authorised issuers (and their certificates) for a given credential type, or a list of authorised Trust Service Providers. Relying Parties use Trust Lists to verify that a received credential was issued by a legitimate, registered entity. Required at production; optional in sandbox. Format specified in [ETSI TS 119 612](https://www.etsi.org/deliver/etsi_ts/119600_119699/119612/02.04.01_60/ts_119612v020401p.pdf). Source definition: [ARF 1.2.0](https://github.com/eu-digital-identity-wallet/eudi-doc-architecture-and-reference-framework). **TSP – Trust Service Provider** A natural or legal person providing one or more Trust Services, either as a qualified (QTSP) or non-qualified Trust Service Provider. Source: [ARF 1.2.0](https://github.com/eu-digital-identity-wallet/eudi-doc-architecture-and-reference-framework). **2FA – Two-Factor Authentication** A security system requiring two distinct forms of identification before granting access. In the German EUDI Wallet, users authenticate to the RWSCA using two factors: a possession factor (hardware-bound key on the mobile device) and a knowledge factor (wallet PIN). Both factors are required for credential presentations at LoA High. --- ## V **VC – Verifiable Credential** A credential issued by an Issuer in a way that the integrity and authenticity of the credential, including the identity of the issuer and the integrity of the claims, can be cryptographically verified by any party. Source: [OID4VCI](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html). **VLOP – Very Large Online Platform** Online platforms designated under the EU Digital Services Act (DSA) with more than 45 million monthly active users in the EU (e.g. Meta, Amazon, Booking.com). VLOPs are required under eIDAS 2.0 to accept EUDI Wallets for user authentication upon request by end of 2027. --- ## W **Wallet Backend (WB)** The server-side infrastructure operated by the Wallet Provider that manages wallet account registration, issues Wallet Instance Attestations (WIA), and interfaces with the Remote WSCD (HSM). The Wallet Backend validates device security posture via the MDVM before issuing WIAs. See [Wallet Architecture Documentation, Section 4](https://bmi.usercontent.opencode.de/eudi-wallet/wallet-development-documentation-public/latest/architecture-concept/04-wallet-backend/01-wallet-registration.html). **Wallet Instance (WI)** The installed instance of the EUDI Wallet application on a specific user device. Each Wallet Instance registers with the Wallet Backend, obtains a Wallet Instance Attestation (WIA) per issuer it interacts with, and manages its own hardware-bound key pairs. See [Wallet Architecture Documentation](https://bmi.usercontent.opencode.de/eudi-wallet/wallet-development-documentation-public/latest/). **Wallet Provider** An entity that develops and distributes a EUDI-compliant wallet application to end users. Germany operates a national reference wallet and supports additional certified third-party wallet providers. Defined in [eIDAS 2.0](https://www.europarl.europa.eu/doceo/document/TA-9-2024-0117_EN.html). **WIA – Wallet Instance Attestation** A signed JWT issued by the Wallet Backend that attests to the validity, security posture, and integrity of a specific Wallet Instance toward Issuers and Relying Parties. The WIA is bound to the Wallet Instance's hardware-backed public key and includes a Status List entry for revocation. Format profiled under [ETSI TS 119 472-3](https://www.etsi.org/deliver/etsi_ts/119400_119499/11947203/01.01.01_60/ts_11947203v010101p.pdf) and [OAuth Attestation-Based Client Authentication](https://www.ietf.org/archive/id/draft-ietf-oauth-attestation-based-client-auth-08.html). See [Wallet Architecture Documentation, Section 4](https://bmi.usercontent.opencode.de/eudi-wallet/wallet-development-documentation-public/latest/architecture-concept/04-wallet-backend/01-wallet-backend.html#4-wallet-backend). **WRP – Wallet-Relying Party** A Relying Party that intends to rely upon EUDI Wallet Units for the provision of public or private services through digital interaction. The term emphasises the wallet-specific context of the relying party relationship, as distinct from general eIDAS relying parties. Defined in [eIDAS 2.0, Article 5b](https://www.europarl.europa.eu/doceo/document/TA-9-2024-0117_EN.html). **WSCD – Wallet Secure Cryptographic Device** A hardware-backed secure environment for creating, storing, and managing cryptographic keys and data within the wallet. Examples include Secure Elements (SE), Trusted Execution Environments (TEE), and local or remote Hardware Security Modules (HSM). The WSCD is the hardware foundation that enables the LoA High security properties of the German EUDI Wallet. Source: [ARF 1.2.0](https://github.com/eu-digital-identity-wallet/eudi-doc-architecture-and-reference-framework). --- --- ## Open Source OpenID4VP Verifier Libraries Overview This document provides a non-exhaustive list of open-source libraries that support OpenID4VP (OpenID for Verifiable Presentations). The list is intended as a starting point for Relying Parties exploring OpenID4VP implementations. --- ## Important Notes for Developers This list is provided for informational purposes only. We have not validated these libraries and do not guarantee their correctness, completeness, or suitability for any specific use case. Developers are responsible for evaluating whether a library meets their requirements and for ensuring its proper and secure use. We do not endorse or warrant the functionality, security, or compliance of any library listed here. If you encounter issues with a library, please contact the library owner or maintainer directly. Do not reach out to us for support or bug fixes. --- ## Add Your Library If you are a provider of an open-source library supporting OpenID4VP and would like to be added to this list, [please reach out](mailto:partner@eudi.sprind.org). --- ## Open Source Libraries | Name | Language | Verifier Capabilities | Format Support | Repo / Docs | License | Maturity | Tags | |--------------------|-------------|------------------------------------------------------------|-------------------------|-------------------------------------------------------------------------------------------------|------------|---------------------|--------------------------| | OWF Credo | TypeScript | Comprehensive OpenID4VC/OpenID4VP support | SD-JWT, W3C VC | [Link](https://github.com/openwallet-foundation/credo-ts/tree/main/packages/openid4vc/src/openid4vc-verifier) | Apache-2.0 | Active development | OID4VP, HAIP, PID | | waltID | Kotlin | Implements OID4VP protocol | SD-JWT, mDoc | [Link](https://github.com/walt-id/waltid-identity/tree/main/waltid-services/waltid-verifier-api2) | Apache-2.0 | Active development | OID4VP, ExtensibleToPID | | Impierce | Rust | Implements OpenID4VP protocol | JWT-based credentials, SD-JWT (partial) | [Link](https://github.com/impierce/openid4vc/tree/dev) | Apache-2.0 | Early-stage open source | OID4VP, OID4VCI, ExtensibleToPID | | SpruceID | Rust | Implements OID4VP verifier with headless wallet for testing | SD-JWT, JWT-based VC, mDoc | [Link](https://github.com/spruceid/openid4vp) | MIT | Active development | OID4VP, ExtensibleToPID | | ThunderID | Javascript, Kotlin, Swift | Implements OID4VC & OID4VP | SD-JWT, JWT-based VC, mDoc | [Link](https://github.com/thunder-id) | MIT | Active development | OID4VP, OID4VCI, ExtensibleToPID | | Sphereon | TypeScript | Implements SIOP v2 and OID4VP verifier logic | SD-JWT, JWT-based VC | [Link](https://github.com/Sphereon-Opensource/OID4VC/) | Apache-2.0 | Active development | OID4VP, OID4VCI, Extensible --- ## End-to-End Example: Berlin Digital Bank ## Organization Overview | Field | Description | |-------|--------------| | **Name** | Berlin Digital Bank (BDB) | | **Sector** | Financial Services | | **Use Case** | Digital account opening (Level of Assurance: High) | | **Goal** | Verify the identity of new customers using the EUDI Wallet | --- ## Plan **Use Case Definition** Berlin Digital Bank (BDB) enables digital account opening for new customers with a **high level of assurance (LoA High)**. During onboarding, BDB must verify a customer’s **name**, **date of birth**, and **residence** using their **Personal Identification Data (PID)** credential from an EUDI Wallet. **Protocol and Profile Selection** Since the use case requires a high LoA, BDB’s presentation request must conform to the [High Assurance Interoperability Profile (HAIP)](https://openid.net/specs/openid4vc-high-assurance-interoperability-profile-1_0-03.html#name-openid-for-verifiable-prese). For the EUDI Wallet Ecosystem, the full PID presentation flow is described in the [EUDI Architecture & Concept documentation](https://bmi.usercontent.opencode.de/eudi-wallet/wallet-development-documentation-public/latest/architecture-concept/flows/22-pid-presentation/#data-flow). Since customers may hold their PID in either **SD-JWT VC** or **mDoc** format, the verifier must accept both formats. **Attribute Selection** - SD-JWT format: `given_name`, `family_name`, `date_of_birth`, `address` - mDoc format: `given_name`, `family_name`, `date_of_birth`, `resident_address` BDB has defined its use case, identified required attributes, and determined the applicable interoperability and assurance requirements. With a clearly defined use case, BDB fills in the [intent form](https://2eut7s.share-eu1.hsforms.com/2QlEzCm_NR6SbkHuq9vbFWw). BDB is invited to a Kick-Off call, after which they obtain access to the National EUDI Wallet Closed Beta by email invitation, and the [German EUDI Ecosystem Sandbox Registrar](https://sandbox.eudi-wallet.org/). Logging into the Sandbox registrar, BDB configures and issues an access certificate and a registration certificate. Now that BDB has issued their access certificate and registration certificate, they can include them in their Presentation Request. As soon as they do, they will be recognisable to the EUDI Wallet Closed Beta App as a Relying Party in the EUDI Ecosystem Sandbox. See [Certificate Usage in Presentation Requests](./registrar_certificate_usage.md) for the expected request fields and encoding steps. --- ## Integrate ### Wallet Invocation Setup BDB has designed an account-registration flow that will lead a new customer into a PID presentation. To make the presentation request available to customers, an **OpenID4VP Authorization Request** is signed and hosted by BDB at its `request_uri`, following the [EUDI Wallet Ecosystem flow rules](https://bmi.usercontent.opencode.de/eudi-wallet/wallet-development-documentation-public/latest/architecture-concept/flows/22-pid-presentation/#data-flow). The presentation request is hosted as a [Pushed Authorization Request](https://datatracker.ietf.org/doc/html/rfc9126). BDB uses a **deeplink** to invoke the EUDI Wallet from its onboarding webpage. The deeplink uses a custom URI scheme defined by ETSI: ``` openid4vp://https://berlin.digitalbank.example/presentation_request ``` > **Note:** In the HAIP, the custom uri scheme haip:// is required. This is not a requirement for the sandbox environment today. ### Integrate OpenID4VP Verifier Component; SD-JWT and mDoc parsing enabled There are open source OpenID4VP libraries available that support usually different profiles. BDB researches which open source libraries could support their use case. Berlin Digital Bank finds an open source library that supports PID presentation conforming to HAIP. The library helps BDB to perform the steps as described in the [EUDI Wallet Ecosystem PID Presentation](https://bmi.usercontent.opencode.de/eudi-wallet/wallet-development-documentation-public/latest/architecture-concept/flows/22-pid-presentation/#data-flow). This includes: - Generating keys - Generating an Authorization Request - Signing an Authorization Request - Decrypting an Authorization Response - Parsing and verifying an Authorization Response ### Configure credential requests for required attributes only Subsequently, BDB designs an example of what their authorization request could look like: ``` { "iss": "https://berlin.digitalbank.example", "aud": "https://wallet.example.org", "iat": 1698163200, "exp": 1698166800, "client_id": "x509_hash:007ac9762a0e74d0a1a3cfd37e3cc3f2bd97055fcad79ef6355d7a4f0aaeb8da", "response_type": "vp_token", "response_mode": "direct_post.jwt", "scope": "openid", "nonce": "n-abcdef1234567890", "state": "s-xyz987654321", "client_metadata": { "jwks": { "keys": [ { "kty": "EC", "kid": "ec-key-1", "use": "enc", "alg": "ES256", "crv": "P-256", "x": "f83OJ3D2xF4yG9mXQnKkT7H5Y3WZxR5m7yZy2P1XQ0", "y": "x_FEzRu9zM6T5nHcJZJx4Z3VtP4yKQz5xA6cD7E8F9" } ] } }, "verifier_info":{ "data": "eY43rfc...", "format": "registration_cert" }, "response_uri": "https://response.example", "dcql_query": { "credentials": [ { "id": "pid", "format": "dc+sd-jwt", "meta": { "vct_values": ["https://demo.pid-provider.bundesdruckerei.de/credentials/pid/1.0"] }, "claims": [ {"path": ["given_name"]}, {"path": ["family_name"]}, {"path": ["address", "street_address"]}, {"path": ["birthdate"]} ] }, { "id": "pid-mdoc", "format": "mso_mdoc", "meta": { "doctype_value": "eu.europa.ec.eudi.pid.1" }, "claims": [ { "id": "first_name", "path": ["eu.europa.ec.eudi.pid.1", "given_name"] }, { "id": "last_name", "path": ["eu.europa.ec.eudi.pid.1", "family_name"] }, { "id": "resident_address", "path": ["eu.europa.ec.eudi.pid.1", "resident_address"] }, { "id": "date_of_birth", "path": ["eu.europa.ec.eudi.pid.1", "birth_date"] } ] } ], "credential_sets": [ { "purpose": "Identification", "options": [ [ "pid" ], [ "pid-mdoc" ] ] } ] } } ``` **Pay extra attention to:** `client_id`: The prefix for the client_id used in this example is `x509_hash`, which describes the client_id_scheme used in the presentation. `client_metadata`: Includes the JWKS for encryption; other metadata is provided in the Registration Certificate. `credential_sets`: Allows acceptance of equivalent credentials in SD-JWT or mDoc format. `verifier_info`: Contains the Registration Certificate, establishing presentation purpose. `vct_values`: Please note that the current vct_values for an sd-jwt request is subject to change. In the future, we expect to use the value `urn:eudi:pid:de:1` for the German PID Provider. In the long run, relying parties across Europe can query for a PID from any member state using the vct_value `urn:eudi:pid:1`. At this point, BDB has integrated an OpenID4VP component that can take care of all the key actions taken during a PID presentation, and knows how to generate properly configured Authorization Requests. The OpenID4VP library helps to decrypt and parse the content, now BDB needs to make sure that they can handle the data relayed in the Presentation Response. As such, BDB once again relies on the PID Rulebook to understand German EUDI Wallet specific handling of special characters. Finally, BDB implements validator code that checks whether the response follows the PID schema and all required attributes have been relayed correctly. ## Operate - Test in Sandbox with the National EUDI Wallet - Explore & expand future services (e.g., mortgage applications) - Maintain service, manage logs and certificate lifecycle - Prepare for production with logging and monitoring --- ## Logging and Monitoring Effective logging and monitoring are critical for transparency, security, and compliance within the EUDI Wallet Ecosystem. The following guidance distinguishes between sandbox and production environments. --- ## Sandbox Logging **Purpose:** To support troubleshooting, integration testing, and conformance validation without handling live personal data. **Best Practices:** - Log only pseudonymized or synthetic data. - Record all OpenID4VP and OpenID4VCI message exchanges for test sessions. - Include timestamps, request IDs, and status codes, trace ID, for each transaction. - Capture cryptographic validation results (e.g., signature verification success/failure). - Avoid storing wallet identifiers or end-user personal data. - Rotate logs frequently (e.g., daily) and purge automatically after 14–30 days. - Provide structured log export (JSON). --- ## Production Logging **Purpose:** To ensure service reliability, detect anomalies, and support audits — while maintaining GDPR and ARF compliance. **Key Principles:** - Data Minimization: Log only what is strictly necessary for operational or security purposes. - Pseudonymization: Replace or hash any user identifiers before storage. - Purpose Limitation: Logs may only be used for security, audit, or compliance purposes — not profiling or analytics. - Storage Limitation: Retain logs for the minimum period required (e.g., 90 days operational, 12 months audit). - Access Control: Restrict access to authorized DevOps and compliance personnel only. - Integrity & Confidentiality: Protect logs using encryption in transit (TLS 1.3) and at rest (AES-256). - Traceability: Maintain a verifiable chain of custody for all log archives. - Incident Reporting: Log security-related events in real-time to the Orchestrator’s incident interface. **Recommended Data Points:** - Timestamp and unique transaction/session ID - Request type (presentation/issuance) - Wallet invocation method (deeplink, QR, API) - Cryptographic validation outcome (success/failure, certificate used) - Error or exception messages (without sensitive payloads) - System performance metrics (latency, uptime, throughput) --- ## Sandbox Monitoring **Objective:** Monitor integration health and readiness for compliance certification. **Recommended Metrics:** - API responsiveness and uptime - Request/response validation rate - Credential format interoperability (SD-JWT vs mDoc) - Wallet invocation success ratio - Transaction pass/fail rates --- ## Production Monitoring **Objective:** Ensure ongoing trustworthiness, performance, and security of the RP’s integration in compliance with GDPR and ARF operational guidelines. **Recommended Metrics:** - Service uptime (target ≥ 99.9%) - Average response time and load latency - Presentation success rate per credential format - Error rates by category (validation, cryptographic, transport) - Certificate validity and expiration tracking - Real-time security event alerts (e.g., replay attempts, unauthorized access) **Monitoring Practices:** - Deploy a secure, privacy-preserving monitoring solution with centralized observability (e.g., Prometheus + Grafana). - Anonymize event identifiers before transmission to third-party monitoring services. - Configure automated alerts for expiring Access/Registration Certificates. - Support incident notification and trust status reporting to the Orchestrator. - Document monitoring responsibilities in the RP’s Data Protection Impact Assessment (DPIA). --- ## Using Registrar Certificates in Presentation Requests > Audience: Relying Parties building OpenID4VP / HAIP presentation requests for the German EUDI Wallet Ecosystem. This page explains how to use the two registrar certificates in your presentation request: the Access Certificate in the request header and the Registration Certificate in `verifier_info`. ## 1. Which certificate belongs in which part of the request? The Sandbox Registrar provides two distinct certificates: - **Access Certificate**: identifies the RP or EAA Provider to the wallet and establishes verifier trust in the presentation request. This certificate is delivered in the `x5c` header of the presentation request. - **Registration Certificate**: contains verifier metadata used by the wallet to determine additional information such as the requested presentation purpose and user-facing context. This certificate is carried in `verifier_info`. ## 2. What the registrar actually provides - The **Access Certificate** is provided as a `.crt` file. - The **Registration Certificate** is provided as a `registration-certificate.json` file containing a JWT. Because the naming is confusing, remember: - `.crt` → Access Certificate → `x5c` - `registration-certificate.json` → Registration Certificate (JWT) → `verifier_info` ## 3. Include the Access Certificate in `x5c` Your signed request object should include the Access Certificate in its JOSE header, using the `x5c` header array. ### Preparing the Access Certificate The Access Certificate is downloaded as a `.crt` file. Before including it in the `x5c` header, you must: 1. Convert the `.crt` file to DER format: ```bash openssl x509 -in access-certificate.crt -outform der -out access-certificate.der ``` 2. Base64 encode the DER file: ```bash base64 access-certificate.der > access-certificate.b64 ``` 3. Include the Base64 encoded certificate in the `x5c` header array. Example header fragment: ```json { "alg": "ES256", "typ": "JWT", "x5c": [ "MIID..." ] } ``` > Only include the access certificate and not any other additional certificate in the header! The wallet uses this certificate to identify your verifier and validate the request signature or certificate binding. ## 4. Include the Registration Certificate in `verifier_info` The Registration Certificate is conveyed in the request body using `verifier_info`. The `registration-certificate.json` file contains a JWT that can be used directly. Example: ```json "verifier_info": { "format": "registration_cert", "data": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." } ``` Simply extract the JWT string from the `registration-certificate.json` file and place it in the `verifier_info.data` field. The wallet reads this JWT to obtain metadata such as the purpose of the presentation and other verifier-specific context. ## 5. How these pieces work together - The Access Certificate in `x5c` proves who is making the request. - The Registration Certificate in `verifier_info` tells the wallet why the request is being made and how it should be presented to the user. ## 6. Validation guidance - Confirm the `.crt` file you received is the Access Certificate and that it has been converted to DER and Base64URL encoded before appearing in the request header `x5c`. - Confirm the `registration-certificate.json` file contains the Registration Certificate JWT and that the JWT appears in `verifier_info.data`. - Do not swap these two certificates. ## 7. Common issues - **Wallet rejects the request**: make sure the Access Certificate is present in `x5c` and the signed request object uses it consistently. - If the Access Certificate is missing, the wallet often reports: `Validation Error: Could not trust certificate chain`. - **Wallet does not show the presentation purpose**: ensure the Registration Certificate JWT is present in `verifier_info` and contains the required metadata. --- ## Relying Party Technical Integration This guide provides Relying Party-specific requirements and technical integration steps for organizations that want to verify credentials in the German EUDI Wallet Ecosystem. :::info[Start Here] This guide assumes you have already reviewed the [general onboarding process](../../sandbox/onboarding/joining.md). If you haven't completed the Plan stage and the Kick-off Call, please start there first. ::: --- ## Relying Party-Specific Requirements ### Organizational Requirements | Requirement | Reason | |-------|------| | Documented service use case for credential verification | The use case should be achievable within the bounds of the sandbox.| | Legal entity registered in Germany | According to eIDAS, organizations must register in the country where they are established (Art. 5b(1)). This is not a requirement for joining the sandbox, but it is for entering production.| | Designated operational contact | The Orchestrator requires a single point of contact with respect to RP operations.| | Designated technical contact | The Orchestrator requires a single point of contact for technical communication | ### Technical Requirements | Requirement | Reason | |-------|------| | Integrate a Verifier Component using **OpenID4VP** | You are expected to bring your own software solution to the Sandbox. You can either develop your own solution, integrate an open source component, or employ a third party service provider.| | Run secure HTTPS network services | All Presentation Requests need to be secured using HTTPS.| ### Security & Compliance Requirements | Requirement | Reason | |-------|------| | Implement data minimization and purpose binding | Relying Parties must not request data attributes that are not needed for the completion of the business flow. Presentation Requests should be configured to clearly present the purpose of the data request to the Wallet User.| | Follow the ecosystem policies | Aside from the technical requirements, Sandbox participation involves behavioral rules that support an open and collaborative ecosystem.| --- ## Relying Party Integration Steps These steps are the Relying Party-specific part of the **Integrate** stage of the general onboarding process: ### Step 1: Define Your Verification Use Case **Identify Integration Point** Specify when verification occurs in the service flow (login, registration, checkout, access request). Identify the type(s) of interactions you need to support. For example, same device and/or cross-device flows, or in-person/remote interactions. **Select Credential Types** Request only necessary attributes (e.g., identity data, age confirmation, or proof of address). Choose a supported credential format (SD-JWT VC or mDoc). --- ### Step 2: Integrate Verifier Component Deploy or integrate a Verifier Component supporting OpenID4VP and SD-JWT and mDoc credential formats. This verifier component can be built from scratch, leverage open-source software, or be provided by a commercial service provider. When consuming a PID presentation, your implementation must comply with the German PID Rulebook. **Integration Options:** - Build from scratch using OpenID4VP specifications - Use open-source verifier libraries (see [Open Source Libraries](../../resources/os_libraries.md)) - Employ a commercial service provider ### Step 3: Test and Validate **Test in Sandbox** Validate request and response flows using the provided Wallet and the credentials issued to it. **Key Testing Areas:** - Credential request flows (same-device and cross-device) - Attribute validation and processing - Error handling and edge cases - User experience and presentation clarity - Selective disclosure functionality - Certificate and trust chain validation --- ### Step 4: Operate and Maintain **Maintain Service** Manage certificate lifecycle and operational logs. See [Logging and Monitoring](./logging_and_monitoring.md) for approaches in the sandbox and for operations. **Operational Responsibilities:** | Requirement | Responsibility | |-------------|----------------| | Secure Operation | HTTPS endpoints, secure key and credential management | | Purpose Limitation | Request only attributes necessary for the service | | Logging & Auditing | Maintain minimal logs, pseudonymize identifiers, do not store full credential data | | RP Credential Management | Renew and revoke Access Certificates as required | | Trust Registry Updates | Maintain status in the RP Registry | | Policy Adherence | Agree to terms and conditions | **Prepare for Production** Fine-tune logging, preparation for scaling, and compliance processes. --- ## Terms & Definitions - **Relying Party (RP):** Organization verifying credentials - **Issuer:** Entity issuing verifiable credentials - **Access Certificate:** Digital credential authorizing RP participation - **Registration Certificate:** Certificate confirming RP registration in Trust Registry - **OpenID4VP:** OpenID for Verifiable Presentations protocol - **SD-JWT:** Selective Disclosure JSON Web Token format - **mDoc:** Mobile Document format used in wallets --- ## Additional Resources - **Readiness Check:** Review the [Readiness Checklist](../../sandbox/onboarding/sandbox_readiness_checklist.md) to ensure you're prepared - **Example Implementation:** Explore the [Example Use Case](./example_bdb.md) for a concrete implementation scenario - **Technical Details:** See the [PID Presentation guide](../pid_presentation.md) for detailed technical implementation - **Certificate Usage:** Learn how to include registrar certificates in your presentation request at [Certificate Usage in Presentation Requests](./registrar_certificate_usage.md) - **Tools & Libraries:** Check [Open Source Libraries](../../resources/os_libraries.md) for available components - **Logging:** Review [Logging and Monitoring](./logging_and_monitoring.md) for operational guidance - **Operating in the Sandbox:** Once integration is complete, return to the [general onboarding guide](../../sandbox/onboarding/joining.md) to learn about the Operate stage :::tip[Expanding Your Role] Many Relying Parties joining the sandbox later become EAA Providers themselves, offering varied opportunities to consume new types of credentials to enable new business flows. If you're interested in issuing credentials in addition to verifying them, see the [EAA Provider guide](../../eaa/onboarding/overview.md). ::: --- ## Implementer guide — implementing PID presentations with OpenID4VP > Audience: Relying Parties implementing an OpenID4VP flow to enable the consumption of a PID credential in the German EUDI Wallet Ecosystem. --- ## Contents 1. Introduction 2. Concepts and Validation Layers 3. Building a PID Presentation Request 4. Interpreting the PID Presentation Response --- ## 1. Introduction This guide targets implementers of OpenID4VP to support PID presentations in the German EUDI Ecosystem. Following this short introduction, Chapter 2 explains the security concepts and validation layers that make PID presentations trustworthy. Chapter 3 shows the structure of a PID Presentation Request and how to construct one correctly. Chapter 4 explains the response structure and provides a complete validation checklist. The specification that forms the basis for this documentation can be found in the [Blueprint for the German EUDI Wallet Ecosystem](https://bmi.usercontent.opencode.de/eudi-wallet/wallet-development-documentation-public/latest/architecture-concept/03-data-flows/22-pid-presentation.html). Throughout this guide, rules or optionality specific to Germany will be called out. Notes have been added with information specific to Sandbox participation. ## 2. Concepts and Validation Layers This chapter explains the foundations of PID (Person Identification Data) presentation as defined by the ARF PID Rulebook and realized through the [OpenID4VP High Assurance Interoperability Profile](https://openid.net/specs/openid4vc-high-assurance-interoperability-profile-1_0.html). It covers the security concepts that make PID presentations trustworthy, then explains the validation layers that implement these concepts in practice. ### 2.1 Trust Anchors and Governance Everything in PID verification begins with the trust framework. Before any cryptographic validation is done, the verifier must know which issuers it is allowed to trust and which certificate hierarchies govern PID issuance. This prevents the acceptance of PID data from unrecognized or malicious sources. In the eIDAS context, this usually means the use of trust lists. :::info[German PID Provider] In Germany, there is only one PID Provider (Bundesdruckerei). For the purpose of Sandbox testing, we provide [mock trust lists](https://bmi.usercontent.opencode.de/eudi-wallet/test-trust-lists/) ::: ### 2.2 Authenticity of PID Credentials Once trust anchors are known, the verifier evaluates whether the PID itself was authentically issued. PIDs include signatures created by the issuing authority. Validating these signatures against the keys from the trust framework establishes that the data presented by the wallet corresponds to a genuine PID record and has not been modified since issuance. This ensures that the identifier information originates from the correct governmental entity and is in an unaltered state. ### 2.3 Holder Binding Even if the PID was authentically issued, the verifier must ensure that the wallet instance presenting it is the same wallet that the PID was issued into. This is called Holder Binding or Key Binding. A detailed discussion can be found in the [SD-JWT specification](https://www.rfc-editor.org/rfc/rfc9901.html#name-key-binding-2); it is applicable in general to all credential formats. During the presentation, the wallet signs the presented credential along with information bound to the particular presentation (see below) in the VP Token. The signature is performed with a private key that is tied to the secure wallet instance and for which the corresponding public key is contained in the Issuer-signed credential. This ensures the presentation comes from a valid wallet on a valid device, not from an exported credential or a cloned environment. Without holder binding, an intercepted presentation, a malicious verifier, a cloned backup, or a compromised device could be used to impersonate someone. The holder-binding bridges the gap between "this is a valid PID" and "this PID is being presented by the right person". The signature produced by the wallet cannot be replayed or forged without access to the protected key material. ### 2.4 Session and Transaction Binding A high-assurance PID presentation must be tied explicitly to the verifier’s session. The verifier generates a cryptographically strong nonce and embeds it in the presentation request. The wallet then includes this nonce in the proof-of-possession signature along with information about the verifier's identity and a timestamp. When the verifier later validates the response, it confirms that the presentation is fresh and could only have been created in response to its own request, mitigating phishing and replay attacks. ### 2.5 Selective Disclosure and Data Minimization The ARF mandates that verifiers request only the attributes they genuinely need. HAIP and the underlying credential formats (SD-JWT or mDoc) allow the wallet to disclose only these attributes selectively. The verifier cannot collect anything outside its stated purpose, and over disclosed elements should be ignored. #### Validation Philosophy Validation should follow a fail-fast, layered approach: - Foundational checks first (trust, transport, session binding) - Cryptographic assurance next (credential validity, holder binding) - Privacy and policy enforcement next (selective disclosure) - Business logic last If any layer fails, validation should stop immediately. Continuing after a critical failure increases attack surface and risks incorrect authorization decisions. --- #### Layer 1: Trust and Transport Before interpreting any protocol data, the verifier must establish a trusted baseline. At this layer, the verifier conceptually validates that: - Trust anchors for PID issuers and wallet providers are loaded from an official, trusted registry - Trust lists are authentic, fresh, and correctly parsed - Communication occurs over secure transport (HTTPS with valid TLS) - The received payload is of the expected type and within reasonable size limits --- #### Layer 2: Session Binding The verifier must ensure that the response is cryptographically bound to its own request. At this layer, the verifier validates that: - A nonce is present and exactly matches the verifier-issued nonce - The audience (`aud`) identifies the verifier and matches the request - Timestamps (`iat`, `exp`) are present, valid, and within an acceptable freshness window - The wallet follows the verifier's redirect_uri after posting a response This layer prevents replay attacks, phishing, and cross-session injection. --- #### Layer 3: Holder Binding Even a valid PID credential is insufficient unless it is presented by its rightful holder. At this layer, the verifier conceptually validates that: - A proof-of-possession mechanism is present - The proof is cryptographically valid - The proof binds the presentation to the verifier’s nonce, the verifier’s identifier (audience), and the presented credential. - The signing key is correctly associated with the PID’s holder binding mechanism --- #### Layer 4: Credential Assurance The verifier must confirm that the presented PID credential itself is genuine and valid. At this layer, the verifier validates that: - A PID credential is present and extractable from the presentation - The credential format is supported (SD-JWT or mDoc) - Issuer signatures are valid - The issuer is trusted according to the loaded trust anchors - The credential is within its validity period and not revoked - The credential type matches what was requested #### Layer 5: Selective Disclosure and Data Minimization PID verification is subject to strict data minimization requirements. At this layer, the verifier validates that: - Only attributes explicitly requested are disclosed - All mandatory attributes for the transaction are present - No additional credentials are included - The disclosed credential combination matches one of the allowed credential sets --- #### Layer 6: Business Rules and Policy Enforcement Only after all assurance layers succeed should the verifier apply business logic. At this layer, the verifier validates that: - Required attributes are present and usable - Attribute values satisfy business requirements - Data quality and completeness meet application expectations - A clear authorization- and or business decision can be derived and audited :::info[Wallet Integrity] ::: Wallet integrity is established during the issuance process through a Wallet Unit Attestation. The issuer ensures that the PID is only issued into valid wallets. During presentation, the verifier relies on that established trust implicitly. --- ## 3. Building a PID Presentation Request This chapter explains the structure of a PID Presentation Request and how to construct one correctly. It covers what a wallet receives when it calls a `request_uri` and how to understand a HAIP-aligned PID Presentation Request. The examples below are illustrative but structurally realistic for the German PID profile. --- ### 3.1 HTTPS Response When Dereferencing `request_uri` When the wallet fetches the Presentation Request referenced by the `request_uri`, the verifier responds with a JSON object containing the actual Presentation Request. In HAIP-aligned deployments, this request is a signed Pushed Authorization Request (PAR). ```http HTTP/1.1 200 OK Content-Type: application/json Cache-Control: no-store Pragma: no-cache { eyJ0eXAiOiJvYXV0aC1hdXRoei1yZXErand0IiwiYWxnIjoiRVMyNTYiLCJ4NWMiOlsiTUlJQ2N6Q0NBaGlnQXdJQkFnSVVPeEQ3SkZrS1lnRlBrOEozWm1Tc0VDMkJIazR3Q2dZSUtvWkl6ajBFQXdJd0tERUxNQWtHQTFVRUJoTUNSRVV4R1RBWEJnTlZCQU1NRUVkbGNtMWhiaUJTWldkcGMzUnlZWEl3SGhjTk1qWXdNVEkzTURreU9ETTFXaGNOTWpjd01USTNNRGt5T0RNMVdqQmZNUXN3Q1FZRFZRUUdFd0pFUlRFWU1CWUdBMVVFQ2d3UFJWVkVTU0JRYkdGNVozSnZkVzVrTVJ3d0dnWURWUVJoREJORVJTNDBPRGN6TlRKRE1FWTJNVGN4UTBRek1SZ3dGZ1lEVlFRRERBOUZWVVJKSUZCc1lYbG5jbTkxYm1Rd1dUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFSdEFiL3d2MXYyTXJHKzNDSE1sQmFnQXgwQ1NXL0IzNUZWQThkTC9DdjRYWG81ZU9xeHl0c1dlQThmbHFLamRhNjBnTTdnWVF4MUpWMEJ3akRQbVIwK280SG9NSUhsTUF3R0ExVWRFd0VCL3dRQ01BQXdIUVlEVlIwT0JCWUVGRXlpMG1VSFRnVmFCTDRHRS9ZeTlRQ1EvMXdHTUI4R0ExVWRJd1FZTUJhQUZLbkNvOW92YmF4VTdzNjVUdWdzeVN3QWc0QXpNQTRHQTFVZER3RUIvd1FFQXdJSGdEQVNCZ05WSFNVRUN6QUpCZ2NvZ1l4ZEJRRUdNQ1VHQTFVZEVRUWVNQnlDR25Cc1lYbG5jbTkxYm1RdVpYVmthUzEzWVd4c1pYUXViM0puTUVvR0ExVWRId1JETUVFd1A2QTlvRHVHT1doMGRIQnpPaTh2YzJGdVpHSnZlQzVsZFdScExYZGhiR3hsZEM1dmNtY3ZZWEJwTDNOMFlYUjFjeTF0WVc1aFoyVnRaVzUwTDJOeWJEQUtCZ2dxaGtqT1BRUURBZ05KQURCR0FpRUFrbmsydkd0MUw4cGU2RXR0elhKMlIxdHVlRm5kQ1RVNDJFYUlSbGo5MlRvQ0lRRGova0ZmcldpN2p5UXRLbVNLb3JIY0JsWGluV09uRGtXLzZwQU5tZ1hHZGc9PSJdfQ.eyJyZXNwb25zZV90eXBlIjoidnBfdG9rZW4iLCJjbGllbnRfaWQiOiJ4NTA5X2hhc2g6ZlF1b2JWd0p2MDAwdkRXY010cmlYUHpvMnNQVG01X01wMTBPODdsQ3FjRSIsInJlc3BvbnNlX3VyaSI6Imh0dHBzOi8vcGxheWdyb3VuZC5ldWRpLXdhbGxldC5vcmcvZXVkaXBsby8zYWE0NzA2Yy02ZjM1LTQ3YjgtOGEwNS02YTBhMzM0YzMwMWQvb2lkNHZwIiwicmVzcG9uc2VfbW9kZSI6ImRpcmVjdF9wb3N0Lmp3dCIsIm5vbmNlIjoiODVkNWUyNGEtNzE2My00ZTQ0LWEwZWQtOTRiZTk4Y2FiNjY2IiwiZGNxbF9xdWVyeSI6eyJjcmVkZW50aWFscyI6W3siaWQiOiJwaWQtc2Qtand0IiwiZm9ybWF0IjoiZGMrc2Qtand0IiwiY2xhaW1zIjpbeyJwYXRoIjpbImdpdmVuX25hbWUiXX0seyJwYXRoIjpbImZhbWlseV9uYW1lIl19LHsicGF0aCI6WyJiaXJ0aGRhdGUiXX0seyJwYXRoIjpbImFkZHJlc3MiLCJzdHJlZXRfYWRkcmVzcyJdfSx7InBhdGgiOlsiYWRkcmVzcyIsInBvc3RhbF9jb2RlIl19LHsicGF0aCI6WyJhZGRyZXNzIiwibG9jYWxpdHkiXX0seyJwYXRoIjpbImFkZHJlc3MiLCJjb3VudHJ5Il19LHsicGF0aCI6WyJuYXRpb25hbGl0aWVzIl19XSwibWV0YSI6eyJ2Y3RfdmFsdWVzIjpbInVybjpldWRpOnBpZDpkZToxIl19fSx7ImlkIjoicGlkLW1zby1tZG9jIiwiZm9ybWF0IjoibXNvX21kb2MiLCJjbGFpbXMiOlt7InBhdGgiOlsiZXUuZXVyb3BhLmVjLmV1ZGkucGlkLjEiLCJnaXZlbl9uYW1lIl19LHsicGF0aCI6WyJldS5ldXJvcGEuZWMuZXVkaS5waWQuMSIsImZhbWlseV9uYW1lIl19LHsicGF0aCI6WyJldS5ldXJvcGEuZWMuZXVkaS5waWQuMSIsImJpcnRoX2RhdGUiXX0seyJwYXRoIjpbImV1LmV1cm9wYS5lYy5ldWRpLnBpZC4xIiwicmVzaWRlbnRfc3RyZWV0Il19LHsicGF0aCI6WyJldS5ldXJvcGEuZWMuZXVkaS5waWQuMSIsInJlc2lkZW50X3Bvc3RhbF9jb2RlIl19LHsicGF0aCI6WyJldS5ldXJvcGEuZWMuZXVkaS5waWQuMSIsInJlc2lkZW50X2NpdHkiXX0seyJwYXRoIjpbImV1LmV1cm9wYS5lYy5ldWRpLnBpZC4xIiwicmVzaWRlbnRfY291bnRyeSJdfSx7InBhdGgiOlsiZXUuZXVyb3BhLmVjLmV1ZGkucGlkLjEiLCJuYXRpb25hbGl0eSJdfV0sIm1ldGEiOnsiZG9jdHlwZV92YWx1ZSI6ImV1LmV1cm9wYS5lYy5ldWRpLnBpZC4xIn19XSwiY3JlZGVudGlhbF9zZXRzIjpbeyJvcHRpb25zIjpbWyJwaWQtc2Qtand0Il0sWyJwaWQtbXNvLW1kb2MiXV19XX0sImNsaWVudF9tZXRhZGF0YSI6eyJqd2tzIjp7ImtleXMiOlt7Imt0eSI6IkVDIiwieCI6IlNoVTRGcjNOSDd2OVRPQWM5YVlpdTllaWNka2ZWVDllY1ZDUGFQZ0pyTXMiLCJ5IjoiaVYwVlhBU3lsUjBxV29Ecl9tS1VXd3pvLU01OVd6M1FCenBDbTRvaVhUMCIsImNydiI6IlAtMjU2IiwiYWxnIjoiRUNESC1FUyIsImtpZCI6ImE0MjBlZTgzLWVjZmEtNDRmYy1iYjE2LTgwMzIwZDg3Zjc0NSJ9XX0sInZwX2Zvcm1hdHNfc3VwcG9ydGVkIjp7Im1zb19tZG9jIjp7ImFsZyI6WyJFUzI1NiIsIkVkMjU1MTkiXX0sImRjK3NkLWp3dCI6eyJrYi1qd3RfYWxnX3ZhbHVlcyI6WyJFUzI1NiIsIkVkMjU1MTkiXSwic2Qtand0X2FsZ192YWx1ZXMiOlsiRVMyNTYiLCJFZDI1NTE5Il19fSwiZW5jcnlwdGVkX3Jlc3BvbnNlX2VuY192YWx1ZXNfc3VwcG9ydGVkIjpbIkExMjhHQ00iXX0sInN0YXRlIjoiM2FhNDcwNmMtNmYzNS00N2I4LThhMDUtNmEwYTMzNGMzMDFkIiwiYXVkIjoiaHR0cHM6Ly9zZWxmLWlzc3VlZC5tZS92MiIsImV4cCI6MTc2OTUxMzY5NywiaWF0IjoxNzY5NTEwMDk3fQ.2VHx61tIyHCisIXq3v_QoDpOxMBVHs7R5K0qOlIRwc5FgP-O0Q0qVhBgt6Sr-1SmN9u2jinH4Fx7lIav5J4p6g } ``` The wallet receives a single signed request object rather than individual parameters. The signature allows the wallet to verify authenticity, and cache-control headers prevent reuse. ### 3.2 Example PID Presentation Request (Decoded) Below is the decoded content of the Presentation Request contained in the signed request object. Line breaks and formatting are added for readability. Each field is annotated with a number (1) that corresponds to the explanation table below. ```json { "response_type": "vp_token", // (1) "client_id": "x509_hash:fQuobVwJv000vDWcMtriXPzo2sPTm5_Mp10O87lCqcE", // (2) "response_uri": "https://playground.eudi-wallet.org/eudiplo/3aa4706c-6f35-47b8-8a05-6a0a334c301d/oid4vp", // (3) "response_mode": "direct_post.jwt", // (4) "nonce": "85d5e24a-7163-4e44-a0ed-94be98cab666", // (5) "dcql_query": { // (6) "credentials": [ { "id": "pid-sd-jwt", // (7) "format": "dc+sd-jwt", // (8) "claims": [ // (9) {"path": ["given_name"]}, // (10) {"path": ["family_name"]}, // (11) {"path": ["birthdate"]}, // (12) {"path": ["address", "street_address"]}, // (13) {"path": ["address", "postal_code"]}, // (14) {"path": ["address", "locality"]}, // (15) {"path": ["address", "country"]}, // (16) {"path": ["nationalities"]} // (17) ], "meta": { "vct_values": ["urn:eudi:pid:de:1"] // (18) } }, { "id": "pid-mso-mdoc", // (19) "format": "mso_mdoc", // (20) "claims": [ {"path": ["eu.europa.ec.eudi.pid.1", "given_name"]}, // (21) {"path": ["eu.europa.ec.eudi.pid.1", "family_name"]}, // (22) {"path": ["eu.europa.ec.eudi.pid.1", "birth_date"]}, // (23) {"path": ["eu.europa.ec.eudi.pid.1", "resident_street"]}, // (24) {"path": ["eu.europa.ec.eudi.pid.1", "resident_postal_code"]}, // (25) {"path": ["eu.europa.ec.eudi.pid.1", "resident_city"]}, // (26) {"path": ["eu.europa.ec.eudi.pid.1", "resident_country"]},// (27) {"path": ["eu.europa.ec.eudi.pid.1", "nationality"]} // (28) ], "meta": { "doctype_value": "eu.europa.ec.eudi.pid.1" // (29) } } ], "credential_sets": [ { "options": [ ["pid-sd-jwt"], // (30) ["pid-mso-mdoc"] // (31) ] } ] }, "client_metadata": { // (32) "jwks": { "keys": [ { "kty": "EC", // (33) "crv": "P-256", // (34) "x": "ShU4Fr3NH7v9TOAc9aYiu9eicdkfVT9ecVCPaPgJrMs", // (35) "y": "iV0VXASylR0qWoDr_mKUWwzo-M59Wz3QBzpCm4oiXT0", // (36) "alg": "ECDH-ES", // (37) "kid": "a420ee83-ecfa-44fc-bb16-80320d87f745" // (38) } ] }, "vp_formats_supported": { // (39) "mso_mdoc": { "alg": ["ES256", "Ed25519"] // (40) }, "dc+sd-jwt": { "kb-jwt_alg_values": ["ES256", "Ed25519"], // (41) "sd-jwt_alg_values": ["ES256", "Ed25519"] // (42) } }, "encrypted_response_enc_values_supported": ["A128GCM"] // (43) }, "state": "3aa4706c-6f35-47b8-8a05-6a0a334c301d", // (44) "aud": "https://self-issued.me/v2", // (45) "exp": 1769513697, // (46) "iat": 1769510097 // (47) } ``` #### Field-by-Field Explanation | # | Field | Purpose | Notes | | ------- | ----------------------------------------- | --------------------- | ------------------------------------------------------------------------------ | | (1) | `response_type` | Response type | Must be `vp_token` for OpenID4VP. | | (2) | `client_id` | Verifier identifier | Uses `x509_hash:` scheme per HAIP; hash binds request to verifier certificate. | | (3) | `response_uri` | Response endpoint | HTTPS endpoint where wallet posts the `vp_token`. | | (4) | `response_mode` | Response delivery | `direct_post.jwt` is mandatory for HAIP high-assurance flows. | | (5) | `nonce` | Session binding | Cryptographically random value preventing replay attacks. | | (6) | `dcql_query` | Credential query | Defines which credentials and attributes are requested. | | (7) | `credentials[].id` | Credential identifier | Used to reference credentials in `credential_sets`. | | (8) | `credentials[].format` | Credential format | `dc+sd-jwt` for SD-JWT VC format. | | (9) | `claims` | Requested claims | List of attributes to be selectively disclosed. | | (10–17) | `claims[].path` | Claim paths (SD-JWT) | JSON path segments into the SD-JWT payload. | | (18) | `meta.vct_values` | Credential type | Identifies German PID VC (`urn:eudi:pid:de:1`). | | (19) | `credentials[].id` | Credential identifier | Identifier for mDoc PID request. | | (20) | `credentials[].format` | Credential format | ISO/IEC 18013-5 mDoc format. | | (21–28) | `claims[].path` | Claim paths (SD-JWT) | Namespace + attribute name as defined in PID mDoc profile. | | (29) | `meta.doctype_value` | Document type | Identifies the PID mDoc document type. | | (30) | `credential_sets.options[0]` | Option 1 | Wallet may respond using mDOc PID. | | (31) | `credential_sets.options[1]` | Option 2 | Wallet may respond using SD-JWT PID. | | (32) | `client_metadata` | Verifier metadata | Provides encryption keys and supported formats. | | (33) | `jwks.keys[].kty` | Key type | Elliptic Curve key. | | (34) | `jwks.keys[].crv` | Curve | P-256 curve (secp256r1). | | (35) | `jwks.keys[].x` | Public key X | Base64url-encoded coordinate. | | (36) | `jwks.keys[].y` | Public key Y | Base64url-encoded coordinate. | | (37) | `jwks.keys[].alg` | Key algorithm | Used for ECDH encryption of response. | | (38) | `jwks.keys[].kid` | Key ID | Used by wallet to select encryption key. | | (39) | `vp_formats_supported` | Format capabilities | Declares supported VP formats and algorithms. | | (40) | `vp_formats_supported.mso_mdoc.alg` | mDoc algorithms | Algorithms supported for mDoc signatures. | | (41) | `kb-jwt_alg_values` | Holder binding algs | Algorithms supported for key binding JWT. | | (42) | `sd-jwt_alg_values` | SD-JWT algs | Algorithms supported for SD-JWT issuer signatures. | | (43) | `encrypted_response_enc_values_supported` | Encryption | Symmetric encryption algorithms supported. | | (44) | `state` | CSRF protection | Returned unmodified in the response. | | (45) | `aud` | Audience | Fixed value for HAIP aligned responses. | | (46) | `exp` | Expiration | Limits lifetime of request object. | | (47) | `iat` | Issued-at | Used for freshness validation. | :::warning[Ephemeral Encryption Keys in client_metadata] The `jwks` contained in `client_metadata` are used only for encrypting the wallet's response. These keys **must be ephemeral**. The are generated fresh for each transaction and discarded after use. Never reuse the same encryption keypair across multiple presentation requests. Reusing keys increases the risk of exposure if a key is compromised, and defeats the purpose of per-transaction encryption. ::: ### 3.3 Request Construction Checklist Before sending a Presentation Request, verify that your request includes all elements needed for a PID presentation: | Check | Field | Validation | Failure Action | |-------|-------|------------|----------------| | | `iss` | Present and matches your verifier identifier | Request will be rejected | | | `aud` | Present and correctly targets the wallet | Request will be rejected | | | `iat` | Present and current timestamp | Request will be rejected if expired | | | `exp` | Present and set to reasonable future time (5-10 min) | Request will be rejected if expired | | | `client_id` | Present and uses correct scheme (e.g., `x509_hash:`). `x509_hash:` | Request will be rejected | | | `response_type` | Set to `"vp_token"` | Request will be rejected | | | `response_mode` | Set to `"direct_post.jwt"` for HAIP | Request will be rejected | | | `nonce` | Present, cryptographically random, unique per request | Security risk: replay attacks possible | | | `client_metadata.jwks` | Contains valid encryption key | Response cannot be encrypted | | | `verifier_info` | Contains valid registration certificate | Wallet will reject untrusted verifier | | | `response_uri` | Valid HTTPS URL, accessible by wallet | Response cannot be delivered | | | `dcql_query.credentials` | At least one credential specified | No data will be returned | | | `dcql_query.credentials[].format` | Valid format (`dc+sd-jwt` or `mso_mdoc`) | Request will be rejected | | | `dcql_query.credentials[].claims` | At least one claim per credential | Credential will be empty | | | Request object signature | Request object is properly signed (JAR) | Wallet will reject unsigned request | :::warning[Nonce Security] The `nonce` must be cryptographically random and unique per request. Reusing nonces enables replay attacks. Generate using a cryptographically secure random number generator. ::: :::tip[Expiration Time] Set `exp` to 5-10 minutes after `iat`. Too short may cause timeouts; too long increases security risk if the request is intercepted. ::: #### 3.3.1 Calculating your client_id The `client_id` always uses the prefix `x509_hash:`. The value after the colon is derived from your access certificate and calculated as follows. **Step 1: Obtain your verifier certificate in DER format** If you have your certificate in PEM format, convert it to DER: ```bash openssl x509 -in access_certificate.pem -outform DER -out access_certificate.der ``` **Step 2: Calculate the SHA-256 hash** Hash the DER-encoded certificate: ```bash openssl dgst -sha256 -binary access_certificate.der | base64 - ``` Or using standard utilities: ```bash sha256sum access_certificate.der # Shows hex hash ``` **Step 3: Base64url encode the hash** If you used `openssl dgst` above, it outputs base64 (standard). Convert standard base64 to base64url by replacing: - `+` with `-` - `/` with `_` - Remove padding (`=`) Alternatively, in one command: ```bash openssl dgst -sha256 -binary access_certificate.der | base64 | tr '+/' '-_' | tr -d '=' ``` The resulting `client_id` is formatted as `x509_hash:` and must match in both the deeplink and the authorization request. #### 3.3.2 Constructing your deeplink A deeplink is a URL that the verifier sends to the wallet to initiate a PID presentation flow. The wallet opens this deeplink, which contains all the information needed to fetch and process the presentation request. **Deeplink structure:** ``` openid4vp://?client_id=&request_uri=&request_uri_method=get ``` **Example from the [EUDI Playground](https://playground.eudi-wallet.org/):** ``` openid4vp://?client_id=x509_hash%3AtMqdcPmXTb5yZPBdQ7RTJcXOBViD9wUAO_SzvOaBXv0&request_uri=https%3A%2F%2Feudiplo.eudi-wallet.org%2Fpresentations%2Ff1020d4e-9578-4bf1-a233-1431d4c93af3%2Foid4vp%2Frequest&request_uri_method=get ``` **Key components:** - **Scheme:** `openid4vp://` identifies this as an OpenID4VP presentation request - **client_id:** Must match the `client_id` in your authorization request (see 3.3.1). It is URL-encoded here (`:` becomes `%3A`) - **request_uri:** HTTPS endpoint where the wallet fetches your signed presentation request. This URI should contain a unique session identifier that allows you to correlate the response to the request - **request_uri_method:** Specifies the HTTP method to use when fetching the request (`get` or `post`) #### 3.3.3 Handling the redirect after response After the wallet successfully posts its presentation response to your `response_uri`, you must return a redirect to the wallet. This is a key part of session finalization and is validated in Chapter 4 (step 3.10). **Response requirements:** - **HTTP Status Code:** Return `200 OK` - **Content-Type:** `application/json` - **Response body:** Include a `redirect_uri` field pointing to a page on your service where the wallet should navigate next **Example response:** ```json { "redirect_uri": "https://your-verifier.example/presentation-complete?session_id=abc123" } ``` **Important:** The wallet MUST follow the provided `redirect_uri`. As a verifier, you must validate that the wallet actually navigates to this URI (e.g., by observing a subsequent HTTP request from the wallet's user agent). This confirms that the wallet accepted the transaction completion and prevents attacks where a malicious application intercepts the response without the user's interaction. For details, see Chapter 4, step 3.10. ## 4. Interpreting the PID Presentation Response This chapter explains the structure of the PID Presentation Response and provides a complete validation checklist. It covers what the verifier receives at the response_uri, how to interpret the vp_token structure, and how to validate it using the validation layers described in Chapter 2. ### 4.1 Receiving the Presentation Response When the wallet has satisfied the Presentation Request and the user has consented, it sends the response to the verifier’s response_uri. In a HAIP-aligned flow using direct_post.jwt, this happens as a HTTPS POST. A typical HTTP interaction looks like this: ```http POST /response HTTP/1.1 Host: response.example Content-Type: application/x-www-form-urlencoded response=eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTEyOEdDTSIsImtpZCI6ImFjIn0... ``` The `response` parameter contains an encrypted JWT (JWE). The verifier should treat this object as opaque until basic transport-level checks (TLS, content type, size limits) have passed. ### 4.2 High-Level Structure of the Response The presentation response structure with `direct_post.jwt` consists of: **Outer Layer (Encrypted Response):** - An encrypted JWT (JWE) containing the entire response - Encrypted using the verifier's public key (from the request's `client_metadata.jwks`) - After decryption, contains session binding claims (`nonce`, `aud`, `iat`, `exp`, `state`) and the `vp_token` **Inner Layer (vp_token):** - For SD-JWT: A signed credential in the format `~~...~~` - For mDoc: CBOR-encoded credential with deviceAuth structure - The vp_token itself is NOT encrypted separately - it's simply included in the encrypted response payload :::info[Response Encryption] The entire response is encrypted as a single JWE. The vp_token inside is a signed (not encrypted) credential format. This prevents "double encryption" which would be unnecessary. ::: ### 4.3 Response Validation Checklist The checklist below provides a complete validation workflow for the PID presentation response. It follows the validation layers described in [Chapter 2](#2-concepts-and-validation-layers), proceeding in order from transport checks through business rules. Each layer builds on the previous one, following a fail-fast principle: reject immediately upon any validation failure to save processing time and reduce attack surface. **Important:** With `direct_post.jwt` response mode, the entire response is encrypted as a JWE. The vp_token inside is a signed credential (SD-JWT or mDoc), NOT separately encrypted. Decryption must happen before session binding can be validated. For detailed explanations of why each check matters, refer back to the corresponding validation layer in Chapter 2. | Step | Check | Implementation | Failure Action | |------|-------|----------------|----------------| | **1. Transport Layer** | | | | | 1.1 | Verify HTTPS connection | Confirm TLS 1.2+ is used and server certificate is valid | Reject: Insecure transport | | 1.2 | Check content type | Verify `Content-Type: application/x-www-form-urlencoded` header is present | Reject: Wrong content type | | 1.3 | Verify payload size | Confirm payload is within reasonable limits (e.g., < 1MB) to prevent DoS | Optionally reject: Payload too large | | **2. Decrypt Response** | | | | | 2.1 | Extract response parameter | Parse the POST body and extract the `response` parameter value | Reject: Missing response parameter | | 2.2 | Parse JWE structure | Verify the response has valid JWE format (five base64url-encoded parts) | Reject: Malformed JWE | | 2.3 | Decode JWE header | Parse JWE header as valid JSON and extract `alg`, `enc`, and `kid` fields | Reject: Invalid JWE header | | 2.4 | Verify encryption algorithm | Confirm `alg` is in allowed list (e.g., `ECDH-ES`) and `enc` is supported (e.g., `A128GCM`) | Reject: Unsupported encryption | | 2.5 | Decrypt response | Use your private key (matching the public key sent in request `client_metadata.jwks`) to decrypt the JWE | Reject: Decryption failed | | 2.6 | Parse decrypted payload | Parse the decrypted plaintext as valid JSON | Reject: Invalid payload structure | | **3. Session Binding** | | | | | 3.1 | Extract `nonce` from payload | Locate `nonce` claim in decrypted response payload | Reject: Missing nonce | | 3.2 | Compare nonce with request | Verify nonce matches exactly (byte-for-byte) the nonce from your original request | Reject: Nonce mismatch | | 3.3 | Extract `aud` from payload | Locate `aud` claim in decrypted response payload | Reject: Missing audience | | 3.4 | Verify audience matches verifier | Confirm `aud` matches your verifier's `client_id` from the request | Reject: Wrong audience | | 3.5 | Extract `iat` timestamp | Locate `iat` (issued-at) claim in decrypted response payload | Reject: Missing timestamp | | 3.6 | Extract `exp` timestamp | Locate `exp` (expiration) claim in decrypted response payload | Reject: Missing expiration | | 3.7 | Verify timestamp validity | Confirm current time is between `iat` and `exp` (accounting for clock skew, e.g., ±30s) | Reject: Expired or not yet valid | | 3.8 | Check response freshness | Verify `iat` is within acceptable window from request time (e.g., last 5 minutes) | Reject: Response too old | | 3.9 | Extract `state` (if used) | If `state` was included in your request, locate it in decrypted response payload and verify it matches | Reject: State mismatch | | 3.10 | In a same-device flow: Wallet follows the redirect_uri | validate that the wallet follows the redirect link so the presentation stays tied to the session. | Reject: Wallet denied redirect | | **4. Holder Binding** | | | | | 4.1 | Extract `vp_token` | Locate the `vp_token` field in decrypted response payload | Reject: Missing vp_token | | 4.2 | Verify credential format matches request | Confirm vp_token format (SD-JWT with `~` separators or mDoc CBOR) matches the format you requested in `dcql_query` | Reject: Wrong credential format | | 4.3 | SD-JWT: Extract credential | Extract the issuer-signed JWT (first part before `~`) | Reject: Cannot extract credential | | 4.4 | SD-JWT: Parse credential structure | Decode credential JWT header and payload | Reject: Malformed credential | | 4.5 | SD-JWT: Extract holder's public key | Extract `cnf.jwk` claim from credential payload per RFC 7800 | Reject: Missing cnf claim | | 4.6 | SD-JWT: Extract KB-JWT | Extract the KB-JWT (last element after final `~`) | Reject: Missing KB-JWT | | 4.7 | SD-JWT: Parse KB-JWT structure | Decode KB-JWT header and payload | Reject: Malformed KB-JWT | | 4.8 | SD-JWT: Verify KB-JWT audience | Confirm `aud` in KB-JWT matches your verifier identifier | Reject: Wrong KB-JWT audience | | 4.9 | SD-JWT: Verify KB-JWT nonce | Confirm `nonce` in KB-JWT matches the nonce from your request | Reject: KB-JWT nonce mismatch | | 4.10 | SD-JWT: Verify KB-JWT timestamp | Confirm `iat` in KB-JWT is recent and consistent with response `iat` from step 3.5 | Reject: Invalid KB-JWT timestamp | | 4.11 | SD-JWT: Verify KB-JWT signature | Verify the KB-JWT signature using the public key extracted in step 4.5 | Reject: Invalid holder binding signature | | 4.12 | SD-JWT: Verify hash binding | Confirm KB-JWT's `sd_hash` matches SHA-256 hash of the presentation (issuer-signed JWT + `~` + disclosures) | Reject: Hash mismatch | | 4.13 | mDoc: Validate holder binding | Validate deviceAuth and holder binding in accordance with ISO 18013-5 specification | Reject: Invalid mDoc holder binding | | **5. Credential Validation** | | | | | 5.1 | SD-JWT: Identify credential type | Verify `vct` claim matches requested PID type (e.g., `urn:eudi:pid:de:1`) | Reject: Wrong credential type | | 5.2 | SD-JWT: Extract issuer identifier | Extract `iss` claim from credential payload | Reject: Missing issuer | | 5.3 | SD-JWT: Verify issuer is trusted | Look up issuer in your loaded trust list (PID providers) and verify it is authorized to issue PIDs | Reject: Untrusted issuer | | 5.4 | SD-JWT: Extract issuer's public key | Retrieve issuer's public key from trust list or from credential's `x5c` chain in credential header | Reject: Cannot find issuer key | | 5.5 | SD-JWT: Verify issuer signature | Verify the credential signature using issuer's public key | Reject: Invalid credential signature | | 5.6 | SD-JWT: Verify credential validity | Check `iat`, `exp` claims and confirm credential is currently valid (accounting for clock skew) | Reject: Expired credential | | 5.7 | SD-JWT: Check revocation status | Query revocation mechanism (status list, OCSP, or CRL) to confirm credential is not revoked | Reject: Revoked credential | | 5.8 | SD-JWT: Verify certificate chain | If issuer uses X.509 certificates (`x5c` in header), verify the full chain up to trust anchor and check certificate validity | Reject: Invalid certificate chain | | 5.9 | mDoc: Validate credential | Validate mDoc credential structure, issuer signature, trust, and validity in accordance with ISO 18013-5 specification | Reject: Invalid mDoc credential | | **6. Selective Disclosure** | | | | | 6.1 | SD-JWT: Extract disclosures | Parse all disclosed attributes from the SD-JWT (elements between `~` separators, excluding issuer-signed JWT and KB-JWT) | Reject: Cannot parse disclosures | | 6.2 | SD-JWT: Verify disclosure integrity | Verify each disclosure's hash matches the corresponding `_sd` hash in the issuer-signed JWT | Reject: Disclosure tampering detected | | 6.3 | mDoc: Validate selective disclosure | Validate disclosed namespaces and elements in accordance with ISO 18013-5 specification | Reject: Invalid mDoc disclosures | | 6.4 | Compare with requested claims | Check that all disclosed claims were explicitly requested in your `dcql_query` | Warning: Extra claims disclosed | | 6.5 | Verify mandatory claims present | Confirm all claims you marked as mandatory in your request are present in the response | Reject: Missing mandatory claims | | 6.6 | Verify credential set compliance (if used) | If `credential_sets` was used in your request, confirm the combination of credentials received matches one of the options | Reject: Invalid credential combination | | 6.7 | Check for over-disclosure | Verify no additional credentials beyond what was requested are included in the response | Warning: Unexpected credentials | | **7. Business Rules** | | | | | 7.1 | Validate attribute formats | Verify each disclosed claim value conforms to expected data types and formats (dates, strings, structured data) | Reject: Invalid attribute format | | 7.2 | Evaluate business requirements | Apply your application-specific logic (e.g., age >= 18, nationality checks, address validation) | Reject: Business rule violation | | 7.3 | Check data completeness | Verify all attributes needed for your use case are present and non-empty | Reject: Incomplete data | | 7.4 | Record authorization decision | Log the validation outcome, disclosed attributes (respecting privacy), and authorization decision for audit purposes | N/A | :::tip[Fail-Fast Principle] Reject immediately upon any validation failure that is more severe than a warning. Do not continue validation after a critical failure (e.g., signature verification). This prevents information leakage and reduces processing costs. ::: --- ### 4.4 Failure Handling and Diagnostics Verifier implementations should provide clear diagnostics internally. Common rejection points include: - Signature verification failure - Nonce or audience mismatch - Unsupported credential format - Missing or extra disclosed attributes - Invalid or untrusted wallet attestation While error details should not be exposed to the wallet or user, precise internal logging is essential for debugging interoperability issues. --- ## German PID Reference for Relying Parties This page provides a practical reference for Relying Parties integrating with the German Personal Identification Data (PID) credential. It summarises the key technical details from the [German PID Rulebook (PID-DE)](https://bmi.usercontent.opencode.de/eudi-wallet/eidas-2.0-architekturkonzept/content/ecosystem-architecture/PID/german-pid-rulebook/) that you need to understand in order to successfully request, receive, and verify a PID. --- ## Overview The German PID is issued by a single, nationally designated PID Provider. It is based on the German eID card (Personalausweis), electronic Residence Permit, or eID Card for Union Citizens. The PID is available in two credential formats: - **SD-JWT VC** - **ISO mDoc (ISO/IEC 18013-5)** Both formats carry the same underlying identity data. :::info[Special Characters] All string values in the German PID are stored in **UPPERCASE**, following ICAO Doc 9303. Special characters such as `Ö` are represented in their ISO/IEC 8859-1 form and are **not** transliterated (e.g., `Ö` is not replaced with `OE`). ::: --- ## Credential Identifiers When matching a received credential against the expected type, use the following identifiers: | Format | Identifier | |---|---| | SD-JWT VC | `vct`: `urn:eudi:pid:de:1` | | mDoc | `docType`: `eu.europa.ec.eudi.pid.1` | German-specific extension attributes in the mDoc format are in the namespace `eu.europa.ec.eudi.pid.de.1`. --- ## PID Attributes ### Mandatory Attributes (CIR 2024/2977) These attributes are always present in a German PID. | SD-JWT VC claim | mDoc identifier | Description | Example value | |---|---|---|---| | `family_name` | `family_name` | Holder's surname | `MUSTERMANN` | | `given_name` | `given_name` | Holder's given name(s) | `ERIKA` | | `birthdate` | `birth_date` | Date of birth (YYYY-MM-DD). Unknown month or day encoded as `00` | `1983-08-12` or `1983-00-00` | | `place_of_birth` | `place_of_birth` | JSON object / CBOR map with at least `locality`. If no place is recorded, `no_place_info: true` is set | `{"locality": "BERLIN"}` | | `nationalities` | `nationality` | Array of ISO 3166-1 alpha-2 codes. If eID was issued before Nov. 2019, automatically set to `["DE"]` | `["DE"]` | ### Optional Attributes (CIR 2024/2977) These attributes are included when available from the holder's eID. Note that `resident_address` and `resident_house_number` are **not available** in the German PID. | SD-JWT VC claim | mDoc identifier | Description | Example value | |---|---|---|---| | `address.country` | `resident_country` | ISO 3166-1 alpha-2 country code of residence | `DE` | | `address.region` | `resident_state` | Federal state or region of residence | `NORDRHEIN-WESTFALEN` | | `address.locality` | `resident_city` | City of residence | `KÖLN` | | `address.postal_code` | `resident_postal_code` | Postal code | `51147` | | `address.street_address` | `resident_street` | Street name and house number combined | `HEIDESTRASSE 17` | :::note Residential address fields are only present if the eID returns a `StructuredPlace`. They may be absent even when a holder has a registered address. ::: ### Mandatory Metadata (CIR 2024/2977) | SD-JWT VC claim | mDoc identifier | Description | Example value | |---|---|---|---| | `date_of_expiry` | `expiry_date` | PID expiry date (YYYY-MM-DD) | `2035-01-01` | | `issuing_authority` | `issuing_authority` | Set to the issuing country code (no separate authority available in German eID) | `DE` | | `issuing_country` | `issuing_country` | ISO 3166-1 alpha-2 country code, always `DE` for German IDs | `DE` | ### Germany-Specific Additional Attributes These attributes are defined by the German PID Rulebook and extend the base EU specification. In mDoc, they are found in the `eu.europa.ec.eudi.pid.de.1` namespace. | SD-JWT VC claim | mDoc identifier | Description | Example value | |---|---|---|---| | `source_document_type` | `source_document_type` | Type of source document. `ID` = German eID card, `AR`/`AS`/`AF` = Residence Permit, `IC` = EU Citizen ID Card | `ID` | | `age_equal_or_over` (object) | `age_over_NN` (individual fields) | Boolean age thresholds. SD-JWT groups these under one object; mDoc has individual claims per threshold | See below | | `birth_name` | `birth_name` | Birth name (may include both given and family name at birth); may be empty | `GABLER` | | `also_known_as` | `also_known_as` | Artistic or stage name | *(optional)* | | `academic_title` | `academic_title` | Academic title | *(optional)* | #### Age Verification Thresholds The PID supports age verification without revealing the holder's exact date of birth. Available thresholds are **12, 14, 16, 18, 21, and 65**. In **SD-JWT VC**, these are grouped under the `age_equal_or_over` object: ```json "age_equal_or_over": { "12": true, "14": true, "16": true, "18": true, "21": true, "65": false } ``` In **mDoc**, each threshold is an individual claim (e.g., `age_over_18`) in the `eu.europa.ec.eudi.pid.de.1` namespace. When implementing an age-gate (e.g., 18+ access), you should **request only the relevant threshold** rather than the holder's `birthdate`, in line with the principle of data minimisation. ### Revocation Status Attribute | SD-JWT VC claim | mDoc identifier | Description | |---|---|---| | `status` | `status` | Reference to a Token Status List (IETF OAuth Status List draft) for revocation checking | ```json "status": { "status_list": { "idx": 0, "uri": "https://example.com/statuslists/1" } } ``` --- ## Complete Example PIDs ### SD-JWT VC Example The decoded payload of a German PID issued as SD-JWT VC looks as follows. In practice the credential is delivered as a compact serialized SD-JWT, with selectively disclosable claims hidden until disclosed by the holder. ```json { "vct": "urn:eudi:pid:de:1", "family_name": "MUSTERMANN", "given_name": "ERIKA", "birthdate": "1983-08-12", "place_of_birth": { "locality": "BERLIN" }, "nationalities": ["DE"], "address": { "country": "DE", "region": "NORDRHEIN-WESTFALEN", "locality": "KÖLN", "postal_code": "51147", "street_address": "HEIDESTRASSE 17" }, "birth_name": "GABLER", "date_of_expiry": "2031-08-01", "issuing_authority": "DE", "issuing_country": "DE", "source_document_type": "ID", "age_equal_or_over": { "12": true, "14": true, "16": true, "18": true, "21": true, "65": false }, "status": { "status_list": { "idx": 0, "uri": "https://example.com/statuslists/1" } }, "cnf": { "jwk": { "kty": "EC", "crv": "P-256", "x": "52aDI_ur05n1f_p3jiYGUU82oKZr3m4LsAErM536crQ", "y": "ckhZ-KQ5aXNL91R8Eufg1aOf8Z5pZJnIvuCzNGfdnzo" } } } ``` **Key points for SD-JWT VC:** - The `vct` claim identifies this credential as a German PID. Always validate this before trusting any claims. - Claims `vct`, `vct#integrity`, `cnf`, and `metadata` cannot be selectively disclosed — they are always present in the header. - `iat` and `exp` define the lifetime of the individual PID token (not to be confused with `date_of_expiry`, which reflects the eID card's expiry). - `birthdate` may take the form `1983-00-00` when the day or month is unknown. - `nationalities` is always an array, even when only one nationality is present. --- ### mDoc Example The following is the decoded logical structure of a German PID in mDoc format (ISO/IEC 18013-5). In practice the data is CBOR-encoded and the attributes are individually integrity-protected using digest IDs and random salts to enable selective disclosure. ``` { "docType": "eu.europa.ec.eudi.pid.1", "issuerSigned": { "nameSpaces": { "eu.europa.ec.eudi.pid.1": { "family_name": "MUSTERMANN", "given_name": "ERIKA", "birth_date": "1983-08-12", "place_of_birth": { "locality": "BERLIN" }, "nationality": ["DE"], "resident_country": "DE", "resident_state": "NORDRHEIN-WESTFALEN", "resident_city": "KÖLN", "resident_postal_code": "51147", "resident_street": "HEIDESTRASSE 17", "expiry_date": "2035-01-01", "issuing_authority": "DE", "issuing_country": "DE" }, "eu.europa.ec.eudi.pid.de.1": { "source_document_type": "ID", "birth_name": "GABLER", "age_over_12": true, "age_over_14": true, "age_over_16": true, "age_over_18": true, "age_over_21": true, "age_over_65": false, "status": { "status_list": { "idx": 0, "uri": "https://example.com/statuslists/1" } } } }, "issuerAuth": { ... } }, "deviceSigned": { ... } } ``` **Key points for mDoc:** - The `docType` `eu.europa.ec.eudi.pid.1` identifies this as a PID. Always verify this before processing. - Core attributes are in the `eu.europa.ec.eudi.pid.1` namespace; German-specific extensions are in `eu.europa.ec.eudi.pid.de.1`. - Each attribute is individually salted and hashed, allowing the holder to selectively disclose only the claims you have requested. - `birth_date` uses the format `YYYY-MM-DD` but may contain `00` for unknown month or day (e.g., `1983-00-00`). - `place_of_birth` is a CBOR map with a `locality` key. This differs from the ISO mDL standard, where `birth_place` is a plain string. - `nationality` is always an array of alpha-2 country codes. --- ## Attribute Mapping Reference The same underlying data is labelled differently across formats. Use this table when writing queries or parsing responses across both credential types. | CIR 2024/2977 identifier | SD-JWT VC claim | mDoc identifier (namespace) | |---|---|---| | `family_name` | `family_name` | `family_name` (pid.1) | | `given_name` | `given_name` | `given_name` (pid.1) | | `birth_date` | `birthdate` | `birth_date` (pid.1) | | `birth_place` | `place_of_birth` | `place_of_birth` (pid.1) | | `nationality` | `nationalities` | `nationality` (pid.1) | | `resident_country` | `address.country` | `resident_country` (pid.1) | | `resident_state` | `address.region` | `resident_state` (pid.1) | | `resident_city` | `address.locality` | `resident_city` (pid.1) | | `resident_postal_code` | `address.postal_code` | `resident_postal_code` (pid.1) | | `resident_street` | `address.street_address` | `resident_street` (pid.1) | | `expiry_date` | `date_of_expiry` | `expiry_date` (pid.1) | | `issuing_authority` | `issuing_authority` | `issuing_authority` (pid.1) | | `issuing_country` | `issuing_country` | `issuing_country` (pid.1) | | *(German extension)* | `age_equal_or_over.NN` | `age_over_NN` (pid.de.1) | | *(German extension)* | `source_document_type` | `source_document_type` (pid.de.1) | | *(German extension)* | `birth_name` | `birth_name` (pid.de.1) | --- ## What Is and Is Not Available Several attributes defined in the EU specification are **not available** in the German PID due to limitations of the source eID document. Plan your integration accordingly and do not assume these fields will be present. | Attribute | Available? | Notes | |---|---|---| | `family_name` | ✅ Always | | | `given_name` | ✅ Always | | | `birthdate` | ✅ Always | May contain `00` for unknown month/day | | `place_of_birth` | ✅ Always | Only `locality` populated | | `nationalities` | ✅ Always | Defaults to `["DE"]` for pre-Nov 2019 cards | | `expiry_date` | ✅ Always | | | `issuing_country` | ✅ Always | Always `DE` | | `issuing_authority` | ✅ Always | Populated with `DE` (no separate authority field exists) | | `source_document_type` | ✅ Always | German extension | | Residential address fields | ⚠️ Conditional | Only present if eID returns a structured address | | `birth_name` | ⚠️ Conditional | May be empty | | `age_equal_or_over` / `age_over_NN` | ⚠️ Conditional | Present when requested; reflects thresholds supported by the PID Provider | | `academic_title`, `also_known_as` | ⚠️ Conditional | Optional German extensions | | `resident_address` (combined) | ❌ Not available | | | `resident_house_number` | ❌ Not available | House number is part of `resident_street` | | `portrait` | ❌ Not available | | | `document_number` | ❌ Not available | | | `sex` | ❌ Not available | Set to `9` (other) in Residence Permit PIDs only | | `email_address` | ❌ Not available | | | `mobile_phone_number` | ❌ Not available | | | `personal_administrative_number` | ❌ Not available | | --- ## SPRIND EUDI-Wallet Sandbox Nutzungsbedingungen ## 1. Geltungsbereich 1.1 Die Sandbox soll vertrauenden Parteien, Ausstellern von EAA und vergleichbaren Akteuren (“EUDIW-Partner”) dabei helfen, ihre Dienste im Einklang mit dem nationalen EUDI-Wallet-Ökosystem zu erkunden, zu testen, vorzubereiten und zu implementieren. 1.2 Die Sandbox wird von SPRIND GmbH, Lagerhofstraße 4, 04103 Leipzig, Deutschland (nachfolgend “Betreiberin”) zur Verfügung gestellt. Die Benutzung der Sandbox ist ausschließlich aufgrund dieser Bedingungen zulässig. \ Diese Nutzungsbedingungen regeln, ihre wirksame Einbeziehung vorausgesetzt, das gesamte Rechtsverhältnis, insbesondere die Rechte und Pflichten den EUDIW-Partnern im Zusammenhang mit der Nutzung der **Sandbox des deutschen EUDI Wallet Ökosystems* ***(“Sandbox”). Die Online-Inhalte sind Eigentum der Betreiberin, soweit nichts anderes geregelt ist. Diese Nutzungsbedingungen beziehen sich ausschließlich auf die Nutzung der mit der Testumgebung einhergehenden Dienste. Für andere Leistungen der Betreiberin gelten diese nur, wenn im Zusammenhang mit der betreffenden Leistung ausdrücklich auf diese Nutzungsbedingungen verwiesen wird. 1.3 Die Sandbox befindet sich noch in einem Testzustand. Der EUDIW-Partner muss davon ausgehen, dass noch nicht alle Produkteigenschaften vollständig funktionsfähig sind. Sie ist nicht für den produktiven oder wirtschaftlichen Einsatz geeignet. Der Einsatz dieser Sandbox erfolgt deshalb auf eigenes Risiko des EUDIW-Partners. 1.4 Die Nutzungsbedingungen der Betreiberin gelten ausschließlich. Geschäftsbedingungen des EUDIW-Partners finden keine Anwendung, auch wenn die Betreiberin ihrer Geltung nicht gesondert widerspricht. Abweichende oder widersprechende Bedingungen gelten nur, wenn sie von der Betreiberin schriftlich anerkannt worden sind. Selbst wenn die Betreiberin auf ein Schreiben Bezug nimmt, das Geschäftsbedingungen des EUDIW-Partners oder eines Dritten enthält oder auf solche verweist, liegt darin kein Einverständnis mit der Geltung jener Geschäftsbedingungen. 1.5 Die Betreiberin behält sich das Recht vor, diese Nutzungsbedingungen jederzeit mit Wirkung für die Zukunft zu ändern oder anzupassen. Änderungen werden dem EUDIW-Partner mindestens vier Wochen vor ihrem geplanten Inkrafttreten in Textform (z. B. per E-Mail oder über die Kommunikationskanäle der Sandbox) unter Mitteilung des Inhalts der Änderungen bekannt gegeben. Die Änderungen gelten als von dem EUDIW-Partner angenommen, wenn er ihnen nicht innerhalb von vier Wochen nach Zugang der Mitteilung in Textform widerspricht. Die Betreiberin wird den EUDIW-Partner in der Änderungsmitteilung ausdrücklich auf dieses Widerspruchsrecht und die Folgen eines unterlassenen Widerspruchs hinweisen. Widerspricht der EUDIW-Partner den Änderungen, haben beide Parteien das Recht, das Nutzungsverhältnis mit Wirkung zum Zeitpunkt des Inkrafttretens der Änderungen außerordentlich zu kündigen. In diesem Fall wird der Zugang zur Sandbox gesperrt. 1.6 Vertragssprache ist Deutsch. Etwaige Übersetzungen dieser Nutzungsbedingungen in anderen Sprachen dienen ausschließlich Informationszwecken. Im Falle von Widersprüchen geht die deutsche Fassung vor. 1.7 Sofern Hinweise auf die Geltung gesetzlicher Vorschriften erfolgen, ist zu beachten, dass diesen lediglich eine klarstellende Bedeutung zukommt. Es gelten die gesetzlichen Vorschriften – auch wenn keine entsprechende Klarstellung erfolgt ist – in den Grenzen, in denen sie nicht durch die Nutzungsbedingungen abgeändert oder ausgeschlossen werden. ## 2. Umfang der Sandbox & Vertragsschluss 2.1 Der Zweck der Sandbox besteht darin, Anwendungsfälle zu entwickeln, zu erproben und Konformitätstests durchzuführen. So sollen EUDIW-Partner auf eine Produktionsumgebung vorbereitet werden. Die Sandbox ist Teil des deutschen EUDI-Wallet-Ökosystems und wird auch nach dem produktiven Start des Hauptangebots weiter bestehen. Sie dient als Testumgebung für verschiedene EUDIW-Partner (z. B. vertrauende Parteien, Aussteller von EAA, Wallet-Anbieter, Vertrauensdiensteanbieter), um sicherzustellen, dass ihre Implementierungen funktionieren, konform und interoperabel sind, und um Betriebsabläufe zu testen. 2.2 Die Sandbox als Testumgebung umfasst die folgenden Inhalte: * Zugang zu der von der Betreiberin bereitgestellten nationalen “EUDI Wallet”-App * Zugang zum durch den Betreiber bereitgestellten Aussteller von PID * Zugang zur German EUDI Ecosystem Sandbox Registrar API inkl. Zugangszertifikat für Wallet-Anfragen * Zugang zu einem Kommunikationskanal für Austausch mit anderen EUDIW-Partnern und Ansprechpartnern der Betreiberin 2.3 Die Nutzung der Sandbox-Dienste erfolgt derzeit unentgeltlich. Es besteht jedoch kein Anspruch auf eine dauerhaft kostenlose Bereitstellung. Die Betreiberin behält sich vor, die Dienste künftig gegen Entgelt anzubieten. Eine Umstellung auf ein kostenpflichtiges Vertragsverhältnis bedarf der gesonderten, ausdrücklichen Zustimmung des registrierten EUDIW-Partners. Sollte der EUDIW-Partner der Einführung einer Vergütung nicht zustimmen, endet das Nutzungsrecht automatisch und fristlos mit dem Zeitpunkt des Inkrafttretens der Kostenpflicht. 2.4 Die Registrierung über das [Registrierungsformular](https://2eut7s.share-eu1.hsforms.com/2QlEzCm_NR6SbkHuq9vbFWw) gilt als eine an die Betreiberin gerichtete Aufforderung zur Abgabe eines Angebots für die Nutzung der Sandbox. Der tatsächliche Vertrag zur Nutzung der Sandbox kommt erst durch die Annahme des durch die Betreiberin im Zuge des Onboarding-Verfahrens gestellten Angebots zustande. Die Betreiberin behält sich das Recht vor, die Registrierungsanfragen nach eigenem Ermessen abzulehnen; ein Rechtsanspruch auf Nutzung der Sandbox besteht nicht. *2*.5 Die Nutzung der Sandbox ist beschränkt auf die Anzahl der bei der Betreiberin registrierten Nutzer. Die Weitergabe von Zugängen ist untersagt. Werden weitere Zugänge benötigt, müssen diese bei der Betreiberin registriert werden. 2.6 Der Zugang zu der Sandbox führt nicht automatisch zu einem späteren Zugang zu der Produktionsumgebung. ## 3. Nutzung der Sandbox 3.1 Die erwünschte Nutzung der Sandbox sieht das Testen der Kompatibilität der für die eigenen Anwendungsfälle entwickelten technischen Lösungen mit dem EUDI-Wallet-Ökosystem vor. Von dem EUDIW-Partner wird ein kooperatives Testverhalten erwartet. Dies beinhaltet insbesondere die Rücksichtnahme darauf, dass das System möglicherweise noch nicht fehlerfrei ist, die aktive Meldung technischer Probleme sowie einen ressourcenschonenden Umgang mit der bereitgestellten Infrastruktur, um die Testaktivitäten anderer Teilnehmer nicht durch unverhältnismäßige Systemlast zu beeinträchtigen. Die Betreiberin untersagt ausdrücklich Nutzungsweisen, die zu einer ungewöhnlichen Belastung der angebotenen Testumgebung sowie Beeinträchtigung der Nutzung und Systeme anderer Nutzer führen oder darauf ausgelegt sind, technische Schwachstellen aufzudecken (z. B. “Penetration Testing”), außer im Rahmen einer von der Betreiberin definierten Vulnerability Disclosure Policy. Darüber hinaus begrüßt die Betreiberin Hinweise, Feedback sowie gegenseitigen Austausch über die angebotenen Kommunikationskanäle. 3.2 Der EUDIW-Partner ist zur Erhebung und Übermittlung technischer Kennzahlen und anonymisierter Statistiken in einer von der Betreiberin vorgegebenen Art und Weise verpflichtet. Dies dient der Verbesserung des Angebots. 3.3 Es dürfen ausschließlich Testdaten verwendet werden. Die Nutzung von echten personenbezogenen Daten ist strikt untersagt („No Real Data Policy“). ## 4. Gewährleistung & Verfügbarkeit 4.1 Bei der Sandbox handelt es sich um die Bereitstellung einer technischen Testumgebung, die dabei helfen soll, Anwendungsfälle und die Nutzung der technischen Infrastruktur rund um die EUDI Wallet zu testen. Die angebotenen Services werden deshalb ohne Vereinbarung einer bestimmten Beschaffenheit oder Qualität und ohne die Vereinbarung der Eignung für einen bestimmten Zweck bereitgestellt. Die Betreiberin ist nicht verpflichtet, dafür Sorge zu tragen, dass die Anwendung fehlerfrei funktioniert und unterbrechungsfrei zur Verfügung steht. Eine bestimmte Verfügbarkeit der Services ist nicht geschuldet. Die Betreiberin ist nicht zur Mängelbeseitigung verpflichtet, sofern der Mangel nicht arglistig verschwiegen wurde und ein Schadensrisiko darstellt. 4.2 Die Betreiberin kann die angebotenen Services zu jeder Zeit einschränken oder wegen Wartung sowie Updates offline nehmen. 4.3 Die Betreiberin garantiert keinen technischen Support. Die angebotene Unterstützung richtet sich nach den verfügbaren Kapazitäten und stellt eine freiwillige Leistung dar. Ein Anspruch auf einen bestimmten Supportumfang oder Reaktionszeiten (SLA) besteht nicht. 4.4 Die Betreiberin übernimmt keine Gewährleistung oder Garantie dafür, dass die Testergebnisse der Sandbox die tatsächliche technische oder wirtschaftliche Machbarkeit oder die spätere Produktreife des Anwendungsfalls des EUDIW-Partners widerspiegeln. Selbst wenn die Sandbox die technische Funktionsfähigkeit eines Anwendungsfalls suggeriert, trägt der EUDIW-Partner das ausschließliche Risiko dafür, dass ihr Use Case in der Produktionsumgebung oder unter realen Bedingungen nicht umsetzbar oder wirtschaftlich nicht tragfähig ist. Die Betreiberin haftet nicht für Fehlinvestitionen oder Fehlplanungen, die auf den Ergebnissen der Sandbox basieren. 4.5 Die Betreiberin erbringt im Rahmen der Sandbox und über die angebotenen Kommunikationskanäle keine Rechts-, Steuer-, Fach- oder Unternehmensberatung (Consulting). Die zur Verfügung gestellten Informationen, Dokumentationen und der Community-Austausch dienen lediglich dem technischen Test und der Unterstützung. Es obliegt der alleinigen Verantwortung des EUDIW-Partners die technische, rechtliche und geschäftliche Zulässigkeit und Umsetzbarkeit ihrer spezifischen Anwendung zu prüfen und sicherzustellen, dass diese den geltenden nationalen und europäischen Vorschriften entspricht. ## 5. Vertraulichkeitsklausel 5.1 Der EUDIW-Partner verpflichtet sich, alle technischen, betrieblichen und geschäftlichen Informationen, die sie im Rahmen des Sandbox-Zugangs von der Betreiberin erhält (insbesondere Zugangsdaten, nicht-öffentliche Dokumentationen zur EUDI-Wallet sowie Informationen über Sicherheitslücken oder Bugs), streng vertraulich zu behandeln und nicht an Dritte weiterzugeben, es sei denn, dies ist zur Erreichung des Vertragszwecks (Testung der Interoperabilität) zwingend erforderlich, die Betreiberin hat schriftlich zugestimmt oder die Informationen wurden durch die Betreiberin bereits veröffentlicht oder sind auf andere Weise öffentlich. 5.2 Die Betreiberin stellt einen Kommunikationskanal (z. B. Mattermost) bereit, in dem sich verschiedene Teilnehmer der Sandbox austauschen können. Um einen offenen Austausch zu fördern und gleichzeitig die Interessen aller Beteiligten zu wahren, gilt für die Nutzung dieses Kanals und die Verwertung der dort erlangten Informationen Folgendes: * **Inhaltliche Nutzung:** Dem EUDIW-Partner ist es gestattet, die im Kanal geteilten fachlichen Informationen und Erkenntnisse für eigene Testzwecke zu nutzen. * **Vertraulichkeit Dritter:** Die Existenz und Teilnahme anderer spezifischer Unternehmen oder Organisationen an der Sandbox und den zugehörigen Kommunikationskanälen gilt als vertrauliche Information. Das Erstellen von Teilnehmerlisten oder Screenshots, die Nutzernamen oder Firmenzugehörigkeiten zeigen, und deren Weitergabe an Dritte ist untersagt. 5.3** **Der EUDIW-Partner ist sich bewusst, dass der Support-Channel für einen weiteren Teilnehmerkreis zugänglich ist. Es obliegt der alleinigen Verantwortung des EUDIW-Partners keine eigenen Geschäftsgeheimnisse, sensiblen Daten oder vertraulichen Interna im Channel zu posten. Die Betreiberin übernimmt keine Haftung für die Wahrung der Vertraulichkeit von Informationen, die der EUDIW-Partner proaktiv teilt. ## 6. Datenschutz Die Regelungen und Erklärungen zur Erhebung, Speicherung und Verarbeitung Ihrer personenbezogenen Daten im Rahmen der Nutzung der Sandbox befinden sich in unserem Datenschutzhinweis. Dieser ist abrufbar unter [https://gitlab.opencode.de/bmi/eudi-wallet/eidas2/-/blob/main/00_terms_privacy__participation/250513_Hinweise_Datenschutz.pdf?ref_type=heads](https://gitlab.opencode.de/bmi/eudi-wallet/eidas2/-/blob/main/00_terms_privacy__participation/250513_Hinweise_Datenschutz.pdf?ref_type=heads). ## 7. Haftung 7.1 Die Betreiberin haftet bei Verletzung von Leben, Körper und Gesundheit für Schäden, die auf Vorsatz, grober Fahrlässigkeit oder der Verletzung einer Garantie beruhen, uneingeschränkt nach den gesetzlichen Bestimmungen. 7.2 Die Betreiberin übernimmt keine Gewähr für Richtigkeit und Vollständigkeit der im Rahmen der Sandbox befindlichen Informationen. Verweise auf Links zu Webseiten Dritter bedeuten nicht, dass sich SPRIND die hinter dem Link liegenden Inhalte zu eigen macht. 7.3 Weiterhin haftet die Betreiberin nach den gesetzlichen Regelungen für vorsätzlich oder grob fahrlässig verursachte Schäden und/oder für Schäden, die auf einer schuldhaften Verletzung wesentlicher Vertragspflichten beruhen, das heißt solcher vertraglicher Pflichten, deren Erfüllung die ordnungsgemäße Durchführung des Vertrages überhaupt erst ermöglicht und auf deren Einhaltung der Vertragspartner regelmäßig vertrauen darf und deren Verletzung die Erreichung des Vertragszwecks gefährdet. 7.4 Im Übrigen ist die Haftung ausgeschlossen. Dies gilt insbesondere für den Verlust von Testdaten, Konfigurationen oder entgangenem Gewinn. ## 8. Schadloshaltung Der EUDIW-Partner stellt die Betreiberin von sämtlichen Ansprüchen Dritter (einschließlich angemessener Kosten der Rechtsverteidigung) frei, die durch die schuldhafte Verletzung dieser Nutzungsbedingungen entstehen. \ Dies gilt insbesondere bei: 1. Verstoß gegen die “No Real Data Policy” und daraus resultierende datenschutzrechtliche Forderungen. 2. Missbräuchliche Nutzung der API und daraus resultierende Schäden oder den Systemen Dritter. 3. Verletzung von Urheber- oder anderen Schutzrechten oder anderen Rechtsgütern Dritter durch Inhalte, die in für den Austausch eingerichteten Kommunikationskanälen oder anderen Wegen geteilt werden. ## 9. Beendigung und Sperrung des Zugangs 9.1 Das Nutzungsverhältnis zwischen dem EUDIW-Partner als Nutzerin und der Betreiberin bezüglich der Sandbox endet: * mit vollständiger Einstellung des Angebots der Sandbox. * mit Kündigung durch die Betreiberin oder durch den EUDIW-Partner aus wichtigem Grund nach den gesetzlichen oder vertraglichen Bestimmungen. * aufgrund einer außerordentlichen Kündigung durch die Betreiberin wegen Verstoßes gegen die Nutzungsbedingungen oder aus wichtigem Grund nach den gesetzlichen Bestimmungen. * durch Löschung der Nutzer-Zugänge durch den EUDIW-Partner. 9.2 Die Betreiberin ist berechtigt, den Zugriff des EUDIW-Partners auf die Sandbox-Dienste (einschließlich API, EUDI-Wallet App und Kommunikationskanäle) nach eigenem Ermessen und ohne vorherige Ankündigung sofort zu sperren oder den Vertrag außerordentlich zu kündigen, wenn die Betreiberin feststellt oder den begründeten Verdacht hat, dass der EUDIW-Partner: 1. gegen diese Nutzungsbedingungen verstoßen hat, insbesondere durch die Verwendung echter personenbezogener Daten ("No Real Data Policy"); 2. Sicherheitsrichtlinien verletzt hat (z. B. Weitergabe von API-Keys, unautorisierte Lasttests); oder 3. andere Richtlinien verletzt hat, die mit der Nutzung der Sandbox in Verbindung stehen (z. B. der Use Policy für die Kommunikation der EUDIW-Partner untereinander). 9.3** **Die Betreiberin ist ferner berechtigt, den Zugriff auf die Sandbox oder Teile davon jederzeit ohne vorherige Ankündigung zu beenden oder einzuschränken, insbesondere aus folgenden Gründen: 1. Auf Anordnung von Vollzugsbehörden, Aufsichtsbehörden oder anderen staatlichen Stellen (z. B. BSI, EU-Kommission); 2. Auf Wunsch des EUDIW-Partners (z. B. Account-Löschung); 3. Aufgrund der Einstellung, Änderung oder des "Resets" der Sandbox-Umgebung (z. B. Ende der Beta-Phase, Löschung aller Testdaten zu Wartungszwecken); 4. Bei unerwarteten technischen Schwierigkeiten oder Sicherheitsvorfällen, die den Weiterbetrieb gefährden; oder 5. Bei längerer Inaktivität des Accounts des EUDIW-Partners. ## 10. Schlussbestimmungen 10.1 Es gilt deutsches Recht. 10.2 Gerichtsstand für alle Streitigkeiten aus oder in Verbindung mit dieser Plattform zwischen der Betreiberin und dem EUDIW-Partner ist Leipzig, wenn sie Kaufmann, eine juristische Person des öffentlichen Rechts oder ein öffentlich-rechtliches Sondervermögen ist. 10.3 Sollte eine Bestimmung dieses Vertrags unwirksam oder undurchführbar sein, bleibt die Wirksamkeit der übrigen Bestimmungen unberührt. *Letzte Aktualisierung am 22. Januar 2026* --- ## Use Policy Kommunikationskanäle: SPRIND EUDI Wallet Sandbox Diese Use Policy ergänzt die **SPRIND EUDI Wallet Sandbox Nutzungsbedingungen** und gilt für alle Teilnehmer in den im Zusammenhang mit der Sandbox stehenden Kommunikationskanälen. Das Ziel ist die Förderung einer professionellen, konstruktiven und respektvollen Testumgebung. ### **Grundsätze für eine konstruktive Zusammenarbeit** * **Respektvoller Umgang:** Behandeln Sie alle Teilnehmer mit Würde und Respekt. Wir dulden keine Diskriminierung, Belästigung, persönliche Angriffe oder beleidigende Sprache. * **Fokus und Professionalität:** Halten Sie Ihre Kommunikation klar, sachlich und auf die technischen und fachlichen Themen der EUDI Wallet Sandbox beschränkt. Vermeiden Sie störendes Verhalten und respektieren Sie die Zeit anderer. * **Offenheit und Feedback:** Hören Sie aktiv zu und seien Sie offen für unterschiedliche Ansichten und technische Vorschläge. Geben Sie Feedback konstruktiv und freundlich. * **Einhaltung der Nutzungsbedingungen:** Die Regeln aus den **SPRIND EUDI Wallet Sandbox Nutzungsbedingungen** gelten uneingeschränkt, insbesondere in Bezug auf die Vertraulichkeit und das Verbot der Nutzung echter personenbezogener Daten. ### **Was zu vermeiden ist** * **Keine persönlichen Angriffe:** Konzentrieren Sie sich auf Ideen und Vorschläge, nicht auf Personen. * **Keine vertraulichen Interna/Geschäftsgeheimnisse:** Gemäß den Nutzungsbedingungen (Abschnitt 5.3 der Nutzungsbedingungen) übernehmen wir keine Haftung für die Vertraulichkeit von Informationen, die Sie proaktiv im Channel teilen. Posten Sie keine eigenen sensiblen Daten oder Geschäftsgeheimnisse. * **Keine unautorisierten Tests:** Diskussionen oder Anleitungen zu Penetration Testing oder anderen Handlungen, die die Sandbox-Umgebung ungewöhnlich belasten, sind strengstens untersagt (Abschnitt 3.3 der Nutzungsbedingungen). ### **Verstöße und Konsequenzen** Bei einem Verstoß gegen diese Use Policy oder die Nutzungsbedingungen behalten wir uns vor, den Verursacher aus dem Channel zu entfernen und den Zugang zur gesamten Sandbox (API, App etc.) gemäß Abschnitt 9 der Nutzungsbedingungen fristlos zu sperren oder zu kündigen. Bei vermuteten Verstößen gegen diese Use Policy oder bei ungerechtfertigter Verwarnung/Entfernung wenden sich die Teilnehmer bitte an die Sandbox-Betreiberin. *Letzte Aktualisierung am 22. Januar 2026* --- ## Joining the Ecosystem :::tip[One Onboarding Process for All Roles] Whether you plan to verify credentials (Relying Party), issue credentials (EAA Provider), or do both, you complete the same onboarding process once. During the Plan stage, simply declare all the roles you intend to fulfill in your use case description. ::: ## Prerequisites To participate in the German EUDI Wallet Ecosystem Sandbox, your organization should meet the following prerequisites: One of the following must be true: - You are based in Germany and operate a digital service or platform requiring user verification, credential issuance, or related identity services. - You are a service provider actively serving or expecting to serve organizations registered in Germany. - You are an international party desiring to test interoperability in the German Sandbox, recognizing that a German legal entity is required to enter production. All of the following must be true: - You can integrate web-based APIs and manage secure HTTPS endpoints. - You are able to designate a technical contact for integration activities. - You are able to designate an operational contact for compliance communication. - You are authorized to process user identity or attribute data under applicable law. :::warning[Sandbox Environment] The ecosystem is under constant development. Solutions developed in the sandbox today may require modifications before entering the production environment when it becomes available. ::: --- ## Onboarding Stages The onboarding process involves you getting a formal introduction and access to support resources, culminating in a trusted status in the EUDI Wallet Ecosystem Sandbox. We define the process in three main stages: | Stage | Goal | Output | |-------|------|--------| | Plan | Define your use case and requirements & organizational alignment | Internal alignment & resource allocation | | Integrate | Build out technical components | Verified sandbox integration | | Operate | Use case expansion, lifecycle management| Sandbox operations | The first stage is meant for prospective participants to research the support materials, get organized internally, index the needs they have, and reserve the necessary resources for participation. When the planning stage has been completed, you can reach out to us and start engaging with the technical part of the onboarding. Kick-off calls happen on a monthly schedule, and upcoming dates are posted in the Ecosystem Knowledge Center. The kick-off call is the entry point for access to the Support Resources associated with the sandbox. This process allows us to scale the sandbox in a controlled way, as we roll out our support features. Additionally, there will be regularly recurring sessions for technical support and Q&A. Once the technical integration has been achieved, you can move on to operating in the sandbox. --- ### Plan #### 1. Define Your Use Case Document your intended participation mode (credential issuance, verification, or both), why it is required, and which level of assurance is needed. #### 2. Research the Ecosystem - Review the [Architecture Documentation](https://bmi.usercontent.opencode.de/eudi-wallet/wallet-development-documentation-public/latest/) - Understand the [Blueprint for the EUDI Wallet Ecosystem](https://bmi.usercontent.opencode.de/eudi-wallet/eidas-2.0-architekturkonzept/) #### 3. Assess Organizational Readiness - Evaluate benefits for your users and organization - Consider privacy and security implications - Estimate technical effort and resources required - Identify team members and their roles #### 4. Organize Internally As you have decided the initial scope of your work, we ask you to align your team and resources before seeking contact with us. #### 5. Declare Your Intent to Join Please declare your intent to join the sandbox and share the working title of your use case via the [intent form](https://2eut7s.share-eu1.hsforms.com/2QlEzCm_NR6SbkHuq9vbFWw). Per use case, one intent form should be submitted. Once you have submitted the form, our team will reach out to you. --- ### Integrate #### 1. Attend a Kick-Off Call and Gain Access After attending the kick-off call, you will be offered access to the EUDI Wallet Closed Beta. Additionally, you will gain access to the [German EUDI Ecosystem Sandbox Registrar](https://sandbox.eudi-wallet.org/). Access to this environment allows you to configure and issue the necessary certificates for your participation. For details on next steps after attending the kick-off call, see [Onboarding](./onboarding.md). #### 2. Complete Technical Integration Depending on your intended role(s), complete the relevant technical integration(s): - **If you are an EAA Provider** (issuing credentials): Follow the [EAA Provider roadmap](../../eaa/onboarding/overview.md#your-roadmap-as-an-eaa-provider) - **If you are a Relying Party** (verifying credentials): Follow the [Relying Party integration steps](../../rp/onboarding/rp_highlevel_onboarding.md#relying-party-integration-steps) - **If you are both**: Complete both integration processes. You only onboard to the ecosystem once, but you'll need to set up both technical components (issuer and verifier). --- ### Operate The Sandbox is a place for you to iterate and collaborate. #### 1. Test in Sandbox Validate your integration flows using the provided Wallet and credentials. #### 2. Maintain Your Service Manage certificate lifecycle and operational logs. #### 3. Prepare for Production Fine-tune logging, preparation for scaling, and compliance processes. #### 4. Collaborate on New Use Cases Explore opportunities to expand your participation or collaborate with other ecosystem participants. > **Note:** Use this stage to document lessons learned and operational improvements. --- ## Compliance Responsibilities All participants must adhere to a minimum set of operational responsibilities: | Requirement | Responsibility | |-------------|----------------| | Secure Operation | HTTPS endpoints, secure key and credential management | | Purpose Limitation | Process only data necessary for the declared service | | Logging & Auditing | Maintain minimal logs, pseudonymize identifiers | | Certificate Management | Renew and revoke certificates as required | | Trust Registry Updates | Maintain status in the appropriate registry | | Policy Adherence | Agree to terms and conditions | --- ## Support Resources - **Mattermost Community:** Join our technical discussion channels - **Documentation:** Access comprehensive technical guides - **Regular Q&A Sessions:** Participate in scheduled support calls - **Sandbox Registrar:** Manage your certificates and configuration --- ## Next Steps After the Kick-off Call Welcome to Germany's EUDI Wallet Sandbox. After your kick-off call, complete the following steps to get access to all resources. ## What You Need to Send Send **two email addresses** to [partner@eudi.sprind.org](mailto:partner@eudi.sprind.org), a designated Operational contact and a Technical contact. These will be used to grant access to the Sandbox Registrar and the Mattermost community. For wallet access, identify which email(s) should be given access to iOS and/or Android wallet apps. These emails can be the same as your Opertational and Technical contacts, or you may list others. --- ## Sandbox Wallet App Access :::note The email addresses you provide must be linked to an Apple or Google account. ::: ### iOS (TestFlight) You will receive an invitation email from TestFlight. Follow the link to install the sandbox wallet app. ### Android No invitation email is sent for Android. Once your email has been added to the testers list, you can then download the app directly: - [Download on Google Play](https://play.google.com/store/apps/details?id=org.sprind.wallet.sandbox) If it has been more than one week since you provided the email addresses for access and you are still unable to see and download the app in the Google Play Store, post a support request in Mattermost. --- ## Sandbox Registrar Access Access to the Sandbox Registrar will be set up using the email addresses you provided. You will receive an email asking you to configure your password. After you have done so, you can access the Registar here: [https://sandbox.eudi-wallet.org](https://sandbox.eudi-wallet.org) :::note These invites come from a separate system, not the `@eudi.sprind.org` domain, so please be sure to check all Spam / Junk folders. ::: If it has been more than one week since you provided the email addresses for access and you have not yet received access, post a support request in Mattermost. --- ## Mattermost Community Access The provided email addresses will be added as users in our Mattermost instance. You will receive an invitation to join the Mattermost community where you can ask questions and connect with other relying parties and the EUDI team. --- ## Relying Party Readiness Checklist ## 1. Introduction This checklist helps you determine how prepared your organisation is to onboard into the German EUDI Wallet ecosystem. It supports **two types of RPs**: * RPs already building with a clear use case and technical resources. * RPs still exploring their use case and needing consultation support. --- ## 1. Organisational Readiness ### 1.1. Internal Alignment * [ ] Your organisation understands the purpose of participating in the EUDI Wallet ecosystem. * [ ] A high-level use case or service concept has been defined (even if still exploratory). * [ ] A decision-maker or project sponsor is identified. * [ ] A project lead or primary contact person for the onboarding process is appointed. --- ## 2. Use Case Maturity ### 2.1. For RPs Already Building * [ ] A concrete use case has been defined with clear user flows, using the PID. * [ ] The credential types required for the use case are known. * [ ] The expected interactions with the Wallet are identified. * [ ] You have initial technical resources allocated (internal dev team or vendor). * [ ] You have reviewed the current ARF-aligned standards relevant to your implementation. ### 2.2. For RPs Still Exploring * [ ] You can describe your domain (public service, private service, regulated sector, etc.). * [ ] You can identify the problem or process you believe the EUDI Wallet may improve. * [ ] You have collected internal requirements or constraints (legal, operational, UX). * [ ] You are prepared to discuss your service concept. ### 2.3. Participation Preparedness * [ ] You have registered and submitted your use case using the Use Case Form. * [ ] You have identified and discussed possible unusual and/or specific requirements with the orchestrator. --- ## 3. Technical Readiness ### 3.1. Minimal Technical Readiness (for all RPs) * [ ] A technical point of contact is identified. * [ ] You understand that your system will need to integrate with standards such as **OpenID4VP**, **SD-JWT**, and **ISO mDoc**. ### 3.2. Advanced Technical Readiness (for building RPs) * [ ] You can already test or prototype VP requests. * [ ] You have reviewed trust and security requirements (e.g., certificate usage, authentication, cryptographic bindings). --- ## 4. Compliance & Trust Framework Readiness * [ ] You understand that receiving the **Access Certificate** and **Registration Certificate** requires acceptance of the Terms and Conditions. * [ ] You have or can identify the legal/compliance team who can support you when moving from the Sandbox into the production environment. * [ ] You understand that GDPR will apply to your solution. --- ## 5. Collaboration & Support Readiness * [ ] You have joined or intend to join the RP communication channel. * [ ] You understand that recurring Q&A / tech support sessions are open for ongoing help. * [ ] You understand that the orchestrator is not be able to commit to 1:1 consulting sessions and are prepared to make use of open and shared resources. --- ## 6. Next Steps After Completing This Checklist * [Submit your intent to join](https://2eut7s.share-eu1.hsforms.com/2QlEzCm_NR6SbkHuq9vbFWw): * Prepare initial questions or topics you want support with. * Bring any existing diagrams, user flows, or technical documents (optional but helpful). * Join the communication and support channels after the kick-off call upon invitation. * Join the EUDI National Wallet Closed Beta by invitation --- ## Erica: Local Verifier Testing Tool Erica is a public, lightweight, locally-runnable tool designed to help Relying Parties (RPs) accelerate their verifier development and testing. Rather than waiting for integration with the full EUDI Wallet ecosystem, you can use Erica to simulate wallet behavior and test your presentation request handling in isolation. ## Key Features - **HAIP Validation**: Validates presentation requests against High Assurance Interoperability Profile (HAIP) rules to ensure compliance with EUDI ecosystem requirements - **Wallet Simulation Modes**: Test different wallet behaviors and edge cases, including: - Incorrect or malformed credentials - Special characters in claims - Various error scenarios and credential states - **Local Execution**: Run entirely on your development machine without external dependencies - **Immediate Feedback**: Test your OpenID4VP verifier implementation immediately - **Development Speed**: Explore edge cases and validate your implementation quickly before moving to sandbox integration ## Getting Started For installation instructions, usage examples, and detailed documentation, visit the [Erica repository](https://gitlab.opencode.de/bmi/eudi-wallet/erica). --- ## Sandbox Support Resources Overview ## Introduction The sandbox is designed to help Relying Parties explore, prepare, and implement their services in alignment with the national EUDI Wallet ecosystem. To ensure fairness, transparency, and efficient use of public resources, the sandbox follows a **self-service–first support model**. Most early discovery work can be completed independently using the resources outlined below. Our team provides structured guidance, documentation, and shared support formats. While we are available to answer questions, we cannot offer individual consulting sessions to develop or refine use cases. RPs are encouraged to make use of the self-service materials and the established communication channels before requesting additional guidance. --- ## Support Resources and Expected RP Use The following resources are arranged in the recommended order of use, particularly for RPs who are still exploring their service concept. --- ## 1. Ecosystem Knowledge Center — **Start Here** **Purpose:** The primary and authoritative source for understanding the sandbox, technical standards, and integration expectations. **Recommended use:** * To gain an overview of the EUDI Wallet ecosystem, its vision, and its standards. * To evaluate whether your intended service fits the EUDI Wallet model. * To review sample flows, architecture references, terminology, and compliance requirements. * To get detailed information on what is needed for technical integration **Link:** https://bmi.usercontent.opencode.de/eudi-wallet/eidas2/en/ecosystem_knowledge_centre/ --- ## 2. Use Case Exploration Materials & Self-Service **Purpose:** Helps organisations shape their early ideas into clear, structured use cases before entering formal onboarding. **Includes:** * [Ecosystem BluePrint](https://bmi.usercontent.opencode.de/eudi-wallet/eidas-2.0-architekturkonzept/) * [German PID Presentation Requirements](https://bmi.usercontent.opencode.de/eudi-wallet/wallet-development-documentation-public/latest/architecture-concept/flows/22-pid-presentation/) * [Examples of Use Cases](https://bmi.usercontent.opencode.de/eudi-wallet/developer-guide/Use_Case_Examples/) * [RP Readiness Checklist](https://bmi.usercontent.opencode.de/eudi-wallet/developer-guide/Sandbox_Readiness_Checklist/) * [PID Implementation Guide](https://bmi.usercontent.opencode.de/eudi-wallet/developer-guide/PID_Presentation/) * A curated FAQ covering common early-stage questions (Coming soon) **Recommended use:** * For initial planning and internal alignment. * To understand what makes a good RP use case. * To understand what is needed on a technical level. --- ## 3. Erica — Local Verifier Testing Tool **Purpose:** Accelerate verifier development and testing by simulating wallet behavior locally before sandbox integration. **Includes:** * HAIP validation of presentation requests * Wallet simulation modes for testing edge cases (incorrect credentials, special characters, etc.) * Local execution with fake test keys * Immediate feedback without sandbox dependencies **Recommended use:** * During active development to iterate quickly on your verifier logic. * To test edge cases and error handling before moving to the sandbox. * To validate HAIP compliance before formal testing. **Link:** [Erica - Debug Tool](./erica.md) or [Repository](https://gitlab.opencode.de/bmi/eudi-wallet/erica) --- ## 4. RP Communication Channels (Chat) **Purpose:** A collaborative space for short clarifications, practical pointers, and peer exchange. **Appropriate use:** * Quick questions to unblock progress. * Requests for direction toward existing documentation. * Learning how other RPs approach similar challenges. **Not intended for:** * Detailed design discussions. * Step-by-step consulting. --- ## 5. Written Asynchronous Consultation **Purpose:** Enables RPs to submit structured questions or early concepts for review without requiring meeting time. **Recommended use:** * When you have drafted a use case or initial technical flow and you are unsure about its suitability. * When you seek confirmation that your approach aligns with ARF requirements. **Guidance:** Submissions should be clear and concise. Written summaries ensure faster and more precise responses. --- ## 6. Monthly Kick-Off Call — **Entry Point to the Sandbox** **Purpose:** The formal starting point for active participation in the sandbox. During this session, RPs receive: * An introduction to the sandbox structure and expectations. * Access to the EUDI Wallet Closed Beta * Access to the Sandbox Registrar **Recommended use:** * Once your organisation has a clear service concept. * When you are ready to begin technical integration. * OR when you are ready to test your implementation --- ## 7. Q&A / Technical Information Sessions **Purpose:** A recurring group format for implementation questions and clarifications. **Recommended use:** * After reviewing available documentation. * When encountering technical challenges during preparation. * When questions may benefit multiple RPs. This offers a platform for questions that are broader than individual implementation difficulties. --- ## 8. Sandbox Test Environment (Post Kick-Off) **Purpose:** An environment for RPs to test presentation and wallet interactions. **Includes:** * Access to the German EUDI National Wallet Closed Beta * Access to the Mock RP Registrar * Debugging support. * Test suites (Coming later) **Recommended use:** * When initial design work is complete. * When the RP is ready to validate its implementation. --- ## Use Case Example Library Illustrative scenarios for participants in the EUDI Wallet ecosystem. --- ## Relying Parties | # | Use case | RP type | Context | User goal | Credential | Value | Extensions | | - | ---------------------------------------------------- | ------- | ---------------------------------------------------- | --------------------------------------------------------- | ------------------------------------------------------------- | -------------------------------------------------------------------- | --------------------------------------------------------- | | 1 | Account opening at a digital bank | Private | Citizen opens an account with a fully digital bank | Open an account without branch visits or document uploads | PID with selective disclosure of name, date of birth, address | Faster onboarding, higher assurance, reduced fraud, clear consent | Age verification; address updates | | 2 | Secure login to an online government portal | Public | Resident accesses eGovernment services | Log in securely to access personal services | PID for authentication and selective disclosure | Passwordless login, strong assurance, reduced account fragmentation | Step-up authentication; cross-border access | | 3 | Age-restricted online service access | Private | User accesses an age-restricted digital service | Prove eligibility without revealing full identity | PID with age-over-threshold assertion | Privacy-preserving compliance; no document storage | Location-based eligibility; one-time access tokens | | 4 | University enrollment verification | Public | Graduate applies for further studies | Prove enrollment and degree status instantly | EAA issued by a university | Faster decisions; no paper transcripts | Cross-border recognition; automated checks | | 5 | Professional qualification for regulated professions | Public | Licensed professional applies for regulated role | Prove qualification and licensing status | EAA issued by a professional body | Reduced credential fraud; easier compliance | Revocation checks; time-bound authorisations | | 6 | Corporate role verification for B2B services | Private | Employee acts on behalf of an organisation | Prove authorisation to act for the company | EAA asserting role and mandate | Clear accountability; fine-grained access control | Transaction limits; temporary delegation | | 7 | Event or conference access control | Private | Participant attends a professional or academic event | Access restricted sessions or benefits | EAA confirming membership or affiliation | Faster access; reduced manual checks | Hybrid events; time-limited access rights | | 8 | Automated eligibility check for public funding | Public | Startup applies for public funding | Prove eligibility efficiently | EAAs such as business registration or certifications | Faster processing; reduced paperwork | Ongoing compliance monitoring; cross-agency reuse | | 9 | One-per-person disaster relief | Public | Authority distributes emergency aid | Claim a benefit once, quickly and fairly | EAA or PID with scoped, time-limited eligibility attributes | Prevents duplicate claims; rapid aid distribution; data minimisation | Time-limited tokens; household EAAs; offline verification | --- ## Wallet Use Instructions Developers testing with the National EUDI Wallet in the Sandbox should be aware of the following details, peculiarities and known bugs. --- ## 1. Wallet Set-Up and Operation ### 1.1 Distribution and Installation The Closed Beta wallet is distributed via TestFlight, and later via the Google Beta Store as well. :::info[Testflight Configuration] TestFlight should be configured to automatically install updates. ::: During the Closed Beta phase, updates will be frequent, and not announced in advance. - Always verify the wallet version when testing or reporting issues. - Re-test issues after installing the latest update. --- ## 2. Wallet Functionality and Quirks This section documents current wallet behavior that may be surprising, non-obvious, or different from production expectations, but is considered expected behavior for the closed beta. These items are not bugs, but known characteristics or limitations of the wallet at this stage. ### 2.1 Simulated eID Card PID Issuance For the benefit of testing, the wallet offers PID issuance with a simulated eID, that doesn't require a physical testing card. Note that with a simulated eID PID issuance, any PIN you enter will be accepted. To issue your simulated eID: 1. Open the wallet app. 2. Toggle the **Use simulated eID card** option. 3. Click **Get Started**. 4. When prompted, select **Yes, ID card is available**. 5. When prompted, select **Yes, card PIN is set and known**. 6. The wallet will display the ID card data that will be shared. To continue, select **Agree by entering your card PIN**. 7. When prompted, enter any 6 digit PIN. - **Android Only**: When prompted to scan an ID, tap the screen to proceed. On iOS you proceed automatically. 8. Finally, set your Wallet PIN when prompted. This PIN will be required for any presentation request when testing. ### 2.2 Credential Usage Limits In the National EUDI Wallet, PIDs are single-use credentials to combat linkability. The current batch limit is 10 presentations per issuance event. Once the limit is reached, further presentations will fail. The credential must be reissued before it can be used again. This behavior is expected and should be accounted for in test scenarios. ### 2.3 Log Exports To export the Wallet Logs for debugging purposes, enter the hamburger menu in the Wallet App. Press "Download Logs" to download a text file. ### 2.3 SD-JWT vc_values The `vct_values` for a PID Presentation Request in SD-JWT format uses the following value for the German PID Provider: `urn:eudi:pid:de:1`. In the long run, relying parties across Europe can query for a PID from any member state using the `vct_value` `urn:eudi:pid:1`. ## 3. Known Bugs and Temporary Workarounds The following section lists known bugs. Be aware that the list here could be mildly out of date and some of these bugs may have already been resolved. ## iOS ### Nested Claims Not Returned The iOS app currently does not return nested claims values in credential responses. Relying Parties should account for this during testing and avoid relying on nested claim structures until this is resolved. ### Multiple VCT Values The wallet returns an error when requesting multiple `vct_values` in a specific order: ``` "vct_values" : [ "urn:eudi:pid:1", "urn:eudi:pid:de:1" ] ``` ## Android ### Multiple Credential Instances Returned In response to a presentation request, the Android wallet currently returns two instances of the credential. ### EAA Credentials Details Incorrecly Displayed Varied issues with EAA credentials in the wallet: * Background color – EAA displays with dark grey background instead of the color from metadata. * Credential title – Detail view shows "Digitaler Ausweis" instead of the correct EAA title. * Claims not displayed – No EAA claims are shown under "Persönliche Daten", unrelated PID claims appear instead. * Deletion without confirmation – Deleting an EAA removes all PID and EAA credentials without a confirmation prompt. ### Nationalities Attribute Not Disclosed The wallet does not currently disclose the Nationalities attribute when requested. **Status**: The cause has been identified and a fix is in progress. ### Simulated eID Card Does Not Advance Automatically When using a simulated eID card (enabled via the toggle) instead of a physical card, the flow may not advance automatically on the card presentation screen. **Workaround:** Tap the animated illustration displayed on the screen to proceed to the next step. ### "Personal data" Entry Missing in Issued PID View On some devices, opening an issued PID may not show the “Personal data” entry. **Details:** This is a UI rendering issue where the entry is partially or fully obscured by the header, making it invisible. The underlying data is unaffected. **Status:** A fix is planned for an upcoming release. ### Issuance Flow May Stall After Wallet PIN Setup During credential issuance, the application may become stuck on the “Wallet PIN Set – Success” screen without further user feedback. **Workaround:** Restart the application and repeat the issuance flow. ### Deleting a PID May Prevent Re-Issuance In some cases, deleting a PID does not fully reset the internal state, which can block subsequent re-issuance attempts. **Workaround:** - Clear the application's cache and storage - Restart the application - Start the issuance flow again ### General Recovery for Unexpected Behavior If you encounter any other unexpected or blocking behavior during testing: **Recommended recovery step:** Clear the application cache and storage, restart the app, and repeat the affected flow. --- ## Banking and Finance :::info[Living Document] Sector-specific guidance is still evolving alongside the ecosystem. This page collects the most relevant starting points today; dedicated Banking and Finance guides will be added over time. ::: Banks and financial service providers typically act as Relying Parties, verifying a customer's identity or attributes to meet KYC (Know Your Customer) and other regulatory requirements without collecting or storing more data than necessary. ## Representative use cases - **Account opening at a digital bank** — customers open an account with a PID presenting only the required attributes (name, date of birth, address) via selective disclosure, avoiding branch visits or manual document uploads. - **Age-restricted financial products** — proving eligibility (e.g. age-over-threshold) for a product without revealing full identity details. - **Corporate role verification for B2B services** — an employee proves they are authorised to act on behalf of their organisation using an EAA asserting role and mandate, useful for corporate banking and B2B financial transactions. See the full [Use Case Example Library](/sandbox/resources/use_case_examples) for details on credentials, value, and extension scenarios. ## Where to go next - New to the ecosystem? Start with [Joining the Ecosystem](/sandbox/onboarding/joining). - Verifying customer credentials for onboarding, KYC, or age checks? See the [Relying Party Technical Integration guide](/rp/onboarding/rp_highlevel_onboarding). - Issuing attestations (e.g. mandates, authorisations)? See the [EAA Provider Onboarding guide](/eaa/onboarding/overview). --- ## Education :::info[Living Document] Sector-specific guidance is still evolving alongside the ecosystem. This page collects the most relevant starting points today; dedicated Education guides will be added over time. ::: Educational institutions can act as both EAA Providers — issuing verifiable credentials such as degrees and enrollment confirmations — and Relying Parties, verifying a student's identity or existing credentials during enrollment or access processes. ## Representative use cases - **University enrollment verification** — graduates prove enrollment and degree status instantly using an EAA issued by a university, replacing paper transcripts and speeding up admission decisions for further studies. - **Event or conference access control** — students or academics use an EAA confirming membership or affiliation to access restricted sessions or benefits at academic events. - **Professional qualification for regulated professions** — professional bodies issue EAAs asserting qualification and licensing status for graduates entering regulated professions. See the full [Use Case Example Library](/sandbox/resources/use_case_examples) for details on credentials, value, and extension scenarios. ## Where to go next - New to the ecosystem? Start with [Joining the Ecosystem](/sandbox/onboarding/joining). - Verifying student or applicant credentials? See the [Relying Party Technical Integration guide](/rp/onboarding/rp_highlevel_onboarding). - Issuing degrees, transcripts, or enrollment attestations? See the [EAA Provider Onboarding guide](/eaa/onboarding/overview). --- ## Health :::info[Living Document] Sector-specific guidance is still evolving alongside the ecosystem. This page collects the most relevant starting points today; dedicated Health guides will be added over time. ::: Healthcare providers and payers are an emerging area of the ecosystem, with potential use cases spanning both credential verification (e.g. confirming a patient's identity or insurance status) and credential issuance (e.g. prescriptions, vaccination records, professional licenses for medical staff). ## Potential use cases - **Secure patient identification** — verifying a patient's identity via PID when accessing health portals or services, reducing reliance on physical ID documents. - **Insurance and eligibility checks** — confirming coverage or eligibility attributes before a service is rendered. - **Prescription and medical credential issuance** — issuing EAAs for prescriptions or professional qualifications (e.g. medical licenses), similar in structure to the professional qualification use case in the [Use Case Example Library](/sandbox/resources/use_case_examples). This sector does not yet have a dedicated worked example in the use case library — if you are exploring a health-related use case, the general onboarding guidance below is the best starting point. ## Where to go next - New to the ecosystem? Start with [Joining the Ecosystem](/sandbox/onboarding/joining). - Verifying patient or member credentials? See the [Relying Party Technical Integration guide](/rp/onboarding/rp_highlevel_onboarding). - Issuing prescriptions, records, or professional credentials? See the [EAA Provider Onboarding guide](/eaa/onboarding/overview). --- ## Public Administration :::info[Living Document] Sector-specific guidance is still evolving alongside the ecosystem. This page collects the most relevant starting points today; dedicated Public Administration guides will be added over time. ::: Public sector bodies are among the primary Relying Parties and EAA Providers in the German EUDI Wallet Ecosystem — from eGovernment portals verifying a citizen's identity, to agencies issuing attestations such as professional licenses or eligibility confirmations. ## Representative use cases - **Secure login to an online government portal** — residents authenticate to eGovernment services using their PID instead of passwords, with selective disclosure of only the attributes required. - **Automated eligibility checks for public funding** — agencies verify EAAs such as business registrations or certifications to speed up funding decisions. - **One-per-person disaster relief distribution** — authorities use scoped, time-limited eligibility attributes from a PID or EAA to prevent duplicate claims while distributing aid quickly. - **Professional qualification verification** — regulators or licensing bodies issue EAAs asserting a qualification or licensing status for regulated professions. See the full [Use Case Example Library](/sandbox/resources/use_case_examples) for details on credentials, value, and extension scenarios. ## Where to go next - New to the ecosystem? Start with [Joining the Ecosystem](/sandbox/onboarding/joining). - Verifying citizen credentials (e.g. portal login, eligibility checks)? See the [Relying Party Technical Integration guide](/rp/onboarding/rp_highlevel_onboarding). - Issuing attestations (e.g. licenses, permits, certifications)? See the [EAA Provider Onboarding guide](/eaa/onboarding/overview).