passkey etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
passkey etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

30 Kasım 2025 Pazar

Passkey (WebAuthn) ile Şifresiz Giriş: Next.js ve SimpleWebAuthn ile Uçtan Uca Kurulum

Passkey (WebAuthn) ile Şifresiz Giriş: Adım Adım Uygulama Rehberi

Parola yorgunluğu, kimlik avı ve SMS tabanlı doğrulamanın zayıflıkları derken, modern web uygulamalarında şifresiz giriş kaçınılmaz hale geldi. Passkey’ler, WebAuthn ve FIDO2 standartlarıyla desteklenen, biyometri veya cihaz kilidiyle korunan anahtar çiftlerine dayanır. Kullanıcı deneyimini hızlandırır, kimlik avına dayanıklıdır ve iCloud Anahtar Zinciri ile Google Password Manager gibi yöneticiler arasında senkronize olabilir. Bu yazıda, Next.js ve SimpleWebAuthn kütüphanesi ile uçtan uca bir passkey (WebAuthn) entegrasyonunu nasıl kuracağınızı adım adım anlatıyorum.

Neden Passkey?

Passkey, kullanıcının cihazında saklanan bir özel anahtar ile sunucunun sakladığı açık anahtarın eşleşmesine dayanır. Parola gönderilmez, dolayısıyla veri tabanı sızıntılarında parolaların çalınması gibi riskler ortadan kalkar. Kullanıcı cihazında biyometri (Face ID, Touch ID), donanım anahtarı (YubiKey) veya cihaz PIN’i ile doğrulama yapar. Tarayıcı ve platform desteği artık yaygınlaştı; Chrome, Safari ve Firefox, iOS/Android ve masaüstü işletim sistemlerinde kullanılabiliyor.

Mimari ve Gereksinimler

WebAuthn iki ana akış içerir: kayıt (registration) ve kimlik doğrulama (authentication). Her akışta sunucu benzersiz bir challenge üretir, istemci tarafında tarayıcı navigator.credentials API’si ile doğrulama cihazıyla imza atılır ve bu yanıt sunucuda doğrulanır. Üretimde HTTPS zorunludur ve RP ID (Relying Party ID) alan adınızla birebir eşleşmelidir (ör. rpID = example.com).

Teknoloji Seçimi

Örnek kurulum için Next.js 14 (Route Handlers veya App Router), sunucu tarafında @simplewebauthn/server, istemci tarafında @simplewebauthn/browser kullanılabilir. Veritabanı olarak Postgres veya bir KV deposu tercih edebilirsiniz. Saklanacak başlıca alanlar: kullanıcı kimliği, credentialID, publicKey, counter ve tercihen cihaz/metaveri.

Kayıt Akışı (Registration)

1) Kullanıcı e-posta/ID ile kayıt başlatır. Sunucu generateRegistrationOptions ile seçenekleri üretir, challenge’ı geçici olarak saklar ve istemciye döner. Örnek:

const opts = generateRegistrationOptions({ rpName: 'Uygulama Adı', rpID: 'example.com', userName: 'ali', userID: 'user-123', attestationType: 'none', authenticatorSelection: { residentKey: 'preferred', userVerification: 'preferred', authenticatorAttachment: 'platform' } });

2) İstemci, tarayıcıda @simplewebauthn/browser yardımıyla kullanıcıdan biyometri izni ister ve kimlik bilgisi oluşturur:

const attResp = await startRegistration(opts);

3) Sunucu, verifyRegistrationResponse ile gelen cevabı doğrular; doğrulama başarılıysa credentialID, publicKey ve counter değerlerini kullanıcıyla ilişkilendirerek kalıcı olarak saklar.

Giriş Akışı (Authentication)

1) Kullanıcı giriş sayfasında e-posta/ID girer veya koşullu UI ile otomatik olarak öneri alır. Sunucu generateAuthenticationOptions ile yeni bir challenge üretir. İsteğe bağlı olarak yalnızca ilgili kullanıcının kayıtlı cihazlarını allowCredentials ile sınırlandırabilirsiniz.

2) İstemci tarafında çağrı yapılır:

const authResp = await startAuthentication(options);

3) Sunucu verifyAuthenticationResponse ile imzayı ve origin/rpID alanlarını doğrular, counter değerini günceller. Başarılıysa oturumu (cookie/JWT) kurar.

Koşullu UI (Conditional Mediation) ile Tek Tık Giriş

Chrome ve destekleyen tarayıcılarda, kullanıcı adı alanı odaktayken passkey önerilerini otomatik gösterebilirsiniz. Basitçe bir email input’unuz varken:

navigator.credentials.get({ publicKey: authOptions, mediation: 'conditional' });

Bunun çalışması için sayfanız HTTPS olmalı, autocomplete öznitelikleri doğru ayarlanmalı ve kullanıcı daha önce passkey kaydetmiş olmalıdır. Bu yöntem giriş sürtünmesini ciddi şekilde azaltır.

Güvenlik İpuçları ve En İyi Uygulamalar

- RP ID alan adınızla aynı olmalı; yerelde test ederken localhost kullanın veya geçerli bir sertifika ile alt alan adı hazırlayın.

- attestationType: 'none' çoğu senaryo için en iyisidir; gereksiz attestation verisi toplamayın.

- userVerification için 'required' yüksek güvenlikli sayfalar için uygundur (örn. ödeme, ayarlar). Genel girişte 'preferred' iyi bir dengedir.

- Kullanıcıların cihaz değiştirme/ekleme senaryoları için birden fazla passkey kaydına izin verin ve kurtarma seçenekleri (e-posta bağlantısı veya destek akışı) sunun.

- Rate limit, yeniden oynatma (replay) engelleme, kaynak (origin) ve challenge ömrünü doğrulamayı ihmal etmeyin.

Hata Ayıklama ve Test

- SecurityError: The operation is insecure genelde HTTPS veya RP ID uyuşmazlığını gösterir.

- NotAllowedError kullanıcı etkileşimi yokken veya işlemi iptal ettiğinde görülür; buton tıklamasıyla tetikleyin.

- Unknown authenticator veya eşleşmeyen credentialID için doğru kullanıcıyla ilişkilendirme yapıldığından emin olun.

- Tarayıcı konsolu ve about://webauthn (Chrome) test araçları ile sanal güvenlik anahtarı oluşturup akışları yerelde deneyebilirsiniz.

Performans ve UX

Passkey akışı minimal JSON veri alışverişine dayanır; SSR ve edge işleme ile gecikmeyi azaltabilirsiniz. Başarılı kayıt sonrası kullanıcının cihazı üzerinde parolayı da “kaldırmayı” önermek, geçişi hızlandırır. Kullanıcıya hangi cihazların kayıtlı olduğunu gösteren bir yönetim ekranı sunmak güveni artırır.

Sonuç

Passkey (WebAuthn) ile şifresiz giriş, güvenliği artırırken kullanıcı deneyimini de basitleştirir. Next.js ve SimpleWebAuthn ile kurulumu birkaç uç noktaya indirgenebilir: kayıt için seçenek üretme/doğrulama ve giriş için seçenek üretme/doğrulama. RP ID, HTTPS ve challenge yönetimi gibi kritik ayrıntılara dikkat ettiğiniz sürece, modern tarayıcılarda hızlı ve güvenli bir oturum altyapısı sağlayabilirsiniz.

25 Kasım 2025 Salı

Passkey ve WebAuthn ile Parolasız Giriş: Adım Adım Entegrasyon Rehberi (2025)

WebAuthn ve Passkey Nedir, Neden Önemli?

