Więcej informacji znajdziesz w przykładzie kodu demonstracyjnego dostawcy poczty e-mail oraz w propozycjach interfejsu Email Verification API i protokołu Email Verification.
Jako wydawca nie musisz rejestrować się w programie testów pochodzenia ani podawać tokena, ponieważ zachowanie przeglądarki jest wywoływane przez witrynę strony ufającej. Upewnij się, że punkty końcowe są skonfigurowane tak, aby odpowiadać na te żądania.
Konfigurowanie wykrywania wystawcy
Aby umożliwić przeglądarkom automatyczne wykrywanie punktów końcowych weryfikacji po wybraniu adresu e-mail należącego do Twojej domeny, udostępnij konfigurację za pomocą DNS i punktu końcowego HTTP .well-known.
Konfigurowanie rekordu delegowania DNS
Skonfiguruj rekord DNS typu TXT w domenie e-mail, który przekazuje uprawnienia do weryfikacji identyfikatorowi wystawcy. W zależności od infrastruktury identyfikatory te mogą używać tej samej domeny.
Format rekordu: _email-verification.<email-domain>
Przykładowy plik strefy:
_email-verification.example.com IN TXT "iss=accounts.issuer.example"
Hostowanie punktu końcowego .well-known/email-verification
Umieść plik JSON z metadanymi w domenie wydawcy w ścieżce /.well-known/.
Ten plik zawiera informacje o możliwościach wydawania i algorytmach podpisywania kryptograficznego obsługiwanych przez Twoją infrastrukturę.
Punkt końcowy: https://<issuer-domain>/.well-known/email-verification
Przykładowa odpowiedź:
{
"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"]
}
Hostowanie punktu końcowego .well-known/web-identity
Być może masz już wdrożony dodatkowy.well-known zasób JSON jako część interfejsu Federated Credentials (FedCM) API. Ten zasób zawiera linki do punktu końcowego kont i adresu URL logowania.
Punkt końcowy: https://<domain>/.well-known/web-identity
Przykładowa odpowiedź:
{
"accounts_endpoint": "https://accounts.issuer.example/accounts",
"login_url": "https://accounts.issuer.example/login"
}
Używanie punktu końcowego kont
Punkt końcowy kont z interfejsu FedCM API udostępnia listę zalogowanych kont w danym momencie. Poniższy przykład pokazuje minimalną odpowiedź. Więcej informacji znajdziesz w przewodniku po implementacji dostawcy tożsamości.
Punkt końcowy: zgodnie z informacjami w .well-known/web-identity
Oto przykładowa odpowiedź:
{
"accounts": [
{
"id": "demo-example",
"name": "Demo User",
"email": "demo@example.com",
"given_name": "Demo"
}
]
}
Integracja z interfejsem Login Status API
Użytkownik musi mieć aktywną sesję u dostawcy, a Ty musisz poinformować o tym przeglądarkę za pomocą interfejsu Login Status API.
Gdy użytkownik zaloguje się lub wyloguje, wyświetl odpowiedni nagłówek odpowiedzi HTTP:
Set-Login: logged-in
Set-Login: logged-out
Możesz też zaktualizować stan za pomocą JavaScriptu w kontekście aplikacji internetowej:
navigator.login.setStatus("logged-in");
navigator.login.setStatus("logged-out");
Obsługa próśb o wydanie
Twój issuance_endpoint otrzymuje żądanie application/json POST, które zawiera klucz email i nagłówki HTTP Message
Signatures dla Signature, Signature-Input i Signature-Key.
Użyj biblioteki, która obsługuje nagłówki strukturalne i podpisy wiadomości HTTP w Twoim środowisku. W Node.js możesz używać structured-headers i http-message-sig.
Format pełnego żądania:
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"}
Przeanalizuj i zweryfikuj żądanie:
- Uwierzytelnianie sesji: sprawdzanie poprawności własnych plików cookie sesji wysyłanych z żądaniem. Użytkownik musi być uwierzytelniony.
- Nagłówek
Sec-Fetch-Dest: ustaw naemail-verification. - Podpisy wiadomości HTTP: zweryfikuj podpis żądania za pomocą efemerycznego klucza publicznego w
Signature-Keyi sprawdźContent-Digest. - Ładunek: treść JSON zawiera ciąg
emailwymagany do weryfikacji.
Odpowiedź dotycząca wydania
Po pomyślnej weryfikacji sesji i tokena żądania wygeneruj podpisany token JWT z wybiórczym ujawnianiem (SD-JWT), który zostanie zwrócony w formacie JSON przy użyciu odpowiednich bibliotek dla Twojej platformy. Na przykład w przypadku Node możesz użyć @sd-jwt/core i jose.
Format nieprzetworzonego ładunku powinien wyglądać podobnie do tego:
{
"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
}
Utwórz, podpisz i zwróć token:
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);
Wynikowa treść odpowiedzi wygląda mniej więcej tak:
{
"issuance_token": "tOkEn123tOkEn123tOkEn123...~"
}
Po utworzeniu i podpisaniu tokena potwierdzania adresu e-mail przeglądarka przekazuje go do witryny weryfikującej w celu sprawdzenia.
Rozwiązywanie problemów
Jeśli przeglądarka nie kontaktuje się z punktami końcowymi lub odrzuca wydane tokeny, sprawdź, czy nie występują te typowe problemy:
Przeglądarka nigdy nie wywołuje funkcji accounts_endpoint ani issuance_endpoint
- Stan logowania nie jest ustawiony: Chrome wysyła zapytania do punktów końcowych tylko wtedy, gdy wie, że użytkownik jest zalogowany. Sprawdź, czy proces logowania ustawia nagłówek HTTP
Set-Login: logged-inlub wywołuje funkcjęnavigator.login.setStatus("logged-in"). - Pliki cookie sesji zablokowane (
SameSite=None): przeglądarka pobiera Twojeaccounts_endpointiissuance_endpointod podmiotu polegającego na tożsamości na innej stronie. Ponieważ są to żądania z innych witryn, plik cookie sesji musi zawieraćSameSite=None; Secure. Plik cookieSameSite=Laxdziała podczas testowania w weryfikatorze tej samej witryny, ale jest pomijany w przypadku żądań z innych witryn. - Wykrywanie lub niezgodność konta: sprawdź, czy
_email-verification.<email-domain>zwraca pojedynczy rekord TXT (iss=<issuer-domain>, bezhttps://), oba punkty końcowe.well-knownzwracająContent-Type: application/json, a odpowiedźaccounts_endpointzawiera konto, któregoemailpasuje do wpisanego adresu.
Nie udało się zweryfikować prośby o wydanie
- Pisownia nagłówka
Sec-Fetch-Dest: Chrome 154 i nowsze wysyłająSec-Fetch-Dest: email-verification(z myślnikiem), a Chrome 153 wysyłałemailverification(bez myślnika). Podczas wdrażania akceptuj obie wartości. - Niezgodność podpisu wiadomości HTTP (
@authority) za serwerem proxy: podczas weryfikacji podpisu RFC 9421 komponent@authorityodzwierciedla hosta publicznego. Jeśli serwer znajduje się za odwrotnym serwerem proxy lub systemem równoważenia obciążenia, zrekonstruuj URL do weryfikacji, używającX-Forwarded-Host(lub publicznego źródła), a nie wewnętrznej nazwy hosta, i obliczContent-Digestna podstawie surowych bajtów treści żądania przed analizą JSON.
Przeglądarka odrzuca zwrócony element issuance_token
- Zmodyfikowane lub znormalizowane roszczenie
email: od wersji Chrome 156 przeglądarka sprawdza, czy roszczenie EVTemailjest identyczne z żądanym roszczeniememail. Jeśli backend normalizuje adres do kanonicznego formatu konta (np. zwracaFirst.Last@example.com, gdy zażądanofirst.LAST@example.com), Chrome usuwa token. Dopasuj żądanie do konta użytkownika, ale zwróć dokładny ciąg znakówemailotrzymany w treści żądania. - Brak tyldy na końcu (
~): nawet w przypadku braku ujawnieńissuance_tokenmusi być prawidłowym tokenem SD-JWT zakończonym tyldą (<Issuer-signed-JWT>~), aby przeglądarka mogła dołączyć<KB-JWT>. - Niezgodność
isslubcnf.jwk: upewnij się, że roszczenie EVTissjest dokładnym źródłem HTTPS (https://<issuer-domain>, bez ukośnika na końcu) zgodnym z metadanymi.well-known/email-verification, acnf.jwkzawiera efemeryczny klucz publiczny przeglądarki z nagłówkaSignature-Key.