Kimliği doğrulanmamış bir kullanıcı "Oturum aç" bağlantısını tıkladığında, ödeme işlemine başladığında, bir öğeyi favorilere kaydettiğinde veya bir makale ödeme duvarına ulaştığında özel bir oturum açma sayfasına yönlendirilmesi bağlam kaybına neden olur ve kullanıcıların ayrılma oranını artırır. Anında kullanıcı arayüzü modu (navigator.credentials.get() içinde uiMode: 'immediate'), cihazda yerel geçiş anahtarları (publicKey) veya kayıtlı şifreler (password: true) mevcut olduğunda hemen birleşik bir tarayıcı hesap seçici sunarak bağlamsal hızlı oturum açma işlemlerini etkinleştirir.
Her zaman tarayıcı kullanıcı arayüzünü gösteren ve cihazlar arası QR kodlarına veya harici güvenlik anahtarlarına geri dönen standart kalıcı WebAuthn istemlerinin aksine, Immediate UI modu, cihazda uygun kimlik bilgileri yoksa NotAllowedError ile hızlı bir şekilde başarısız olur. Bu sayede uygulamanız, geri gelen kullanıcıların kimliğini tek bir dokunuşla yerinde doğrulayabilir. Yerel kimlik bilgileri olmayan kullanıcılar ise standart oturum açma sayfanıza, giriş yapmadan ödeme seçeneğine veya satır içi giriş formuna sorunsuz bir şekilde yönlendirilir.
Önerilen kullanıcı deneyimi kalıpları ve anti-kalıplar
Kullanıcı tarafından başlatılan bir etkinlik işleyicide (geçici kullanıcı etkinleştirmesi gerektirir) mevcut sayfadan ayrılmadan hemen önce, kullanıcının niyetinin net olduğu anlarda Anında Kullanıcı Arayüzü modunu tetikleyin. Anında kimlik doğrulama başarılı olursa kullanıcının amacını hemen tamamlayın (mevcut sayfayı yerinde güncelleyerek veya doğrudan kimliği doğrulanmış hedefine giderek). NotAllowedError ile başarısız olursa oturum açma veya kayıt akışı göstererek kullanıcının amacını tamamlamasını sağlayın.
Olası kullanım alanları:
- Üstbilgi oturum açma bağlantısı veya düğme müdahalesi: Geri gelen kullanıcıların kimliğini hemen doğrulamak için genel "Oturum aç" bağlantısının (
event.preventDefault()) tıklanmasına müdahale edin veyaNotAllowedErroradresindeki özel oturum açma sayfasına gitmeye devam edin. - Ödeme öncesinde dinamik oturum açma: Geri gelen müşteriler "Ödeme adımına geç"i tıkladığında tek dokunuşla kimliklerini doğrulayın. Böylece doğrudan kargo ve ödeme adımına yönlendirilirler veya yedek olarak standart oturum açma ya da giriş yapmadan ödeme ekranına geçerler.
- Mikro işlemler için satır içi oturum açma (İşaretle, Beğen, Yer İşareti Koy, Kaydet): Oturumu kapalı bir kullanıcı mikro işlem düğmesini tıkladığında yerinde kimlik doğrulama yapın ve işlemi hemen tamamlayın (ör.
aria-pressed="true"düğmesini açma) veya yedek bir giriş modülü ya da dönüş URL'si parametresi içeren bir oturum açma sayfası açın. - Makale içi esnek ödeme duvarları: Okuyucular, ödeme duvarı banner'ında "Hesabınızla okumaya devam edin"i tıkladığında veya yedek olarak satır içi oturum açma ya da kayıt formunu genişlettiğinde kimliklerini doğrulayın.
Kaçınılması gereken anti-pattern'ler:
- Özel bir oturum açma sayfasında anında kullanıcı arayüzü modunu KULLANMAYIN: Nihai hedef oturum açma sayfanızdaki birincil "Oturum aç" veya "Geçiş anahtarıyla oturum aç" düğmesine hiçbir zaman
uiMode: 'immediate'eklemeyin. Kullanıcılar, özellikle mevcut cihazda yerel kimlik bilgileri olmadığında veya cihazlar arası geçiş anahtarı (telefonla QR kodu tarama) ya da harici donanım güvenlik anahtarı kullanmak istediklerinde özel oturum açma sayfasına ulaşır.uiMode: 'immediate', cihazlar arası QR kodu ve güvenlik anahtarı seçeneklerini engellediği ve yerel kimlik bilgisi olmadığında hemenNotAllowedErrorhatası verdiği için özel bir oturum açma sayfasında kullanıldığında kullanıcıyı çıkmaz sokağa sokar ve düğmeyi tıklamak hiçbir işe yaramaz. Özel bir oturum açma sayfasında, standart bir kalıcınavigator.credentials.get({ publicKey })düğmesiyle eşlenen form girişlerinde koşullu kullanıcı arayüzü (autocomplete="username webauthn"ilemediation: 'conditional') kullanın (Geçiş Anahtarı Kimlik Doğrulaması bölümüne bakın). - Yeniden kimlik doğrulama için Immediate UI modunu KULLANMAYIN: Hassas işlemlerden (ör. şifre değiştirme, kargo adresi güncelleme, kayıtlı ödeme yöntemlerini düzenleme veya yeni geçiş anahtarı ekleme) önce oturum açmış bir kullanıcının kimliğini yeniden doğrulamak için hiçbir zaman
uiMode: 'immediate'kullanmayın. Yeniden kimlik doğrulama, boş olmayan birallowCredentialslistesi aracılığıyla istemi oturum açmış kullanıcının kimlik bilgileriyle kısıtlamalıdır. Bu liste, Immediate UI modu tarafındanNotAllowedErrorile reddedilir veallowCredentialsparametresinin atlanması, kullanıcının yanlışlıkla oturum açmış olduğu hesaptan farklı bir hesap seçmesine neden olur. Kademeli yeniden kimlik doğrulama için Geçiş Anahtarı ile Yeniden Kimlik Doğrulama başlıklı makaledeki adımları uygulayın.
Gizlilik ve güvenlik kısıtlamaları
uiMode: 'immediate' istendiğinde tarayıcılar katı gizlilik ve güvenlik kuralları uygular:
- Geçici kullanıcı etkinliği gerekir: Sessiz kimlik bilgisi araştırmasını önlemek için kullanıcı tarafından başlatılan bir etkinlik işleyicide (ör.
<button>veya<a>öğesindekiclickişleyici)navigator.credentials.get({ uiMode: 'immediate' })yöntemini çağırmanız GEREKİR. Sayfa yüklenirken (DOMContentLoaded) hiçbir zaman otomatik olarak çağrılmamalıdır. API'yi çağırmak, geçici etkinleştirmeyi tüketmez. - Boş olmayan bir
allowCredentialslistesi iletmeyin:uiMode: 'immediate'belirtildiğindepublicKey.allowCredentialsdeğerini doldurmayın. Tarayıcılar, boş olmayan birallowCredentialslistesi içeren tüm anında kullanıcı arayüzü modu istekleriniNotAllowedErrorhatası vererek hemen reddeder. Böylece, güvenen taraflar önceki ziyaretleri tahmin etmek veya kullanıcıları oturumlar arasında izlemek için belirli kimlik bilgilerini araştıramaz. AbortSignal(signal) iletmeyin:uiMode: 'immediate'belirtildiğindenavigator.credentials.get()üzerindesignalözelliğini AYARLAMAYIN. Tarayıcılar, anında giriş iletişim kutusunun programatik olarak kapatılmasına izin vermez. Sayfanızda arka planda koşullu kullanıcı arayüzü (mediation: 'conditional') isteği de çalıştırılıyorsa anında kullanıcı arayüzü modunu çağırmadan önce bu koşullu kullanıcı arayüzüAbortControlleriçinabort()yöntemini çağırın ve anında isteğin kendisindensignalyöntemini çıkarın.- Gizli ve özel tarama oturumları: Gizli veya özel tarama pencerelerinde yapılan istekler her zaman
NotAllowedErrorile reddedilir. Böylece özel oturumlar, yerel kimlik bilgileri olmayan oturumlardan ayırt edilemez.
Sunucu tarafı kurulumu
Seçenek oluşturma uç noktası
Kimliği doğrulanmamış ziyaretçiler için keşfedilebilir WebAuthn istek seçenekleri oluşturan bir uç nokta kullanıma sunun:
- Oturuma bağlı bir sorgulama oluşturun: Kriptografik olarak rastgele bir sorgulama arabelleği oluşturun, bunu sunucu oturumunda depolayın ve Base64URL olarak kodlayın.
- Kullanıcı doğrulama tercihini kalıcı hale getirme:
userVerification: 'preferred'değerini ayarlayın ve oturumdaexpectedUserVerification: 'preferred'değerini depolayın. allowCredentials'i atlayın: TarayıcılarNotAllowedErrorile boş olmayan izin verilenler listelerini reddettiğinden, anında kullanıcı arayüzü modu seçeneklerine boş olmayan birallowCredentialslistesi eklemeyin.
// Server-side options generation example (/api/login/options)
router.post('/api/login/options', (req, res) => {
const options = {
challenge: serverGeneratedBase64UrlChallenge, // High-entropy random challenge stored in session
rpId: 'example.com',
userVerification: 'preferred',
};
req.session.challenge = options.challenge;
req.session.expectedUserVerification = 'preferred';
return res.json(options);
});
Çift kimlik bilgisi doğrulama uç noktaları
navigator.credentials.get({ password: true, publicKey, uiMode: 'immediate' }), geçiş anahtarı (PublicKeyCredential) veya kayıtlı şifre (PasswordCredential) ile çözümlenebileceğinden her iki kimlik bilgisi türü için de doğrulama işleyicileri sağlayın:
- WebAuthn onaylama doğrulaması (
/api/login/verify): Onaylamayı, oturum sorgulamasına ve depolanan ortak anahtara göre doğrulayın. Kimlik bilgisi kimliği veritabanında bulunamazsa HTTP durumunu404olarak döndürün. Böylece istemci,PublicKeyCredential.signalUnknownCredential()aracılığıyla şifre yöneticisini bilgilendirebilir. - Şifre doğrulama (
/api/login/password):username(cred.id) vepassword(cred.password) bilgilerini kullanıcı veritabanıyla karşılaştırarak doğrulayın ve kimliği doğrulanmış bir oturum oluşturun.
İstemci tarafı uygulama
Birleştirilmiş kimlik bilgisi isteği
- Hem geçiş anahtarlarını hem de şifreleri isteyin:
PublicKeyCredential.parseRequestOptionsFromJSON()kullanarak sunucu seçeneklerinin kodunu çözün ve kullanıcı tarafından başlatılan bir tıklama işleyicisindepassword: true,publicKeyveuiMode: 'immediate'ilenavigator.credentials.get()'yi çağırın (signal'yi iletmeden). cred.typetarafından şube doğrulaması:cred.type === 'public-key'ise onayıcred.toJSON()ile serileştirin ve WebAuthn doğrulama uç noktanıza POST edin. Sunucu HTTP404ile yanıt verdiğindePublicKeyCredential.signalUnknownCredential({ rpId: publicKey.rpId, credentialId: encoded.id })yöntemini çağırın.cred.type === 'password'ise şifre doğrulama uç noktanıza POST{ username: cred.id, password: cred.password }gönderin.
- Yerel kimlik bilgisi olmadığında
NotAllowedErrorişlemini gerçekleştirme:NotAllowedErrorişlemini yakalayın (cihazda yerel geçiş anahtarı veya kayıtlı şifre olmadığında ya da kullanıcı seçiciyi kapattığında hemen tetiklenir) ve standart oturum açma akışınıza geçin (ör. satır içi oturum açma iletişim kutusu açma veya dönüş URL'si ile/signinsayfasına gitme).
let isAuthenticating = false;
async function authenticateWithImmediateUi() {
const optionsResponse = await fetch('/api/login/options', { method: 'POST' });
if (!optionsResponse.ok) {
throw new Error('Failed to fetch authentication options');
}
const optionsJSON = await optionsResponse.json();
const publicKey = PublicKeyCredential.parseRequestOptionsFromJSON(optionsJSON);
// MANDATORY: Request both password and publicKey with uiMode: 'immediate' and NO signal parameter
const cred = await navigator.credentials.get({
password: true,
publicKey,
uiMode: 'immediate',
});
if (!cred) {
throw new DOMException('No credential returned', 'NotAllowedError');
}
if (cred.type === 'public-key') {
const encoded = cred.toJSON();
const verifyResponse = await fetch('/api/login/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(encoded),
});
if (verifyResponse.status === 404) {
await PublicKeyCredential.signalUnknownCredential({
rpId: publicKey.rpId || window.location.hostname,
credentialId: encoded.id, // Base64URL-encoded credential ID string
});
throw new Error('Passkey credential is no longer recognized by the server');
}
if (!verifyResponse.ok) {
throw new Error('Passkey verification failed');
}
return await verifyResponse.json();
}
if (cred.type === 'password') {
const verifyResponse = await fetch('/api/login/password', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username: cred.id,
password: cred.password,
}),
});
if (!verifyResponse.ok) {
throw new Error('Password verification failed');
}
return await verifyResponse.json();
}
throw new Error(`Unsupported credential type: ${cred.type}`);
}
// Example: Intercepting an inline micro-action (Favorite button) or Header Sign-in click
const favoriteButton = document.getElementById('favorite-btn');
favoriteButton.addEventListener('click', async (event) => {
event.preventDefault();
if (isAuthenticating) return;
// If already signed in, toggle the micro-action state immediately
if (document.body.dataset.signedIn === 'true') {
const nextPressed = favoriteButton.getAttribute('aria-pressed') !== 'true';
favoriteButton.setAttribute('aria-pressed', String(nextPressed));
return;
}
isAuthenticating = true;
try {
const user = await authenticateWithImmediateUi();
// Complete the user's intended action immediately
document.body.dataset.signedIn = 'true';
favoriteButton.setAttribute('aria-pressed', 'true');
updateHeaderUserState(user);
} catch (err) {
if (err.name === 'NotAllowedError') {
// No local credentials available or user dismissed dialog:
// transition smoothly to fallback sign-in UI or redirect with return URL
showFallbackSignInUi({ returnAction: 'favorite' });
return;
}
console.error('Authentication error:', err);
} finally {
isAuthenticating = false;
}
});
Yedek stratejiler
Anında kullanıcı arayüzü modu yedeği
- Progresif geliştirme deneyimi:
uiMode: 'immediate''yi progresif geliştirme olarak değerlendirin. Bir tarayıcının adından veya sürümünden destek çıkarımı yapmayın. Tek güvenilir destek sinyali,PublicKeyCredential.getClientCapabilities()aracılığıyla yapılancapabilities.immediateGetçalışma zamanı kontrolüdür. ZORUNLU:navigator.credentials.get({ uiMode: 'immediate' })yöntemini çağırmadan önce her zamancapabilities.immediateGetözelliğini kontrol edin.uiMode: 'immediate'özelliğini tanımayan tarayıcılar, bilinmeyen sözlük anahtarını yoksayar ve engelleyici bir kalıcı geçiş anahtarı ve QR kodu iletişim kutusu açar.capabilities.immediateGetyanlış olduğunda veyanavigator.credentials.get(),NotAllowedErrorile reddettiğinde uygulamanızın standart oturum açma deneyimine (ör. satır içi oturum açma modülü açma veya dönüş URL'si parametresiyle özel oturum açma sayfasına gitme) geri dönün. Böylece, desteklenmeyen tarayıcılardaki kullanıcılar mevcut oturum açma akışınızı değişmeden görür:
async function isImmediateUiSupported() {
if (!window.PublicKeyCredential || !PublicKeyCredential.getClientCapabilities) {
return false;
}
try {
const capabilities = await PublicKeyCredential.getClientCapabilities();
return Boolean(capabilities?.immediateGet);
} catch {
return false;
}
}
async function authenticateWithImmediateUi() {
// MANDATORY: Check immediateGet support so unsupported browsers do not open a modal QR dialog
const supported = await isImmediateUiSupported();
if (!supported) {
// Throw NotAllowedError so the caller's single catch path treats "unsupported" like "no local credentials"
throw new DOMException('Immediate UI mode is not supported', 'NotAllowedError');
}
// Proceed with fetching options and calling navigator.credentials.get({ password: true, publicKey, uiMode: 'immediate' })
}
İstemci özellikleri algılama yedeği
'webauthn-polyfills' öğesini her zaman yükleyin ve istemci modülünüze aktarın. Böylece PublicKeyCredential desteklendiğinde PublicKeyCredential.getClientCapabilities() kullanılabilir:
import 'webauthn-polyfills';
Şifre kimlik bilgileri yedeği
navigator.credentials.get() içinde publicKey ile birlikte password: true parametresinin iletilmesi, destekleyen tarayıcıların, tarayıcının şifre yöneticisindeki kayıtlı şifreleri birleştirilmiş anında hesap seçiciye dahil etmesine olanak tanır.
- Yedek deneyim:
PasswordCredentialözelliğini desteklemeyen tarayıcılar,password: trueseçeneğini yoksayar veyacapabilities.immediateGetözellik kontrolü aracılığıyla standart oturum açma formuna geri döner. Bu formda<input autocomplete="username webauthn">ve<input type="password" autocomplete="current-password">, yerel şifre otomatik doldurma özelliği sağlar.
Kolay JSON serileştirme yedeği
'webauthn-polyfills' öğesini her zaman yükleyin ve istemci paketine aktarın. Böylece PublicKeyCredential.parseRequestOptionsFromJSON() ve PublicKeyCredential.prototype.toJSON(), PublicKeyCredential öğesini destekleyen tarayıcılarda tutarlı bir şekilde çalışır:
import 'webauthn-polyfills';
Signal API senkronizasyonu yedek seçeneği
WebAuthn Signal API (webauthn-signals), sunucu HTTP 404 ile yanıt verdiğinde kullanıcının şifre yöneticisinden eski geçiş anahtarlarını kaldıran bir progresif geliştirmedir.
- Yedek deneyim: Aramaları
if (PublicKeyCredential.signalUnknownCredential)ile engelleyin. Desteklenmediğinde, sinyal çağrısını düzgün bir şekilde atlayın ve kullanıcıyı alternatif bir yöntemle oturum açmaya yönlendiren net bir mesaj gösterin.