Parola yorgunluğu ve oltalama saldırıları, modern uygulamalar için en büyük güvenlik sorunlarından biri. WebAuthn (W3C standardı) ve FIDO2 ile gelen passkey yaklaşımı, kriptografik anahtarlar kullanarak parolasız ve oltalama dirençli oturum açmayı mümkün kılar. Kullanıcılar cihazlarındaki biyometrik doğrulama (Face ID, Touch ID, Windows Hello) veya güvenlik anahtarı (YubiKey) ile giriş yapar. 2025 itibarıyla Chrome, Safari ve Firefox, platformlar arası passkey senkronizasyonunu (iCloud Anahtarlık, Google Password Manager, 1Password) yaygın biçimde destekliyor.

Temel Kavramlar

Relying Party (RP) ID: Genellikle alan adınızdır (ör. example.com). HTTPS zorunludur ve RP ID ile domain eşleşmelidir.

Authenticator: Kimlik doğrulayıcı cihaz. Platform (cihazın kendi biyometri/TPM’i) veya roaming (USB/NFC/BLE güvenlik anahtarı) olabilir.

Attestation ve Assertion: Kayıt (credential üretimi) ve giriş (imzalı kanıt) aşamalarında tarayıcı ile sunucu arasında değiş tokuş edilen verilerin adlarıdır.

Discoverable Credentials (Resident Keys): Kullanıcının kullanıcı adı yazmadan sadece cihaz doğrulamasıyla oturum açmasına olanak tanır; passkey deneyiminin kalbidir.

Entegrasyon Mimarisi ve Akış

WebAuthn, istemci (tarayıcı) ve sunucu arasında iki ana akış tanımlar: (1) Kayıt ve (2) Giriş. Tipik uç noktalar: /webauthn/register/options (sunucu challenge üretir), /webauthn/register/verify (sunucu attestation doğrular), /webauthn/login/options (sunucu challenge üretir), /webauthn/login/verify (sunucu assertion doğrular). Sunucu tarafında kullanıcıya ait credentialID, publicKey (COSE formatında), signCount, transports ve isteğe bağlı userHandle kalıcı olarak saklanır.

Gereksinimler ve Dikkat Edilecekler

- Uygulamanız HTTPS üzerinde çalışmalı. Lokal geliştirme için localhost istisnası var, ancak üretimde sertifika zorunlu.
- RP ID, alt alan adları ile farklılık gösterebilir. Örneğin app.example.com için RP ID’yi example.com seçerseniz, alt alanlar arasında passkey paylaşımı kolaylaşır.
- Sunucuda doğru algoritma setini (ES256 gibi) destekleyin.
- Origin ve RP ID mutlak doğrulanmalı; aksi halde güvenlik modeli bozulur.
- signCount (veya signature counter) replay tespitinde kullanılmalıdır.

Uygulama Örneği: Sunucu ve İstemci

Sunucu teknolojisi fark etmeksizin yaklaşım aynıdır. Node.js için @simplewebauthn/server, tarayıcı tarafı için @simplewebauthn/browser; Java için webauthn4j; .NET için Fido2NetLib yaygın kütüphanelerdir.

Kayıt adımları: 1) Kullanıcı oturum açmış veya e-posta doğrulamış olmalı. 2) Sunucu challenge üretir, rp (name, id), user (id, name), pubKeyCredParams, authenticatorSelection (residentKey=required, userVerification=preferred/required) gibi alanlarla tarayıcıya döner. 3) Tarayıcı navigator.credentials.create({ publicKey: ... }) çağırır. 4) Tarayıcıdan dönen attestation, sunucuda doğrulanır; geçerliyse publicKey kaydedilir.

Giriş adımları: 1) Sunucu challenge üretir ve allowCredentials (istemciye bağlı ise opsiyonel) ile döner. 2) Tarayıcı navigator.credentials.get({ publicKey: ... }) çağırır. 3) Dönen assertion, imza ve authenticatorData sunucuda doğrulanır; signCount güncellenir ve oturum açılır.

Passkey UX İpuçları ve Conditional UI

Modern tarayıcılarda “Conditional UI” desteğiyle, kullanıcı adı alanı odaklanmadan dahi passkey önerisi açılabilir. Tarayıcı tarafında mediation: "conditional" kullanımı, şifre doldurma ile tutarlı bir deneyim sunar. Özellikle mobilde autofill entegrasyonu dönüşüm oranlarını artırır. “Kullanıcı adı olmadan giriş” senaryosu için “discoverable credentials” etkin olmalıdır.

Cihazlar Arası Senkronizasyon ve Kurtarma

Passkey’ler iCloud Keychain, Google Password Manager veya destekleyen parola kasalarında şifrelenmiş biçimde senkronize olabilir. Kullanıcılara en az bir roaming güvenlik anahtarı veya alternatif kurtarma yöntemi önerin. SMS/e-posta yedekleri zorunlu olmamalı; mümkünse TOTP veya ek bir passkey kaydı sağlayın.

Güvenlik ve Uyum

- Attestation politikanızı belirleyin: “none” çoğu tüketici uygulaması için yeterli, kurumsal ortamda AAGUID bazlı kısıtlama gerekebilir.
- Phishing dirençli olması, RP ID sabitlemesi ve kullanıcı doğrulaması (UV) ile sağlanır.
- Rate limiting, origin checking ve replay protection uygulayın.
- Log’larda özel anahtar yer almaz; yalnızca publicKey ve metadata saklanır.

Test, Hata Ayıklama ve Yayına Alma

Geliştirme sürecinde Chrome’un Virtual Authenticator panelini (chrome://webauthn) kullanarak farklı cihaz senaryolarını simüle edebilirsiniz. Staging ortamında gerçek cihazlarla (iOS Safari, Android Chrome, Windows Hello) çapraz test yapın. CDN veya ters proxy arkasında origin/RP ID uyuşmazlıklarına dikkat edin. Son aşamada parola ile hibrit oturum açma bir süre daha açık tutulabilir; ardından parolayı kademeli kaldırma stratejisi izlenebilir.

Sonuç

WebAuthn ve passkey, kullanıcı deneyimini iyileştirirken güvenliği ciddi biçimde artırır. Doğru RP ID, sıkı origin doğrulaması, güvenilir kütüphaneler ve iyi bir kurtarma politikası ile entegrasyon sorunsuz ilerler. Bugün küçük bir pilot başlatıp kullanıcılarınızın en çok kullandığı platformlarda test ederek geçişi adım adım hızlandırabilirsiniz.

21 Kasım 2025 Cuma

Passkeys (FIDO2/WebAuthn) ile Şifresiz Kimlik Doğrulama: Adım Adım Uygulama Rehberi

Passkey nedir ve neden şimdi?

Parola yorgunluğu, kimlik avı ve bot saldırıları modern uygulamaların en büyük tehditlerinden. Passkey, FIDO2 ve WebAuthn standartlarına dayanan, parolasız ve kimlik avına dirençli bir doğrulama yaklaşımıdır. Kullanıcı, cihazındaki biyometri (parmak izi, Face ID) veya PIN ile giriş yapar; tarayıcı, cihazda üretilen özel anahtarı kullanır ve sunucuya sadece genel anahtar gider. Böylece veri ihlallerinde çalınacak parola kalmaz.

Mimariyi bir bakışta anlayın

WebAuthn istemci tarafı (tarayıcı ve platform autentikatörü) ile sunucu arasında çalışır. Kayıtta, sunucu bir “challenge” üretir; tarayıcı bu challenge’ı cihazın güvenli çipinde üretilen özel anahtar ile imzalar ve sunucuya genel anahtar + imza döner. Girişte de benzer şekilde geçici bir challenge imzalanır. Sunucu, daha önce sakladığı genel anahtar ile imzayı doğrular ve kullanıcıyı oturum açmış sayar.

Önkoşullar ve planlama

1) Alan adınız için HTTPS zorunludur. 2) Sunucu tarafında WebAuthn doğrulama yapabilecek bir kütüphane seçin (örn. dilinize uygun FIDO2/WebAuthn paketleri). 3) Kullanıcı akışını planlayın: önce passkey kaydı, ardından giriş. 4) Çoklu cihaz senaryosunu düşünün: senkronize passkey (Apple/Google ekosistemleri) ve güvenlik anahtarlarını (YubiKey vb.) desteklemek isteyebilirsiniz.

