发布时间:2026 年 7 月 8 日
在注册、登录、订阅、结账、账号恢复或其他流程中收集电子邮件地址时,通常会确认该电子邮件地址是否属于输入者本人。现有的验证方法(例如一次性密码 [OTP] 或电子邮件验证链接 [magic link])要求用户离开您的网站。这种中断性流程可能会增加用户(无论是真人还是代理)完全放弃会话并永远无法完成身份验证流程的风险。
电子邮件验证 API 是一项提案,可让浏览器直接与电子邮件提供商通信,以验证用户是否拥有相应电子邮件地址。用户从浏览器的自动填充或自动补全建议中选择电子邮件地址, 提交表单,然后网站会与提供商验证电子邮件地址, 而不会发送电子邮件或中断用户流程。
收集电子邮件地址是用户转化历程中的一个关键转化点,Chrome 希望收到反馈,反馈来自以下各方:希望验证电子邮件地址的网站、可以执行验证的电子邮件服务提供商,以及体验该流程的用户。您可以立即注册源试用,并按照此处的实现说明操作。如需了解常规的源试用配置,请参阅源试用使用入门。
您可以使用演示账号试用此流程:
电子邮件验证流程
以下部分介绍了您和您的用户需要执行哪些操作才能开始电子邮件验证流程,以及在使用电子邮件验证协议时的整个工作流程。
关键词
电子邮件验证 API 的关键术语如下:
- 验证方:收集电子邮件地址并希望验证该地址的网站。验证方也称为信赖方。
- 电子邮件提供商:提供用户电子邮件地址的服务,例如
gmail.com。 - 发卡机构:管理用户电子邮件账号的服务,例如
accounts.google.com。签发者也称为身份提供方。
在某些情况下,电子邮件服务提供商和发卡机构可能在同一网域中运营。不过,请务必区分拥有电子邮件地址与拥有关联账号的有效会话。
前提条件
- 用户必须在同一浏览器个人资料中登录其电子邮件服务提供商或发卡机构。例如,如果用户使用 Gmail,则必须登录其 Google 账号。
- 作为参与验证的网站,您必须注册源试用,并在电子邮件表单所在的同一页面上提供令牌。
用户必须从自动填充或自动完成下拉菜单中选择自己的电子邮件地址。
- 如果用户之前在该字段中输入过电子邮件地址,系统会使用自动补全功能提供该地址。
如果用户已使用 Chrome 设置中的“自动填充和密码”功能 (
chrome://settings/autofill) 添加电子邮件地址,系统会使用自动填充功能提供该地址。
用户首次提供电子邮件地址以进行验证时,系统会显示权限提示。每个电子邮件地址只能执行一次此操作。
用户在浏览器中拥有有效会话后,即可开始该流程:
- 在包含电子邮件地址字段的表单中,用户从自动填充下拉菜单中选择自己的电子邮件地址。验证方网站会在表单中提供一个包含每个实例的 nonce 的隐藏字段,以验证此请求。
然后,浏览器将检索电子邮件网域的电子邮件验证 DNS 记录。这会将浏览器指向签发者。然后,发卡机构会确认该电子邮件地址是否处于有效会话中。
然后,签发者将提供相应地址的电子邮件验证令牌 (EVT)。浏览器会将这些信息与 EVT、网站来源和输入表单中的随机数合并为绑定密钥的 JWT。
当表单提交时,EVT 软件包会添加到隐藏字段并发送到网站。
验证方网站随后会验证这些详细信息:预期电子邮件地址、随机数以及来自浏览器和签发者的签名。
用户会看到一条小通知,告知其电子邮件提供商已验证其地址。
此流程可向验证方网站确认电子邮件地址有效且属于当前用户,这意味着该网站可以跳过发送验证电子邮件的步骤。
用户可以依次前往设置 > 自动填充和密码 > 联系信息 > 已验证的电子邮件地址(或打开 chrome://settings/contactInfo),管理已验证的电子邮件地址。
使用场景注意事项
电子邮件验证是对现有流程的渐进式增强,可让用户无需离开您的网站即可检索一次性密码或点击链接。网站可以将电子邮件验证字段添加到所有相关表单中,例如登录、简报订阅、账号创建和密码恢复表单。仅当浏览器支持时,EVP 才会触发。如果在提交后未收到任何验证码,或者任何验证步骤失败,您可以回退到默认的电子邮件确认流程。这也意味着该 API 没有功能检测;验证器网站会将 EVT 视为可选,如果请求中包含 EVT,则会处理它。
电子邮件验证可确认用户是否与电子邮件地址提供商建立了有效会话。但它不会验证您的电子邮件是否已送达用户。您可能仍想发送现有的欢迎电子邮件或新手入门电子邮件,并且可能想或需要提示用户检查其垃圾邮件设置。
实现验证方网站
如需了解更多详情,您可以浏览端到端演示代码,并参阅电子邮件验证 API 和电子邮件验证协议提案中的验证步骤。
配置表单字段
确保表单字段具有正确的属性:
<input
name="email-address"
type="email"
autocomplete="email">
<input
type="hidden"
name="token"
nonce="rAnD0m-VaLuE"
autocomplete="email-verification-token">
将 email 输入的 type 和 autocomplete 属性设置为 email,以便浏览器提供电子邮件地址自动补全功能。
提交表单后,新的 hidden 字段将填充电子邮件验证令牌。必需的属性包括:
- 设置
type="hidden",因为此字段不需要用户输入。 - 只需设置
nonce="rAnD0m-VaLuE"。网站必须提供与会话绑定的唯一随机数,以验证表单提交。 - 只需设置
autocomplete="email-verification-token"。浏览器使用此属性来标识要填充的字段。
通过检查开发者工具中的“网络”面板来验证表单元素。选择电子邮件地址后,您会看到浏览器为电子邮件提供商和发卡机构触发 DNS 和后续的账号查找查询。这些是内部浏览器请求;在提交表单之前,您的网站不会收到任何内容。
验证 EVT
验证 EVT 软件包的每个组件需要执行五个步骤。
- 解析令牌。
- 验证预期值。
- 验证密钥绑定。
- 验证 DNS 记录。
- 发现签发者并验证 EVT 签名。
1. 解析令牌
表单提交中的原始数据包含 EVT 和签名声明,这些数据以波浪号 (~ 字符) 分隔,采用选择性披露 JSON Web 令牌 (SD-JWT+KB) 格式。您需要将这些内容分开,并对 JavaScript 对象签名和加密 (JOSE) 标头和载荷进行解码(例如,使用 Node.js 的 jose)。
如果 example.com 验证 demo@gmail.com,则解码后的载荷类似于以下示例:
{
"evtJwtDecodedPayload": {
"cnf": {
"jwk": {
"crv": "Ed25519",
"kty": "OKP",
"x": "pUbLiCkEy123pUbLiCkEy123pUbLiCkEy123"
}
},
"email": "demo@gmail.com",
"email_verified": true,
"iat": 1782911685,
"iss": "https://accounts.google.com"
},
"kbJwtDecodedPayload": {
"aud": "https://example.com",
"iat": 1782911685,
"nonce": "rAnDoM123rAnDoM123rAnDoM123rAnDoM123",
"sd_hash": "hAsH456hAsH456hAsH456hAsH456hAsH456"
}
}
2. 验证预期值
检查载荷中的基本值是否与您提供的值一致:
- 验证
email_verified是否设置为true。 - 验证
email是否与您在表单中提供的电子邮件地址一致。 - 验证
nonce是否与表单中提供的随机数一致。 - 验证
aud是否与您网站的来源一致。 - 验证
iat是否具有相对较新的时间戳,例如,在表单呈现之后。
3. 验证密钥绑定
浏览器为交易创建一个临时密钥,以确认其已对令牌进行签名。从 EVT 中的 cnf(确认)声明提取此密钥,然后使用该密钥验证与密钥绑定的 JWT。
然后,计算预期哈希值并将其与 sd_hash 声明进行比较。以下 Node.js 示例展示了如何执行此计算:
const calculatedHash = createHash("sha256")
.update(evtJwt + "~")
.digest("base64url");
4. 验证 DNS 记录
验证电子邮件地址网域的 _email-verification DNS 记录。例如,对于 demo@gmail.com,查询 _email-verification.gmail.com TXT 记录。对于此提供程序,查询会返回账号提供程序的位置,即 accounts.google.com。
$ dig +short TXT _email-verification.gmail.com
"iss=accounts.google.com"
5. 发现签发者并验证 EVT 签名
确保发布者提供 /.well-known/email-verification 资源,该资源提供用于发布令牌的端点、网站的 JSON Web 密钥 (JWK) 以及支持的签名算法。
$ curl https://accounts.google.com/.well-known/email-verification
{
"issuance_endpoint": "https://accounts.google.com/gsi/email-verification/issue",
"jwks_uri": "https://verifiablecredentials-pa.googleapis.com/.well-known/vc-public-jwks",
"signing_alg_values_supported": ["EdDSA"]
}
使用 JWK 验证从令牌中提取的 EVT JWT。大多数 JOSE 库都提供用于处理此验证的函数。
如果所有五个步骤都成功完成,则表示您已针对提供商验证了电子邮件地址。如果不是,请按照正常流程向用户发送确认电子邮件。
实现电子邮件提供方和签发者服务
如需了解更多详情,您可以浏览电子邮件提供商模拟演示代码,并参阅电子邮件验证 API 和电子邮件验证协议提案中的身份验证方步骤。
作为发行方,您无需注册源试用或提供令牌,因为浏览器行为是由信赖方网站触发的。您只需确保设置了预期的端点来响应这些请求。
配置提供方发现
如需允许浏览器在选择属于您网域的电子邮件地址时自动发现您的验证端点,请使用 DNS 和 .well-known HTTP 端点公开您的配置。
配置 DNS 委托记录
在您的电子邮件网域中配置 DNS TXT 记录,以将验证权限委托给您的签发者标识符。这些标识符可以使用相同的网域,具体取决于您的基础架构。
记录格式:_email-verification.<email-domain>
区域文件示例:
_email-verification.example.com IN TXT "iss=accounts.issuer.example"
托管 .well-known/email-verification 端点
在您的发布者网域上托管一个 JSON 元数据文件,该文件位于 /.well-known/ 路径下。
此文件概述了您的签发功能以及您的基础架构支持的加密签名算法。
端点:https://<issuer-domain>/.well-known/email-verification
示例响应:
{
"issuance_endpoint": "https://accounts.issuer.example/email-verification/issuance",
"jwks_uri": "https://accounts.issuer.example/.well-known/vc-public-jwks",
"signing_alg_values_supported": ["EdDSA", "ES256"]
}
托管 .well-known/web-identity 端点
您可能已实现的其他 .well-known JSON 资源,作为联合凭据 (FedCM) API 的一部分。这会提供指向您的账号端点和登录网址的链接。
端点:https://<domain>/.well-known/web-identity
示例响应:
{
"accounts_endpoint": "https://accounts.issuer.example/accounts",
"login_url": "https://accounts.issuer.example/login"
}
使用账号端点
FedCM API 中的 accounts 端点目前提供已登录账号的列表。以下示例展示了最简单的响应。如需了解详情,请参阅身份提供方实现指南。
端点:如 .well-known/web-identity 中所指定
以下是一个示例响应:
{
"accounts": [
{
"id": "demo-example",
"name": "Demo User",
"email": "demo@example.com",
"given_name": "Demo"
}
]
}
与登录状态 API 集成
用户需要与提供方建立有效的会话,并且您必须使用 Login Status API 向浏览器发出信号。
当用户成功登录或退出时,提供匹配的 HTTP 响应标头:
Set-Login: logged-in
Set-Login: logged-out
或者,在 Web 应用上下文中使用 JavaScript 更新状态:
navigator.login.setStatus("logged-in");
navigator.login.setStatus("logged-out");
处理签发请求
您的 issuance_endpoint 收到包含 request_token 的 application/x-www-form-urlencoded POST 请求。
以下部分展示了处理签发请求的完整流程。
1. 验证签发请求
解析并验证传入的浏览器载荷:
- 方法:
POST - 会话验证:验证随请求一起传输的用户第一方
session/authenticationCookie,以确保存在有效的授权身份情境。 - 参数验证:提取
request_token参数(由浏览器生成的已签名 JWT)。验证该令牌是否包含预期的临时公钥、目标电子邮件地址、正确的受众群体和有效的时间戳。
解码后的令牌应如下所示:
{
"decodedHeader": {
"alg": "ES256",
"typ": "JWT",
"jwk": {
"kty": "EC",
"crv": "P-256",
"x": "pUbLiCKeY123pUbLiCKeY123pUbLiCKeY123",
"y": "pUbLiCKeY456pUbLiCKeY456pUbLiCKeY456"
}
},
"decodedPayload": {
"iss": "https://accounts.issuer.example",
"sub": "demo@example.com",
"email": "demo@example.com",
"iat": 1780272000,
"exp": 1780272300
},
"signature": "SIGnatURE-123_SIGnatURE-123_SIGnatURE-123"
}
2. 使用令牌进行响应
成功验证会话令牌和请求令牌后,使用载荷生成已签名的选择性披露 JWT (SD-JWT):
{
"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
}
使用您的私钥和支持的算法对载荷进行签名。例如,在 Node.js 中使用 jose:
const evtJwt = await new SignJWT(evtPayload)
.setProtectedHeader({
alg: "EdDSA",
kid: PRIVATE_KEY_JWK.kid, // Key ID corresponding to our JWKS keys
typ: "evt+jwt", // Standard Token Type for EVTs
})
.sign(privateKey);
// Standard SD-JWT compatibility requires appending a trailing tilde "~"
// to separate the signed token from the key binding section.
const issuanceToken = `${evtJwt}~`;
成功响应示例 (HTTP 200):
{
"issuance_token": "tOkEn123tOkEn123tOkEn123...~"
}
源试用注意事项
源试用是一项旨在收集反馈的实验,因此如果您以信赖方或身份提供方的身份参与,您的意见至关重要。如需报告问题,请使用以下 GitHub 代码库:
- 浏览器电子邮件验证 API:WICG/email-verification
- 电子邮件验证协议:dickhardt/email-verification
如果您在 Chrome 实现中遇到 bug,请针对以下组件提交 bug:
您可以通过包含 OT 令牌来控制是否在每个响应中启用源试用功能。这意味着,如果您希望仅向部分用户提供此功能,则可以进行精细控制。例如,如果您已有 A/B 测试框架,则可以在其中集成源试用,以实现受控实验人群。或者,如果您有一组参与 Beta 版测试或抢先预览的用户,您可能需要为他们启用此功能。在这种情况下,在签发或验证令牌之前,请对照所提供的电子邮件地址进行检查。
源试用还设置了流量限制,以尽量减少在发布之前依赖该功能的网站数量。签发者 API 正在开发中,您应了解,随着 Chrome 用户体验的更新,该 API 会发生向后不兼容的变更。
随着开发工作的推进,我们会在本博客和 evp-announce@chromium.org 邮件列表中发布更多更新。