پیاده‌سازی طرف اعتماد

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

برای آزمایش معرفی ویژگی جدید ثبت‌نام کنید

سایت‌های درستی‌سنجی باید آزمایش اصلی را در سایتشان پیکربندی کرده باشند.

از Chrome 154، آزمایش‌های مبدأ طرف سوم با یک نکته مهم پشتیبانی می‌شوند: مبدأ ثبت‌شده برای آزمایش باید هم‌سایت با صادرکننده باشد. برای مثال:

  • دامنه صادرکننده: issuer.example
  • ثبت‌کننده OT: https://issuer.example
  • مبدأ جاوا اسکریپت: https://issuer.example (یا https://app.issuer.example با مطابقت زیردامنه)

پیکربندی فیلدهای فرم

فیلد کد پنهانی را به فرم ارسال ایمیل خود اضافه کنید:

<input
  type="email"
  name="email-address"
  autocomplete="email">
<input
  type="hidden"
  name="token"
  autocomplete="email-verification-token"
  nonce="rAnD0m-VaLuE">

الزامات فیلد:

  • فیلد ایمیل: type="email" و autocomplete="email" را تنظیم کنید تا Chrome بتواند نشانی را تکمیل خودکار و تشخیص دهد.
  • مشخصه‌های فیلد کد:
    • تنظیم autocomplete="email-verification-token": Chrome این فیلد را شناسایی می‌کند تا نشان را هنگام ارسال تکمیل کند.
    • تنظیم nonce="<VALUE>": سایت باید یک مقدار یک‌باره منحصربه‌فرد و محدود به جلسه برای درستی‌سنجی ارسال فرم ارائه دهد.

نمودار اصالت‌سنجی درستی‌سنجی ایمیل (EVT) را اعتبارسنجی کنید

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

اگر کد وجود دارد، آن را به این صورت اعتبارسنجی کنید:

  1. رمز را بااستفاده از کتابخانه SD-JWT تجزیه کنید.
  2. ادعاهای جلسه و مقادیر موردانتظار را اعتبارسنجی کنید.
  3. واگذاری ساناد را درستی‌سنجی کنید.
  4. فراداده صادرکننده را کاوش کنید و JWKS را واکشی کنید.
  5. امضاهای رمزنگاری و اتصال کلید را درستی‌سنجی کنید.

۱. تجزیه کردن کد

این کد از قالب RFC 9901: Selective Disclosure JWT (SD-JWT+KB) استفاده می‌کند. برای تجزیه و اعتبارسنجی کردن کد، از کتابخانه‌های مناسب برای پلاتفرم خود استفاده کنید. برای مثال، برای Node می‌توانید از @sd-jwt/core و jose استفاده کنید. در شکل خام، این‌گونه به‌نظر می‌رسد: این: یک JWT امضاشده توسط صادرکننده، به‌دنبال آن صفر یا چند «شفاف‌سازی»، و در پایان یک JWT «پیوند کلید» که هر مؤلفه با یک تیلدا از هم جدا شده است:

<Issuer-signed EVT>~<Disclosure 1>~...~<Disclosure N>~<Key Binding JWT>

در پیاده‌سازی فعلی، این کد دارای صفر شفاف‌سازی است (<Issuer-signed EVT>~<Key Binding JWT>). بااین‌حال، این وضعیت ممکن است در آینده تغییر کند.

رمزگشایی کردن نشان با کتابخانه:

import { decodeSdJwtSync } from "@sd-jwt/core";
import { createHash } from "node:crypto";
const hasher = (data, alg) =>
  createHash(alg === "sha-256" ? "sha256" : alg)
    .update(data)
    .digest();
const decoded = decodeSdJwtSync(rawToken, hasher);
const evtPayload = decoded.jwt.payload;
const kbPayload = decoded.kbJwt?.payload;

اگر verifier.example demo@provider.example را درستی‌سنجی کند، کد رمزگشایی‌شده شبیه به این خواهد بود:

{
  "evtJwtDecodedHeader": {
    "typ": "evt+jwt",
    "alg": "EdDSA",
    "kid": "issuer-key-id"
  },
  "evtJwtDecodedPayload": {
    "iss": "https://provider.example",
    "iat": 12345678901,
    "exp": 12345679901,
    "cnf": {
      "jwk": {
        "kty": "OKP",
        "crv": "Ed25519",
        "x": "pUbLiCkEy123pUbLiCkEy123pUbLiCkEy123"
      }
    },
    "email": "demo@provider.example",
    "email_verified": true
  },
  "kbJwtDecodedHeader": {
    "alg": "EdDSA",
    "typ": "kb+jwt"
  },
  "kbJwtDecodedPayload": {
    "aud": "https://verifier.example",
    "iat": 12345678901,
    "nonce": "rAnDoM123rAnDoM123rAnDoM123rAnDoM123",
    "sd_hash": "hAsH456hAsH456hAsH456hAsH456hAsH456"
  },
  "disclosures": []
}

