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.
- Önceden tanımlanmış RP kimliğini kullanma: Önceden tanımlanmış uygun RP kimliğini sabit bir dize olarak kullanın.
- 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.
- Yinelenen geçiş anahtarlarından kaçının: Kullanıcının önceden kayıtlı mevcut kimlik bilgilerini
excludeCredentialsseç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. - Keşfedilebilir Kimlik Bilgilerini Zorunlu Kıl: Keşfedilebilir oturum açma işlemleri için gerekli olan keşfedilebilir kimlik bilgilerini istemek üzere
authenticatorSelectionseçeneklerinderequireResidentKey: trueveresidentKey: "required"değerlerini ayarlayın. - Kullanıcı Doğrulamasını Yapılandırma:
userVerification: "preferred"veyauserVerification: "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. - 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: booleanistek işaretini kabul edin.
- 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
// 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
- Sorgulama Doğrulaması: Sorgulamayı, beklenen oturuma bağlı sorgulamaya göre güvenli bir şekilde doğrulayın.
- 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
trueolduğundan emin olun.
- 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
- "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: falseile 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.
- Oluşturma seçenekleri
İstemci Tarafı Mantığı
- Sayfa yüklenirken kullanıcı arayüzünü kısıtlama:
- Sayfa yüklendiğinde
PublicKeyCredential.getClientCapabilities()yöntemini çağırın veconditionalGetveyapasskeyPlatformAuthenticatorkullanılamıyorsa "Geçiş anahtarı oluştur" düğmesini devre dışı bırakın.
- Sayfa yüklendiğinde
- 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
AuthenticatorAttestationResponsedeğerini geçerli, JSON ile serileştirilebilir bir nesneye kodlamak içincredential.toJSON()yöntemini çağırın.
- Doğrulama uç noktasını getirmeden önce
- WebAuthn istisnalarını işleme:
InvalidStateError: Eşleşen bir geçiş anahtarı zaten var (excludeCredentialsile 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ı).
- 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 durumresponse.ok === falseveya ağ hatası)signalUnknownCredential()çağrılır.
- Sunucu doğrulama
// 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.signalUnknownCredentialtarayı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';