Kayıt (Registration) akışı

Adım 1: Sunucu, kullanıcı için bir kayıt challenge’ı ve RP (Relying Party) bilgilerini üretir. Adım 2: Tarayıcıda WebAuthn API’sini çağırarak cihazın bir anahtar çifti oluşturmasını istersiniz. Adım 3: Kullanıcı biyometri veya cihaz PIN’i ile onay verir. Adım 4: Tarayıcı, oluşturulan genel anahtarı, attestation ve ilgili meta verileri sunucuya gönderir. Adım 5: Sunucu, gelen veriyi doğrular; geçerli ise kullanıcı hesabına bu genel anahtarı bağlar. Bu aşamadan sonra kullanıcı, parolasız giriş yapabilecek duruma gelir.

Giriş (Authentication) akışı

Adım 1: Sunucu, giriş yapmak isteyen kullanıcı için bir challenge üretir ve tarayıcıya yollar. Adım 2: Tarayıcı, cihazda saklı özel anahtarı kullanarak challenge’ı imzalar; kullanıcı yine biyometri veya PIN ile onay verir. Adım 3: İmzalı veri sunucuya döner. Adım 4: Sunucu, ilgili genel anahtar ile imzayı doğrular ve başarılıysa oturumu başlatır. Tüm süreçte parola veya tek seferlik SMS kodu gerekmez.

Sunucu tarafı doğrulama ipuçları

Doğrulamada RP ID, origin ve challenge eşleşmelerine dikkat edin. Signature counter (sign count) değerini izleyerek olası anahtar klonlama şüphesini tespit edin. Mümkünse attestation doğrulamasını aktif edin ve yalnızca güvenilir üreticilerin autentikatörlerine izin verin. Kullanıcı başına birden çok passkey kaydını desteklemek, hesap kurtarma ve cihaz değişimi için hayat kurtarır.

Kullanıcı deneyimi: Mikro kılavuz

Kullanıcıları “Parolasız giriş” kavramına kısa bir açıklama ile hazırlayın. Kayıtta hangi yöntemlerin desteklendiğini (cihazın dahili biyometrisi, harici güvenlik anahtarı) net ifade edin. Giriş ekranında “Passkey ile devam et” butonu görünür olmalı; alternatif olarak e-posta ile büyülü bağlantı (magic link) veya yedek kodlar ikinci seçenek olarak sunulabilir. Cihaz desteklemiyorsa anlaşılır hata mesajları verin ve geçici olarak SMS/OTP gibi yöntemlere yönlendirin.

Güvenlik en iyi uygulamalar

- HTTPS her ortamda zorunlu. - CSRF ve oturum sabitleme saldırılarına karşı koruma sağlayın. - Rate limiting ve IP itibar kontrolleri ekleyin. - Kademeli yayına geçin: önce beta kullanıcıları ile test, sonra genelleme. - Attestation politikasını, regülasyon gerekliliklerine (ör. finans, sağlık) uyacak şekilde ayarlayın.

Uyumluluk ve tarayıcı desteği

Modern Chromium, Safari ve Firefox tarayıcıları WebAuthn’ı destekliyor. Mobilde iOS ve Android tarafında platform autentikatörleri olgunlaştı. Eski cihazlarda destek kısıtlı olabilir; bu nedenle özelliği feature detection ile koşullu olarak göstermek iyi bir pratik. Ayrıca kurumsal ağlarda güvenlik anahtarlarını tercih eden ekipler için USB/NFC/Bluetooth seçeneklerini dökümante edin.

Performans ve ölçüm

Kayıt ve giriş sürelerini ölçün; özellikle challenge üretimi ve sunucu doğrulama gecikmelerini izleyin. Hata oranlarını, başarısız biyometri denemelerini ve cihaz uyumluluk dağılımını panolara taşıyın. A/B testleri ile “passkey öncelikli” akışların dönüşüm oranlarını kıyaslayın; genellikle ilk oturum açma süreleri ve tekrar girişler belirgin şekilde hızlanır.

Geçiş stratejisi ve hesap kurtarma

Var olan parola tabanlı sisteme passkey eklerken kademeli geçiş önerilir. Önce iki faktör olarak ekleyin, ardından parolasız moda yükseltin. Hesap kurtarma için ikinci e-posta, güvenlik anahtarı veya doğrulanmış cihazda QR onayı gibi seçenekler hazırlayın. Parola sıfırlama sürecini, passkey kayıtlarını koruyacak şekilde yeniden tasarlayın ve her kritik adımda ek onay isteyin.

Sonuç

Passkeys, parolaların zayıflıklarını ortadan kaldıran, kullanıcı dostu ve güvenliği yüksek bir çözüm sunuyor. Doğru akış tasarımı, sıkı sunucu doğrulaması ve temiz bir UX ile WebAuthn’ı uygulamanıza eklemek düşündüğünüzden daha hızlı olabilir. Bugün küçük bir kullanıcı grubunda başlatın, metrikleri izleyin ve parolasız geleceğe emin adımlarla geçin.

20 Kasım 2025 Perşembe

Passkey (WebAuthn) ile Şifresiz Giriş Uygulaması: Adım Adım Geliştirici Rehberi

Giriş

Şifresiz kimlik doğrulama, hem güvenliği artırmak hem de kullanıcı deneyimini iyileştirmek için son yılların en önemli trendlerinden biri. Passkey (WebAuthn/FIDO2) yaklaşımı, parola sızıntılarını ve kimlik avı risklerini azaltırken tek dokunuşla giriş deneyimi sunuyor. Bu yazıda, modern bir web uygulamasına passkey tabanlı şifresiz girişi nasıl entegre edebileceğinizi adım adım ele alacağız. Anlatım sade, SEO uyumlu ve güncel standartları takip edecek şekilde hazırlandı.

Passkey Nedir ve Neden Önemlidir?

Passkey, FIDO2 ve WebAuthn standartlarının bir ürünüdür. Kullanıcının cihazında güvenli bir şekilde saklanan asimetrik anahtar çifti ile çalışır. Sunucu sadece kullanıcının açık anahtarını tutar; özel anahtar cihazdan çıkmaz. Böylece parola sızıntıları, tekrar kullanım ve kimlik avı (phishing) riskleri büyük ölçüde azalır. Ek olarak, platform doğrulayıcıları (iOS/Android/Windows/macOS) ve bulut senkronizasyonu sayesinde kullanıcı deneyimi neredeyse otomatik doldurma kadar kolay hale gelir.

Temel Kavramlar

Relying Party (RP) ID, genellikle uygulamanızın kök etki alanıdır ve WebAuthn işlemlerinin bağlandığı kimliktir. Origin ise protokol, alan adı ve portun birleşimidir; HTTPS zorunludur. Kayıt sırasında attestation, giriş sırasında assertion adı verilen imzalı veriler üretilir. Sunucu bu verileri doğrular, sayaç (signCount) ile tekrar saldırılarını engeller ve açık anahtarı (public key) güvenli şekilde saklar.

Gereksinimler