۲. اعتبارسنجی مقادیر موردانتظار و ادعاهای جلسه

بررسی کنید که مقادیر پایه در پیام‌واره با مقادیر ارائه‌شده و موردانتظار شما مطابقت داشته باشد:

  • ‫email_verified: باید true باشد.
  • ‫email: باید با نشانی ایمیل ارسال‌شده در فرم مطابقت داشته باشد.
  • ‫aud (مخاطب): باید با مبدأ سایتتان مطابقت داشته باشد.
  • nonce: باید با مقدار یک‌بارمصرف ارائه‌شده در فرم شما مطابقت داشته باشد.
  • ‫iat (صادرشده در) و exp (انقضا): تأیید کنید کد در بازه زمانی معتبر خود قرار دارد و منقضی نشده است.

۳. درستی‌سنجی واگذاری ساناد

سابقه ساناد _email-verification را برای دامنه نشانی ایمیل درستی‌سنجی کنید. برای مثال، برای demo@gmail.com، ساختار TXT _email-verification.gmail.com را پُرسمان کنید. برای این ارائه‌دهنده، پُرسمان مکان ارائه‌دهنده حساب را برمی‌گرداند، یعنی accounts.google.com.

$ dig +short TXT _email-verification.gmail.com
"iss=accounts.google.com"

تأیید کنید که طرح صادرکننده https:// است و https://<domain> با ادعای iss در EVT مطابقت دارد.

۴. درستی‌سنجی امضای EVT

فراداده‌های شناسایی صادرکننده را از https://<issuer>/.well-known/email-verification واکشی کنید:

{
  "issuance_endpoint": "https://accounts.google.com/gsi/email-verification/issue",
  "jwks_uri": "https://verifiablecredentials-pa.googleapis.com/.well-known/vc-public-jwks",
  "signing_alg_values_supported": ["EdDSA"]
}

«مجموعه کلید وب JSON» را از jwks_uri واکشی کنید.

از کتابخانه SD-JWT خود برای درستی‌سنجی بسته کد استفاده کنید. کتابخانه اعتبارسنجی را هماهنگ می‌کند:

  1. امضای صادرکننده در EVT دربرابر JWKS واکشی‌شده اعتبارسنجی می‌شود.
  2. درحال اعتبارسنجی امضای مرورگر در KB-JWT بااستفاده از کلید عمومی گذرا در cnf.jwk.
  3. درحال درستی‌سنجی پیوند کلید (aud،‏ nonce، و درهم‌سازی خلاصه sd_hash).

نمونه منطق درستی‌سنجی در Node.js:

import { SDJwtInstance } from "@sd-jwt/core";
import { importJWK, compactVerify } from "jose";
import { createHash } from "node:crypto";
const hasher = (data, alg) =>
  createHash(alg === "sha-256" ? "sha256" : alg)
    .update(data)
    .digest();
const sdJwt = new SDJwtInstance({ hasher });
sdJwt.config({
  hasher,
  // Verifier for the Issuer-signed EVT
  verifier: async (data, sig) => {
    const token = `${data}.${sig}`;
    const header = decoded.jwt.header;
    const headerAlg = header.alg || "ES256";
    // Match by kid if present, or iterate across matching algorithm keys
    const keysToTry = header.kid
      ? jwksData.keys.filter(k => k.kid === header.kid)
      : jwksData.keys;
    for (const jwk of keysToTry) {
      try {
        const pubKey = await importJWK(jwk, jwk.alg || headerAlg);
        await compactVerify(token, pubKey);
        return true;
      } catch {
        // Try next candidate key
      }
    }
    return false;
  },
  // Verifier for the Key Binding JWT (KB-JWT)
  kbVerifier: async (data, sig) => {
    try {
      const browserJwkKey = evtPayload.cnf?.jwk;
      if (!browserJwkKey) return false;
      const pubKey = await importJWK(browserJwkKey, decoded.kbJwt.header.alg || "ES256");
      await compactVerify(`${data}.${sig}`, pubKey);
      return true;
    } catch {
      return false;
    }
  },
});
// The library automatically verifies EVT signature, KB-JWT signature, audience, nonce, and sd_hash
const result = await sdJwt.verify(rawToken, {
  kb: {
    expectedNonce: sessionNonce,
    expectedAudience: "https://example.com",
    required: true,
  },
});
const verifiedPayload = result.payload;

