আপনার প্রথম WebAuthn অ্যাপ তৈরি করুন

1. আপনি শুরু করার আগে

ওয়েব প্রমাণীকরণ API, যা WebAuthn নামেও পরিচিত, আপনাকে ব্যবহারকারীদের প্রমাণীকরণের জন্য অরিজিন-স্কোপড, পাবলিক-কী শংসাপত্র তৈরি এবং ব্যবহার করতে দেয়।

API BLE, NFC, এবং USB-রোমিং U2F বা FIDO2 প্রমাণীকরণের ব্যবহার সমর্থন করে—যা নিরাপত্তা কী নামেও পরিচিত—সেইসাথে একটি প্ল্যাটফর্ম প্রমাণীকরণকারী, যা ব্যবহারকারীদের তাদের আঙ্গুলের ছাপ বা স্ক্রিন লক দিয়ে প্রমাণীকরণ করতে দেয়।

এই কোডল্যাবে, আপনি একটি সহজ পুনঃপ্রমাণ কার্যকারিতা সহ একটি ওয়েবসাইট তৈরি করেন যা একটি ফিঙ্গারপ্রিন্ট সেন্সর ব্যবহার করে। পুনঃপ্রমাণকরণ অ্যাকাউন্টের ডেটাকে সুরক্ষিত করে কারণ এটির জন্য যে ব্যবহারকারীরা ইতিমধ্যেই একটি ওয়েবসাইটে সাইন ইন করেছেন তাদের আবার প্রমাণীকরণ করতে হবে যখন তারা ওয়েবসাইটের গুরুত্বপূর্ণ বিভাগে প্রবেশ করার চেষ্টা করে বা নির্দিষ্ট সময় পরে ওয়েবসাইটটি পুনরায় দেখার চেষ্টা করে৷

পূর্বশর্ত

  • WebAuthn কিভাবে কাজ করে তার প্রাথমিক ধারণা
  • জাভাস্ক্রিপ্টের সাথে প্রাথমিক প্রোগ্রামিং দক্ষতা

আপনি কি করবেন

  • ফিঙ্গারপ্রিন্ট সেন্সর ব্যবহার করে এমন একটি সাধারণ পুনরায় প্রমাণীকরণ কার্যকারিতা সহ একটি ওয়েবসাইট তৈরি করুন৷

আপনি কি প্রয়োজন হবে

  • নিম্নলিখিত ডিভাইসগুলির মধ্যে একটি:
    • একটি অ্যান্ড্রয়েড ডিভাইস, বিশেষত একটি বায়োমেট্রিক সেন্সর সহ
    • iOS 14 বা উচ্চতর সংস্করণে টাচ আইডি বা ফেস আইডি সহ একটি iPhone বা iPad
    • একটি ম্যাকবুক প্রো বা এয়ার ম্যাকওএস বিগ সুর বা তার উপরে টাচ আইডি সহ
    • Windows Hello সেট আপ সহ Windows 10 19H1 বা উচ্চতর
  • নিম্নলিখিত ব্রাউজারগুলির মধ্যে একটি:
    • Google Chrome 67 বা উচ্চতর
    • Microsoft Edge 85 বা উচ্চতর
    • Safari 14 বা তার বেশি

2. সেট আপ করুন

এই কোডল্যাবে, আপনি glitch নামে একটি পরিষেবা ব্যবহার করেন। এখানে আপনি JavaScript দিয়ে ক্লায়েন্ট এবং সার্ভার-সাইড কোড এডিট করতে পারেন এবং তাৎক্ষণিকভাবে সেগুলি স্থাপন করতে পারেন।

https://glitch.com/edit/#!/webauthn-codelab-start- এ নেভিগেট করুন।

দেখো এটা কিভাবে কাজ করে

ওয়েবসাইটের প্রাথমিক অবস্থা দেখতে এই পদক্ষেপগুলি অনুসরণ করুন:

  1. ক্লিক 62bb7a6aac381af8.png দেখান > 3343769d04c09851.png লাইভ ওয়েবসাইট দেখতে একটি নতুন উইন্ডোতে
  2. আপনার পছন্দের একটি ব্যবহারকারীর নাম লিখুন এবং পরবর্তী ক্লিক করুন।
  3. একটি পাসওয়ার্ড লিখুন এবং সাইন-ইন ক্লিক করুন।

পাসওয়ার্ড উপেক্ষা করা হয়েছে, কিন্তু আপনি এখনও প্রমাণীকৃত। আপনি হোম পেজে অবতরণ করুন.

  1. ট্রাই রিআউথ ক্লিক করুন এবং দ্বিতীয়, তৃতীয় এবং চতুর্থ ধাপগুলি পুনরাবৃত্তি করুন।
  2. সাইন আউট ক্লিক করুন.

লক্ষ্য করুন যে আপনি প্রতিবার সাইন ইন করার চেষ্টা করার সময় আপনাকে অবশ্যই পাসওয়ার্ড লিখতে হবে৷ এটি এমন একটি ব্যবহারকারীকে অনুকরণ করে যাকে একটি ওয়েবসাইটের একটি গুরুত্বপূর্ণ বিভাগে অ্যাক্সেস করার আগে পুনরায় প্রমাণীকরণ করতে হবে৷

