ইমেল পরিষেবা প্রদানকারী (ইস্যুকারী) প্রয়োগ

আরও বিস্তারিত জানতে, আপনি মক ইমেল পরিষেবা প্রদানকারী ডেমো কোড দেখে নিতে পারেন এবং ইমেল যাচাইকরণ এপিআই ও ইমেল যাচাইকরণ প্রোটোকল প্রস্তাবে উল্লিখিত ইস্যুকারী ধাপগুলি দেখতে পারেন।

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

ইস্যুকারী শনাক্তকরণ কনফিগার করা

আপনার ডোমেনের কোনও ইমেল আইডি বেছে নেওয়া হলে, ব্রাউজারকে অটোমেটিক আপনার যাচাইকরণ এন্ডপয়েন্ট খুঁজে পাওয়ার অনুমতি দিতে, DNS ও .well-known HTTP এন্ডপয়েন্ট ব্যবহার করে আপনার কনফিগারেশন এক্সপোজ করুন।

ডিএনএস ডেলিগেট রেকর্ড কনফিগার করা

আপনার ইমেল ডোমেনে একটি DNS TXT রেকর্ড কনফিগার করুন যা আপনার ইস্যুকারী শনাক্তকারীকে যাচাইকরণ কর্তৃত্ব প্রদান করে। আপনার পরিকাঠামোর উপর নির্ভর করে এইসব শনাক্তকারী একই ডোমেন ব্যবহার করতে পারে।

রেকর্ড ফর্ম্যাট: _email-verification.<email-domain>

জোন ফাইলের উদাহরণ:

_email-verification.example.com IN TXT "iss=accounts.issuer.example"

.well-known/email-verification এন্ডপয়েন্ট হোস্ট করা

/.well-known/ পাথের অধীনে আপনার ইস্যুকারী ডোমেনে একটি JSON মেটাডেটা ফাইল হোস্ট করুন। এই ফাইলে আপনার ইস্যু করার ক্ষমতা এবং আপনার ইনফ্রাস্ট্রাকচার যেসব ক্রিপ্টোগ্রাফিক সই করার অ্যালগরিদম সমর্থন করে, তার বিবরণ দেওয়া থাকে।

এন্ডপয়েন্ট: https://<issuer-domain>/.well-known/email-verification

উত্তরের উদাহরণ:

{
  "issuance_endpoint": "https://accounts.issuer.example/email-verification/issuance",
  "jwks_uri": "https://accounts.issuer.example/.well-known/vc-public-jwks",
  "signing_alg_values_supported": ["EdDSA", "ES256"]
}

.well-known/web-identity এন্ডপয়েন্ট হোস্ট করা

আপনি হয়ত ইতিমধ্যেই Federated Credentials (FedCM) API-এর অংশ হিসেবে অতিরিক্ত .well-known JSON রিসোর্স প্রয়োগ করেছেন। এই রিসোর্স আপনার অ্যাকাউন্টের এন্ডপয়েন্ট এবং লগ-ইন URL-এর লিঙ্ক প্রদান করে।

এন্ডপয়েন্ট: https://<domain>/.well-known/web-identity

উত্তরের উদাহরণ:

{
  "accounts_endpoint": "https://accounts.issuer.example/accounts",
  "login_url": "https://accounts.issuer.example/login"
}

অ্যাকাউন্ট এন্ডপয়েন্ট ব্যবহার করা

FedCM API-এর অ্যাকাউন্ট এন্ডপয়েন্ট বর্তমানে সাইন-ইন করা অ্যাকাউন্টের একটি তালিকা প্রদান করে। নিচে দেওয়া উদাহরণে ন্যূনতম উত্তর দেখানো হয়েছে। আরও বিবরণের জন্য, পরিচয় প্রদানকারী প্রয়োগ করা সংক্রান্ত নির্দেশিকা দেখুন।

এন্ডপয়েন্ট: .well-known/web-identity-এ উল্লেখ করা হয়েছে

