এসএমএস ওটিপি ফর্ম যাচাইকরণ

ফোন নম্বর যাচাইকরণ, অ্যাকাউন্ট ফিরিয়ে আনা, স্টেপ-আপ যাচাইকরণ ও পেমেন্ট কনফার্মেশনের জন্য এসএমএসের মাধ্যমে ওয়ান-টাইম পাসকোড (OTP) পাঠানো একটি সাধারণ পদ্ধতি। ব্রাউজার ও এসএমএস মেসেজিং অ্যাপের মধ্যে পরিবর্তন করে মাল্টি-ডিজিট কোড মনে রাখা বা কপি-পেস্ট করার ফলে ব্যবহারকারীর অসুবিধা হয়, ড্রপ-অফ রেট বাড়ে এবং স্পুফ করা ডোমেনে ফিশিংয়ের ক্ষেত্রে ব্যবহারকারী অসুরক্ষিত হয়ে পড়েন।

সিম্যান্টিক <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 এসএমএস ফর্ম্যাটের সাথে পেয়ার করুন যাতে ব্রাউজার এবং OS কোড সাজেস্ট বা ডেলিভার করার আগে টার্গেট ডোমেন ম্যাচ করছে কিনা তা যাচাই করতে পারে। ফিশিং-প্রতিরোধী প্রাথমিক যাচাইকরণের জন্য, এসএমএস-ভিত্তিক কোডের পরিবর্তে পাসকী ব্যবহার করুন (পাসকী ওরিয়েন্টেশন দেখুন)।

সেম্যান্টিক ফর্ম মার্কআপ

