सर्वर-साइड पासकी रजिस्ट्रेशन

खास जानकारी

पासकी रजिस्ट्रेशन के बारे में खास जानकारी यहां दी गई है:

पासकी के रजिस्ट्रेशन फ़्लो

  • पासकी बनाने के विकल्प बताएं. उन्हें क्लाइंट को भेजें, ताकि आप उन्हें पासकी बनाने वाले कॉल में भेज सकें: वेब पर WebAuthn API कॉल navigator.credentials.create और Android पर credentialManager.createCredential. जब उपयोगकर्ता पासकी बनाने की पुष्टि करता है, तब पासकी बनाने के लिए कॉल बंद हो जाता है और क्रेडेंशियल PublicKeyCredential दिखता है.
  • क्रेडेंशियल की पुष्टि करें और उसे सर्वर पर सेव करें.

नीचे दिए सेक्शन में, हर चरण से जुड़ी खास जानकारी दी गई है.

क्रेडेंशियल बनाने के विकल्प बनाएं

आपको सर्वर पर सबसे पहले PublicKeyCredentialCreationOptions ऑब्जेक्ट बनाना होगा.

इसके लिए, अपनी FIDO सर्वर साइड लाइब्रेरी पर भरोसा करें. आम तौर पर, यह एक यूटिलिटी फ़ंक्शन उपलब्ध कराता है, जिससे आपके लिए ये विकल्प बनाए जा सकते हैं. SimpleWebAuthn के ऑफ़र, जैसे कि generateRegistrationOptions.

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

PublicKeyCredentialCreationOptions के कुछ फ़ील्ड कॉन्स्टेंट हो सकते हैं. अन्य वैल्यू को सर्वर पर डाइनैमिक तौर पर तय किया जाना चाहिए:

  • rpId: सर्वर पर आरपी आईडी भरने के लिए, सर्वर साइड फ़ंक्शन या वैरिएबल का इस्तेमाल करें, जो आपको अपने वेब ऐप्लिकेशन का होस्टनेम देते हैं, जैसे कि example.com.
  • user.name और user.displayName: इन फ़ील्ड में जानकारी भरने के लिए, साइन इन किए हुए उपयोगकर्ता के सेशन की जानकारी का इस्तेमाल करें. अगर उपयोगकर्ता साइन अप के दौरान पासकी बना रहा है, तो उसके नए खाते की जानकारी का इस्तेमाल करें. आम तौर पर, user.name एक ईमेल पता होता है. यह आरपीपी के लिए यूनीक होता है. user.displayName, उपयोगकर्ता के लिए आसान नाम है. ध्यान दें कि सभी प्लैटफ़ॉर्म, displayName का इस्तेमाल नहीं करेंगे.
  • user.id: खाता बनने के बाद जनरेट होने वाली एक रैंडम और यूनीक स्ट्रिंग. बदलाव करने लायक उपयोगकर्ता नाम के उलट, यह हमेशा के लिए होना चाहिए. यूज़र आईडी, किसी खाते की पहचान करता है. हालांकि, इसमें व्यक्तिगत पहचान से जुड़ी कोई जानकारी शामिल नहीं होनी चाहिए. ऐसा हो सकता है कि आपके सिस्टम में पहले से ही यूज़र आईडी मौजूद हो. हालांकि, अगर ज़रूरत हो, तो खास तौर पर पासकी के लिए एक यूज़र आईडी बनाएं, ताकि उस में व्यक्तिगत पहचान से जुड़ी जानकारी न हो.
  • excludeCredentials: पासकी की सेवा देने वाली कंपनी से पासकी का डुप्लीकेट बनाने से बचने के लिए, मौजूदा क्रेडेंशियल के आईडी की सूची. इस फ़ील्ड में अपने-आप जानकारी भरने के लिए, अपने डेटाबेस में इस उपयोगकर्ता के मौजूदा क्रेडेंशियल खोजें. अगर कोई पासकी पहले से मौजूद है, तो उसे बनाने से रोकें पर जाकर, दी गई जानकारी देखें.
  • challenge: क्रेडेंशियल के रजिस्ट्रेशन के लिए, पुष्टि करना तब तक काम का नहीं है, जब तक पुष्टि करने की सुविधा का इस्तेमाल नहीं किया जाता. यह पासकी देने वाले की पहचान और उससे मिलने वाले डेटा की पुष्टि करने की बेहतर तकनीक है. हालांकि, अगर आपने पुष्टि करने की प्रक्रिया का इस्तेमाल नहीं किया है, तब भी चैलेंज को फ़ील्ड में भरना ज़रूरी है. ऐसे में, इस चैलेंज को एक 0 पर सेट किया जा सकता है, ताकि इसे आसानी से पूरा किया जा सके. पुष्टि करने के लिए सुरक्षित चैलेंज बनाने के निर्देश, सर्वर साइड पासकी की पुष्टि करने की सुविधा में उपलब्ध हैं.

