Parolasız Gelecek: WebAuthn Passkey Entegrasyonu ile Güvenli Giriş (Next.js Örneği)
Parolalar, yıllardır sızıntıların ve kimlik avı saldırılarının baş aktörü oldu. Passkey (WebAuthn + FIDO2) yaklaşımı, biyometrik doğrulama (Touch ID, Windows Hello, Android Biometrics) veya donanım anahtarı ile tek dokunuşla güvenli oturum açmayı mümkün kılar. Bu yazıda, güncel tarayıcılar ve işletim sistemleriyle uyumlu passkey altyapısını nasıl kuracağınızı, geliştirici bakışıyla adım adım anlatıyorum. Örnek teknoloji olarak Next.js kullanacağız ancak anlatım, API uç noktaları olan her yığını kapsar.
Passkey Nedir? Passkey, kullanıcının cihazında güvenli bir şekilde saklanan asimetrik anahtar çifti ile çalışır. Sunucu yalnızca genel anahtarı tutar, gizli anahtar cihazdan çıkmaz. Oturum açma akışı; origin, RP ID (Relying Party ID) ve kullanıcı doğrulaması gibi bağlamlarla sınırlandırıldığı için kimlik avı ve kimlik bilgisi doldurma saldırılarına karşı son derece dirençlidir.
Neden Passkey?
- Kimlik avına dayanıklı: İmzalama süreci etki alanına (origin) bağlıdır, sahte sitelerde işe yaramaz.
- Kullanıcı deneyimi: Tek dokunuş, yüz tanıma ya da PIN ile saniyeler içinde giriş.
- Çoklu cihaz desteği: iCloud Keychain, Google Password Manager gibi kasalarla cihazlar arası senkronizasyon.
Mimari Özet ve Gereksinimler
Sunucu tarafında challenge üretir, saklar ve istemciye gönderirsiniz. İstemci bu challenge ile cihazdaki authenticator üzerinden kayıt (registration) veya giriş (assertion) akışını tamamlar; dönen yanıtı sunucuya iletir ve sunucu WebAuthn kurallarına göre doğrular. Sunucunuzda TLS zorunlu olmalı (localhost geliştirmenin istisnasıdır). RP ID genellikle alan adınızın köküdür (ör. example.com). Veritabanında kullanıcı, keyId, publicKey, alg, signCount gibi alanlar saklanır.
Kayıt (Registration) Akışı Adımları
1) Kullanıcı e-postasını veya benzersiz kimliğini alıp sunucunuza gönderin. Sunucu, PublicKeyCredentialCreationOptions üretir: challenge (base64url), rp.id, rp.name, user.id, user.name, pubKeyAlgo listesi (örn. -7 ES256, -257 RS256), authenticatorSelection (residentKey, userVerification) ve timeout gibi alanları içerir.
2) İstemci tarafında navigator.credentials.create() ile native WebAuthn API’sini çağırın. Dönen yanıt, attestationObject ve clientDataJSON içerir.
3) Bu verileri base64url ile sunucuya POST edin. Sunucu, attestation’ı doğrular; origin, challenge eşleşmesi, RP ID hash kontrolü ve sertifika zinciri doğrulaması yapar. Başarılıysa kullanıcıya ait publicKey, keyId ve signCount’ı veritabanına kaydeder.
Giriş (Authentication) Akışı Adımları
1) Sunucu, PublicKeyCredentialRequestOptions üretir: challenge, rpId, allowCredentials (kayıtlı anahtarlar) ve userVerification.
2) İstemci, navigator.credentials.get() çağırır ve authenticator’dan imzalı assertion alır: authenticatorData, clientDataJSON ve signature.
3) Sunucu; challenge, origin, rpIdHash doğrular; publicKey ile imzayı denetler ve signCount güncellenir. Ardından oturum açma token’ı veya session başlatılır.
Next.js ile Minimal Örnek Mantığı
- /api/webauthn/register/options: Kullanıcı kimliği ile çağrılır, creationOptions döner ve server-side Session’a challenge yazılır.
- /api/webauthn/register/verify: İstemciden gelen attestation yanıtını doğrular ve anahtarı kaydeder.
- /api/webauthn/login/options: Kullanıcının kayıtlı keyId’lerine göre requestOptions üretir, challenge’ı saklar.
- /api/webauthn/login/verify: Assertion’ı doğrular, session veya JWT üretir.
Doğrulama adımları için community tarafından desteklenen simplewebauthn gibi kütüphaneler süreci kolaylaştırır. İstemci tarafında base64url dönüştürmeleri (ArrayBuffer ⇄ base64url) dikkat ister; hataların çoğu burada çıkar.
En İyi Uygulamalar ve Güvenlik İpuçları
- Origin ve RP ID tutarlılığı: www ile çıplak alan adı karışıklığı yaşamamak için tek tercih belirleyin.
- User Verification: “required” seçeneği güvenliği yükseltir (biyometrik/PIN zorunlu).
- Resident Key (Discoverable Credentials): Kullanıcı adı sormadan doğrudan passkey ile giriş akışı sağlar; UX’i iyileştirir.
- Attestation politikası: Genellikle “none” yeterlidir; donanım güveni gerekiyorsa “indirect” veya belirli CA’lar ile doğrulayın.
- signCount ve Clone Detection: signCount düşerse potansiyel klon tespitine karşı hesabı işaretleyin.
- Yedekleme: Kullanıcıya birden fazla passkey ekletin ve acil kurtarma için e-posta linki veya destek kanalı sunun (parolaya geri dönüş önermeyin).
Sık Karşılaşılan Hatalar
- TypeError: create() ya da get() için beklenen ArrayBuffer yerine base64 string gönderildi. Çözüm: Base64url’ü Uint8Array’e çevirin.
- DOMException NotAllowedError: Kullanıcı iptal etti veya sayfa güvenli değil. HTTPS kullanın ve UI’de açık bilgi verin.
- InvalidStateError: Aynı kullanıcı için aynı authenticator’da ikinci kez kayıt deneniyor. allowCredentials stratejisini gözden geçirin.
SEO ve Üretim Notları
Passkey, WebAuthn ve FIDO2 anahtar kelimelerini sayfadaki başlık ve açıklamalara ekleyin. Schema.org Person ve WebSite şemaları ile login sayfasını işaretlemek, arama motorlarına daha net sinyaller verir. Üretimde HSTS etkinleştirin, doğru CORS politikası tanımlayın ve CSRF koruması kullanın. Reverse proxy arkasında çalışıyorsanız X-Forwarded-Proto başlığının doğruluğunu garanti edin; aksi halde origin eşleşmeleri başarısız olur.
Sonuç
Passkey entegrasyonu, modern web uygulamalarında güvenliği yükseltirken kullanıcı deneyimini ciddi ölçüde iyileştirir. Next.js veya benzeri bir yığınla, birkaç API uç noktası ve iyi kurgulanmış bir doğrulama katmanı ile parolasız girişe geçebilirsiniz. Küçük bir POC ile başlayın, kayıt ve giriş akışlarını uçtan uca test edin, ardından çoklu cihaz ve kurtarma senaryolarını ekleyerek üretime taşıyın. Parolasız gelecek, bugün erişilebilir durumda.