Parola sızıntıları, phishing ve tekrar kullanılan şifreler derken kimlik doğrulama artık güvenliğin en zayıf halkası olmaya başladı. Tam da bu noktada WebAuthn ve Passkey yaklaşımı devreye giriyor. Tarayıcılar ve işletim sistemleri, donanımsal anahtarlar veya biyometrik sensörler aracılığıyla şifresiz, kimlik doğrulama destekli bir deneyim sunuyor. Bu yazıda, Next.js tabanlı bir web uygulamasına WebAuthn (Passkey) entegrasyonunu adım adım, pratik ipuçlarıyla anlatıyorum.
WebAuthn nedir? Özetle, tarayıcınız üzerinde çalışan ve FIDO2 standardıyla uyumlu modern bir kimlik doğrulama API’sidir. Kullanıcı, cihazındaki güvenli öğeler (Secure Enclave, TPM veya güvenlik anahtarı) aracılığıyla bir anahtar çifti oluşturur. Sunucu tarafında yalnızca genel anahtar saklanır; özel anahtar cihazı terk etmez. Böylece phishing riski ciddi ölçüde azalır, parola çalma senaryoları boşa düşer ve kullanıcı deneyimi hızlanır.
Temel kavramlar: Relying Party (RP), uygulamanızın kimlik doğrulamayı kullanan tarafıdır. rpId genellikle alan adınızdır (ör. example.com). origin ise protokol ve alan adını içerir (ör. https://example.com). Bu iki değer, güvenlik için kritik ve tarayıcı doğrulamasının merkezinde yer alır.
Ön koşullar ve gereksinimler: WebAuthn yalnızca HTTPS üzerinde çalışır. Geliştirme ortamı için https://localhost istisnası geçerlidir. Sunucu tarafında FIDO2 doğrulamasını kolaylaştırmak için SimpleWebAuthn gibi bir kütüphane kullanabilirsiniz. Next.js API Routes veya Route Handlers ile kayıt (registration) ve giriş (authentication) uç noktalarını ayırın. Veritabanında kullanıcıya ait credentialId, publicKey, signCount, transports gibi alanları saklayacağınız bir tablo/collection hazır bulunsun.
Kayıt (Registration) akışı nasıl işler? 1) Kullanıcı oturum açmadan önce ya da profil ekranından “Passkey ekle”yi seçer. 2) Sunucu bir challenge üretir ve istemciye PublicKeyCredentialCreationOptions ile gönderir: rp.name, rp.id, user.id, user.name, challenge, pubKeyAlg, attestation vb. 3) Tarayıcıda navigator.credentials.create() çağrılır; cihaz parmak izi/yüz tanıma ya da PIN ile onay ister. 4) Tarayıcıdan dönen attestation/clientDataJSON verileri sunucuya POST edilir. 5) Sunucu, veriyi doğrular; eşleşen rpId, origin, challenge ve imza kontrol edilir. 6) Doğrulama geçerse credentialId ve publicKey veritabanına eklenir. Artık kullanıcı, parolasız girişe hazırdır.
Giriş (Authentication) akışı nasıl işler? 1) Sunucu yeni bir challenge üretir ve istemciye PublicKeyCredentialRequestOptions ile gönderir (allowCredentials ile belirli credentialId’leri belirtebilir ya da disoverable credentials (resident keys) için boş bırakabilirsiniz). 2) Tarayıcıda navigator.credentials.get() çağrılır; kullanıcı cihazında biyometrik doğrulama ile imzalama yapılır. 3) İmza, clientDataJSON ve authenticatorData sunucuya geri gönderilir. 4) Sunucu, saklanan publicKey ile imzayı doğrular; signCount (anti-replay) kontrolünü günceller. 5) Başarılıysa güvenli bir oturum veya token üretir ve kullanıcıyı içeri alır.
rpId ve origin ayarları: rpId, üst düzey alan adınız olmalı (subdomain kullanıyorsanız, rpId genellikle kök alan adıdır). origin ise kesin ve tam eşleşmelidir (ör. https://app.example.com). Geliştirmede localhost kullanıyorsanız origin: https://localhost:3000 gibi olmalı; http yerine https zorunludur. Yanlış rpId ya da origin, en sık karşılaşılan hata nedenidir.
Güvenlik ve UX önerileri: 1) userVerification değerini “required” olarak ayarlayın; böylece biyometrik/PIN doğrulaması garanti altına alınır. 2) attestation genellikle “none” seçilir; gizliliği korur ve gereksiz sertifika karmaşıklığını azaltır. 3) Discoverable credentials (resident keys) ile kullanıcı adı girmeden giriş mümkündür; modern passkey deneyimi için önerilir. 4) Platform (cihaz içi) ve cross-platform (USB/NFC/Bluetooth anahtarlar) seçiminde kullanıcıya rehberlik edin. 5) iCloud Anahtar Zinciri ve Google Password Manager ile multi-device passkey senkronizasyonu sayesinde cihaz değişimlerinde erişim kolaylaşır. 6) Fallback akışı olarak e-posta sihirli linki veya destek süreci tasarlayın; fakat SMS OTP’yi temel kurtarma yolu yapmaktan kaçının.
Çerez ve CORS nüansları: Oturum için çerez kullanıyorsanız, üçüncü taraf senaryolarda SameSite=None; Secure ayarlarına dikkat edin. CSRF token korumasını atlamayın. API uç noktalarında yalnızca gerekli origin’lere izin verin ve yanıtlarınızda Cache-Control: no-store gibi başlıklarla hassas verilerin önbelleğe düşmesini engelleyin.
Sık yapılan hatalar: 1) rpId/origin eşleşmiyor: Tarayıcı reddeder. 2) Base64url kodlama/çözme yanlış: Özellikle credentialId ve ham bayt verilerinde hatalara neden olur. 3) Saat farklılıkları (clock skew): Token ve imza doğrulamaları etkilenebilir. 4) signCount güncellenmiyor: Anti-replay koruması devre dışı kalır. 5) Geliştirmede HTTP kullanma: WebAuthn çalışmaz, her zaman HTTPS.
Performans ve izleme: Kimlik doğrulama isteklerini ayrıntılı log’layın (challenge ID, rpId, origin, tarayıcı, platform). Başarısızlık oranlarını, cihaz türlerini ve geri dönüş sürelerini izlemek sorunları hızla yakalamanızı sağlar. Kullanıcı akışında gereksiz adımları azaltın; “Passkey ile giriş yap” butonunu görünür ve ilk seçenek haline getirin, parolayı ise ikincil yol olarak sunun.
Sonuç: WebAuthn ve Passkey entegrasyonu, hem güvenlik çıtasını yükseltir hem de kullanıcı deneyimini modernize eder. Next.js üzerinde birkaç uç nokta, doğru rpId/origin ayarı ve basit bir istemci çağrısıyla dakikalar içinde çalışan bir şifresiz giriş deneyimi elde edebilirsiniz. Başlangıçta kavramlar yoğun görünse de pratikte süreç net: challenge üret, tarayıcıda kimlik doğrula, imzayı doğrula, oturumu başlat. Bugün başlarsanız, yarın kullanıcılarınız parolasız bir geleceğe geçmiş olur.
.png)


.jpg)
.jpg)
.jpg)
.jpg)
.jpg)

