एसएमएस से मिलने वाले ओटीपी की मदद से, फ़ॉर्म की पुष्टि करना

एसएमएस के ज़रिए एक बार इस्तेमाल होने वाला पासकोड (ओटीपी) भेजना, फ़ोन नंबर की पुष्टि करने, खाता वापस पाने, पुष्टि करने के अतिरिक्त चरण, और पेमेंट की पुष्टि करने का एक सामान्य तरीका है. कई अंकों वाले कोड को याद रखने या कॉपी-पेस्ट करने के लिए, ब्राउज़र और एसएमएस मैसेजिंग ऐप्लिकेशन के बीच स्विच करने से, उपयोगकर्ताओं को परेशानी होती है. इससे वेबसाइट छोड़ने की दर बढ़ जाती है. साथ ही, उपयोगकर्ताओं के लिए नकली डोमेन पर फ़िशिंग का खतरा बढ़ जाता है.

सिमैंटिक <form> मार्कअप, <input autocomplete="one-time-code" inputmode="numeric">, ओरिजिन-बाउंड एसएमएस फ़ॉर्मैटिंग (@domain #code), और WebOTP API (OTPCredential) को मिलाकर, पुष्टि करने का एक परतदार वृद्धि वाला फ़्लो बनाया जा सकता है:

  1. अपने-आप कोड निकालना और सबमिट करना: WebOTP API के साथ काम करने वाले ब्राउज़र, एसएमएस में मौजूद @domain #code लाइन से कोड की पुष्टि करते हैं. इसके बाद, वे <input> को भरते हैं और एक टैप से पुष्टि की प्रोसेस शुरू करते हैं.
  2. ओरिजिन-बाउंड कीबोर्ड ऑटोमैटिक भरने की सुविधा: autocomplete="one-time-code" के साथ काम करने वाले ब्राउज़र और ऑपरेटिंग सिस्टम (जैसे, iOS, iPadOS, और macOS पर Safari), @domain #code मैसेज को पार्स करते हैं. साथ ही, डोमेन मैच होने पर ही कीबोर्ड के ऑटोमैटिक भरने वाले बार में कोड का सुझाव देते हैं.
  3. मैन्युअल तरीके से आसानी से कोड डालना: किसी दूसरे डिवाइस से कोड डालने वाले उपयोगकर्ताओं को inputmode="numeric" के ज़रिए एक न्यूमेरिक वर्चुअल कीपैड मिलता है. इसमें <input type="number"> की तरह इस्तेमाल करने में कोई परेशानी नहीं होती.

सुरक्षा से जुड़ी ज़रूरी बातें

एसएमएस से मिलने वाले ओटीपी से, फ़ोन नंबर के मालिकाना हक की पुष्टि की जाती है. साथ ही, इससे पुष्टि करने की प्रोसेस को आसान बनाया जाता है. हालांकि, एसएमएस भेजने और फ़ोन नंबरों का इस्तेमाल करने पर, सिम की अदला-बदली, नंबर को फिर से इस्तेमाल करने, और इंटरसेप्शन का खतरा हो सकता है. क्लाइंट-साइड पर ऑटोमैटिक तरीके से भरने की सुविधा और WebOTP API को हमेशा ओरिजन-बाउंड @domain #code एसएमएस फ़ॉर्मैट के साथ जोड़ें. इससे ब्राउज़र और ओएस, कोड का सुझाव देने या उसे डिलीवर करने से पहले यह पुष्टि कर पाते हैं कि टारगेट डोमेन मैच हो रहा है या नहीं. फ़िशिंग से बचने के लिए, पुष्टि करने के मुख्य तरीके के तौर पर एसएमएस से मिलने वाले कोड के बजाय पासकी को प्राथमिकता दें. ज़्यादा जानकारी के लिए, पासकी ओरिएंटेशन देखें.

सिमैंटिक फ़ॉर्म मार्कअप

पुष्टि करने वाले कंट्रोल को method="POST" और साफ़ तौर पर <button type="submit"> वाले सिमैंटिक <form> एलिमेंट में रैप करें. अंकों को कई बॉक्स में बांटने के बजाय, हमेशा एक ही <input> एलिमेंट का इस्तेमाल करें.

  • inputmode="numeric" के साथ type="text" का इस्तेमाल करें: ओटीपी कोड के लिए, type="number" का इस्तेमाल कभी न करें. एक बार इस्तेमाल होने वाला पासकोड, आइडेंटिफ़ायर स्ट्रिंग होती है. इसमें शुरुआत में शून्य हो सकते हैं (जैसे कि 012345). यह कोई संख्या नहीं होती. type="number" का इस्तेमाल करने से, वैल्यू बढ़ाने/घटाने वाले स्पिनर बटन जुड़ जाते हैं. इससे, वैल्यू के शुरू में मौजूद शून्य हट सकते हैं. साथ ही, स्क्रोल व्हील की मदद से वैल्यू में गलती से बदलाव हो सकते हैं. type="tel" का इस्तेमाल करने से भी बचें. यह एक लेगसी तरीका है, जिसका इस्तेमाल पूरे टेलीफ़ोन नंबर के लिए किया जाता है. मोबाइल डिवाइसों पर सिर्फ़ अंकों वाला वर्चुअल कीबोर्ड दिखाने के लिए, type="text" को inputmode="numeric" के साथ जोड़ें.
  • autocomplete="one-time-code" जोड़ें: इससे ब्राउज़र और ऑपरेटिंग सिस्टम को निर्देश मिलता है कि <input> पर फ़ोकस होने पर, ऑटोमैटिक भरने वाले बार में, पुष्टि करने के लिए आने वाले एसएमएस कोड का सुझाव दें.
  • निर्देशों को <input> के ऊपर रखें: for और id का इस्तेमाल करके, दिखने वाले <label> को जोड़ें. साथ ही, फ़ॉर्मैट से जुड़े किसी भी निर्देश (aria-describedby के ज़रिए लिंक किया गया) को <input> के ऊपर रखें, ताकि जानकारी अपने-आप भरने की सुविधा वाले पॉपओवर और मोबाइल वर्चुअल कीबोर्ड, निर्देशों को कभी न छिपाएं.
  • अंकों को कई <input> एलिमेंट में न बांटें: कई बॉक्स वाले ओटीपी पैटर्न (छह अलग-अलग maxlength="1" इनपुट) कई ब्राउज़र में, autocomplete="one-time-code" की जानकारी अपने-आप भरने की सुविधा को रोकते हैं. साथ ही, कॉपी-पेस्ट करने में रुकावट डालते हैं और स्क्रीन रीडर के नेविगेशन को मुश्किल बनाते हैं. अगर आपको सेगमेंट वाला विज़ुअल डिज़ाइन चाहिए, तो letter-spacing और मोनोस्पेस फ़ॉन्ट का इस्तेमाल करके, एक <input> को स्टाइल करें.
<form id="otp-form" action="/verify-otp" method="POST">
  <div class="form-group">
    <label for="otp-input">Verification code</label>
    <!-- Place the hint above the input so autofill popovers and mobile keyboards do not obscure it -->
    <p id="otp-format-hint" class="field-hint">
      Enter the 6-digit code sent to your phone.
    </p>
    <!-- MANDATORY: Use a single input with type="text", inputmode="numeric", and autocomplete="one-time-code" -->
    <input
      type="text"
      id="otp-input"
      name="otp"
      inputmode="numeric"
      autocomplete="one-time-code"
      pattern="\d{6}"
      maxlength="6"
      aria-describedby="otp-format-hint"
      required>
  </div>
  <button type="submit">Verify code</button>
</form>
/* Optional: Style a single input to visually space digits without splitting the DOM element */
#otp-input {
  font-family: ui-monospace, monospace;
  font-size: 1.25rem; /* Example value: must be at least 1rem (16px) to prevent mobile browser auto-zoom */
  letter-spacing: 0.35em;
  min-height: 48px;
  padding: 0.5rem 0.75rem;
  box-sizing: border-box;
}