اگر همه مراحل موفقیت‌آمیز باشد، نشانی ایمیل را دربرابر ارائه‌دهنده درستی‌سنجی کرده‌اید. اگر درستی‌سنجی ناموفق بود، به ارسال ایمیل تأیید به کاربر بااستفاده از جریان عادی‌تان برگردید.

عیب‌یابی

اگر درستی‌سنجی ناموفق بود یا مرورگر کد ارائه نکرد، مشکلات رایج زیر را بررسی کنید:

فیلد کد در ارسال خالی است

  • ثبت‌نام دوره آزمایشی مبدأ: تأیید کنید که سرایند Origin-Trial یا برچسب <meta> در صفحه ارائه می‌شود. برای آزمایش‌های مبدأ طرف سوم (Chrome 154 و نسخه‌های بالاتر)، مبدأ آزمایش ثبت‌شده باید هم‌سایت با صادرکننده باشد (https://<issuer-domain>). می‌توانید پیکربندی آزمایش مبدأ را در سایتی در «ابزارهای توسعه‌دهندگان» در بخش برنامه > قاب‌ها > (قاب مربوطه را انتخاب کنید) > آزمایش‌های مبدأ بازرسی کنید.
  • نشانه‌گذاری فرم: هم <input type="email" autocomplete="email"> و هم <input type="hidden" autocomplete="email-verification-token" nonce="..."> باید در همان عنصر <form> باشند (در مرزهای «سایه DOM» جدا نباشند)، و nonce نباید خالی باشد.
  • ارسال زودرس یا صفحه بازاستفاده‌شده: مرورگر پس‌از وارد شدن یا تکمیل خودکار ایمیل، نشان را در پس‌زمینه واکشی می‌کند. ارسال قبل‌از تکمیل درخواست باعث می‌شود نشان خالی بماند. این اتفاق زمانی روی می‌دهد که کاربر پس‌از وارد کردن ایمیل خود، برای ارسال فرم کلید «برگشت» را فشار دهد.
  • پیش‌نیازهای مرورگر و ارائه‌دهنده: کاربر باید در نمایه مرورگر یکسان به سیستم ارائه‌دهنده شرکت‌کننده وارد شده باشد و ایمیل تأییدشده در تنظیمات Chrome فعال باشد (chrome://settings/contactInfo).

درستی‌سنجی امضای صادرکننده ناموفق بود

  • سرصفحه kid وجود ندارد: ادعای kid (شناسه کلید) در سرصفحه EVT و JWKS اختیاری است (برای مثال، Gmail kid را حذف می‌کند). اگر kid وجود ندارد، به‌جای اینکه جستجوی شناسه کلید ناموفق باشد، در همه کلیدهای نامزد در jwks_uri صادرکننده تکرار کنید.
  • شناسه‌های الگوریتم (EdDSA و Ed25519): صادرکنندگان و کتابخانه‌ها می‌توانند EdDSA یا Ed25519 (همراه با ES256) را مشخص کنند. مطمئن شوید که منطق درستی‌سنجی و وارد کردن JWK شما هر دو شناسه را می‌پذیرد.
  • صادرکننده (iss) قالب مبدأ: گزارش DNS TXT (_email-verification.<domain>) حاوی نام میزبان ساده (iss=accounts.issuer.example) است، درحالی‌که ادعای EVT iss مبدأ HTTPS کامل (https://accounts.issuer.example، بدون اسلش انتهایی) است. پیشوند https:// را به مقدار گزارش ساناد قبل‌از مقایسه اضافه کنید.

اعتبارسنجی پیوند کلید (KB-JWT) ناموفق بود

  • مقدار یک‌بارمصرف نامنطبق یا منقضی‌شده: مطمئن شوید nonce ارائه‌شده در <input> با مقدار یک‌بارمصرف جلسه فعال در سرورتان مطابقت داشته باشد و توسط برگه دیگری بازنویسی نشده باشد یا توسط درخواست قبلی مصرف نشده باشد.
  • عدم تطابق مخاطب (aud): ادعای aud مبدأ HTTPS درستی‌سنج (https://verifier.example، بدون مسیر یا خط مورب انتهایی) است.

مقایسه ادعای ایمیل (email) ناموفق بود

  • حروف و استانداردسازی: Chrome نسخه ۱۵۶ و بالاتر ادعای email را بایت‌به‌بایت همان‌طور که در فرم وارد شده است برمی‌گرداند، اما نسخه‌های قدیمی‌تر مرورگر یا ارائه‌دهندگان ممکن است نشانی استانداردسازی‌شده‌ای برگردانند (برای مثال، First.Last@example.com به‌جای first.last@example.com). هنگام مطابقت دادن ادعای email نشان با مقدار فرم ارسال‌شده، از مقایسه غیرحساس به حروف استفاده کنید.