कोड में बदलना और डिकोड करना

सर्वर की ओर से भेजे गए PublicKeyक्रेडेंशियलCreationOptions
PublicKeyCredentialCreationOptions को सर्वर से भेजा गया. challenge, user.id, और excludeCredentials.credentials को सर्वर साइड से base64URL में एन्कोड किया जाना चाहिए, ताकि PublicKeyCredentialCreationOptions को एचटीटीपीएस पर डिलीवर किया जा सके.

PublicKeyCredentialCreationOptions में ArrayBuffer वाले फ़ील्ड शामिल हैं. इसलिए, वे JSON.stringify() के साथ काम नहीं करते. इसका मतलब है कि इस समय, एचटीटीपीएस पर PublicKeyCredentialCreationOptions डिलीवर करने के लिए, कुछ फ़ील्ड को सर्वर पर मैन्युअल तरीके से कोड में बदलना होगा. इसके लिए base64URL का इस्तेमाल करना होगा. इसके बाद, उन्हें क्लाइंट पर डिकोड करना होगा.

  • आम तौर पर, सर्वर पर कोड में बदलने और डिकोड करने का काम आपकी FIDO सर्वर साइड लाइब्रेरी से किया जाता है.
  • क्लाइंट पर, कोड में बदलने और डिकोड करने का काम मैन्युअल तरीके से इस समय किया जाना चाहिए. आने वाले समय में यह आसान हो जाएगा: विकल्पों को JSON के तौर पर PublicKeyCredentialCreationOptions में बदलने का एक तरीका उपलब्ध होगा. Chrome में लागू करने की स्थिति देखें.

कोड का उदाहरण: क्रेडेंशियल बनाने के विकल्प बनाना

हम अपने उदाहरणों में SimpleWebAuthn लाइब्रेरी का इस्तेमाल कर रहे हैं. यहां, हम सार्वजनिक कुंजी के क्रेडेंशियल बनाने के विकल्प को इसके generateRegistrationOptions फ़ंक्शन के लिए उपलब्ध कराते हैं.

import {
  generateRegistrationOptions,
  verifyRegistrationResponse,
  generateAuthenticationOptions,
  verifyAuthenticationResponse
} from '@simplewebauthn/server';
import { isoBase64URL } from '@simplewebauthn/server/helpers';

