şifresiz kimlik doğrulama etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
şifresiz kimlik doğrulama etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

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.

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.