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

खास जानकारी

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

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

  • पासकी बनाने के विकल्प तय करें. उन्हें क्लाइंट को भेजें, ताकि आप उन्हें पासकी बनाने वाले कॉल में पास कर सकें: वेब पर 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 पर सेट किया जा सकता है. पुष्टि करने के लिए सुरक्षित चैलेंज बनाने के निर्देश, सर्वर साइड पासकी से पुष्टि करना में उपलब्ध हैं.

एन्कोडिंग और डिकोडिंग

सर्वर से भेजे गए PublicKeyCredentialCreationOptions
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 });
  }
});

सार्वजनिक पासकोड सेव करें

सर्वर से भेजे गए PublicKeyCredentialCreationOptions
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 से दिखाया जाता है. जब उपयोगकर्ता बाद में पुष्टि करने की कोशिश करेगा, तो पुष्टि करने वाला व्यक्ति इसpasskey_user_id को userHandle में पुष्टि करने के रिस्पॉन्स में उपलब्ध करा देगा. हमारा सुझाव है कि आप 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 देखें).
  • AAGUID, जो पासकी की सेवा देने वाली कंपनी की पहचान करता है. पासकी की सेवा देने वाली कंपनी को दिखाना, आपके उपयोगकर्ताओं के लिए मददगार साबित हो सकता है. खास तौर पर, ऐसा तब होता है, जब उन्होंने आपकी सेवा के लिए, एक से ज़्यादा पासकी उपलब्ध कराई हैं.

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

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

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

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

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

अगला अवॉर्ड

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