Weitere Informationen finden Sie im Democode für einen Mock-E‑Mail-Anbieter und in den Schritten für Aussteller in den Vorschlägen für die E‑Mail-Bestätigungs-API und das E‑Mail-Bestätigungsprotokoll.
Als Aussteller müssen Sie sich nicht für den Ursprungstest anmelden oder ein Token bereitstellen, da das Browserverhalten durch die Website der vertrauenden Partei ausgelöst wird. Achten Sie darauf, dass Ihre Endpunkte so konfiguriert sind, dass sie auf diese Anfragen reagieren.
Ausstellererkennung konfigurieren
Damit Browser Ihre Bestätigungsendpunkte automatisch erkennen können, wenn eine E-Mail-Adresse Ihrer Domain ausgewählt wird, müssen Sie Ihre Konfiguration über DNS und einen .well-known-HTTP-Endpunkt verfügbar machen.
DNS-Delegierungseintrag konfigurieren
Konfigurieren Sie einen DNS-TXT-Eintrag in Ihrer E-Mail-Domain, der die Bestätigungsautorität an Ihre Aussteller-ID delegiert. Diese Kennungen können je nach Infrastruktur dieselbe Domain verwenden.
Aufzeichnungsformat: _email-verification.<email-domain>
Beispiel für eine Zonendatei:
_email-verification.example.com IN TXT "iss=accounts.issuer.example"
.well-known/email-verification-Endpunkt hosten
Hosten Sie eine JSON-Metadatendatei in Ihrer Ausstellerdomain unter dem Pfad /.well-known/.
In dieser Datei werden Ihre Ausstellungsfunktionen und die kryptografischen Signaturalgorithmen beschrieben, die von Ihrer Infrastruktur unterstützt werden.
Endpunkt: https://<issuer-domain>/.well-known/email-verification
Beispielantwort:
{
"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-Endpunkt hosten
Möglicherweise haben Sie bereits eine zusätzliche .well-known-JSON-Ressource als Teil der FedCM API (Federated Credentials Management) implementiert. Diese Ressource enthält Links zu Ihrem Kontenendpunkt und Ihrer Anmelde-URL.
Endpunkt: https://<domain>/.well-known/web-identity
Beispielantwort:
{
"accounts_endpoint": "https://accounts.issuer.example/accounts",
"login_url": "https://accounts.issuer.example/login"
}
Kontenendpunkt verwenden
Der Kontenendpunkt der FedCM API bietet derzeit eine Liste der angemeldeten Konten. Das folgende Beispiel zeigt eine minimale Antwort. Weitere Informationen finden Sie im Implementierungsleitfaden für Identitätsanbieter.
Endpunkt: wie in .well-known/web-identity angegeben
Hier ist ein Beispiel für eine Antwort:
{
"accounts": [
{
"id": "demo-example",
"name": "Demo User",
"email": "demo@example.com",
"given_name": "Demo"
}
]
}
In die Login Status API einbinden
Der Nutzer muss eine aktive Sitzung beim Anbieter haben und Sie müssen dies dem Browser über die Login Status API signalisieren.
Wenn sich ein Nutzer erfolgreich an- oder abmeldet, müssen Sie den entsprechenden HTTP-Antwortheader bereitstellen:
Set-Login: logged-in
Set-Login: logged-out
Alternativ können Sie den Status auch mit JavaScript im Kontext Ihrer Webanwendung aktualisieren:
navigator.login.setStatus("logged-in");
navigator.login.setStatus("logged-out");
Ausstellungsanträge bearbeiten
Ihre issuance_endpoint empfängt eine application/json-POST-Anfrage, die den email-Schlüssel und die HTTP-Nachrichtensignaturen-Header für Signature, Signature-Input und Signature-Key enthält.
Verwenden Sie eine Bibliothek, die strukturierte Header und HTTP Message Signatures für Ihre Umgebung unterstützt. In Node.js können Sie structured-headers und http-message-sig verwenden.
Vollständiges Anfrageformat:
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"}
Anfrage parsen und validieren:
- Sitzungsauthentifizierung: Validieren Sie die mit der Anfrage gesendeten eigenen Sitzungscookies. Der Nutzer muss authentifiziert sein.
Sec-Fetch-Dest-Header: Legen Sieemail-verificationfest.- HTTP-Nachrichtensignaturen: Überprüfen Sie die Anfrage-Signatur mit dem temporären öffentlichen Schlüssel in
Signature-Keyund validieren SieContent-Digest. - Nutzlast: Der JSON-Textkörper enthält den String
email, der für die Bestätigung angefordert wurde.
Antwort auf die Ausstellung
Generieren Sie nach erfolgreicher Validierung des Sitzungs- und Anfrage-Tokens ein signiertes SD-JWT (Selective Disclosure JWT), das als JSON zurückgegeben wird. Verwenden Sie dazu die entsprechenden Bibliotheken für Ihre Plattform. Für Node können Sie beispielsweise @sd-jwt/core und jose verwenden.
Das Rohdaten-Nutzlast-Format sollte in etwa so aussehen:
{
"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
}
Token erstellen, signieren und zurückgeben:
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);
Der resultierende Antworttext sieht etwa so aus:
{
"issuance_token": "tOkEn123tOkEn123tOkEn123...~"
}
Nachdem Sie das E-Mail-Bestätigungstoken erstellt und signiert haben, übergibt der Browser es zur Validierung an die Verifizierungswebsite.
Fehlerbehebung
Wenn der Browser Ihre Endpunkte nicht kontaktiert oder ausgestellte Tokens ablehnt, prüfen Sie, ob eines der folgenden häufigen Probleme vorliegt:
Browser ruft accounts_endpoint oder issuance_endpoint nie auf
- Anmeldestatus nicht festgelegt: Chrome fragt Ihre Endpunkte nur ab, wenn der Nutzer angemeldet ist. Achten Sie darauf, dass in Ihrem Anmeldevorgang der
Set-Login: logged-in-HTTP-Header festgelegt odernavigator.login.setStatus("logged-in")aufgerufen wird. - Sitzungscookies blockiert (
SameSite=None): Der Browser ruft Ihreaccounts_endpointundissuance_endpointvon einer vertrauenden Partei auf einer anderen Website ab. Da es sich um websiteübergreifende Anfragen handelt, muss Ihr SitzungscookieSameSite=None; Secureenthalten. EinSameSite=Lax-Cookie funktioniert beim Testen mit einem Same-Site-Prüftool, wird aber bei websiteübergreifenden Anfragen ausgelassen. - Erkennung oder Konto stimmt nicht überein: Prüfen Sie, ob
_email-verification.<email-domain>einen einzelnen TXT-Eintrag (iss=<issuer-domain>, ohnehttps://) zurückgibt, beide.well-known-EndpunkteContent-Type: application/jsonzurückgeben und dieaccounts_endpoint-Antwort ein Konto mit eineremailenthält, die mit der eingegebenen Adresse übereinstimmt.
Validierung der Ausstellungsanfrage fehlgeschlagen
Sec-Fetch-Dest-Header-Schreibweise: In Chrome 154 und höher wirdSec-Fetch-Dest: email-verification(mit Bindestrich) gesendet, in Chrome 153 wurdeemailverification(ohne Bindestrich) gesendet. Akzeptieren Sie beide Werte während des Rollouts.- Fehler bei der HTTP-Nachrichtensignatur (
@authority) hinter einem Proxy: Beim Prüfen der RFC 9421-Signatur spiegelt die@authority-Komponente den öffentlich zugänglichen Host wider. Wenn sich Ihr Server hinter einem Reverseproxy oder Load Balancer befindet, rekonstruieren Sie die Bestätigungs-URL mitX-Forwarded-Host(oder Ihrem öffentlichen Ursprung) anstelle des internen Hostnamens und berechnen SieContent-Digestanhand der Rohbytes des Anfragetexts vor der JSON-Analyse.
Der Browser lehnt die zurückgegebene issuance_token ab.
- Geänderter oder kanonisierter
email-Anspruch: Ab Chrome 156 prüft der Browser, ob der EVT-email-Anspruch bytegenau mit dem angefordertenemailübereinstimmt. Wenn Ihr Backend die Adresse in ein kanonisches Kontoformat normalisiert (z. B.First.Last@example.comzurückgibt, wennfirst.LAST@example.comangefordert wurde), wird das Token von Chrome verworfen. Gleiche die Anfrage mit dem Konto des Nutzers ab, gib aber den genauenemail-String zurück, der im Anfragetext empfangen wurde. - Fehlende abschließende Tilde (
~): Auch wenn keine Offenlegungen vorhanden sind, mussissuance_tokenein gültiges SD-JWT sein, das mit einer abschließenden Tilde (<Issuer-signed-JWT>~) endet, damit der Browser<KB-JWT>anhängen kann. - Nicht übereinstimmende
iss- odercnf.jwk-Werte: Achten Sie darauf, dass der EVT-iss-Anspruch genau dem HTTPS-Ursprung (https://<issuer-domain>, kein nachgestellter Schrägstrich) entspricht, der mit Ihren.well-known/email-verification-Metadaten übereinstimmt, und dasscnf.jwkden temporären öffentlichen Schlüssel des Browsers aus demSignature-Key-Header einbettet.