কোড রিমিক্স করুন

  1. WebAuthn / FIDO2 API কোডল্যাবে নেভিগেট করুন।
  2. আপনার প্রকল্পের নাম > রিমিক্স প্রকল্পে ক্লিক করুন 306122647ce93305.png একটি নতুন URL-এ আপনার নিজস্ব সংস্করণের সাথে প্রজেক্টটি চালু করতে।

8d42bd24f0fd185c.png

3. একটি আঙ্গুলের ছাপ দিয়ে একটি শংসাপত্র নিবন্ধন করুন৷

আপনাকে একটি UVPA দ্বারা উত্পন্ন একটি শংসাপত্র নিবন্ধন করতে হবে, একটি প্রমাণীক যা ডিভাইসের মধ্যে তৈরি এবং ব্যবহারকারীর পরিচয় যাচাই করে৷ এটি সাধারণত ব্যবহারকারীর ডিভাইসের উপর নির্ভর করে একটি ফিঙ্গারপ্রিন্ট সেন্সর হিসাবে দেখা হয়।

আপনি /home পেজে এই বৈশিষ্ট্যটি যোগ করুন:

260aab9f1a2587a7.png

registerCredential() ফাংশন তৈরি করুন

একটি registerCredential() ফাংশন তৈরি করুন, যা একটি নতুন শংসাপত্র নিবন্ধন করে।

public/client.js

export const registerCredential = async () => {

};

সার্ভার এন্ডপয়েন্ট থেকে চ্যালেঞ্জ এবং অন্যান্য বিকল্পগুলি পান

আপনি ব্যবহারকারীকে একটি নতুন শংসাপত্র নিবন্ধন করতে বলার আগে, একটি চ্যালেঞ্জ সহ WebAuthn-এ সার্ভার রিটার্ন প্যারামিটারগুলি পাস করার অনুরোধ করুন৷ সৌভাগ্যবশত, আপনার কাছে ইতিমধ্যে একটি সার্ভারের শেষ পয়েন্ট রয়েছে যা এই ধরনের পরামিতিগুলির সাথে প্রতিক্রিয়া জানায়।

registerCredential() করতে নিম্নলিখিত কোড যোগ করুন।

public/client.js

const opts = {
  attestation: 'none',
  authenticatorSelection: {
    authenticatorAttachment: 'platform',
    userVerification: 'required',
    requireResidentKey: false
  }
};

const options = await _fetch('/auth/registerRequest', opts);

একটি সার্ভার এবং একটি ক্লায়েন্টের মধ্যে প্রোটোকল WebAuthn স্পেসিফিকেশনের একটি অংশ নয়। যাইহোক, এই কোডল্যাবটি WebAuthn স্পেসিফিকেশনের সাথে সারিবদ্ধ করার জন্য ডিজাইন করা হয়েছে এবং আপনি সার্ভারে যে JSON অবজেক্টটি পাস করেন তা PublicKeyCredentialCreationOptions এর সাথে খুব মিল যাতে এটি আপনার জন্য স্বজ্ঞাত হয়। নিম্নলিখিত সারণীতে গুরুত্বপূর্ণ প্যারামিটার রয়েছে যা আপনি সার্ভারে পাস করতে পারেন এবং তারা কী করে তা ব্যাখ্যা করে:

পরামিতি

বর্ণনা

attestation

প্রত্যয়ন পরিবহনের জন্য অগ্রাধিকার— none , indirect বা direct । আপনার প্রয়োজন না হলে none বেছে নিন না।

excludeCredentials

PublicKeyCredentialDescriptor এর অ্যারে যাতে প্রমাণীকরণকারী ডুপ্লিকেট তৈরি করা এড়াতে পারে।

authenticatorSelection

authenticatorAttachment

উপলব্ধ প্রমাণীকরণগুলি ফিল্টার করুন৷ আপনি যদি ডিভাইসের সাথে একটি প্রমাণীকরণকারী সংযুক্ত করতে চান তবে " platform " ব্যবহার করুন৷ রোমিং প্রমাণীকরণের জন্য, " cross-platform " ব্যবহার করুন।

userVerification

প্রমাণীকরণকারী স্থানীয় ব্যবহারকারী যাচাইকরণ " required ", " preferred ", বা " discouraged " কিনা তা নির্ধারণ করুন। আপনি যদি আঙ্গুলের ছাপ বা স্ক্রিন-লক প্রমাণীকরণ চান, " required " ব্যবহার করুন৷

requireResidentKey

ভবিষ্যতের অ্যাকাউন্ট পিকার UX-এর জন্য তৈরি শংসাপত্র উপলব্ধ থাকলে true ব্যবহার করুন।

এই বিকল্পগুলি সম্পর্কে আরও জানতে, 5.4 দেখুন। শংসাপত্র তৈরির জন্য বিকল্পগুলি (অভিধান PublicKeyCredentialCreationOptions ক্রেডেনশিয়াল ক্রিয়েশন বিকল্পগুলি)

নিম্নলিখিত উদাহরণ বিকল্পগুলি যা আপনি সার্ভার থেকে গ্রহণ করেন৷

