पासकी रजिस्ट्रेशन

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

डेटाबेस से जुड़ी ज़रूरी शर्तें

पासकी के रजिस्ट्रेशन की सुविधा देने के लिए, आपके डेटाबेस क्रेडेंशियल टेबल में ये फ़ील्ड सेव होने चाहिए:

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
}

सर्वर-साइड

विकल्प जनरेट करने की सुविधा

ऐसा एंडपॉइंट बनाएं जो WebAuthn क्रेडेंशियल बनाने के पैरामीटर जनरेट करता हो. क्रिप्टोग्राफ़ी को खुद से लागू करने के बजाय, कैटगरी के स्टैंडर्ड के हिसाब से जांची गई लाइब्रेरी पर भरोसा करें.

  1. पहले से तय आरपी आईडी का इस्तेमाल करना: पहले से तय सही आरपी आईडी को कॉन्स्टेंट स्ट्रिंग के तौर पर इस्तेमाल करें.
  2. सुरक्षित चैलेंज बनाना: सर्वर पर हाई-एंट्रॉपी वाला, क्रिप्टोग्राफ़िक तरीके से सुरक्षित रैंडम बफ़र जनरेट करें. इसे उपयोगकर्ता के सेशन में सुरक्षित तरीके से सेव करें. साथ ही, विकल्पों को डिलीवर करने के लिए, इसे Base64URL के तौर पर कोड में बदलें.
  3. डुप्लीकेट पासकी बनाने से बचें: उपयोगकर्ता के पहले से रजिस्टर किए गए क्रेडेंशियल आईडी को excludeCredentials विकल्प के कलेक्शन में मैप करें. इससे, ऑथेंटिकेटर को पासकी की सेवा देने वाली कंपनी के एक ही खाते पर, डुप्लीकेट क्रेडेंशियल रजिस्टर करने से रोका जाता है.
  4. खोजे जा सकने वाले क्रेडेंशियल लागू करें: खोजे जा सकने वाले क्रेडेंशियल का अनुरोध करने के लिए, authenticatorSelection विकल्पों में requireResidentKey: true और residentKey: "required" सेट करें. खोजे जा सकने वाले साइन-इन के लिए, यह ज़रूरी है.
  5. उपयोगकर्ता की पुष्टि करने की सुविधा को कॉन्फ़िगर करें: userVerification: "preferred" या userVerification: "required" डालें. नीति का पालन करने से जुड़े कई मामलों (जैसे, फ़ाइनेंस, स्वास्थ्य सेवा) में, 'required' को उपयोगकर्ता की पुष्टि करने की ज़रूरत होती है.
  6. अटैचमेंट का दायरा तय करना:
    • प्रमोशन फ़्लो: स्टैंडर्ड पासवर्ड से साइन-इन करने या साइनअप के बाद प्रमोशन के तुरंत बाद पासकी बनाने का सुझाव देते समय, authenticatorAttachment: "platform" को सेट करें. इससे प्लैटफ़ॉर्म ऑथेंटिकेटर को लागू किया जा सकेगा और बाहरी सुरक्षा कुंजी के प्रॉम्प्ट को बायपास किया जा सकेगा.
    • मैनेजमेंट फ़्लो: जब किसी खास सेटिंग या सुरक्षा पैनल से कॉल किया जाता है, जहां प्लैटफ़ॉर्म ऑथेंटिकेटर के अलावा बाहरी सुरक्षा कुंजियां भी काम करती हैं, तो authenticatorAttachment प्रॉपर्टी को पूरी तरह से हटा दें.
    • सलाह: एक ही एंडपॉइंट से दोनों फ़्लो को शर्त के साथ मैनेज करने के लिए, promotion: boolean अनुरोध फ़्लैग स्वीकार करें.
// 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" }),
  },
};

