---
url: 'https://www.corbado.com/id/blog/cara-membuat-verifikator-kredensial-digital'
title: 'Cara Membuat Verifikator Kredensial Digital (Panduan Developer)'
description: 'Pelajari cara membuat verifikator kredensial digital dari awal menggunakan Next.js, OpenID4VP, dan ISO mDoc. Panduan developer langkah demi langkah ini menunjukkan cara membuat verifikator yang dapat meminta, menerima, dan memvalidasi surat izin mengemudi'
lang: 'id'
author: 'Amine'
date: '2025-08-20T15:39:30.536Z'
lastModified: '2026-03-27T07:07:19.955Z'
keywords: 'verifikator kredensial digital, tutorial verifikator, membuat verifikator'
category: 'Digital Credentials'
---

# Cara Membuat Verifikator Kredensial Digital (Panduan Developer)

## 1. Pendahuluan

Membuktikan identitas secara online adalah tantangan yang terus-menerus, yang menyebabkan
ketergantungan pada kata sandi dan berbagi dokumen sensitif melalui saluran yang tidak
aman. Hal ini membuat verifikasi identitas untuk bisnis menjadi proses yang lambat, mahal,
dan rawan penipuan. [Kredensial Digital](https://www.corbado.com/id/blog/digital-credentials-api) menawarkan
pendekatan baru, yang mengembalikan kendali data kepada pengguna. Kredensial ini adalah
versi digital dari [dompet](https://www.corbado.com/id/blog/jaminan-dompet-digital) fisik, yang berisi segalanya
mulai dari surat izin mengemudi hingga ijazah universitas, tetapi dengan manfaat tambahan
yaitu aman secara kriptografis, menjaga privasi, dan dapat diverifikasi secara instan.

Panduan ini memberikan [tutorial](https://www.corbado.com/id/blog/aplikasi-crud-react-express-mysql) praktis dan
langkah demi langkah bagi para developer untuk membangun verifikator
[Kredensial Digital](https://www.corbado.com/id/blog/digital-credentials-api). Meskipun standarnya sudah ada,
panduan tentang penerapannya masih sedikit.
[Tutorial](https://www.corbado.com/id/blog/aplikasi-crud-react-express-mysql) ini mengisi kekosongan tersebut,
dengan menunjukkan cara membangun verifikator menggunakan
[API Kredensial Digital](https://www.corbado.com/id/blog/digital-credentials-api) bawaan browser,
[OpenID4VP](https://www.corbado.com/glossary/open-id-4-vp) untuk protokol presentasi, dan ISO
[mDoc](https://www.corbado.com/glossary/mdoc) (misalnya,
[surat izin mengemudi mobile](https://www.corbado.com/id/blog/surat-izin-mengemudi-mobile)) sebagai format
kredensial.

Hasil akhirnya adalah aplikasi [Next.js](https://www.corbado.com/blog/nextjs-passkeys) yang sederhana namun
fungsional yang dapat meminta, menerima, dan memverifikasi
[Kredensial Digital](https://www.corbado.com/id/blog/digital-credentials-api) dari
[dompet](https://www.corbado.com/id/blog/jaminan-dompet-digital) mobile yang kompatibel.

Berikut adalah gambaran singkat dari aplikasi final saat beraksi. Prosesnya melibatkan
empat langkah utama:

**Langkah 1: Halaman Awal** Pengguna mendarat di halaman awal dan mengeklik "Verify with
[Digital Identity](https://www.corbado.com/blog/digital-identity-guide)" untuk memulai proses.
![Halaman awal untuk permintaan verifikasi](https://s3.eu-central-1.amazonaws.com/corbado-cloud-staging-website-assets/Screenshot_2025_07_25_at_11_00_33_5217b35c96.png)

**Langkah 2: Permintaan Kepercayaan** Browser meminta kepercayaan dari pengguna. Pengguna
mengeklik "Continue" untuk melanjutkan.
![Permintaan kepercayaan browser](https://s3.eu-central-1.amazonaws.com/corbado-cloud-staging-website-assets/Screenshot_2025_07_25_at_11_00_39_ba390a8097.png)

**Langkah 3: Pemindaian Kode QR** Sebuah [kode QR](https://www.corbado.com/id/blog/kode-qr-login-autentikasi)
ditampilkan, yang dipindai oleh pengguna dengan aplikasi
[dompet](https://www.corbado.com/id/blog/jaminan-dompet-digital) mereka yang kompatibel.
![Kode QR untuk pemindaian](https://s3.eu-central-1.amazonaws.com/corbado-cloud-staging-website-assets/Screenshot_2025_07_25_at_11_00_45_3b30669a10.png)

**Langkah 4: Kredensial yang Didekode** Setelah verifikasi berhasil, aplikasi menampilkan
data kredensial yang telah didekode.
![Hasil kredensial yang didekode](https://s3.eu-central-1.amazonaws.com/corbado-cloud-staging-website-assets/Screenshot_2025_07_25_at_11_01_36_684f7489cd.png)

### 1.1 Cara Kerjanya

Kecanggihan di balik kredensial digital terletak pada model "**segitiga kepercayaan**"
yang sederhana namun kuat yang melibatkan tiga pemain kunci:

- **Penerbit (Issuer):** Otoritas tepercaya (misalnya, lembaga
  [pemerintah](https://www.corbado.com/passkeys-for-public-sector), universitas, atau bank) yang secara
  kriptografis menandatangani dan menerbitkan kredensial kepada pengguna.
- **Pemegang (Holder):** Pengguna, yang menerima kredensial dan menyimpannya dengan aman
  di [dompet digital](https://www.corbado.com/id/blog/jaminan-dompet-digital) pribadi di perangkat mereka.
- **Verifikator (Verifier):** Aplikasi atau layanan yang perlu memeriksa kredensial
  pengguna.

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

Ketika seorang pengguna ingin mengakses layanan, mereka menunjukkan kredensial dari dompet
mereka. Verifikator kemudian dapat langsung memeriksa keasliannya tanpa perlu menghubungi
penerbit asli secara langsung.

### 1.2 Mengapa Verifikator Penting (dan Mengapa Anda di Sini)

Agar **ekosistem identitas terdesentralisasi** ini dapat berkembang, peran **verifikator**
sangatlah penting. Mereka adalah penjaga gerbang dari
[infrastruktur](https://www.corbado.com/passkeys-for-critical-infrastructure) kepercayaan baru ini, pihak yang
mengonsumsi kredensial dan membuatnya berguna di dunia nyata. Seperti yang diilustrasikan
diagram di bawah, seorang verifikator melengkapi segitiga kepercayaan dengan meminta,
menerima, dan memvalidasi kredensial dari pemegang.

Jika Anda seorang developer, membangun layanan untuk melakukan verifikasi ini adalah
keterampilan dasar untuk generasi aplikasi yang aman dan berpusat pada pengguna di masa
depan. Panduan ini dirancang untuk memandu Anda melalui proses tersebut. Kita akan
membahas semua yang perlu Anda ketahui untuk **membangun verifikator kredensial
terverifikasi Anda sendiri**, mulai dari konsep dan standar inti hingga detail
implementasi langkah demi langkah dalam memvalidasi tanda tangan dan memeriksa status
kredensial.

> **Ingin langsung mencoba?** Anda dapat menemukan proyek lengkap dan jadi untuk
> [tutorial](https://www.corbado.com/id/blog/aplikasi-crud-react-express-mysql) ini di GitHub. Silakan clone dan
> coba sendiri:
> [https://github.com/corbado/digital-credentials-example](https://github.com/corbado/digital-credentials-example)

Mari kita mulai.

## 2. Prasyarat untuk Membangun Verifikator

Sebelum memulai, pastikan Anda memiliki:

1. **Pemahaman Dasar tentang Kredensial Digital dan mdoc**
    - Tutorial ini berfokus pada format **ISO mDoc** (misalnya, untuk surat izin mengemudi
      mobile) dan tidak mencakup format lain seperti W3C
      [Verifiable Credentials](https://www.corbado.com/glossary/microcredentials) (VCs). Memahami konsep dasar
      [mdoc](https://www.corbado.com/glossary/mdoc) akan sangat membantu.
2. **Docker dan Docker Compose**
    - Proyek kami menggunakan database [MySQL](https://www.corbado.com/blog/passkey-webauthn-database-guide)
      dalam sebuah kontainer Docker untuk mengelola status sesi OIDC. Pastikan Anda telah
      menginstal dan menjalankan keduanya.
3. **Protokol yang Dipilih: OpenID4VP**
    - Kita akan menggunakan protokol **OpenID4VP** (OpenID for Verifiable Presentations)
      untuk alur pertukaran kredensial.
4. **Tech Stack Siap**
    - Gunakan **TypeScript** (Node.js) untuk logika backend.
    - Gunakan **Next.js** untuk backend (API routes) dan frontend (UI).
    - Library utama: library decoding [CBOR](https://www.corbado.com/glossary/cbor) untuk parsing
      [mdoc](https://www.corbado.com/glossary/mdoc) dan klien [MySQL](https://www.corbado.com/blog/passkey-webauthn-database-guide).
5. **Kredensial dan Dompet Uji**
    - Kita akan menggunakan
      **[CMWallet](https://github.com/digitalcredentialsdev/CMWallet/actions/runs/16407676816/artifacts/3574255220)**
      untuk [Android](https://www.corbado.com/blog/how-to-enable-passkeys-android), yang memahami permintaan
      [OpenID4VP](https://www.corbado.com/glossary/open-id-4-vp) dan dapat mempresentasikan kredensial mdoc.
6. **Pengetahuan Dasar Kriptografi**
    - Memahami tanda tangan digital dan konsep kunci publik/privat karena berkaitan dengan
      mdoc dan alur OIDC.

---

Sekarang kita akan membahas setiap prasyarat ini secara detail, dimulai dengan standar dan
protokol yang mendasari verifikator berbasis mdoc ini.

### 2.1 Pilihan Protokol

Verifikator kita dibangun untuk hal-hal berikut:

| Standar / Protokol                                                                        | Deskripsi                                                                                                                                                                                                                        |
| :---------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **[W3C VC](https://www.w3.org/TR/vc-data-model/)**                                        | W3C Verifiable Credentials Data Model. Ini mendefinisikan struktur standar untuk kredensial digital, termasuk klaim, metadata, dan bukti.                                                                                        |
| **[SD-JWT](https://datatracker.ietf.org/doc/draft-ietf-oauth-selective-disclosure-jwt/)** | Selective Disclosure for JWTs. Format untuk VC berbasis JSON Web Tokens yang memungkinkan pemegang untuk secara selektif hanya mengungkapkan klaim tertentu dari sebuah kredensial, sehingga meningkatkan privasi.               |
| **[ISO mDoc](https://www.corbado.com/id/blog/digital-credentials-api)**                                          | ISO/IEC 18013-5. Standar internasional untuk Surat Izin Mengemudi mobile (mDL) dan ID mobile lainnya, yang mendefinisikan struktur data dan protokol komunikasi untuk penggunaan offline dan online.                             |
| **OpenID4VP**                                                                             | OpenID for Verifiable Presentations. Protokol presentasi yang dapat dioperasikan yang dibangun di atas OAuth 2.0. Ini mendefinisikan bagaimana verifikator meminta kredensial dan bagaimana dompet pemegang mempresentasikannya. |

Untuk tutorial ini, kita secara spesifik menggunakan:

- **OpenID4VP** sebagai protokol untuk meminta dan menerima kredensial.
- **ISO mDoc** sebagai format kredensial (misalnya, untuk
  [surat izin mengemudi mobile](https://www.corbado.com/id/blog/surat-izin-mengemudi-mobile)).

> **Catatan tentang Lingkup:** Meskipun kita secara singkat memperkenalkan W3C VC dan
> SD-JWT untuk memberikan konteks yang lebih luas, tutorial ini secara eksklusif
> mengimplementasikan kredensial [ISO mDoc](https://www.corbado.com/id/blog/digital-credentials-api) melalui
> [OpenID4VP](https://www.corbado.com/glossary/open-id-4-vp). VC berbasis W3C berada di luar lingkup contoh ini.

#### 2.1.1 ISO mDoc (Mobile Document)

Standar ISO/IEC 18013-5 mDoc mendefinisikan struktur dan pengkodean untuk dokumen mobile
seperti [surat izin mengemudi mobile](https://www.corbado.com/id/blog/surat-izin-mengemudi-mobile) (mDL).
Kredensial mDoc dienkode dengan [CBOR](https://www.corbado.com/glossary/cbor), ditandatangani secara
kriptografis, dan dapat dipresentasikan secara digital untuk verifikasi. Verifikator kita
akan fokus pada decoding dan validasi kredensial mdoc ini.

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

OpenID4VP adalah protokol yang dapat dioperasikan untuk meminta dan mempresentasikan
kredensial digital, dibangun di atas [OAuth 2.0](https://www.corbado.com/glossary/oauth2) dan OpenID Connect.
Dalam implementasi ini, OpenID4VP digunakan untuk:

- Memulai alur presentasi kredensial (melalui
  [kode QR](https://www.corbado.com/id/blog/kode-qr-login-autentikasi) atau API browser)
- Menerima kredensial mdoc dari dompet pengguna
- Memastikan pertukaran kredensial yang aman, stateful, dan menjaga privasi

### 2.2 Pilihan Tech Stack

Setelah kita memiliki pemahaman yang jelas tentang standar dan protokol, kita perlu
memilih tech stack yang tepat untuk membangun verifikator kita. Pilihan kita dirancang
untuk ketahanan, pengalaman developer, dan kompatibilitas dengan ekosistem web modern.

#### 2.2.1 Bahasa: TypeScript

Kita akan menggunakan **TypeScript** untuk kode frontend dan backend kita. Sebagai
superset dari [JavaScript](https://www.corbado.com/id/blog/aplikasi-crud-react-express-mysql), ia menambahkan
pengetikan statis, yang membantu menangkap kesalahan lebih awal, meningkatkan kualitas
kode, dan membuat aplikasi kompleks lebih mudah dikelola. Dalam konteks yang sensitif
terhadap [keamanan](https://www.corbado.com/id/blog/cara-mengaktifkan-passkey-android) seperti verifikasi
kredensial, [keamanan](https://www.corbado.com/id/blog/cara-mengaktifkan-passkey-android) tipe adalah keuntungan
besar.

#### 2.2.2 Framework: Next.js

**Next.js** adalah framework pilihan kita karena menyediakan pengalaman yang mulus dan
terintegrasi untuk membangun aplikasi
[full-stack](https://www.corbado.com/id/blog/aplikasi-crud-react-express-mysql).

- **Untuk Frontend:** Kita akan menggunakan [Next.js](https://www.corbado.com/blog/nextjs-passkeys) dengan
  [React](https://www.corbado.com/blog/react-passkeys) untuk membangun antarmuka pengguna di mana proses
  verifikasi dimulai (misalnya, menampilkan
  [kode QR](https://www.corbado.com/id/blog/kode-qr-login-autentikasi)).
- **Untuk Backend:** Kita akan memanfaatkan **Next.js API Routes** untuk membuat endpoint
  sisi server. Endpoint ini bertanggung jawab untuk membuat permintaan OpenID4VP yang
  valid dan untuk bertindak sebagai `redirect_uri` untuk menerima dan memverifikasi
  respons akhir dari CMWallet dengan aman.

#### 2.2.3 Library Utama

Implementasi kita bergantung pada serangkaian library khusus untuk frontend dan backend:

- **next**: Framework [Next.js](https://www.corbado.com/blog/nextjs-passkeys), digunakan untuk API routes backend
  dan UI frontend.
- **react** dan **react-dom**: Menggerakkan antarmuka pengguna frontend.
- **cbor-web**: Digunakan untuk mendekode kredensial mdoc yang dienkode
  [CBOR](https://www.corbado.com/glossary/cbor) menjadi objek
  [JavaScript](https://www.corbado.com/id/blog/aplikasi-crud-react-express-mysql) yang dapat digunakan.
- **mysql2**: Menyediakan konektivitas database
  [MySQL](https://www.corbado.com/blog/passkey-webauthn-database-guide) untuk menyimpan challenges dan sesi
  verifikasi.
- **uuid**: Library untuk menghasilkan string challenge (nonces) yang unik.
- **@types/uuid**: Tipe TypeScript untuk pembuatan UUID.

> **Catatan tentang `openid-client`:** Verifikator yang lebih canggih dan siap produksi
> mungkin menggunakan library `openid-client` untuk menangani protokol OpenID4VP langsung
> di backend, memungkinkan fitur seperti `redirect_uri` dinamis. Dalam alur OpenID4VP yang
> digerakkan server dengan `redirect_uri`, `openid-client` akan digunakan untuk mengurai
> dan memvalidasi respons `vp_token` secara langsung. Untuk tutorial ini, kita menggunakan
> alur yang lebih sederhana, yang dimediasi browser yang tidak memerlukannya, membuat
> prosesnya lebih mudah dipahami.

Tech stack ini memastikan implementasi verifikator yang tangguh, aman-tipe, dan dapat
diskalakan yang berfokus pada [API Kredensial Digital](https://www.corbado.com/id/blog/digital-credentials-api)
browser dan format kredensial [ISO mDoc](https://www.corbado.com/id/blog/digital-credentials-api).

### 2.3 Dapatkan Dompet dan Kredensial Uji

Untuk menguji verifikator Anda, Anda memerlukan dompet mobile yang dapat berinteraksi
dengan [API Kredensial Digital](https://www.corbado.com/id/blog/digital-credentials-api) browser.

Kita akan menggunakan
**[CMWallet](https://github.com/digitalcredentialsdev/CMWallet/actions/runs/16407676816/artifacts/3574255220)**,
-dompet pengujian yang tangguh dan sesuai dengan OpenID4VP untuk
[Android](https://www.corbado.com/blog/how-to-enable-passkeys-android).

**Cara Menginstal CMWallet (Android):**

1. **Unduh file APK** menggunakan tautan di atas langsung di perangkat
   [Android](https://www.corbado.com/blog/how-to-enable-passkeys-android) Anda.
2. Buka **Settings > Security** di perangkat Anda.
3. Aktifkan **"Install unknown apps"** untuk browser yang Anda gunakan untuk mengunduh
   file.
4. Cari APK yang diunduh di folder "Downloads" Anda dan ketuk untuk memulai instalasi.
5. Ikuti petunjuk di layar untuk menyelesaikan instalasi.
6. Buka CMWallet, dan Anda akan menemukannya sudah terisi dengan kredensial uji, siap
   untuk alur verifikasi.

> **Catatan:** Hanya instal file APK dari sumber yang Anda percayai. Tautan yang
> disediakan berasal dari repositori proyek resmi.

### 2.4 Pengetahuan Kriptografi

Sebelum kita masuk ke implementasi, penting untuk memahami konsep kriptografi yang
mendasari kredensial terverifikasi. Inilah yang membuatnya "dapat diverifikasi" dan dapat
dipercaya.

#### 2.4.1 Tanda Tangan Digital: Fondasi Kepercayaan

Pada intinya, Kredensial Terverifikasi adalah seperangkat klaim (seperti nama, tanggal
lahir, dll.) yang telah ditandatangani secara digital oleh penerbit. Tanda tangan digital
memberikan dua jaminan penting:

- **Keaslian (Authenticity):** Ini membuktikan bahwa kredensial tersebut memang dibuat
  oleh penerbit dan bukan oleh penipu.
- **Integritas (Integrity):** Ini membuktikan bahwa kredensial tersebut belum diubah atau
  dirusak sejak ditandatangani.

#### 2.4.2 Kriptografi Kunci Publik/Privat

Tanda tangan digital dibuat menggunakan
[kriptografi kunci publik](https://www.corbado.com/id/blog/webauthn-pubkeycredparams-credentialpublickey)/privat
(juga disebut kriptografi asimetris). Begini cara kerjanya dalam konteks kita:

1. **Penerbit memiliki sepasang kunci:** kunci privat, yang dirahasiakan, dan kunci
   publik, yang tersedia untuk semua orang (biasanya melalui Dokumen DID mereka).
2. **Penandatanganan:** Ketika penerbit membuat kredensial, mereka menggunakan **kunci
   privat** mereka untuk menghasilkan tanda tangan digital yang unik untuk data kredensial
   spesifik tersebut.
3. **Verifikasi:** Ketika verifikator kita menerima kredensial, ia menggunakan **kunci
   publik** penerbit untuk memeriksa tanda tangan. Jika pemeriksaan berhasil, verifikator
   tahu kredensial itu asli dan belum dirusak. Perubahan apa pun pada data kredensial akan
   membatalkan tanda tangan.

> **Catatan tentang DID:** Dalam tutorial ini, kita tidak menyelesaikan kunci penerbit
> melalui DID. Di lingkungan produksi, penerbit biasanya akan mengekspos kunci publik
> melalui DID atau endpoint otoritatif lainnya, yang akan digunakan verifikator untuk
> validasi kriptografis.

#### 2.4.3 Kredensial Terverifikasi sebagai JWT

Kredensial Terverifikasi sering diformat sebagai **JSON Web Tokens (JWTs)**. JWT adalah
cara yang ringkas dan aman untuk URL untuk merepresentasikan klaim yang akan ditransfer
antara dua pihak. JWT yang ditandatangani (juga dikenal sebagai JWS) memiliki tiga bagian
yang dipisahkan oleh titik (`.`):

- **Header:** Berisi metadata tentang token, seperti algoritma penandatanganan yang
  digunakan (`alg`).
- **Payload:** Berisi klaim aktual dari Kredensial Terverifikasi (klaim `vc`), termasuk
  `issuer`, `credentialSubject`, dll.
- **Signature:** Tanda tangan digital yang dihasilkan oleh penerbit, yang mencakup header
  dan payload.

```
// Contoh struktur JWT
[Header].[Payload].[Signature]
```

> **Catatan:** Kredensial Terverifikasi berbasis JWT berada di luar lingkup posting blog
> ini. Implementasi ini berfokus pada kredensial ISO mDoc dan OpenID4VP, bukan Kredensial
> Terverifikasi W3C atau kredensial berbasis JWT.

#### 2.4.4 Presentasi Terverifikasi: Membuktikan Kepemilikan

Tidak cukup bagi verifikator untuk mengetahui bahwa kredensial itu valid; ia juga perlu
tahu bahwa orang yang _mempresentasikan_ kredensial adalah pemegang yang sah. Ini mencegah
seseorang menggunakan kredensial yang dicuri.

Ini diselesaikan menggunakan **Presentasi Terverifikasi (VP)**. VP adalah pembungkus di
sekitar satu atau lebih VC yang **ditandatangani oleh pemegang itu sendiri**.

Alurnya adalah sebagai berikut:

1. Verifikator meminta pengguna untuk mempresentasikan kredensial.
2. Dompet pengguna membuat Presentasi Terverifikasi, membundel kredensial yang diperlukan
   di dalamnya, dan menandatangani seluruh presentasi menggunakan **kunci privat
   pemegang**.
3. Dompet mengirimkan VP yang ditandatangani ini ke verifikator.

Verifikator kita kemudian harus melakukan **dua** pemeriksaan tanda tangan terpisah:

1. **Verifikasi Kredensial:** Periksa tanda tangan pada setiap VC di dalam presentasi
   menggunakan **kunci publik penerbit**. (Membuktikan kredensial itu nyata).
2. **Verifikasi Presentasi:** Periksa tanda tangan pada VP itu sendiri menggunakan **kunci
   publik pemegang**. (Membuktikan orang yang mempresentasikannya adalah pemiliknya).

Cek dua tingkat ini memastikan keaslian kredensial dan identitas orang yang
mempresentasikannya, menciptakan model kepercayaan yang kuat dan aman.

> **Catatan:** Konsep Presentasi Terverifikasi seperti yang didefinisikan dalam ekosistem
> W3C VC berada di luar lingkup posting blog ini. Istilah Presentasi Terverifikasi di sini
> mengacu pada respons `vp_token` OpenID4VP, yang berperilaku mirip dengan VP W3C tetapi
> didasarkan pada semantik ISO mDoc daripada model tanda tangan
> [JSON-LD](https://www.corbado.com/glossary/json-ld) W3C. Panduan ini berfokus pada kredensial ISO mDoc dan
> OpenID4VP, bukan pada Presentasi Terverifikasi W3C atau validasi tanda tangan mereka.

## 3. Tinjauan Arsitektur

Arsitektur verifikator kita menggunakan **API Kredensial Digital** bawaan browser sebagai
perantara aman untuk menghubungkan
[aplikasi web](https://www.corbado.com/id/blog/aplikasi-crud-react-express-mysql) kita dengan **CMWallet** mobile
pengguna. Pendekatan ini menyederhanakan alur dengan membiarkan browser menangani tampilan
kode QR asli dan komunikasi dompet.

- **Frontend (Next.js & React):** Situs web ringan yang menghadap pengguna. Tugasnya
  adalah mengambil objek permintaan dari backend kita, meneruskannya ke API
  `navigator.credentials.get()` browser, menerima hasilnya, dan meneruskannya ke backend
  kita untuk verifikasi.
- **Backend (Next.js API Routes):** Pusat kekuatan verifikator. Ini menghasilkan objek
  permintaan yang valid untuk API browser dan mengekspos endpoint untuk menerima
  presentasi kredensial dari frontend untuk validasi akhir.
- **Browser (Credential API):** Fasilitator. Ini menerima objek permintaan dari frontend
  kita, memahami protokol `openid4vp`, dan secara native menghasilkan kode QR. Kemudian
  menunggu dompet mengembalikan respons.
- **CMWallet (Aplikasi Mobile):** Dompet pengguna. Ini memindai kode QR, memproses
  permintaan, mendapatkan persetujuan pengguna, dan mengirimkan respons yang
  ditandatangani kembali ke browser.

Berikut adalah diagram urutan yang mengilustrasikan alur yang lengkap dan akurat:

![Alur Verifikasi menggunakan API Kredensial Digital Browser](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)

**Penjelasan Alur:**

1. **Inisiasi:** Pengguna mengeklik tombol "Verify" di **Frontend** kita.
2. **Objek Permintaan:** Frontend memanggil **Backend** kita (`/api/verify/start`), yang
   menghasilkan objek permintaan yang berisi kueri dan nonce, lalu mengembalikannya.
3. **Panggilan API Browser:** Frontend memanggil `navigator.credentials.get()` dengan
   objek permintaan.
4. **Kode QR Asli:** **Browser** melihat permintaan protokol `openid4vp` dan secara native
   menampilkan kode QR. Promise `.get()` sekarang dalam status pending.

> **Catatan:** Alur kode QR ini terjadi pada browser desktop. Pada browser mobile (Android
> Chrome dengan flag eksperimental diaktifkan), browser dapat langsung berkomunikasi
> dengan dompet yang kompatibel di perangkat yang sama, menghilangkan kebutuhan untuk
> pemindaian kode QR. Untuk mengaktifkan fitur ini di Android Chrome, navigasikan ke
> `chrome://flags#web-identity-digital-credentials` dan atur flag ke "Enabled".

5. **Pindai & Presentasikan:** Pengguna memindai kode QR dengan **CMWallet**. Dompet
   mendapatkan persetujuan pengguna dan mengirimkan Presentasi Terverifikasi kembali ke
   browser.
6. **Penyelesaian Promise:** Browser menerima respons, dan promise `.get()` asli di
   frontend akhirnya selesai, mengirimkan payload presentasi.
7. **Verifikasi Backend:** Frontend melakukan **POST** payload presentasi ke endpoint
   `/api/verify/finish` backend kita. Backend memvalidasi nonce dan kredensial.
8. **Hasil:** Backend mengembalikan pesan keberhasilan atau kegagalan akhir ke frontend,
   yang memperbarui UI.

## 4. Membangun Verifikator

Setelah kita memiliki pemahaman yang kuat tentang standar, protokol, dan alur arsitektur,
kita dapat mulai membangun verifikator kita.

> **Ikuti atau Gunakan Kode Final**
>
> Kita sekarang akan melalui penyiapan dan implementasi kode langkah demi langkah. Jika
> Anda lebih suka langsung ke produk jadi, Anda dapat meng-clone proyek lengkap dari
> repositori GitHub kami dan menjalankannya secara lokal.
>
> ```bash
> git clone https://github.com/corbado/digital-credentials-example.git
> ```

### 4.1 Menyiapkan Proyek

Pertama, kita akan menginisialisasi proyek Next.js baru, menginstal dependensi yang
diperlukan, dan memulai database kita.

#### 4.1.1 Menginisialisasi Aplikasi Next.js

Buka terminal Anda, navigasikan ke direktori tempat Anda ingin membuat proyek Anda, dan
jalankan perintah berikut. Kita menggunakan App Router, TypeScript, dan Tailwind CSS untuk
proyek ini.

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

Perintah ini membuat aplikasi Next.js baru di direktori Anda saat ini.

#### 4.1.2 Menginstal Dependensi

Selanjutnya, kita perlu menginstal library yang akan menangani decoding CBOR, koneksi
database, dan pembuatan UUID.

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

Perintah ini menginstal:

- `cbor-web`: Untuk mendekode payload kredensial mdoc.
- `mysql2`: Klien MySQL untuk database kita.
- `uuid`: Untuk menghasilkan string challenge yang unik.
- `@types/uuid`: Tipe TypeScript untuk library `uuid`.

#### 4.1.3 Memulai Database

Backend kita memerlukan database MySQL untuk menyimpan data sesi OIDC, memastikan bahwa
setiap alur verifikasi aman dan stateful. Kami telah menyertakan file `docker-compose.yml`
untuk memudahkan ini.

Jika Anda telah meng-clone repositori, Anda cukup menjalankan `docker-compose up -d`. Jika
Anda membangun dari awal, buat file bernama `docker-compose.yml` dengan konten berikut:

```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:
```

Pengaturan Docker Compose ini juga memerlukan skrip inisialisasi SQL. Buat direktori
bernama `sql` dan di dalamnya, file bernama `init.sql` dengan konten berikut untuk
menyiapkan tabel yang diperlukan:

```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)
);
```

Setelah kedua file berada di tempatnya, buka terminal Anda di root proyek dan jalankan:

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

Perintah ini akan memulai kontainer MySQL di latar belakang.

### 4.2 Tinjauan Arsitektur Aplikasi Next.js

Aplikasi Next.js kita terstruktur untuk memisahkan urusan antara frontend dan backend,
meskipun mereka adalah bagian dari proyek yang sama.

- **Frontend (`src/app/page.tsx`):** Satu halaman [React](https://www.corbado.com/blog/react-passkeys) yang
  menginisiasi alur verifikasi dan menampilkan hasilnya. Ia berinteraksi dengan API
  Kredensial Digital browser.
- **Backend API Routes (`src/app/api/verify/...`):**
    - `start/route.ts`: Menghasilkan permintaan OpenID4VP dan nonce
      [keamanan](https://www.corbado.com/id/blog/cara-mengaktifkan-passkey-android).
    - `finish/route.ts`: Menerima presentasi dari dompet (melalui browser), memvalidasi
      nonce, dan mendekode kredensial.
- **Library (`src/lib/`):**
    - `database.ts`: Mengelola semua interaksi database (membuat challenges, memverifikasi
      sesi).
    - `crypto.ts`: Menangani decoding kredensial mDoc berbasis CBOR.

Berikut adalah diagram yang mengilustrasikan arsitektur internal:

![Arsitektur Internal NextJS](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 Membangun Frontend

Frontend kita sengaja dibuat ringan. Tanggung jawab utamanya adalah bertindak sebagai
pemicu yang menghadap pengguna untuk alur verifikasi dan untuk berkomunikasi dengan
backend kita dan kemampuan penanganan kredensial asli browser. Ia tidak mengandung logika
protokol yang kompleks; semua itu didelegasikan.

Secara spesifik, frontend akan menangani hal berikut:

- **Interaksi Pengguna:** Menyediakan antarmuka sederhana, seperti tombol "Verify", bagi
  pengguna untuk memulai proses.
- **Manajemen State:** Mengelola state UI, menunjukkan indikator pemuatan saat verifikasi
  sedang berlangsung dan menampilkan pesan keberhasilan atau kesalahan akhir.
- **Komunikasi Backend (Permintaan):** Memanggil `/api/verify/start` dan menerima payload
  JSON terstruktur (`protocol`, `request`, `state`) yang menjelaskan dengan tepat apa yang
  harus dipresentasikan oleh dompet.
- **Pemanggilan API Browser:** Menyerahkan objek JSON tersebut ke
  `navigator.credentials.get()`, yang merender kode QR asli dan menunggu respons dompet.
- **Komunikasi Backend (Respons):** Setelah API browser mengembalikan Presentasi
  Terverifikasi, ia mengirimkan data ini ke endpoint `/api/verify/finish` kita dalam
  permintaan POST untuk validasi sisi server akhir.
- **Menampilkan Hasil:** Memperbarui UI untuk memberi tahu pengguna apakah verifikasi
  berhasil atau gagal, berdasarkan respons dari backend.

Logika inti ada di dalam fungsi `startVerification`:

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

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

    try {
        // 1. Check if the browser supports the API
        if (!navigator.credentials?.get) {
            throw new Error("Browser does not support the Credential API.");
        }

        // 2. Ask our backend for a request object
        const res = await fetch("/api/verify/start");
        const { protocol, request } = await res.json();

        // 3. Hand that object to the browser – this triggers the native QR code
        const credential = await (navigator.credentials as any).get({
            mediation: "required",
            digital: {
                requests: [
                    {
                        protocol, // "openid4vp"
                        data: request, // contains dcql_query, nonce, etc.
                    },
                ],
            },
        });

        // 4. Forward the wallet response (from the browser) to our finish endpoint for server-side checks
        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);
    }
};
```

Fungsi ini menunjukkan empat langkah kunci dari logika frontend: memeriksa dukungan API,
mengambil permintaan dari backend, memanggil API browser, dan mengirimkan hasilnya kembali
untuk verifikasi. Sisa dari file ini adalah boilerplate [React](https://www.corbado.com/blog/react-passkeys)
standar untuk state dan rendering UI, yang dapat Anda lihat di
[repositori GitHub](https://github.com/corbado/digital-credentials-example).

#### Mengapa `digital` dan `mediation: 'required'`?

Anda mungkin memperhatikan panggilan kita ke `navigator.credentials.get()` terlihat
berbeda dari contoh yang lebih sederhana. Ini karena kita mengikuti secara ketat
spesifikasi resmi
[W3C Digital Credentials API](https://www.w3.org/TR/digital-credentials/#the-digital-credentials-api).

- **`digital` Member:** Spesifikasi mengharuskan semua permintaan kredensial digital
  disarangkan di dalam objek `digital`. Ini menyediakan namespace yang jelas dan
  terstandarisasi untuk API ini, membedakannya dari jenis kredensial lain (seperti
  `password` atau `federated`) dan memungkinkan ekstensi di masa depan tanpa konflik.

- **`mediation: 'required'`:** Opsi ini adalah fitur keamanan dan pengalaman pengguna yang
  krusial. Ini memberlakukan bahwa pengguna harus secara aktif berinteraksi dengan prompt
  (misalnya, pemindaian biometrik, entri PIN, atau layar persetujuan) untuk menyetujui
  permintaan kredensial. Tanpa itu, sebuah situs web berpotensi mencoba mengakses
  kredensial secara diam-diam di latar belakang, yang menimbulkan risiko privasi yang
  signifikan. Dengan mewajibkan mediasi, kita memastikan bahwa pengguna selalu memegang
  kendali dan memberikan persetujuan eksplisit untuk setiap transaksi.

### 4.4 Membangun Endpoint Backend

Dengan UI React yang sudah ada, kita sekarang membutuhkan dua rute API yang melakukan
pekerjaan berat di server:

1. **`/api/verify/start`** – membangun permintaan OpenID4VP, menyimpan challenge sekali
   pakai di MySQL dan menyerahkan semuanya kembali ke browser.
2. **`/api/verify/finish`** – menerima respons dompet, memvalidasi challenge,
   memverifikasi & mendekode kredensial, dan akhirnya mengembalikan hasil JSON ringkas ke
   UI.

#### 4.4.1 `/api/verify/start`: Hasilkan Permintaan OpenID4VP

```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️⃣  Buat nonce (challenge) acak yang berumur pendek
    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️⃣  Buat kueri DCQL yang menjelaskan *apa* yang kita inginkan
    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️⃣  Kembalikan objek yang dapat diteruskan browser ke navigator.credentials.get()
    return NextResponse.json({
        protocol: "openid4vp", // memberitahu browser protokol dompet mana yang harus digunakan
        request: {
            dcql_query: dcqlQuery, // APA yang harus dipresentasikan
            nonce: challenge, // anti-replay
            response_type: "vp_token",
            response_mode: "dc_api", // dompet akan melakukan POST langsung ke /finish
        },
        state: {
            credential_type: "mso_mdoc", // disimpan untuk pemeriksaan nanti
            nonce: challenge,
            challenge_id: challengeId,
        },
    });
}
```

> **Parameter kunci**
>
> • **`nonce`** – [cryptographic challenge](https://www.corbado.com/glossary/cryptographic-challenge) yang
> mengikat permintaan & respons (mencegah replay). • **`dcql_query`** – Objek yang
> menjelaskan klaim persis yang kita butuhkan. Untuk panduan ini, kita menggunakan
> struktur `dcql_query` yang terinspirasi oleh draf terbaru dari Digital Credential Query
> Language, meskipun ini belum menjadi standar final. • **`state`** – JSON acak yang
> dipantulkan kembali oleh dompet sehingga kita dapat mencari catatan DB.

#### 4.4.2 Helper Database

File `src/lib/database.ts` membungkus operasi dasar MySQL untuk challenges & sesi
verifikasi (insert, read, mark-used). Menyimpan logika ini dalam satu modul membuatnya
mudah untuk menukar datastore nanti.

---

### 4.5 `/api/verify/finish`: Validasi & Dekode Presentasi

```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️⃣  Ekstrak bagian-bagian presentasi yang dapat diverifikasi
    const vpTokenMap = body.vp_token ?? body.data?.vp_token;
    const state = body.state;
    const mdocToken = vpTokenMap?.cred1; // kita meminta ID ini di dcqlQuery

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

    // 2️⃣  Validasi challenge sekali pakai
    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️⃣  Pemeriksaan kriptografi (Pseudo) – ganti dengan validasi mDL nyata di prod
    // Dalam aplikasi nyata, Anda akan menggunakan library khusus untuk melakukan validasi
    // kriptografi penuh dari tanda tangan mdoc terhadap kunci publik penerbit.
    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️⃣  Dekode payload mobile-DL (mdoc) menjadi JSON yang dapat dibaca manusia
    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,
    });
}
```

> **Bidang penting dalam respons dompet**
>
> • **`vp_token`** – map yang menampung _setiap_ kredensial yang dikembalikan dompet.
> Untuk demo kita, kita menarik `vp_token.cred1`. • **`state`** – gema dari blob yang kita
> berikan di `/start`; berisi `nonce` sehingga kita dapat mencari catatan DB. •
> **`mdocToken`** – struktur CBOR yang dienkode Base64URL yang merepresentasikan ISO mDoc.

### 4.6 Mendekode Kredensial mdoc

Ketika verifikator menerima kredensial mdoc dari browser, itu adalah string Base64URL yang
berisi data biner yang dienkode CBOR. Untuk mengekstrak klaim aktual, endpoint `finish`
melakukan proses decoding multi-langkah menggunakan fungsi pembantu dari
`src/lib/crypto.ts`.

#### 4.6.1 Langkah 1: Decoding Base64URL dan CBOR

Fungsi `decodeDigitalCredential` menangani konversi dari string yang dienkode menjadi
objek yang dapat digunakan:

```typescript
// src/lib/crypto.ts
export async function decodeDigitalCredential(encodedCredential: string) {
    // 1. Convert Base64URL to standard Base64
    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. Decode Base64 to binary
    const binaryString = atob(base64);
    const byteArray = Uint8Array.from(binaryString, (char) => char.charCodeAt(0));

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

- **Base64URL ke Base64:** Mengonversi kredensial dari Base64URL ke pengkodean Base64
  standar.
- **Base64 ke Biner:** Mendekode string Base64 menjadi array byte biner.
- **Decoding CBOR:** Menggunakan library `cbor-web` untuk mendekode data biner menjadi
  objek [JavaScript](https://www.corbado.com/id/blog/aplikasi-crud-react-express-mysql) terstruktur.

#### 4.6.2 Langkah 2: Mengekstrak Klaim Bernamespace

Fungsi `decodeAllNamespaces` memproses lebih lanjut objek CBOR yang telah didekode untuk
mengekstrak klaim aktual dari namespace yang relevan:

```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 (if present):
            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;
}
```

- **Mengulangi semua dokumen** dalam kredensial yang telah didekode.
- **Mendekode setiap namespace** (misalnya, `eu.europa.ec.eudi.pid.1`) untuk mengekstrak
  nilai klaim aktual (seperti nama, tanggal lahir, dll.).
- **Menangani namespace yang ditandatangani penerbit dan perangkat** jika ada.

#### Contoh Output

Setelah melalui langkah-langkah ini, endpoint finish memperoleh objek yang dapat dibaca
manusia yang berisi klaim dari mdoc, misalnya:

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

Proses ini memastikan bahwa verifikator dapat mengekstrak informasi yang diperlukan dari
kredensial mdoc dengan aman dan andal untuk ditampilkan dan diproses lebih lanjut.

### 4.7 Menampilkan Hasil di UI

Endpoint finish mengembalikan objek JSON minimal ke frontend:

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

Frontend menerima respons ini di `startVerification()` dan hanya menyimpannya di state
React sehingga kita dapat merender kartu konfirmasi yang bagus atau menampilkan klaim
individual – mis. _“Selamat datang, John Doe (lahir 1990-01-01)!”_.

## 5. Menjalankan Verifikator dan Langkah Selanjutnya

Anda sekarang memiliki verifikator lengkap yang berfungsi yang menggunakan kemampuan
penanganan kredensial asli browser. Berikut cara menjalankannya secara lokal dan apa yang
dapat Anda lakukan untuk mengembangkannya dari bukti konsep menjadi aplikasi siap
produksi.

### 5.1 Cara Menjalankan Contoh

1. **Clone Repositori:**

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

2. **Instal Dependensi:**

    ```bash
    npm install
    ```

3. **Mulai Database:** Pastikan Docker berjalan di mesin Anda, lalu mulai kontainer MySQL:

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

4. **Jalankan Aplikasi:**

    ```bash
    npm run dev
    ```

    Buka browser Anda ke `http://localhost:3000`, dan Anda akan melihat UI verifikator.
    Anda sekarang dapat menggunakan CMWallet Anda untuk memindai kode QR dan menyelesaikan
    alur verifikasi.

### 5.2 Langkah Selanjutnya: Dari Demo ke Produksi

Tutorial ini menyediakan blok bangunan dasar untuk verifikator. Untuk membuatnya siap
produksi, Anda perlu mengimplementasikan beberapa fitur tambahan:

- **Validasi Kriptografi Penuh:** Implementasi saat ini menggunakan pemeriksaan
  placeholder (`mdocToken.length > 0`). Dalam skenario dunia nyata, Anda harus melakukan
  validasi kriptografi penuh dari tanda tangan mdoc terhadap kunci publik penerbit
  (misalnya, dengan menyelesaikan DID mereka atau mengambil sertifikat kunci publik
  mereka). Untuk standar resolusi DID, lihat
  [spesifikasi Resolusi DID W3C](https://www.w3.org/TR/did-resolution/).

- **Pemeriksaan Pencabutan Penerbit:** Kredensial dapat dicabut oleh penerbit sebelum
  tanggal kedaluwarsanya. Verifikator produksi harus memeriksa status kredensial dengan
  menanyakan daftar pencabutan atau endpoint status yang disediakan oleh penerbit.
  [W3C Verifiable Credentials Status List](https://www.w3.org/TR/vc-bitstring-status-list/)
  menyediakan standar untuk daftar pencabutan kredensial.

- **Penanganan Kesalahan & Keamanan yang Kuat:** Tambahkan penanganan kesalahan yang
  komprehensif, validasi input, pembatasan laju pada endpoint API, dan pastikan semua
  komunikasi melalui HTTPS (TLS) untuk melindungi data saat transit.
  [Panduan Keamanan API OWASP](https://owasp.org/www-project-api-security/) menyediakan
  praktik terbaik keamanan API yang komprehensif.

- **Dukungan untuk Beberapa Jenis Kredensial:** Perluas logika untuk menangani nilai
  `doctype` dan format kredensial yang berbeda jika Anda berharap menerima lebih dari
  sekadar kredensial PID [Identitas Digital](https://www.corbado.com/id/blog/digital-credentials-api) Eropa
  (EUDI). [W3C Verifiable Credentials Data Model](https://www.w3.org/TR/vc-data-model/)
  menyediakan spesifikasi format VC yang komprehensif.

### 5.3 Apa yang di Luar Lingkup Tutorial Ini

Contoh ini sengaja difokuskan pada alur inti yang dimediasi browser agar mudah dipahami.
Topik-topik berikut dianggap di luar lingkup:

- **Keamanan Siap Produksi:** Verifikator ini untuk tujuan pendidikan dan tidak memiliki
  pengerasan yang diperlukan untuk lingkungan live.
- **W3C Verifiable Credentials:** Tutorial ini berfokus secara eksklusif pada format ISO
  mDoc untuk surat izin mengemudi mobile. Ini tidak mencakup format populer lainnya
  seperti JWT-VC atau VC dengan Linked Data Proofs (LD-Proofs).
- **Alur OpenID4VP Lanjutan:** Kita tidak mengimplementasikan fitur OpenID4VP yang lebih
  kompleks, seperti komunikasi langsung dompet-ke-backend menggunakan `redirect_uri` atau
  pendaftaran klien dinamis.

Dengan membangun di atas fondasi ini dan memasukkan langkah-langkah selanjutnya ini, Anda
dapat mengembangkan verifikator yang kuat dan aman yang mampu mempercayai dan memvalidasi
kredensial digital di aplikasi Anda sendiri.

## Kesimpulan

Selesai! Dengan kurang dari 250 baris TypeScript, kita sekarang memiliki verifikator
end-to-end yang:

1. Mempublikasikan permintaan untuk API kredensial browser.
2. Membiarkan dompet yang kompatibel menyediakan Presentasi Terverifikasi.
3. Memvalidasi presentasi di server.
4. Memperbarui UI secara real-time.

Di lingkungan produksi, Anda akan mengganti validasi placeholder dengan pemeriksaan
[ISO 18013-5](https://www.corbado.com/glossary/iso-18013-5) penuh, menambahkan pencarian pencabutan penerbit,
pembatasan laju, logging audit, dan tentu saja, TLS end-to-end—tetapi blok bangunan inti
tetap sama persis.

## Sumber Daya

Berikut adalah beberapa sumber daya, spesifikasi, dan alat utama yang digunakan atau
direferensikan dalam tutorial ini:

- **Repositori Proyek:**
    - [Kode Sumber Lengkap di GitHub](https://github.com/corbado/digital-credentials-example)

- **Spesifikasi Utama:**
    - [W3C Verifiable Credentials Data Model](https://www.w3.org/TR/vc-data-model/):
      Standar dasar untuk VC.
    - [OpenID for Verifiable Presentations (OpenID4VP)](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html):
      Protokol presentasi yang digunakan untuk pertukaran kredensial.
    - [ISO/IEC 18013-5 (mDoc)](https://www.iso.org/standard/69084.html): Standar
      internasional untuk Surat Izin Mengemudi (mDL) mobile.
    - [W3C Digital Credentials API](https://www.w3.org/TR/digital-credentials/#the-digital-credentials-api):
      API browser yang digunakan untuk meminta kredensial dari dompet.

- **Alat:**
    - [CMWallet for Android](https://github.com/digitalcredentialsdev/CMWallet/actions/runs/16407676816/artifacts/3574255220):
      Dompet uji yang digunakan dalam panduan ini.

- **Library:**
    - Next.js: Framework React untuk membangun frontend dan backend.
    - [cbor-web](https://github.com/hildjj/cbor-web): Untuk mendekode kredensial mdoc yang
      dienkode CBOR.
    - [mysql2](https://github.com/sidorares/node-mysql2): Klien MySQL untuk
      [Node.js](https://www.corbado.com/blog/nodejs-passkeys).
