---
url: 'https://www.corbado.com/tr/blog/verifiable-credential-issuer-nasil-olusturulur'
title: 'Dijital Kimlik Bilgisi Vericisi Nasıl Oluşturulur (Geliştirici Kılavuzu)'
description: 'OpenID4VCI protokolünü kullanarak bir W3C Doğrulanabilir Kimlik Bilgisi vericisi oluşturmayı öğrenin. Bu adım adım kılavuz, dijital cüzdanlarla uyumlu, kriptografik olarak imzalanmış kimlik bilgileri veren bir Next.js uygulamasını nasıl oluşturacağınızı g'
lang: 'tr'
author: 'Amine'
date: '2025-08-20T15:39:22.726Z'
lastModified: '2026-03-25T10:08:03.722Z'
keywords: 'dijital kimlik bilgisi vericisi, verici oluşturma rehberi, verici oluşturma'
category: 'Digital Credentials'
---

# Dijital Kimlik Bilgisi Vericisi Nasıl Oluşturulur (Geliştirici Kılavuzu)

## 1. Giriş

[Dijital Kimlik Bilgileri](https://www.corbado.com/tr/blog/dijital-kimlik-bilgileri-passkeys), kimliği ve
beyanları güvenli ve gizliliği koruyan bir şekilde kanıtlamanın güçlü bir yoludur. Peki
kullanıcılar bu kimlik bilgilerini ilk etapta nasıl elde eder? İşte bu noktada
**Verici'nin (Issuer)** rolü kritik hale gelir. Bir Verici, bir
[devlet](https://www.corbado.com/passkeys-for-public-sector) kurumu, üniversite veya banka gibi güvenilir bir
varlıktır ve kullanıcılara dijital olarak imzalanmış kimlik bilgileri oluşturmaktan ve
dağıtmaktan sorumludur.

Bu kılavuz, bir [Dijital Kimlik](https://www.corbado.com/tr/glossary/open-id-4-vp) Bilgisi Vericisi oluşturmak
için kapsamlı, adım adım bir rehber sunmaktadır. Kullanıcıların bir Verici'den nasıl
kimlik bilgisi alabileceğini ve bunları dijital cüzdanlarında güvenli bir şekilde nasıl
saklayabileceğini tanımlayan modern bir standart olan **OpenID for Verifiable Credential
Issuance (OpenID4VCI)** protokolüne odaklanacağız.

Sonuçta, aşağıdaki işlemleri yapabilen fonksiyonel bir [Next.js](https://www.corbado.com/blog/nextjs-passkeys)
uygulaması ortaya çıkacak:

1. Basit bir web formu aracılığıyla kullanıcı verilerini kabul etme.
2. Güvenli, tek kullanımlık bir kimlik bilgisi teklifi oluşturma.
3. Teklifi, kullanıcının mobil [cüzdan](https://www.corbado.com/tr/blog/dijital-cuzdan-guvencesi)ı ile
   tarayabilmesi için bir QR kodu olarak görüntüleme.
4. Kullanıcının saklayabileceği ve doğrulama için sunabileceği, kriptografik olarak
   imzalanmış bir kimlik bilgisi düzenleme.

### 1.1 Terminolojiyi Anlamak: Dijital Kimlik Bilgileri ve Doğrulanabilir Kimlik Bilgileri

Devam etmeden önce, birbiriyle ilişkili ancak farklı iki kavram arasındaki ayrımı
netleştirmek önemlidir:

- **Dijital Kimlik Bilgileri (Genel Terim):** Bu, kimlik bilgilerinin, sertifikaların veya
  onayların her türlü dijital formunu kapsayan geniş bir kategoridir. Bunlar arasında
  basit dijital sertifikalar, temel dijital rozetler veya kriptografik güvenlik
  özelliklerine sahip olabilen ya da olmayan elektronik olarak saklanan herhangi bir
  kimlik bilgisi bulunabilir.

- **Doğrulanabilir Kimlik Bilgileri (VC'ler - W3C Standardı):** Bu, W3C Doğrulanabilir
  Kimlik Bilgileri Veri Modeli standardını takip eden özel bir
  [dijital kimlik](https://www.corbado.com/tr/glossary/open-id-4-vp) bilgisi türüdür. Doğrulanabilir Kimlik
  Bilgileri, bağımsız olarak doğrulanabilen, kriptografik olarak imzalanmış, kurcalamaya
  karşı korumalı ve gizliliğe saygılı kimlik bilgileridir. Aşağıdaki gibi belirli teknik
  gereksinimleri içerirler:
    - Özgünlük ve bütünlük için kriptografik imzalar
    - Standartlaştırılmış veri modeli ve formatlar
    - Gizliliği koruyan sunum mekanizmaları
    - Birlikte çalışabilir doğrulama protokolleri

**Bu kılavuzda, özellikle W3C standardını takip eden bir Doğrulanabilir Kimlik Bilgisi
vericisi oluşturuyoruz**, sadece herhangi bir [dijital kimlik](https://www.corbado.com/tr/glossary/open-id-4-vp)
bilgisi sistemi değil. Kullandığımız [OpenID4VCI](https://www.corbado.com/glossary/openid4vci) protokolü,
özellikle Doğrulanabilir Kimlik Bilgileri düzenlemek için tasarlanmıştır ve
uygulayacağımız JWT-VC formatı, Doğrulanabilir Kimlik Bilgileri için W3C uyumlu bir
formattır.

### 1.2 Nasıl Çalışır?

Dijital kimlik bilgilerinin arkasındaki sihir, üç kilit oyuncuyu içeren basit ama güçlü
bir "**güven üçgeni**" modelinde yatmaktadır:

- **Verici (Issuer):** Bir [devlet](https://www.corbado.com/passkeys-for-public-sector) kurumu, üniversite veya
  banka gibi güvenilir bir otorite, bir kullanıcıya kriptografik olarak imzalanmış bir
  kimlik bilgisi düzenler. **Bu kılavuzda inşa ettiğimiz rol budur.**
- **Sahip (Holder):** Kimlik bilgisini alan ve cihazındaki kişisel
  [dijital cüzdan](https://www.corbado.com/tr/blog/dijital-cuzdan-guvencesi)ında güvenli bir şekilde saklayan
  kullanıcı.
- **Doğrulayıcı (Verifier):** Kullanıcının kimlik bilgisini kontrol etmesi gereken bir
  uygulama veya hizmet.

![W3C Doğrulanabilir Kimlik Bilgileri Ekosistemi](https://www.w3.org/TR/vc-data-model/diagrams/ecosystem.svg)

Düzenleme akışı, bu ekosistemdeki ilk adımdır. Verici, kullanıcının bilgilerini doğrular
ve onlara bir kimlik bilgisi sağlar. Sahip, bu kimlik bilgisini
[cüzdan](https://www.corbado.com/tr/blog/dijital-cuzdan-guvencesi)ına aldıktan sonra, kimliğini veya beyanlarını
kanıtlamak için bir Doğrulayıcıya sunabilir ve böylece üçgen tamamlanır.

İşte son uygulamanın çalışır haldeki hızlı bir görünümü:

**Adım 1: Kullanıcı Veri Girişi** Kullanıcı, yeni bir kimlik bilgisi talep etmek için
kişisel bilgileriyle bir form doldurur.
![Kullanıcı Veri Giriş Formu](https://s3.eu-central-1.amazonaws.com/corbado-cloud-staging-website-assets/issuer_step_1_0733a9e1da.png)

**Adım 2: Kimlik Bilgisi Teklifi Oluşturma** Uygulama, QR kodu ve önceden yetkilendirilmiş
bir kod olarak görüntülenen güvenli bir kimlik bilgisi teklifi oluşturur.
![Kimlik Bilgisi Teklifi QR Kodu](https://s3.eu-central-1.amazonaws.com/corbado-cloud-staging-website-assets/issuer_step_2_3f1881c473.png)

**Adım 3: Cüzdan Etkileşimi** Kullanıcı, uyumlu bir cüzdanla (ör. Sphereon
[Wallet](https://www.corbado.com/blog/digital-wallet-assurance)) QR kodunu tarar ve düzenlemeyi yetkilendirmek
için bir PIN girer.
![Cüzdandaki Kimlik Bilgisi Teklifi](https://s3.eu-central-1.amazonaws.com/corbado-cloud-staging-website-assets/issuer_step_3_b80d689dfe.png)
![PIN Kodu girişi](https://s3.eu-central-1.amazonaws.com/corbado-cloud-staging-website-assets/issuer_step_4_ca8bad8d11.png)

**Adım 4: Kimlik Bilgisi Düzenlendi** [Cüzdan](https://www.corbado.com/tr/blog/dijital-cuzdan-guvencesi), yeni
düzenlenen dijital kimlik bilgisini alır ve gelecekteki kullanım için saklar.
![Kimlik bilgisi ayrıntılarını onaylama](https://s3.eu-central-1.amazonaws.com/corbado-cloud-staging-website-assets/issuer_step_5_55b8150597.png)
![Kimlik bilgisi eklendi](https://s3.eu-central-1.amazonaws.com/corbado-cloud-staging-website-assets/issuer_step_6_7f5ac5745d.png)

## 2. Bir Verici Oluşturmak İçin Ön Koşullar

Koda dalmadan önce, ihtiyacınız olacak temel bilgi ve araçları ele alalım. Bu kılavuz,
[web geliştirme](https://www.corbado.com/tr/blog/react-express-mysql-crud-uygulamasi) kavramlarına temel düzeyde
aşina olduğunuzu varsayar, ancak aşağıdaki ön koşullar bir kimlik bilgisi vericisi
oluşturmak için gereklidir.

### 2.1 Protokol Seçimleri

Vericimiz, cüzdanlar ve düzenleme hizmetleri arasında birlikte çalışabilirliği sağlayan
bir dizi açık standart üzerine kurulmuştur. Bu
[eğitim](https://www.corbado.com/tr/blog/react-express-mysql-crud-uygulamasi) için aşağıdakilere odaklanacağız:

| Standart / Protokol                                               | Açıklama                                                                                                                                                                                                                              |
| :---------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **OpenID4VCI**                                                    | **OpenID for Verifiable Credential Issuance.** Kullanacağımız temel protokol budur. Bir kullanıcının (cüzdanı aracılığıyla) bir Verici'den nasıl kimlik bilgisi talep edip alabileceğine dair standart bir akış tanımlar.             |
| **[JWT-VC](https://www.w3.org/TR/vc-data-model/#json-web-token)** | **JWT tabanlı Doğrulanabilir Kimlik Bilgileri.** Düzenleyeceğimiz kimlik bilgisinin formatıdır. Doğrulanabilir kimlik bilgilerini JSON Web Token (JWT) olarak kodlayan bir W3C standardıdır, bu da onları kompakt ve web dostu yapar. |
| **[ISO mDoc](https://www.iso.org/standard/69084.html)**           | **ISO/IEC 18013-5.** Mobil Sürücü Belgeleri (mDL'ler) için uluslararası standart. Bir JWT-VC düzenlesek de, içindeki _beyanlar_ mDoc veri modeliyle uyumlu olacak şekilde yapılandırılmıştır (ör. `eu.europa.ec.eudi.pid.1`).         |
| **OAuth 2.0**                                                     | OpenID4VCI tarafından kullanılan temel yetkilendirme çerçevesi. Güvenli ve kullanıcı dostu kimlik bilgisi düzenlemesi için tasarlanmış özel bir grant türü olan `pre-authorized_code` akışını uygulayacağız.                          |

#### 2.1.1 Yetkilendirme Akışları: Önceden Yetkilendirilmiş ve Yetkilendirme Kodu

[OpenID4VCI](https://www.corbado.com/glossary/openid4vci), kimlik bilgileri düzenlemek için iki ana yetkilendirme
akışını destekler:

1. **Önceden Yetkilendirilmiş Kod Akışı (Pre-Authorized Code Flow):** Bu akışta, Verici,
   kullanıcıya anında sunulan kısa ömürlü, tek kullanımlık bir kod (`pre-authorized_code`)
   oluşturur. Kullanıcının cüzdanı daha sonra bu kodu doğrudan bir kimlik bilgisiyle
   değiştirebilir. Bu akış, kullanıcının zaten kimliğinin doğrulandığı ve Verici'nin web
   sitesinde bulunduğu senaryolar için idealdir, çünkü yönlendirmeler olmadan sorunsuz,
   anında bir düzenleme deneyimi sağlar.

2. **Yetkilendirme Kodu Akışı (Authorization Code Flow):** Bu, kullanıcının onay vermek
   için bir yetkilendirme sunucusuna yönlendirildiği standart
   [OAuth 2.0](https://www.corbado.com/glossary/oauth2) akışıdır. Onaydan sonra, sunucu kayıtlı bir
   `redirect_uri` adresine bir `authorization_code` gönderir. Bu akış, kullanıcı adına
   düzenleme sürecini başlatan üçüncü taraf uygulamalar için daha uygundur.

**Bu eğitim için `pre-authorized_code` akışını kullanacağız.** Bu yaklaşımı seçtik çünkü
daha basit ve belirli kullanım durumumuz için daha doğrudan bir kullanıcı deneyimi
sağlıyor: bir kullanıcının doğrudan Verici'nin kendi web sitesinden bir kimlik bilgisi
talep etmesi. Karmaşık yönlendirmelere ve istemci kaydına olan ihtiyacı ortadan
kaldırarak, temel düzenleme mantığını anlamayı ve uygulamayı kolaylaştırır.

Bu standartların birleşimi, çok çeşitli dijital cüzdanlarla uyumlu bir verici
oluşturmamıza ve kullanıcı için güvenli, standartlaştırılmış bir süreç sağlamamıza olanak
tanır.

### 2.2 Teknoloji Yığını Seçimleri

Vericimizi oluşturmak için, doğrulayıcı için kullandığımız aynı sağlam ve modern teknoloji
yığınını kullanacağız, böylece tutarlı ve yüksek kaliteli bir geliştirici deneyimi
sağlayacağız.

#### 2.2.1 Dil: TypeScript

Hem ön uç hem de arka uç kodumuz için **TypeScript** kullanacağız. Statik tiplemesi, bir
verici gibi güvenliğin kritik olduğu bir uygulamada paha biçilmezdir, çünkü yaygın
hataları önlemeye yardımcı olur ve kodun genel kalitesini ve sürdürülebilirliğini artırır.

#### 2.2.2 Çerçeve: Next.js

**Next.js**, tam yığın uygulamalar oluşturmak için sorunsuz, entegre bir deneyim sağladığı
için tercih ettiğimiz çerçevedir.

- **Ön Uç İçin:** Kullanıcıların bir kimlik bilgisi talep etmek için verilerini
  girebilecekleri kullanıcı arayüzünü oluşturmak için [React](https://www.corbado.com/blog/react-passkeys) ile
  [Next.js](https://www.corbado.com/blog/nextjs-passkeys) kullanacağız.
- **Arka Uç İçin:** Kimlik bilgisi teklifleri oluşturmaktan son imzalı kimlik bilgisini
  düzenlemeye kadar [OpenID4VCI](https://www.corbado.com/glossary/openid4vci) akışını yöneten sunucu tarafı uç
  noktaları oluşturmak için **Next.js API Rotalarından** yararlanacağız.

#### 2.2.3 Anahtar Kütüphaneler

Uygulamamız, belirli görevleri yerine getirmek için birkaç anahtar kütüphaneye
dayanacaktır:

- **next**, **react**, ve **react-dom**: [Next.js](https://www.corbado.com/blog/nextjs-passkeys) uygulamamızın
  temel kütüphaneleri.
- **mysql2**: Yetkilendirme kodlarını ve oturum verilerini saklamak için kullanılan
  [Node.js](https://www.corbado.com/blog/nodejs-passkeys) için bir [MySQL](https://www.corbado.com/blog/passkey-webauthn-database-guide)
  istemcisi.
- **uuid**: `pre-authorized_code` değerleri oluşturmak için kullanacağımız benzersiz
  tanımlayıcılar üretmek için bir kütüphane.
- **jose**: Düzenlediğimiz kimlik bilgilerini kriptografik olarak imzalamak için
  kullanacağımız JSON Web İmzalarını (JWS) işlemek için sağlam bir kütüphane.

### 2.3 Bir Test Cüzdanı Edinin

Vericinizi test etmek için, OpenID4VCI protokolünü destekleyen bir mobil cüzdana
ihtiyacınız olacak. Bu [eğitim](https://www.corbado.com/tr/blog/react-express-mysql-crud-uygulamasi) için, hem
[Android](https://www.corbado.com/blog/how-to-enable-passkeys-android) hem de
[iOS](https://www.corbado.com/blog/how-to-enable-passkeys-ios) için mevcut olan **Sphereon Wallet**'ı öneriyoruz.

**Sphereon Wallet Nasıl Kurulur:**

1. Cüzdanı
   [Google Play Store](https://play.google.com/store/apps/details?id=com.sphereon.ssi.wallet)
   veya [Apple App Store](https://apps.apple.com/us/app/sphereon-wallet/id1661096796)'dan
   **indirin**.
2. Uygulamayı mobil cihazınıza kurun.
3. Kurulduktan sonra, cüzdan bir QR kodu tarayarak kimlik bilgisi teklifleri almaya
   hazırdır.

### 2.4 Kriptografi Bilgisi

Bir kimlik bilgisi düzenlemek, güven ve özgünlüğü sağlamak için temel kriptografik
kavramlara dayanan, güvenliğin kritik olduğu bir işlemdir.

#### 2.4.1 Dijital İmzalar

Özünde, bir Doğrulanabilir Kimlik Bilgisi, Verici tarafından **dijital olarak imzalanmış**
bir dizi beyandır. Bu imza iki güvence sağlar:

- **Özgünlük:** Kimlik bilgisinin meşru bir Verici tarafından oluşturulduğunu kanıtlar.
- **Bütünlük:** Kimlik bilgisinin düzenlendiğinden beri kurcalanmadığını kanıtlar.

#### 2.4.2 Açık/Özel Anahtar Kriptografisi

Dijital imzalar, açık/özel anahtar kriptografisi kullanılarak oluşturulur. İşte nasıl
çalıştığı:

1. **Vericinin bir anahtar çifti vardır:** gizli ve güvende tutulan bir **özel anahtar**
   ve buna karşılık gelen, halka açık hale getirilen bir **açık anahtar**.
2. **İmzalama:** Verici bir kimlik bilgisi oluşturduğunda, kimlik bilgisi verileri için
   benzersiz bir dijital imza oluşturmak üzere **özel anahtarını** kullanır.
3. **Doğrulama:** Bir Doğrulayıcı daha sonra imzayı kontrol etmek için Verici'nin **açık
   anahtarını** kullanabilir. Kontrol geçerse, Doğrulayıcı kimlik bilgisinin özgün
   olduğunu ve değiştirilmediğini bilir.

Uygulamamızda, bir Eliptik Eğri (EC) anahtar çifti oluşturacağız ve JWT-VC'yi imzalamak
için `ES256` algoritmasını kullanacağız. Açık anahtar, Verici'nin DID'sine (`did:web`)
gömülüdür, bu da herhangi bir Doğrulayıcının onu keşfetmesine ve kimlik bilgisinin
imzasını doğrulamasına olanak tanır. **Not:** JWT'lerimizde `aud` (audience) beyanı
kasıtlı olarak çıkarılmıştır, çünkü kimlik bilgisi genel amaçlı olarak tasarlanmıştır ve
belirli bir cüzdana bağlı değildir. Kullanımı belirli bir kitleyle kısıtlamak
istiyorsanız, bir `aud` beyanı ekleyin ve buna göre ayarlayın.

## 3. Mimari Genel Bakış

Verici uygulamamız, ön uç ve arka uç mantığı arasında net bir ayrım olan tam yığın bir
Next.js projesi olarak inşa edilmiştir. Bu mimari, sunucudaki tüm güvenlik açısından
kritik işlemleri ele alırken sorunsuz bir kullanıcı deneyimi yaratmamızı sağlar.
**Önemli:** SQL'de yer alan `verification_sessions` ve `verified_credentials` tabloları bu
verici için gerekli değildir, ancak bütünlük için dahil edilmiştir.

- **Ön Uç (`src/app/issue/page.tsx`):** Kullanıcıların bir kimlik bilgisi talep etmek için
  verilerini girmelerine olanak tanıyan tek bir [React](https://www.corbado.com/blog/react-passkeys) sayfası.
  Düzenleme sürecini başlatmak için arka ucumuzdaki API'lere çağrılar yapar.
- **Arka Uç API Rotaları (`src/app/api/issue/...`):** OpenID4VCI protokolünü uygulayan bir
  dizi sunucu tarafı uç nokta.
    - `/.well-known/openid-credential-issuer`: Halka açık bir meta veri uç noktası. Bu,
      bir cüzdanın, vericinin yetkilendirme sunucusu, token uç noktası, kimlik bilgisi uç
      noktası ve sunduğu kimlik bilgisi türleri de dahil olmak üzere yeteneklerini
      keşfetmek için kontrol edeceği ilk URL'dir.
    - `/.well-known/openid-configuration`: Standart bir OpenID Connect keşif uç noktası.
      Yukarıdakiyle yakından ilişkili olsa da, bu uç nokta daha geniş OIDC ile ilgili
      yapılandırmayı sunar ve standart OpenID istemcileriyle birlikte çalışabilirlik için
      genellikle gereklidir.
    - `/.well-known/did.json`: Vericimiz için DID Belgesi. `did:web` yöntemini
      kullanırken, bu dosya vericinin açık anahtarlarını yayınlamak için kullanılır;
      doğrulayıcılar bu anahtarları, düzenlediği kimlik bilgilerinin imzalarını doğrulamak
      için kullanabilir.
    - `authorize/route.ts`: Bir `pre-authorized_code` ve bir kimlik bilgisi teklifi
      oluşturur.
    - `token/route.ts`: `pre-authorized_code`'u bir erişim tokenı ile değiştirir.
    - `credential/route.ts`: Son, kriptografik olarak imzalanmış JWT-VC'yi düzenler.
    - `schemas/pid/route.ts`: PID kimlik bilgisi için JSON şemasını sunar. Bu, kimlik
      bilgisinin herhangi bir tüketicisinin yapısını ve veri türlerini anlamasını sağlar.
- **Kütüphane (`src/lib/`):**
    - `database.ts`: Yetkilendirme kodları ve verici anahtarları gibi tüm veritabanı
      etkileşimlerini yönetir.
    - `crypto.ts`: Anahtar oluşturma ve JWT imzalama dahil tüm kriptografik işlemleri
      yönetir.

İşte düzenleme akışını gösteren bir diyagram:

![Dijital Kimlik Bilgisi Düzenleme Akışı](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_29_145228_d28fd13731.svg)

## 4. Vericiyi Oluşturma

Standartlar, protokoller ve mimari hakkında sağlam bir anlayışa sahip olduğumuza göre,
vericimizi oluşturmaya başlayabiliriz.

> **Birlikte Takip Edin veya Son Kodu Kullanın**
>
> Şimdi kurulum ve kod uygulamasını adım adım inceleyeceğiz. Doğrudan bitmiş ürüne geçmeyi
> tercih ederseniz, tam projeyi GitHub depomuzdan klonlayabilir ve yerel olarak
> çalıştırabilirsiniz.
>
> ```bash
> git clone https://github.com/corbado/digital-credentials-example.git
> ```

### 4.1 Projeyi Kurma

İlk olarak, yeni bir Next.js projesi başlatacağız, gerekli bağımlılıkları yükleyeceğiz ve
veritabanımızı başlatacağız.

#### 4.1.1 Next.js Uygulamasını Başlatma

Terminalinizi açın, projenizi oluşturmak istediğiniz dizine gidin ve aşağıdaki komutu
çalıştırın. Bu proje için App Router, TypeScript ve Tailwind CSS kullanıyoruz.

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

Bu komut, mevcut dizininizde yeni bir Next.js uygulaması oluşturur.

#### 4.1.2 Bağımlılıkları Yükleme

Sıradaki adım, JWT'leri, veritabanı bağlantılarını ve UUID üretimini yönetecek
kütüphaneleri yüklemek.

```bash
npm install jose mysql2 uuid @types/uuid
```

Bu komut şunları yükler:

- `jose`: JSON Web Token'larını (JWT) imzalamak ve doğrulamak için.
- `mysql2`: Veritabanımız için [MySQL](https://www.corbado.com/blog/passkey-webauthn-database-guide) istemcisi.
- `uuid`: Benzersiz challenge dizeleri oluşturmak için.
- `@types/uuid`: `uuid` kütüphanesi için TypeScript tipleri.

#### 4.1.3 Veritabanını Başlatma

Arka ucumuz, yetkilendirme kodlarını, düzenleme oturumlarını ve verici anahtarlarını
saklamak için bir [MySQL](https://www.corbado.com/blog/passkey-webauthn-database-guide) veritabanı gerektirir.
Bunu kolaylaştırmak için bir `docker-compose.yml` dosyası ekledik.

Depoyu klonladıysanız, sadece `docker-compose up -d` komutunu çalıştırabilirsiniz.
Sıfırdan oluşturuyorsanız, aşağıdaki içeriğe sahip `docker-compose.yml` adında bir dosya
oluşturun:

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

Bu Docker Compose kurulumu ayrıca bir SQL başlatma betiği gerektirir. `sql` adında bir
dizin oluşturun ve içinde, hem doğrulayıcı hem de verici için gerekli tabloları ayarlamak
üzere aşağıdaki içeriğe sahip `init.sql` adında bir dosya oluşturun:

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

-- ISSUER TABLES

-- Table for storing authorization codes in OpenID4VCI flow
CREATE TABLE IF NOT EXISTS authorization_codes (
    id VARCHAR(36) PRIMARY KEY,
    code VARCHAR(255) NOT NULL UNIQUE,
    client_id VARCHAR(255),
    scope VARCHAR(255),
    code_challenge VARCHAR(255),
    code_challenge_method VARCHAR(50),
    redirect_uri TEXT,
    user_pin VARCHAR(10),
    expires_at TIMESTAMP NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    used BOOLEAN DEFAULT FALSE,
    INDEX idx_code (code),
    INDEX idx_expires_at (expires_at)
);

-- Table for storing issuance sessions
CREATE TABLE IF NOT EXISTS issuance_sessions (
    id VARCHAR(36) PRIMARY KEY,
    authorization_code_id VARCHAR(36),
    access_token VARCHAR(255),
    token_type VARCHAR(50) DEFAULT 'Bearer',
    expires_in INT DEFAULT 3600,
    c_nonce VARCHAR(255),
    c_nonce_expires_at TIMESTAMP,
    status ENUM('pending', 'authorized', 'credential_issued', 'expired', 'failed') DEFAULT 'pending',
    user_data JSON,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (authorization_code_id) REFERENCES authorization_codes(id) ON DELETE CASCADE,
    INDEX idx_access_token (access_token),
    INDEX idx_c_nonce (c_nonce),
    INDEX idx_status (status)
);

-- Table for storing issued credentials
CREATE TABLE IF NOT EXISTS issued_credentials (
    id VARCHAR(36) PRIMARY KEY,
    session_id VARCHAR(36),
    credential_id VARCHAR(255),
    credential_type VARCHAR(255) DEFAULT 'jwt_vc',
    doctype VARCHAR(255) DEFAULT 'eu.europa.ec.eudi.pid.1',
    credential_data LONGTEXT, -- Base64 encoded mDoc
    credential_claims JSON,
    issuer_did VARCHAR(255),
    subject_id VARCHAR(255),
    issued_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    expires_at TIMESTAMP,
    revoked BOOLEAN DEFAULT FALSE,
    revoked_at TIMESTAMP NULL,
    FOREIGN KEY (session_id) REFERENCES issuance_sessions(id) ON DELETE CASCADE,
    INDEX idx_credential_id (credential_id),
    INDEX idx_session_id (session_id),
    INDEX idx_doctype (doctype),
    INDEX idx_subject_id (subject_id),
    INDEX idx_issued_at (issued_at)
);

-- Table for storing issuer keys (simplified for demo)
CREATE TABLE IF NOT EXISTS issuer_keys (
    id VARCHAR(36) PRIMARY KEY,
    key_id VARCHAR(255) NOT NULL UNIQUE,
    key_type VARCHAR(50) NOT NULL, -- 'EC', 'RSA'
    algorithm VARCHAR(50) NOT NULL, -- 'ES256', 'RS256', etc.
    public_key TEXT NOT NULL, -- JWK format
    private_key TEXT NOT NULL, -- JWK format (encrypted in production)
    is_active BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_key_id (key_id),
    INDEX idx_is_active (is_active)
);
```

Her iki dosya da yerinde olduğunda, terminalinizi proje kök dizininde açın ve çalıştırın:

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

Bu komut, uygulamamızın kullanıma hazır olması için arka planda bir MySQL container'ı
başlatacaktır.

### 4.2 Paylaşılan Kütüphaneleri Uygulama

API uç noktalarını oluşturmadan önce, temel iş mantığını yönetecek paylaşılan
kütüphaneleri oluşturalım. Bu yaklaşım, API rotalarımızı temiz ve HTTP isteklerini
işlemeye odaklı tutarken, karmaşık işleri bu modüllere devreder.

#### 4.2.1 Veritabanı Kütüphanesi (`src/lib/database.ts`)

Bu dosya, tüm veritabanı etkileşimleri için tek doğruluk kaynağıdır. MySQL container'ımıza
bağlanmak için `mysql2` kütüphanesini kullanır ve tablolarımızdaki kayıtları oluşturmak,
okumak ve güncellemek için bir dizi dışa aktarılmış fonksiyon sağlar. Bu soyutlama
katmanı, kodumuzu daha modüler ve bakımı daha kolay hale getirir.

`src/lib/database.ts` dosyasını aşağıdaki içerikle oluşturun:

```typescript
// src/lib/database.ts
import mysql from "mysql2/promise";

// Database connection configuration
const dbConfig = {
    host: process.env.DATABASE_HOST || "localhost",
    port: parseInt(process.env.DATABASE_PORT || "3306"),
    user: process.env.DATABASE_USER || "app_user",
    password: process.env.DATABASE_PASSWORD || "app_password",
    database: process.env.DATABASE_NAME || "digital_credentials",
    timezone: "+00:00",
};

let connection: mysql.Connection | null = null;

export async function getConnection(): Promise<mysql.Connection> {
    if (!connection) {
        connection = await mysql.createConnection(dbConfig);
    }
    return connection;
}

// Data-Access-Object (DAO) functions for each table
// ... (e.g., createChallenge, getChallenge, createAuthorizationCode, etc.)
```

> **Not:** Kısalık olması açısından, DAO fonksiyonlarının tam listesi çıkarılmıştır. Tam
> kodu
> [proje deposunda](https://github.com/corbado/digital-credentials-example/blob/main/src/lib/database.ts)
> bulabilirsiniz. Bu dosya, challenge'ları, doğrulama oturumlarını, yetkilendirme
> kodlarını, düzenleme oturumlarını ve verici anahtarlarını yönetmek için fonksiyonlar
> içerir.

#### 4.2.2 Kripto Kütüphanesi (`src/lib/crypto.ts`)

Bu dosya, güvenlik açısından kritik tüm kriptografik işlemleri yönetir. Anahtar çiftleri
oluşturmak ve JSON Web Token'larını (JWT) imzalamak için `jose` kütüphanesini kullanır.

**Anahtar Oluşturma** `generateIssuerKeyPair` fonksiyonu, kimlik bilgilerini imzalamak
için kullanılacak yeni bir Eliptik Eğri anahtar çifti oluşturur. Açık anahtar, `did.json`
belgemizde yayınlanabilmesi için JSON Web Key (JWK) formatında dışa aktarılır.

```typescript
// src/lib/crypto.ts
import { generateKeyPair, exportJWK, SignJWT } from "jose";

export async function generateIssuerKeyPair(keyId: string, issuerDid: string) {
    const { publicKey, privateKey } = await generateKeyPair("ES256", {
        crv: "P-256",
        extractable: true,
    });

    const publicKeyJWK = await exportJWK(publicKey);
    publicKeyJWK.kid = keyId; // Assign a unique key ID

    // ... (private key export and other setup)

    return { publicKey, privateKey, publicKeyJWK /* ... */ };
}
```

**JWT Kimlik Bilgisi Oluşturma** `createJWTVerifiableCredential` fonksiyonu, düzenleme
sürecinin çekirdeğidir. Kullanıcının beyanlarını, vericinin anahtar çiftini ve diğer meta
verileri alır ve bunları imzalı bir JWT-VC oluşturmak için kullanır.

```typescript
// src/lib/crypto.ts

export async function createJWTVerifiableCredential(
    claims: MDocClaims,
    issuerKeyPair: IssuerKeyPair,
    subjectId: string,
    audience: string,
): Promise<string> {
    const now = Math.floor(Date.now() / 1000);
    const oneYear = 365 * 24 * 60 * 60;

    const vcPayload = {
        // The issuer's DID
        iss: issuerKeyPair.issuerDid,
        // The subject's (holder's) DID
        sub: subjectId,
        // The time the credential was issued (iat) and when it expires (exp)
        iat: now,
        exp: now + oneYear,
        // The Verifiable Credential data model
        vc: {
            "@context": [
                "https://www.w3.org/2018/credentials/v1",
                "https://europa.eu/eudi/pid/v1",
            ],
            type: ["VerifiableCredential", "eu.europa.ec.eudi.pid.1"],
            issuer: issuerKeyPair.issuerDid,
            issuanceDate: new Date(now * 1000).toISOString(),
            credentialSubject: {
                id: subjectId,
                ...claims,
            },
        },
    };

    // Sign the payload with the issuer's private key
    return await new SignJWT(vcPayload)
        .setProtectedHeader({
            alg: issuerKeyPair.algorithm,
            kid: issuerKeyPair.keyId,
            typ: "JWT",
        })
        .sign(issuerKeyPair.privateKey);
}
```

Bu fonksiyon, JWT yükünü W3C Doğrulanabilir Kimlik Bilgileri Veri Modeli'ne göre oluşturur
ve vericinin özel anahtarıyla imzalayarak güvenli ve doğrulanabilir bir kimlik bilgisi
üretir.

### 4.2 Next.js Uygulamasının Mimari Genel Bakışı

Next.js uygulamamız, aynı projenin parçası olmalarına rağmen ön uç ve arka uç arasındaki
endişeleri ayırmak için yapılandırılmıştır. Bu, hem UI sayfaları hem de API uç noktaları
için App Router'dan yararlanılarak elde edilir.

- **Ön Uç (`src/app/issue/page.tsx`):** `/issue` rotası için kullanıcı arayüzünü
  tanımlayan tek bir [React](https://www.corbado.com/blog/react-passkeys) sayfa bileşeni. Kullanıcı girişini
  yönetir ve arka uç API'mizle iletişim kurar.

- **Arka Uç API Rotaları (`src/app/api/...`):**
    - **Keşif (`.well-known/.../route.ts`):** Bu rotalar, cüzdanların ve diğer
      istemcilerin vericinin yeteneklerini ve açık anahtarlarını keşfetmesine olanak
      tanıyan halka açık meta veri uç noktalarını sunar.
    - **Düzenleme (`issue/.../route.ts`):** Bu uç noktalar, kimlik bilgisi teklifleri
      oluşturma, token düzenleme ve son kimlik bilgisini imzalama dahil olmak üzere temel
      OpenID4VCI mantığını uygular.
    - **Şema (`schemas/pid/route.ts`):** Bu rota, kimlik bilgisinin yapısını tanımlayan
      JSON şemasını sunar.

- **Kütüphane (`src/lib/`):** Bu dizin, arka uçta paylaşılan yeniden kullanılabilir
  mantığı içerir.
    - `database.ts`: SQL sorgularını soyutlayarak tüm veritabanı etkileşimlerini yönetir.
    - `crypto.ts`: Anahtar oluşturma ve JWT imzalama gibi tüm kriptografik işlemleri
      yönetir.

Bu net ayrım, uygulamayı modüler ve bakımı daha kolay hale getirir.

**Not:** `generateIssuerDid()` fonksiyonu, verici alan adınızla eşleşen geçerli bir
`did:web` döndürmelidir. Dağıtıldığında, `.well-known/did.json` dosyasının,
doğrulayıcıların kimlik bilgilerini doğrulayabilmesi için o alan adında HTTPS üzerinden
sunulması gerekir.

![Next.js Uygulamasının Mimari Genel Bakışı](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_29_151549_6a0aca6477.svg)

### 4.3 Ön Ucu Oluşturma

Ön ucumuz, kullanıcıların yeni bir dijital kimlik bilgisi talep etmeleri için basit bir
form sağlayan tek bir React sayfasıdır. Sorumlulukları şunlardır:

- Kullanıcı verilerini (ad, doğum tarihi vb.) yakalamak.
- Bu verileri, bir kimlik bilgisi teklifi oluşturmak için arka ucumuzdaki API'ye
  göndermek.
- Sonuçta ortaya çıkan QR kodunu ve PIN'i, kullanıcının cüzdanıyla taraması için
  görüntülemek.

Temel mantık, kullanıcı formu gönderdiğinde tetiklenen `handleSubmit` fonksiyonunda ele
alınır.

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

const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setLoading(true);
    setError(null);
    setCredentialOffer(null);

    try {
        // 1. Validate required fields
        if (!userData.given_name || !userData.family_name || !userData.birth_date) {
            throw new Error("Please fill in all required fields");
        }

        // 2. Request a credential offer from the backend
        const response = await fetch("/api/issue/authorize", {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                user_data: userData,
            }),
        });

        if (!response.ok) {
            const errorData = await response.json();
            throw new Error(
                errorData.error_description || "Failed to create credential offer",
            );
        }

        // 3. Set the credential offer in state to display the QR code
        const result = await response.json();
        setCredentialOffer(result);
    } catch (err) {
        const errorMessage = (err as Error).message || "Unknown error occurred";
        setError(errorMessage);
    } finally {
        setLoading(false);
    }
};
```

Bu fonksiyon üç temel eylem gerçekleştirir:

1. **Form verilerini doğrular** ve gerekli tüm alanların doldurulduğundan emin olur.
2. Kullanıcının verileriyle birlikte `/api/issue/authorize` uç noktamıza bir **`POST`
   isteği gönderir**.
3. Arka uçtan alınan kimlik bilgisi teklifiyle **bileşenin durumunu günceller**, bu da
   kullanıcı arayüzünün QR kodunu ve işlem kodunu görüntülemesini tetikler.

Dosyanın geri kalanı, formu ve QR kodu ekranını oluşturmak için standart React kodu
içerir. Tam dosyayı
[proje deposunda](https://github.com/corbado/digital-credentials-example/blob/main/src/app/issue/page.tsx)
görüntüleyebilirsiniz.

### 4.4 Ortamı ve Keşfi Ayarlama

Arka uç API'sini oluşturmadan önce, ortamımızı yapılandırmamız ve keşif uç noktalarını
ayarlamamız gerekir. Bu `.well-known` dosyaları, cüzdanların vericimizi bulması ve onunla
nasıl etkileşim kuracağını anlaması için çok önemlidir.

#### 4.4.1 Ortam Dosyasını Oluşturma

Projenizin kök dizininde `.env.local` adında bir dosya oluşturun ve aşağıdaki satırı
ekleyin. Bu URL, bir mobil cüzdanın ona ulaşabilmesi için halka açık olmalıdır. Yerel
geliştirme için, `localhost`'unuzu dışarıya açmak için
[ngrok](https://www.corbado.com/blog/multi-device-passkey-login-corbado-ngrok) gibi bir tünel hizmeti
kullanabilirsiniz.

```
NEXT_PUBLIC_BASE_URL=http://localhost:3000
```

#### 4.4.2 Keşif Uç Noktalarını Uygulama

Cüzdanlar, bir vericinin yeteneklerini standart `.well-known` URL'lerini sorgulayarak
keşfeder. Bu uç noktalardan üç tane oluşturmamız gerekiyor.

**1. Verici Meta Verileri (`/.well-known/openid-credential-issuer`)**

Bu, OpenID4VCI için birincil keşif dosyasıdır. Cüzdana, uç noktaları, sunduğu kimlik
bilgisi türleri ve desteklenen kriptografik algoritmalar dahil olmak üzere verici hakkında
bilmesi gereken her şeyi anlatır.

`src/app/.well-known/openid-credential-issuer/route.ts` dosyasını oluşturun:

```typescript
// src/app/.well-known/openid-credential-issuer/route.ts
import { NextResponse } from "next/server";

export async function GET() {
    const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3000";

    const issuerMetadata = {
        // The issuer's unique identifier.
        issuer: baseUrl,
        // The URL of the authorization server. For simplicity, our issuer is its own authorization server.
        authorization_servers: [baseUrl],
        // The URL of the credential issuer.
        credential_issuer: baseUrl,
        // The endpoint where the wallet will POST to receive the actual credential.
        credential_endpoint: `${baseUrl}/api/issue/credential`,
        // The endpoint where the wallet exchanges an authorization code for an access token.
        token_endpoint: `${baseUrl}/api/issue/token`,
        // The endpoint for the authorization flow (not used in our pre-authorized flow, but good practice to include).
        authorization_endpoint: `${baseUrl}/api/issue/authorize`,
        // Indicates support for the pre-authorized code flow without requiring client authentication.
        pre_authorized_grant_anonymous_access_supported: true,
        // Human-readable information about the issuer.
        display: [
            {
                name: "Corbado Credentials Issuer",
                locale: "en-US",
            },
        ],
        // A list of the credential types this issuer can issue.
        credential_configurations_supported: {
            "eu.europa.ec.eudi.pid.1": {
                // The format of the credential (e.g., jwt_vc, mso_mdoc).
                format: "jwt_vc",
                // The specific document type, conforming to ISO mDoc standards.
                doctype: "eu.europa.ec.eudi.pid.1",
                // The OAuth 2.0 scope associated with this credential type.
                scope: "eu.europa.ec.eudi.pid.1",
                // Methods the wallet can use to prove possession of its key.
                cryptographic_binding_methods_supported: ["jwk"],
                // Signing algorithms the issuer supports for this credential.
                credential_signing_alg_values_supported: ["ES256"],
                // Proof-of-possession types the wallet can use.
                proof_types_supported: {
                    jwt: {
                        proof_signing_alg_values_supported: ["ES256", "ES384", "ES512"],
                    },
                },
                // Display properties for the credential.
                display: [
                    {
                        name: "Corbado Credential Issuer",
                        locale: "en-US",
                        logo: {
                            uri: `${baseUrl}/logo.png`,
                            alt_text: "EU Digital Identity",
                        },
                        background_color: "#003399",
                        text_color: "#FFFFFF",
                    },
                ],
                // A list of the claims (attributes) in the credential.
                claims: {
                    "eu.europa.ec.eudi.pid.1": {
                        given_name: {
                            mandatory: true,
                            display: [{ name: "Given Name", locale: "en-US" }],
                        },
                        family_name: {
                            mandatory: true,
                            display: [{ name: "Family Name", locale: "en-US" }],
                        },
                        birth_date: {
                            mandatory: true,
                            display: [{ name: "Date of Birth", locale: "en-US" }],
                        },
                    },
                },
            },
        },
        // Authentication methods supported by the token endpoint. 'none' means public client.
        token_endpoint_auth_methods_supported: ["none"],
        // PKCE code challenge methods supported.
        code_challenge_methods_supported: ["S256"],
        // OAuth 2.0 grant types the issuer supports.
        grant_types_supported: [
            "authorization_code",
            "urn:ietf:params:oauth:grant-type:pre-authorized_code",
        ],
    };

    return NextResponse.json(issuerMetadata, {
        headers: {
            "Content-Type": "application/json",
            "Cache-Control": "no-cache, no-store, must-revalidate",
            Pragma: "no-cache",
            Expires: "0",
        },
    });
}
```

**2. OpenID Yapılandırması (`/.well-known/openid-configuration`)**

Bu, daha geniş bir yapılandırma ayrıntıları seti sağlayan standart bir OIDC keşif
belgesidir.

`src/app/.well-known/openid-configuration/route.ts` dosyasını oluşturun:

```typescript
// src/app/.well-known/openid-configuration/route.ts
import { NextResponse } from "next/server";

export async function GET() {
    const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3000";

    const openidConfiguration = {
        // The issuer's unique identifier.
        credential_issuer: baseUrl,
        // The endpoint where the wallet will POST to receive the actual credential.
        credential_endpoint: `${baseUrl}/api/issue/credential`,
        // The endpoint for the authorization flow.
        authorization_endpoint: `${baseUrl}/api/issue/authorize`,
        // The endpoint where the wallet exchanges an authorization code for an access token.
        token_endpoint: `${baseUrl}/api/issue/token`,
        // A list of the credential types this issuer can issue.
        credential_configurations_supported: {
            "eu.europa.ec.eudi.pid.1": {
                format: "jwt_vc",
                scope: "eu.europa.ec.eudi.pid.1",
                cryptographic_binding_methods_supported: ["jwk"],
                credential_signing_alg_values_supported: ["ES256", "ES384", "ES512"],
                proof_types_supported: {
                    jwt: {
                        proof_signing_alg_values_supported: ["ES256", "ES384", "ES512"],
                    },
                },
            },
        },
        // OAuth 2.0 grant types the issuer supports.
        grant_types_supported: [
            "authorization_code",
            "urn:ietf:params:oauth:grant-type:pre-authorized_code",
        ],
        // Indicates support for the pre-authorized code flow.
        pre_authorized_grant_anonymous_access_supported: true,
        // PKCE code challenge methods supported.
        code_challenge_methods_supported: ["S256"],
        // Authentication methods supported by the token endpoint.
        token_endpoint_auth_methods_supported: ["none"],
        // OAuth 2.0 scopes the issuer supports.
        scopes_supported: ["eu.europa.ec.eudi.pid.1"],
    };

    return NextResponse.json(openidConfiguration, {
        headers: {
            "Content-Type": "application/json",
            "Cache-Control": "no-cache, no-store, must-revalidate",
            Pragma: "no-cache",
            Expires: "0",
        },
    });
}
```

**3. DID Belgesi (`/.well-known/did.json`)**

Bu dosya, `did:web` yöntemini kullanarak vericinin açık anahtarını yayınlar ve herkesin
onun tarafından düzenlenen kimlik bilgilerinin imzasını doğrulamasını sağlar.

`src/app/.well-known/did.json/route.ts` dosyasını oluşturun:

```typescript
// src/app/.well-known/did.json/route.ts
import { NextResponse } from "next/server";
import { getActiveIssuerKey } from "../../../lib/database";
import { generateIssuerDid } from "../../../lib/crypto";

export async function GET() {
    const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3000";
    const issuerKey = await getActiveIssuerKey();

    if (!issuerKey) {
        return NextResponse.json(
            { error: "No active issuer key found" },
            { status: 404 },
        );
    }

    const publicKeyJWK = JSON.parse(issuerKey.public_key);
    const didId = generateIssuerDid();
    const didDocument = {
        // The context defines the vocabulary used in the document.
        "@context": [
            "https://www.w3.org/ns/did/v1",
            "https://w3id.org/security/suites/jws-2020/v1",
        ],
        // The DID URI, which is the unique identifier for the issuer.
        id: didId,
        // The DID controller, which is the entity that controls the DID. Here, it's the issuer itself.
        controller: didId,
        // A list of public keys that can be used to verify signatures from the issuer.
        verificationMethod: [
            {
                // A unique identifier for the key, scoped to the DID.
                id: `${didId}#${issuerKey.key_id}`,
                // The type of the key.
                type: "JsonWebKey2020",
                // The DID of the key's controller.
                controller: didId,
                // The public key in JWK format.
                publicKeyJwk: publicKeyJWK,
            },
        ],
        // Specifies which keys can be used for authentication (proving control of the DID).
        authentication: [`${didId}#${issuerKey.key_id}`],
        // Specifies which keys can be used for creating verifiable credentials.
        assertionMethod: [`${didId}#${issuerKey.key_id}`],
        // A list of services provided by the DID subject, such as the issuer endpoint.
        service: [
            {
                id: `${didId}#openid-credential-issuer`,
                type: "OpenIDCredentialIssuer",
                serviceEndpoint: `${baseUrl}/.well-known/openid-credential-issuer`,
            },
        ],
    };

    return NextResponse.json(didDocument, {
        headers: {
            "Content-Type": "application/did+json",
            "Cache-Control": "no-cache, no-store, must-revalidate",
            Pragma: "no-cache",
            Expires: "0",
        },
    });
}
```

> **Neden Önbellekleme Yok?** Bu üç uç noktanın da önbelleklemeyi agresif bir şekilde
> önleyen başlıklar (`Cache-Control: no-cache`, `Pragma: no-cache`, `Expires: 0`)
> döndürdüğünü fark edeceksiniz. Bu, keşif belgeleri için kritik bir güvenlik
> uygulamasıdır. Verici yapılandırmaları değişebilir - örneğin, bir kriptografik anahtar
> döndürülebilir. Bir cüzdan veya istemci `did.json` veya `openid-credential-issuer`
> dosyasının eski bir sürümünü önbelleğe alsaydı, yeni kimlik bilgilerini doğrulayamaz
> veya güncellenmiş uç noktalarla etkileşime giremezdi. İstemcileri her istekte yeni bir
> kopya almaya zorlayarak, her zaman en güncel bilgilere sahip olmalarını sağlarız.

#### 4.4.3 Kimlik Bilgisi Şeması Uç Noktasını Uygulama

Halka açık [altyapımızın](https://www.corbado.com/passkeys-for-critical-infrastructure) son parçası kimlik
bilgisi şeması uç noktasıdır. Bu rota, düzenlediğimiz PID kimlik bilgisinin yapısını, veri
türlerini ve kısıtlamalarını resmi olarak tanımlayan bir JSON Şeması sunar. Cüzdanlar ve
doğrulayıcılar, kimlik bilgisinin içeriğini doğrulamak için bu şemayı kullanabilir.

`src/app/api/schemas/pid/route.ts` dosyasını aşağıdaki içerikle oluşturun:

```typescript
// src/app/api/schemas/pid/route.ts
import { NextResponse } from "next/server";

export async function GET() {
    const schema = {
        $schema: "https://json-schema.org/draft/2020-12/schema",
        $id: "https://example.com/schemas/pid", // Replace with your actual domain
        title: "PID Credential",
        description:
            "A schema for a Verifiable Credential representing a Personal Identification Document (PID).",
        type: "object",
        properties: {
            credentialSubject: {
                type: "object",
                properties: {
                    given_name: { type: "string" },
                    family_name: { type: "string" },
                    birth_date: { type: "string", format: "date" },
                    // ... other properties of the credential subject
                },
                required: ["given_name", "family_name", "birth_date"],
            },
            // ... other top-level properties of a Verifiable Credential
        },
    };

    return NextResponse.json(schema, {
        headers: {
            "Content-Type": "application/schema+json",
            "Access-Control-Allow-Origin": "*", // Allow cross-origin requests
        },
    });
}
```

> **Not:** Bir PID kimlik bilgisi için JSON Şeması oldukça büyük ve ayrıntılı olabilir.
> Kısalık olması açısından, tam şema kısaltılmıştır. Tam dosyayı
> [proje deposunda](https://github.com/corbado/digital-credentials-example/blob/main/src/app/api/schemas/pid/route.ts)
> bulabilirsiniz.

### 4.5 Arka Uç Uç Noktalarını Oluşturma

Ön uç yerinde olduğuna göre, şimdi OpenID4VCI akışını yönetmek için sunucu tarafı
mantığına ihtiyacımız var. Ön ucun çağırdığı ilk uç nokta ile başlayacağız:
`/api/issue/authorize`.

#### 4.5.1 `/api/issue/authorize`: Kimlik Bilgisi Teklifini Oluşturma

Bu uç nokta, kullanıcının verilerini almaktan, güvenli, tek kullanımlık bir kod
oluşturmaktan ve kullanıcının cüzdanının anlayabileceği bir `credential_offer`
oluşturmaktan sorumludur.

İşte temel mantık:

```typescript
// src/app/api/issue/authorize/route.ts
import { NextRequest, NextResponse } from "next/server";
import { v4 as uuidv4 } from "uuid";
import { createAuthorizationCode } from "@/lib/database";

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

        // 1. Validate user data
        if (
            !user_data ||
            !user_data.given_name ||
            !user_data.family_name ||
            !user_data.birth_date
        ) {
            return NextResponse.json({ error: "missing_user_data" }, { status: 400 });
        }

        // 2. Generate a pre-authorized code and a PIN
        const code = uuidv4();
        const expiresAt = new Date(Date.now() + 10 * 60 * 1000); // 10 minutes
        const txCode = Math.floor(1000 + Math.random() * 9000).toString(); // 4-digit PIN

        // 3. Store the code and user data
        await createAuthorizationCode(uuidv4(), code, expiresAt);
        // Note: This uses an in-memory store for demo purposes only.
        // In production, persist data securely in a database with proper expiry.
        if (!(global as any).userDataStore) (global as any).userDataStore = new Map();
        (global as any).userDataStore.set(code, user_data);
        if (!(global as any).txCodeStore) (global as any).txCodeStore = new Map();
        (global as any).txCodeStore.set(code, txCode);

        // 4. Create the credential offer object
        const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3000";
        const credentialOffer = {
            // The issuer's identifier, which is its base URL.
            credential_issuer: baseUrl,
            // An array of credential types the issuer is offering.
            credential_configuration_ids: ["eu.europa.ec.eudi.pid.1"],
            // Specifies the grant types the wallet can use.
            grants: {
                // We are using the pre-authorized code flow.
                "urn:ietf:params:oauth:grant-type:pre-authorized_code": {
                    // The one-time code the wallet will exchange for a token.
                    "pre-authorized_code": code,
                    // Indicates that the user must enter a PIN (tx_code) to redeem the code.
                    user_pin_required: true,
                },
            },
        };

        // 5. Create the full credential offer URI (a deep link for wallets)
        const credentialOfferUri = `openid-credential-offer://?credential_offer=${encodeURIComponent(
            JSON.stringify(credentialOffer),
        )}`;

        // The final response to the frontend.
        return NextResponse.json({
            // The deep link for the QR code.
            credential_offer_uri: credentialOfferUri,
            // The raw pre-authorized code, for display or manual entry.
            pre_authorized_code: code,
            // The 4-digit PIN the user must enter in their wallet.
            tx_code: txCode,
        });
    } catch (error) {
        console.error("Authorization error:", error);
        return NextResponse.json({ error: "server_error" }, { status: 500 });
    }
}
```

Bu uç noktadaki temel adımlar:

1. **Veriyi Doğrulama:** İlk olarak gerekli kullanıcı verilerinin mevcut olduğundan emin
   olur.
2. **Kodları Oluşturma:** Benzersiz bir `pre-authorized_code` (bir UUID) ve ek bir
   güvenlik katmanı için 4 basamaklı bir `tx_code` (PIN) oluşturur.
3. **Veriyi Saklama:** `pre-authorized_code`, kısa bir son kullanma süresiyle
   veritabanında saklanır. Kullanıcının verileri ve PIN, bellekte koda bağlı olarak
   saklanır.
4. **Teklifi Oluşturma:** OpenID4VCI spesifikasyonuna göre `credential_offer` nesnesini
   oluşturur. Bu nesne, cüzdana vericinin nerede olduğunu, hangi kimlik bilgilerini
   sunduğunu ve bunları almak için gereken kodu söyler.
5. **URI'yi Döndürme:** Son olarak, bir derin bağlantı URI'si
   (`openid-credential-offer://...`) oluşturur ve kullanıcıya göstermek için `tx_code` ile
   birlikte ön uca döndürür.

#### 4.5.2 `/api/issue/token`: Kodu Token ile Değiştirme

Kullanıcı QR kodunu tarayıp PIN'ini girdikten sonra, cüzdan bu uç noktaya bir `POST`
isteği gönderir. Görevi, `pre-authorized_code` ve `user_pin` (PIN) kodunu doğrulamak ve
geçerliyse, kısa ömürlü bir erişim tokenı düzenlemektir.

`src/app/api/issue/token/route.ts` dosyasını aşağıdaki içerikle oluşturun:

```typescript
// src/app/api/issue/token/route.ts
import { NextRequest, NextResponse } from "next/server";
import { v4 as uuidv4 } from "uuid";
import {
    getAuthorizationCode,
    markAuthorizationCodeAsUsed,
    createIssuanceSession,
} from "@/lib/database";

export async function POST(request: NextRequest) {
    try {
        const formData = await request.formData();
        const grant_type = formData.get("grant_type") as string;
        const code = formData.get("pre-authorized_code") as string;
        const user_pin = formData.get("user_pin") as string;

        // 1. Validate the grant type
        if (grant_type !== "urn:ietf:params:oauth:grant-type:pre-authorized_code") {
            return NextResponse.json(
                { error: "unsupported_grant_type" },
                { status: 400 },
            );
        }

        // 2. Validate the pre-authorized code
        const authCode = await getAuthorizationCode(code);
        if (!authCode) {
            return NextResponse.json(
                {
                    error: "invalid_grant",
                    error_description: "Invalid or expired code",
                },
                { status: 400 },
            );
        }

        // 3. Validate the PIN (tx_code)
        const expectedTxCode = (global as any).txCodeStore?.get(code);
        if (expectedTxCode !== user_pin) {
            return NextResponse.json(
                { error: "invalid_grant", error_description: "Invalid PIN" },
                { status: 400 },
            );
        }

        // 4. Generate access token and c_nonce
        const accessToken = uuidv4();
        const cNonce = uuidv4();
        const cNonceExpiresAt = new Date(Date.now() + 5 * 60 * 1000); // 5 minutes

        // 5. Create a new issuance session
        const userData = (global as any).userDataStore?.get(code);
        await createIssuanceSession(
            uuidv4(),
            authCode.id,
            accessToken,
            cNonce,
            cNonceExpiresAt,
            userData,
        );

        // 6. Mark the code as used and clean up temporary data
        await markAuthorizationCodeAsUsed(code);
        (global as any).txCodeStore?.delete(code);
        (global as any).userDataStore?.delete(code);

        // 7. Return the access token response
        return NextResponse.json({
            access_token: accessToken,
            token_type: "Bearer",
            expires_in: 3600, // 1 hour
            c_nonce: cNonce,
            c_nonce_expires_in: 300, // 5 minutes
        });
    } catch (error) {
        console.error("Token endpoint error:", error);
        return NextResponse.json({ error: "server_error" }, { status: 500 });
    }
}
```

Bu uç noktadaki temel adımlar:

1. **Grant Türünü Doğrulama:** Cüzdanın doğru `pre-authorized_code` grant türünü
   kullandığından emin olur.
2. **Kodu Doğrulama:** `pre-authorized_code`'un veritabanında mevcut olduğunu, süresinin
   dolmadığını ve daha önce kullanılmadığını kontrol eder.
3. **PIN'i Doğrulama:** Kullanıcının işlemi yetkilendirdiğinden emin olmak için cüzdandan
   gelen `user_pin`'i daha önce sakladığımız `tx_code` ile karşılaştırır.
4. **Token'ları Oluşturma:** Güvenli bir `access_token` ve kimlik bilgisi uç noktasına
   yönelik tekrar saldırılarını önlemek için tek kullanımlık bir değer olan `c_nonce`
   (credential nonce) oluşturur.
5. **Oturum Oluşturma:** Erişim tokenını kullanıcının verileriyle ilişkilendirerek
   veritabanında yeni bir `issuance_sessions` kaydı oluşturur.
6. **Kodu Kullanıldı Olarak İşaretleme:** Aynı teklifin iki kez kullanılmasını önlemek
   için `pre-authorized_code`'u kullanıldı olarak işaretler.
7. **Token'ı Döndürme:** `access_token` ve `c_nonce`'ı cüzdana döndürür.

#### 4.5.3 `/api/issue/credential`: İmzalı Kimlik Bilgisini Düzenleme

Bu, son ve en önemli uç noktadır. Cüzdan, `/token` uç noktasından aldığı erişim tokenını
kullanarak bu rotaya kimliği doğrulanmış bir `POST` isteği yapar. Bu uç noktanın görevi,
son doğrulamayı yapmak, kriptografik olarak imzalanmış kimlik bilgisini oluşturmak ve
cüzdana geri döndürmektir.

`src/app/api/issue/credential/route.ts` dosyasını aşağıdaki içerikle oluşturun:

```typescript
// src/app/api/issue/credential/route.ts
import { NextRequest, NextResponse } from "next/server";
import { v4 as uuidv4 } from "uuid";
import {
    getIssuanceSessionByToken,
    updateIssuanceSession,
    createIssuedCredential,
    getActiveIssuerKey,
} from "@/lib/database";
import {
    createJWTVerifiableCredential,
    importIssuerKeyPair,
    generateIssuerDid,
} from "@/lib/crypto";

export async function POST(request: NextRequest) {
    try {
        // 1. Validate the Bearer token
        const authHeader = request.headers.get("authorization");
        const accessToken = authHeader?.substring(7);
        const session = await getIssuanceSessionByToken(accessToken);

        if (!session) {
            return NextResponse.json({ error: "invalid_token" }, { status: 401 });
        }

        // 2. Get the user data from the session
        const userData = session.user_data;
        if (!userData) {
            return NextResponse.json({ error: "missing_user_data" }, { status: 400 });
        }

        // 3. Get the active issuer key
        const issuerKey = await getActiveIssuerKey();
        if (!issuerKey) {
            // In a real application, you would have a more robust key management system.
            // For this demo, we can generate a key on the fly if one doesn't exist.
            // This part is omitted for brevity but is in the repository.
            return NextResponse.json(
                {
                    error: "server_error",
                    error_description: "Failed to get issuer key",
                },
                { status: 500 },
            );
        }

        // 4. Create the JWT-VC
        const issuerDid = generateIssuerDid();
        const keyPair = await importIssuerKeyPair(
            issuerKey.key_id,
            issuerKey.public_key,
            issuerKey.private_key,
            issuerDid,
        );
        const subjectId = `did:example:${uuidv4()}`;
        const credentialData = await createJWTVerifiableCredential(
            userData,
            keyPair,
            subjectId,
            process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3000",
        );

        // 5. Store the issued credential in the database
        await createIssuedCredential(/* ... credential details ... */);
        await updateIssuanceSession(session.id, "credential_issued");

        // 6. Return the signed credential
        return NextResponse.json({
            format: "jwt_vc",
            credential: credentialData,
            c_nonce: uuidv4(), // A new nonce for subsequent requests
            c_nonce_expires_in: 300,
        });
    } catch (error) {
        console.error("Credential endpoint error:", error);
        return NextResponse.json({ error: "server_error" }, { status: 500 });
    }
}
```

Bu uç noktadaki temel adımlar:

1. **Token'ı Doğrulama:** `Authorization` başlığında geçerli bir `Bearer` tokenı olup
   olmadığını kontrol eder ve aktif düzenleme oturumunu bulmak için kullanır.
2. **Kullanıcı Verilerini Alma:** Token oluşturulduğunda oturumda saklanan kullanıcının
   beyan verilerini alır.
3. **Verici Anahtarını Yükleme:** Vericinin aktif imzalama anahtarını veritabanından
   yükler. Gerçek dünya senaryosunda, bu güvenli bir anahtar yönetim sistemi tarafından
   yönetilirdi.
4. **Kimlik Bilgisini Oluşturma:** JWT-VC'yi oluşturmak ve imzalamak için
   `src/lib/crypto.ts` dosyasındaki `createJWTVerifiableCredential` yardımcımızı çağırır.
5. **Düzenlemeyi Kaydetme:** Denetim ve iptal amaçları için düzenlenen kimlik bilgisinin
   bir kaydını veritabanına kaydeder.
6. **Kimlik Bilgisini Döndürme:** İmzalı kimlik bilgisini bir JSON yanıtıyla cüzdana
   döndürür. Cüzdan daha sonra bunu güvenli bir şekilde saklamaktan sorumludur.

## 5. Vericiyi Çalıştırma ve Sonraki Adımlar

Artık bir dijital kimlik bilgisi vericisinin eksiksiz, uçtan uca bir uygulamasına
sahipsiniz. İşte onu yerel olarak nasıl çalıştıracağınız ve bir kavram kanıtından üretime
hazır bir uygulamaya taşımak için neleri göz önünde bulundurmanız gerektiği.

### 5.1 Örnek Nasıl Çalıştırılır

1. **Depoyu Klonlayın:**

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

2. **Bağımlılıkları Yükleyin:**

    ```bash
    npm install
    ```

3. **Veritabanını Başlatın:** Docker'ın çalıştığından emin olun, ardından MySQL
   container'ını başlatın:

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

4. **Ortamı Yapılandırın ve Tüneli Çalıştırın:** Bu, yerel test için en kritik adımdır.
   Mobil cüzdanınızın geliştirme makinenize internet üzerinden bağlanması gerektiğinden,
   yerel sunucunuzu halka açık bir HTTPS URL'si ile açığa çıkarmalısınız. Bunun için
   `ngrok` kullanacağız.

    a. **ngrok'u başlatın:**

    ```bash
    ngrok http 3000
    ```

    b. [ngrok](https://www.corbado.com/blog/multi-device-passkey-login-corbado-ngrok) çıktısından **HTTPS
    URL'sini kopyalayın** (ör. `https://random-string.ngrok.io`).

    c. **`.env.local` dosyası oluşturun** ve URL'yi ayarlayın:

    ```
    NEXT_PUBLIC_BASE_URL=https://<your-ngrok-url>
    ```

5. **Uygulamayı Çalıştırın:**

    ```bash
    npm run dev
    ```

    Tarayıcınızı `http://localhost:3000/issue` adresinde açın. Artık formu
    doldurabilirsiniz ve oluşturulan QR kodu, mobil cüzdanınızın bağlanmasına ve kimlik
    bilgisini almasına olanak tanıyarak halka açık
    [ngrok](https://www.corbado.com/blog/multi-device-passkey-login-corbado-ngrok) URL'nize doğru bir şekilde
    yönlendirecektir.

### 5.2 HTTPS ve `ngrok`'un Önemi

Dijital kimlik bilgisi protokolleri, güvenlik en üst öncelik olacak şekilde
oluşturulmuştur. Bu nedenle, cüzdanlar neredeyse her zaman güvensiz bir (`http://`)
bağlantı üzerinden bir vericiye bağlanmayı reddedecektir. Tüm süreç, bir **SSL
sertifikası** ile etkinleştirilen güvenli bir **HTTPS** bağlantısına dayanır.

`ngrok` gibi bir tünel hizmeti, tüm trafiği yerel geliştirme sunucunuza yönlendiren
güvenli, halka açık bir HTTPS URL'si (geçerli bir SSL sertifikasıyla) oluşturarak her iki
sorunu da çözer. Cüzdanlar HTTPS gerektirir ve güvensiz (`http://`) uç noktalara
bağlanmayı reddeder. Bu, mobil cihazlarla veya harici webhook'larla etkileşime girmesi
gereken herhangi bir web hizmetini test etmek için temel bir araçtır.

### 5.3 Bu Eğitimin Kapsamı Dışındakiler

Bu örnek, anlaşılmasını kolaylaştırmak için kasıtlı olarak temel düzenleme akışına
odaklanmıştır. Aşağıdaki konular kapsam dışı kabul edilir:

- **Üretime Hazır Güvenlik:** Bu verici
  [eğitim](https://www.corbado.com/tr/blog/react-express-mysql-crud-uygulamasi) amaçlıdır. Bir üretim sistemi,
  veritabanında anahtar saklamak yerine güvenli bir Anahtar Yönetim Sistemi (KMS), sağlam
  hata yönetimi, hız sınırlaması ve kapsamlı denetim günlüğü gerektirir.
- **Kimlik Bilgisi İptali:** Bu kılavuz, düzenlenen kimlik bilgilerini iptal etmek için
  bir mekanizma uygulamamaktadır. Şema gelecekteki kullanım için bir `revoked` bayrağı
  içerse de, burada herhangi bir iptal mantığı sağlanmamıştır.
- **Yetkilendirme Kodu Akışı:** Yalnızca `pre-authorized_code` akışına odaklandık.
  `authorization_code` akışının tam bir uygulaması, bir kullanıcı onay ekranı ve daha
  karmaşık [OAuth 2.0](https://www.corbado.com/glossary/oauth2) mantığı gerektirir.
- **Kullanıcı Yönetimi:** Kılavuz, vericinin kendisi için herhangi bir kullanıcı kimlik
  doğrulaması veya yönetimi içermemektedir. Kullanıcının zaten kimliğinin doğrulandığı ve
  bir kimlik bilgisi almaya yetkili olduğu varsayılmaktadır.

## 6. Sonuç

İşte bu kadar! Birkaç sayfa kodla, artık aşağıdakileri yapan eksiksiz, uçtan uca bir
dijital kimlik bilgisi vericimiz var:

1. Kimlik bilgileri talep etmek için kullanıcı dostu bir ön uç sağlar.
2. Tam OpenID4VCI `pre-authorized_code` akışını uygular.
3. Cüzdan birlikte çalışabilirliği için gerekli tüm keşif uç noktalarını sunar.
4. Güvenli, standartlara uygun bir JWT-Doğrulanabilir Kimlik Bilgisi oluşturur ve imzalar.

Bu kılavuz sağlam bir temel sağlasa da, üretime hazır bir verici, sağlam anahtar yönetimi,
bellek içi depolama yerine kalıcı depolama, kimlik bilgisi iptali ve kapsamlı güvenlik
sıkılaştırması gibi ek özellikler gerektirir. Cüzdan uyumluluğu da değişiklik gösterir;
test için Sphereon [Wallet](https://www.corbado.com/blog/digital-wallet-assurance) önerilir, ancak diğer
cüzdanlar burada uygulanan önceden yetkilendirilmiş akışı desteklemeyebilir. Ancak, temel
yapı taşları ve etkileşim akışı aynı kalacaktır. Bu kalıpları izleyerek, her tür dijital
kimlik bilgisi için güvenli ve birlikte çalışabilir bir verici oluşturabilirsiniz.

## 7. Kaynaklar

İşte bu eğitimde kullanılan veya atıfta bulunulan bazı temel kaynaklar, spesifikasyonlar
ve araçlar:

- **Proje Deposu:**
    - [GitHub'daki Tam Kaynak Kodu](https://github.com/corbado/digital-credentials-example)

- **Anahtar Spesifikasyonlar:**
    - [OpenID for Verifiable Credential Issuance (OpenID4VCI)](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html):
      Temel düzenleme protokolü.
    - [W3C Verifiable Credentials Data Model](https://www.w3.org/TR/vc-data-model/):
      VC'ler için temel standart.
    - [The `did:web` Method](https://w3c-ccg.github.io/did-method-web/): Vericimizin açık
      anahtarı için kullanılan DID yöntemi.

- **Araçlar:**
    - [Sphereon Wallet](https://sphereon.com/wallet/): Bu kılavuzda kullanılan test
      cüzdanı.
    - ngrok: Yerel geliştirme ortamınıza güvenli bir tünel oluşturmak için.

- **Kütüphaneler:**
    - Next.js: Ön uç ve arka ucu oluşturmak için React çerçevesi.
    - [jose](https://github.com/panva/jose): JSON Web Token'larını (JWT) oluşturmak ve
      imzalamak için.
    - [mysql2](https://github.com/sidorares/node-mysql2): [Node.js](https://www.corbado.com/blog/nodejs-passkeys)
      için MySQL istemcisi.