router.post('/registerRequest', csrfCheck, sessionCheck, async (req, res) => {
  const { user } = res.locals;
  // Ensure you nest verification function calls in try/catch blocks.
  // If something fails, throw an error with a descriptive error message.
  // Return that message with an appropriate error code to the client.
  try {
    // `excludeCredentials` prevents users from re-registering existing
    // credentials for a given passkey provider
    const excludeCredentials = [];
    const credentials = Credentials.findByUserId(user.id);
    if (credentials.length > 0) {
      for (const cred of credentials) {
        excludeCredentials.push({
          id: isoBase64URL.toBuffer(cred.id),
          type: 'public-key',
          transports: cred.transports,
        });
      }
    }

    // Generate registration options for WebAuthn create
    const options = generateRegistrationOptions({
      rpName: process.env.RP_NAME,
      rpID: process.env.HOSTNAME,
      userID: user.id,
      userName: user.username,
      userDisplayName: user.displayName || '',
      attestationType: 'none',
      excludeCredentials,
      authenticatorSelection: {
        authenticatorAttachment: 'platform',
        requireResidentKey: true
      },
    });

    // Keep the challenge in the session
    req.session.challenge = options.challenge;

    return res.json(options);
  } catch (e) {
    console.error(e);
    return res.status(400).send({ error: e.message });
  }
});

सार्वजनिक कुंजी सेव करना

सर्वर की ओर से भेजे गए PublicKeyक्रेडेंशियलCreationOptions
navigator.credentials.create PublicKeyCredential ऑब्जेक्ट दिखाता है.

क्लाइंट पर navigator.credentials.create पूरी होने का मतलब है कि पासकी बना दी गई है. PublicKeyCredential ऑब्जेक्ट दिखाया जाता है.

PublicKeyCredential ऑब्जेक्ट में एक AuthenticatorAttestationResponse ऑब्जेक्ट होता है. इससे यह पता चलता है कि पासकी बनाने के लिए, क्लाइंट के निर्देश पर पासकी की सेवा देने वाली कंपनी का जवाब क्या है. इसमें उस नए क्रेडेंशियल के बारे में जानकारी होती है जिसकी ज़रूरत आरपी के तौर पर, आपको बाद में उपयोगकर्ता की पुष्टि करने के लिए करनी होती है. AuthenticatorAttestationResponse के बारे में ज़्यादा जानने के लिए, अपेंडिक्स: AuthenticatorAttestationResponse में जाएं.

PublicKeyCredential ऑब्जेक्ट को सर्वर पर भेजें. दस्तावेज़ मिलने के बाद, उसकी पुष्टि करें.

पुष्टि करने का यह चरण अपनी FIDO सर्वर साइड लाइब्रेरी को दें. आम तौर पर, इस मकसद के लिए यह एक यूटिलिटी फ़ंक्शन उपलब्ध कराता है. SimpleWebAuthn के ऑफ़र, जैसे कि verifyRegistrationResponse. अपेंडिक्स: रजिस्ट्रेशन के जवाब की पुष्टि करना सेक्शन में जानें कि हुड के तहत क्या हो रहा है.

पुष्टि हो जाने के बाद, क्रेडेंशियल की जानकारी अपने डेटाबेस में सेव करें, ताकि बाद में उपयोगकर्ता उस क्रेडेंशियल से जुड़ी पासकी से अपनी पहचान की पुष्टि कर सके.

पासकी से जुड़े सार्वजनिक पासकोड के क्रेडेंशियल के लिए, खास तौर पर बने टेबल का इस्तेमाल करें. किसी उपयोगकर्ता के पास सिर्फ़ एक पासवर्ड हो सकता है, लेकिन उसके पास कई पासकी हो सकती हैं. उदाहरण के लिए, Apple iCloud Keychain से सिंक की गई पासकी और दूसरी Google Password Manager की मदद से सिंक की गई पासकी.

यहां उदाहरण के तौर पर एक स्कीमा दिया गया है, जिसका इस्तेमाल आप क्रेडेंशियल की जानकारी सेव करने के लिए कर सकते हैं:

पासकी के लिए डेटाबेस स्कीमा

  • उपयोगकर्ता टेबल:
    • user_id: प्राइमरी यूज़र आईडी. उपयोगकर्ता के लिए एक रैंडम, यूनीक, और स्थायी आईडी. इसे अपनी उपयोगकर्ता टेबल की प्राथमिक कुंजी के रूप में इस्तेमाल करें.
    • username. उपयोगकर्ता का तय किया गया उपयोगकर्ता नाम, जिसमें बदलाव किया जा सकता है.
    • passkey_user_id: पासकी से जुड़ा बिना व्यक्तिगत पहचान से जुड़ी जानकारी वाला यूज़र आईडी, जो रजिस्ट्रेशन के विकल्पों में user.id के तौर पर दिखता है. जब उपयोगकर्ता बाद में पुष्टि करने की कोशिश करेगा, तो पुष्टि करने वाला टूल userHandle में पुष्टि करने के अपने जवाब में इसpasskey_user_id को उपलब्ध कराएगा. हमारा सुझाव है कि आप passkey_user_id को मुख्य कुंजी के तौर पर सेट न करें. सिस्टम में, मुख्य कुंजियों का इस्तेमाल व्यक्तिगत पहचान से जुड़ी जानकारी के तौर पर किया जाता है, क्योंकि इन्हें बहुत ज़्यादा इस्तेमाल किया जाता है.
  • सार्वजनिक पासकोड के क्रेडेंशियल टेबल:
    • id: क्रेडेंशियल आईडी. इसे अपनी सार्वजनिक कुंजी के क्रेडेंशियल टेबल की प्राथमिक कुंजी के रूप में इस्तेमाल करें.
    • public_key: क्रेडेंशियल की सार्वजनिक कुंजी.
    • passkey_user_id: उपयोगकर्ता टेबल के साथ लिंक बनाने के लिए, इसका इस्तेमाल विदेशी कुंजी के तौर पर करें.
    • backed_up: अगर पासकी को पासकी की सेवा देने वाली कंपनी ने सिंक किया है, तो पासकी का बैक अप लिया जाता है. अगर आपको आने वाले समय में backed_up पासकी रखने वाले उपयोगकर्ताओं को पासवर्ड नहीं डालना है, तो बैकअप की स्थिति को सेव करने की सुविधा चालू की जा सकती है. authenticatorData में मौजूद फ़्लैग की जांच करके, यह पता लगाया जा सकता है कि पासकी का बैक अप लिया गया है या नहीं. इसके अलावा, इस जानकारी को आसानी से ऐक्सेस करने के लिए, आम तौर पर उपलब्ध FIDO सर्वर साइड लाइब्रेरी की सुविधा का इस्तेमाल किया जा सकता है. बैकअप लेने की ज़रूरी शर्तों की जानकारी स्टोर करने से, संभावित उपयोगकर्ता के सवालों के जवाब मिल सकते हैं.
    • name: आपके पास क्रेडेंशियल का डिसप्ले नेम जोड़ने का विकल्प भी है, ताकि उपयोगकर्ता क्रेडेंशियल को पसंद के मुताबिक नाम दे सकें.
    • transports: ट्रांसपोर्ट की कैटगरी. ट्रांसपोर्ट को स्टोर करने से, पुष्टि करने वाले उपयोगकर्ता के अनुभव को बेहतर बनाने में मदद मिलती है. ट्रांसपोर्ट उपलब्ध होने पर, ब्राउज़र ज़रूरत के हिसाब से काम कर सकता है. साथ ही, वह यूज़र इंटरफ़ेस (यूआई) दिखा सकता है जो क्लाइंट से बातचीत करने के लिए, पासकी की सेवा देने वाली कंपनी के इस्तेमाल किए गए ट्रांसपोर्ट से मेल खाता हो. खास तौर पर, फिर से पुष्टि करने के ऐसे मामलों में जहां allowCredentials खाली नहीं होता.

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

कोड का उदाहरण: क्रेडेंशियल सेव करना

हम अपने उदाहरणों में SimpleWebAuthn लाइब्रेरी का इस्तेमाल कर रहे हैं. यहां, हम रजिस्ट्रेशन के रिस्पॉन्स की पुष्टि की प्रक्रिया, उसके verifyRegistrationResponse फ़ंक्शन को सौंपते हैं.

