Parolasız kimlik doğrulama, hem güvenliği hem de kullanıcı deneyimini aynı anda iyileştiren güncel bir yaklaşım. Bu yazıda Next.js tabanlı bir web uygulamasına Passkey (FIDO2/WebAuthn) ile giriş özelliğini nasıl ekleyebileceğinizi, SimpleWebAuthn kütüphanesinden yararlanarak adım adım anlatıyorum. Amaç; phishing’e dayanıklı, cihazlar arası senkronize olabilen ve kullanıcıya tek dokunuşla giriş sağlayan bir çözüm kurmak.
Passkey nedir? Kısaca, kullanıcı adı-parola yerine cihazın güvenli donanımında (veya güvenli modülde) saklanan anahtar çifti ile giriş yapma yöntemidir. Tarayıcı ve cihaz (ör. iPhone, Android, Windows Hello, güvenlik anahtarı) iş birliğiyle çalışır ve WebAuthn standardını uygular.
Gereksinimler ve Kurulum
Başlamadan önce Node.js 18+, Next.js 13+ (App Router veya Pages), HTTPS (geliştirme için localhost’ta istisna), ve bir veritabanına (PostgreSQL, MongoDB, vb.) ihtiyacınız var. Sunucuda SimpleWebAuthn’in server paketi, istemcide ise browser paketi kullanılacak.
npm install @simplewebauthn/server @simplewebauthn/browser
Prod ortamında RP ID (Relying Party ID) alan adınız olmalı (ör. example.com). RP Origin ise protokol + alan adıdır (ör. https://example.com). Local geliştirmede rpID “localhost”, origin ise “http://localhost:3000” olabilir.
Kayıt (Registration) Akışı
Kullanıcı yeni bir passkey oluştururken, sunucu bir “challenge” üretir ve tarayıcıya kayıt seçeneklerini (PublicKeyCredentialCreationOptions) gönderir. Tarayıcı, cihazın güvenli modülünde anahtar çifti oluşturur ve sonucu sunucuya geri yollar. Sunucu, yanıtı doğrular ve kimlik bilgisini (credential) veritabanına kaydeder.
1) Sunucu: Kayıt seçeneklerini üret
generateRegistrationOptions({ rpName, rpID, userID, userName, attestationType: 'none', authenticatorSelection: { residentKey: 'preferred', userVerification: 'preferred' } })
Burada userID rastgele ve stabil bir kimlik olmalı (ör. UUID). Üretilen challenge değerini oturumda saklayın. rpID, alan adınızla birebir uyumlu olmalıdır; alt alan adı farkları doğrulamayı bozabilir.
2) İstemci: navigator.credentials.create()
const att = await startRegistration(options)
@simplewebauthn/browser içindeki startRegistration, tarayıcı API’sini sarmalar. Kullanıcı Touch ID/Face ID/Pin ile onay verir.
3) Sunucu: Kayıt doğrulama
verifyRegistrationResponse({ response, expectedChallenge, expectedOrigin, expectedRPID })
Doğrulama başarılıysa credentialId, publicKey, signCount gibi alanları kullanıcıya bağlı şekilde veritabanınıza kaydedin. Aynı kullanıcıya birden fazla credential tanımlanabilir; cihaz değiştirme senaryoları için faydalıdır.
Giriş (Authentication) Akışı
Giriş için süreç benzer: Sunucu challenge üretir, istemci bir imza üretir, sunucu imzayı doğrular. Burada kullanılacak seçenekler PublicKeyCredentialRequestOptions’tır.
1) Sunucu: Giriş seçenekleri
generateAuthenticationOptions({ rpID, userVerification: 'preferred', allowCredentials: [{ id: credentialId, type: 'public-key' }] })
allowCredentials ile ilgili kullanıcının credentialId’lerini gönderebilirsiniz. Challenge değeri yine oturumda tutulmalı.
2) İstemci: navigator.credentials.get()
const assertion = await startAuthentication(options)
Kullanıcı cihazında biyometrik/doğrulama adımı gerçekleşir ve tarayıcı imzalı yanıtı döner.
3) Sunucu: Giriş doğrulama
verifyAuthenticationResponse({ response, expectedChallenge, expectedOrigin, expectedRPID, authenticator: storedCredential })
Başarılı doğrulamada signCount artışı izlenmeli. Beklenmedik düşüş, olası klonlanmış bir anahtara işaret edebilir.
Next.js’te İstemci Tarafı ve Oturum
Next.js’te App Router kullanıyorsanız /app/api altında iki rota belirlemek (register ve login için “options” ve “verify”) işleri düzenli kılar. İstemci tarafında butona tıklanınca önce “options” endpoint’ine istek atın, ardından dönen opsiyonları startRegistration veya startAuthentication fonksiyonuna verin. Doğrulama başarılıysa sunucudan bir HttpOnly, Secure cookie (ör. JWT veya session id) ayarlayarak oturumu başlatın. Cookie için SameSite=Strict ve kısa ömür tercih edin.
Güvenlik ve Üretim İpuçları
- Origin ve rpID eşleşmesine çok dikkat edin. Port, protokol ve alt alan adı hataları en yaygın sorundur.
- Attestation genellikle “none” seçilir; cihaz tedarikçi doğrulaması gerektiren kurum uygulamalarında “direct” düşünülebilir.
- Kullanıcılara birden fazla passkey kaydetme imkânı verin. Telefon kaybında erişim kapanmasın.
- SSRF, CSRF ve rate limit gibi genel güvenlik önlemlerini ihmal etmeyin.
- Üretimde her zaman HTTPS kullanın; WebAuthn birçok tarayıcıda güvenli bağ zorunludur.
Hata Ayıklama ve Uyumluluk
Chrome, Safari ve Firefox’un güncel sürümlerinde passkey desteği olgunlaştı. Android 14 ve iOS 17 ile birlikte platform passkey’leri cihazlar arası senkronize edilebiliyor. Test sırasında about://webauthn (Chrome) üzerinden sanal güvenlik anahtarı oluşturabilir, geliştirici seçenekleriyle akışı simüle edebilirsiniz. Hata alırsanız tarayıcı konsolundaki NotAllowedError, SecurityError ve InvalidStateError mesajları size yön gösterecektir.
Sonuç
Passkey, modern web uygulamaları için güçlü ve kullanıcı dostu bir kimlik doğrulama yöntemi. Next.js ve SimpleWebAuthn ile birkaç endpoint yazarak hızlıca devreye alabilirsiniz. Doğru rpID/origin ayarı, güvenli cookie yönetimi ve çoklu credential desteği ile hem güvenlik ekibinizi hem kullanıcılarınızı mutlu edecek bir parolasız giriş deneyimi sunabilirsiniz.