Geçiş Anahtarı Yönetimi

Bu kılavuzda, kullanıcıların kayıtlı geçiş anahtarlarını görüntülemesine, yeniden adlandırmasına ve silmesine nasıl izin verileceği ayrıntılı olarak açıklanmaktadır. Bu sırada, kayıtlı kimlik bilgileri ve kullanıcı profili ayrıntıları, Signal API kullanılarak sunucu ile kullanıcının şifre yöneticileri arasında senkronize edilir.

Sunucu Tarafı İşlemler

Arka uç veritabanı katmanınız ve uç noktalarınız, kayıtlı kimlik bilgileri için yaygın CRUD işlemlerini ve kullanıcı profili ayrıntılarını güncellemeyi desteklemelidir. Çerçeveye özgü kitaplıklardan ayrılan sunucu, uç noktaları şunlara gösterir:

  1. Tüm kullanıcı kimlik bilgilerini listeleme: Oturum açmış kullanıcının kimliğiyle eşleşen tüm StoredPasskeyCredential kayıtlarını getirin.
  2. Kimlik bilgisi adlarını güncelleme: Belirli bir kimlik bilgisi kimliği için yeni bir özel takma adı kabul edin ve güncellemeyi sunucuda kalıcı hale getirin.
  3. Kimlik bilgilerini silme: Belirli bir kimlik bilgisi kimliğini veritabanından kaldırın.
  4. Kullanıcı hesabı ayrıntılarını güncelleme: Oturum açmış kullanıcının profili için güncellenmiş name (kullanıcı adı) ve displayName değerlerini kabul edin. Böylece istemci, bunları geçiş anahtarı sağlayıcısıyla senkronize edebilir.
// Node.js routing example for credential CRUD and user profile rename
router.get('/api/credentials', checkUserAuthenticated, async (req, res) => {
  const list = await db.findCredentialsByUserId(req.user.id);
  return res.json(list);
});

router.put('/api/credential/:id', checkUserAuthenticated, async (req, res) => {
  const { id } = req.params;
  const { name } = req.body;
  const cred = await db.findCredentialById(id);
  if (!cred || cred.passkeyUserId !== req.user.id) {
    return res.status(404).json({ error: 'Credential not found.' });
  }
  cred.name = name;
  await db.saveCredential(cred);
  return res.json(cred);
});

router.delete('/api/credential/:id', checkUserAuthenticated, async (req, res) => {
  const { id } = req.params;
  const cred = await db.findCredentialById(id);
  if (!cred || cred.passkeyUserId !== req.user.id) {
    return res.status(404).json({ error: 'Credential not found.' });
  }
  await db.deleteCredential(id);
  return res.json({ success: true });
});

router.put('/api/user', checkUserAuthenticated, async (req, res) => {
  const { name, displayName } = req.body;
  const updatedUser = await db.updateUser(req.user.id, { name, displayName });
  return res.json(updatedUser);
});

İstemci Tarafı Yönetim Kullanıcı Arayüzü