{
  "rp": {
    "name": "WebAuthn Codelab",
    "id": "webauthn-codelab.glitch.me"
  },
  "user": {
    "displayName": "User Name",
    "id": "...",
    "name": "test"
  },
  "challenge": "...",
  "pubKeyCredParams": [
    {
      "type": "public-key",
      "alg": -7
    }, {
      "type": "public-key",
      "alg": -257
    }
  ],
  "timeout": 1800000,
  "attestation": "none",
  "excludeCredentials": [
    {
      "id": "...",
      "type": "public-key",
      "transports": [
        "internal"
      ]
    }
  ],
  "authenticatorSelection": {
    "authenticatorAttachment": "platform",
    "userVerification": "required"
  }
}

একটি শংসাপত্র তৈরি করুন

  1. যেহেতু এই বিকল্পগুলি HTTP প্রোটোকলের মাধ্যমে যাওয়ার জন্য এনকোডেড বিতরণ করা হয়েছে, কিছু পরামিতিগুলিকে আবার বাইনারিতে রূপান্তর করুন, বিশেষত, user.id , challenge এবং id এর excludeCredentials অ্যারেতে অন্তর্ভুক্ত করুন:

public/client.js

options.user.id = base64url.decode(options.user.id);
options.challenge = base64url.decode(options.challenge);

if (options.excludeCredentials) {
  for (let cred of options.excludeCredentials) {
    cred.id = base64url.decode(cred.id);
  }
}
  1. একটি নতুন শংসাপত্র তৈরি করতে navigator.credentials.create() পদ্ধতিতে কল করুন।

এই কলের মাধ্যমে, ব্রাউজার প্রমাণীকরণকারীর সাথে যোগাযোগ করে এবং UVPA এর সাথে ব্যবহারকারীর পরিচয় যাচাই করার চেষ্টা করে।

public/client.js

const cred = await navigator.credentials.create({
  publicKey: options,
});

একবার ব্যবহারকারী তাদের পরিচয় যাচাই করলে, আপনি একটি শংসাপত্র বস্তু পাবেন যা আপনি সার্ভারে পাঠাতে এবং প্রমাণীকরণকারীকে নিবন্ধন করতে পারেন।

সার্ভার এন্ডপয়েন্টে শংসাপত্র নিবন্ধন করুন

এখানে একটি উদাহরণ শংসাপত্র বস্তু যা আপনি প্রাপ্ত করা উচিত ছিল.

{
  "id": "...",
  "rawId": "...",
  "type": "public-key",
  "response": {
    "clientDataJSON": "...",
    "attestationObject": "..."
  }
}
  1. আপনি যখন একটি শংসাপত্র নিবন্ধন করার জন্য একটি বিকল্প অবজেক্ট পেয়েছেন, শংসাপত্রের বাইনারি প্যারামিটারগুলিকে এনকোড করুন যাতে এটি একটি স্ট্রিং হিসাবে সার্ভারে বিতরণ করা যায়:

public/client.js

const credential = {};
credential.id = cred.id;
credential.rawId = base64url.encode(cred.rawId);
credential.type = cred.type;

if (cred.response) {
  const clientDataJSON =
    base64url.encode(cred.response.clientDataJSON);
  const attestationObject =
    base64url.encode(cred.response.attestationObject);
  credential.response = {
    clientDataJSON,
    attestationObject,
  };
}
  1. শংসাপত্র আইডিটি স্থানীয়ভাবে সংরক্ষণ করুন যাতে ব্যবহারকারী ফিরে এলে আপনি এটি প্রমাণীকরণের জন্য ব্যবহার করতে পারেন:

public/client.js

localStorage.setItem(`credId`, credential.id);
  1. বস্তুটিকে সার্ভারে পাঠান এবং, যদি এটি HTTP code 200 ফেরত দেয়, তাহলে নতুন শংসাপত্রটিকে সফলভাবে নিবন্ধিত হিসাবে বিবেচনা করুন।

public/client.js

return await _fetch('/auth/registerResponse' , credential);

আপনার কাছে এখন সম্পূর্ণ registerCredential() ফাংশন আছে!

এই বিভাগের জন্য চূড়ান্ত কোড

public/client.js

...
export const registerCredential = async () => {
  const opts = {
    attestation: 'none',
    authenticatorSelection: {
      authenticatorAttachment: 'platform',
      userVerification: 'required',
      requireResidentKey: false
    }
  };

  const options = await _fetch('/auth/registerRequest', opts);

  options.user.id = base64url.decode(options.user.id);
  options.challenge = base64url.decode(options.challenge);

  if (options.excludeCredentials) {
    for (let cred of options.excludeCredentials) {
      cred.id = base64url.decode(cred.id);
    }
  }
  
  const cred = await navigator.credentials.create({
    publicKey: options
  });

  const credential = {};
  credential.id =     cred.id;
  credential.rawId =  base64url.encode(cred.rawId);
  credential.type =   cred.type;

  if (cred.response) {
    const clientDataJSON =
      base64url.encode(cred.response.clientDataJSON);
    const attestationObject =
      base64url.encode(cred.response.attestationObject);
    credential.response = {
      clientDataJSON,
      attestationObject
    };
  }

  localStorage.setItem(`credId`, credential.id);
  
  return await _fetch('/auth/registerResponse' , credential);
};
...

4. শংসাপত্রগুলি নিবন্ধন, পেতে এবং সরাতে UI তৈরি করুন৷

