للحصول على تفاصيل إضافية، يمكنك الاطّلاع على الرمز التجريبي لموفّر البريد الإلكتروني الوهمي والرجوع إلى خطوات الجهة المصدرة في اقتراحي واجهة برمجة التطبيقات الخاصة بميزة "إثبات ملكية عنوان البريد الإلكتروني" وبروتوكول ميزة "إثبات ملكية عنوان البريد الإلكتروني".
بصفتك جهة إصدار، لن تحتاج إلى الاشتراك في مرحلة التجربة والتقييم أو تقديم رمز مميّز، لأنّ الموقع الإلكتروني الخاص بالجهة المعتمِدة هو الذي يؤدي إلى تشغيل سلوك المتصفّح. تأكَّد من إعداد نقاط النهاية للردّ على هذه الطلبات.
ضبط ميزة "اكتشاف جهة الإصدار"
للسماح للمتصفّحات باكتشاف نقاط نهاية التحقّق تلقائيًا عند اختيار عنوان بريد إلكتروني تابع لنطاقك، يجب عرض إعداداتك باستخدام نظام أسماء النطاقات ونقطة نهاية HTTP .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. يوفر هذا المرجع روابط إلى نقطة نهاية حساباتك وعنوان URL لتسجيل الدخول.
نقطة النهاية: https://<domain>/.well-known/web-identity
مثال على الردّ:
{
"accounts_endpoint": "https://accounts.issuer.example/accounts",
"login_url": "https://accounts.issuer.example/login"
}
استخدام نقطة نهاية للحسابات
تقدّم نقطة نهاية الحسابات من واجهة برمجة التطبيقات FedCM قائمة بالحسابات التي تم تسجيل الدخول إليها في الوقت الحالي. يعرض المثال التالي ردًا بسيطًا. لمزيد من التفاصيل، يُرجى الرجوع إلى دليل تنفيذ موفِّر الهوية.
نقطة النهاية: كما هو محدّد في .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 يتضمّن المفتاح 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 للتصريح الانتقائي موقّعًا يتم عرضه بتنسيق JSON باستخدام المكتبات المناسبة لمنصتك. على سبيل المثال، يمكنك استخدام
@sd-jwt/core و
jose مع Node.
يجب أن يبدو تنسيق الحمولة الأولية على النحو التالي:
{
"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 بيانات من نقاط النهاية إلا إذا كان يعرف أنّ المستخدم مسجّل الدخول. تأكَّد من أنّ عملية تسجيل الدخول تضبط عنوان HTTP الذي يتضمّن
Set-Login: logged-inأو تستدعي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: يرسل الإصدار 154 من Chrome والإصدارات الأحدثSec-Fetch-Dest: email-verification(مع شرطة)، بينما أرسل الإصدار 153 من Chromeemailverification(بدون شرطة). يجب قبول كلتا القيمتين أثناء طرح التغيير. - عدم تطابق توقيع رسالة HTTP (
@authority) خلف خادم وكيل: عند التحقّق من توقيع RFC 9421، يعكس المكوّن@authorityالمضيف المتاح للجميع. إذا كان الخادم يقع خلف خادم وكيل عكسي أو موازن تحميل، أعِد إنشاء عنوان URL لتأكيد رقم الهاتف باستخدامX-Forwarded-Host(أو المصدر العام) بدلاً من اسم المضيف الداخلي، واحتسِبContent-Digestعلى وحدات البايت الخاصة بنص الطلب الأولي قبل تحليل JSON.
يرفض المتصفّح issuance_token الذي تم إرجاعه
- مطالبة
emailمعدَّلة أو أساسية: بدءًا من الإصدار 156 من Chrome، يتحقّق المتصفّح من أنّ مطالبة EVTemailتتطابق معemailالمطلوبة بايت ببايت. إذا كان الخلفية الأساسية تعمل على تسوية العنوان إلى تنسيق حساب أساسي (مثل عرضFirst.Last@example.comعند طلبfirst.LAST@example.com)، سيتجاهل Chrome الرمز المميز. مطابقة الطلب مع حساب المستخدم، ولكن عرض سلسلةemailنفسها التي تم تلقّيها في نص الطلب - عدم تضمين علامة المدّ الأخيرة (
~): حتى في حال عدم تضمين أي معلومات مفصح عنها، يجب أن يكونissuance_tokenصالحًا بتنسيق SD-JWT وينتهي بعلامة مدّ أخيرة (<Issuer-signed-JWT>~) ليتمكّن المتصفّح من إلحاق<KB-JWT>. - عدم تطابق
issأوcnf.jwk: تأكَّد من أنّ مطالبة EVTissهي مصدر HTTPS مطابق تمامًا (https://<issuer-domain>، بدون شرطة مائلة لاحقة) للبيانات الوصفية.well-known/email-verification، وأنّcnf.jwkيتضمّن المفتاح العام المؤقت للمتصفّح من العنوانSignature-Key.