নিচে একটি উদাহরণ উত্তর দেওয়া হল:

{
  "accounts": [
    {
      "id": "demo-example",
      "name": "Demo User",
      "email": "demo@example.com",
      "given_name": "Demo"
    }
  ]
}

Login Status API-এর সাথে ইন্টিগ্রেট করা

প্রোভাইডারের সাথে ব্যবহারকারীর একটি অ্যাক্টিভ সেশন থাকতে হবে এবং আপনাকে Login Status API ব্যবহার করে ব্রাউজারকে সেই বিষয়ে সিগন্যাল দিতে হবে।

ব্যবহারকারী সফলভাবে সাইন-ইন বা সাইন-আউট করলে, ম্যাচ করা HTTP উত্তর হেডার পরিবেশন করুন:

Set-Login: logged-in
Set-Login: logged-out

অথবা, আপনার ওয়েব অ্যাপ্লিকেশন কন্টেক্সটে JavaScript ব্যবহার করে স্ট্যাটাস আপডেট করুন:

navigator.login.setStatus("logged-in");
navigator.login.setStatus("logged-out");

ইস্যু করার অনুরোধ ম্যানেজ করা

আপনার issuance_endpoint একটি application/json POST অনুরোধ পায় যাতে Signature, Signature-Input ও Signature-Key-এর জন্য email কী ও HTTP মেসেজ সিগনেচার হেডার থাকে।

আপনার এনভায়রনমেন্টের জন্য স্ট্রাকচার্ড হেডার ও HTTP মেসেজ সিগনেচার কাজ করে এমন লাইব্রেরি ব্যবহার করুন। Node.js-এ আপনি structured-headers এবং http-message-sig ব্যবহার করতে পারবেন।

সম্পূর্ণ অনুরোধের ফর্ম্যাট:

POST /email-verification/issuance HTTP/1.1
Host: provider.example
Accept: application/json
Content-Digest: sha-256=:aBc123aBc123aBc123aBc123aBc123=:
Content-Type: application/json
Sec-Fetch-Dest: email-verification
Signature: sig=:+dEf567dEf567/dEf567dEf567dEf567/dEf567==:
Signature-Input: sig=("@method" "@authority" "@path" "content-digest" "signature-key");created=1786455840
Signature-Key: sig=hwk;crv="Ed25519";kty="OKP";x="gHi890_gHi890_gHi890"

{email: "demo@example.com"}

অনুরোধ পার্স ও যাচাই করুন:

  • সেশন যাচাইকরণ: অনুরোধের সাথে পাঠানো আপনার ফার্স্ট-পার্টি সেশন কুকি যাচাই করুন। ব্যবহারকারীকে অবশ্যই যাচাই করতে হবে।
  • Sec-Fetch-Dest হেডার: email-verification-এ সেট করা আছে।
  • HTTP মেসেজ সিগনেচার: Signature-Key-এ থাকা ক্ষণস্থায়ী পাবলিক কী ব্যবহার করে অনুরোধের সিগনেচার যাচাই করুন এবং Content-Digest ভ্যালিডেট করুন।
  • পেলোড: JSON বডিতে email যাচাইকরণের জন্য অনুরোধ করা স্ট্রিং থাকে।

ইস্যু সংক্রান্ত উত্তর

সেশন ও অনুরোধ টোকেন সফলভাবে যাচাই করার পরে, আপনার প্ল্যাটফর্মের জন্য উপযুক্ত লাইব্রেরি ব্যবহার করে JSON হিসেবে ফেরত দেওয়া একটি সাইন করা সিলেক্টিভ ডিসক্লোজার JWT (SD-JWT) তৈরি করুন। যেমন, Node-এর জন্য আপনি @sd-jwt/core এবং jose ব্যবহার করতে পারেন।

র পে-লোড ফর্ম্যাটটি এই ধরনের হতে হবে:

