पासकी मैनेज करना

इस गाइड में बताया गया है कि उपयोगकर्ताओं को अपनी रजिस्टर की गई पासकी देखने, उनका नाम बदलने, और उन्हें मिटाने की सुविधा कैसे दी जाए. साथ ही, Signal API का इस्तेमाल करके, सर्वर और उपयोगकर्ता के पासवर्ड मैनेजर के बीच सेव किए गए क्रेडेंशियल और उपयोगकर्ता की प्रोफ़ाइल की जानकारी को सिंक करने का तरीका भी बताया गया है.

सर्वर-साइड ऑपरेशन

यह ज़रूरी है कि आपकी बैकएंड डेटाबेस लेयर और एंडपॉइंट, रजिस्टर किए गए क्रेडेंशियल के लिए सामान्य CRUD कार्रवाइयों के साथ-साथ उपयोगकर्ता की प्रोफ़ाइल की जानकारी अपडेट करने की सुविधा भी दें. फ़्रेमवर्क के हिसाब से बनी लाइब्रेरी से अलग, सर्वर एंडपॉइंट को एक्सपोज़ करता है, ताकि:

  1. उपयोगकर्ता के सभी क्रेडेंशियल की सूची बनाना: साइन इन किए हुए उपयोगकर्ता के आईडी से मैच होने वाले सभी StoredPasskeyCredential रिकॉर्ड फ़ेच करें.
  2. क्रेडेंशियल के नाम अपडेट करना: किसी क्रेडेंशियल आईडी के लिए, पसंद के मुताबिक नया निकनेम स्वीकार करें और अपडेट को सर्वर पर सेव रखें.
  3. क्रेडेंशियल मिटाना: डेटाबेस से कोई क्रेडेंशियल आईडी हटाना.
  4. उपयोगकर्ता खाते की जानकारी अपडेट करना: साइन इन किए हुए उपयोगकर्ता की प्रोफ़ाइल के लिए, अपडेट किए गए name (उपयोगकर्ता नाम) और displayName की वैल्यू स्वीकार करें, ताकि क्लाइंट उन्हें पासकी की सेवा देने वाली कंपनी के साथ सिंक कर सके.
// 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);
});

क्लाइंट-साइड मैनेजमेंट यूज़र इंटरफ़ेस