ओरिजिन-बाउंड एसएमएस मैसेज का फ़ॉर्मैट

WebOTP API और Safari के डोमेन से जुड़े autocomplete="one-time-code" ऑटोमैटिक तरीके से भरने की सुविधा, दोनों के लिए यह पुष्टि करना ज़रूरी है कि कोड आपकी वेबसाइट का है. इसके लिए, आपकी बैकएंड एसएमएस सेवा को मैसेज की आखिरी लाइन को फ़ॉर्मैट करना होगा. इसमें, ऑरिजिन डोमेन से पहले @ और ओटीपी से पहले # लगाना होगा:

Your verification code is 123456.

@example.com #123456

भेजे जाने वाले एसएमएस के लिए, फ़ॉर्मैटिंग के ये नियम लागू करें:

  • सबसे पहले, आसानी से पढ़ा जा सकने वाला टेक्स्ट शामिल करें: एसएमएस की शुरुआत हमेशा साफ़ तौर पर पढ़े जा सकने वाले वाक्य से करें, जिसमें कोड शामिल हो. इससे, दूसरे डिवाइस पर मैन्युअल तरीके से टाइप करने वाले उपयोगकर्ता, इसे आसानी से पढ़ सकते हैं.
  • @<domain> #<otp> को सबसे आखिरी लाइन में रखें: @ वर्ण, आखिरी लाइन का पहला वर्ण होना चाहिए. इसके बाद, कोई लाइन या हस्ताक्षर वाला टेक्स्ट नहीं होना चाहिए.
  • सिर्फ़ होस्ट किया गया डोमेन इस्तेमाल करें: @domain टोकन में यूआरएल स्कीम (https:// या http://), पोर्ट (:8080) या पाथ (/verify) शामिल न करें.
  • टोकन को एक स्पेस से अलग करें: @<domain> और #<otp> के बीच सिर्फ़ एक स्पेस डालें. साथ ही, डोमेन के अंदर या # और कोड के बीच कभी भी व्हाइटस्पेस शामिल न करें.

पुष्टि करने की प्रोसेस में WebOTP API को इंटिग्रेट करना

WebOTP API (OTPCredential) के लिए, सुरक्षित कॉन्टेक्स्ट (प्रोडक्शन में https:// या लोकल डेवलपमेंट के दौरान http://localhost) की ज़रूरत होती है. डीओएम से क्वेरी करने और form.submit() को कॉल करने वाली, डिसकनेक्ट की गई DOMContentLoaded स्क्रिप्ट को चिपकाने के बजाय, navigator.credentials.get() को सीधे अपने पुष्टि करने वाले यूज़र इंटरफ़ेस (यूआई) के लाइफ़साइकल और सबमिशन हैंडलर में इंटिग्रेट करें:

  • अनुरोध की लाइफ़साइकल को ओटीपी वाले चरण से जोड़ें: जब पुष्टि करने के लिए इनपुट रेंडर और चालू हो, तब ओटीपी के लिए सुनना शुरू करें. उदाहरण के लिए, जब उपयोगकर्ता एसएमएस कोड का अनुरोध करता है, तब ओटीपी वाला चरण माउंट होता है. इसके बाद, जब भी उपयोगकर्ता "कोड फिर से भेजें" बटन पर क्लिक करता है, तब भी ऐसा होता है.
  • सभी एग्ज़िट पाथ में AbortController को मैनेज करें: navigator.credentials.get() को AbortSignal (signal: ac.signal) पास करें. ac.abort() को कॉल करें, जब:
    • उपयोगकर्ता, फ़ॉर्म को मैन्युअल तरीके से सबमिट करता है या autocomplete="one-time-code" से भरा गया कोड सबमिट करता है.
    • एसएमएस से मिलने वाले नए कोड का अनुरोध किया जाता है ("कोड फिर से भेजें"). इससे नया कंट्रोलर बनाने से पहले, पिछला कंट्रोलर बंद हो जाता है.
    • पुष्टि करने वाला कॉम्पोनेंट, डायलॉग या रूट अनमाउंट हो जाता है.
  • ऐप्लिकेशन के सबमिशन हैंडलर (form.requestSubmit()) को ट्रिगर करें: जब navigator.credentials.get(), otp के साथ रिज़ॉल्व हो जाए, तो otpInput.value = otp.code सेट करें और form.requestSubmit() को कॉल करें (या पुष्टि करने वाले फ़ंक्शन को सीधे तौर पर शुरू करें). form.submit() को कॉल न करें, क्योंकि form.submit(), submit इवेंट लिसनर (event.preventDefault() / fetch() हैंडलर) को बायपास करता है और नेटिव कंस्ट्रेंट वैलिडेशन को स्किप करता है.
const otpForm = document.getElementById('otp-form');
const otpInput = document.getElementById('otp-input');

let otpAbortController = null;

function stopSmsOtpListener() {
  if (otpAbortController) {
    otpAbortController.abort();
    otpAbortController = null;
  }
}

async function startSmsOtpListener() {
  // Feature-detect WebOTP support
  if (!otpInput || !('OTPCredential' in window)) {
    return;
  }

  // Cancel any previous pending WebOTP request (e.g., when re-sending an SMS code)
  stopSmsOtpListener();
  const ac = new AbortController();
  otpAbortController = ac;

  try {
    const otp = await navigator.credentials.get({
      otp: { transport: ['sms'] },
      signal: ac.signal,
    });

    if (!otp) return;
    otpInput.value = otp.code;

    // Trigger standard form submission so submit event listeners and validation run
    if (otpForm) {
      otpForm.requestSubmit();
    }
  } catch (err) {
    // Expected when aborted by manual submission, resend, unmount, or user dismissal
    if (err && err.name === 'AbortError') {
      return;
    }
    console.error('WebOTP verification error:', err);
  }
}

otpForm.addEventListener('submit', async (event) => {
  // MANDATORY: Abort any in-flight WebOTP request when the form is submitted
  stopSmsOtpListener();

  // Application-specific submission logic (e.g., AJAX fetch or native POST)
  event.preventDefault();
  const formData = new FormData(otpForm);
  // await fetch(otpForm.action, { method: 'POST', body: formData });
});

// Start listening when the OTP verification step becomes active
startSmsOtpListener();

क्रॉस-ऑरिजिन iframe की पुष्टि करना

क्रॉस-ऑरिजिन <iframe> में एसएमएस से ओटीपी पाने की सुविधा, डिफ़ॉल्ट रूप से बंद होती है. इसका इस्तेमाल मुख्य रूप से, एम्बेड किए गए पेमेंट की पुष्टि करने वाले फ़्लो के लिए किया जाता है. जैसे, 3D सिक्योर पुष्टि, जिसमें shop.example, bank.example से जारीकर्ता की चुनौती को एम्बेड करता है. क्रॉस-ऑरिजिन <iframe> में WebOTP की सुविधा का इस्तेमाल करने के लिए, तीन कॉन्फ़िगरेशन की ज़रूरत होती है:

  1. एसएमएस की आखिरी लाइन में दोनों ऑरिजिन को बाइंड करें: सबसे पहले, टॉप-लेवल एम्बेडिंग डोमेन (@shop.example) को शामिल करें. इसके बाद, #<otp> को शामिल करें. इसके बाद, स्पेस और एम्बेड किए गए iframe डोमेन (@bank.example) को शामिल करें:

    Your verification code is 123456.
    
    @shop.example #123456 @bank.example
    
  2. Permissions-Policy एचटीटीपी रिस्पॉन्स हेडर भेजें: पैरंट दस्तावेज़ के एचटीटीपी हेडर में, क्रॉस-ऑरिजिन iframe ऑरिजिन को otp-credentials का ऐक्सेस दें:

    Permissions-Policy: otp-credentials=(self "https://bank.example")
    
  3. <iframe> एलिमेंट में allow="otp-credentials" जोड़ें: एम्बेड किए गए पेज में <iframe> टैग पर, अनुमति से जुड़ी नीति को साफ़ तौर पर अनुमति दें. साथ ही, स्क्रीन रीडर के लिए जानकारी देने वाला title एट्रिब्यूट शामिल करें:

<iframe
  src="https://bank.example/verify"
  allow="otp-credentials"
  title="Payment verification">
</iframe>

https://bank.example से मिले <iframe> दस्तावेज़ में, ऊपर दिखाए गए <form> मार्कअप और navigator.credentials.get({ otp: { transport: ['sms'] }, signal: ac.signal }) की पुष्टि करने की लाइफ़साइकल को चलाएं.

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

WebOTP API फ़ॉलबैक

अगर आपके टारगेट ब्राउज़र, WebOTP API (OTPCredential) के साथ काम नहीं करते हैं, तो तीसरे पक्ष का JavaScript पॉलीफ़िल लोड न करें. ऐसा इसलिए, क्योंकि आने वाले एसएमएस पढ़ने के लिए, ऑपरेटिंग सिस्टम के लेवल पर इंटिग्रेशन की ज़रूरत होती है. इसके बजाय, <input> मार्कअप में पहले से मौजूद, तीन-टियर वाले प्रोग्रेसिव एन्हांसमेंट पर भरोसा करें:

  • परतदार वृद्धि का अनुभव: navigator.credentials.get({ otp: { transport: ['sms'] } }) कॉल को if ('OTPCredential' in window) के पीछे सुरक्षित रखें. OTPCredential के उपलब्ध न होने पर:
    1. autocomplete="one-time-code" के साथ काम करने वाले ब्राउज़र (जैसे, iOS 14+, iPadOS, और iCloud एसएमएस रिले वाले macOS पर Safari), @domain #code एसएमएस को पार्स करते हैं. साथ ही, <input> पर फ़ोकस होने पर, सिस्टम कीबोर्ड के ऑटोमैटिक भरने वाले बार में कोड दिखाते हैं.
    2. अन्य सभी ब्राउज़र, सिंगल <input type="text" inputmode="numeric" pattern="\d{6}" maxlength="6" required> में मैन्युअल एंट्री पर वापस चले जाते हैं. इससे मोबाइल डिवाइसों पर न्यूमेरिक कीपैड दिखता है और <button type="submit"> के ज़रिए सबमिट किया जाता है.

अनुमतियों की नीति का फ़ॉलबैक

अगर ब्राउज़र, Permissions-Policy और <iframe allow="otp-credentials"> के ज़रिए otp-credentials को डेलिगेट करने की सुविधा के साथ काम नहीं करता है, तो क्रॉस-ऑरिजिन <iframe> में एम्बेड की गई पुष्टि करने की सुविधा <form>, बिना किसी गड़बड़ी के autocomplete="one-time-code" कीबोर्ड के सुझावों और मैन्युअल तरीके से नंबर डालने की सुविधा पर स्विच हो जाती है.

Inputmode और AbortController के साथ काम करने की सुविधा

एचटीएमएल inputmode एट्रिब्यूट और AbortController (AbortSignal), दोनों ही मॉडर्न ब्राउज़र पर काम करते हैं. इनके लिए, किसी polyfill या फ़ॉलबैक कोड की ज़रूरत नहीं होती.