Üretimde HTTPS zorunludur; geliştirirken localhost istisna olarak desteklenir. Sunucu tarafında bir WebAuthn doğrulama kütüphanesi kullanmanız pratik olacaktır. Veritabanında kullanıcı kimliği ile eşleştirilmiş credentialId, publicKey, signCount, transports ve tercihe göre attestation bilgilerini saklamalısınız. Tarayıcı tarafında WebAuthn API’sini (navigator.credentials) kullanacaksınız.

Adım 1: RP ID ve Origin Doğrulaması

RP ID, example.com gibi kaydedilebilir kök alan adı olmalıdır. Alt alan adlarında çalışan uygulamalar için RP ID’yi kök alan olarak belirlemek, farklı alt alanlarda aynı passkey’i kullanabilmenizi sağlar. Üretimde https://example.com gibi tam bir origin kullanmalı ve yönlendirmelerinizin origin’i değiştirmediğinden emin olmalısınız.

Adım 2: Kayıt (Registration) Akışı

Önce sunucu, benzersiz bir challenge üretir ve istemciye publicKeyCredentialCreationOptions ile gönderir. Bu seçeneklerde rp, user, challenge, pubKeyAlg (örn. ES256), authenticatorSelection (residentKey ve userVerification tercihleri) ve timeout gibi değerler bulunur. İstemci, navigator.credentials.create({ publicKey: options }) çağrısı ile passkey oluşturur. Elde edilen attestation yanıtını sunucuya gönderirsiniz. Sunucu tarafında imza doğrulanır, RP ID hash’i ve origin kontrol edilir, ardından credentialId, publicKey ve signCount veritabanına kaydedilir. Kullanıcı deneyimi için kayıt sırasında en az iki farklı cihazda passkey oluşturmayı önermek, hesabın kurtarılabilirliğini artırır.

Adım 3: Giriş (Authentication) Akışı

Girişte sunucu yeni bir challenge üretir ve publicKeyCredentialRequestOptions ile istemciye gönderir. Eğer keşfedilebilir kimlik bilgileri (discoverable credentials) kullanıyorsanız kullanıcı adı sormadan doğrudan passkey seçimi mümkün olur. İstemci navigator.credentials.get({ publicKey: options }) çağrısı ile assertion üretir. Sunucu imzayı ve sayaç artışını doğrular; signCount beklenenden düşükse anahtarın çoğaltılmış olabileceğine dair alarm üretir. Başarılı doğrulamadan sonra oturum açılır ve standart oturum yönetimi (cookie veya token) uygulanır.

Adım 4: Conditional UI ve Otomatik Doldurma

Uygun tarayıcılarda “conditional UI” ile kullanıcı adı alanında beklerken passkey önerileri gösterilebilir. Bu deneyim için mediation: "conditional" parametresi ve ilgili tarayıcı desteği gerekir. Kullanıcı adı girmeden tek dokunuşla giriş, dönüşüm oranlarını belirgin biçimde artırabilir.

Güvenlik En İyi Uygulamaları

HTTPS her zaman zorunlu olmalıdır. userVerification’ı mümkünse required yaparak biyometrik/PIN doğrulaması isteyin. COSE algoritmalarını ES256 gibi modern seçeneklerle sınırlayın. Timeout değerlerini gerçekçi tutun ve hataları ayrıntılı ama saldırgana ipucu vermeyecek şekilde loglayın. RP ID ve origin uyuşmazlıkları, SecurityError hatalarının en yaygın sebebidir; alan adı, www/alt alan ayrımları ve proxy ayarlarını dikkatle kontrol edin.

Veri Tabanı Tasarımı

Kullanıcı başına birden çok credential saklayın. Her kayıt için credentialId (ikili/BASE64URL), publicKey (COSE/PEM), signCount (tamsayı), transports (usb, nfc, ble, internal), aaguid ve oluşturulma tarihi alanları tutarlı bir şema oluşturur. Silinen veya kaybolan cihazlar için güvenli kaldırma (soft delete) ve denetim kayıtları (audit log) eklemeniz önerilir.

UX İpuçları

Kullanıcıyı bilgilendiren net mikro metinler kullanın: “Cihaz kilidini açın ve dokunun” gibi. Başarısız denemelerde net ama güvenli hata mesajları verin. İlk kayıt sonrasında “Başka bir cihaz ekle” önerisi sunarak kurtarma olasılığını artırın. Passkey’i mevcut parolalı hesaplara ek ikinci faktör olarak başlatıp zamanla parolayı kaldırma stratejisi de uygulanabilir.

Hata Ayıklama ve Yaygın Sorunlar

NotAllowedError genellikle kullanıcı işlemi iptal ettiğinde veya zaman aşımında görülür. SecurityError RP ID/origin uyumsuzluğunu işaret eder. UnknownError donanım doğrulayıcı veya platform politikası sorunlarına işaret edebilir. Windows Hello için cihazda PIN/Biyometri etkin olmalı; mobilde ekran kilidi şarttır.

Sonuç

Passkey (WebAuthn) ile şifresiz giriş, modern web projelerinde güvenliği artırırken sürtünmeyi azaltır. Doğru RP ID, güvenli sunucu doğrulaması, kullanıcı dostu akış ve çok cihazlı strateji ile hem ölçeklenebilir hem de dayanıklı bir kimlik doğrulama katmanı kurabilirsiniz. Bugünden küçük bir pilotla başlayıp kullanıcı geri bildirimlerine göre geliştirmek, geçiş maliyetini düşürmenin en verimli yoludur.

19 Kasım 2025 Çarşamba

Next.js ile Passkey (WebAuthn/FIDO2) Entegrasyonu: Şifresiz Giriş Nasıl Eklenir?

Özet: Passkey (WebAuthn/FIDO2) ile şifresiz kimlik doğrulama, kimlik avına dayanıklı ve kullanıcı deneyimi yüksek modern bir giriş yöntemidir. Bu rehberde, Next.js üzerinde passkey kaydı ve giriş akışını adım adım nasıl kuracağınızı anlatıyorum. Amaç, pratik bir örnekle üretime yakın bir mimari oluşturmak ve SEO dostu bir içerikle temel kavramları sade şekilde aktarmaktır.

Passkey Nedir ve Neden Önemli?

Passkey, kullanıcıların parolasız şekilde oturum açmasını sağlayan, cihazın güvenli donanım modülü veya güvenli alanında saklanan FIDO2/WebAuthn kimlik bilgilerine dayanan bir yöntemdir. Phishing’e karşı dirençlidir, ikinci faktör ihtiyacını ortadan kaldırabilir ve kullanıcı tarafında dokunma/yüz tanıma gibi biyometrik doğrulamalarla çalışır. iOS, Android, Windows ve macOS’ta platform desteği geniştir.

Gereksinimler

- Next.js 13/14 (App Router veya Pages Router)
- Node.js 18+
- HTTPS (geliştirmede localhost istisnası vardır)
- Sunucu tarafında oturum (session) veya kısa süreli challenge saklama mekanizması
- Bir veritabanı (ör. PostgreSQL, SQLite veya MongoDB)

Mimari ve Terimler

RP ID (Relying Party ID): Genellikle alan adınızın eTLD+1 formatı (ör. example.com). Localhost için rpID: localhost kullanılabilir.
Challenge: Sunucuda üretilen tek seferlik rastgele dize; hem kayıt hem girişte imzalanır ve doğrulanır.
Credential: Kullanıcıya ait public key, credentialId, counter ve transports gibi bilgilerden oluşur.

Kurulum

Next.js projesi oluşturun ve gerekli paketleri ekleyin:

npx create-next-app passkey-app
cd passkey-app
npm i @simplewebauthn/server @simplewebauthn/browser base64url

