پیاده‌سازی ارائه‌دهنده ایمیل (صادرکننده)

برای جزئیات بیشتر، می‌توانید نسخه نمایشی ارائه‌دهنده ایمیل ساختگی کد را گام‌به‌گام دنبال کنید و به مراحل صادرکننده در API درستی‌سنجی ایمیل و پیشنهادهای پروتکل درستی‌سنجی ایمیل مراجعه کنید.

به‌عنوان صادرکننده، لازم نیست برای آزمایش مبدأ ثبت‌نام کنید یا کد ارائه دهید زیرا سایت طرف متکی رفتار مرورگر را راه‌اندازی می‌کند. مطمئن شوید که نقطه‌های پایانی شما برای پاسخ به این درخواست‌ها پیکربندی شده‌اند.

پیکربندی کاوش صادرکننده

برای اینکه مرورگرها بتوانند به‌طور خودکار نقاط پایانی درستی‌سنجی شما را هنگام انتخاب نشانی ایمیل متعلق به دامنه‌تان پیدا کنند، پیکربندی‌تان را بااستفاده از «ساناد» و نقطه پایانی .well-known پروتکل انتقال ابرمتن نمایان کنید.

پیکربندی گزارش نماینده ساناد

یک ساختار ساناد TXT در دامنه ایمیل خود پیکربندی کنید که اختیار درستی‌سنجی را به شناسه صادرکننده شما واگذار می‌کند. این شناسه‌ها می‌توانند از همان دامنه استفاده کنند بسته به زیرساخت شما.

قالب ضبط: _email-verification.<email-domain>

فایل منطقه نمونه:

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

میزبانی نقطه پایانی .well-known/email-verification

فایل فراداده JSON را در دامنه صادرکننده خود تحت مسیر /.well-known/ میزبانی کنید. این فایل قابلیت‌های صدور شما و الگوریتم‌های امضای رمزنگاری‌شده‌ای را که زیرساختتان پشتیبانی می‌کند مشخص می‌کند.

نقطه پایانی: 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

ممکن است ازقبل یک منبع .well-known JSON اضافی را به‌عنوان بخشی از Federated Credentials (FedCM) API پیاده‌سازی کرده باشید. این منبع پیوندهایی به نقطه پایان حساب‌ها و نشانی وب ورود به سیستم شما ارائه می‌دهد.

نقطه پایانی: 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

یا وضعیت را بااستفاده از جاوا اسکریپت در زمینه برنامه وب خود به‌روز کنید:

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

رسیدگی به درخواست‌های صدور

‫issuance_endpoint شما درخواست application/json POST را دریافت می‌کند که شامل کلید email و سرصفحه‌های امضاهای پیام HTTP برای Signature، Signature-Input، و Signature-Key است.

از کتابخانه‌ای استفاده کنید که از سرایندهای ساختاری و «امضاهای پیام 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 درخواستی برای درستی‌سنجی است.

پاسخ صدور

پس‌از اعتبارسنجی موفقیت‌آمیز جلسه و نشان درخواست، یک ‫JWT افشای انتخابی (SD-JWT) امضاشده تولید کنید که بااستفاده از کتابخانه‌های مناسب برای پلاتفرم شما به‌صورت JSON برگردانده می‌شود. برای مثال، برای 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 نسخه ۱۵۴ و بالاتر Sec-Fetch-Dest: email-verification (با خط تیره) ارسال می‌کند، درحالی‌که Chrome نسخه ۱۵۳ emailverification (بدون خط تیره) ارسال می‌کرد. هر دو مقدار را درطول عرضه بپذیرید.
  • عدم تطابق «امضای پیام HTTP» (@authority) در پشت پروکسی: هنگام درستی‌سنجی امضای RFC 9421، عنصر @authority میزبان عمومی را منعکس می‌کند. اگر سرورتان پشت یک پروکسی معکوس یا ترازکننده بار قرار دارد، نشانی وب درستی‌سنجی را بااستفاده از X-Forwarded-Host (یا مبدأ عمومی‌تان) به‌جای نام میزبان داخلی بازسازی کنید و Content-Digest را روی بایت‌های بدنه درخواست خام قبل‌از تجزیه JSON محاسبه کنید.

مرورگر 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: مطمئن شوید ادعای iss EVT دقیقاً منشأ HTTPS (https://<issuer-domain>، بدون اسلش انتهایی) مطابق با فراداده .well-known/email-verification شما باشد، و cnf.jwk کلید عمومی موقت مرورگر را از سرصفحه Signature-Key جاسازی کند.