Kullanıcıların kayıtlı kimlik doğrulama seçeneklerini ve hesap profili ayrıntılarını kolayca denetleyip yönetebileceği özel bir ayarlar paneli oluşturun:

  1. Kayıtlı listeyi göster: Listeyi uç noktanızdan alın ve her bir kimlik bilgisi satırını oluşturun. Yanıt boşsa faydalı bir boş durum mesajı (ör. "Geçiş anahtarı bulunamadı") oluşturun.
  2. AAGUID meta verilerini eşleme: Her geçiş anahtarının sağlayıcı ayrıntılarını oluşturmak için yerel kayıt defterinizde aaguid özelliğini arayın. Daha fazla bilgi için AAGUID'den geçiş anahtarı sağlayıcısını belirleme bölümüne bakın.
  3. Öğe Başına Kullanıcı Arayüzü Şartları: Liste kapsayıcısındaki her satır şunları oluşturmalıdır:
    • Sağlayıcı simgesi: AAGUID'den türetilen resim veya veri URI'si.
    • Sağlayıcı/Özel Ad: AAGUID'den türetilen ad veya kullanıcı tarafından yeniden adlandırılan dize.
    • Kayıt Tarihi: Veritabanında saklanan ham zaman damgası registeredAt, istemci ekranında okunabilir bir tarih biçimine dönüştürülür.
    • Son Kullanım Tarihi: Veritabanında saklanan ham epoch zaman damgası lastUsedAt, istemci ekranı için okunabilir bir tarihe (varsa) biçimlendirilir.
    • Yeniden Adlandır Düğmesi: Sunucudaki (PUT /api/credential/:id) kimlik bilgisinin özel takma adını güncellemek için yeniden adlandırma işlemini tetikler. Not: Tek bir geçiş anahtarının takma adını yeniden adlandırmak, sunucu tarafında bir etiket değişikliğidir ve signalCurrentUserDetails()'yi çağırmaz.
    • Sil düğmesi: Kimlik bilgisinin (DELETE /api/credential/:id) silinmesini tetikler ve kalan kimlik bilgilerini signalAllAcceptedCredentials() üzerinden senkronize eder.
  4. Kullanıcı Hesabı Yeniden Adlandırma Kullanıcı Arayüzü:
    • Kullanıcının hesap kullanıcı adını (name) veya displayName (PUT /api/user) güncellemesine olanak tanıyan bir kontrol sağlayın ve güncellenen kullanıcı ayrıntılarını PublicKeyCredential.signalCurrentUserDetails() aracılığıyla şifre yöneticisiyle hemen senkronize edin.
  5. Koşullu "Geçiş Anahtarı Oluştur" düğmesi:
    • Yönetim sayfasında belirgin bir "Geçiş anahtarı oluştur" kayıt tetikleyici düğmesi sunun. Bu kullanıcı arayüzü öğesini oluşturmadan önce sayfa, platform kimlik doğrulayıcının desteklendiğini doğrulamak için PublicKeyCredential.getClientCapabilities() kullanarak özellikleri algılamalıdır. Geçiş anahtarları desteklenmiyorsa bu düğmeyi gizleyin ve kullanıcıları standart çok faktörlü kimlik doğrulama kayıtlarına yönlendirin.
    • navigator.credentials.create() çağrısında authenticatorSelection.authenticatorAttachment atlanarak güvenlik anahtarı kaydedilmesine izin ver.

Signal API Senkronizasyonu

Signal API, uygulamanın kimlik bilgisi durumlarını şifre yöneticilerine iletmesine olanak tanır. Böylece kullanıcının senkronize edilmiş kasaları ve arka uç veritabanınız senkronize edilir.

  • Özellik Algılama ve Parametre Kodlama Kuralı:
    • Signal API yöntemlerini çağırmadan önce her zaman özellik algılama yapın (if (PublicKeyCredential.signalAllAcceptedCredentials) ve if (PublicKeyCredential.signalCurrentUserDetails)).
    • Signal API yöntemlerine (signalAllAcceptedCredentials, signalCurrentUserDetails) iletilen tüm userId ve kimlik bilgisi kimliği parametreleri Base64URL kodlamalı dizeler OLMALIDIR.
  • Sayfa yükleme senkronizasyonunu başlatma:
    • Uygulama, yönetim sayfası yüklendiğinde signalAllAcceptedCredentials() yöntemini otomatik olarak çağırmalıdır (örneğin, bir DOMContentLoaded etkinlik işleyici, modül başlatma veya useEffect gibi bir çerçeve bileşeni bağlama kancası içinde).
  • Yönetim Güncellemeleri Senkronizasyonu:
    • Uygulama, kimlik bilgisi silme tıklama işleyicinizde getirme işleminden hemen sonra signalAllAcceptedCredentials() yöntemini çağırmalıdır.
    • Uygulama, kullanıcı hesabınızı yeniden adlandırma (name veya displayName) tıklama işleyicisi getirme işleminden hemen sonra signalCurrentUserDetails() yöntemini çağırmalıdır.
// Client-side management synchronization ES module
import { listFetch, renameCredentialFetch, updateUserFetch, deleteFetch } from './api.js';

// Base64URL-encoded User ID string (illustration only)
const base64UrlUserId = "M2YPl-KGnA8";

async function syncAcceptedCredentials(currentCredentialsList) {
  if (!window.PublicKeyCredential || !PublicKeyCredential.signalAllAcceptedCredentials) return;
  try {
    const credentialIds = currentCredentialsList.map(c => c.id); // Array of Base64URL credential ID strings
    
    await PublicKeyCredential.signalAllAcceptedCredentials({
      rpId, // RP ID must match the one defined on the server
      userId: base64UrlUserId, // User ID Base64URL-encoded string
      allAcceptedCredentialIds: credentialIds
    });
  } catch (e) {
    console.error('SignalAllAcceptedCredentials sync failure:', e);
  }
}

async function loadManagementPanel() {
  const response = await listFetch();
  const list = await response.json();
  
  renderUI(list);
  // Sync accepted credentials on initial load
  await syncAcceptedCredentials(list);
}

async function performDelete(credentialId) {
  const response = await deleteFetch(credentialId);
  if (response.ok) {
    const updatedResponse = await listFetch();
    const updatedList = await updatedResponse.json();
    
    renderUI(updatedList);
    // Sync remaining accepted credentials after deletion
    await syncAcceptedCredentials(updatedList);
  }
}