{
  "iss": "https://accounts.issuer.example",
  "iat": 1780272000,
  "exp": 1780272300,
  "cnf": {
    "jwk": {
      "kty": "EC",
      "crv": "P-256",
      "x": "pUbLiCKeY123pUbLiCKeY123pUbLiCKeY123",
      "y": "pUbLiCKeY456pUbLiCKeY456pUbLiCKeY456"
    }
  },
  "email": "demo@example.com",
  "email_verified": true
}

টোকেন তৈরি, স্বাক্ষর ও ফেরত দেওয়া:

import { importJWK, CompactSign } from "jose";
import { SDJwtInstance } from "@sd-jwt/core";
import crypto from "node:crypto";
// Issuer private key from secure storage
const privateKey = await importJWK(PRIVATE_KEY_JWK, "EdDSA");
const origin = url.origin;
const currentTime = Math.floor(Date.now() / 1000);
const evtPayload = {
  iss: origin,
  iat: currentTime,
  exp: currentTime + 300, // 5 minutes
  cnf: {
    jwk: browserJwk,
  },
  email: payload.email, // exactly as received in payload
  email_verified: true,
};
const sdJwt = new SDJwtInstance({
  signer: async (data) => {
    const [headerB64, payloadB64] = data.split(".");
    const header = JSON.parse(Buffer.from(headerB64, "base64url").toString());
    const payload = Buffer.from(payloadB64, "base64url");
    const signed = await new CompactSign(payload).setProtectedHeader(header).sign(privateKey);
    return signed.split(".").pop()!;
  },
  signAlg: "EdDSA",
  hasher: async (data, alg) => {
    const nodeAlg = alg.replace("-", "");
    return new Uint8Array(crypto.createHash(nodeAlg).update(data).digest());
  },
  hashAlg: "sha-256",
  saltGenerator: async () => crypto.randomBytes(16).toString("base64url"),
});
const issuanceToken = await sdJwt.issue(evtPayload, undefined, {
  header: {
    alg: "EdDSA",
    kid: PRIVATE_KEY_JWK.kid,
    typ: "evt+jwt",
  },
});
return sendResponse({ issuance_token: issuanceToken, }, 200);

ফলাফল হিসেবে পাওয়া উত্তরের বডিটি অনেকটা এরকম দেখতে হয়:

{
  "issuance_token": "tOkEn123tOkEn123tOkEn123...~"
}

ইমেল যাচাইকরণ টোকেন তৈরি ও স্বাক্ষর করার পরে, ব্রাউজার যাচাইকরণের জন্য এই টোকেনটি যাচাইকারী সাইটে পাঠায়।

সমস্যার সমাধান

ব্রাউজার আপনার এন্ডপয়েন্টের সাথে যোগাযোগ না করলে বা ইস্যু করা টোকেন বাতিল করলে, নিচে উল্লেখ করা সাধারণ সমস্যাগুলি চেক করুন:

