Next.js, OpenID4VP ve ISO mDoc kullanarak sıfırdan bir dijital kimlik bilgisi doğrulayıcı oluşturmayı öğrenin. Bu adım adım geliştirici rehberi, mobil sürücü belgelerini ve diğer dijital kimlik bilgilerini talep edebilen, alabilen ve doğrulayabilen bir do
Amine
Created: August 20, 2025
Updated: August 21, 2025
See the original blog version in English here.
Çevrimiçi kimlikleri kanıtlamak sürekli bir zorluktur ve bu durum şifrelere ve hassas belgelerin güvenli olmayan kanallar üzerinden paylaşılmasına yol açmaktadır. Bu durum, işletmeler için kimlik doğrulamasını yavaş, pahalı ve dolandırıcılığa açık bir süreç haline getirmiştir. Dijital Kimlik Bilgileri, kullanıcıları verilerinin kontrolüne geri koyarak yeni bir yaklaşım sunar. Bunlar, sürücü belgesinden üniversite diplomasına kadar her şeyi içeren fiziksel bir cüzdanın dijital eşdeğeridir, ancak kriptografik olarak güvenli, gizliliği koruyan ve anında doğrulanabilir olma gibi ek avantajlara sahiptir.
Bu rehber, geliştiricilere Dijital Kimlik Bilgileri için bir doğrulayıcı oluşturmaya yönelik pratik, adım adım bir eğitim sunmaktadır. Standartlar mevcut olsa da, bunların nasıl uygulanacağına dair çok az rehberlik bulunmaktadır. Bu eğitim bu boşluğu doldurarak, tarayıcının yerel Dijital Kimlik Bilgisi API'sini, sunum protokolü için OpenID4VP ve kimlik bilgisi formatı olarak ISO mDoc (örneğin mobil sürücü belgesi) kullanarak bir doğrulayıcının nasıl oluşturulacağını gösterir.
Sonuç, uyumlu bir mobil cüzdandan bir Dijital Kimlik Bilgisini talep edebilen, alabilen ve doğrulayabilen basit ama işlevsel bir Next.js uygulaması olacaktır.
İşte nihai uygulamanın çalışır haldeki hızlı bir görünümü. Süreç dört ana adımdan oluşur:
Adım 1: Başlangıç Sayfası Kullanıcı başlangıç sayfasına gelir ve süreci başlatmak için "Dijital Kimlik ile Doğrula"ya tıklar.
Adım 2: Güven Onayı Tarayıcı kullanıcıdan güven onayı ister. Kullanıcı devam etmek için "Devam Et"e tıklar.
Adım 3: QR Kod Taraması Kullanıcının uyumlu cüzdan uygulamasıyla tarayacağı bir QR kodu görüntülenir.
Adım 4: Çözümlenmiş Kimlik Bilgisi Başarılı doğrulamadan sonra, uygulama çözümlenmiş kimlik bilgisi verilerini görüntüler.
Recent Articles
📝
Dijital Kimlik Bilgisi Doğrulayıcı Nasıl Oluşturulur (Geliştirici Rehberi)
📝
Dijital Kimlik Bilgisi Vericisi Nasıl Oluşturulur (Geliştirici Kılavuzu)
📖
WebAuthn Resident Key: Passkey Olarak Keşfedilebilir Kimlik Bilgileri
🔑
Fiziksel Yaka Kartı Erişimi ve Passkey'ler: Teknik Rehber
🔑
MFA'yı Zorunlu Kılma ve Passkey'lere Geçiş: En İyi Uygulamalar
Dijital kimlik bilgilerinin arkasındaki sihir, üç kilit oyuncuyu içeren basit ama güçlü bir "güven üçgeni" modelinde yatar:
Bir kullanıcı bir hizmete erişmek istediğinde, cüzdanından kimlik bilgisini sunar. Doğrulayıcı daha sonra orijinal yayıncıyla doğrudan iletişime geçmeye gerek kalmadan orijinalliğini anında kontrol edebilir.
Bu merkeziyetsiz kimlik ekosisteminin gelişmesi için, doğrulayıcının rolü kesinlikle kritiktir. Onlar bu yeni güven altyapısının bekçileridir, kimlik bilgilerini tüketen ve onları gerçek dünyada kullanışlı hale getirenlerdir. Aşağıdaki diyagramda gösterildiği gibi, bir doğrulayıcı, sahipten bir kimlik bilgisini talep ederek, alarak ve doğrulayarak güven üçgenini tamamlar.
Eğer bir geliştiriciyseniz, bu doğrulamayı gerçekleştirecek bir hizmet oluşturmak, yeni nesil güvenli ve kullanıcı merkezli uygulamalar için temel bir beceridir. Bu rehber, sizi tam olarak bu süreçte yönlendirmek için tasarlanmıştır. İmza doğrulama ve kimlik bilgisi durumunu kontrol etme gibi temel kavramlardan ve standartlardan adım adım uygulama detaylarına kadar, kendi doğrulanabilir kimlik bilgisi doğrulayıcınızı oluşturmak için bilmeniz gereken her şeyi ele alacağız.
İleri atlamak mı istiyorsunuz? Bu eğitimin tamamlanmış projesini GitHub'da bulabilirsiniz. Klonlayıp kendiniz denemekten çekinmeyin: https://github.com/corbado/digital-credentials-example
Haydi başlayalım.
Başlamadan önce, şunlara sahip olduğunuzdan emin olun:
Şimdi bu ön koşulların her birini, bu mdoc tabanlı doğrulayıcının temelini oluşturan standartlar ve protokollerle başlayarak ayrıntılı olarak ele alacağız.
Doğrulayıcımız aşağıdakiler için oluşturulmuştur:
Standart / Protokol | Açıklama |
---|---|
W3C VC | W3C Doğrulanabilir Kimlik Bilgileri Veri Modeli. Beyanlar, meta veriler ve kanıtlar dahil olmak üzere dijital kimlik bilgileri için standart yapıyı tanımlar. |
SD-JWT | JWT'ler için Seçici Açıklama. Sahiplerin bir kimlik bilgisinden yalnızca belirli beyanları seçici olarak açıklamasına olanak tanıyan, gizliliği artıran JSON Web Token'larına dayalı bir VC formatı. |
ISO mDoc | ISO/IEC 18013-5. Mobil Sürücü Belgeleri (mDL'ler) ve diğer mobil kimlikler için uluslararası standart, çevrimdışı ve çevrimiçi kullanım için veri yapılarını ve iletişim protokollerini tanımlar. |
OpenID4VP | Doğrulanabilir Sunumlar için OpenID. OAuth 2.0 üzerine inşa edilmiş, birlikte çalışabilir bir sunum protokolü. Bir doğrulayıcının kimlik bilgilerini nasıl talep ettiğini ve bir sahibinin cüzdanının bunları nasıl sunduğunu tanımlar. |
Bu eğitim için özel olarak şunları kullanıyoruz:
Kapsam Notu: W3C VC ve SD-JWT'yi daha geniş bir bağlam sağlamak için kısaca tanıtmamıza rağmen, bu eğitim yalnızca OpenID4VP aracılığıyla ISO mDoc kimlik bilgilerini uygular. W3C tabanlı VC'ler bu örnek için kapsam dışıdır.
ISO/IEC 18013-5 mDoc standardı, mobil sürücü belgeleri (mDL'ler) gibi mobil belgeler için yapıyı ve kodlamayı tanımlar. mDoc kimlik bilgileri CBOR ile kodlanmış, kriptografik olarak imzalanmış ve doğrulama için dijital olarak sunulabilir. Doğrulayıcımız bu mdoc kimlik bilgilerini çözmeye ve doğrulamaya odaklanacaktır.
OpenID4VP, OAuth 2.0 ve OpenID Connect üzerine inşa edilmiş, dijital kimlik bilgilerini talep etmek ve sunmak için birlikte çalışabilir bir protokoldür. Bu uygulamada, OpenID4VP şunlar için kullanılır:
Standartları ve protokolleri net bir şekilde anladığımıza göre, doğrulayıcımızı oluşturmak için doğru teknoloji yığınını seçmemiz gerekiyor. Seçimlerimiz, sağlamlık, geliştirici deneyimi ve modern web ekosistemiyle uyumluluk için tasarlanmıştır.
Hem ön uç hem de arka uç kodumuz için TypeScript kullanacağız. JavaScript'in bir üst kümesi olarak, statik tipleme ekler, bu da hataları erken yakalamaya, kod kalitesini artırmaya ve karmaşık uygulamaları yönetmeyi kolaylaştırmaya yardımcı olur. Kimlik bilgisi doğrulaması gibi güvenliğe duyarlı bir bağlamda, tür güvenliği büyük bir avantajdır.
Next.js, tam yığın uygulamalar oluşturmak için sorunsuz, entegre bir deneyim sağladığı için tercih ettiğimiz çerçevedir.
redirect_uri
olarak hareket etmekten sorumludur.Uygulamamız, ön uç ve arka uç için belirli bir kütüphane setine dayanmaktadır:
openid-client
Üzerine Not: Daha gelişmiş, üretim düzeyindeki doğrulayıcılar,
OpenID4VP protokolünü doğrudan arka uçta işlemek için openid-client
kütüphanesini
kullanabilir ve dinamik bir redirect_uri
gibi özellikleri etkinleştirebilir. Bir
redirect_uri
ile sunucu odaklı bir OpenID4VP akışında, openid-client
vp_token
yanıtlarını doğrudan ayrıştırmak ve doğrulamak için kullanılacaktır. Bu eğitim için,
süreci anlamayı kolaylaştıran, gerektirmeyen daha basit, tarayıcı aracılı bir akış
kullanıyoruz.
Bu teknoloji yığını, tarayıcının Dijital Kimlik Bilgisi API'sine ve ISO mDoc kimlik bilgisi formatına odaklanmış, sağlam, tür güvenli ve ölçeklenebilir bir doğrulayıcı uygulaması sağlar.
Doğrulayıcınızı test etmek için, tarayıcının Dijital Kimlik Bilgisi API'si ile etkileşime girebilen bir mobil cüzdana ihtiyacınız var.
Android için sağlam bir OpenID4VP uyumlu test cüzdanı olan CMWallet'ı kullanacağız.
CMWallet Nasıl Kurulur (Android):
Not: Yalnızca güvendiğiniz kaynaklardan APK dosyaları yükleyin. Sağlanan bağlantı resmi proje deposundandır.
Uygulamaya dalmadan önce, doğrulanabilir kimlik bilgilerinin temelini oluşturan kriptografik kavramları anlamak önemlidir. Onları "doğrulanabilir" ve güvenilir kılan budur.
Özünde, bir Doğrulanabilir Kimlik Bilgisi, bir yayıncı tarafından dijital olarak imzalanmış bir dizi beyandır (ad, doğum tarihi vb. gibi). Bir dijital imza iki kritik güvence sağlar:
Dijital imzalar, açık/özel anahtar kriptografisi (asimetrik kriptografi olarak da adlandırılır) kullanılarak oluşturulur. İşte bizim bağlamımızda nasıl çalıştığı:
DID'ler Üzerine Not: Bu eğitimde, yayıncı anahtarlarını DID'ler aracılığıyla çözümlemiyoruz. Üretimde, yayıncılar genellikle açık anahtarları DID'ler veya diğer yetkili uç noktalar aracılığıyla sunar ve doğrulayıcı bunları kriptografik doğrulama için kullanır.
Doğrulanabilir Kimlik Bilgileri genellikle JSON Web Token'ları (JWT'ler) olarak
biçimlendirilir. Bir JWT, iki taraf arasında aktarılacak beyanları temsil etmenin kompakt,
URL-güvenli bir yoludur. İmzalı bir JWT'nin (JWS olarak da bilinir) noktalarla (.
)
ayrılmış üç bölümü vardır:
alg
) gibi belirteç hakkındaki
meta verileri içerir.issuer
, credentialSubject
vb. dahil olmak üzere
Doğrulanabilir Kimlik Bilgisinin (vc
beyanı) gerçek
beyanlarını içerir.// Bir JWT yapısı örneği [Başlık].[Yük].[İmza]
Not: JWT tabanlı Doğrulanabilir Kimlik Bilgileri bu blog yazısının kapsamı dışındadır. Bu uygulama, W3C Doğrulanabilir Kimlik Bilgileri veya JWT tabanlı kimlik bilgileri yerine ISO mDoc kimlik bilgilerine ve OpenID4VP'ye odaklanmaktadır.
Bir doğrulayıcının bir kimlik bilgisinin geçerli olduğunu bilmesi yeterli değildir; aynı zamanda kimlik bilgisini sunan kişinin meşru sahibi olduğunu da bilmesi gerekir. Bu, birinin çalınan bir kimlik bilgisini kullanmasını önler.
Bu, bir Doğrulanabilir Sunum (VP) kullanılarak çözülür. Bir VP, sahibinin kendisi tarafından imzalanan bir veya daha fazla VC etrafındaki bir sarmalayıcıdır.
Akış şu şekildedir:
Doğrulayıcımızın daha sonra iki ayrı imza kontrolü yapması gerekir:
Bu iki seviyeli kontrol, hem kimlik bilgisinin orijinalliğini hem de onu sunan kişinin kimliğini sağlayarak sağlam ve güvenli bir güven modeli oluşturur.
Not: W3C VC ekosisteminde tanımlandığı şekliyle
Doğrulanabilir Sunumlar kavramı bu blog yazısının kapsamı
dışındadır. Buradaki Doğrulanabilir Sunum terimi,
W3C VP'sine benzer şekilde davranan ancak W3C'nin JSON-LD imza
modelinden ziyade ISO mDoc semantiğine dayanan OpenID4VP vp_token
yanıtını ifade eder.
Bu kılavuz, W3C Doğrulanabilir Sunumları veya imza doğrulamaları
yerine ISO mDoc kimlik bilgilerine ve OpenID4VP'ye odaklanmaktadır.
Doğrulayıcı mimarimiz, web uygulamamızı kullanıcının mobil CMWallet'ı ile bağlamak için tarayıcının yerleşik Dijital Kimlik Bilgisi API'sini güvenli bir aracı olarak kullanır. Bu yaklaşım, tarayıcının yerel QR kod görüntülemesini ve cüzdan iletişimini yönetmesine izin vererek akışı basitleştirir.
navigator.credentials.get()
API'sine geçirmek, sonucu almak ve doğrulama için arka uçumuza iletmektir.openid4vp
protokolünü anlar ve yerel olarak bir QR kodu oluşturur. Ardından cüzdanın
bir yanıt döndürmesini bekler.İşte tam ve doğru akışı gösteren bir sıra diyagramı:
Akışın Açıklaması:
/api/verify/start
) çağırır.navigator.credentials.get()
çağrısını yapar.openid4vp
protokolü talebini görür ve yerel olarak
bir QR kodu görüntüler. .get()
promise'i şimdi beklemede.Not: Bu QR kodu akışı masaüstü tarayıcılarda gerçekleşir. Mobil tarayıcılarda
(deneysel bayrak etkinleştirilmiş Android Chrome), tarayıcı aynı cihazdaki uyumlu
cüzdanlarla doğrudan iletişim kurabilir, bu da QR kodu
tarama ihtiyacını ortadan kaldırır. Bu özelliği Android Chrome'da etkinleştirmek için
chrome://flags#web-identity-digital-credentials
adresine gidin ve bayrağı "Etkin"
olarak ayarlayın.
.get()
promise'i
nihayet çözümlenerek sunum yükünü teslim eder./api/verify/finish
uç
noktasına POST eder. Arka uç, nonce'ı ve kimlik bilgisini doğrular.Standartlar, protokoller ve mimari akışı hakkında sağlam bir anlayışa sahip olduğumuza göre, doğrulayıcımızı oluşturmaya başlayabiliriz.
Takip Edin veya Nihai Kodu Kullanın
Şimdi kurulum ve kod uygulamasını adım adım ele alacağız. Doğrudan bitmiş ürüne atlamayı tercih ederseniz, tam projeyi GitHub depomuzdan klonlayabilir ve yerel olarak çalıştırabilirsiniz.
git clone https://github.com/corbado/digital-credentials-example.git
İlk olarak, yeni bir Next.js projesi başlatacağız, gerekli bağımlılıkları kuracağız ve veritabanımızı başlatacağız.
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.
npx create-next-app@latest . --ts --eslint --tailwind --app --src-dir --import-alias "@/*" --use-npm
Bu komut, mevcut dizininizde yeni bir Next.js uygulamasının iskelesini oluşturur.
Ardından, CBOR çözümleme, veritabanı bağlantıları ve UUID oluşturmayı yönetecek kütüphaneleri kurmamız gerekiyor.
npm install cbor-web mysql2 uuid @types/uuid
Bu komut şunları yükler:
cbor-web
: mdoc kimlik bilgisi yükünü çözmek için.mysql2
: Veritabanımız için MySQL istemcisi.uuid
: Benzersiz zorluk dizeleri oluşturmak için.@types/uuid
: uuid
kütüphanesi için TypeScript türleri.Arka ucumuz, OIDC oturum verilerini depolamak için bir MySQL veritabanı gerektirir, bu da
her doğrulama akışının güvenli ve durum bilgisiyle donatılmasını sağlar. 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:
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, gerekli tabloları ayarlamak için aşağıdaki içeriğe sahip
init.sql
adında bir dosya oluşturun:
-- 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) );
Her iki dosya da yerinde olduğunda, terminalinizi proje kökünde açın ve şunu çalıştırın:
docker-compose up -d
Bu komut, arka planda bir MySQL container'ı başlatacaktır.
Next.js uygulamamız, aynı projenin parçası olmalarına rağmen, ön uç ve arka uç arasındaki görevleri ayırmak için yapılandırılmıştır.
src/app/page.tsx
): Doğrulama akışını başlatan ve sonucu görüntüleyen tek
bir React sayfası. Tarayıcının Dijital Kimlik Bilgisi API'si ile
etkileşime girer.src/app/api/verify/...
):
start/route.ts
: OpenID4VP talebini ve bir güvenlik nonce'unu oluşturur.finish/route.ts
: Cüzdandan sunumu alır (tarayıcı aracılığıyla), nonce'ı doğrular
ve kimlik bilgisini çözer.src/lib/
):
database.ts
: Tüm veritabanı etkileşimlerini yönetir (zorluklar oluşturma,
oturumları doğrulama).crypto.ts
: CBOR tabanlı mDoc kimlik bilgisinin çözülmesini yönetir.İşte iç mimariyi gösteren bir diyagram:
Ön ucumuz kasıtlı olarak hafiftir. Birincil sorumluluğu, doğrulama akışı için kullanıcıya dönük bir tetikleyici olarak hareket etmek ve hem arka ucumuzla hem de tarayıcının yerel kimlik bilgisi işleme yetenekleriyle iletişim kurmaktır. Kendi içinde herhangi bir karmaşık protokol mantığı içermez; hepsi delege edilmiştir.
Özellikle, ön uç şunları yönetecektir:
/api/verify/start
'ı çağırır ve cüzdanın tam olarak ne
sunması gerektiğini açıklayan yapılandırılmış bir JSON yükü (protocol
, request
,
state
) alır.navigator.credentials.get()
'e verir./api/verify/finish
uç noktamıza bir POST
isteğinde gönderir.Çekirdek mantık startVerification
fonksiyonundadır:
// src/app/page.tsx const startVerification = async () => { setLoading(true); setVerificationResult(null); try { // 1. Tarayıcının API'yi destekleyip desteklemediğini kontrol et if (!navigator.credentials?.get) { throw new Error("Tarayıcı Credential API'sini desteklemiyor."); } // 2. Arka uçumuzdan bir talep nesnesi iste const res = await fetch("/api/verify/start"); const { protocol, request } = await res.json(); // 3. Bu nesneyi tarayıcıya ver – bu yerel QR kodunu tetikler const credential = await (navigator.credentials as any).get({ mediation: "required", digital: { requests: [ { protocol, // "openid4vp" data: request, // dcql_query, nonce, vb. içerir }, ], }, }); // 4. Cüzdan yanıtını (tarayıcıdan) sunucu tarafı kontrolleri için finish uç noktamıza ilet 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(`Başarılı: ${result.message}`); } else { throw new Error(result.message || "Doğrulama başarısız."); } } catch (err) { setVerificationResult(`Hata: ${(err as Error).message}`); } finally { setLoading(false); } };
Bu fonksiyon, ön uç mantığının dört anahtar adımını gösterir: API desteğini kontrol etme, arka uçtan talebi alma, tarayıcı API'sini çağırma ve sonucu doğrulama için geri gönderme. Dosyanın geri kalanı, durum ve UI oluşturma için standart React şablon kodudur ve bunu GitHub deposunda görüntüleyebilirsiniz.
digital
ve mediation: 'required'
?#navigator.credentials.get()
çağrımızın daha basit örneklerden farklı göründüğünü fark
edebilirsiniz. Bunun nedeni, resmi
W3C Dijital Kimlik Bilgileri API spesifikasyonuna
sıkı sıkıya bağlı kalmamızdır.
digital
Üyesi: Spesifikasyon, tüm dijital kimlik bilgisi taleplerinin bir
digital
nesnesi içinde iç içe olmasını gerektirir. Bu, bu API için açık,
standartlaştırılmış bir ad alanı sağlar, onu diğer kimlik bilgisi türlerinden
(password
veya federated
gibi) ayırır ve çakışmalar olmadan gelecekteki uzantılara
izin verir.
mediation: 'required'
: Bu seçenek, önemli bir güvenlik ve kullanıcı deneyimi
özelliğidir. Kullanıcının kimlik bilgisi talebini onaylamak için bir istemle (örneğin,
biyometrik tarama, PIN girişi veya bir onay ekranı) aktif olarak etkileşimde bulunmasını
zorunlu kılar. Bu olmadan, bir web sitesi potansiyel olarak arka planda kimlik
bilgilerine sessizce erişmeye çalışabilir, bu da önemli bir gizlilik riski oluşturur.
Arabuluculuğu zorunlu kılarak, kullanıcının her zaman kontrol sahibi olmasını ve her
işlem için açık rıza vermesini sağlarız.
React UI yerinde olduğuna göre, şimdi sunucuda ağır işi yapan iki API rotasına ihtiyacımız var:
/api/verify/start
– bir OpenID4VP talebi oluşturur, MySQL'de tek kullanımlık bir
zorluk devam ettirir ve her şeyi tarayıcıya geri verir./api/verify/finish
– cüzdan yanıtını alır, zorluğu doğrular, kimlik bilgisini
doğrular ve çözer ve son olarak UI'ye özlü bir JSON sonucu döndürür./api/verify/start
: OpenID4VP Talebini Oluştur#// 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️⃣ Kısa ömürlü, rastgele bir nonce (zorluk) oluştur 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️⃣ *Ne istediğimizi* açıklayan bir DCQL sorgusu oluştur 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️⃣ Tarayıcının navigator.credentials.get() fonksiyonuna geçirebileceği bir nesne döndür return NextResponse.json({ protocol: "openid4vp", // tarayıcıya hangi cüzdan protokolünü kullanacağını söyler request: { dcql_query: dcqlQuery, // NE sunulacağı nonce: challenge, // yeniden oynatma saldırılarına karşı response_type: "vp_token", response_mode: "dc_api", // cüzdan doğrudan /finish'e POST yapacak }, state: { credential_type: "mso_mdoc", // sonraki kontroller için saklanır nonce: challenge, challenge_id: challengeId, }, }); }
Anahtar parametreler
• nonce
– talep ve yanıtı bağlayan
kriptografik zorluk (yeniden oynatmayı önler). •
dcql_query
– tam olarak ihtiyacımız olan beyanları tanımlayan bir nesne. Bu
kılavuz için, henüz sonuçlandırılmış bir standart olmamasına rağmen, Dijital Kimlik
Bilgisi Sorgu Dili'nin son taslaklarından esinlenerek bir dcql_query
yapısı
kullanıyoruz. • state
– cüzdan tarafından geri yansıtılan keyfi JSON, böylece
veritabanı kaydını arayabiliriz.
src/lib/database.ts
dosyası, zorluklar ve doğrulama oturumları için temel MySQL
işlemlerini (ekleme, okuma, kullanıldı olarak işaretleme) sarmalar. Bu mantığı tek bir
modülde tutmak, veri deposunu daha sonra değiştirmeyi kolaylaştırır.
/api/verify/finish
: Sunumu Doğrula ve Çöz#// 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️⃣ Doğrulanabilir sunum parçalarını çıkar const vpTokenMap = body.vp_token ?? body.data?.vp_token; const state = body.state; const mdocToken = vpTokenMap?.cred1; // bu ID'yi dcqlQuery'de istemiştik if (!vpTokenMap || !state || !mdocToken) { return NextResponse.json( { verified: false, message: "Hatalı biçimlendirilmiş yanıt" }, { status: 400 }, ); } // 2️⃣ Tek kullanımlık zorluk doğrulaması const stored = await getChallenge(state.nonce); if (!stored) { return NextResponse.json( { verified: false, message: "Geçersiz veya süresi dolmuş zorluk" }, { status: 400 }, ); } const sessionId = uuidv4(); await createVerificationSession(sessionId, stored.id); // 3️⃣ (Sözde) kriptografik kontroller – prod'da gerçek mDL doğrulamasıyla değiştirin // Gerçek bir uygulamada, mdoc imzasının yayıncının açık anahtarına karşı // tam kriptografik doğrulamasını yapmak için özel bir kütüphane kullanırdınız. const isValid = mdocToken.length > 0; if (!isValid) { await updateVerificationSession(sessionId, "failed", { reason: "mdoc doğrulaması başarısız", }); return NextResponse.json( { verified: false, message: "Kimlik bilgisi doğrulaması başarısız" }, { status: 400 }, ); } // 4️⃣ Mobil sürücü belgesi (mdoc) yükünü insan tarafından okunabilir JSON'a çöz 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 kimlik bilgisi başarıyla doğrulandı!", credentialData: readable, sessionId, }); }
Cüzdan yanıtındaki önemli alanlar
• vp_token
– cüzdanın döndürdüğü her kimlik bilgisini tutan harita. Demomuz için
vp_token.cred1
'i çekiyoruz. • state
– /start
'ta sağladığımız blobun yankısı;
veritabanı kaydını arayabilmemiz için nonce
'u içerir. • mdocToken
– ISO mDoc'u
temsil eden Base64URL ile kodlanmış bir CBOR yapısı.
Doğrulayıcı, tarayıcıdan bir mdoc kimlik bilgisi aldığında, bu, CBOR ile kodlanmış ikili
veriler içeren bir Base64URL dizesidir. Gerçek beyanları çıkarmak için, finish
uç
noktası src/lib/crypto.ts
'den yardımcı fonksiyonlar kullanarak çok adımlı bir çözme
işlemi gerçekleştirir.
decodeDigitalCredential
fonksiyonu, kodlanmış dizeden kullanılabilir bir nesneye
dönüşümü yönetir:
// src/lib/crypto.ts export async function decodeDigitalCredential(encodedCredential: string) { // 1. Base64URL'yi standart Base64'e dönüştür const base64UrlToBase64 = (input: string) => { let base64 = input.replace(/-/g, "+").replace(/_/g, "/"); const pad = base64.length % 4; if (pad) base64 += "=".repeat(4 - pad); return base64; }; const base64 = base64UrlToBase64(encodedCredential); // 2. Base64'ü ikiliye çöz const binaryString = atob(base64); const byteArray = Uint8Array.from(binaryString, (char) => char.charCodeAt(0)); // 3. CBOR'u çöz const decoded = await cbor.decodeFirst(byteArray); return decoded; }
cbor-web
kütüphanesini kullanır.decodeAllNamespaces
fonksiyonu, ilgili ad alanlarından gerçek beyanları çıkarmak için
çözülmüş CBOR nesnesini daha fazla işler:
// 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 (varsa): 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; }
eu.europa.ec.eudi.pid.1
) çözer.Bu adımları çalıştırdıktan sonra, finish uç noktası mdoc'tan beyanları içeren insan tarafından okunabilir bir nesne elde eder, örneğin:
{ "family_name": "Doe", "given_name": "John", "birth_date": "1990-01-01" }
Bu süreç, doğrulayıcının görüntüleme ve daha fazla işleme için mdoc kimlik bilgisinden gerekli bilgileri güvenli ve güvenilir bir şekilde çıkarabilmesini sağlar.
Finish uç noktası, ön uca minimal bir JSON nesnesi döndürür:
{ "verified": true, "message": "mdoc kimlik bilgisi başarıyla doğrulandı!", "credentialData": { "family_name": "Doe", "given_name": "John", "birth_date": "1990-01-01" } }
Ön uç bu yanıtı startVerification()
içinde alır ve sadece React durumunda saklar,
böylece güzel bir onay kartı oluşturabilir veya bireysel beyanları görüntüleyebiliriz –
örneğin, "Hoş geldiniz, John Doe (doğum tarihi 1990-01-01)!".
Şimdi tarayıcının yerel kimlik bilgisi işleme yeteneklerini kullanan eksiksiz, çalışan bir doğrulayıcınız var. İşte onu yerel olarak nasıl çalıştıracağınız ve bir kavram kanıtından üretime hazır bir uygulamaya nasıl götürebileceğiniz.
Depoyu Klonlayın:
git clone https://github.com/corbado/digital-credentials-example.git cd digital-credentials-example
Bağımlılıkları Yükleyin:
npm install
Veritabanını Başlatın: Docker'ın makinenizde çalıştığından emin olun, ardından MySQL container'ını başlatın:
docker-compose up -d
Uygulamayı Çalıştırın:
npm run dev
Tarayıcınızı http://localhost:3000
adresinde açtığınızda doğrulayıcının UI'sini
görmelisiniz. Artık QR kodunu taramak ve doğrulama akışını tamamlamak için
CMWallet'ınızı kullanabilirsiniz.
Bu eğitim, bir doğrulayıcı için temel yapı taşlarını sağlar. Üretime hazır hale getirmek için, birkaç ek özellik uygulamanız gerekir:
Tam Kriptografik Doğrulama: Mevcut uygulama bir yer tutucu kontrolü
(mdocToken.length > 0
) kullanır. Gerçek dünya senaryosunda, mdoc imzasının
yayıncının açık anahtarına karşı tam kriptografik doğrulamasını
yapmanız gerekir (örneğin, DID'lerini çözerek veya açık anahtar sertifikalarını alarak).
DID çözümleme standartları için
W3C DID Çözümleme spesifikasyonuna bakın.
Yayıncı İptal Kontrolü: Kimlik bilgileri, son kullanma tarihlerinden önce yayıncı tarafından iptal edilebilir. Bir üretim doğrulayıcısı, yayıncı tarafından sağlanan bir iptal listesini veya durum uç noktasını sorgulayarak kimlik bilgisinin durumunu kontrol etmelidir. W3C Doğrulanabilir Kimlik Bilgileri Durum Listesi, kimlik bilgisi iptal listeleri için standardı sağlar.
Sağlam Hata Yönetimi ve Güvenlik: Kapsamlı hata yönetimi, girdi doğrulama, API uç noktalarında hız sınırlama ekleyin ve aktarımdaki verileri korumak için tüm iletişimin HTTPS (TLS) üzerinden olduğundan emin olun. OWASP API Güvenlik Yönergeleri, kapsamlı API güvenlik en iyi uygulamalarını sağlar.
Çoklu Kimlik Bilgisi Türleri Desteği: Yalnızca Avrupa
Dijital Kimliği (EUDI) PID kimlik bilgisinden daha
fazlasını almayı bekliyorsanız, farklı doctype
değerlerini ve kimlik bilgisi
formatlarını işlemek için mantığı genişletin.
W3C Doğrulanabilir Kimlik Bilgileri Veri Modeli,
kapsamlı VC formatı spesifikasyonları sağlar.
Bu örnek, anlaşılmasını kolaylaştırmak için kasıtlı olarak çekirdek tarayıcı aracılı akışa odaklanmıştır. Aşağıdaki konular kapsam dışı kabul edilir:
redirect_uri
kullanarak doğrudan cüzdandan arka
uca iletişim veya dinamik istemci kaydı gibi daha karmaşık OpenID4VP özelliklerini
uygulamıyoruz.Bu temel üzerine inşa ederek ve bu sonraki adımları dahil ederek, kendi uygulamalarınızda dijital kimlik bilgilerini güvenebilen ve doğrulayabilen sağlam ve güvenli bir doğrulayıcı geliştirebilirsiniz.
İşte bu kadar! 250 satırdan az TypeScript ile şimdi uçtan uca bir doğrulayıcımız var:
Üretimde, yer tutucu doğrulamayı tam ISO 18013-5 kontrolleriyle değiştirir, yayıncı iptal sorguları, hız sınırlama, denetim günlüğü ve tabii ki uçtan uca TLS eklersiniz—ancak çekirdek yapı taşları tamamen aynı kalır.
İşte bu eğitimde kullanılan veya referans verilen bazı anahtar kaynaklar, spesifikasyonlar ve araçlar:
Proje Deposu:
Anahtar Spesifikasyonlar:
Araçlar:
Kütüphaneler:
Related Articles
Table of Contents