TBD D. Hardt Internet-Draft Hellō Intended status: Standards Track S. Goto Expires: 26 February 2027 Google 25 August 2026 Email Verification Protocol draft-hardt-email-verification-02 Abstract This document defines the Email Verification Protocol (EVP), the HTTP-level protocol by which a browser obtains a signed email verification token from an issuer and presents it to a relying party (RP). The protocol enables web applications to verify that a user controls an email address without sending a verification email. It uses a three-party model in which the browser intermediates between the RP and the issuer, hiding the RP's identity from the issuer and supporting private, per-RP email addresses to prevent cross-site correlation. This document covers issuer discovery, the token issuance request, the Email Verification Token (EVT) and Key Binding JWT (KB-JWT) formats, and token verification. The browser API — how the user selects an email address and how the token is delivered to the RP — is defined in the companion W3C Email Verification API ([EVP-Browser]). Discussion Venues _Note: This section is to be removed before publishing as an RFC._ Source for this draft and an issue tracker can be found at https://github.com/dickhardt/email-verification (https://github.com/dickhardt/email-verification). Discussion of this document takes place on the evp-discuss mailing list. Subscribe at https://mailman3.ietf.org/mailman3/lists/evp- discuss.ietf.org/ (https://mailman3.ietf.org/mailman3/lists/evp- discuss.ietf.org/) and view the archive at https://mailarchive.ietf.org/arch/browse/evp-discuss/ (https://mailarchive.ietf.org/arch/browse/evp-discuss/). The browser API aspects are being developed separately by the W3C ([EVP-Browser]). Hardt & Goto Expires 26 February 2027 [Page 1] Internet-Draft EVP August 2026 Status of This Memo This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79. Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet- Drafts is at https://datatracker.ietf.org/drafts/current/. Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress." This Internet-Draft will expire on 26 February 2027. Copyright Notice Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved. This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/ license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License. Table of Contents 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 4 1.1. Verification Friction . . . . . . . . . . . . . . . . . . 4 1.2. Privacy Leakage . . . . . . . . . . . . . . . . . . . . . 5 1.3. Friction Solution . . . . . . . . . . . . . . . . . . . . 5 1.4. Privacy Solution . . . . . . . . . . . . . . . . . . . . 5 2. Protocol Flow . . . . . . . . . . . . . . . . . . . . . . . . 6 2.1. Email Discovery . . . . . . . . . . . . . . . . . . . . . 7 2.2. Session Binding . . . . . . . . . . . . . . . . . . . . . 7 2.3. Email Acquisition . . . . . . . . . . . . . . . . . . . . 7 2.4. Token Request . . . . . . . . . . . . . . . . . . . . . . 7 2.5. EVT Issuance . . . . . . . . . . . . . . . . . . . . . . 8 2.6. KB Creation . . . . . . . . . . . . . . . . . . . . . . . 9 2.7. Token Presentation . . . . . . . . . . . . . . . . . . . 9 2.8. Token Verification . . . . . . . . . . . . . . . . . . . 9 3. Issuer Discovery . . . . . . . . . . . . . . . . . . . . . . 10 Hardt & Goto Expires 26 February 2027 [Page 2] Internet-Draft EVP August 2026 3.1. DNS Delegation . . . . . . . . . . . . . . . . . . . . . 10 3.2. Issuer Identifier . . . . . . . . . . . . . . . . . . . . 11 3.3. Issuer Metadata . . . . . . . . . . . . . . . . . . . . . 11 4. HTTP Message Signatures . . . . . . . . . . . . . . . . . . . 12 4.1. HTTP Request Signing . . . . . . . . . . . . . . . . . . 12 4.1.1. Request Body . . . . . . . . . . . . . . . . . . . . 13 4.1.2. Content-Digest Header . . . . . . . . . . . . . . . . 13 4.1.3. Signature-Key Header . . . . . . . . . . . . . . . . 13 4.1.4. Signature-Input Header . . . . . . . . . . . . . . . 14 4.1.5. Example Signed Request . . . . . . . . . . . . . . . 14 4.2. HTTP Request Verification . . . . . . . . . . . . . . . . 14 4.3. Valid Email Address . . . . . . . . . . . . . . . . . . . 16 5. Email Verification Token (EVT) . . . . . . . . . . . . . . . 16 5.1. EVT Structure . . . . . . . . . . . . . . . . . . . . . . 16 5.1.1. Header . . . . . . . . . . . . . . . . . . . . . . . 16 5.1.2. Payload . . . . . . . . . . . . . . . . . . . . . . . 17 5.1.3. Format . . . . . . . . . . . . . . . . . . . . . . . 17 5.2. EVT Creation . . . . . . . . . . . . . . . . . . . . . . 18 5.3. EVT Verification . . . . . . . . . . . . . . . . . . . . 18 6. Key Binding (EVT+KB) . . . . . . . . . . . . . . . . . . . . 19 6.1. KB-JWT Structure . . . . . . . . . . . . . . . . . . . . 19 6.1.1. Header . . . . . . . . . . . . . . . . . . . . . . . 19 6.1.2. Payload . . . . . . . . . . . . . . . . . . . . . . . 19 6.2. EVT+KB Format . . . . . . . . . . . . . . . . . . . . . . 19 6.3. SD-JWT Compatibility . . . . . . . . . . . . . . . . . . 20 6.4. KB-JWT Creation . . . . . . . . . . . . . . . . . . . . . 20 6.5. KB-JWT Verification . . . . . . . . . . . . . . . . . . . 20 7. Private Email Addresses . . . . . . . . . . . . . . . . . . . 21 7.1. Request Parameters . . . . . . . . . . . . . . . . . . . 21 7.2. Example Requests . . . . . . . . . . . . . . . . . . . . 21 7.3. Requirements . . . . . . . . . . . . . . . . . . . . . . 22 7.4. Issuer Flexibility . . . . . . . . . . . . . . . . . . . 22 7.5. Example EVT Payload . . . . . . . . . . . . . . . . . . . 22 8. Error Responses . . . . . . . . . . . . . . . . . . . . . . . 23 8.1. Invalid Content-Type Header . . . . . . . . . . . . . . . 23 8.2. Invalid Sec-Fetch-Dest Header . . . . . . . . . . . . . . 23 8.3. Invalid or Missing HTTP Message Signature . . . . . . . . 24 8.4. Authentication Required . . . . . . . . . . . . . . . . . 25 8.5. Invalid Parameters . . . . . . . . . . . . . . . . . . . 25 8.6. Private Email Not Supported . . . . . . . . . . . . . . . 25 8.7. Invalid Directed Email . . . . . . . . . . . . . . . . . 25 8.8. Server Errors . . . . . . . . . . . . . . . . . . . . . . 26 9. Privacy Considerations . . . . . . . . . . . . . . . . . . . 26 9.1. Reduced Friction Tradeoff . . . . . . . . . . . . . . . . 26 9.2. Timing Correlation by Email Providers . . . . . . . . . . 26 9.3. RP Correlation via Email Addresses . . . . . . . . . . . 27 9.4. Issuer Knowledge . . . . . . . . . . . . . . . . . . . . 27 9.5. RP Knowledge . . . . . . . . . . . . . . . . . . . . . . 27 Hardt & Goto Expires 26 February 2027 [Page 3] Internet-Draft EVP August 2026 9.6. Browser Storage . . . . . . . . . . . . . . . . . . . . . 27 10. Security Considerations . . . . . . . . . . . . . . . . . . . 27 10.1. HTTP Message Signature Security . . . . . . . . . . . . 28 10.2. Signature-Key hwk Scheme . . . . . . . . . . . . . . . . 28 10.3. Fully-Specified Algorithms . . . . . . . . . . . . . . . 29 10.4. DNS Delegation . . . . . . . . . . . . . . . . . . . . . 29 10.5. Email Existence Probing . . . . . . . . . . . . . . . . 30 10.5.1. Uniform Error Responses . . . . . . . . . . . . . . 30 10.5.2. Timing Attack Mitigations . . . . . . . . . . . . . 31 10.5.3. Additional Mitigations . . . . . . . . . . . . . . . 31 11. Design Rationale . . . . . . . . . . . . . . . . . . . . . . 31 11.1. Why Not Solve Email Like SMS OTP? . . . . . . . . . . . 32 11.2. Why the Three-Party Model? . . . . . . . . . . . . . . . 32 11.3. Why SD-JWT? . . . . . . . . . . . . . . . . . . . . . . 33 11.4. Why DNS Delegation? . . . . . . . . . . . . . . . . . . 33 11.5. Why JWKS Over DKIM Keys? . . . . . . . . . . . . . . . . 34 11.6. Why HTTP Message Signatures Rather Than Request JWT? . . 34 12. Implementation Status . . . . . . . . . . . . . . . . . . . . 34 13. Document History . . . . . . . . . . . . . . . . . . . . . . 35 14. References . . . . . . . . . . . . . . . . . . . . . . . . . 38 14.1. Normative References . . . . . . . . . . . . . . . . . . 38 14.2. Informative References . . . . . . . . . . . . . . . . . 39 Appendix A. Acknowledgments . . . . . . . . . . . . . . . . . . 40 Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 40 1. Introduction Web applications verify email addresses to send emails to users (transactional notifications, marketing, password resets) and to identify users (as a stable identifier for account creation and authentication). The standard verification method—sending a one-time code via email—has two problems: verification friction and privacy leakage. 1.1. Verification Friction The email one-time code flow requires the user to switch to their email client, wait for the message to arrive, find it (possibly in spam), read the code, return to the application, and enter it. Many users abandon this process before completing it. Some approaches to reduce this friction: * *Social login*: When a user has an account with Google, Apple, or another identity provider, the application can obtain a verified email without sending a verification message. However, this requires the user to have and use a social account, and requires developers to integrate with each provider separately. Hardt & Goto Expires 26 February 2027 [Page 4] Internet-Draft EVP August 2026 * *Magic links*: Instead of a code, the verification email contains a link the user clicks to verify. This eliminates copying and pasting the code, but still requires switching to the email client, waiting for delivery, and finding the email. 1.2. Privacy Leakage Email verification creates two privacy problems: 1. *RP-to-RP correlation*: When a user provides their real email address to multiple relying parties (RPs), those RPs can correlate the user across sites by comparing email addresses. 2. *User-RP visibility*: The email provider learns which RPs the user visits and when. With email OTP, the provider sees verification emails from the sender and the delivery timing for every verification. With social login, the identity provider sees every RP request. Reducing friction in email verification accelerates both privacy problems — users verify to more sites, increasing correlation potential and provider visibility. 1.3. Friction Solution The Email Verification Protocol (EVP) enables a web application to obtain a verified email address *without sending an email* and *without the user leaving the web page*. The browser intermediates between the RP and an issuer, obtaining a signed token that contains an email address for the user that the RP can verify. This eliminates the email delivery step entirely. *Note on deliverability*: Like social login, this protocol verifies that the user controls an email address — it does not verify that the email address can receive mail. 1.4. Privacy Solution EVP addresses both privacy problems: *Three-party model*: The browser intermediates between the RP and issuer, ensuring the issuer never learns which RP requested verification. See Protocol Flow (#protocol-flow) for details. *Private email addresses*: The browser can request a private email address instead of the user's actual email. Private addresses that differ per RP cannot be correlated across sites. See Private Email Addresses (#private-email) for details. Hardt & Goto Expires 26 February 2027 [Page 5] Internet-Draft EVP August 2026 2. Protocol Flow This document specifies the IETF protocol aspects of email verification: the HTTP-level interactions between the browser, issuer, and the application, aka relying party (RP). How the browser obtains the email address from the user (browser APIs, user interface elements, etc.) and how the browser communicates with the RP is being defined by the W3C ([EVP-Browser]). * *Issuer*: The service that verifies the user controls an email address. See Issuer Discovery (#issuer-discovery) for how email domains delegate to issuers. * *Three-party model*: The protocol uses a three-party model where the browser intermediates between the RP and issuer. The issuer issues a email verification token (EVT) to the browser containing the email address and the browser's key material—but not the RP identity. The browser then creates a key binding token (KB-JWT) that ties the EVT to a specific RP. The combined token (EVT+KB) is what the RP receives. This separation hides the RP from the issuer during verification. The following diagram illustrates the protocol flow between the RP Server, Browser, and Issuer: Step RP Server Browser Issuer | | | 2.1 Email Discovery | |<- discover accts ->| | | (push or pull) | | | | 2.2 Session Binding |--- nonce ->| | | | | 2.3 Email Acquisition | [obtain email from user] | | | | 2.4 Token Request | |-- POST /issuance ->| | | (email, ...) | | | | 2.5 EVT Creation | | [create EVT] | | | 2.6 Token Issuance | |<------ EVT --------| | | | 2.7 KB Creation | [create KB-JWT] | | | | 2.8 Token Presentation |<-- EVT+KB -| | | | | 2.9 Token Verification [verify EVT+KB] | | | | | Hardt & Goto Expires 26 February 2027 [Page 6] Internet-Draft EVP August 2026 2.1. Email Discovery How the browser assembles candidate addresses — from its own store, from account information an issuer pushes or the browser pulls via FedCM ([LightweightFedCM]), or from the user typing one — is defined by the W3C Email Verification API ([EVP-Browser]) and is out of scope for this document. The browser MUST NOT offer an address that is not a valid email address (see Valid Email Address (#valid-email- address)). Once the user selects an address, the browser performs Issuer Discovery (#issuer-discovery) on the address's domain; the DNS delegation alone determines the issuer, whatever the source of the address. From selection onward the address is never altered: the browser sends it byte-for-byte in the token request, and an issuer that verifies it — by its own processing rules — returns it byte-for- byte in the email claim. Every comparison of email addresses in this protocol is byte-for-byte. 2.2. Session Binding The RP Server generates a cryptographically random nonce with at least 128 bits of entropy and binds it to a session it has with the browser. The nonce MUST be unique per verification request and SHOULD be valid for a limited time window. How the RP Server provides the nonce to the browser is being defined by the W3C ([EVP-Browser]). 2.3. Email Acquisition The browser obtains an email address from the user. This mechanism is being defined by the W3C ([EVP-Browser]). 2.4. Token Request Once the browser has the email address and nonce: 1. The browser performs Issuer Discovery (#issuer-discovery) for the email address to obtain the issuer's metadata, including the issuance_endpoint. 2. The browser generates a fresh private/public key pair. The browser SHOULD select an algorithm from the issuer's signing_alg_values_supported array, or use Ed25519 if not present. Hardt & Goto Expires 26 February 2027 [Page 7] Internet-Draft EVP August 2026 3. The browser creates a signed request per HTTP Message Signatures (#http-signatures) and POSTs to the issuance_endpoint, including the issuer's cookies. The request body is a JSON object with the following parameters: * email (REQUIRED): The email address to verify, byte-for-byte as the user selected it * See Private Email Addresses (#private-email) for parameters to request private email addresses POST /email-verification/issuance HTTP/1.1 Host: accounts.issuer.example Cookie: session=... Content-Type: application/json Sec-Fetch-Dest: email-verification Content-Digest: \ sha-256=:p8W2nSiyrdmtuSb49YHusp+pXM3er/ZiwRThsUgvua8=: Signature-Input: sig=("@method" "@authority" "@path" \ "content-digest" "signature-key");created=1692345600 Signature: sig=:MEQCIHd8Y8qYKm5e3dV8y....: Signature-Key: sig=hwk;kty="OKP";crv="Ed25519"; \ x="JrQLj5P_89iXES9-vFgrIy29clF9CC_oPPsw3c5D0bs";alg="Ed25519" {"email":"user@example.com"} | Note: The W3C Email Verification API ([EVP-Browser]) currently | specifies a different request format: the browser constructs a | signed JWT (request_token) with the public key in the header (alg, | jwk) and aud, iat, and email in the payload, sent with Content- | Type: application/x-www-form-urlencoded. Existing deployments use | this format; it is considered deprecated by this specification in | favor of HTTP Message Signatures. 2.5. EVT Issuance On receipt of a token request: 1. The issuer verifies the request per Request Verification (#request-verification). 2. The issuer checks if the cookies represent a logged-in user who controls the requested email address. If they do not, the issuer returns an authentication_required error (see Authentication Required (#authentication-required)). Hardt & Goto Expires 26 February 2027 [Page 8] Internet-Draft EVP August 2026 3. If authentication succeeds, the issuer creates an EVT per EVT Creation (#evt-creation) and returns it as the value of issuance_token in an application/json response. The issuer MAY include Set-Cookie headers to establish or update session state: HTTP/1.1 200 OK Content-Type: application/json Set-Cookie: session=...; Secure; HttpOnly; SameSite=None {"issuance_token":"eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiMjAyNC0wOC0xOSIsInR5cCI6ImV2dCtqd3QifQ...~"} The browser MUST process any Set-Cookie headers in the response. 2.6. KB Creation On receiving the issuance_token: 1. The browser verifies the EVT per EVT Verification (#evt- verification), additionally confirming: * The email claim is byte-for-byte identical to the email address being verified * The cnf.jwk claim matches the public key the browser generated 2. The browser creates a KB-JWT per KB-JWT Creation (#kb-creation- detail), binding the EVT to the RP's origin and session nonce. 3. The browser concatenates the EVT and KB-JWT to form the EVT+KB. Example EVT+KB (line breaks for display): eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiMjAyNC0wOC0xOSIsInR5cCI6ImV2dCtqd3QifQ. eyJpc3MiOiJodHRwczovL2lzc3Vlci5leGFtcGxlIiwiaWF0IjoxNzI0MDgzMjAw...}. signature~ eyJhbGciOiJFZDI1NTE5IiwidHlwIjoia2Irand0In0. eyJhdWQiOiJodHRwczovL3JwLmV4YW1wbGUiLCJub25jZSI6IjI1OWM1ZWFlLTQ4...}. signature 2.7. Token Presentation The browser provides the EVT+KB to the RP. This mechanism is being defined by the W3C ([EVP-Browser]). 2.8. Token Verification The RP receives the EVT+KB and verifies it by: 1. Verifying the KB-JWT per KB-JWT Verification (#kb-verification) Hardt & Goto Expires 26 February 2027 [Page 9] Internet-Draft EVP August 2026 2. Verifying the EVT per EVT Verification (#evt-verification) 3. Verifying the KB-JWT signature using the public key from the EVT's cnf.jwk claim If all verification steps pass, the RP has successfully verified that the user controls the email address in the email claim. 3. Issuer Discovery Both the browser and the RP need to discover information about the issuer for a given email address. This section describes the discovery process. | Note: The W3C Email Verification API ([EVP-Browser]) uses FedCM's | well-known configuration (/.well-known/fedcm.json) for issuer | discovery. This specification uses /.well-known/email- | verification. These two specifications will align on a single | well-known file. 3.1. DNS Delegation The email domain delegates email verification to an issuer via a DNS TXT record. Given an email address, parse the email domain (EMAIL_DOMAIN) and look up the `TXT` record for `_email- verification.EMAIL_DOMAIN. The contents of the record MUST start withiss=followed by the issuer host name. There MUST be only oneTXTrecord for_email-verification.$EMAIL_DOMAIN`. Example record: _email-verification.email-domain.example TXT iss=issuer.example This record states that email-domain.example has delegated email verification to the issuer at the host issuer.example. If the email domain and the issuer are the same domain, then the record would be: _email-verification.issuer.example TXT iss=issuer.example | Access to DNS records and email is often independent of website | deployments. This provides assurance that an issuer is truly | authorized as an insider with only access to websites on | issuer.example could not setup an issuer that would grant them | verified emails for any email at issuer.example. Hardt & Goto Expires 26 February 2027 [Page 10] Internet-Draft EVP August 2026 3.2. Issuer Identifier The record carries a host name. The *issuer identifier* is the HTTPS origin of that host: the string https:// followed by the host, with no port, no path, and no trailing slash. For the record above, the issuer identifier is https://issuer.example. The identifier is an origin rather than a bare host so that it names the same thing as the iss claim of [OpenID.Core] and the issuer of [RFC8414], and so that it is directly comparable to the values those specifications define. A bare host would have to be widened to an origin at every comparison, which is where mismatches arise. Every use of the issuer identity in this specification — the iss claim of the EVT, the issuer member of the metadata document, and the base of the well-known URL — is this origin. Comparisons between them are byte-for-byte on the derived string; no scheme defaulting, port normalization, or trailing-slash tolerance is applied. 3.3. Issuer Metadata Once the issuer identifier is known, fetch the metadata document from $ISSUER/.well-known/email-verification, where $ISSUER is the issuer identifier defined above. For https://issuer.example this is https://issuer.example/.well-known/email-verification. The metadata document is JSON containing the following properties: * _issuer_ - the issuer identifier. The value MUST be identical to the issuer identifier the document was fetched under, and a party that fetches the document MUST reject it if it is not. This is the check of [RFC8414], Section 3.3, and it prevents a document served at one identity from claiming another. * _issuance_endpoint_ - the API endpoint the browser calls to obtain an EVT * _jwks_uri_ - the URL where the issuer provides its public keys to verify the EVT Hardt & Goto Expires 26 February 2027 [Page 11] Internet-Draft EVP August 2026 * _signing_alg_values_supported_ - OPTIONAL. JSON array containing a list of the signing algorithms (alg values) supported by the issuer for both HTTP Message Signatures and issued EVTs. Algorithm identifiers MUST be from the IANA "JSON Web Signature and Encryption Algorithms" registry, and MUST be fully specified per [RFC9864]: the polymorphic EdDSA identifier MUST NOT be used, and Ed25519 or Ed448 used instead. none and the symmetric MAC identifiers MUST NOT be used. For the HTTP Message Signature case these are the algorithms an issuer would state in Accept- Signature-Alg ([I-D.hardt-httpbis-signature-key]), and that document's requirements on the conveyed key apply. If omitted, Ed25519 is the default. Ed25519 SHOULD be included in the supported algorithms list. * _private_email_supported_ - OPTIONAL. Boolean indicating whether the issuer supports generating private email addresses. Defaults to false. Following is an example .well-known/email-verification file: { "issuer": "https://issuer.example", "issuance_endpoint": "https://accounts.issuer.example/email-verification/issuance", "jwks_uri": "https://accounts.issuer.example/email-verification/jwks", "signing_alg_values_supported": ["Ed25519", "ES256"], "private_email_supported": true } 4. HTTP Message Signatures This section defines how HTTP Message Signatures ([RFC9421]) are used in token requests. The browser signs requests to prove possession of a key pair, and the issuer verifies these signatures. The browser's public key is conveyed with the hwk scheme of [I-D.hardt-httpbis-signature-key]; that document's requirements apply in full, and this section states how they are met here. 4.1. HTTP Request Signing The browser creates a signed request by: 1. Creating a JSON request body with the email address and optional parameters 2. Creating the Content-Digest header ([RFC9530]) over the request body 3. Creating the Signature-Key header using the hwk scheme ([I-D.hardt-httpbis-signature-key]) with the browser's public key 4. Creating the Signature-Input header specifying the covered components Hardt & Goto Expires 26 February 2027 [Page 12] Internet-Draft EVP August 2026 5. Computing the signature base per [RFC9421] Section 2.5 and signing with the browser's private key 6. Creating the Signature header with the base64-encoded signature 4.1.1. Request Body The request body is a JSON object with the following fields: * email (REQUIRED): The email address to verify, byte-for-byte as the user selected it * private_email (OPTIONAL): Request a new private email address. See Private Email Addresses (#private-email). * directed_email (OPTIONAL): A previously issued private email address to reuse. See Private Email Addresses (#private-email). Example: { "email": "user@example.com" } 4.1.2. Content-Digest Header The browser MUST include a Content-Digest header ([RFC9530]) carrying a digest of the request body. The sha-256 algorithm MUST be supported and SHOULD be used. Content-Digest: \ sha-256=:p8W2nSiyrdmtuSb49YHusp+pXM3er/ZiwRThsUgvua8=: The email address being verified is carried in the request body, not in any covered component of the URL. Without a digest of that body among the covered components, the signature says nothing about which address was requested, and an intermediary or a party that captures the request can substitute a different one within the created window while the signature still verifies. Covering content-digest binds the address to the signature, and so to the key that appears in the EVT's cnf claim. 4.1.3. Signature-Key Header The Signature-Key header uses the hwk scheme to convey the browser's public key. The scheme is defined by [I-D.hardt-httpbis-signature-key], whose parameter and algorithm requirements apply unchanged; the key's alg is the fully-specified identifier the browser selected per Token Request (#token-request). Hardt & Goto Expires 26 February 2027 [Page 13] Internet-Draft EVP August 2026 Signature-Key: sig=hwk;kty="OKP";crv="Ed25519"; \ x="JrQLj5P_89iXES9-vFgrIy29clF9CC_oPPsw3c5D0bs";alg="Ed25519" 4.1.4. Signature-Input Header The covered components MUST include @method, @authority, @path, content-digest, and signature-key. The created parameter MUST be included. Covering the cookie component is RECOMMENDED when the Cookie header is available to the signer at signing time; the component MUST be omitted when the header is not present (per [RFC9421] Section 2.5). An issuer MUST NOT reject a request solely because cookie is not covered. See HTTP Message Signature Security (#http-message- signature-security) for what coverage provides. Signature-Input: sig=("@method" "@authority" "@path" \ "content-digest" "signature-key");created=1692345600 4.1.5. Example Signed Request POST /email-verification/issuance HTTP/1.1 Host: accounts.issuer.example Cookie: session=... Content-Type: application/json Sec-Fetch-Dest: email-verification Content-Digest: \ sha-256=:p8W2nSiyrdmtuSb49YHusp+pXM3er/ZiwRThsUgvua8=: Signature-Input: sig=("@method" "@authority" "@path" \ "content-digest" "signature-key");created=1692345600 Signature: sig=:MEQCIHd8Y8qYKm5e3dV8y....: Signature-Key: sig=hwk;kty="OKP";crv="Ed25519"; \ x="JrQLj5P_89iXES9-vFgrIy29clF9CC_oPPsw3c5D0bs";alg="Ed25519" {"email":"user@example.com"} 4.2. HTTP Request Verification The issuer MUST verify the request headers: * Content-Type is application/json * Sec-Fetch-Dest is email-verification * Content-Digest is present and names a digest algorithm the issuer supports * Signature-Input is present * Signature is present * Signature-Key is present with sig=hwk scheme Hardt & Goto Expires 26 February 2027 [Page 14] Internet-Draft EVP August 2026 The issuer MUST verify the HTTP Message Signature per [RFC9421] and [I-D.hardt-httpbis-signature-key]. Resolving the key from the Signature-Key header, determining the signature algorithm, validating the key, and reporting failure through the Signature-Error header are defined by [I-D.hardt-httpbis-signature-key] and are not restated here. This specification profiles that processing as follows: 1. The hwk scheme is the only scheme this specification defines a use for. An issuer need implement no other, and rejects a request presenting one with unsupported_scheme. 2. The signature MUST cover at minimum @method, @authority, @path, content-digest, and signature-key. The issuer MUST reject a request whose signature does not. The cookie component is optional; an issuer MUST NOT reject a request solely because cookie is not covered, and MUST verify the signature over whatever covered components the request does list. 3. The issuer MUST recompute the digest of the received body and reject the request if it does not match the Content-Digest header, per [RFC9530], Section 2. The issuer MUST perform this check before acting on the body. Verifying the signature over content-digest establishes only that the header was signed; it is the recomputation that ties the header to the bytes received. 4. The issuer MUST reject a request whose created timestamp is more than 300 seconds from the current time. The window allows for client clock skew, following the guidance of [RFC8725] that leeway should be a few minutes at most. 5. The algorithms the issuer accepts are the values of signing_alg_values_supported in its metadata (see Issuer Metadata (#issuer-metadata)), which serves the role Accept-Signature-Alg serves in [I-D.hardt-httpbis-signature-key]. An issuer that publishes the member SHOULD also send Accept-Signature-Alg on an unsupported_algorithm response, so a client that did not read the metadata learns the same set. See Signature Errors (#signature-errors) for how the issuer reports a failure alongside this specification's JSON error body. The issuer MUST verify the request body: 1. Parsing the JSON body and extracting the email field 2. Verifying the email field is a valid email address as defined below Hardt & Goto Expires 26 February 2027 [Page 15] Internet-Draft EVP August 2026 4.3. Valid Email Address An email address is valid for the purposes of this specification if it matches the "valid e-mail address" ABNF production of [WHATWG.HTML], Section 4.10.5.1.5. The same definition applies to the email field of the token request, to the email claim of the EVT, and to a private email address issued under Private Email Addresses (#private-email). [RFC5322] defines a broader grammar than this. It admits comments, folding whitespace, and quoted local parts that no provider issues in practice and that no browser will accept from a user, and parsers that implement it fully disagree with each other at the edges. The [WHATWG.HTML] production is a deliberate narrowing of [RFC5322] chosen for exactly this problem, and it is already what the browser applies to the address before the protocol begins, since the user supplies it through a control validated against that production. Adopting it here makes the issuer's check and the browser's the same check. The production is willfully non-compliant with [RFC5322] and this specification adopts that non-compliance knowingly. An issuer whose users hold addresses outside it cannot verify them through this protocol. 5. Email Verification Token (EVT) The Email Verification Token (EVT) is a JWT issued by the issuer that contains a verified email address and the browser's public key. This section defines the EVT structure and how it is created and verified. 5.1. EVT Structure The EVT is a JWT with the following structure: 5.1.1. Header * alg (REQUIRED): Signing algorithm. MUST be a fully-specified identifier from the IANA "JSON Web Signature and Encryption Algorithms" registry. The polymorphic EdDSA identifier MUST NOT be used; use Ed25519 or Ed448. * kid (REQUIRED): Key identifier of the key used to sign * typ (REQUIRED): Set to "evt+jwt" Example: Hardt & Goto Expires 26 February 2027 [Page 16] Internet-Draft EVP August 2026 { "alg": "Ed25519", "kid": "2024-08-19", "typ": "evt+jwt" } 5.1.2. Payload Required claims: * iss: The issuer identifier, an HTTPS origin (see Issuer Identifier (#issuer-identifier)) * iat: Issued at time (seconds since epoch) * cnf: Confirmation claim containing the browser's public key in jwk format (for SD-JWT Key Binding compatibility). The jwk is the key conveyed in the Signature-Key header of the token request, reproduced with the same members, including its fully-specified alg. * email: The verified email address, byte-for-byte as it appeared in the token request * email_verified: Boolean, MUST be true Optional claims: * is_private_email: Boolean, set to true when the email is a private address Example: { "iss": "https://issuer.example", "iat": 1724083200, "cnf": { "jwk": { "kty": "OKP", "crv": "Ed25519", "x": "JrQLj5P_89iXES9-vFgrIy29clF9CC_oPPsw3c5D0bs", "alg": "Ed25519" } }, "email": "user@example.com", "email_verified": true } 5.1.3. Format The EVT has a ~ appended to it for SD-JWT compatibility (see SD-JWT Compatibility (#sd-jwt-compatibility)). Hardt & Goto Expires 26 February 2027 [Page 17] Internet-Draft EVP August 2026 5.2. EVT Creation After verifying the request (see Request Verification (#request- verification)) and authenticating the user, the issuer creates the EVT: 1. Construct the header with alg, kid, and typ 2. Construct the payload with iss, iat, cnf (containing the public key from the Signature-Key header, including its alg member), email, and email_verified 3. If a private email is requested, include is_private_email: true and set email to the private address 4. Sign the JWT with the issuer's private key corresponding to the kid 5. Append ~ to the signed JWT | Note: The is_private_email claim name matches Apple's Sign in with | Apple for compatibility with existing RP implementations. 5.3. EVT Verification Both the browser and RP verify the EVT. The verification steps are: 1. Parse the EVT into header, payload, and signature components 2. Extract and validate the alg and kid from the header. Reject an alg that is absent, polymorphic, none, or a symmetric MAC identifier. 3. Extract and validate the iss, iat, cnf, email, and email_verified claims from the payload 4. Perform Issuer Discovery (#issuer-discovery) for the email domain to derive the issuer identifier, and verify the iss claim is byte-for-byte identical to it 5. Fetch the issuer's public keys from the jwks_uri in the issuer metadata 6. Verify the EVT signature using the public key identified by kid 7. Verify iat is within an acceptable time window 8. Verify email_verified is true The browser additionally verifies: * The email claim is byte-for-byte identical to the email address being verified * The cnf.jwk claim matches the public key the browser generated Hardt & Goto Expires 26 February 2027 [Page 18] Internet-Draft EVP August 2026 6. Key Binding (EVT+KB) Key Binding ties an EVT to a specific RP and session through a Key Binding JWT (KB-JWT). The combined EVT+KB is what the RP receives and verifies. 6.1. KB-JWT Structure The KB-JWT is a JWT with the following structure: 6.1.1. Header * alg (REQUIRED): Signing algorithm. MUST be the fully-specified identifier carried in the alg member of the EVT's cnf.jwk, which is the same key the browser used to sign the token request. * typ (REQUIRED): Set to "kb+jwt" for SD-JWT library compatibility Example: { "alg": "Ed25519", "typ": "kb+jwt" } 6.1.2. Payload * aud (REQUIRED): The RP's origin * nonce (REQUIRED): The nonce from the RP's session * iat (REQUIRED): Issued at time * sd_hash (REQUIRED): SHA-256 hash of the EVT for SD-JWT library compatibility Example: { "aud": "https://rp.example", "nonce": "259c5eae-486d-4b0f-b666-2a5b5ce1c925", "iat": 1724083260, "sd_hash": "X9yH0Ajrdm1Oij4tWso9UzzKJvPoDxwmuEcO3XAdRC0" } 6.2. EVT+KB Format The EVT+KB is formed by concatenating the EVT and KB-JWT separated by a tilde: ~ Hardt & Goto Expires 26 February 2027 [Page 19] Internet-Draft EVP August 2026 The EVT already has a trailing ~ from its SD-JWT format, so the full structure is: ~ 6.3. SD-JWT Compatibility The EVT+KB format is compatible with SD-JWT with Key Binding as specified in [I-D.ietf-oauth-selective-disclosure-jwt], though this protocol does not use selective disclosure features. The following SD-JWT features are used: * *Trailing ~ on EVT*: The EVT uses the SD-JWT format (JWT with ~ suffix) * *cnf claim*: The EVT includes the cnf claim with jwk for holder key binding * *typ: "kb+jwt"*: The KB-JWT uses the SD-JWT Key Binding JWT type * *sd_hash claim*: The KB-JWT includes the SD-JWT hash of the EVT * *Concatenation format*: The EVT+KB uses the SD-JWT ~ format Standard SD-JWT libraries can be used to parse and validate EVT+KB tokens. 6.4. KB-JWT Creation After verifying the EVT (see EVT Verification (#evt-verification)), the browser creates the KB-JWT: 1. Construct the header with alg and typ 2. Construct the payload with: * aud: The RP's origin * nonce: The nonce from the RP's session * iat: Current time * sd_hash: SHA-256 hash of the EVT (including the trailing ~) 3. Sign the KB-JWT with the browser's private key 4. Concatenate with the EVT to form the EVT+KB 6.5. KB-JWT Verification The RP verifies the KB-JWT by: 1. Parse the EVT+KB by separating at the tilde 2. Parse the KB-JWT into header, payload, and signature 3. Extract alg from the header and aud, nonce, iat, sd_hash from the payload 4. Verify alg matches the alg member of the EVT's cnf.jwk, and reject the KB-JWT if it does not Hardt & Goto Expires 26 February 2027 [Page 20] Internet-Draft EVP August 2026 5. Verify aud matches the RP's origin 6. Verify nonce matches the nonce from the RP's session 7. Verify iat is within a reasonable time window 8. Compute the SHA-256 hash of the EVT and verify it matches sd_hash 9. Verify the KB-JWT signature using the public key from the EVT's cnf.jwk claim, under the algorithm named by that key's alg member 7. Private Email Addresses Private email addresses allow users to provide site-specific email addresses to RPs, preventing RP-to-RP correlation of users by email address. A private email address can be: * *Single-use*: The browser requests a new private email and does not store it * *Reusable*: The browser stores the private email and passes it back via directed_email for account continuity The choice between single-use and reusable is made by the browser or user, not the issuer. The first request to an RP always uses private_email: true to obtain a new private email address. For subsequent requests, the browser can either request another new private email or reuse an existing one by passing it in directed_email. 7.1. Request Parameters The token request body supports one of the following parameters for private email addresses (mutually exclusive): * private_email (OPTIONAL): Boolean. When set to true, requests a new private email address instead of the user's actual email. * directed_email (OPTIONAL): String. A previously issued private email address. When provided, the issuer returns the same private email address if it is valid and linked to the email in the request. 7.2. Example Requests Request for a new private email address: { "email": "user@example.com", "private_email": true } Request to reuse a previously issued private email address: Hardt & Goto Expires 26 February 2027 [Page 21] Internet-Draft EVP August 2026 { "email": "user@example.com", "directed_email": "u7x9k2m4@privaterelay.example" } 7.3. Requirements * The private email MUST be a valid email address that the issuer can route to the user's actual mailbox * The private email SHOULD be unique per user and per RP origin (derived from the browser's context) * If directed_email is provided and is linked to the email address in the request, the issuer MUST return the same private email address * If directed_email is provided but is invalid or not linked to the email, the issuer MUST return an error * The private email address is included in the EVT email claim * The EVT MUST include is_private_email: true when a private email address is issued 7.4. Issuer Flexibility The domain of the private email address does not need to match the domain of the user's actual email address. Additionally, the iss claim in the EVT is the issuer identifier for the private email domain, which may differ from the issuer the browser initially contacted. For example, a user with user@example.com may receive a private email address u7x9k2m4@privaterelay.different.example. The EVT's iss claim would be the issuer identifier derived for privaterelay.different.example. The browser verifies the EVT by performing issuer discovery on the private email domain and validating the signature against that issuer's JWKS. This allows email providers to delegate private email functionality to a separate service. It also enables privacy for users with vanity domains (e.g., me@dickhardt.example) where the domain itself is a unique identifier that would otherwise reveal the user's identity. 7.5. Example EVT Payload When a private email is issued, the EVT contains the private address in the email claim and includes is_private_email: true: Hardt & Goto Expires 26 February 2027 [Page 22] Internet-Draft EVP August 2026 { "iss": "https://privaterelay.different.example", "iat": 1724083200, "cnf": { "jwk": { "kty": "OKP", "crv": "Ed25519", "x": "JrQLj5P_89iXES9-vFgrIy29clF9CC_oPPsw3c5D0bs", "alg": "Ed25519" } }, "email": "u7x9k2m4@privaterelay.different.example", "email_verified": true, "is_private_email": true } The browser MAY store the private email address so it can provide it as directed_email in future requests if the user wants to reuse the same private email address at an RP. This is analogous to how browsers store usernames and passwords for sites. See Privacy Considerations (#privacy-considerations) for privacy analysis of private email addresses. 8. Error Responses If the issuer cannot process the token request successfully, it MUST return an appropriate HTTP status code with a JSON error response containing an error field and optionally an error_description field. 8.1. Invalid Content-Type Header When the request does not include the required Content-Type: application/json header, the server MUST return the 415 HTTP response code. 8.2. Invalid Sec-Fetch-Dest Header When the request does not include the required Sec-Fetch-Dest: email- verification header: *HTTP 400 Bad Request* { "error": "invalid_request", "error_description": "Missing or invalid Sec-Fetch-Dest header" } Hardt & Goto Expires 26 February 2027 [Page 23] Internet-Draft EVP August 2026 The error_description SHOULD specify that the Sec-Fetch-Dest header is missing or invalid. 8.3. Invalid or Missing HTTP Message Signature When the HTTP Message Signature is missing, malformed, or verification fails, the issuer MUST return the error field invalid_signature in the JSON body, and SHOULD include the Signature- Error response header of [I-D.hardt-httpbis-signature-key] naming the specific error code. The header is the machine-readable carrier; the JSON body remains for clients that do not process it. *HTTP 400 Bad Request* HTTP/1.1 400 Bad Request Content-Type: application/json Signature-Error: error=invalid_signature { "error": "invalid_signature", "error_description": "HTTP Message Signature verification failed" } This covers every signature-related failure defined by [I-D.hardt-httpbis-signature-key] — an unresolvable or invalid key, an unsupported algorithm or scheme, a missing covered component, an expired created timestamp, and a signature that does not verify — and a Content-Digest that does not match the received body. The body's error field is invalid_signature in all of them; the Signature-Error header carries which one it was, and the accompanying headers that document defines say what would have worked: HTTP/1.1 400 Bad Request Content-Type: application/json Signature-Error: error=unsupported_algorithm Accept-Signature-Alg: Ed25519, ES256 { "error": "invalid_signature", "error_description": "Unsupported signature algorithm" } The two carry the same failure at different granularities, so an issuer MUST NOT return a Signature-Error header naming a failure the body contradicts. Hardt & Goto Expires 26 February 2027 [Page 24] Internet-Draft EVP August 2026 8.4. Authentication Required When the request lacks valid authentication cookies, contains expired/invalid cookies, or the authenticated user does not have control of the requested email address: *HTTP 401 Unauthorized* { "error": "authentication_required", "error_description": "User must be authenticated and have control of the requested email address" } 8.5. Invalid Parameters When the request body is malformed, missing the email field, or contains invalid values: *HTTP 400 Bad Request* { "error": "invalid_request", "error_description": "Invalid or malformed request body" } 8.6. Private Email Not Supported When the request includes private_email or directed_email but the issuer does not support private email addresses (private_email_supported is false or absent in metadata): *HTTP 400 Bad Request* { "error": "private_email_not_supported", "error_description": "This issuer does not support private email addresses" } 8.7. Invalid Directed Email When the request includes directed_email but the private email address is invalid or not linked to the email address in the request: *HTTP 400 Bad Request* Hardt & Goto Expires 26 February 2027 [Page 25] Internet-Draft EVP August 2026 { "error": "invalid_directed_email", "error_description": "The directed_email is invalid or not linked to this email address" } 8.8. Server Errors For internal server errors or temporary unavailability: *HTTP 500 Internal Server Error* { "error": "server_error", "error_description": "Temporary server error, please try again later" } 9. Privacy Considerations This section analyzes the privacy properties of the Email Verification Protocol, following the guidance in [RFC6973]. 9.1. Reduced Friction Tradeoff By reducing friction in email verification, EVP makes it easier for users to provide their email address to more sites. This convenience could accelerate the RP correlation problem—users may share a correlatable identifier with more RPs than they would if verification required more effort. EVP addresses this tradeoff through private email addresses. When supported by the issuer, users can present a site-specific private email that cannot be correlated across RPs. This makes sharing a non-correlatable identifier just as easy as sharing the user's real email address, giving users a privacy-preserving option without additional friction. 9.2. Timing Correlation by Email Providers The three-party model (see Protocol Flow (#protocol-flow)) prevents the issuer from learning which RP requested verification. When the RP uses the email only for identification and does not send emails, the email provider never learns about the RP at all. When the RP does send emails, the provider eventually learns about that RP, but only when email is actually sent—not at verification time. This dulls timing correlation. Hardt & Goto Expires 26 February 2027 [Page 26] Internet-Draft EVP August 2026 9.3. RP Correlation via Email Addresses Private email addresses prevent RPs from correlating users across sites. Additional benefits: *Protection from data breaches*: If an RP suffers a data breach, only the private email is exposed—not the user's primary email address. *Protection from unwanted email*: Because the issuer controls private email routing, users can revoke or filter mail to specific addresses without affecting their primary inbox. 9.4. Issuer Knowledge The issuer learns certain information through the protocol: 1. *Email addresses*: The issuer learns that the user controls the email address in the request. This may reveal email addresses at domains the issuer is authoritative for that it did not previously know the user had. 2. *Verification requests*: The issuer sees that verification was requested but does not learn which RP requested it (maintained by the three-party model). 3. *Private email mappings*: When generating private emails, the issuer stores mappings between private addresses and user email addresses for mail routing. 4. *Email traffic*: When RPs send email to private addresses, the issuer (operating the relay) learns about those communications. 9.5. RP Knowledge The RP can infer whether the user is logged into the issuer: the RP receives an EVT when the user is logged in, and receives an error when the user is not. This is inherent to any authentication-based verification scheme. 9.6. Browser Storage The browser MAY store the private email address per RP origin to enable account continuity by passing it as directed_email in future requests. This is analogous to how browsers store usernames and passwords for sites. 10. Security Considerations Hardt & Goto Expires 26 February 2027 [Page 27] Internet-Draft EVP August 2026 10.1. HTTP Message Signature Security The use of HTTP Message Signatures ([RFC9421]) provides several security benefits: 1. *Request Integrity*: The signature covers the HTTP method, authority, path, and — through content-digest — the request body, preventing tampering with any of these components. The body is what carries the email address being verified, so without that coverage the signature would attest to a request without attesting to which address it asked for. 2. *Cookie Binding (when covered)*: Covering the cookie component binds the authenticating cookie to the signed request. Coverage cannot be assumed — in some browser architectures the Cookie header is attached after the request is signed, so the value does not exist at signing time. Without it, the signature still binds the request to the browser's key, and the cookie still authenticates the user. 3. *Replay Protection*: The created timestamp in the Signature-Input header is verified to be within 300 seconds, bounding the replay window. A replayed request yields an EVT bound to the original browser's key through the cnf claim, so replay gains an attacker no token another party can use. 4. *Public Key Binding*: The browser's public key transmitted via the Signature-Key header with the hwk scheme is bound to the request signature, ensuring the issuer knows which public key to include in the EVT's cnf claim. Because signature-key is a covered component, an attacker cannot substitute the key or the scheme without invalidating the signature; see Signature-Key Integrity in [I-D.hardt-httpbis-signature-key]. 10.2. Signature-Key hwk Scheme The hwk (Header Web Key) scheme provides: 1. *Self-Contained Key Distribution*: The public key is transmitted inline, eliminating the need for a separate key lookup or registration process. 2. *Pseudonymity*: The browser does not need to identify itself - the key serves as a pseudonymous identifier for the request. 3. *Ephemeral Keys*: The browser generates fresh key pairs for each verification flow, limiting the correlation potential across different verification attempts. Hardt & Goto Expires 26 February 2027 [Page 28] Internet-Draft EVP August 2026 10.3. Fully-Specified Algorithms Every algorithm identifier in this protocol is fully specified: it names the signature operation completely, including curve and hash where applicable. A key whose algorithm is not determined by its identifier invites downgrade and confused-verifier conditions, in which two parties disagree on the operation a signature represents. [RFC9864] gives the analysis and deprecates the polymorphic identifiers, of which EdDSA is the one this protocol would otherwise have used. For the browser's request-signing key, [I-D.hardt-httpbis-signature-key] imposes this and states how the issuer enforces it. The EVT and the KB-JWT are outside that document's scope, so this specification imposes it on them directly: on the issuer's EVT signing key (see EVT Structure (#evt-structure)) and on the KB-JWT, whose alg is fixed by the cnf.jwk it is verified against (see KB-JWT Structure (#kb-structure)). 10.4. DNS Delegation The delegation from an email domain to an issuer is carried in an unauthenticated DNS TXT record (DNS Delegation (#dns-delegation)). An attacker who controls what a party sees for _email- verification.$EMAIL_DOMAIN controls which issuer that party believes is authoritative for the domain. This section states what that yields, and to whom. Two parties resolve the record independently: the browser, to find the issuance endpoint, and the RP, to check the iss claim of the EVT it receives (EVT Verification (#evt-verification)). The two lookups are not equally valuable to an attacker. *Spoofing the browser's resolver alone gains little.* The browser is directed to an attacker-controlled issuer and discloses the email address to it. It does not disclose the user's credentials: cookies are scoped to the real issuer's origin and are not sent to a different one. The attacker can mint an EVT, but its iss is the attacker's identifier, and the RP — resolving the record itself, over its own path — derives the real issuer identifier and rejects the token. The mismatch is what stops it, which is why Issuer Identifier (#issuer-identifier) requires the comparison to be exact. Hardt & Goto Expires 26 February 2027 [Page 29] Internet-Draft EVP August 2026 *Spoofing the RP's resolver is the attack that matters.* An attacker who controls the RP's view of DNS for the email domain, and who runs an issuer, can present an EVT the RP accepts for any address at that domain. Nothing later in verification catches this: the token is well-formed, correctly signed, and signed by the key the RP was told to trust. This is the residual risk of the design, and it is not mitigated elsewhere in this document. The RP is better placed to address this than the browser. It is a server, resolving on its own infrastructure, and can validate DNSSEC or use a validating resolver over an authenticated channel. RPs SHOULD validate DNSSEC for _email-verification.$EMAIL_DOMAIN where the email domain is signed, and SHOULD resolve through a resolver they trust rather than whatever the host is configured with. Email domain operators publishing this record SHOULD sign their zone. It has been argued that an authentication decision should never depend on data fetched from DNS. The objection is sound as a general rule and this document does not dismiss it. Two things are true alongside it. The delegation is a property of an email domain, and the authoritative source for a property of an email domain is that domain's DNS — the same place SPF, DKIM, and DMARC already sit, and the same records an attacker with this capability could already forge to redirect or authenticate mail for the domain. And moving the delegation to HTTPS would require every email domain to operate a web server, which Why DNS Delegation? (#why-dns-delegation) explains is the barrier this design set out to avoid. The exposure this creates is nonetheless real, unsigned DNS is weaker than HTTPS, and a deployment that cannot obtain DNSSEC on the email domains it accepts is accepting the risk described above. 10.5. Email Existence Probing Any software—not just browsers—can send requests to an issuer's issuance endpoint. An attacker could attempt to use this to probe for valid email addresses: 1. *Build email lists*: Probe many addresses to identify valid ones for spam targeting. 2. *Account enumeration*: Determine which email addresses have accounts at specific issuers. 10.5.1. Uniform Error Responses To prevent probing, issuers MUST NOT return different error responses based on whether an email address exists. The authentication_required error should be returned uniformly whether: Hardt & Goto Expires 26 February 2027 [Page 30] Internet-Draft EVP August 2026 * The email address does not exist at this issuer * The email address exists but the user is not authenticated * The email address exists but the authenticated user does not control it This ensures attackers cannot distinguish between "email exists" and "email does not exist" based on error responses. 10.5.2. Timing Attack Mitigations Response timing can also reveal whether an email address exists. If the issuer performs a database lookup only when the email exists, or takes different code paths based on email existence, an attacker can measure response times to infer information. Issuers SHOULD mitigate timing attacks using techniques such as: * *Uniform code paths*: Execute the same operations (database lookups, cryptographic operations) regardless of whether the email exists, avoiding early returns that skip processing steps. * *Response delay normalization*: Add delays to normalize response times across all error conditions to a consistent baseline. 10.5.3. Additional Mitigations * *User interaction required*: The browser API requires user gesture and consent before initiating verification, preventing automated probing from browsers. * *Rate limiting*: Issuers SHOULD rate-limit requests per IP address to slow down probing attempts from any client. * *Sec-Fetch-Dest verification*: The required Sec-Fetch-Dest: email- verification header provides a signal that the request originates from a browser, though this can be spoofed by non-browser clients. * *Same information as email OTP*: An attacker can already determine email existence by sending verification emails and checking for bounces. EVP does not create new information disclosure beyond what is already possible. Issuers SHOULD implement appropriate rate limiting and abuse detection. 11. Design Rationale Hardt & Goto Expires 26 February 2027 [Page 31] Internet-Draft EVP August 2026 11.1. Why Not Solve Email Like SMS OTP? The WebOTP API and autocomplete="one-time-code" standards dramatically reduced friction for SMS verification. A natural question is why email verification cannot use the same approach. Several fundamental differences make this impractical: *SMS is a mobile OS feature; email is application-layer* SMS is integrated into mobile operating systems. The OS receives incoming messages and can parse them before any application sees them. This privileged position enables the OS to recognize origin- bound OTP formats and offer autofill directly to the browser. Email operates at the application layer. There is no OS-level email subsystem that intercepts incoming messages. Email clients are ordinary applications—whether native apps, desktop programs, or web applications—with no special ability to coordinate with browsers for autofill. *SMS verification is mobile; email verification spans platforms* SMS OTP autofill works on mobile devices where the OS controls the messaging stack. Email verification happens on desktop computers, laptops, tablets, and phones. Any solution for email must work across all these platforms, not just mobile. *SMS senders are aggregators; email senders are RPs* SMS verification messages are typically sent through aggregator services (Twilio, AWS SNS, etc.) that send on behalf of many relying parties. The "sender" of the SMS is often a short code or phone number shared across multiple services. This means the phone number or sender ID carries little identifying information about which RP sent the message. Email verification messages come directly from the RP's domain. The sender address, domain, and email headers identify the RP. This architectural difference means that email verification inherently reveals more about the RP to the email provider than SMS verification reveals to the carrier. 11.2. Why the Three-Party Model? A simpler design would have the issuer create a token directly for the RP, with the RP as the audience. This is how social login works: the identity provider knows which application the user is logging into. Hardt & Goto Expires 26 February 2027 [Page 32] Internet-Draft EVP August 2026 EVP uses a three-party model where the browser intermediates between the issuer and the RP. The issuer creates an EVT bound to the browser's ephemeral public key, and the browser creates a separate KB-JWT that binds the EVT to the RP. The issuer never learns the RP's identity. This design choice is driven by privacy: for users with domain-based email accounts (personal domains, work accounts), the email provider should not learn which applications the user accesses. The architectural complexity of the three-party model is justified by this privacy benefit. 11.3. Why SD-JWT? The EVT uses the SD-JWT structure (specifically, the key binding capability from SD-JWT+KB) rather than a plain JWT. This choice provides: 1. *Key Binding*: The ~ separator and KB-JWT mechanism provide a standard way to bind a token to a holder's key, enabling the three-party model where issuance and presentation are separate operations. 2. *Library Support*: SD-JWT libraries already exist and can parse EVTs, reducing implementation burden for RPs. 3. *Extensibility*: While EVP does not currently use selective disclosure, the SD-JWT structure allows future extensions without changing the token format. 11.4. Why DNS Delegation? The mail domain delegates email verification to an issuer via a DNS TXT record rather than a .well-known file. This choice aligns with how email infrastructure already works: 1. *Email domains often lack web hosting*: Many users have personal domains used only for email. Requiring a web server to host a .well-known file would create a barrier to adoption. 2. *Apex domain challenges*: Email domains are typically apex domains (e.g., example.com), which do not support CNAME records. Hosting a web site on an apex domain requires additional infrastructure. 3. *Familiar tooling*: Domain owners already manage DNS records for email (MX, SPF, DKIM, DMARC). Adding another TXT record fits existing workflows. Hardt & Goto Expires 26 February 2027 [Page 33] Internet-Draft EVP August 2026 11.5. Why JWKS Over DKIM Keys? The issuer publishes signing keys via a JWKS endpoint rather than reusing DKIM keys. While DKIM keys are already associated with email domains, JWKS provides practical advantages: 1. *Key rotation*: DKIM keys are rarely rotated in practice. JWKS rotation is common in OIDC deployments and follows established patterns. 2. *Algorithm flexibility*: JWKS supports multiple key types and algorithms. DKIM key distribution was designed for a specific use case. 3. *Operational familiarity*: Developers implementing EVP are likely familiar with JWKS from OAuth/OIDC work. 11.6. Why HTTP Message Signatures Rather Than Request JWT? The original design used a JWT signed by the browser to carry the email address and browser's public key. The HTTP Message Signatures approach was chosen because: 1. *Standards-Based*: [RFC9421] is a published standard for signing HTTP messages, providing better interoperability 2. *Cookie Binding*: HTTP Message Signatures can directly sign the cookie header where a signer is able to, providing stronger binding between authentication cookies and the request 3. *Flexibility*: The signature can cover any HTTP components, making it easier to add additional protections in the future 4. *Simpler Key Distribution*: The Signature-Key header provides a standardized way to distribute keys inline with the request 12. Implementation Status _Note: This section is to be removed before publishing as an RFC._ This section records the status of known implementations of the protocol defined by this specification at the time of posting of this Internet-Draft, and is based on a proposal described in [RFC7942]. The description of implementations in this section is intended to assist the IETF in its decision processes in progressing drafts to RFCs. The following implementations are known: Hardt & Goto Expires 26 February 2027 [Page 34] Internet-Draft EVP August 2026 * *Hellō* — hello.coop (https://hello.coop). Organization: Hellō. Role: Issuer. Coverage: EVT issuance endpoint, issuer discovery via DNS TXT, JWKS endpoint. Level of maturity: exploratory. * *GMail* — gmail.com (https://gmail.com). Organization: Google. Role: Issuer. Coverage: EVT issuance endpoint, issuer discovery via DNS TXT, JWKS endpoint. Level of maturity: origin trial. * *Chrome* — Organization: Google. Role: Browser. An origin trial is available at https://developer.chrome.com/ origintrials/#/view_trial/10696049115004929 (https://developer.chrome.com/ origintrials/#/view_trial/10696049115004929). * *Edge* — Organization: Microsoft. Role: Browser. An origin trial is available at https://developer.microsoft.com/en-us/microsoft- edge/origin-trials/trials/683027d2-a798-460f-9b35-03a95002d9af (https://developer.microsoft.com/en-us/microsoft-edge/origin- trials/trials/683027d2-a798-460f-9b35-03a95002d9af). 13. Document History _Note: This section is to be removed before publishing as an RFC._ * draft-hardt-email-verification-02 Tracked the breaking changes in draft-hardt-httpbis-signature-key-08. A token request serialized per -01 is rejected by an issuer implementing this version, and one serialized per this version is rejected by an issuer implementing -01. There is nothing to negotiate over, so both ends of a deployment move together. * Added the required alg parameter to every Signature-Key example. Signature-Key -08 makes the key's alg the single source of truth for the algorithm; -07 forbade the parameter outright. * Replaced the polymorphic EdDSA identifier with Ed25519 throughout: the browser's key, the default when signing_alg_values_supported is absent, the EVT header, the KB-JWT header, and every example. EdDSA is deprecated by [RFC9864] and forbidden by Signature-Key -08. * Required signing_alg_values_supported to carry fully-specified identifiers, and forbade EdDSA, none, and the symmetric MAC identifiers. The member governs issued EVTs as well as HTTP Message Signatures, so the constraint is stated here rather than left to Signature-Key. Hardt & Goto Expires 26 February 2027 [Page 35] Internet-Draft EVP August 2026 * Rewrote HTTP Request Verification to delegate to [RFC9421] and [I-D.hardt-httpbis-signature-key] rather than restate them, and to keep only what this specification profiles: hwk as the only scheme, the required covered components, the created window, and signing_alg_values_supported as the discovery-time form of Accept- Signature-Alg. The restated text was what went stale — the previous step listing the hwk parameters as kty, crv, and x silently became wrong when -08 added alg. * Carried the key's alg member into the EVT cnf.jwk, so the RP verifies the KB-JWT under a fully-specified algorithm rather than one derived from the key. Required the KB-JWT header alg to match it, and had the RP reject a KB-JWT where the two disagree. * Required the EVT header alg to be fully specified. The EVT is an ordinary JWT and outside the scope of Signature-Key, so nothing else imposes this. * Added the Signature-Error response header to signature error responses, alongside this specification's existing JSON error body, and said how the two relate: the body reports invalid_signature in every case and the header carries which failure it was. * Added a Fully-Specified Algorithms subsection to Security Considerations giving the reason and naming which of the three signatures each rule reaches. * Defined "valid email address" as the "valid e-mail address" production of [WHATWG.HTML] rather than leaving the term undefined, and said why that production rather than [RFC5322]. Addresses issue #2. * Added a DNS Delegation subsection to Security Considerations. The delegation rests on an unauthenticated TXT record and the document said nothing about it. Separated the two lookups: spoofing the browser's resolver discloses the email address but yields no token the RP will accept, since cookies are origin-scoped and the RP resolves the record itself; spoofing the RP's resolver is the attack that matters and is not mitigated elsewhere. Recommended DNSSEC validation by RPs and zone signing by email domain operators, and stated the residual risk. Addresses issue #6. Hardt & Goto Expires 26 February 2027 [Page 36] Internet-Draft EVP August 2026 * Made the issuer identifier an HTTPS origin rather than a bare host name, aligning the iss claim with [OpenID.Core] and [RFC8414] and with what the browser implementation already enforces. The DNS TXT record still carries a host name; the identifier is derived from it by prefixing https://, and every comparison is byte-for- byte on the derived string. Added an Issuer Identifier section stating the derivation once. Addresses issue #7. * Added the issuer member to the metadata document and required a fetching party to reject a document whose issuer does not match the identity it was fetched under, per [RFC8414], Section 3.3. This is the check Signature-Key -08 added for its own discovery, applied here. * Made coverage of the cookie component RECOMMENDED when the header is available to the signer at signing time, rather than required, and forbade an issuer from rejecting a request solely because it is not covered. In some browser architectures the Cookie header is attached after the request is constructed and signed, so the requirement was unimplementable. Addresses issue #11. * Replaced the Email Discovery models with what this protocol requires: candidate addresses come from the W3C Email Verification API, the browser offers only valid email addresses, and from selection onward the address is never altered — it is sent byte- for-byte in the token request, returned byte-for-byte in the email claim, and every email comparison is byte-for-byte. Addresses issues #4, #13, and #24. * Required the Content-Digest header ([RFC9530]) on the token request and added content-digest to the covered components. The email address being verified is carried in the request body, which no covered component reached, so the signature attested to a request without attesting to which address it asked for. Required the issuer to recompute the digest against the received bytes rather than rely on the signature over the header alone. Addresses issue #3. * Removed the WebAuthn Authentication section, the webauthn_supported metadata member, and the WebAuthn challenge/ response exchange. Nothing implements it, and it hardwired one authentication method into the protocol. A request without a valid session returns authentication_required. An issuer-provided login URL using the FedCM Login Status continuation is being explored as a replacement; that flow is an exploration only, dependent on the browser developing an acceptable user experience for the login popup. Hardt & Goto Expires 26 February 2027 [Page 37] Internet-Draft EVP August 2026 * Widened the created acceptance window from 60 to 300 seconds. Device clocks skew by minutes, not seconds, and the window is not load-bearing for security: a replayed request yields an EVT bound to the original browser's key. Follows the clock-skew guidance of [RFC8725]. * draft-hardt-email-verification-01 - Updated Implementation Status: completed GMail issuer entry, added Chrome and Edge origin trials. * draft-hardt-email-verification-00 - Initial draft. 14. References 14.1. Normative References [I-D.hardt-httpbis-signature-key] Hardt, D. and T. Meunier, "HTTP Signature Keys", Work in Progress, Internet-Draft, draft-hardt-httpbis-signature- key-08, 5 August 2026, . [I-D.ietf-oauth-selective-disclosure-jwt] Fett, D., Yasuda, K., and B. Campbell, "Selective Disclosure for JWTs (SD-JWT)", Work in Progress, Internet- Draft, draft-ietf-oauth-selective-disclosure-jwt-22, 29 May 2025, . [OpenID.Core] Sakimura, N., Bradley, J., Jones, M., de Medeiros, B., and C. Mortimore, "OpenID Connect Core 1.0", November 2014, . [RFC5322] Resnick, P., Ed., "Internet Message Format", RFC 5322, DOI 10.17487/RFC5322, October 2008, . [RFC8414] Jones, M., Sakimura, N., and J. Bradley, "OAuth 2.0 Authorization Server Metadata", RFC 8414, DOI 10.17487/RFC8414, June 2018, . Hardt & Goto Expires 26 February 2027 [Page 38] Internet-Draft EVP August 2026 [RFC9421] Backman, A., Ed., Richer, J., Ed., and M. Sporny, "HTTP Message Signatures", RFC 9421, DOI 10.17487/RFC9421, February 2024, . [RFC9530] Polli, R. and L. Pardue, "Digest Fields", RFC 9530, DOI 10.17487/RFC9530, February 2024, . [RFC9864] Jones, M.B. and O. Steele, "Fully-Specified Algorithms for JSON Object Signing and Encryption (JOSE) and CBOR Object Signing and Encryption (COSE)", RFC 9864, DOI 10.17487/RFC9864, October 2025, . [WHATWG.HTML] WHATWG, "HTML Standard", Living Standard, 2026, . 14.2. Informative References [EVP-Browser] WICG, "Email Verification API", 2025, . [LightweightFedCM] FedID CG, "Lightweight FedCM", 2025, . [RFC6973] Cooper, A., Tschofenig, H., Aboba, B., Peterson, J., Morris, J., Hansen, M., and R. Smith, "Privacy Considerations for Internet Protocols", RFC 6973, DOI 10.17487/RFC6973, July 2013, . [RFC7942] Sheffer, Y. and A. Farrel, "Improving Awareness of Running Code: The Implementation Status Section", BCP 205, RFC 7942, DOI 10.17487/RFC7942, July 2016, . [RFC8725] Sheffer, Y., Hardt, D., and M. Jones, "JSON Web Token Best Current Practices", BCP 225, RFC 8725, DOI 10.17487/RFC8725, February 2020, . Hardt & Goto Expires 26 February 2027 [Page 39] Internet-Draft EVP August 2026 Appendix A. Acknowledgments The authors would like to thank reviewers for their feedback on this specification. Authors' Addresses Dick Hardt Hellō Email: dick.hardt@gmail.com Sam Goto Google Email: goto@google.com Hardt & Goto Expires 26 February 2027 [Page 40]