method="POST" এবং একটি স্পষ্ট <button type="submit"> সহ একটি সিম্যান্টিক <form> এলিমেন্টে যাচাইকরণ কন্ট্রোলটি র‍্যাপ করুন। একাধিক বক্সে সংখ্যা ভাগ করে দেওয়ার পরিবর্তে সবসময় একটিই <input> এলিমেন্ট ব্যবহার করুন।

  • inputmode="numeric"-এর সাথে type="text" ব্যবহার করুন: ওটিপি কোডের জন্য কখনও type="number" ব্যবহার করবেন না। ওয়ান-টাইম পাসকোড হল একটি শনাক্তকারী স্ট্রিং যাতে প্রথমে শূন্য (যেমন 012345) থাকতে পারে, এটি কোনও গণনাযোগ্য পরিমাণ নয়। type="number" ব্যবহার করলে ইনক্রিমেন্ট/ডিক্রিমেন্ট স্পিনার বোতাম যোগ হয়, প্রথমে থাকা শূন্য বাদ দিতে পারে এবং স্ক্রল-হুইল ভ্যালু ভুলবশত পরিবর্তন করার অনুমতি দেয়। এছাড়াও, type="tel" এড়িয়ে চলুন, যা সম্পূর্ণ টেলিফোন নম্বরের জন্য একটি লিগ্যাসি সমাধান। মোবাইল ডিভাইসে শুধুমাত্র সংখ্যা-ভিত্তিক ভার্চুয়াল কীবোর্ড দেখানোর জন্য inputmode="numeric"-এর সাথে type="text" পেয়ার করুন।
  • 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 টোকেনে URL স্কিম (https:// বা http://), পোর্ট (:8080) বা পাথ (/verify) যোগ করবেন না।
  • একটি স্পেস দিয়ে টোকেন আলাদা করুন: @<domain> ও #<otp>-এর মধ্যে ঠিক একটি স্পেস দিন এবং ডোমেনের মধ্যে বা # ও কোডের মধ্যে কখনও হোয়াইটস্পেস দেবেন না।

আপনার যাচাইকরণ ফ্লোতে WebOTP API ইন্টিগ্রেট করা

WebOTP API (OTPCredential)-এর জন্য একটি সুরক্ষিত প্রসঙ্গ (প্রোডাকশনে https:// বা লোকাল ডেভেলপমেন্টের সময় http://localhost) প্রয়োজন। DOM কোয়েরি করে এবং form.submit() কল করে এমন একটি ডিসকানেক্ট করা DOMContentLoaded স্ক্রিপ্ট পেস্ট করার পরিবর্তে, আপনার যাচাইকরণ UI-এর লাইফসাইকেল এবং সাবমিশন হ্যান্ডলারে সরাসরি navigator.credentials.get() ইন্টিগ্রেট করুন:

  • অ্যাক্টিভ OTP স্টেপে অনুরোধের লাইফসাইকেল টাই করা: যাচাইকরণ ইনপুট রেন্ডার ও অ্যাক্টিভ হলে OTP-র জন্য শোনা শুরু করুন (যেমন, ব্যবহারকারী এসএমএস কোডের অনুরোধ করার পরে OTP স্টেপ মাউন্ট হলে এবং ব্যবহারকারী যখনই "কোড আবার পাঠান" বোতামে ক্লিক করেন)।
  • সব এক্সিট পাথ জুড়ে 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 Secure যাচাইকরণ যেখানে 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 HTTP রেসপন্স হেডার পাঠান: মূল ডকুমেন্টের HTTP হেডারে ক্রস-অরিজিন 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) কাজ না করলে, থার্ড-পার্টি জাভাস্ক্রিপ্ট পলিফিল লোড করবেন না, কারণ ইনকামিং এসএমএস মেসেজ পড়ার জন্য অপারেটিং সিস্টেম লেভেল ইন্টিগ্রেশন প্রয়োজন। পরিবর্তে, <input> মার্কআপে তৈরি থ্রি-টিয়ার প্রোগ্রেসিভ এনহ্যান্সমেন্টের উপর নির্ভর করুন:

  • প্রোগ্রেসিভ এনহ্যান্সমেন্ট অভিজ্ঞতা: if ('OTPCredential' in window)-এর পিছনে navigator.credentials.get({ otp: { transport: ['sms'] } }) কল গার্ড করুন। OTPCredential উপলভ্য না থাকলে:
    1. যেসব ব্রাউজার autocomplete="one-time-code" (যেমন, iOS 14+, iPadOS-এ Safari এবং iCloud SMS রিলে সহ macOS) সাপোর্ট করে, সেগুলি @domain #code এসএমএস পার্স করে এবং <input>-এ ফোকাস করা হলে সিস্টেম কীবোর্ড অটোফিল বারে কোড দেখায়।
    2. অন্যান্য সব ব্রাউজার মোবাইল ডিভাইসে একটি সংখ্যাসূচক কীপ্যাড দেখিয়ে এবং <button type="submit">-এর মাধ্যমে জমা দিয়ে, একটি <input type="text" inputmode="numeric" pattern="\d{6}" maxlength="6" required>-এ ম্যানুয়াল এন্ট্রিতে ফিরে আসে।

অনুমতি সংক্রান্ত নীতি ফলব্যাক

ব্রাউজার যদি otp-credentials-কে Permissions-Policy ও <iframe allow="otp-credentials">-এর মাধ্যমে ডেলিগেট করতে না পারে, তাহলে ক্রস-অরিজিন <iframe>-এর মধ্যে এম্বেড করা যাচাইকরণ <form>, কোনও আনহ্যান্ডেলড এক্সেপশন না দেখিয়েই, autocomplete="one-time-code" কীবোর্ড সাজেশন ও ম্যানুয়াল সংখ্যাগত ইনপুটে ডিগ্রেড করে।

Inputmode ও AbortController সাপোর্ট

HTML inputmode অ্যাট্রিবিউট এবং AbortController (AbortSignal) উভয়ই আধুনিক ব্রাউজার জুড়ে কাজ করে এবং এর জন্য কোনও পলিফিল বা ফলব্যাক কোডের প্রয়োজন হয় না।