एक खास सेटिंग पैनल रेंडर करें, ताकि उपयोगकर्ता अपने रजिस्टर किए गए पुष्टि करने के विकल्पों और खाते की प्रोफ़ाइल की जानकारी को आसानी से ऑडिट और मैनेज कर सकें:

  1. सेव की गई सूची दिखाएं: अपने एंडपॉइंट से सूची फ़ेच करें और क्रेडेंशियल की अलग-अलग लाइनें रेंडर करें. अगर रिस्पॉन्स खाली है, तो खाली स्थिति वाला मददगार मैसेज रेंडर करें. जैसे, "कोई पासकी नहीं मिली".
  2. एएजीयूआईडी मेटाडेटा मैप करना: हर पासकी के लिए, अपनी लोकल रजिस्ट्री में उसकी aaguid प्रॉपर्टी देखें, ताकि सेवा देने वाली कंपनी की जानकारी रेंडर की जा सके. ज़्यादा जानकारी के लिए, एएजीयूआईडी से पासकी की सेवा देने वाली कंपनी का पता लगाना सेक्शन देखें.
  3. हर आइटम के लिए यूज़र इंटरफ़ेस से जुड़ी ज़रूरी शर्तें: सूची कंटेनर की हर लाइन में ये चीज़ें रेंडर होनी चाहिए:
    • सेवा देने वाली कंपनी का आइकॉन: एएजीयूआईडी से मिली इमेज या डेटा यूआरआई.
    • सेवा देने वाली कंपनी/पसंद के मुताबिक नाम: एएजीयूआईडी से लिया गया नाम या उपयोगकर्ता की ओर से बदली गई स्ट्रिंग.
    • रजिस्ट्रेशन की तारीख: डेटाबेस में सेव किया गया रॉ एपॉक टाइमस्टैंप registeredAt. इसे क्लाइंट को दिखाने के लिए, आसानी से पढ़ी जा सकने वाली तारीख के तौर पर फ़ॉर्मैट किया जाता है.
    • पिछली बार इस्तेमाल किए जाने की तारीख: डेटाबेस में सेव किया गया रॉ एपॉक टाइमस्टैंप lastUsedAt. इसे क्लाइंट को दिखाने के लिए, किसी व्यक्ति के लिए आसानी से पढ़ी जा सकने वाली तारीख (अगर मौजूद हो) के तौर पर फ़ॉर्मैट किया जाता है.
    • नाम बदलने का बटन: इससे नाम बदलने की कार्रवाई ट्रिगर होती है, ताकि सर्वर (PUT /api/credential/:id) पर क्रेडेंशियल के लिए चुने गए निकनेम को अपडेट किया जा सके. ध्यान दें: किसी पासकी के निकनेम का नाम बदलने पर, सर्वर-साइड लेबल में बदलाव होता है. इससे signalCurrentUserDetails() को कॉल नहीं किया जाता.
    • Delete Button: Triggers deletion of the credential (DELETE /api/credential/:id) and synchronizes the remaining credential IDs via signalAllAcceptedCredentials().
  4. उपयोगकर्ता खाते का नाम बदलने के लिए यूज़र इंटरफ़ेस:
    • उपयोगकर्ता को अपने खाते का उपयोगकर्ता नाम (name) या displayName (PUT /api/user) अपडेट करने की सुविधा दें. साथ ही, उपयोगकर्ता की अपडेट की गई जानकारी को PublicKeyCredential.signalCurrentUserDetails() की मदद से, Password Manager के साथ तुरंत सिंक करें.
  5. शर्त के साथ "पासकी बनाएं" बटन:
    • मैनेजमेंट पेज पर, "पासकी बनाएं" रजिस्ट्रेशन ट्रिगर बटन को साफ़ तौर पर दिखाएं. इस यूज़र इंटरफ़ेस (यूआई) एलिमेंट को रेंडर करने से पहले, पेज को PublicKeyCredential.getClientCapabilities() का इस्तेमाल करके सुविधाओं का पता लगाना होगा. इससे यह पुष्टि की जा सकेगी कि प्लैटफ़ॉर्म ऑथेंटिकेटर काम करता है या नहीं. अगर पासकी काम नहीं करती हैं, तो इस बटन को छिपाएं और स्टैंडर्ड एमएफ़ए रजिस्ट्रेशन को बढ़ावा दें.
    • navigator.credentials.create() कॉल पर authenticatorSelection.authenticatorAttachment को छोड़कर, सुरक्षा कुंजी को रजिस्टर करने की अनुमति दें.

Signal API का सिंक्रनाइज़ेशन

Signal API की मदद से, ऐप्लिकेशन क्रेडेंशियल की स्थितियों को Password Manager के साथ शेयर कर सकता है. इससे उपयोगकर्ता के सिंक किए गए वॉल्ट और आपके बैकएंड डेटाबेस को एक साथ सिंक किया जा सकता है.

  • सुविधा की पहचान और पैरामीटर को कोड में बदलने का नियम:
    • Signal API के तरीकों (if (PublicKeyCredential.signalAllAcceptedCredentials) और if (PublicKeyCredential.signalCurrentUserDetails)) को शुरू करने से पहले, हमेशा उनकी सुविधाओं का पता लगाएं.
    • Signal API के तरीकों (signalAllAcceptedCredentials, signalCurrentUserDetails) को पास किए गए सभी userId और क्रेडेंशियल आईडी पैरामीटर, Base64URL-एन्कोड की गई स्ट्रिंग होनी चाहिए.
  • पेज लोड सिंक करने की प्रोसेस शुरू करना:
    • मैनेजमेंट पेज लोड होने पर, ऐप्लिकेशन को signalAllAcceptedCredentials() को अपने-आप शुरू करना चाहिए. उदाहरण के लिए, DOMContentLoaded इवेंट लिसनर, मॉड्यूल शुरू करने या फ़्रेमवर्क कॉम्पोनेंट माउंट हुक, जैसे कि useEffect के अंदर.
  • मैनेजमेंट अपडेट सिंक करना:
    • क्रेडेंशियल फ़ेच करने के बाद, ऐप्लिकेशन को क्रेडेंशियल मिटाने वाले क्लिक हैंडलर में तुरंत signalAllAcceptedCredentials() को शुरू करना होगा.
    • ऐप्लिकेशन को उपयोगकर्ता खाते का नाम बदलने (name या displayName) के क्लिक हैंडलर पोस्ट-फ़ेच के तुरंत बाद, signalCurrentUserDetails() को शुरू करना चाहिए.