নিবন্ধিত শংসাপত্রের একটি তালিকা এবং সেগুলি সরানোর জন্য বোতাম থাকা ভাল।

9b5b5ae4a7b316bd.png

UI প্লেসহোল্ডার তৈরি করুন

শংসাপত্রের তালিকায় UI যোগ করুন এবং একটি নতুন শংসাপত্র নিবন্ধন করার জন্য একটি বোতাম। বৈশিষ্ট্যটি উপলব্ধ কি না তার উপর নির্ভর করে, আপনি একটি নতুন শংসাপত্র নিবন্ধনের জন্য সতর্কতা বার্তা বা বোতাম থেকে hidden শ্রেণীটি সরিয়ে ফেলবেন৷ ul#list হল নিবন্ধিত শংসাপত্রের একটি তালিকা যোগ করার স্থানধারক।

views/home.html

<p id="uvpa_unavailable" class="hidden">
  This device does not support User Verifying Platform Authenticator. You can't register a credential.
</p>
<h3 class="mdc-typography mdc-typography--headline6">
  Your registered credentials:
</h3>
<section>
  <div id="list"></div>
</section>
<mwc-button id="register" class="hidden" icon="fingerprint" raised>Add a credential</mwc-button>

বৈশিষ্ট্য সনাক্তকরণ এবং UVPA প্রাপ্যতা

UVPA উপলব্ধতা পরীক্ষা করতে এই পদক্ষেপগুলি অনুসরণ করুন:

  1. window.PublicKeyCredential উপলব্ধ কিনা তা পরীক্ষা করতে window.PublicKeyCredential পরীক্ষা করুন।
  2. একটি UVPA উপলব্ধ কিনা তা পরীক্ষা করতে PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable() এ কল করুন। সেগুলি উপলব্ধ থাকলে, আপনি একটি নতুন শংসাপত্র নিবন্ধন করার জন্য বোতামটি দেখান৷ যদি তাদের কোনটি উপলব্ধ না হয় তবে আপনি সতর্কতা বার্তাটি দেখান।

views/home.html

const register = document.querySelector('#register');

if (window.PublicKeyCredential) {
  PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()
  .then(uvpaa => {
    if (uvpaa) {
      register.classList.remove('hidden');
    } else {
      document
        .querySelector('#uvpa_unavailable')
        .classList.remove('hidden');
    }
  });        
} else {
  document
    .querySelector('#uvpa_unavailable')
    .classList.remove('hidden');
}

শংসাপত্রের একটি তালিকা পান এবং প্রদর্শন করুন

  1. একটি getCredentials() ফাংশন তৈরি করুন যাতে আপনি নিবন্ধিত শংসাপত্র পেতে পারেন এবং একটি তালিকায় প্রদর্শন করতে পারেন। সৌভাগ্যবশত, সার্ভার /auth/getKeys এ আপনার ইতিমধ্যেই একটি সুবিধাজনক এন্ডপয়েন্ট রয়েছে যেখান থেকে আপনি সাইন-ইন করা ব্যবহারকারীর জন্য নিবন্ধিত শংসাপত্র আনতে পারেন।

ফিরে আসা JSON-এ শংসাপত্রের তথ্য অন্তর্ভুক্ত থাকে, যেমন id এবং publicKey । ব্যবহারকারীকে দেখানোর জন্য আপনি HTML তৈরি করতে পারেন।

views/home.html

const getCredentials = async () => {
  const res = await _fetch('/auth/getKeys');
  const list = document.querySelector('#list');
  const creds = html`${res.credentials.length > 0 ? res.credentials.map(cred => html`
    <div class="mdc-card credential">
      <span class="mdc-typography mdc-typography--body2">${cred.credId}</span>
      <pre class="public-key">${cred.publicKey}</pre>
      <div class="mdc-card__actions">
        <mwc-button id="${cred.credId}" @click="${removeCredential}" raised>Remove</mwc-button>
      </div>
    </div>`) : html`
    <p>No credentials found.</p>
    `}`;
  render(creds, list);
};
  1. ব্যবহারকারী /home পেজে অবতরণ করার সাথে সাথে উপলব্ধ শংসাপত্রগুলি প্রদর্শন করতে getCredentials() আহ্বান করুন।

views/home.html

getCredentials();

শংসাপত্র সরান

শংসাপত্রের তালিকায়, আপনি প্রতিটি শংসাপত্র সরাতে একটি বোতাম যোগ করেছেন। আপনি তাদের অপসারণের জন্য credId ক্যোয়ারী প্যারামিটার সহ /auth/removeKey এ একটি অনুরোধ পাঠাতে পারেন।

public/client.js

export const unregisterCredential = async (credId) => {
  localStorage.removeItem('credId');
  return _fetch(`/auth/removeKey?credId=${encodeURIComponent(credId)}`);
};
  1. বিদ্যমান import বিবৃতিতে unregisterCredential -রেজিস্টার ক্রেডেনশিয়াল যুক্ত করুন।

views/home.html

import { _fetch, unregisterCredential } from '/client.js';
  1. ব্যবহারকারী অপসারণ ক্লিক করলে কল করার জন্য একটি ফাংশন যোগ করুন।

views/home.html

const removeCredential = async e => {
  try {
    await unregisterCredential(e.target.id);
    getCredentials();
  } catch (e) {
    alert(e);
  }
};