import { isoBase64URL } from '@simplewebauthn/server/helpers';


router.post('/registerResponse', csrfCheck, sessionCheck, async (req, res) => {
  const expectedChallenge = req.session.challenge;
  const expectedOrigin = getOrigin(req.get('User-Agent'));
  const expectedRPID = process.env.HOSTNAME;
  const response = req.body;
  // This sample code is for registering a passkey for an existing,
  // signed-in user

  // Ensure you nest verification function calls in try/catch blocks.
  // If something fails, throw an error with a descriptive error message.
  // Return that message with an appropriate error code to the client.
  try {
    // Verify the credential
    const { verified, registrationInfo } = await verifyRegistrationResponse({
      response,
      expectedChallenge,
      expectedOrigin,
      expectedRPID,
      requireUserVerification: false,
    });

    if (!verified) {
      throw new Error('Verification failed.');
    }

    const { credentialPublicKey, credentialID } = registrationInfo;

    // Existing, signed-in user
    const { user } = res.locals;
    
    // Save the credential
    await Credentials.update({
      id: base64CredentialID,
      publicKey: base64PublicKey,
      // Optional: set the platform as a default name for the credential
      // (example: "Pixel 7")
      name: req.useragent.platform, 
      transports: response.response.transports,
      passkey_user_id: user.passkey_user_id,
      backed_up: registrationInfo.credentialBackedUp
    });

    // Kill the challenge for this session
    delete req.session.challenge;

    return res.json(user);
  } catch (e) {
    delete req.session.challenge;

    console.error(e);
    return res.status(400).send({ error: e.message });
  }
});

अपेंडिक्स: AuthenticatorAttestationResponse

AuthenticatorAttestationResponse में दो ज़रूरी ऑब्जेक्ट हैं:

  • response.clientDataJSON, क्लाइंट के डेटा का JSON वर्शन है, जो वेब पर मौजूद वह डेटा होता है जो ब्राउज़र से दिखता है. अगर क्लाइंट Android ऐप्लिकेशन है, तो इसमें आरपी का ऑरिजिन, चैलेंज, और androidPackageName शामिल होता है. आरपी के तौर पर clientDataJSON पढ़ने से, आपको वह जानकारी ऐक्सेस करने में मदद मिलती है जो ब्राउज़र ने create के अनुरोध के समय देखी थी.
  • response.attestationObjectइसमें दो तरह की जानकारी शामिल होती है:
    • attestationStatement यह तब तक काम का नहीं है, जब तक आप प्रमाणित करने की सुविधा का इस्तेमाल नहीं करते.
    • authenticatorData वह डेटा है जो पासकी की सेवा देने वाली कंपनी को दिखता है. आरपी के तौर पर authenticatorData को पढ़ने से, आपको उस डेटा का ऐक्सेस मिल जाता है जो पासकी की सेवा देने वाली कंपनी ने देखा है. साथ ही, इसका डेटा create के अनुरोध के समय दिखता है.

authenticatorDataइसमें नई बनाई गई पासकी से जुड़े सार्वजनिक पासकोड के क्रेडेंशियल के बारे में ज़रूरी जानकारी शामिल होती है:

  • सार्वजनिक पासकोड का क्रेडेंशियल और उसके लिए एक यूनीक क्रेडेंशियल आईडी.
  • क्रेडेंशियल से जुड़ा आरपी आईडी.
  • ऐसे फ़्लैग जो पासकी बनाते समय उपयोगकर्ता की स्थिति के बारे में बताते हैं: क्या वाकई में कोई उपयोगकर्ता मौजूद था और क्या उपयोगकर्ता की पुष्टि हो गई है (userVerification देखें).
  • AACUD, पासकी की सुविधा देने वाली कंपनी की पहचान करता है. पासकी की जानकारी देने वाली सेवा, आपके उपयोगकर्ताओं के लिए काम की हो सकती है. यह खास तौर पर तब काम आती है, जब उन्होंने आपकी सेवा के लिए, पासकी की सुविधा देने वाली एक से ज़्यादा कंपनियों के लिए पासकी रजिस्टर की हो.