// Renaming a passkey's nickname updates the server record only
async function performCredentialRename(credentialId, newCredentialName) {
  const response = await renameCredentialFetch(credentialId, { name: newCredentialName });
  if (response.ok) {
    const updatedResponse = await listFetch();
    renderUI(await updatedResponse.json());
  }
}

// Renaming the user's account username or displayName triggers signalCurrentUserDetails
async function performUserRename(rpId, userId, updatedName, updatedDisplayName) {
  const response = await updateUserFetch({ name: updatedName, displayName: updatedDisplayName });
  if (response.ok && window.PublicKeyCredential && PublicKeyCredential.signalCurrentUserDetails) {
    try {
      await PublicKeyCredential.signalCurrentUserDetails({
        rpId, // RP ID must match the one defined on the server
        userId, // Base64URL-encoded user ID
        name: updatedName, // Updated username
        displayName: updatedDisplayName // Updated display name
      });
    } catch (e) {
      console.error('SignalCurrentUserDetails sync failure:', e);
    }
  }
}

window.addEventListener('DOMContentLoaded', loadManagementPanel);

AAGUID'den geçiş anahtarı sağlayıcısını belirleme

AAGUID (Kimlik Doğrulayıcı Onayı Küresel Benzersiz Tanımlayıcısı), belirli bir örneği değil, kimlik doğrulayıcının modelini temsil eden 128 bitlik bir tanımlayıcıdır. Geçiş anahtarı kaydı sırasında kimlik doğrulayıcı verilerine dahil edilir ve hangi geçiş anahtarı sağlayıcısının (ör. Google Şifre Yöneticisi, iCloud Anahtar Zinciri, 1Password) kimlik bilgisi oluşturduğunu belirlemek için kullanılabilir.

AAGUID yalnızca kullanıcılara geçiş anahtarı yönetiminde yardımcı olmak için kullanılmalıdır. Kriptografik olarak onaylanmadığı sürece değiştirilebilir. Platform geçiş anahtarları şu anda bunu desteklemez.

1. AAGUID Kayıt Defteri

AAGUID'lerin sağlayıcı adları ve simgeleriyle topluluk tarafından yönetilen JSON eşlemesi şu adreste bulunabilir:

https://raw.githubusercontent.com/passkeydeveloper/passkey-authenticator-aaguids/refs/heads/main/combined_aaguid.json

Her giriş aşağıdaki şemaya sahiptir:

{
  "<aaguid-uuid>": {
    "name": "Provider Name",
    "icon_light": "data:image/png;base64,...",
    "icon_dark": "data:image/png;base64,..."
  }
}

2. Kayıttan sonra AAGUID'yi kullanma

Kayıt yanıtını doğruladıktan sonra kayıt sonucundaki aaguid değerini okuyun ve kimlik bilgisinin name ve providerIcon değerlerini doldurmak için kayıt defterinde arayın:

Kayıt defterinde AAGUID'yi aramadan önce '00000000-0000-0000-0000-000000000000' değerine eşit olup olmadığını kontrol edin. Bu durumda, kayıt defteri aramasını atlayın ve name değerini bir yedek (ör. kullanıcı aracısından alınan cihaz adı veya "Bilinmeyen geçiş anahtarı sağlayıcı") ve providerIcon değerini undefined olarak ayarlayın. Yalnızca sıfırlanmamış AAGUID'ler için kayıt defterine bakın.

import aaguids from './aaguids.json' with { type: 'json' };

const { aaguid } = registrationInfo;
if (aaguid === '00000000-0000-0000-0000-000000000000') {
  // use the device name as the passkey provider based on
  // the information derived from the user agent string,
  // or just say "Unknown passkey provider"
} else {
  const provider = aaguids[aaguid];
  const credential = {
    // ...other fields
    aaguid,
    name: provider?.name || 'Unknown passkey provider',
    providerIcon: provider?.icon_light,
  };
}

Yedek Stratejiler

Geçiş anahtarı özelliği algılama yedeği

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

import 'webauthn-polyfills';

Signal API Senkronizasyonu Yedeklemesi

Tarayıcı PublicKeyCredential.parseRequestOptionsFromJSON özelliğini desteklemiyorsa "webauthn-polyfills" kullanın:

<script type="module"> 
  if (!PublicKeyCredential.parseRequestOptionsFromJSON) { 
     await import('https://unpkg.com/webauthn-polyfills'); 
   } 
 </script> 

Bu işlem, PublicKeyCredential.prototype.toJSON için de destek ekler.