একটি শংসাপত্র নিবন্ধন

আপনি একটি নতুন শংসাপত্র নিবন্ধন করতে registerCredential() কল করতে পারেন যখন ব্যবহারকারী একটি শংসাপত্র যোগ করুন ক্লিক করে।

  1. বিদ্যমান import বিবৃতিতে registerCredential ক্রেডেনশিয়াল যুক্ত করুন।

views/home.html

import { _fetch, registerCredential, unregisterCredential } from '/client.js';
  1. navigator.credentials.create() এর বিকল্পগুলির সাথে registerCredential() চালু করুন।

রেজিস্ট্রেশনের পর getCredentials() কল করে শংসাপত্রের তালিকা পুনর্নবীকরণ করতে ভুলবেন না।

views/home.html

register.addEventListener('click', e => {
  registerCredential().then(user => {
    getCredentials();
  }).catch(e => alert(e));
});

এখন আপনি একটি নতুন শংসাপত্র নিবন্ধন করতে এবং এটি সম্পর্কে তথ্য প্রদর্শন করতে সক্ষম হওয়া উচিত। আপনি আপনার লাইভ ওয়েবসাইটে এটি চেষ্টা করতে পারেন.

এই বিভাগের জন্য চূড়ান্ত কোড

views/home.html

...
      <p id="uvpa_unavailable" class="hidden">
        This device does not support User Verifying Platform Authenticator. You can't register a credential.
      </p>
      <h3 class="mdc-typography mdc-typography--headline6">
        Your registered credentials:
      </h3>
      <section>
        <div id="list"></div>
        <mwc-fab id="register" class="hidden" icon="add"></mwc-fab>
      </section>
      <mwc-button raised><a href="/reauth">Try reauth</a></mwc-button>
      <mwc-button><a href="/auth/signout">Sign out</a></mwc-button>
    </main>
    <script type="module">
      import { _fetch, registerCredential, unregisterCredential } from '/client.js';
      import { html, render } from 'https://unpkg.com/lit-html@1.0.0/lit-html.js?module';

      const register = document.querySelector('#register');

      if (window.PublicKeyCredential) {
        PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()
        .then(uvpaa => {
          if (uvpaa) {
            register.classList.remove('hidden');
          } else {
            document
              .querySelector('#uvpa_unavailable')
              .classList.remove('hidden');
          }
        });        
      } else {
        document
          .querySelector('#uvpa_unavailable')
          .classList.remove('hidden');
      }

      const getCredentials = async () => {
        const res = await _fetch('/auth/getKeys');
        const list = document.querySelector('#list');
        const creds = html`${res.credentials.length > 0 ? res.credentials.map(cred => html`
          <div class="mdc-card credential">
            <span class="mdc-typography mdc-typography--body2">${cred.credId}</span>
            <pre class="public-key">${cred.publicKey}</pre>
            <div class="mdc-card__actions">
              <mwc-button id="${cred.credId}" @click="${removeCredential}" raised>Remove</mwc-button>
            </div>
          </div>`) : html`
          <p>No credentials found.</p>
          `}`;
        render(creds, list);
      };

      getCredentials();

      const removeCredential = async e => {
        try {
          await unregisterCredential(e.target.id);
          getCredentials();
        } catch (e) {
          alert(e);
        }
      };

      register.addEventListener('click', e => {
        registerCredential({
          attestation: 'none',
          authenticatorSelection: {
            authenticatorAttachment: 'platform',
            userVerification: 'required',
            requireResidentKey: false
          }
        })
        .then(user => {
          getCredentials();
        })
        .catch(e => alert(e));
      });
    </script>
...

public/client.js

...
export const unregisterCredential = async (credId) => {
  localStorage.removeItem('credId');
  return _fetch(`/auth/removeKey?credId=${encodeURIComponent(credId)}`);
};
...

5. একটি আঙ্গুলের ছাপ দিয়ে ব্যবহারকারীকে প্রমাণীকরণ করুন৷

আপনার কাছে এখন একটি শংসাপত্র নিবন্ধিত এবং ব্যবহারকারীকে প্রমাণীকরণের উপায় হিসাবে ব্যবহার করার জন্য প্রস্তুত। এখন আপনি ওয়েবসাইটে পুনরায় প্রমাণীকরণ কার্যকারিতা যোগ করুন। এখানে ব্যবহারকারীর অভিজ্ঞতা আছে:

যখন একজন ব্যবহারকারী /reauth পৃষ্ঠায় অবতরণ করেন, তখন তারা একটি প্রমাণীকরণ বোতাম দেখতে পান যদি বায়োমেট্রিক প্রমাণীকরণ সম্ভব হয়। একটি আঙ্গুলের ছাপ (UVPA) দিয়ে প্রমাণীকরণ শুরু হয় যখন তারা প্রমাণীকরণে ট্যাপ করে, সফলভাবে প্রমাণীকরণ করে এবং তারপর /home পৃষ্ঠায় ল্যান্ড করে। বায়োমেট্রিক প্রমাণীকরণ উপলব্ধ না হলে বা বায়োমেট্রিক সহ একটি প্রমাণীকরণ ব্যর্থ হলে, UI বিদ্যমান পাসওয়ার্ড ফর্মটি ব্যবহার করতে ফিরে আসে।

