---
url: 'https://www.corbado.com/de/blog/verifier-fuer-verifiable-credentials-bauen'
title: 'So erstellst du einen Verifier für digitale Nachweise (Entwickler-Guide)'
description: 'In diesem Entwickler-Guide bauen wir Schritt für Schritt einen Verifier für digitale Nachweise mit Next.js, OpenID4VP und ISO mDoc. Wir zeigen, wie ein Verifier mobile Führerscheine und andere digitale Nachweise anfordern, empfangen und validieren kann.'
lang: 'de'
author: 'Amine'
date: '2025-08-20T15:39:40.613Z'
lastModified: '2026-03-27T07:03:15.730Z'
keywords: 'verifier für digitale nachweise, tutorial verifier, verifier bauen'
category: 'Digital Credentials'
---

# So erstellst du einen Verifier für digitale Nachweise (Entwickler-Guide)

## 1. Einführung

Der Online-Nachweis von Identitäten ist eine ständige Herausforderung. Das führt dazu,
dass wir uns auf Passwörter verlassen und sensible Dokumente über unsichere Kanäle teilen.
Für Unternehmen wird die Identitätsprüfung dadurch zu einem langsamen, teuren und
betrugsanfälligen Prozess. [Digitale Nachweise](https://www.corbado.com/de/blog/digital-credentials-api) (Digital
Credentials) bieten einen neuen Ansatz, der den Nutzern die Kontrolle über ihre Daten
zurückgibt. Sie sind das digitale Äquivalent einer physischen
[Wallet](https://www.corbado.com/blog/digital-wallet-assurance), die von einem Führerschein bis zum
Universitätsabschluss alles enthalten kann – aber mit den zusätzlichen Vorteilen,
kryptografisch sicher, datenschutzfreundlich und sofort überprüfbar zu sein.

Dieser Guide ist ein praktisches Schritt-für-Schritt-Tutorial für Entwickler, um einen
Verifier für [digitale Nachweise](https://www.corbado.com/de/blog/digital-credentials-api) zu bauen. Obwohl die
Standards existieren, gibt es kaum Anleitungen zur Implementierung. Dieses Tutorial füllt
diese Lücke. Wir zeigen, wie man einen Verifier mit der nativen Digital Credential API des
Browsers, [OpenID4VP](https://www.corbado.com/glossary/open-id-4-vp) für das Präsentationsprotokoll und ISO
[mDoc](https://www.corbado.com/glossary/mdoc) (z. B. für den mobilen Führerschein) als Nachweisformat erstellt.

Das Endergebnis wird eine einfache, aber funktionale
[Next.js](https://www.corbado.com/blog/nextjs-passkeys)-Anwendung sein, die einen digitalen Nachweis von einer
kompatiblen mobilen [Wallet](https://www.corbado.com/blog/digital-wallet-assurance) anfordern, empfangen und
verifizieren kann.

Hier ist ein kurzer Blick auf die fertige Anwendung in Aktion. Der Prozess besteht aus
vier Hauptschritten:

**Schritt 1: Startseite** Der User landet auf der Startseite und klickt auf „Mit digitaler
[Identität](https://www.corbado.com/de/blog/digital-credentials-api) verifizieren“, um den Prozess zu starten.
![Initial page for verification request](https://s3.eu-central-1.amazonaws.com/corbado-cloud-staging-website-assets/Screenshot_2025_07_25_at_11_00_33_5217b35c96.png)

**Schritt 2: Vertrauensabfrage** Der Browser fragt den User um sein Einverständnis. Der
User klickt auf „Weiter“, um fortzufahren.
![Browser trust prompt](https://s3.eu-central-1.amazonaws.com/corbado-cloud-staging-website-assets/Screenshot_2025_07_25_at_11_00_39_ba390a8097.png)

**Schritt 3: QR-Code-Scan** Ein QR-Code wird angezeigt, den der User mit seiner
kompatiblen [Wallet](https://www.corbado.com/blog/digital-wallet-assurance)-Anwendung scannt.
![QR code for scanning](https://s3.eu-central-1.amazonaws.com/corbado-cloud-staging-website-assets/Screenshot_2025_07_25_at_11_00_45_3b30669a10.png)

**Schritt 4: Entschlüsselter Nachweis** Nach erfolgreicher Verifizierung zeigt die
Anwendung die entschlüsselten Nachweisdaten an.
![Decoded credential result](https://s3.eu-central-1.amazonaws.com/corbado-cloud-staging-website-assets/Screenshot_2025_07_25_at_11_01_36_684f7489cd.png)

### 1.1 Wie es funktioniert

Die Magie hinter digitalen Nachweisen liegt in einem einfachen, aber leistungsstarken
„**Vertrauensdreieck**“-Modell, das drei Hauptakteure umfasst:

- **Aussteller (Issuer):** Eine vertrauenswürdige Instanz (z. B. eine Regierungsbehörde,
  eine Universität oder eine Bank), die einen Nachweis kryptografisch signiert und an
  einen User ausstellt.
- **Inhaber (Holder):** Der User, der den Nachweis erhält und ihn sicher in einer
  persönlichen digitalen Wallet auf seinem Gerät speichert.
- **Verifier:** Eine Anwendung oder ein Dienst, der den Nachweis des Users überprüfen
  muss.

![W3C Verifiable Credentials Ecosystem](https://www.w3.org/TR/vc-data-model/diagrams/ecosystem.svg)

Wenn ein User auf einen Dienst zugreifen möchte, präsentiert er den Nachweis aus seiner
Wallet. Der Verifier kann dann sofort dessen Echtheit überprüfen, ohne den ursprünglichen
Aussteller direkt kontaktieren zu müssen.

### 1.2 Warum Verifier unerlässlich sind (und warum du hier bist)

Damit dieses **dezentrale Identitäts-Ökosystem** erfolgreich sein kann, ist die Rolle des
**Verifiers** absolut entscheidend. Sie sind die Gatekeeper dieser neuen
Vertrauensinfrastruktur, diejenigen, die die Nachweise nutzen und sie in der realen Welt
einsetzbar machen. Wie das nachstehende Diagramm zeigt, vervollständigt ein Verifier das
Vertrauensdreieck, indem er einen Nachweis vom Inhaber anfordert, empfängt und validiert.

Wenn du Entwickler bist, ist das Erstellen eines Dienstes zur Durchführung dieser
Verifizierung eine grundlegende Fähigkeit für die nächste Generation sicherer und
nutzerzentrierter Anwendungen. Dieser Guide wurde entwickelt, um dich genau durch diesen
Prozess zu führen. Wir werden alles behandeln, was du wissen musst, um **deinen eigenen
Verifier für verifizierbare Nachweise zu bauen**, von den Kernkonzepten und Standards bis
hin zu den schrittweisen Implementierungsdetails zur Validierung von Signaturen und zur
Überprüfung des Nachweisstatus.

> **Möchtest du direkt loslegen?** Du findest das komplette, fertige Projekt für dieses
> Tutorial auf GitHub. Klone es einfach und probiere es selbst aus:
> [https://github.com/corbado/digital-credentials-example](https://github.com/corbado/digital-credentials-example)

Fangen wir an.

## 2. Voraussetzungen, um einen Verifier zu bauen

Bevor du beginnst, stelle sicher, dass du Folgendes hast:

1. **Grundlegendes Verständnis von Digital Credentials und mdoc**
    - Dieses Tutorial konzentriert sich auf das **ISO mDoc**-Format (z. B. für mobile
      Führerscheine) und behandelt keine anderen Formate wie W3C
      [Verifiable Credentials](https://www.corbado.com/glossary/microcredentials) (VCs). Kenntnisse der
      grundlegenden Konzepte von [mdoc](https://www.corbado.com/glossary/mdoc) sind hilfreich.
2. **Docker und Docker Compose**
    - Unser Projekt verwendet eine
      [MySQL](https://www.corbado.com/blog/passkey-webauthn-database-guide)-Datenbank in einem Docker-Container,
      um den OIDC-Sitzungsstatus zu verwalten. Stelle sicher, dass beides installiert ist
      und läuft.
3. **Gewähltes Protokoll: OpenID4VP**
    - Wir werden das **OpenID4VP**-Protokoll (OpenID for Verifiable Presentations) für den
      Austausch von Nachweisen verwenden.
4. **Tech-Stack bereit**
    - Verwende **TypeScript** (Node.js) für die Backend-Logik.
    - Verwende **Next.js** sowohl für das Backend (API-Routen) als auch für das Frontend
      (UI).
    - Wichtige Bibliotheken: [CBOR](https://www.corbado.com/glossary/cbor)-Dekodierungsbibliotheken für das
      Parsen von [mdoc](https://www.corbado.com/glossary/mdoc) und ein
      [MySQL](https://www.corbado.com/blog/passkey-webauthn-database-guide)-Client.
5. **Test-Credentials und Wallet**
    - Wir werden die
      **[CMWallet](https://github.com/digitalcredentialsdev/CMWallet/actions/runs/16407676816/artifacts/3574255220)**
      für [Android](https://www.corbado.com/blog/how-to-enable-passkeys-android) verwenden, die
      [OpenID4VP](https://www.corbado.com/glossary/open-id-4-vp)-Anfragen versteht und mdoc-Nachweise
      präsentieren kann.
6. **Grundkenntnisse in Kryptografie**
    - Verständnis von digitalen Signaturen und Public-/Private-Key-Konzepten im
      Zusammenhang mit mdoc- und OIDC-Flows.

---

Wir werden nun jede dieser Voraussetzungen im Detail durchgehen, beginnend mit den
Standards und Protokollen, die diesem mdoc-basierten Verifier zugrunde liegen.

### 2.1 Protokoll-Entscheidungen

Unser Verifier ist für Folgendes ausgelegt:

| Standard / Protokoll                                    | Beschreibung                                                                                                                                                                                                  |
| :------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **[W3C VC](https://www.w3.org/TR/vc-data-model/)**      | Das W3C Verifiable Credentials Data Model. Es definiert die Standardstruktur für digitale Nachweise, einschließlich Claims, Metadaten und Proofs.                                                             |
| **SD-JWT**                                              | Selective Disclosure for JWTs. Ein Format für VCs, das auf JSON Web Tokens basiert und es Inhabern ermöglicht, nur bestimmte Claims aus einem Nachweis selektiv preiszugeben, was den Datenschutz verbessert. |
| **[ISO mDoc](https://www.iso.org/standard/69084.html)** | ISO/IEC 18013-5. Der internationale Standard für mobile Führerscheine (mDLs) und andere mobile IDs, der Datenstrukturen und Kommunikationsprotokolle für den Offline- und Online-Einsatz definiert.           |
| **OpenID4VP**                                           | OpenID for Verifiable Presentations. Ein interoperables Präsentationsprotokoll, das auf OAuth 2.0 aufbaut. Es definiert, wie ein Verifier Nachweise anfordert und die Wallet eines Inhabers sie präsentiert.  |

Für dieses Tutorial verwenden wir speziell:

- **OpenID4VP** als Protokoll für die Anforderung und den Empfang von Nachweisen.
- **ISO mDoc** als Nachweisformat (z. B. für mobile Führerscheine).

> **Hinweis zum Umfang:** Obwohl wir W3C VC und
> [SD-JWT](https://www.corbado.com/de/glossary/selektive-offenlegung-sd-jwt) kurz vorstellen, um einen breiteren
> Kontext zu schaffen, implementiert dieses Tutorial ausschließlich ISO mDoc-Nachweise
> über [OpenID4VP](https://www.corbado.com/glossary/open-id-4-vp). W3C-basierte VCs sind nicht Teil dieses
> Beispiels.

#### 2.1.1 ISO mDoc (Mobile Document)

Der Standard ISO/IEC 18013-5 mDoc definiert die Struktur und Kodierung für mobile
Dokumente wie mobile Führerscheine (mDLs). mDoc-Nachweise sind
[CBOR](https://www.corbado.com/glossary/cbor)-kodiert, kryptografisch signiert und können zur Verifizierung
digital vorgelegt werden. Unser Verifier wird sich darauf konzentrieren, diese
mdoc-Nachweise zu dekodieren und zu validieren.

#### 2.1.2 OpenID4VP (OpenID for Verifiable Presentations)

OpenID4VP ist ein interoperables Protokoll zur Anforderung und Präsentation von digitalen
Nachweisen, das auf [OAuth 2.0](https://www.corbado.com/glossary/oauth2) und OpenID Connect aufbaut. In dieser
Implementierung wird OpenID4VP verwendet, um:

- den Prozess der Nachweispräsentation zu initiieren (über QR-Code oder Browser-API)
- den mdoc-Nachweis von der Wallet des Users zu erhalten
- einen sicheren, zustandsbehafteten und datenschutzfreundlichen Austausch von Nachweisen
  zu gewährleisten

### 2.2 Tech-Stack-Entscheidungen

Nachdem wir ein klares Verständnis der Standards und Protokolle haben, müssen wir den
richtigen Tech-Stack für den Bau unseres Verifiers auswählen. Unsere Entscheidungen sind
auf Robustheit, Entwicklererfahrung und Kompatibilität mit dem modernen Web-Ökosystem
ausgelegt.

#### 2.2.1 Sprache: TypeScript

Wir werden **TypeScript** sowohl für unseren Frontend- als auch für unseren Backend-Code
verwenden. Als Superset von JavaScript fügt es statische Typisierung hinzu, was hilft,
Fehler frühzeitig zu erkennen, die Codequalität zu verbessern und komplexe Anwendungen
einfacher zu verwalten. In einem sicherheitssensiblen Kontext wie der
Nachweisverifizierung ist Typsicherheit ein enormer Vorteil.

#### 2.2.2 Framework: Next.js

**Next.js** ist unser Framework der Wahl, da es eine nahtlose, integrierte Erfahrung für
die Erstellung von Full-Stack-Anwendungen bietet.

- **Für das Frontend:** Wir werden [Next.js](https://www.corbado.com/blog/nextjs-passkeys) mit
  [React](https://www.corbado.com/blog/react-passkeys) verwenden, um die Benutzeroberfläche zu erstellen, auf der
  der Verifizierungsprozess initiiert wird (z. B. durch Anzeige eines QR-Codes).
- **Für das Backend:** Wir werden **Next.js API Routes** nutzen, um die serverseitigen
  Endpunkte zu erstellen. Diese Endpunkte sind dafür verantwortlich, gültige
  OpenID4VP-Anfragen zu erstellen und als `redirect_uri` zu fungieren, um die endgültige
  Antwort von der CMWallet sicher zu empfangen und zu verifizieren.

#### 2.2.3 Wichtige Bibliotheken

Unsere Implementierung stützt sich auf eine spezifische Reihe von Bibliotheken für
Frontend und Backend:

- **next**: Das [Next.js](https://www.corbado.com/blog/nextjs-passkeys)-Framework, das sowohl für
  Backend-API-Routen als auch für das Frontend-UI verwendet wird.
- **react** und **react-dom**: Sorgen für die Funktionalität der
  Frontend-Benutzeroberfläche.
- **cbor-web**: Wird verwendet, um [CBOR](https://www.corbado.com/glossary/cbor)-kodierte mdoc-Nachweise in
  nutzbare JavaScript-Objekte zu dekodieren.
- **mysql2**: Stellt die
  [MySQL](https://www.corbado.com/blog/passkey-webauthn-database-guide)-Datenbankverbindung zum Speichern von
  Challenges und Verifizierungssitzungen bereit.
- **uuid**: Eine Bibliothek zur Erzeugung eindeutiger Challenge-Strings (Nonces).
- **@types/uuid**: TypeScript-Typen für die UUID-Generierung.

> **Hinweis zu `openid-client`:** Fortgeschrittenere, produktionsreife Verifier könnten
> die Bibliothek `openid-client` verwenden, um das OpenID4VP-Protokoll direkt im Backend
> zu handhaben und Funktionen wie eine dynamische `redirect_uri` zu ermöglichen. In einem
> servergesteuerten OpenID4VP-Flow mit einer `redirect_uri` würde `openid-client`
> verwendet, um `vp_token`-Antworten direkt zu parsen und zu validieren. Für dieses
> Tutorial verwenden wir einen einfacheren, browservermittelten Flow, der dies nicht
> erfordert, was den Prozess verständlicher macht.

Dieser Tech-Stack gewährleistet eine robuste, typsichere und skalierbare
Verifier-Implementierung, die sich auf die Digital Credential API des Browsers und das ISO
mDoc-Nachweisformat konzentriert.

### 2.3 Test-Wallet und -Nachweise besorgen

Um deinen Verifier zu testen, benötigst du eine mobile Wallet, die mit der Digital
Credential API des Browsers interagieren kann.

Wir werden die
**[CMWallet](https://github.com/digitalcredentialsdev/CMWallet/actions/runs/16407676816/artifacts/3574255220)**
verwenden, eine robuste, OpenID4VP-kompatible Test-Wallet für
[Android](https://www.corbado.com/blog/how-to-enable-passkeys-android).

**So installierst du CMWallet (Android):**

1. **Lade die APK-Datei** über den obigen Link direkt auf dein
   [Android](https://www.corbado.com/blog/how-to-enable-passkeys-android)-Gerät herunter.
2. Öffne auf deinem Gerät **Einstellungen > Sicherheit**.
3. Aktiviere **„Unbekannte Apps installieren“** für den Browser, mit dem du die Datei
   heruntergeladen hast.
4. Finde die heruntergeladene APK in deinem „Downloads“-Ordner und tippe darauf, um die
   Installation zu starten.
5. Folge den Anweisungen auf dem Bildschirm, um die Installation abzuschließen.
6. Öffne die CMWallet. Sie ist bereits mit Test-Nachweisen vorinstalliert und bereit für
   den Verifizierungsprozess.

> **Hinweis:** Installiere APK-Dateien nur aus Quellen, denen du vertraust. Der
> bereitgestellte Link stammt aus dem offiziellen Projekt-Repository.

### 2.4 Kryptografie-Kenntnisse

Bevor wir in die Implementierung eintauchen, ist es wichtig, die kryptografischen Konzepte
zu verstehen, die verifizierbaren Nachweisen zugrunde liegen. Das ist es, was sie
„verifizierbar“ und vertrauenswürdig macht.

#### 2.4.1 Digitale Signaturen: Die Grundlage des Vertrauens

Im Kern ist ein [Verifiable Credential](https://www.corbado.com/glossary/verifiable-credential) eine Reihe von
Claims (wie Name, Geburtsdatum usw.), die von einem Aussteller digital signiert wurden.
Eine digitale Signatur bietet zwei entscheidende Garantien:

- **Authentizität:** Sie beweist, dass der Nachweis tatsächlich vom Aussteller und nicht
  von einem Betrüger erstellt wurde.
- **Integrität:** Sie beweist, dass der Nachweis seit seiner Signierung nicht verändert
  oder manipuliert wurde.

#### 2.4.2 Public-Key-Kryptografie

Digitale Signaturen werden mittels Public-Key-Kryptografie (auch asymmetrische
Kryptografie genannt) erstellt. So funktioniert es in unserem Kontext:

1. **Der Aussteller hat ein Schlüsselpaar:** einen privaten Schlüssel, der geheim gehalten
   wird, und einen öffentlichen Schlüssel, der allen zur Verfügung gestellt wird
   (normalerweise über sein DID-Dokument).
2. **Signieren:** Wenn ein Aussteller einen Nachweis erstellt, verwendet er seinen
   **privaten Schlüssel**, um eine eindeutige digitale Signatur für diese spezifischen
   Nachweisdaten zu erzeugen.
3. **Verifizierung:** Wenn unser Verifier den Nachweis erhält, verwendet er den
   **öffentlichen Schlüssel** des Ausstellers, um die Signatur zu überprüfen. Wenn die
   Prüfung erfolgreich ist, weiß der Verifier, dass der Nachweis authentisch ist und nicht
   manipuliert wurde. Jede Änderung der Nachweisdaten würde die Signatur ungültig machen.

> **Hinweis zu DIDs:** In diesem Tutorial lösen wir die Schlüssel der Aussteller nicht
> über DIDs auf. In der Produktion würden Aussteller ihre öffentlichen Schlüssel
> typischerweise über DIDs oder andere autoritative Endpunkte bereitstellen, die der
> Verifier zur kryptografischen Validierung verwenden würde.

#### 2.4.3 Verifizierbare Nachweise als JWTs

Verifizierbare Nachweise werden oft als **JSON Web Tokens (JWTs)** formatiert. Ein JWT ist
eine kompakte, URL-sichere Methode, um Claims darzustellen, die zwischen zwei Parteien
übertragen werden. Ein signiertes JWT (auch als JWS bekannt) besteht aus drei Teilen, die
durch Punkte (`.`) getrennt sind:

- **Header:** Enthält Metadaten über das Token, wie den verwendeten Signaturalgorithmus
  (`alg`).
- **Payload:** Enthält die eigentlichen Claims des
  [Verifiable Credential](https://www.corbado.com/glossary/verifiable-credential) (`vc`-Claim), einschließlich
  des `issuer`, `credentialSubject` usw.
- **Signatur:** Die vom Aussteller erzeugte digitale Signatur, die den Header und die
  Payload abdeckt.

```
// Beispiel für eine JWT-Struktur
[Header].[Payload].[Signatur]
```

> **Hinweis:** JWT-basierte [Verifiable Credentials](https://www.corbado.com/glossary/microcredentials) sind
> nicht Gegenstand dieses Blog-Beitrags. Diese Implementierung konzentriert sich auf ISO
> mDoc-Nachweise und OpenID4VP, nicht auf W3C
> [Verifiable Credentials](https://www.corbado.com/glossary/microcredentials) oder JWT-basierte Nachweise.

#### 2.4.4 Die verifizierbare Präsentation: Der Besitznachweis

Es reicht nicht aus, dass ein Verifier weiß, dass ein Nachweis gültig ist; er muss auch
wissen, dass die Person, die den Nachweis _präsentiert_, der rechtmäßige Inhaber ist. Dies
verhindert, dass jemand einen gestohlenen Nachweis verwendet.

Dies wird durch eine **Verifiable Presentation (VP)** gelöst. Eine VP ist eine Hülle um
einen oder mehrere VCs, die **vom Inhaber selbst signiert** ist.

Der Ablauf ist wie folgt:

1. Der Verifier bittet den User, einen Nachweis zu präsentieren.
2. Die Wallet des Users erstellt eine
   [Verifiable Presentation](https://www.corbado.com/glossary/verifiable-presentation), bündelt die
   erforderlichen Nachweise darin und signiert die gesamte Präsentation mit dem **privaten
   Schlüssel des Inhabers**.
3. Die Wallet sendet diese signierte VP an den Verifier.

Unser Verifier muss dann **zwei** separate Signaturprüfungen durchführen:

1. **Nachweise verifizieren:** Überprüfe die Signatur auf jedem VC innerhalb der
   Präsentation mit dem **öffentlichen Schlüssel des Ausstellers**. (Beweist, dass der
   Nachweis echt ist).
2. **Präsentation verifizieren:** Überprüfe die Signatur auf der VP selbst mit dem
   **öffentlichen Schlüssel des Inhabers**. (Beweist, dass die präsentierende Person der
   Besitzer ist).

Diese zweistufige Prüfung gewährleistet sowohl die Authentizität des Nachweises als auch
die [Identität](https://www.corbado.com/de/blog/digital-credentials-api) der Person, die ihn präsentiert, und
schafft so ein robustes und sicheres Vertrauensmodell.

> **Hinweis:** Das Konzept der Verifiable Presentations, wie es im W3C VC-Ökosystem
> definiert ist, ist nicht Gegenstand dieses Blog-Beitrags. Der Begriff Verifiable
> Presentation bezieht sich hier auf die OpenID4VP `vp_token`-Antwort, die sich ähnlich
> wie eine W3C VP verhält, aber auf ISO mDoc-Semantik anstelle des
> [JSON-LD](https://www.corbado.com/glossary/json-ld)-Signaturmodells von W3C basiert. Dieser Guide konzentriert
> sich auf ISO mDoc-Nachweise und OpenID4VP, nicht auf W3C Verifiable Presentations oder
> deren Signaturvalidierung.

## 3. Architekturübersicht

Unsere Verifier-Architektur nutzt die eingebaute **Digital Credential API** des Browsers
als sicheren Vermittler, um unsere Webanwendung mit der mobilen **CMWallet** des Users zu
verbinden. Dieser Ansatz vereinfacht den Ablauf, da der Browser die native QR-Code-Anzeige
und die Kommunikation mit der Wallet übernimmt.

- **Frontend (Next.js & React):** Eine schlanke, nutzerorientierte Website. Ihre Aufgabe
  ist es, ein Anfrageobjekt von unserem Backend abzurufen, es an die
  `navigator.credentials.get()` API des Browsers zu übergeben, das Ergebnis zu empfangen
  und es zur Verifizierung an unser Backend weiterzuleiten.
- **Backend (Next.js API Routes):** Das Kraftpaket des Verifiers. Es generiert ein
  gültiges Anfrageobjekt für die Browser-API und stellt einen Endpunkt bereit, um die
  Nachweispräsentation vom Frontend zur endgültigen Validierung zu empfangen.
- **Browser (Credential API):** Der Vermittler. Er empfängt das Anfrageobjekt von unserem
  Frontend, versteht das `openid4vp`-Protokoll und generiert nativ einen QR-Code. Dann
  wartet er auf die Antwort der Wallet.
- **CMWallet (Mobile App):** Die Wallet des Users. Sie scannt den QR-Code, verarbeitet die
  Anfrage, holt die Zustimmung des Users ein und sendet die signierte Antwort zurück an
  den Browser.

Hier ist ein Sequenzdiagramm, das den vollständigen und korrekten Ablauf veranschaulicht:

![Verification Flow using the Browser's Digital Credentials API](https://s3.eu-central-1.amazonaws.com/corbado-cloud-staging-website-assets/Mermaid_Chart_Create_complex_visual_diagrams_with_text_A_smarter_way_of_creating_diagrams_2025_07_24_233623_1b6ed9b957.svg)

**Der Ablauf erklärt:**

1. **Initiierung:** Der User klickt auf den „Verifizieren“-Button in unserem **Frontend**.
2. **Anfrageobjekt:** Das Frontend ruft unser **Backend** (`/api/verify/start`) auf, das
   ein Anfrageobjekt mit der Abfrage und einer Nonce generiert und zurückgibt.
3. **Browser-API-Aufruf:** Das Frontend ruft `navigator.credentials.get()` mit dem
   Anfrageobjekt auf.
4. **Nativer QR-Code:** Der **Browser** erkennt die `openid4vp`-Protokollanfrage und zeigt
   nativ einen QR-Code an. Das `.get()`-Promise ist nun ausstehend.

> **Hinweis:** Dieser QR-Code-Flow findet in Desktop-Browsern statt. In mobilen Browsern
> (Android [Chrome](https://www.corbado.com/de/blog/digital-credentials-api) mit aktiviertem experimentellem
> Flag) kann der Browser direkt mit kompatiblen Wallets auf demselben Gerät kommunizieren,
> wodurch das Scannen von QR-Codes entfällt. Um diese Funktion in Android
> [Chrome](https://www.corbado.com/de/blog/digital-credentials-api) zu aktivieren, navigiere zu
> `chrome://flags#web-identity-digital-credentials` und setze das Flag auf „Enabled“.

5. **Scannen & Präsentieren:** Der User scannt den QR-Code mit der **CMWallet**. Die
   Wallet holt die Zustimmung des Users ein und sendet die
   [Verifiable Presentation](https://www.corbado.com/glossary/verifiable-presentation) zurück an den Browser.
6. **Promise-Auflösung:** Der Browser empfängt die Antwort, und das ursprüngliche
   `.get()`-Promise im Frontend wird schließlich aufgelöst und liefert die
   Präsentations-Payload.
7. **Backend-Verifizierung:** Das Frontend sendet die Präsentations-Payload per **POST**
   an den `/api/verify/finish`-Endpunkt unseres Backends. Das Backend validiert die Nonce
   und den Nachweis.
8. **Ergebnis:** Das Backend gibt eine endgültige Erfolgs- oder Fehlermeldung an das
   Frontend zurück, das die Benutzeroberfläche aktualisiert.

## 4. Den Verifier bauen

Nachdem wir nun ein solides Verständnis der Standards, Protokolle und des
Architekturflusses haben, können wir mit dem Bau unseres Verifiers beginnen.

> **Folge mit oder verwende den fertigen Code**
>
> Wir werden nun Schritt für Schritt die Einrichtung und Code-Implementierung durchgehen.
> Wenn du lieber direkt zum fertigen Produkt springen möchtest, kannst du das vollständige
> Projekt aus unserem GitHub-Repository klonen und lokal ausführen.
>
> ```bash
> git clone https://github.com/corbado/digital-credentials-example.git
> ```

### 4.1 Projekt einrichten

Zuerst initialisieren wir ein neues Next.js-Projekt, installieren die notwendigen
Abhängigkeiten und starten unsere Datenbank.

#### 4.1.1 Next.js-App initialisieren

Öffne dein Terminal, navigiere zu dem Verzeichnis, in dem du dein Projekt erstellen
möchtest, und führe den folgenden Befehl aus. Wir verwenden für dieses Projekt den App
Router, TypeScript und Tailwind CSS.

```bash
npx create-next-app@latest . --ts --eslint --tailwind --app --src-dir --import-alias "@/*" --use-npm
```

Dieser Befehl erstellt eine neue Next.js-Anwendung in deinem aktuellen Verzeichnis.

#### 4.1.2 Abhängigkeiten installieren

Als Nächstes müssen wir die Bibliotheken installieren, die sich um die CBOR-Dekodierung,
Datenbankverbindungen und die UUID-Generierung kümmern.

```bash
npm install cbor-web mysql2 uuid @types/uuid
```

Dieser Befehl installiert:

- `cbor-web`: Zum Dekodieren der mdoc-Nachweis-Payload.
- `mysql2`: Der MySQL-Client für unsere Datenbank.
- `uuid`: Zur Erzeugung eindeutiger Challenge-Strings.
- `@types/uuid`: TypeScript-Typen für die `uuid`-Bibliothek.

#### 4.1.3 Datenbank starten

Unser Backend benötigt eine MySQL-Datenbank, um OIDC-Sitzungsdaten zu speichern und
sicherzustellen, dass jeder Verifizierungsablauf sicher und zustandsbehaftet ist. Wir
haben eine `docker-compose.yml`-Datei beigefügt, um dies zu vereinfachen.

Wenn du das Repository geklont hast, kannst du einfach `docker-compose up -d` ausführen.
Wenn du von Grund auf neu beginnst, erstelle eine Datei namens `docker-compose.yml` mit
dem folgenden Inhalt:

```yaml
services:
    mysql:
        image: mysql:8.0
        restart: always
        environment:
            MYSQL_ROOT_PASSWORD: rootpassword
            MYSQL_DATABASE: digital_credentials
            MYSQL_USER: app_user
            MYSQL_PASSWORD: app_password
        ports:
            - "3306:3306"
        volumes:
            - mysql_data:/var/lib/mysql
            - ./sql/init.sql:/docker-entrypoint-initdb.d/init.sql
        healthcheck:
            test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
            timeout: 20s
            retries: 10

volumes:
    mysql_data:
```

Dieses Docker-Compose-Setup erfordert auch ein SQL-Initialisierungsskript. Erstelle ein
Verzeichnis namens `sql` und darin eine Datei namens `init.sql` mit dem folgenden Inhalt,
um die notwendigen Tabellen einzurichten:

```sql
-- Create database if not exists
CREATE DATABASE IF NOT EXISTS digital_credentials;
USE digital_credentials;

-- Table for storing challenges
CREATE TABLE IF NOT EXISTS challenges (
    id VARCHAR(36) PRIMARY KEY,
    challenge VARCHAR(255) NOT NULL UNIQUE,
    expires_at TIMESTAMP NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    used BOOLEAN DEFAULT FALSE,
    INDEX idx_challenge (challenge),
    INDEX idx_expires_at (expires_at)
);

-- Table for storing verification sessions
CREATE TABLE IF NOT EXISTS verification_sessions (
    id VARCHAR(36) PRIMARY KEY,
    challenge_id VARCHAR(36),
    status ENUM('pending', 'verified', 'failed', 'expired') DEFAULT 'pending',
    presentation_data JSON,
    verified_at TIMESTAMP NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (challenge_id) REFERENCES challenges(id) ON DELETE CASCADE,
    INDEX idx_challenge_id (challenge_id),
    INDEX idx_status (status)
);

-- Table for storing verified credentials data (optional)
CREATE TABLE IF NOT EXISTS verified_credentials (
    id VARCHAR(36) PRIMARY KEY,
    session_id VARCHAR(36),
    credential_type VARCHAR(255),
    issuer VARCHAR(255),
    subject VARCHAR(255),
    claims JSON,
    verified_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (session_id) REFERENCES verification_sessions(id) ON DELETE CASCADE,
    INDEX idx_session_id (session_id),
    INDEX idx_credential_type (credential_type)
);
```

Sobald beide Dateien vorhanden sind, öffne dein Terminal im Projektstammverzeichnis und
führe aus:

```bash
docker-compose up -d
```

Dieser Befehl startet einen MySQL-Container im Hintergrund.

### 4.2 Architekturübersicht der Next.js-App

Unsere Next.js-Anwendung ist so strukturiert, dass die Zuständigkeiten zwischen Frontend
und Backend getrennt sind, obwohl sie Teil desselben Projekts sind.

- **Frontend (`src/app/page.tsx`):** Eine einzelne [React](https://www.corbado.com/blog/react-passkeys)-Seite,
  die den Verifizierungsablauf initiiert und das Ergebnis anzeigt. Sie interagiert mit der
  Digital Credential API des Browsers.
- **Backend API Routes (`src/app/api/verify/...`):**
    - `start/route.ts`: Generiert die OpenID4VP-Anfrage und eine Sicherheits-Nonce.
    - `finish/route.ts`: Empfängt die Präsentation von der Wallet (über den Browser),
      validiert die Nonce und dekodiert den Nachweis.
- **Library (`src/lib/`):**
    - `database.ts`: Verwaltet alle Datenbankinteraktionen (Erstellen von Challenges,
      Verifizieren von Sitzungen).
    - `crypto.ts`: Kümmert sich um die Dekodierung des CBOR-basierten mDoc-Nachweises.

Hier ist ein Diagramm, das die interne Architektur veranschaulicht:

![NextJS Internal Architecture](https://s3.eu-central-1.amazonaws.com/corbado-cloud-staging-website-assets/Mermaid_Chart_Create_complex_visual_diagrams_with_text_A_smarter_way_of_creating_diagrams_2025_07_25_091202_f96ccb049f.svg)

### 4.3 Das Frontend bauen

Unser Frontend ist bewusst schlank gehalten. Seine Hauptaufgabe ist es, als
benutzerseitiger Auslöser für den Verifizierungsablauf zu dienen und sowohl mit unserem
Backend als auch mit den nativen Funktionen des Browsers zur Handhabung von Nachweisen zu
kommunizieren. Es enthält selbst keine komplexe Protokolllogik; das wird alles delegiert.

Konkret wird das Frontend Folgendes handhaben:

- **Benutzerinteraktion:** Bietet eine einfache Oberfläche, wie einen
  „Verifizieren“-Button, damit der User den Prozess starten kann.
- **Zustandsverwaltung:** Verwaltet den UI-Zustand, zeigt Ladeindikatoren während der
  Verifizierung an und präsentiert die endgültige Erfolgs- oder Fehlermeldung.
- **Backend-Kommunikation (Anfrage):** Ruft `/api/verify/start` auf und empfängt eine
  strukturierte JSON-Payload (`protocol`, `request`, `state`), die genau beschreibt, was
  die Wallet präsentieren soll.
- **Aufruf der Browser-API:** Übergibt dieses JSON-Objekt an
  `navigator.credentials.get()`, was einen nativen QR-Code rendert und auf die Antwort der
  Wallet wartet.
- **Backend-Kommunikation (Antwort):** Sobald die Browser-API die
  [Verifiable Presentation](https://www.corbado.com/glossary/verifiable-presentation) zurückgibt, sendet sie
  diese Daten zur endgültigen serverseitigen Validierung in einer POST-Anfrage an unseren
  `/api/verify/finish`-Endpunkt.
- **Ergebnisse anzeigen:** Aktualisiert die UI, um den User darüber zu informieren, ob die
  Verifizierung erfolgreich war oder fehlgeschlagen ist, basierend auf der Antwort des
  Backends.

Die Kernlogik befindet sich in der `startVerification`-Funktion:

```typescript
// src/app/page.tsx

const startVerification = async () => {
    setLoading(true);
    setVerificationResult(null);

    try {
        // 1. Prüfen, ob der Browser die API unterstützt
        if (!navigator.credentials?.get) {
            throw new Error("Browser does not support the Credential API.");
        }

        // 2. Unser Backend nach einem Anfrageobjekt fragen
        const res = await fetch("/api/verify/start");
        const { protocol, request } = await res.json();

        // 3. Dieses Objekt an den Browser übergeben – dies löst den nativen QR-Code aus
        const credential = await (navigator.credentials as any).get({
            mediation: "required",
            digital: {
                requests: [
                    {
                        protocol, // "openid4vp"
                        data: request, // enthält dcql_query, nonce, etc.
                    },
                ],
            },
        });

        // 4. Die Wallet-Antwort (vom Browser) zur serverseitigen Prüfung an unseren finish-Endpunkt weiterleiten
        const verifyRes = await fetch("/api/verify/finish", {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify(credential),
        });

        const result = await verifyRes.json();

        if (verifyRes.ok && result.verified) {
            setVerificationResult(`Success: ${result.message}`);
        } else {
            throw new Error(result.message || "Verification failed.");
        }
    } catch (err) {
        setVerificationResult(`Error: ${(err as Error).message}`);
    } finally {
        setLoading(false);
    }
};
```

Diese Funktion zeigt die vier entscheidenden Schritte der Frontend-Logik: Prüfung der
API-Unterstützung, Abrufen der Anfrage vom Backend, Aufrufen der Browser-API und
Zurücksenden des Ergebnisses zur Verifizierung. Der Rest der Datei ist
Standard-[React](https://www.corbado.com/blog/react-passkeys)-Boilerplate für Zustands- und UI-Rendering, das du
im [GitHub-Repository](https://github.com/corbado/digital-credentials-example) einsehen
kannst.

#### Warum `digital` und `mediation: 'required'`?

Du könntest bemerken, dass unser Aufruf von `navigator.credentials.get()` anders aussieht
als in einfacheren Beispielen. Das liegt daran, dass wir uns strikt an die offizielle
[W3C Digital Credentials API-Spezifikation](https://www.w3.org/TR/digital-credentials/#the-digital-credentials-api)
halten.

- **`digital`-Member:** Die Spezifikation verlangt, dass alle Anfragen für digitale
  Nachweise in einem `digital`-Objekt verschachtelt sind. Dies bietet einen klaren,
  standardisierten Namensraum für diese API, unterscheidet sie von anderen Nachweistypen
  (wie `password` oder `federated`) und ermöglicht zukünftige Erweiterungen ohne
  Konflikte.

- **`mediation: 'required'`:** Diese Option ist ein entscheidendes Sicherheits- und
  Benutzererlebnis-Feature. Sie erzwingt, dass der User aktiv mit einer Aufforderung
  interagieren muss (z. B. einem biometrischen Scan, einer PIN-Eingabe oder einem
  Zustimmungsbildschirm), um die Nachweisanfrage zu genehmigen. Ohne diese Option könnte
  eine Website potenziell versuchen, im Hintergrund unbemerkt auf Nachweise zuzugreifen,
  was ein erhebliches Datenschutzrisiko darstellt. Indem wir Mediation erfordern, stellen
  wir sicher, dass der User immer die Kontrolle behält und für jede Transaktion seine
  ausdrückliche Zustimmung gibt.

### 4.4 Die Backend-Endpunkte bauen

Mit der React-UI an Ort und Stelle benötigen wir nun zwei API-Routen, die die schwere
Arbeit auf dem Server erledigen:

1. **`/api/verify/start`** – erstellt eine OpenID4VP-Anfrage, speichert eine einmalige
   Challenge in MySQL und gibt alles an den Browser zurück.
2. **`/api/verify/finish`** – empfängt die Wallet-Antwort, validiert die Challenge,
   verifiziert & dekodiert den Nachweis und gibt schließlich ein prägnantes JSON-Ergebnis
   an die UI zurück.

#### 4.4.1 `/api/verify/start`: Die OpenID4VP-Anfrage generieren

```typescript
// src/app/api/verify/start/route.ts
import { NextResponse } from "next/server";
import { v4 as uuidv4 } from "uuid";
import { createChallenge, cleanupExpiredChallenges } from "@/lib/database";

export async function GET() {
    // 1️⃣  Eine kurzlebige, zufällige Nonce (Challenge) erstellen
    const challenge = uuidv4();
    const challengeId = uuidv4();
    const expiresAt = new Date(Date.now() + 5 * 60 * 1000);

    await createChallenge(challengeId, challenge, expiresAt);
    cleanupExpiredChallenges().catch(console.error);

    // 2️⃣  Eine DCQL-Abfrage erstellen, die beschreibt, *was* wir wollen
    const dcqlQuery = {
        credentials: [
            {
                id: "cred1",
                format: "mso_mdoc",
                meta: { doctype_value: "eu.europa.ec.eudi.pid.1" },
                claims: [
                    { path: ["eu.europa.ec.eudi.pid.1", "family_name"] },
                    { path: ["eu.europa.ec.eudi.pid.1", "given_name"] },
                    { path: ["eu.europa.ec.eudi.pid.1", "birth_date"] },
                ],
            },
        ],
    };

    // 3️⃣  Ein Objekt zurückgeben, das der Browser an navigator.credentials.get() übergeben kann
    return NextResponse.json({
        protocol: "openid4vp", // teilt dem Browser mit, welches Wallet-Protokoll zu verwenden ist
        request: {
            dcql_query: dcqlQuery, // WAS zu präsentieren ist
            nonce: challenge, // Schutz vor Wiederholungsangriffen
            response_type: "vp_token",
            response_mode: "dc_api", // die Wallet wird direkt an /finish POSTen
        },
        state: {
            credential_type: "mso_mdoc", // für spätere Prüfungen aufbewahrt
            nonce: challenge,
            challenge_id: challengeId,
        },
    });
}
```

> **Schlüsselparameter**
>
> • **`nonce`** – eine kryptografische Challenge, die Anfrage & Antwort verbindet
> (verhindert Replay-Angriffe). • **`dcql_query`** – Ein Objekt, das die genauen Claims
> beschreibt, die wir benötigen. Für diesen Guide verwenden wir eine
> `dcql_query`-Struktur, die von neueren Entwürfen der Digital Credential Query Language
> inspiriert ist, auch wenn dies noch kein finalisierter Standard ist. • **`state`** –
> beliebiges JSON, das von der Wallet zurückgespiegelt wird, damit wir den DB-Eintrag
> nachschlagen können.

#### 4.4.2 Datenbank-Helfer

Die Datei `src/lib/database.ts` kapselt die grundlegenden MySQL-Operationen für Challenges
& Verifizierungssitzungen (einfügen, lesen, als verwendet markieren). Diese Logik in einem
einzigen Modul zu halten, macht es einfach, den Datenspeicher später auszutauschen.

---

### 4.5 `/api/verify/finish`: Die Präsentation validieren & dekodieren

```typescript
// src/app/api/verify/finish/route.ts
import { NextResponse, NextRequest } from "next/server";
import { v4 as uuidv4 } from "uuid";
import {
    getChallenge,
    markChallengeAsUsed,
    createVerificationSession,
    updateVerificationSession,
} from "@/lib/database";
import { decodeDigitalCredential, decodeAllNamespaces } from "@/lib/crypto";

export async function POST(request: NextRequest) {
    const body = await request.json();

    // 1️⃣  Die Teile der verifizierbaren Präsentation extrahieren
    const vpTokenMap = body.vp_token ?? body.data?.vp_token;
    const state = body.state;
    const mdocToken = vpTokenMap?.cred1; // nach dieser ID haben wir in dcqlQuery gefragt

    if (!vpTokenMap || !state || !mdocToken) {
        return NextResponse.json(
            { verified: false, message: "Malformed response" },
            { status: 400 },
        );
    }

    // 2️⃣  Einmalige Challenge-Validierung
    const stored = await getChallenge(state.nonce);
    if (!stored) {
        return NextResponse.json(
            { verified: false, message: "Invalid or expired challenge" },
            { status: 400 },
        );
    }

    const sessionId = uuidv4();
    await createVerificationSession(sessionId, stored.id);

    // 3️⃣  (Pseudo) kryptografische Prüfungen – in der Produktion durch echte mDL-Validierung ersetzen
    // In einer echten Anwendung würdest du eine dedizierte Bibliothek verwenden, um eine vollständige
    // kryptografische Validierung der mdoc-Signatur gegen den öffentlichen Schlüssel des Ausstellers durchzuführen.
    const isValid = mdocToken.length > 0;
    if (!isValid) {
        await updateVerificationSession(sessionId, "failed", {
            reason: "mdoc validation failed",
        });
        return NextResponse.json(
            { verified: false, message: "Credential validation failed" },
            { status: 400 },
        );
    }

    // 4️⃣  Die mobile-DL (mdoc) Payload in lesbares JSON dekodieren
    const decoded = await decodeDigitalCredential(mdocToken);
    const readable = decodeAllNamespaces(decoded)["eu.europa.ec.eudi.pid.1"];

    await markChallengeAsUsed(state.nonce);
    await updateVerificationSession(sessionId, "verified", { readable });

    return NextResponse.json({
        verified: true,
        message: "mdoc credential verified successfully!",
        credentialData: readable,
        sessionId,
    });
}
```

> **Wichtige Felder in der Wallet-Antwort**
>
> • **`vp_token`** – Map, die _jeden_ Nachweis enthält, den die Wallet zurückgibt. Für
> unsere Demo holen wir `vp_token.cred1`. • **`state`** – Echo des Blobs, den wir in
> `/start` bereitgestellt haben; enthält die `nonce`, damit wir den DB-Eintrag
> nachschlagen können. • **`mdocToken`** – eine Base64URL-kodierte CBOR-Struktur, die das
> ISO mDoc darstellt.

### 4.6 Den mdoc-Nachweis dekodieren

Wenn der Verifier einen mdoc-Nachweis vom Browser erhält, handelt es sich um einen
Base64URL-String, der CBOR-kodierte Binärdaten enthält. Um die eigentlichen Claims zu
extrahieren, führt der `finish`-Endpunkt einen mehrstufigen Dekodierungsprozess mit
Hilfsfunktionen aus `src/lib/crypto.ts` durch.

#### 4.6.1 Schritt 1: Base64URL- und CBOR-Dekodierung

Die Funktion `decodeDigitalCredential` kümmert sich um die Umwandlung des kodierten
Strings in ein nutzbares Objekt:

```typescript
// src/lib/crypto.ts
export async function decodeDigitalCredential(encodedCredential: string) {
    // 1. Base64URL in Standard-Base64 umwandeln
    const base64UrlToBase64 = (input: string) => {
        let base64 = input.replace(/-/g, "+").replace(/_/g, "/");
        const pad = base64.length % 4;
        if (pad) base64 += "=".repeat(4 - pad);
        return base64;
    };

    const base64 = base64UrlToBase64(encodedCredential);

    // 2. Base64 in Binärdaten dekodieren
    const binaryString = atob(base64);
    const byteArray = Uint8Array.from(binaryString, (char) => char.charCodeAt(0));

    // 3. CBOR dekodieren
    const decoded = await cbor.decodeFirst(byteArray);
    return decoded;
}
```

- **Base64URL zu Base64:** Wandelt den Nachweis von Base64URL- in
  Standard-Base64-Kodierung um.
- **Base64 zu Binär:** Dekodiert den Base64-String in ein binäres Byte-Array.
- **CBOR-Dekodierung:** Verwendet die `cbor-web`-Bibliothek, um die Binärdaten in ein
  strukturiertes JavaScript-Objekt zu dekodieren.

#### 4.6.2 Schritt 2: Namespaced Claims extrahieren

Die Funktion `decodeAllNamespaces` verarbeitet das dekodierte CBOR-Objekt weiter, um die
eigentlichen Claims aus den relevanten Namespaces zu extrahieren:

```typescript
// src/lib/crypto.ts
export function decodeAllNamespaces(jsonObj) {
    const decoded = {};

    try {
        jsonObj.documents.forEach((doc, idx) => {
            // 1) issuerSigned.nameSpaces:
            const issuerNS = doc.issuerSigned?.nameSpaces || {};
            Object.entries(issuerNS).forEach(([nsName, entries]) => {
                if (!decoded[nsName]) decoded[nsName] = {};
                (entries as any[]).forEach((entry) => {
                    const bytes = Uint8Array.from(entry.value);
                    const decodedEntry = cbor.decodeFirstSync(bytes);
                    Object.assign(decoded[nsName], decodedEntry);
                });
            });

            // 2) deviceSigned.nameSpaces (falls vorhanden):
            const deviceNS = doc.deviceSigned?.nameSpaces;
            if (deviceNS?.value?.data) {
                const bytes = Uint8Array.from(deviceNS.value);
                decoded[`deviceSigned_ns_${idx}`] = cbor.decodeFirstSync(bytes);
            }
        });
    } catch (e) {
        console.error(e);
    }

    return decoded;
}
```

- **Iteriert über alle Dokumente** im dekodierten Nachweis.
- **Dekodiert jeden Namespace** (z. B. `eu.europa.ec.eudi.pid.1`), um die tatsächlichen
  Claim-Werte (wie Name, Geburtsdatum usw.) zu extrahieren.
- **Behandelt sowohl vom Aussteller signierte als auch vom Gerät signierte Namespaces**,
  falls vorhanden.

#### Beispiel-Ausgabe

Nachdem diese Schritte durchlaufen wurden, erhält der `finish`-Endpunkt ein
menschenlesbares Objekt, das die Claims aus dem mdoc enthält, zum Beispiel:

```json
{
    "family_name": "Doe",
    "given_name": "John",
    "birth_date": "1990-01-01"
}
```

Dieser Prozess stellt sicher, dass der Verifier die notwendigen Informationen aus dem
mdoc-Nachweis sicher und zuverlässig zur Anzeige und weiteren Verarbeitung extrahieren
kann.

### 4.7 Das Ergebnis in der UI anzeigen

Der `finish`-Endpunkt gibt ein minimales JSON-Objekt an das Frontend zurück:

```json
{
    "verified": true,
    "message": "mdoc credential verified successfully!",
    "credentialData": {
        "family_name": "Doe",
        "given_name": "John",
        "birth_date": "1990-01-01"
    }
}
```

Das Frontend empfängt diese Antwort in `startVerification()` und speichert sie einfach im
React-Zustand, sodass wir eine schöne Bestätigungskarte rendern oder einzelne Claims
anzeigen können – z. B. _„Willkommen, John Doe (geboren 1990-01-01)!“_.

## 5. Den Verifier ausführen und nächste Schritte

Du hast jetzt einen vollständigen, funktionierenden Verifier, der die nativen Funktionen
des Browsers zur Handhabung von Nachweisen nutzt. Hier erfährst du, wie du ihn lokal
ausführen kannst und was du tun kannst, um ihn von einem Proof-of-Concept zu einer
produktionsreifen Anwendung zu machen.

### 5.1 Wie man das Beispiel ausführt

1. **Repository klonen:**

    ```bash
    git clone https://github.com/corbado/digital-credentials-example.git
    cd digital-credentials-example
    ```

2. **Abhängigkeiten installieren:**

    ```bash
    npm install
    ```

3. **Datenbank starten:** Stelle sicher, dass Docker auf deinem Rechner läuft, und starte
   dann den MySQL-Container:

    ```bash
    docker-compose up -d
    ```

4. **Anwendung ausführen:**

    ```bash
    npm run dev
    ```

    Öffne deinen Browser unter `http://localhost:3000`, und du solltest die
    Benutzeroberfläche des Verifiers sehen. Du kannst nun deine CMWallet verwenden, um den
    QR-Code zu scannen und den Verifizierungsprozess abzuschließen.

### 5.2 Nächste Schritte: Vom Demo zur Produktion

Dieses Tutorial liefert die grundlegenden Bausteine für einen Verifier. Um ihn
produktionsreif zu machen, müsstest du mehrere zusätzliche Funktionen implementieren:

- **Vollständige kryptografische Validierung:** Die aktuelle Implementierung verwendet
  eine Platzhalterprüfung (`mdocToken.length > 0`). In einem realen Szenario musst du eine
  vollständige kryptografische Validierung der mdoc-Signatur gegen den öffentlichen
  Schlüssel des Ausstellers durchführen (z. B. durch Auflösung seiner DID oder Abrufen
  seines Public-Key-Zertifikats). Für Standards zur DID-Auflösung siehe die
  [W3C DID Resolution-Spezifikation](https://www.w3.org/TR/did-resolution/).

- **Überprüfung des Widerrufsstatus durch den Aussteller:** Nachweise können vom
  Aussteller vor ihrem Ablaufdatum widerrufen werden. Ein Produktions-Verifier muss den
  Status des Nachweises überprüfen, indem er eine Widerrufsliste oder einen
  Status-Endpunkt des Ausstellers abfragt. Die
  [W3C Verifiable Credentials Status List](https://www.w3.org/TR/vc-bitstring-status-list/)
  bietet den Standard für Widerrufslisten von Nachweisen.

- **Robuste Fehlerbehandlung & Sicherheit:** Füge eine umfassende Fehlerbehandlung,
  Eingabevalidierung und Ratenbegrenzung für API-Endpunkte hinzu und stelle sicher, dass
  die gesamte Kommunikation über HTTPS (TLS) erfolgt, um Daten während der Übertragung zu
  schützen. Die
  [OWASP API Security Guidelines](https://owasp.org/www-project-api-security/) bieten
  umfassende Best Practices für die
  API-[Sicherheit](https://www.corbado.com/de/blog/vollstaendig-passwortlos-werden).

- **Unterstützung für mehrere Nachweistypen:** Erweitere die Logik, um verschiedene
  `doctype`-Werte und Nachweisformate zu handhaben, wenn du erwartest, mehr als nur den
  europäischen [Digital Identity](https://www.corbado.com/blog/digital-identity-guide) (EUDI) PID-Nachweis zu
  erhalten. Das
  [W3C Verifiable Credentials Data Model](https://www.w3.org/TR/vc-data-model/) bietet
  umfassende Spezifikationen für VC-Formate.

### 5.3 Was nicht im Rahmen dieses Tutorials liegt

Dieses Beispiel konzentriert sich bewusst auf den zentralen browservermittelten Ablauf, um
es leicht verständlich zu machen. Die folgenden Themen werden als außerhalb des Rahmens
betrachtet:

- **Produktionsreife Sicherheit:** Der Verifier dient zu Bildungszwecken und verfügt nicht
  über die für eine Live-Umgebung erforderliche Härtung.
- **W3C Verifiable Credentials:** Dieses Tutorial konzentriert sich ausschließlich auf das
  ISO mDoc-Format für mobile Führerscheine. Es behandelt keine anderen gängigen Formate
  wie JWT-VCs oder VCs mit Linked Data Proofs (LD-Proofs).
- **Fortgeschrittene OpenID4VP-Flows:** Wir implementieren keine komplexeren
  OpenID4VP-Funktionen wie die direkte Kommunikation zwischen Wallet und Backend über eine
  `redirect_uri` oder die dynamische Client-Registrierung.

Indem du auf diesem Fundament aufbaust und diese nächsten Schritte einbeziehst, kannst du
einen robusten und sicheren Verifier entwickeln, der in der Lage ist,
[digitale Nachweise](https://www.corbado.com/de/blog/digital-credentials-api) in deinen eigenen Anwendungen zu
vertrauen und zu validieren.

## Fazit

Das war's! Mit weniger als 250 Zeilen TypeScript haben wir jetzt einen
End-to-End-Verifier, der:

1. Eine Anfrage für die Credential-API des Browsers veröffentlicht.
2. Jeder konformen Wallet erlaubt, eine Verifiable Presentation bereitzustellen.
3. Die Präsentation auf dem Server validiert.
4. Die Benutzeroberfläche in Echtzeit aktualisiert.

In der Produktion würdest du die Platzhalter-Validierung durch vollständige
[ISO 18013-5](https://www.corbado.com/glossary/iso-18013-5)-Prüfungen ersetzen, Widerrufsabfragen beim Aussteller
hinzufügen, Ratenbegrenzung, Audit-Logging und natürlich End-to-End-TLS – aber die
grundlegenden Bausteine bleiben genau dieselben.

## Ressourcen

Hier sind einige der wichtigsten Ressourcen, Spezifikationen und Werkzeuge, die in diesem
Tutorial verwendet oder referenziert wurden:

- **Projekt-Repository:**
    - [Vollständiger Quellcode auf GitHub](https://github.com/corbado/digital-credentials-example)

- **Wichtige Spezifikationen:**
    - [W3C Verifiable Credentials Data Model](https://www.w3.org/TR/vc-data-model/): Der
      grundlegende Standard für VCs.
    - [OpenID for Verifiable Presentations (OpenID4VP)](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html):
      Das Präsentationsprotokoll, das für den Austausch von Nachweisen verwendet wird.
    - [ISO/IEC 18013-5 (mDoc)](https://www.iso.org/standard/69084.html): Der
      internationale Standard für mobile Führerscheine (mDLs).
    - [W3C Digital Credentials API](https://www.w3.org/TR/digital-credentials/#the-digital-credentials-api):
      Die Browser-API, die zur Anforderung von Nachweisen von einer Wallet verwendet wird.

- **Werkzeuge:**
    - [CMWallet für Android](https://github.com/digitalcredentialsdev/CMWallet/actions/runs/16407676816/artifacts/3574255220):
      Die in diesem Guide verwendete Test-Wallet.

- **Bibliotheken:**
    - Next.js: Das React-Framework zum Erstellen von Frontend und Backend.
    - [cbor-web](https://github.com/hildjj/cbor-web): Zum Dekodieren von CBOR-kodierten
      mdoc-Nachweisen.
    - [mysql2](https://github.com/sidorares/node-mysql2): Der MySQL-Client für
      [Node.js](https://www.corbado.com/blog/nodejs-passkeys).