Veritabanında şu alanlara ihtiyacınız olacak: userId, credentialId (Buffer/Base64URL), publicKey (PEM/COSE), counter (number), transports (string[]). Örnek bir tablo/collection şeması oluşturun.

Kayıt (Registration) Akışı

1) Sunucu tarafında kayıt seçeneklerini üretin ve challenge’ı oturumda saklayın:

// app/api/webauthn/register-options/route.ts
import { generateRegistrationOptions } from '@simplewebauthn/server';

export async function GET() {
  const rpID = process.env.RP_ID || 'localhost';
  const user = { id: 'u_123', name: '[email protected]', displayName: 'Ali' };

  const options = await generateRegistrationOptions({
    rpName: 'Passkey Demo',
    rpID,
    userID: user.id,
    userName: user.name,
    userDisplayName: user.displayName,
    attestationType: 'none',
    authenticatorSelection: {
      residentKey: 'preferred',
      userVerification: 'required',
      authenticatorAttachment: 'platform',
    },
  });

  // TODO: session.set('currentChallenge', options.challenge)
  return Response.json(options);
}

2) İstemci tarafında kullanıcının cihazında passkey oluşturun:

// app/register/page.tsx
'use client';
import { startRegistration } from '@simplewebauthn/browser';

export default function Register() {
  const onRegister = async () => {
    const opts = await fetch('/api/webauthn/register-options').then(r => r.json());
    const attRes = await startRegistration(opts);
    await fetch('/api/webauthn/register-verify', {
      method: 'POST',
      body: JSON.stringify(attRes),
    });
  };
  return <button onClick={onRegister}>Passkey Oluştur</button>;
}

3) Sunucu tarafında yanıtı doğrulayın ve kimlik bilgilerini veritabanına kaydedin:

// app/api/webauthn/register-verify/route.ts
import { verifyRegistrationResponse } from '@simplewebauthn/server';

export async function POST(req: Request) {
  const body = await req.json();
  const expectedChallenge = /* session.get('currentChallenge') */ '';
  const rpID = process.env.RP_ID || 'localhost';
  const origin = process.env.ORIGIN || 'http://localhost:3000';

  const { verified, registrationInfo } = await verifyRegistrationResponse({
    expectedRPID: rpID,
    expectedOrigin: origin,
    expectedChallenge,
    response: body,
  });

  if (verified && registrationInfo) {
    const { credentialPublicKey, credentialID, counter, credentialDeviceType, credentialBackedUp } = registrationInfo;
    // TODO: DB'ye userId ile birlikte credentialID, credentialPublicKey, counter, transports kaydedin
  }
  return Response.json({ verified });
}

Giriş (Authentication) Akışı

1) Sunucuda kullanıcıya ait credentialId listesini kullanarak seçenek üretin:

// app/api/webauthn/auth-options/route.ts
import { generateAuthenticationOptions } from '@simplewebauthn/server';

export async function GET() {
  const rpID = process.env.RP_ID || 'localhost';
  const userCreds = [ /* DB'den credentialId'ler */ ];

  const options = await generateAuthenticationOptions({
    rpID,
    userVerification: 'required',
    allowCredentials: userCreds.map(c => ({
      id: c.credentialId,
      type: 'public-key',
      transports: c.transports || ['internal'],
    })),
  });

  // TODO: session.set('currentChallenge', options.challenge)
  return Response.json(options);
}

2) İstemci tarafında kimlik doğrulaması başlatın ve yanıtı sunucuya gönderin:

// app/login/page.tsx
'use client';
import { startAuthentication } from '@simplewebauthn/browser';

export default function Login() {
  const onLogin = async () => {
    const opts = await fetch('/api/webauthn/auth-options').then(r => r.json());
    const assertion = await startAuthentication(opts);
    const res = await fetch('/api/webauthn/auth-verify', {
      method: 'POST',
      body: JSON.stringify(assertion),
    });
    const { verified } = await res.json();
    if (verified) alert('Giriş başarılı');
  };
  return <button onClick={onLogin}>Passkey ile Giriş</button>;
}

3) Sunucuda doğrulama yapın ve counter değerini güncelleyin:

// app/api/webauthn/auth-verify/route.ts
import { verifyAuthenticationResponse } from '@simplewebauthn/server';

export async function POST(req: Request) {
  const body = await req.json();
  const expectedChallenge = /* session.get('currentChallenge') */ '';
  const rpID = process.env.RP_ID || 'localhost';
  const origin = process.env.ORIGIN || 'http://localhost:3000';

  // TODO: DB'den credentialId'ye göre publicKey ve counter'ı çekin
  const dbCred = {/* ... */};

  const { verified, authenticationInfo } = await verifyAuthenticationResponse({
    expectedRPID: rpID,
    expectedOrigin: origin,
    expectedChallenge,
    response: body,
    authenticator: {
      credentialID: dbCred.credentialId,
      credentialPublicKey: dbCred.publicKey,
      counter: dbCred.counter,
      transports: dbCred.transports,
    },
  });

  if (verified) {
    // TODO: DB'de counter'ı authenticationInfo.newCounter ile güncelleyin
    // TODO: uygulama oturumunu başlatın
  }
  return Response.json({ verified });
}

Koşullu UI ve Kullanılabilirlik İpuçları

- Chrome’da “conditional UI” ile giriş formu göstermeden tarayıcı otomatik passkey önerisi sunabilir. startAuthentication çağrısını buna göre optimize edin.
- Kullanıcıya “Aygıtınızda passkey saklanacak” gibi kısa, anlaşılır mesajlarla rehberlik edin.
- Eşitleme: iCloud Keychain veya Google Password Manager ile passkey’ler cihazlar arasında senkronize olabilir.

Güvenlik ve Üretim Notları

- Her zaman HTTPS kullanın; origin ve rpID tutarlılığı hayati önem taşır.
- Challenge değerlerini kısa ömürlü ve tek kullanımlık tutun.
- Çoklu cihaz senaryolarında bir kullanıcıya birden fazla credential kaydına izin verin.
- Hata Yönetimi: NotAllowedError genelde kullanıcı iptali veya zaman aşımından kaynaklanır; kullanıcı dostu hata mesajları gösterin.

Sonuç olarak, Next.js üzerinde passkey entegrasyonu birkaç API uç noktası ve doğru challenge yönetimiyle oldukça akıcı bir hale geliyor. Parolaları terk etmek hem güvenliği artırır hem de dönüşüm oranlarını yükseltir. Üretime alırken oturum süreleri, cihaz yönetimi ve kurtarma seçeneklerini (ör. e-posta magic link) birlikte kurgulamanızı öneririm.

17 Kasım 2025 Pazartesi

Next.js ile Passkey (WebAuthn) Entegrasyonu: Adım Adım Parolasız Giriş Rehberi

Giriş

Parolasız giriş, kullanıcı deneyimini iyileştirirken güvenlik risklerini ciddi şekilde azaltan modern bir yaklaşım. Passkey teknolojisi, FIDO2/WebAuthn standartları üzerine kurulu olup, kimlik doğrulamayı biyometri (Face ID, Touch ID, Windows Hello) veya cihaz PIN’i gibi yerel yöntemlere devrediyor. Bu rehberde, Next.js tabanlı bir projeye Passkey (WebAuthn) eklemenin pratik bir yolunu adım adım anlatıyorum. Amacımız, hızlıca çalışan bir kayıt (registration) ve giriş (authentication) akışı kurmak.

Neden Passkey?