ব্রাউজার কখনও accounts_endpoint বা issuance_endpoint-এ কল করে না

  • লগ-ইন স্ট্যাটাস সেট করা নেই: Chrome শুধুমাত্র তখনই আপনার এন্ডপয়েন্ট কোয়েরি করে যদি এটি জানে যে ব্যবহারকারী সাইন-ইন করেছেন। আপনার সাইন-ইন ফ্লো Set-Login: logged-in HTTP হেডার সেট করে বা navigator.login.setStatus("logged-in") কল করে কিনা তা নিশ্চিত করুন।
  • সেশন কুকি ব্লক করা হয়েছে (SameSite=None): ব্রাউজার আপনার accounts_endpoint ও issuance_endpoint কোনও আলাদা সাইটে কোনও নির্ভরযোগ্য পার্টি থেকে সংগ্রহ করে। যেহেতু এগুলি ক্রস-সাইট অনুরোধ, তাই আপনার সেশন কুকিতে SameSite=None; Secure থাকতেই হবে। SameSite=Lax কুকি একই সাইটের যাচাইকারীকে দিয়ে পরীক্ষা করার সময় কাজ করে, কিন্তু ক্রস-সাইট অনুরোধের ক্ষেত্রে এটি বাদ দেওয়া হয়।
  • ডিসকভারি বা অ্যাকাউন্ট না মেলা: যাচাই করে দেখুন যে _email-verification.<email-domain> একটি TXT রেকর্ড রিটার্ন করে (iss=<issuer-domain>, https:// ছাড়া), দুটি .well-known এন্ডপয়েন্টই Content-Type: application/json রিটার্ন করে এবং accounts_endpoint উত্তরে এমন একটি অ্যাকাউন্ট আছে যার email, লেখা ঠিকানার সাথে মেলে।

ইস্যু করার অনুরোধ যাচাইকরণ সম্পূর্ণ হয়নি

  • Sec-Fetch-Dest হেডার বানান: Chrome 154+ Sec-Fetch-Dest: email-verification (হাইফেন সহ) পাঠায়, যেখানে Chrome 153 emailverification (হাইফেন ছাড়া) পাঠায়। রোল-আউটের সময় দুটি ভ্যালুই গ্রহণ করা হবে।
  • প্রক্সির পিছনে HTTP মেসেজ সিগনেচার (@authority) না মেলা: RFC 9421 সিগনেচার যাচাই করার সময়, @authority কম্পোনেন্টটি সর্বজনীন হোস্টকে প্রতিফলিত করে। আপনার সার্ভার যদি কোনও রিভার্স প্রক্সি বা লোড ব্যালেন্সারের পিছনে থাকে, তাহলে ইন্টার্নাল হোস্টনেমের পরিবর্তে X-Forwarded-Host (বা আপনার পাবলিক অরিজিন) ব্যবহার করে যাচাইকরণ URL রিকনস্ট্রাক্ট করুন এবং JSON পার্সিংয়ের আগে কাঁচা অনুরোধের বডির বাইট Content-Digest গণনা করুন।

ব্রাউজার রিটার্ন করা issuance_token প্রত্যাখ্যান করে

  • পরিবর্তিত বা ক্যাননিক্যাল email দাবি: Chrome 156 থেকে, ব্রাউজার চেক করে যে EVT email দাবিটি অনুরোধ করা email বাইটের সাথে বাইট-ফর-বাইট ম্যাচ করছে কিনা। আপনার ব্যাকএন্ড যদি কোনও ক্যাননিকাল অ্যাকাউন্ট ফর্ম্যাটে (যেমন, first.LAST@example.com অনুরোধ করা হলে First.Last@example.com রিটার্ন করা) ঠিকানাটি স্বাভাবিক করে তোলে, তাহলে Chrome টোকেনটি বাদ দেয়। ব্যবহারকারীর অ্যাকাউন্টের সাথে অনুরোধটি ম্যাচ করাও, কিন্তু অনুরোধের বডিতে পাওয়া হুবহু email স্ট্রিংটি ফেরত দিও।
  • শেষের দিকে টিল্ড (~) নেই: কোনও ডিসক্লোজার না থাকলেও, issuance_token অবশ্যই একটি বৈধ SD-JWT হতে হবে যার শেষে টিল্ড (<Issuer-signed-JWT>~) থাকবে, যাতে ব্রাউজার <KB-JWT> যোগ করতে পারে।
  • ম্যাচ না করা iss বা cnf.jwk: EVT iss দাবিটি যেন আপনার .well-known/email-verification মেটাডেটার সাথে হুবহু মিলে যায়, এটি নিশ্চিত করুন। এটি যেন সঠিক HTTPS অরিজিন (https://<issuer-domain>, শেষে কোনও স্ল্যাশ নেই) হয় এবং cnf.jwk যেন Signature-Key হেডার থেকে ব্রাউজারের ক্ষণস্থায়ী পাবলিক কী এম্বেড করে।