पुष्टि

  1. चैलेंज की पुष्टि करना: सेशन से जुड़े चैलेंज की पुष्टि सुरक्षित तरीके से करना.
  2. उपयोगकर्ता की मौजूदगी की पुष्टि करना:
    • पक्का करें कि पार्स किए गए ऑथेंटिकेटर डेटा में दिखाया गया यूज़र प्रेज़ेंट (यूपी) फ़्लैग true हो. इससे, क्रेडेंशियल बनाते समय उपयोगकर्ता की मौजूदगी की पुष्टि होती है.
  3. 'पसंदीदा' के लिए पुष्टि करने की प्रक्रिया को आसान बनाना:
    • जब क्रिएशन के विकल्प userVerification: "preferred" के बारे में बताया जाता है, तो सर्वर साइड से की जाने वाली पुष्टि के कॉल को requireUserVerification: false के साथ कॉन्फ़िगर करना ज़रूरी है. ऐसा न करने पर, उपयोगकर्ता की पुष्टि किए बिना रजिस्टर करने वाले ऑथेंटिकेटर (जैसे, स्क्रीन लॉक बंद होने पर), सर्वर की पुष्टि न होने की गलत सूचनाएं ट्रिगर करेंगे.

क्लाइंट-साइड लॉजिक

  1. पेज लोड होने पर यूज़र इंटरफ़ेस (यूआई) को गेट करना:
    • पेज लोड होने पर, PublicKeyCredential.getClientCapabilities() को कॉल करें. साथ ही, अगर conditionalGet या passkeyPlatformAuthenticator उपलब्ध नहीं है, तो "पासकी बनाएं" बटन को बंद करें.
  2. Invoke creation & Serialize: PublicKeyCredential.parseCreationOptionsFromJSON() का इस्तेमाल करके, सर्वर के विकल्पों को डिकोड करें. इसके बाद, नतीजे के तौर पर मिले कॉन्फ़िगरेशन को navigator.credentials.create() को पास करें.
    • पुष्टि करने वाले एंडपॉइंट को फ़ेच करने से पहले, AuthenticatorAttestationResponse को मान्य और JSON-सीरियलाइज़ेबल ऑब्जेक्ट में कोड करने के लिए, credential.toJSON() को कॉल करें.
  3. WebAuthn से जुड़े अपवादों को मैनेज करना:
    • InvalidStateError: इससे मिलती-जुलती पासकी पहले से मौजूद है (excludeCredentials से मैच हुई).
    • NotAllowedError: उपयोगकर्ता ने पासकी से पुष्टि करने का डायलॉग बॉक्स रद्द कर दिया या उसकी समयसीमा खत्म हो गई.
    • AbortError: कार्रवाई को बीच में ही रद्द कर दिया गया है.
    • SecurityError: सुरक्षित ऑरिजिन (एचटीटीपीएस) या आरपी आईडी के मेल न खाने से जुड़ी गड़बड़ियां (कॉन्फ़िगरेशन से जुड़ी समस्याएं).
  4. सिग्नल एपीआई के लिए Try/Catch Segregation:
    • सर्वर से पुष्टि करने वाले fetch() कॉल को try/catch ब्लॉक में रैप करें. सर्वर की पुष्टि करने के लिए फ़ेच करने की प्रोसेस पूरी न होने पर, signalUnknownCredential() को कॉल करें. ऐसा तब होता है, जब कोई स्टेटस response.ok === false या नेटवर्क थ्रो होता है.
// 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
    });
  }
}

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

Signal API Synchronization Fallback

WebAuthn Signal API (webauthn-signals) एक प्रोग्रेसिव ऑप्टिमाइज़ेशन है. इसका इस्तेमाल, पासवर्ड मैनेजर को सर्वर क्रेडेंशियल की स्थिति के साथ सिंक रखने के लिए किया जाता है.

  • फ़ॉलबैक अनुभव: अगर ब्राउज़र में PublicKeyCredential.signalUnknownCredential काम नहीं करता है, तो कॉल को सुरक्षित तरीके से बायपास किया जाना चाहिए. इसके लिए, सुविधा की पहचान करने वाले गेटिंग (if (PublicKeyCredential.signalUnknownCredential)) का इस्तेमाल करें. साथ ही, सर्वर साइड से की जाने वाली पुष्टि, मैनेजर के अपडेट को ट्रिगर किए बिना, गड़बड़ी को लॉग करती है.

Easy JSON Serialization Fallback

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

import 'webauthn-polyfills';