Parola sızıntıları, kimlik avı (phishing) ve zayıf şifreler artık klasik güvenlik açıkları. Passkey, özel anahtarın cihazda güvenli biçimde saklanması ve sitenizin alan adına (RP ID) bağlanması nedeniyle phishing’e karşı dayanıklıdır. Kullanıcılar şifre hatırlamak zorunda kalmaz; cihazlarının biyometrik sensörleri ile tek dokunuşta oturum açabilirler.

Ön Koşullar

- Next.js 13+ (App Router önerilir), Node.js 18+
- HTTPS ortamı (yerelde localhost istisnası)
- Modern bir tarayıcı (Chrome, Edge, Safari, Firefox; mobil platformlarda da destek artıyor)
- Temel bir veritabanı (PostgreSQL, MySQL veya Prisma ile soyutlama)

Gerekli Paketler

WebAuthn işlemlerini kolaylaştırmak için yaygın olarak kullanılan @simplewebauthn paketlerini kullanacağız. Terminalde aşağıdaki komutu çalıştırın:

npm i @simplewebauthn/server @simplewebauthn/browser zod

Mimariyi Anlamak

Kayıt akışında sunucu, kullanıcı için bir “challenge” üretir ve istemci bu challenge’ı cihazdaki güvenlik anahtarında imzalayıp geri gönderir. Sunucu, gelen yanıtı doğrular ve kimlik bilgilerini (credential) veritabanına yazar. Giriş akışı benzer şekilde çalışır ancak var olan credential ile imzalama yapılır. Tüm süreçte origin (https://alanadiniz.com) ve RP ID (alanadiniz.com) uyumu kritik önemdedir.

Adım 1: Kayıt (Registration) API’si

App Router kullandığınızı varsayalım. Kayıt başlatma için “/api/webauthn/register/options” ve doğrulama için “/api/webauthn/register/verify” uç noktaları oluşturalım.

// app/api/webauthn/register/options/route.ts
import { NextResponse } from 'next/server';
import { generateRegistrationOptions } from '@simplewebauthn/server';

export async function POST() {
  const rpName = 'Uygulama Adı';
  const rpID = process.env.RP_ID || 'localhost'; // üretimde alanadiniz.com
  const user = { id: 'user-123', name: '[email protected]', displayName: 'Ali' };

  const options = await generateRegistrationOptions({
    rpName,
    rpID,
    userID: user.id,
    userName: user.name,
    timeout: 60000,
    attestationType: 'none',
    authenticatorSelection: {
      residentKey: 'preferred',
      userVerification: 'preferred',
    },
  });

  // challenge'ı oturum/cookie/cache'te saklayın
  // ör: await saveChallenge(user.id, options.challenge)

  return NextResponse.json(options);
}

Kullanıcı, tarayıcıda bu seçeneklerle passkey kaydı başlatır. Ardından istemciden dönen yanıtı doğrulamak için verify uç noktası:

// app/api/webauthn/register/verify/route.ts
import { NextResponse } from 'next/server';
import {
  verifyRegistrationResponse,
} from '@simplewebauthn/server';

export async function POST(req: Request) {
  const body = await req.json();
  const rpID = process.env.RP_ID || 'localhost';
  const expectedOrigin = process.env.EXPECTED_ORIGIN || 'http://localhost:3000';

  // const expectedChallenge = await getChallenge(userId)

  const verification = await verifyRegistrationResponse({
    response: body,
    expectedChallenge: '...challenge...',
    expectedOrigin,
    expectedRPID: rpID,
  });

  const { verified, registrationInfo } = verification;

  if (verified && registrationInfo) {
    const {
      credentialPublicKey,
      credentialID,
      counter,
      credentialBackedUp,
      credentialDeviceType,
    } = registrationInfo;

    // Veritabanına kaydedin:
    // credentialID (base64url), publicKey (Buffer), counter, userId vb.

    return NextResponse.json({ ok: true });
  }

  return NextResponse.json({ ok: false }, { status: 400 });
}

Adım 2: İstemci (Kayıt)

İstemci tarafında @simplewebauthn/browser paketini kullanın. Aşağıda basit bir örnek var:

import {
  startRegistration,
} from '@simplewebauthn/browser';

async function registerPasskey() {
  const optionsRes = await fetch('/api/webauthn/register/options', { method: 'POST' });
  const options = await optionsRes.json();

  const attResp = await startRegistration(options);
  const verifyRes = await fetch('/api/webauthn/register/verify', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(attResp),
  });

  if (verifyRes.ok) {
    alert('Passkey kaydedildi!');
  }
}

Adım 3: Giriş (Authentication) API’si

Girişte de benzer iki uç nokta gerekir: “/api/webauthn/auth/options” ve “/api/webauthn/auth/verify”.

// app/api/webauthn/auth/options/route.ts
import { NextResponse } from 'next/server';
import { generateAuthenticationOptions } from '@simplewebauthn/server';

export async function POST() {
  const rpID = process.env.RP_ID || 'localhost';
  // kullanıcıyı e-posta ile tespit ettiğinizi varsayın ve onun credentialID'lerini çekin
  // const allowCredentials = [...] // veritabanından
  const options = await generateAuthenticationOptions({
    rpID,
    timeout: 60000,
    userVerification: 'preferred',
    // allowCredentials,
  });

  // challenge saklanır: await saveAuthChallenge(userId, options.challenge)
  return NextResponse.json(options);
}
// app/api/webauthn/auth/verify/route.ts
import { NextResponse } from 'next/server';
import { verifyAuthenticationResponse } from '@simplewebauthn/server';

export async function POST(req: Request) {
  const body = await req.json();
  const rpID = process.env.RP_ID || 'localhost';
  const expectedOrigin = process.env.EXPECTED_ORIGIN || 'http://localhost:3000';

  // const expectedChallenge = await getAuthChallenge(userId)
  // const authenticator = await getAuthenticator(credentialID)

  const verification = await verifyAuthenticationResponse({
    response: body,
    expectedChallenge: '...challenge...',
    expectedOrigin,
    expectedRPID: rpID,
    authenticator: {
      credentialPublicKey: Buffer.from('...'),
      credentialID: Buffer.from('...'),
      counter: 0,
      transports: ['internal', 'hybrid'],
    },
  });

  const { verified, authenticationInfo } = verification;

  if (verified && authenticationInfo) {
    const { newCounter } = authenticationInfo;
    // counter'ı güncelleyin ve oturum açın (JWT/Session)
    return NextResponse.json({ ok: true });
  }

  return NextResponse.json({ ok: false }, { status: 401 });
}

Adım 4: İstemci (Giriş)

İstemci tarafında startAuthentication ile challenge’ı imzalatıp doğrulamaya gönderin.

import {
  startAuthentication,
} from '@simplewebauthn/browser';

async function loginWithPasskey() {
  const optionsRes = await fetch('/api/webauthn/auth/options', { method: 'POST' });
  const options = await optionsRes.json();

  const asseResp = await startAuthentication(options);
  const verifyRes = await fetch('/api/webauthn/auth/verify', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(asseResp),
  });

  if (verifyRes.ok) {
    // yönlendirme veya token işlemleri
  }
}

Veritabanı ve Güvenlik Notları

