为什么 Auth0 email_verified 在我的 Access Token 中缺失
发布: (2026年2月19日 GMT+8 09:35)
4 分钟阅读
原文: Dev.to
Source: Dev.to
问题
Spring Boot 过滤器在用户已在 Auth0 仪表盘标记为 Verified 的情况下仍然拒绝了他们。
Boolean emailVerified = jwt.getClaimAsBoolean("email_verified");
if (emailVerified == null || !emailVerified) {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.getWriter().write("{\"error\":\"Email not verified\"}");
return;
}
日志输出:
User email not verified: sub=auth0|67c6a695657d0f4f7ac8736f
这些用户 已经 验证过了。原因是:Auth0 默认 不 在访问令牌中包含 email_verified——只在 ID 令牌中包含。
在解码访问令牌时,你可能会看到:
{
"iss": "https://your-tenant.auth0.com/",
"sub": "auth0|67c6a695657d0f4f7ac8736f",
"aud": ["https://your-api/"],
"iat": 1767421775,
"exp": 1767508175,
"scope": "openid profile email"
}
请注意缺少 email_verified 声明,即使已经请求了 email 范围。
Source: …
将 email_verified 添加到访问令牌
使用登录后操作
- 在 Auth0 仪表盘中进入 Actions → Flows → Login。
- 添加一个新 Action 并粘贴以下代码:
exports.onExecutePostLogin = async (event, api) => {
// Add email_verified to access token for API validation
api.accessToken.setCustomClaim('email_verified', event.user.email_verified);
};
- 部署该 Action 并将其拖入 Login 流程。
部署后,访问令牌将包含:
{
"email_verified": true,
"iss": "https://your-tenant.auth0.com/",
"sub": "auth0|67c6a695657d0f4f7ac8736f",
...
}
为什么这样做有效
- ID 令牌 用于客户端(前端)传递身份信息。
- 访问令牌 用于 API(后端)授权请求。
- Auth0 的默认理念是将身份相关的声明从访问令牌中剔除,但许多 API 需要这些信息。通过 Action 添加声明是推荐的做法。
替代方案:查询 UserInfo 端点
如果无法使用 Actions,当缺少该声明时,您可以从 /userinfo 端点获取该声明:
if (emailVerified == null) {
String userinfoUrl = auth0Issuer + "userinfo";
// Perform GET with Bearer token
// Parse JSON response for "email_verified"
}
注意: 这会为每个请求增加额外的延迟,因此通常更倾向于使用 Action 方法。
社交登录
对于社交连接(Google、GitHub 等),Auth0 会自动设置 email_verified: true,因为提供者已经验证了电子邮件。您可以跳过对非数据库连接的检查:
String subject = jwt.getSubject();
boolean isDatabaseConnection = subject != null && subject.startsWith("auth0|");
if (isDatabaseConnection) {
// Only check email_verified for username/password users
}
完整操作示例(包括可选的电子邮件重新发送)
exports.onExecutePostLogin = async (event, api) => {
// Always include email_verified in access token
api.accessToken.setCustomClaim('email_verified', event.user.email_verified);
// Auto‑resend verification email for unverified users (rate‑limited)
if (!event.user.email_verified) {
const lastSent = event.user.user_metadata?.verification_email_last_sent;
const now = Date.now();
const ONE_HOUR = 60 * 60 * 1000;
if (!lastSent || (now - lastSent) > ONE_HOUR) {
// Trigger verification email via Management API
// (see Auth0 docs for Management API setup)
api.user.setUserMetadata('verification_email_last_sent', now);
}
}
};
要点
- ID 令牌 ≠ Access 令牌 – 它们的用途不同,包含的声明也不同。
- 使用 Auth0 Actions 向访问令牌添加所需的自定义声明。
- 验证实际的令牌内容(例如使用 jwt.io),而不要仅仅依赖仪表板 UI。