b8770c4e7475b075.png

authenticate() ফাংশন তৈরি করুন

authenticate() নামে একটি ফাংশন তৈরি করুন, যা আঙ্গুলের ছাপ দিয়ে ব্যবহারকারীর পরিচয় যাচাই করে। আপনি এখানে জাভাস্ক্রিপ্ট কোড যোগ করুন:

public/client.js

export const authenticate = async () => {

};

সার্ভার এন্ডপয়েন্ট থেকে চ্যালেঞ্জ এবং অন্যান্য বিকল্পগুলি পান

  1. প্রমাণীকরণের আগে, ব্যবহারকারীর কাছে একটি সঞ্চিত শংসাপত্র আইডি আছে কিনা তা পরীক্ষা করুন এবং যদি তারা তা করে তবে এটি একটি ক্যোয়ারী প্যারামিটার হিসাবে সেট করুন৷

আপনি যখন অন্যান্য বিকল্পগুলির সাথে একটি শংসাপত্র আইডি প্রদান করেন, তখন সার্ভার প্রাসঙ্গিক allowCredentials প্রদান করতে পারে এবং এটি ব্যবহারকারীর যাচাইকরণকে নির্ভরযোগ্য করে তোলে।

public/client.js

const opts = {};

let url = '/auth/signinRequest';
const credId = localStorage.getItem(`credId`);
if (credId) {
  url += `?credId=${encodeURIComponent(credId)}`;
}
  1. আপনি ব্যবহারকারীকে প্রমাণীকরণ করতে বলার আগে, সার্ভারকে একটি চ্যালেঞ্জ এবং অন্যান্য পরামিতি ফেরত পাঠাতে বলুন। সার্ভারে একটি POST অনুরোধ পাঠাতে একটি যুক্তি হিসাবে opts সহ _fetch() এ কল করুন।

public/client.js

const options = await _fetch(url, opts);

এখানে উদাহরণের বিকল্পগুলি রয়েছে যা আপনার গ্রহণ করা উচিত ( PublicKeyCredentialRequestOptions এর সাথে সারিবদ্ধ)।

{
  "challenge": "...",
  "timeout": 1800000,
  "rpId": "webauthn-codelab.glitch.me",
  "userVerification": "required",
  "allowCredentials": [
    {
      "id": "...",
      "type": "public-key",
      "transports": [
        "internal"
      ]
    }
  ]
}

এখানে সবচেয়ে গুরুত্বপূর্ণ বিকল্প হল allowCredentials । আপনি যখন সার্ভার থেকে বিকল্পগুলি গ্রহণ করেন, তখন সার্ভারের পাশে ক্যোয়ারী প্যারামিটারে আইডি সহ একটি শংসাপত্র পাওয়া যায় কিনা তার উপর নির্ভর করে allowCredentials একটি অ্যারেতে একটি একক বস্তু বা একটি খালি অ্যারে হওয়া উচিত৷

  1. null দিয়ে প্রতিশ্রুতি সমাধান করুন যখন allowCredentials একটি খালি অ্যারে হয় যাতে UI একটি পাসওয়ার্ড চাওয়ার জন্য ফিরে আসে।
if (options.allowCredentials.length === 0) {
  console.info('No registered credentials found.');
  return Promise.resolve(null);
}

স্থানীয়ভাবে ব্যবহারকারীকে যাচাই করুন এবং একটি শংসাপত্র পান

  1. যেহেতু এই বিকল্পগুলি HTTP প্রোটোকলের মাধ্যমে যাওয়ার জন্য এনকোডেড বিতরণ করা হয়, কিছু পরামিতিগুলিকে আবার বাইনারিতে রূপান্তর করুন, বিশেষত challenge এবং allowCredentials অ্যারেতে অন্তর্ভুক্ত id উদাহরণ:

public/client.js

options.challenge = base64url.decode(options.challenge);

for (let cred of options.allowCredentials) {
  cred.id = base64url.decode(cred.id);
}
  1. একটি UVPA দিয়ে ব্যবহারকারীর পরিচয় যাচাই করতে navigator.credentials.get() পদ্ধতিতে কল করুন।

public/client.js

const cred = await navigator.credentials.get({
  publicKey: options
});

একবার ব্যবহারকারী তাদের পরিচয় যাচাই করলে, আপনার একটি শংসাপত্র পাওয়া উচিত যা আপনি সার্ভারে পাঠাতে এবং ব্যবহারকারীকে প্রমাণীকরণ করতে পারেন।

শংসাপত্র যাচাই করুন

এখানে একটি উদাহরণ PublicKeyCredential অবজেক্ট ( response হল AuthenticatorAssertionResponse ) যা আপনার পাওয়া উচিত ছিল:

{
  "id": "...",
  "type": "public-key",
  "rawId": "...",
  "response": {
    "clientDataJSON": "...",
    "authenticatorData": "...",
    "signature": "...",
    "userHandle": ""
  }
}
  1. শংসাপত্রের বাইনারি প্যারামিটার এনকোড করুন যাতে এটি একটি স্ট্রিং হিসাবে সার্ভারে বিতরণ করা যেতে পারে:

public/client.js