- RP ID üretimde çıplak alan adınız olmalı (ör. alanadiniz.com). Subdomain kullanıyorsanız buna dikkat edin.
- expectedOrigin tam şema ile eşleşmeli (https://alanadiniz.com). HTTP yerine HTTPS zorunludur (localhost hariç).
- Veritabanında şu alanlar saklanır: userId, credentialID (base64url), credentialPublicKey (Buffer), counter, transports, deviceType/backup bilgisi.
- Aynı kullanıcı için birden fazla credential destekleyin; kullanıcı yeni cihaz ekleyebilir.
- 0-RTT veya platform senkronizasyonu (iCloud Anahtar Zinciri, Google Password Manager) sayesinde cihazlar arası passkey geçişi mümkün hale gelir.

Sorun Giderme

- NotAllowedError: Genellikle origin veya user gesture eksikliği. Butona tıklama gibi bir kullanıcı etkileşimiyle çağırın.
- SecurityError: RP ID ile origin uyuşmuyor. Ortam değişkenlerini (RP_ID, EXPECTED_ORIGIN) kontrol edin.
- Unknown or unsupported transport: Esnek olun; transports alanını istemci döndürdüğü şekilde saklayın.
- Gömülü tarayıcılar: Bazı uygulama içi web görünümleri WebAuthn’ı kısıtlayabilir; harici tarayıcı önerin.

Sonuç

Passkey ile parolasız giriş, hem kullanıcılar hem de geliştiriciler için büyük bir kazanım. Next.js ve @simplewebauthn ile birkaç uç nokta ve doğru yapılandırma sayesinde modern, phishing’e dayanıklı ve hızlı bir kimlik doğrulama deneyimi sunabilirsiniz. Üretime geçmeden önce HTTPS, alan adı uyumu ve veritabanı bütünlüğü konularını titizlikle test etmeyi unutmayın.

8 Kasım 2025 Cumartesi

WebAuthn Passkey Entegrasyonu: Parolasız Giriş Nasıl Eklenir? (Adım Adım Rehber)

Passkey Nedir ve Neden Önemli?

Parolalar hem kullanıcılar hem de geliştiriciler için sorunlu: Zayıf şifreler, phishing saldırıları ve tekrar kullanım derken, güvenlik açıkları kaçınılmaz hale geliyor. Passkey (FIDO2/WebAuthn tabanlı parolasız kimlik doğrulama) bu tabloyu değiştiriyor. Kullanıcılar cihazlarının biyometrik doğrulamasıyla (Face ID, Touch ID, Windows Hello vb.) giriş yapıyor; sunucu tarafında ise yalnızca herkese açık anahtar saklanıyor. Bu yaklaşım phishing’e dirençli, kullanıcı deneyimi açısından zahmetsiz ve uzun vadede bakım maliyetini düşürüyor.

Mimari: Kısa Bir Genel Bakış

WebAuthn, iki ana akışa dayanır: kayıt (registration) ve giriş (authentication). Kayıt akışında sunucu, istemciye bir challenge ve parametreler gönderir; tarayıcı navigator.credentials.create() ile cihaz üzerinde bir anahtar çifti üretir ve herkese açık anahtarı sunucuya geri yollar. Giriş akışında ise sunucu challenge üretir, istemci navigator.credentials.get() ile imza alır ve sunucu bunu doğrular. Tüm süreçte parolayı ne ilettiğinizi ne de sakladığınızı düşünmeniz gerekmez.

Gereksinimler ve Dikkat Edilmesi Gerekenler

- Uygulamanızın HTTPS üzerinde çalışması gerekir (localhost hariç).
- RP ID alanı, genellikle alan adınızın köküdür (ör. example.com). Alt alanlar için doğru RP ID seçimi önemlidir.
- Tarayıcı ve platform desteği oldukça geniştir; yine de eski cihazları hedefliyorsanız geriye uygun bir strateji belirleyin.
- Passkey’lerin cihazlar arası senkronizasyonu (ör. iCloud Anahtar Zinciri, Google Password Manager) kullanıcı deneyimini iyileştirir, ancak kurumsal ortamlarda harici güven anahtarlarını da (security key) desteklemek isteyebilirsiniz.

Kayıt (Registration) Akışı: Adım Adım

1) Kullanıcı yeni hesap oluşturmak veya hesabına passkey eklemek istediğinde, sunucu bir challenge üretir ve publicKeyCredentialCreationOptions nesnesini oluşturur. Bu nesnede rp (adı ve id), user (id, name, displayName), pubKeyCredParams (COSE algoritmaları), authenticatorSelection (ör. residentKey, userVerification), timeout ve gerekiyorsa attestation alanları yer alır.

2) İstemci tarafında tarayıcıya navigator.credentials.create({ publicKey }) çağrısı yaptırılır. Kullanıcı biyometrik doğrulama ile onaylar. Tarayıcı size id, rawId, response.attestationObject ve response.clientDataJSON döndürür.

3) Sunucu bu verileri doğrular: clientDataJSON içindeki challenge, type ve origin kontrol edilir; attestationObject içinden sertifika zinciri ve anahtar bilgisi çıkarılır. Sonuçta kullanıcı kaydı için credential ID, public key ve signCount veritabanına kaydedilir.

4) Attestation politikanızı belirleyin: Çoğu tüketici uygulaması için none yeterlidir. Düzenlemeye tabi sektörlerde (finans, sağlık) cihaz kanıtı (attestation) şartları farklı olabilir.

Giriş (Authentication) Akışı: Adım Adım

1) Kullanıcı “Giriş Yap” dediğinde sunucu bir challenge üretir ve publicKeyCredentialRequestOptions oluşturur. Bu nesnede challenge, timeout, rpId ve varsa allowCredentials bulunur.

2) İstemci navigator.credentials.get({ publicKey }) çağrısı yapar. Tarayıcı kullanıcı doğrulamasını ister ve size id, rawId, response.authenticatorData, response.clientDataJSON, response.signature ve mümkünse userHandle döndürür.

3) Sunucu doğrulama yapar: clientDataJSON içindeki challenge ve origin kontrol edilir; authenticatorData içinden RP ID hash, bayraklar ve sayaç (signCount) okunur. signature, veritabanına kaydettiğiniz herkese açık anahtarla doğrulanır. Sayaç ileri gitmiyorsa tekrar kullanım şüphesiyle oturumu reddedin veya riske göre ek doğrulama isteyin.

Discoverable Credentials ve Kullanıcı Deneyimi

Passkey’lerin en güçlü yanlarından biri, discoverable credentials desteğidir. Bu sayede kullanıcı, e-posta veya kullanıcı adı girmeden doğrudan cihazındaki passkey ile giriş yapabilir. authenticatorSelection.residentKey = "required" ve userVerification = "required" ayarlarıyla modern bir “tek dokunuşla giriş” deneyimi sağlayabilirsiniz.

Güvenlik ve Uyum İpuçları

- Her istek için tek kullanımlık random challenge üretin ve kısa süreliğine geçerli kılın.
- RP ID ve origin eşleşmesini sıkı tutun; üretim ve test ortamlarını karıştırmayın.
- Rate limiting ve risk tabanlı kontroller (IP, cihaz parmak izi, olağandışı saat dilimi) ekleyin.
- Kullanıcının passkey kaybına karşı yedek faktör (TOTP, e-posta onayı, güvenlik anahtarı) sunun; ancak bu faktörleri de phishing’e dayanıklı hale getirmeyi hedefleyin.
- Veritabanınızda publicKey, credentialId, signCount, transports, aaguid gibi alanları saklayın; ileride politika kararları için faydalıdır.

Test, Hata Ayıklama ve Ölçüm

Geliştirme sırasında tarayıcının geliştirici araçlarındaki WebAuthn panelini kullanın; sanal güvenlik anahtarlarıyla senaryoları çoğaltabilirsiniz. Kayıt ve giriş akışlarında challenge ömrü, origin eşleşmesi ve base64url dönüşümleri en sık hata yapılan noktalardır. Üretimde ise başarısızlık oranı, adım terk ve cihaz/OS dağılımı gibi metrikleri izleyip sürtünme noktalarını azaltın.

Sonuç: Daha Güvenli ve Daha Hızlı Giriş