authenticatorData को attestationObject में नेस्ट किया गया है, लेकिन इसमें मौजूद जानकारी की ज़रूरत पासकी को लागू करने के लिए होती है. इससे कोई फ़र्क़ नहीं पड़ता कि आपको पुष्टि करने का तरीका इस्तेमाल करना है या नहीं. authenticatorData को कोड में बदला गया है. इसमें ऐसे फ़ील्ड शामिल हैं जिन्हें बाइनरी फ़ॉर्मैट में कोड में बदला जाता है. आम तौर पर, सर्वर साइड की लाइब्रेरी पार्स और डिकोड करने की प्रोसेस को हैंडल करती है. अगर सर्वर साइड लाइब्रेरी का इस्तेमाल नहीं किया जा रहा है, तो getAuthenticatorData() क्लाइंट-साइड का इस्तेमाल करें. इससे, वर्क सर्वर साइड को पार्स और डिकोड करने से बचा जा सकता है.

अपेंडिक्स: रजिस्ट्रेशन के रिस्पॉन्स की पुष्टि करना

हुड के तहत, रजिस्ट्रेशन के जवाब की पुष्टि करने के लिए ये जांच होती हैं:

  • पक्का करें कि आरपी आईडी आपकी साइट से मेल खाता हो.
  • देख लें कि अनुरोध का ऑरिजिन, आपकी साइट का ऑरिजिन (मुख्य साइट यूआरएल, Android ऐप्लिकेशन) ही हो.
  • अगर आपको उपयोगकर्ता की पुष्टि करना ज़रूरी है, तो पक्का करें कि उपयोगकर्ता की पुष्टि करने वाला फ़्लैग authenticatorData.uv true हो. पक्का करें कि उपयोगकर्ता की मौजूदगी का फ़्लैग authenticatorData.up, true हो. ऐसा इसलिए, क्योंकि पासकी के लिए उपयोगकर्ता की मौजूदगी हमेशा ज़रूरी होती है.
  • देखें कि क्लाइंट ने आपके दिए गए चैलेंज को पूरा किया है या नहीं. अगर आपने पुष्टि करने की प्रक्रिया का इस्तेमाल नहीं किया है, तो इस जांच को समझना ज़रूरी नहीं है. हालांकि, इस जांच को लागू करना सबसे सही तरीका है: इससे यह पक्का होता है कि आने वाले समय में प्रमाणित करने की सुविधा का इस्तेमाल करने पर, आपका कोड तैयार है.
  • पक्का करें कि क्रेडेंशियल आईडी अब तक किसी उपयोगकर्ता के लिए रजिस्टर नहीं किया गया है.
  • पुष्टि करें कि पासकी बनाने वाली कंपनी, क्रेडेंशियल बनाने के लिए जिस एल्गोरिदम का इस्तेमाल करती है वह आपके एल्गोरिदम की सूची में शामिल है. यह एल्गोरिदम publicKeyCredentialCreationOptions.pubKeyCredParams के हर alg फ़ील्ड में मौजूद होता है. आम तौर पर, यह एल्गोरिदम आपकी सर्वर साइड लाइब्रेरी में मौजूद होता है और यह आपको नहीं दिखता. इससे यह पक्का होता है कि उपयोगकर्ता सिर्फ़ उन एल्गोरिदम के साथ रजिस्टर कर सकते हैं जिन्हें आपने अनुमति देने के लिए चुना है.

ज़्यादा जानने के लिए, SimpleWebAuthn के verifyRegistrationResponse के लिए सोर्स कोड को देखें या जानकारी में पुष्टि की पूरी सूची देखें.

अगला

सर्वर साइड पासकी की पुष्टि करना