1. Introduction
Historically, verifying email addresses during account creation, sign-in, or account recovery has relied on manual, out-of-band mechanisms. A typical flow involves a website sending a one-time passcode (OTP) or a "magic link" to the user’s email address. The user must then navigate to their email inbox, retrieve the code or click the link, and return to the website to prove ownership.
This process introduces significant friction, impacting user experience and conversion rates. Furthermore, it is vulnerable to phishing attacks, where a malicious site can trick a user into providing the OTP.
The Email Verification Protocol (EVP) introduces a browser-mediated mechanism to verify email ownership cryptographically. By leveraging an active session between the user and their email provider (the issuer), the browser can request a cryptographically signed Email Verification Token (EVT) and present it to the website (the verifier).
This specification defines the HTML extensions used by verifiers to request EVTs, and the client-side browser behavior to coordinate with issuers to acquire tokens.
1.1. Protocol Overview
The protocol involves three main parties:
-
The Verifier: The website requesting email verification.
-
The User Agent (UA): The browser intermediating the request.
-
The Issuer: The email provider authoritative for the email address.
A typical flow consists of the following steps:
-
Login: The user logs in to their email provider. The provider updates the browser’s login status to logged-in (using the Login Status API).
-
Request: The verifier’s website includes a hidden input field with
autocomplete="email-verification-token"and anonceattribute. -
Discovery & Validation: When the user selects an email address (e.g., via autofill), the UA discovers the authoritative issuer via DNS. The UA checks if the user has an active session with that issuer.
-
Issuance: The UA requests an EVT from the issuer.
-
Binding & Presentation: The UA binds the verifier’s origin and nonce to the EVT, creating a Key-Bound JWT (KB-JWT), and fills it into the verifier’s input field before form submission.
-
Verification: The verifier verifies the EVT and KB-JWT to complete the verification.
1.2. Example
This section is non-normative.
Consider a verifier website https://rp.example that wants to verify a user’s email address during sign-up.
1.2.1. Verifier HTML Form
The verifier includes a standard email input and a hidden input for the Email Verification Token (EVT), with autocomplete="email-verification-token" and a unique nonce:
< form action = "/signup" method = "post" > < label for = "email" > Email address:</ label > < input type = "email" id = "email" name = "email" autocomplete = "email" > <!-- EVP hidden input --> < input type = "hidden" name = "evt" autocomplete = "email-verification-token" nonce = "xyz123456789" > < button type = "submit" > Sign Up</ button > </ form >
1.2.2. Browser Interaction
-
When the user focuses the
emailinput, the browser suggests autofill email addresses (e.g.,user@email.example). -
The user selects
user@email.example. -
The browser performs issuer discovery (finding
email.exampledelegates toissuer.example), validates the user’s session with the issuer, and shows a prompt: Verify your email? Do you want to share a verified token foruser@email.examplewithrp.example? [Allow] [Deny] -
If the user selects "Allow", the browser fetches the EVT from
issuer.example, binds it torp.exampleand the noncexyz123456789, and stores the bound token.
1.2.3. Form Submission
When the user submits the form, the browser automatically injects the bound token into the hidden input field. The verifier receives the following POST payload:
POST /signup HTTP / 1.1 Host : rp.example Content-Type : application/x-www-form-urlencoded = user%40email.example & evt = eyJhbGciOiJFZERTQSIsImtpZCI6IjIwMjQtMDgtMTkiLCJ0eXAiOiJldnQrand0In0...
The verifier then parses and verifies the evt token (verifying the signature, origin rp.example, nonce xyz123456789, and matching the email user@email.example) to complete the registration without needing to send a verification email.
2. HTML Extensions
This specification extends the HTML standard [HTML] by introducing a new autofill field name and extending the use of the nonce attribute.
2.1. The email-verification-token Autofill Value
The email-verification-token keyword is added to the list of autofill field names (specifically, detail tokens) defined in [HTML].
When an <input> element has its autocomplete attribute set to email-verification-token, it indicates that the User Agent SHOULD attempt to fill this field with a cryptographically bound Email Verification Token (EVT) upon form submission, provided the user has selected and verified an email address in the same form.
Typically, this keyword is used on <input type="hidden"> elements.
2.2. The nonce Attribute on input Elements
This specification extends the nonce attribute, originally defined for <script> and <style> elements in [HTML] (and used in Content Security Policy [CSP]), to <input> elements.
When present on an <input> element with autocomplete="email-verification-token", the nonce attribute contains a server-side generated, cryptographically strong random value (a cryptographic nonce).
This nonce is used to bind the resulting EVT to a specific form presentation, preventing replay attacks.
The value of the nonce attribute MUST be unique for each page rendering.
3. Browser Processing Model
3.1. Handling Autofill Selection
When the user selects an email address email from the user agent’s autofill suggestions for an <input> element emailInput within a <form> form:
-
Let evtInput be the first
<input>element in form that has anautocompleteattribute whose value isemail-verification-token. -
If no such evtInput exists, terminate these steps.
-
Let nonce be the value of the
nonceattribute of evtInput. -
If nonce is empty, terminate these steps.
-
Set form’s EVP state to:
-
email: email -
inputElement: emailInput -
token: null
-
-
In the background, perform the following steps:
-
Let issuer be the result of performing the issuer discovery steps defined in [EVP-Protocol] on email.
-
If issuer is null, terminate these steps.
-
Let accountMatch be the result of performing Accounts Validation on email with issuer.
-
If accountMatch is false, terminate these steps.
-
Show a user prompt asking for permission to verify the email address email with issuer.
-
If the user denies permission, terminate these steps.
-
Let evtResult be the result of performing EVT Issuance for email from issuer.
-
If evtResult is null, terminate these steps.
-
Let evt be evtResult[0].
-
Let keyPair be evtResult[1].
-
Let kbEvt be the result of performing the key-binding creation steps defined in [EVP-Protocol] on evt using the private key component of keyPair, with nonce and the origin of the document.
-
Let state be form’s EVP state.
-
If state is not null and state’s
emailis case-insensitively equal to email:-
Set state’s
tokento kbEvt.
-
-
3.2. Form Submission Integration
When a <form> form is submitted:
-
Let state be form’s EVP state.
-
If state is null or state’s
tokenis null, continue with standard form submission steps. -
Let currentEmail be the value of state’s
inputElement. -
If currentEmail is case-insensitively equal to state’s
email:-
Let evtInput be the first
<input>element in form that has anautocompleteattribute whose value isemail-verification-token. -
If evtInput exists:
-
Set the value of evtInput to state’s
token.
-
-
-
Continue with the standard form submission steps defined in [HTML].
3.3. Accounts Validation
Given an email address email and an issuer domain issuer, the user agent MUST perform the following steps to validate the accounts:
-
Check the login status of issuer using the Login Status API [login-status].
-
If the status is logged-out, return false.
-
Let wellKnown be the result of fetching the well-known file for issuer as defined in [fedcm].
-
If wellKnown is null, return false.
-
Let accountsEndpoint be the value of the
accounts_endpointmember of wellKnown. -
If accountsEndpoint is not present or is not a valid URL, return false.
-
Let accounts be the result of executing the fetch accounts algorithm defined in [fedcm] for issuer and accountsEndpoint.
-
If accounts is null or empty, return false.
-
For each account in accounts:
-
Let accountEmail be the value of the
emailmember of account. -
If accountEmail is case-insensitively equal to email, return true.
-
-
Return false.
3.4. EVT Issuance
Given an email address email and an issuer domain issuer, the user agent MUST perform the following steps to obtain an EVT:
-
Generate an ephemeral asymmetric key pair keyPair (using an algorithm supported by the issuer, as defined in the issuer’s metadata, defaulting to Ed25519 if not specified).
-
Construct a JSON Web Token [JWT] requestToken:
-
The header MUST contain:
-
alg: The signing algorithm (matching keyPair’s algorithm). -
jwk: The public key component of keyPair.
-
-
The payload MUST contain:
-
aud: The issuer’s identifier (domain). -
iat: The current time. -
email: The email address.
-
-
Sign requestToken using the private key component of keyPair.
-
-
Let issuanceUrl be the issuer’s token issuance endpoint (obtained from the issuer’s metadata).
-
Construct an HTTP POST request request to issuanceUrl:
-
Set the
Content-Typeheader toapplication/x-www-form-urlencoded. -
Set the
Sec-Fetch-Destheader toemail-verification. -
Include first-party cookies for issuer.
-
Set the request body to the URL-encoded representation of:
request_token= requestToken (serialized as a string).
-
-
Send request and wait for the response callResponse.
-
If callResponse’s status code is not 200 OK or its
Content-Typeis notapplication/json, return null. -
Parse callResponse’s body as JSON and let json be the result.
-
If json does not contain
issuance_token, return null. -
Let evt be json’s
issuance_token. -
Verify evt:
-
Verify evt is a valid SD-JWT [SD-JWT] signed by the issuer.
-
Verify the
cnfclaim in evt contains a public key that matches the public key of keyPair. -
Verify the
emailclaim in evt matches email.
-
-
If verification fails, return null.
-
Return a tuple containing (evt, keyPair).
4. Verifier Processing Model
When a verifier receives a submitted form containing an email address email and a bound token boundToken (from the input field with autocomplete="email-verification-token"):
-
Perform the token verification steps defined in [EVP-Protocol] on boundToken for the verifier’s origin and the expected nonce.
-
If the verification fails, fail verification.
-
Let verifiedEmail be the email address extracted from the verified token.
-
If verifiedEmail is not case-insensitively equal to email, fail verification.
-
Otherwise, verification succeeds.
5. Security and Privacy Considerations
5.1. Privacy
5.1.1. Blinding the Issuer
A key privacy goal of EVP is to prevent the issuer from learning which verifier the user is interacting with.The User Agent MUST ensure that the Well-Known fetch and the Accounts fetch do not leak the verifier’s origin.
The Issuance request, although credentialed, MUST NOT include the verifier’s origin in the request headers (e.g., Referer or Origin MUST be omitted).
5.1.2. Tracking Risks
Since the issuance request uses cookies, the issuer learns that the user is active. However, it only learns this when the user actively selects their email for verification, which is a user-initiated action.5.2. Security
5.2.1. Replay Attacks
The presentation token (EVT+KB) is bound to a specificnonce and audience (verifier origin) via the Key-Binding JWT.
Verifiers MUST verify:
-
The
audiencematches their origin. -
The
noncematches the one they generated for the form presentation. -
The
expclaim has not expired.
This prevents an attacker from intercepting an EVT+KB and replaying it to another site or in a different context.