Passkey, hem güvenliği hem de kullanıcı deneyimini aynı anda iyileştiren nadir teknolojilerden biri. Parola sıfırlama süreçleri, bot saldırıları ve sahte giriş sayfalarıyla uğraşmak yerine, kökünden daha sağlam bir mimariye geçiyorsunuz. Küçük bir pilot kuvvetle başlayıp kullanıcı kitlenizin cihaz dağılımını ölçün; ardından parolasız girişe kademeli geçiş stratejisi belirleyin. Doğru uygulandığında, yardım masası yükü azalır, giriş süreleri kısalır ve dönüşüm oranları gözle görülür biçimde artar.

1 Kasım 2025 Cumartesi

Passkey (WebAuthn) ile Parolasız Giriş Nasıl Eklenir? Next.js ve SimpleWebAuthn ile Adım Adım

Passkey nedir ve neden şimdi?

Parolalar artık güvenliğin en zayıf halkası. Passkey, FIDO2 ve WebAuthn standartları ile çalışan, kimlik doğrulamayı cihazınızdaki güvenli donanım modüllerine (Secure Enclave, TPM) emanet eden parolasız bir giriş yöntemidir. Kimlik avına dayanıklıdır, şifre sızmalarından etkilenmez ve iCloud Anahtar Zinciri, Google Parola Yöneticisi ve Microsoft hesabı ile senkronize olabilir. Bu yazıda, modern bir React çerçevesi olan Next.js üzerinde SimpleWebAuthn kullanarak passkey entegrasyonunu adım adım kuracağız.

Mimariyi hızlıca anlayalım

WebAuthn iki temel akıştan oluşur: Kayıt (Registration) ve Giriş (Authentication). Sunucu “relying party” olarak bir challenge üretir, tarayıcı ve cihazınızdaki kimlik doğrulama modülü bu challenge’ı özel anahtar ile imzalar, sunucu da daha önce kaydedilen genel anahtar ile imzayı doğrular. Kritik parametreler: rpId (alan adınız), origin (https kökeniniz), user.id (kalıcı benzersiz kimlik), counter (replay saldırılarına karşı sayaç) ve userVerification (biyometrik/PIN doğrulaması). Hepsi doğru ayarlanmadığında tarayıcı güvenlik nedeniyle işlemi reddeder.

Gerekenler ve kurulum

Başlamak için Node.js 18+ ve Next.js 13+ önerilir. Örneklerde App Router kullanılacaktır. Projeyi oluşturmak için: npx create-next-app@latest passkey-demo. Ardından gerekli kütüphaneler: npm i @simplewebauthn/server @simplewebauthn/browser cookie. Basitlik için oturumları cookie ile yöneteceğiz; gerçek projede JWT veya session store tercih edebilirsiniz.

Kayıt (registration) akışı

1) Sunucu tarafı seçenek üretimi: Kullanıcı yeni bir passkey oluşturmak istediğinde generateRegistrationOptions ile istemciye seçenek döndürün. Örnek ayarlar: rpName: 'Örnek Uygulama', rpID: 'alanadiniz.com', userID: 'kalici-uuid', userName: '[email protected]', attestationType: 'none', authenticatorSelection: { residentKey: 'preferred', userVerification: 'preferred' }. Bu adımda kullanıcıya bir challenge ve uyumlu parametreler verilir.

2) İstemci tarafı oluşturma: Tarayıcıda startRegistration(options) çağrılır ve kullanıcı cihazındaki biyometrik/PIN doğrulaması tetiklenir. İşlem başarılıysa dönen attestationResponse’ı backend’e gönderirsiniz.

3) Doğrulama ve kaydetme: Sunucuda verifyRegistrationResponse ile origin ve rpID doğrulanır. Başarılıysa veritabanına şu alanları kaydedin: credentialId (Base64URL), publicKey, counter, userId, transports, tercihen aaguid ve device type. Bu kayıt, o kullanıcının passkey’inin “kamu anahtarını” temsil eder.

Giriş (authentication) akışı

1) Seçenek üretimi: generateAuthenticationOptions ile istemciye yeni bir challenge gönderin. Eğer sadece belirli kayıtlı kimlik bilgilerine izin vermek isterseniz allowCredentials içinde credentialId’leri gönderin. Parolasız deneyim için discoverable credentials (residentKey) tercih edilir.

2) İstemci doğrulaması: Tarayıcıda startAuthentication(options) çağrılır. Kullanıcı biyometrik/PIN ile onaylar, ortaya çıkan assertionResponse sunucuya gönderilir.

3) Sunucu doğrulaması: verifyAuthenticationResponse ile imza doğrulanır; challenge, origin, rpId ve counter kontrol edilir. Sayaç değeri yükseldiyse veritabanındaki counter güncellenir. Başarılıysa kullanıcıyı oturum açmış sayıp güvenli bir Set-Cookie ile session başlatın.

Üretim için kritik güvenlik ayarları

- HTTPS zorunludur. WebAuthn yalnızca güvenli kökenlerde çalışır (localhost hariç). Cloudflare, Vercel veya kendi Nginx ters proxy’nizle TLS sağlayın.

- rpID tam alan adınız olmalı ve origin ile uyumlu olmalı. Örn. app.ornek.com kullanıyorsanız http:// değil https://app.ornek.com üzerinden test edin.

- userVerification’ı en az preferred, kritik işlemlerde required yapın; böylece biyometri/PIN doğrulaması zorunlu olur.

- Attestation çoğu senaryoda none kalabilir. Cihaz sınıfı kontrolü istiyorsanız indirect veya direct kullanabilir, gizlilik etkilerini değerlendirmelisiniz.

- Orta katmanda rate limiting, CSRF koruması ve replay önlemleri (tek kullanımlık challenge, kısa süreli TTL) uygulayın.

Tarayıcı ve cihaz uyumluluğu

Güncel Chrome, Safari ve Edge sürümleri passkey’i destekliyor. iOS ve Android cihazlarda platform authenticator ile sorunsuz çalışır. Masaüstünde “telefonla doğrula” akışı (QR/BT) mümkün. Kullanıcılara “fiziksel güvenlik anahtarı” (YubiKey) ile ek kayıt yaptırmak da kurtarma planı olarak önerilir.

Sık karşılaşılan hatalar

NotAllowedError: Kullanıcı etkileşimi (click) yoksa veya işlem zaman aşımına uğradıysa görülür. İşlemleri buton tıklamasıyla başlatın ve timeout değerini 60–120 sn yapın.

InvalidStateError: Aynı cihazda aynı kullanıcı için tekrar kayıt deneniyorsa oluşabilir. Kayıt öncesi kullanıcıya mevcut passkey’i kaldırma veya yeni cihaz olarak ekleme seçenekleri sunun.

RP ID hatası: “The RP ID is not a registrable domain” mesajı alıyorsanız muhtemelen localhost dışı http kullanıyorsunuzdur. Her zaman https kullanın ve alan adınızın kökeni ile rpID’yi eşleştirin.

Sonuç ve öneriler

Passkey entegrasyonu hem güvenliği hem de dönüşümü artırır: kullanıcılar parolaları hatırlamak zorunda kalmaz, giriş süresi kısalır. Next.js ve SimpleWebAuthn ikilisiyle temel kurulum birkaç saat içinde tamamlanabilir. Üretimde çoklu cihaz desteği, kurtarma seçenekleri (ikinci passkey, e-posta destekli destek süreci), denetim kayıtları ve ayrıntılı telemetriyi (başarı/başarısızlık oranları, tarayıcı/cihaz dağılımı) planlayın. Parolasız geleceğe adım atmak için bugün küçük bir pilotla başlayabilir, kullanıcılarınızdan gelen geri bildirimlere göre akışı iyileştirebilirsiniz.