// 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);

एएजीयूआईडी से पासकी की सेवा देने वाली कंपनी का पता लगाना

एएजीयूआईडी (ऑथेंटिकेटर अटेस्टेशन ग्लोबली यूनीक आइडेंटिफ़ायर), 128-बिट का आइडेंटिफ़ायर होता है. यह ऑथेंटिकेटर के मॉडल को दिखाता है, न कि किसी खास इंस्टेंस को. पासकी रजिस्ट्रेशन के दौरान, इसे पुष्टि करने वाले डेटा में शामिल किया जाता है. इसका इस्तेमाल यह पता लगाने के लिए किया जा सकता है कि पासकी की सेवा देने वाली किस कंपनी (जैसे, Google Password Manager, iCloud Keychain, 1Password) ने क्रेडेंशियल बनाया है.

एएजीयूआईडी का इस्तेमाल सिर्फ़ पासकी मैनेज करने में उपयोगकर्ताओं की मदद करने के लिए किया जाना चाहिए. इसमें तब तक बदलाव किया जा सकता है, जब तक इसकी पुष्टि क्रिप्टोग्राफ़िक तरीके से न की गई हो. फ़िलहाल, प्लैटफ़ॉर्म पासकी में यह सुविधा काम नहीं करती.

1. AAGUID रजिस्ट्री

कम्यूनिटी की ओर से मैनेज की जाने वाली JSON मैपिंग, एएजीयूआईडी को सेवा देने वाली कंपनी के नाम और आइकॉन से मैप करती है. यह मैपिंग यहां उपलब्ध है:

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

हर एंट्री का स्कीमा इस तरह होता है:

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

2. रजिस्ट्रेशन के बाद एएजीयूआईडी का इस्तेमाल करना

रजिस्ट्रेशन के जवाब की पुष्टि करने के बाद, रजिस्ट्रेशन के नतीजे से aaguid को पढ़ें. इसके बाद, क्रेडेंशियल के name और providerIcon को भरने के लिए, रजिस्ट्री में इसे देखें:

रजिस्ट्री में एएजीयूआईडी देखने से पहले, देखें कि यह '00000000-0000-0000-0000-000000000000' के बराबर है या नहीं. अगर ऐसा है, तो रजिस्ट्री लुकअप को छोड़ें और name को फ़ॉलबैक पर सेट करें.जैसे, उपयोगकर्ता एजेंट से डिवाइस का नाम या "पासकी की सेवा देने वाली अज्ञात कंपनी". साथ ही, providerIcon को undefined पर सेट करें. सिर्फ़ उन एएजीयूआईडी के लिए रजिस्ट्री देखें जो शून्य नहीं हैं.

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,
  };
}

फ़ॉलबैक रणनीतियां

पासकी की सुविधा का पता लगाने के लिए फ़ॉलबैक

हमेशा 'webauthn-polyfills' इंस्टॉल करें और उसे कॉन्टेक्स्ट में इंपोर्ट करें. ध्यान रखें कि जब तक PublicKeyCredential का इस्तेमाल किया जा सकता है, तब तक PublicKeyCredential.getClientCapabilities का भी इस्तेमाल किया जा सकता है.

import 'webauthn-polyfills';

Signal API Synchronization Fallback

अगर ब्राउज़र PublicKeyCredential.parseRequestOptionsFromJSON के साथ काम नहीं करता है, तो 'webauthn-polyfills' का इस्तेमाल करें:

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

इससे PublicKeyCredential.prototype.toJSON के साथ काम करने की सुविधा भी जुड़ जाएगी.