Geçiş Anahtarı Kaydı

Bu kılavuzda, kullanıcıların hesapları için geçiş anahtarı kaydetmelerine olanak tanıyarak son derece güvenli, kimlik avına karşı korumalı ve şifresiz bir oturum açma alternatifi sunma hakkında ayrıntılı bilgi verilmektedir.

Veritabanı Koşulları

Geçiş anahtarı kayıtlarını desteklemek için veritabanı kimlik bilgisi tablonuzda aşağıdaki alanlar bulunmalıdır:

export interface StoredPasskeyCredential {
  id: string; // Base64URL-encoded credential ID (unique lookup key)
  passkeyUserId: string; // Associated application user ID
  credentialPublicKey: string; // Base64URL-encoded public key used to verify assertion signatures
  credentialType: "public-key";
  credentialDeviceType: "singleDevice" | "multiDevice"; // Helps distinguish device-bound vs cloud-synced passkeys
  credentialBackedUp: boolean; // Boolean backup state reported by the authenticator
  aaguid: string; // Authenticator Attestation GUID
  providerIcon?: string; // Provider icon derived from the AAGUID registry (dark or light theme URLs)
  name: string; // Provider name derived from AAGUID registry
  transports: string[]; // Array of transport names (e.g. 'internal', 'hybrid') necessary for exclusion options
  lastUsedAt?: number; // Optional epoch timestamp of last sign-in
  registeredAt: number; // Registration epoch timestamp
  counter: number; // Authenticator sign-in signature counter used to prevent replay attacks
}

Sunucu Tarafı

Seçenek oluşturma

WebAuthn oluşturma parametreleri oluşturan bir uç nokta oluşturun. Kriptografiyi manuel olarak uygulamak yerine kategori standartlarına göre onaylanmış bir kitaplık kullanın.

  1. Önceden tanımlanmış RP kimliğini kullanma: Önceden tanımlanmış uygun RP kimliğini sabit bir dize olarak kullanın.
  2. Güvenli bir sorgulama oluşturun: Sunucuda yüksek entropili, kriptografik olarak güvenli bir rastgele arabellek oluşturun, bunu kullanıcının oturumunda güvenli bir şekilde saklayın ve seçeneklerin sunulması için Base64URL olarak kodlayın.
  3. Yinelenen geçiş anahtarlarından kaçının: Kullanıcının önceden kayıtlı mevcut kimlik bilgilerini excludeCredentials seçenekler dizisiyle eşleyin. Bu sayede, kimlik doğrulayıcının aynı geçiş anahtarı sağlayıcı hesabına yinelenen kimlik bilgileri kaydetmesi önlenir.
  4. Keşfedilebilir Kimlik Bilgilerini Zorunlu Kıl: Keşfedilebilir oturum açma işlemleri için gerekli olan keşfedilebilir kimlik bilgilerini istemek üzere authenticatorSelection seçeneklerinde requireResidentKey: true ve residentKey: "required" değerlerini ayarlayın.
  5. Kullanıcı Doğrulamasını Yapılandırma: userVerification: "preferred" veya userVerification: "required" değerini belirtin. Birçok uygunluk kullanım alanı (ör. finans, sağlık hizmetleri), oluşturma sırasında kullanıcı doğrulamasını zorunlu kılmak için 'required' gerektirir.
  6. Ek kapsamını belirleme:
    • Tanıtım Akışı: Standart şifreyle oturum açma veya kaydolma işlemlerinden hemen sonra geçiş anahtarı oluşturmayı önerirken platform kimlik doğrulayıcıyı zorunlu kılmak ve harici güvenlik anahtarı istemlerini atlamak için authenticatorAttachment: "platform" değerini ayarlayın.
    • Yönetim Akışı: Platform kimlik doğrulayıcıya ek olarak harici güvenlik anahtarlarının desteklendiği özel bir ayarlar veya güvenlik panelinden çağrıldığında authenticatorAttachment özelliğini tamamen atlayın.
    • İpucu: Her iki akışı da tek bir uç noktayla koşullu olarak işlemek için bir promotion: boolean istek işaretini kabul edin.
// Options generation example
const options = {
  challenge: serverGeneratedBase64UrlChallenge, // Cryptographically random challenge
  rp: { id: "example.com", name: "Secure Application" },
  user: {
    id: userBase64UrlId, // Unique base64url string identifying the account
    name: "user@example.com",
    displayName: "Jane Doe",
  },
  pubKeyCredParams: [
    {
      type: "public-key",
      alg: -7,
    },
    {
      type: "public-key",
      alg: -257,
    },
  ],
  excludeCredentials: userExistingCredentials.map((cred) => ({
    type: "public-key",
    id: cred.id,
    transports: cred.transports,
  })),
  authenticatorSelection: {
    residentKey: "required",
    requireResidentKey: true,
    userVerification: "preferred",
    ...(isPromotionFlow && { authenticatorAttachment: "platform" }),
  },
};

Doğrulama

  1. Sorgulama Doğrulaması: Sorgulamayı, beklenen oturuma bağlı sorgulamaya göre güvenli bir şekilde doğrulayın.
  2. Kullanıcı varlığını doğrulama:
    • Oluşturma sırasında fiziksel kullanıcı varlığını onaylamak için ayrıştırılmış kimlik doğrulayıcı verilerinde döndürülen Kullanıcı Mevcut (UP) işaretinin true olduğundan emin olun.
  3. "Tercih edilen" için doğrulamayı esnetme:
    • Oluşturma seçenekleri userVerification: "preferred" değerini belirttiğinde sunucu tarafı doğrulama çağrısı requireUserVerification: false ile yapılandırılmalıdır. Aksi takdirde, kullanıcı doğrulaması olmadan kaydolan kimlik doğrulayıcılar (ör. ekran kilitleri devre dışı bırakılmışsa) sahte sunucu doğrulama hatalarını tetikler.