const credential = {};
credential.id = cred.id;
credential.type = cred.type;
credential.rawId = base64url.encode(cred.rawId);

if (cred.response) {
  const clientDataJSON =
    base64url.encode(cred.response.clientDataJSON);
  const authenticatorData =
    base64url.encode(cred.response.authenticatorData);
  const signature =
    base64url.encode(cred.response.signature);
  const userHandle =
    base64url.encode(cred.response.userHandle);
  credential.response = {
    clientDataJSON,
    authenticatorData,
    signature,
    userHandle,
  };
}
  1. বস্তুটিকে সার্ভারে পাঠান এবং, যদি এটি HTTP code 200 ফেরত দেয়, তাহলে ব্যবহারকারীকে সফলভাবে সাইন ইন করা হয়েছে বলে বিবেচনা করুন:

public/client.js

return await _fetch(`/auth/signinResponse`, credential);

আপনার কাছে এখন সম্পূর্ণ authentication() ফাংশন আছে!

এই বিভাগের জন্য চূড়ান্ত কোড

public/client.js

...
export const authenticate = async () => {
  const opts = {};

  let url = '/auth/signinRequest';
  const credId = localStorage.getItem(`credId`);
  if (credId) {
    url += `?credId=${encodeURIComponent(credId)}`;
  }
  
  const options = await _fetch(url, opts);
  
  if (options.allowCredentials.length === 0) {
    console.info('No registered credentials found.');
    return Promise.resolve(null);
  }

  options.challenge = base64url.decode(options.challenge);

  for (let cred of options.allowCredentials) {
    cred.id = base64url.decode(cred.id);
  }

  const cred = await navigator.credentials.get({
    publicKey: options
  });

  const credential = {};
  credential.id = cred.id;
  credential.type = cred.type;
  credential.rawId = base64url.encode(cred.rawId);

  if (cred.response) {
    const clientDataJSON =
      base64url.encode(cred.response.clientDataJSON);
    const authenticatorData =
      base64url.encode(cred.response.authenticatorData);
    const signature =
      base64url.encode(cred.response.signature);
    const userHandle =
      base64url.encode(cred.response.userHandle);
    credential.response = {
      clientDataJSON,
      authenticatorData,
      signature,
      userHandle,
    };
  }

  return await _fetch(`/auth/signinResponse`, credential);
};
...

6. পুনরায় প্রমাণীকরণ অভিজ্ঞতা সক্ষম করুন

UI তৈরি করুন

যখন ব্যবহারকারী ফিরে আসে, আপনি চান যে তারা যতটা সম্ভব সহজে এবং নিরাপদে পুনরায় প্রমাণীকরণ করুক। এখানেই বায়োমেট্রিক প্রমাণীকরণ উজ্জ্বল হয়। যাইহোক, এমন কিছু ক্ষেত্রে রয়েছে যেখানে বায়োমেট্রিক প্রমাণীকরণ কাজ নাও করতে পারে:

  • UVPA পাওয়া যায় না।
  • ব্যবহারকারী এখনও তাদের ডিভাইসে কোনো শংসাপত্র নিবন্ধন করেনি।
  • স্টোরেজ সাফ করা হয়েছে এবং ডিভাইসটি আর শংসাপত্রের আইডি মনে রাখে না।
  • ব্যবহারকারী কোনো কারণে তাদের পরিচয় যাচাই করতে অক্ষম, যেমন যখন তাদের আঙুল ভেজা থাকে বা তারা মাস্ক পরে থাকে।

এই কারণেই এটা সবসময় গুরুত্বপূর্ণ যে আপনি ফলব্যাক হিসাবে অন্যান্য সাইন-ইন বিকল্পগুলি প্রদান করেন৷ এই কোডল্যাবে, আপনি ফর্ম-ভিত্তিক পাসওয়ার্ড সমাধান ব্যবহার করেন।

19da999b0145054.png

  1. একটি প্রমাণীকরণ বোতাম দেখানোর জন্য UI যোগ করুন যা পাসওয়ার্ড ফর্ম ছাড়াও বায়োমেট্রিক প্রমাণীকরণকে আহ্বান করে।

ব্যবহারকারীর অবস্থার উপর নির্ভর করে বেছে বেছে তাদের মধ্যে একটি প্রদর্শন এবং লুকানোর জন্য hidden শ্রেণী ব্যবহার করুন।

views/reauth.html

<div id="uvpa_available" class="hidden">
  <h2>
    Verify your identity
  </h2>
  <div>
    <mwc-button id="reauth" raised>Authenticate</mwc-button>
  </div>
  <div>
    <mwc-button id="cancel">Sign-in with password</mwc-button>
  </div>
</div>
  1. ফর্মটিতে class="hidden" যোগ করুন:

views/reauth.html

<form id="form" method="POST" action="/auth/password" class="hidden">

বৈশিষ্ট্য সনাক্তকরণ এবং UVPA প্রাপ্যতা

এই শর্তগুলির মধ্যে একটি পূরণ হলে ব্যবহারকারীদের অবশ্যই একটি পাসওয়ার্ড দিয়ে সাইন ইন করতে হবে:

  • WebAuthn উপলব্ধ নয়।
  • UVPA পাওয়া যায় না।
  • এই UVPA-এর জন্য একটি শংসাপত্র আইডি আবিষ্কারযোগ্য নয়।

