Ga voor meer informatie naar de democode voor een nep-e-mailprovider en neem de stappen voor de uitgever door in de voorstellen voor de Email Verification API en het Email Verification Protocol.
Als uitgever hoeft u zich niet aan te melden voor de oorsprongsproefperiode en hoeft u geen token aan te leveren, omdat de site van de afhankelijke partij het browsergedrag triggert. Zorg dat uw eindpunten zijn ingesteld om op die verzoeken te reageren.
Detectie van uitgevers instellen
Als u wilt dat browsers uw verificatie-eindpunten automatisch kunnen vinden als er een e-mailadres van uw domein is geselecteerd, stelt u uw configuratie beschikbaar via DNS en een .well-known HTTP-eindpunt.
DNS-delegeringsrecord configureren
Stel een DNS TXT-record in voor uw e-maildomein dat de verificatiebevoegdheid delegeert aan uw uitgever-ID. Deze ID's kunnen hetzelfde domein gebruiken, afhankelijk van uw infrastructuur.
Opname-indeling: _email-verification.<email-domain>
Voorbeeld van een zonebestand:
_email-verification.example.com IN TXT "iss=accounts.issuer.example"
Een .well-known/email-verification-eindpunt hosten
Host een json-bestand met metadata in uw uitgevende domein onder het pad /.well-known/.
Dit bestand beschrijft je uitgiftecapaciteiten en de cryptografische ondertekeningsalgoritmen die je infrastructuur ondersteunt.
Eindpunt: https://<issuer-domain>/.well-known/email-verification
Voorbeeld van een reactie:
{
"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"]
}
Een .well-known/web-identity-eindpunt hosten
Je hebt misschien al een extra .well-known json-resource geïmplementeerd als onderdeel van de Federated Credentials (FedCM) API. Deze bron biedt links naar het eindpunt van uw account en de inlog-URL.
Eindpunt: https://<domain>/.well-known/web-identity
Voorbeeld van een reactie:
{
"accounts_endpoint": "https://accounts.issuer.example/accounts",
"login_url": "https://accounts.issuer.example/login"
}
Een eindpunt voor accounts gebruiken
Het eindpunt accounts van de FedCM API biedt op dit moment een lijst met ingelogde accounts. Het volgende voorbeeld toont een minimale reactie. Ga voor meer informatie naar de implementatiehandleiding voor identiteitsproviders.
Eindpunt: zoals aangegeven in .well-known/web-identity
Dit is een voorbeeldreactie:
{
"accounts": [
{
"id": "demo-example",
"name": "Demo User",
"email": "demo@example.com",
"given_name": "Demo"
}
]
}
Integreren met de Login Status API
De gebruiker moet een actieve sessie bij de provider hebben en u moet dat aan de browser doorgeven met de Login Status API.
Als een gebruiker in- of uitlogt, serveer je de bijbehorende HTTP-reactieheader:
Set-Login: logged-in
Set-Login: logged-out
U kunt de status ook updaten met JavaScript in de context van uw web-app:
navigator.login.setStatus("logged-in");
navigator.login.setStatus("logged-out");
Uitgifteverzoeken verwerken
Je issuance_endpoint krijgt een application/json POST-verzoek dat de email-sleutel en de headers HTTP Message Signatures bevat voor Signature, Signature-Input en Signature-Key.
Gebruik een bibliotheek die gestructureerde headers en HTTP-berichtondertekeningen voor je omgeving ondersteunt. In Node.js kunt u structured-headers en http-message-sig gebruiken.
Indeling van volledig verzoek:
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"}
Parse en valideer het verzoek:
- Sessie-verificatie: Valideer uw first-party sessiecookies die met het verzoek zijn gestuurd. De gebruiker moet zijn geverifieerd.
Sec-Fetch-Dest-kop: ingesteld opemail-verification.- HTTP-berichtondertekeningen: Verifieer de verzoekhandtekening met de tijdelijke openbare sleutel in
Signature-Keyen valideer deContent-Digest. - Payload: De json-body bevat de
email-tekenreeks die voor verificatie is aangevraagd.
Reactie op uitgifte
Nadat de sessie- en verzoektoken zijn gevalideerd, genereer je een ondertekende JWT voor selectieve bekendmaking (SD-JWT) die als json wordt geretourneerd met de juiste bibliotheken voor je platform. Voor Node kun je bijvoorbeeld @sd-jwt/core en jose gebruiken.
De indeling van de onbewerkte payload moet er ongeveer zo uitzien:
{
"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
}
Maak, onderteken en stuur de token terug:
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);
De resulterende reactiebody ziet er ongeveer zo uit:
{
"issuance_token": "tOkEn123tOkEn123tOkEn123...~"
}
Nadat je de token voor e-mailverificatie hebt gemaakt en ondertekend, geeft de browser deze door aan de verifiersite voor validatie.
Problemen oplossen
Als de browser geen contact opneemt met je eindpunten of uitgegeven tokens weigert, check je de volgende veelvoorkomende problemen:
De browser belt nooit accounts_endpoint of issuance_endpoint
- Inlogstatus niet ingesteld: Chrome vraagt je eindpunten alleen op als de browser weet dat de gebruiker is ingelogd. Zorg dat je inlogproces de
Set-Login: logged-inHTTP-header instelt ofnavigator.login.setStatus("logged-in")aanroept. - Sessiecookies geblokkeerd (
SameSite=None): De browser haalt jeaccounts_endpointenissuance_endpointop bij een vertrouwende partij op een andere site. Omdat dit cross-site verzoeken zijn, moet je sessiecookieSameSite=None; Securebevatten. EenSameSite=Lax-cookie werkt als je test met een same-site verifier, maar wordt weggelaten bij cross-site verzoeken. - Ontdekkings- of accountmismatch: Verifieer dat
_email-verification.<email-domain>één TXT-record retourneert (iss=<issuer-domain>, zonderhttps://), beide.well-known-eindpuntenContent-Type: application/jsonretourneren en deaccounts_endpoint-reactie een account bevat waarvan deemailovereenkomt met het ingevoerde adres.
Validatie van uitgifteverzoek mislukt
- Spelling van de header
Sec-Fetch-Dest: Chrome 154 en hoger stuurtSec-Fetch-Dest: email-verification(met een streepje), terwijl Chrome 153emailverificationstuurde (zonder streepje). Accepteer beide waarden tijdens de uitrol. - Niet-overeenkomende HTTP-berichtenhandtekening (
@authority) achter een proxy: Als je de RFC 9421-handtekening verifieert, weerspiegelt de component@authorityde openbare host. Als je server achter een omgekeerde proxy of load balancer staat, reconstrueer je de verificatie-URL metX-Forwarded-Host(of je openbare oorsprong) in plaats van de interne hostnaam en bereken jeContent-Digestover de onbewerkte bytes van de verzoekbody vóór het JSON-parsen.
De browser wijst de geretourneerde issuance_token af
- Aangepaste of genormaliseerde
email-claim: Vanaf Chrome 156 controleert de browser of deemail-claim van de EVT byte voor byte overeenkomt met de aangevraagdeemail. Als je backend het adres normaliseert naar een canoniek accountformaat (zoalsFirst.Last@example.comretourneren alsfirst.LAST@example.comis aangevraagd), laat Chrome het token vallen. Vergelijk het verzoek met het account van de gebruiker, maar retourneer de exacteemail-tekenreeks die in de verzoekbody is ontvangen. - Ontbrekend tilde-teken (
~) aan het einde: Zelfs als er geen kennisgevingen zijn, moet deissuance_tokeneen geldig SD-JWT zijn dat eindigt met een tilde-teken (<Issuer-signed-JWT>~), zodat de browser de<KB-JWT>kan toevoegen. - Niet-overeenkomende
issofcnf.jwk: Zorg dat de EVT-claimissde exacte HTTPS-oorsprong is (https://<issuer-domain>, geen slash aan het einde) die overeenkomt met je.well-known/email-verification-metadata en datcnf.jwkde tijdelijke openbare sleutel van de browser insluit vanuit deSignature-Key-header.