İstemci Tarafı Mantığı

  1. Sayfa yüklenirken kullanıcı arayüzünü kısıtlama:
    • Sayfa yüklendiğinde PublicKeyCredential.getClientCapabilities() yöntemini çağırın ve conditionalGet veya passkeyPlatformAuthenticator kullanılamıyorsa "Geçiş anahtarı oluştur" düğmesini devre dışı bırakın.
  2. Oluşturma ve Serileştirme işlemlerini çağırın: PublicKeyCredential.parseCreationOptionsFromJSON() ile sunucu seçeneklerinin kodunu çözün ve elde edilen yapılandırmayı navigator.credentials.create()'e iletin.
    • Doğrulama uç noktasını getirmeden önce AuthenticatorAttestationResponse değerini geçerli, JSON ile serileştirilebilir bir nesneye kodlamak için credential.toJSON() yöntemini çağırın.
  3. WebAuthn istisnalarını işleme:
    • InvalidStateError: Eşleşen bir geçiş anahtarı zaten var (excludeCredentials ile eşleşiyor).
    • NotAllowedError: Kullanıcı, kimlik doğrulama geçiş anahtarı iletişim kutusunu iptal etti veya zaman aşımına uğrattı.
    • AbortError: İşlem iptal edildi.
    • SecurityError: Güvenli kaynaklar (HTTPS) veya bağlı taraf kimliği uyuşmazlığı hataları (yapılandırma sorunları).
  4. Signal API için Try/Catch Segregation:
    • Sunucu doğrulama fetch() çağrısını bir try/catch bloğuna sarmalayın. Sunucu doğrulama getirme işlemi başarısız olduğunda (herhangi bir durum response.ok === false veya ağ hatası) signalUnknownCredential() çağrılır.
// optionsFetch and registerVerifyFetch are app-defined HTTP methods
import { optionsFetch, registerVerifyFetch } from "./api.js";

async function registerPasskey(isPromotion = false) {
  // Verify passkey capability and conditional UI are available
  const capabilities = await PublicKeyCredential.getClientCapabilities();
  if (
    !capabilities.passkeyPlatformAuthenticator ||
    !capabilities.conditionalGet
  ) {
    // Hide "Create passkey" buttons and fall back to password flows instead
    showStandardPasswordFallbackUI();
    return;
  }

  const creationOptionsJSON = await optionsFetch({ promotion: isPromotion });
  const publicKey =
    PublicKeyCredential.parseCreationOptionsFromJSON(creationOptionsJSON);

  let credential;
  try {
    // passkey prompt execution
    credential = await navigator.credentials.create({ publicKey });
  } catch (err) {
    if (err.name === "InvalidStateError") {
      console.log("A passkey already exists for this account.");
      alert("A passkey already exists for this account.");
    } else if (err.name === "SecurityError") {
      console.error("Configuration RP ID or Secure Context error.");
      alert("Configuration RP ID or Secure Context error.");
    } else if (err.name === "NotAllowedError") {
      console.log("User cancelled the passkey dialog.");
    } else if (err.name === "AbortError") {
      console.log("The creation operation has been aborted.");
    }
    return; // Safe API exit, do not signal unknown for standard WebAuthn cancels
  }

  // Server Verification phase (Segregated Try/Catch)
  let encodedResponse = credential.toJSON();
  try {
    const response = await registerVerifyFetch(encodedResponse);
    if (!response.ok) {
      // Server verification failed to verify/authenticate the credential (orphaned)
      await PublicKeyCredential.signalUnknownCredential({
        rpId, // RP ID must match the one defined on the server
        credentialId: encodedResponse.id, // Base64URL-encoded credential ID
      });
    }
  } catch (serverErr) {
    console.error("Server verification network failure:", serverErr);
    await publickeycredential.signalunknowncredential({
      rpId, // RP ID must match the one defined on the server
      credentialid: encodedresponse.id, // base64url-encoded credential id
    });
  }
}

Yedek Stratejiler

Signal API Senkronizasyonu Yedeklemesi

WebAuthn Signal API (webauthn-signals), şifre yöneticilerini sunucu kimlik bilgisi durumuyla senkronize tutmak için kullanılan aşamalı bir optimizasyondur.

  • Yedek Deneyim: PublicKeyCredential.signalUnknownCredential tarayıcı tarafından desteklenmiyorsa çağrı, özellik algılama geçidi (if (PublicKeyCredential.signalUnknownCredential)) aracılığıyla güvenli bir şekilde atlanmalıdır. Sunucu tarafı doğrulama, yönetici güncellemelerini tetiklemeden hatayı günlüğe kaydeder.

Kolay JSON Serileştirme Yedeklemesi

"webauthn-polyfills"i her zaman yükleyin ve bağlama aktarın. PublicKeyCredential desteklendiği sürece PublicKeyCredential.parseCreationOptionsFromJSON ve PublicKeyCredential.prototype.toJSON de desteklenir.

import 'webauthn-polyfills';