বেছে বেছে প্রমাণীকরণ বোতাম দেখান বা লুকান:

views/reauth.html

if (window.PublicKeyCredential) {
  PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()
  .then(uvpaa => {
    if (uvpaa && localStorage.getItem(`credId`)) {
      document
        .querySelector('#uvpa_available')
        .classList.remove('hidden');
    } else {
      form.classList.remove('hidden');
    }
  });        
} else {
  form.classList.remove('hidden');
}

পাসওয়ার্ড ফর্মে পতন

ব্যবহারকারীর পাসওয়ার্ড দিয়ে সাইন ইন করতেও সক্ষম হওয়া উচিত।

পাসওয়ার্ড ফর্ম দেখান এবং ব্যবহারকারী পাসওয়ার্ড দিয়ে সাইন ইন ক্লিক করলে প্রমাণীকরণ বোতামটি লুকান:।

views/reauth.html

const cancel = document.querySelector('#cancel');
cancel.addEventListener('click', e => {
  form.classList.remove('hidden');
  document
    .querySelector('#uvpa_available')
    .classList.add('hidden');
});

c4a82800889f078c.png

বায়োমেট্রিক প্রমাণীকরণ আহ্বান করুন

অবশেষে, বায়োমেট্রিক প্রমাণীকরণ সক্ষম করুন।

  1. বিদ্যমান import বিবৃতিতে authenticate যুক্ত করুন:

views/reauth.html

import { _fetch, authenticate } from '/client.js';
  1. যখন ব্যবহারকারী বায়োমেট্রিক প্রমাণীকরণ শুরু করতে প্রমাণীকরণে ট্যাপ করে তখন authenticate() চালু করুন।

নিশ্চিত করুন যে বায়োমেট্রিক প্রমাণীকরণে ব্যর্থতা পাসওয়ার্ড ফর্মে ফিরে আসে।

views/reauth.html

const button = document.querySelector('#reauth');
button.addEventListener('click', e => {
  authenticate().then(user => {
    if (user) {
      location.href = '/home';
    } else {
      throw 'User not found.';
    }
  }).catch(e => {
    console.error(e.message || e);
    alert('Authentication failed. Use password to sign-in.');
    form.classList.remove('hidden');
    document.querySelector('#uvpa_available').classList.add('hidden');
  });        
});

এই বিভাগের জন্য চূড়ান্ত কোড

views/reauth.html

...
    <main class="content">
      <div id="uvpa_available" class="hidden">
        <h2>
          Verify your identity
        </h2>
        <div>
          <mwc-button id="reauth" raised>Authenticate</mwc-button>
        </div>
        <div>
          <mwc-button id="cancel">Sign-in with password</mwc-button>
        </div>
      </div>
      <form id="form" method="POST" action="/auth/password" class="hidden">
        <h2>
          Enter a password
        </h2>
        <input type="hidden" name="username" value="{{username}}" />
        <div class="mdc-text-field mdc-text-field--filled">
          <span class="mdc-text-field__ripple"></span>
          <label class="mdc-floating-label" id="password-label">password</label>
          <input type="password" class="mdc-text-field__input" aria-labelledby="password-label" name="password" />
          <span class="mdc-line-ripple"></span>
        </div>
        <input type="submit" class="mdc-button mdc-button--raised" value="Sign-In" />
        <p class="instructions">password will be ignored in this demo.</p>
      </form>
    </main>
    <script src="https://unpkg.com/material-components-web@7.0.0/dist/material-components-web.min.js"></script>
    <script type="module">
      new mdc.textField.MDCTextField(document.querySelector('.mdc-text-field'));
      import { _fetch, authenticate } from '/client.js';
      const form = document.querySelector('#form');
      form.addEventListener('submit', e => {
        e.preventDefault();
        const form = new FormData(e.target);
        const cred = {};
        form.forEach((v, k) => cred[k] = v);
        _fetch(e.target.action, cred)
        .then(user => {
          location.href = '/home';
        })
        .catch(e => alert(e));
      });

      if (window.PublicKeyCredential) {
        PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()
        .then(uvpaa => {
          if (uvpaa && localStorage.getItem(`credId`)) {
            document
              .querySelector('#uvpa_available')
              .classList.remove('hidden');
          } else {
            form.classList.remove('hidden');
          }
        });        
      } else {
        form.classList.remove('hidden');
      }

      const cancel = document.querySelector('#cancel');
      cancel.addEventListener('click', e => {
        form.classList.remove('hidden');
        document
          .querySelector('#uvpa_available')
          .classList.add('hidden');
      });

      const button = document.querySelector('#reauth');
      button.addEventListener('click', e => {
        authenticate().then(user => {
          if (user) {
            location.href = '/home';
          } else {
            throw 'User not found.';
          }
        }).catch(e => {
          console.error(e.message || e);
          alert('Authentication failed. Use password to sign-in.');
          form.classList.remove('hidden');
          document.querySelector('#uvpa_available').classList.add('hidden');
        });        
      });
    </script>
...

7. অভিনন্দন!

আপনি এই কোডল্যাব শেষ!

আরও জানুন

আপনার সাহায্যের জন্য FIDO জোট থেকে Yuriy Ackermann কে বিশেষ ধন্যবাদ।