Skip to content
返回构建教程

第三方认证

集成认证提供方(Auth0、Clerk、NextAuth)而非自研会话加密。

为什么

认证是自研 bug 变成全面账户接管头条的那个功能。托管提供方处理密码哈希、会话轮换、MFA 和泄露检测——正确实现要 200+ 小时,不安全实现只要 5 分钟。提供方还吸收合规负担(SOC2、GDPR)的认证面,这是审计员最细查的部分。代价是按用户收费,但比一个安全工程师的工资便宜,远比泄露披露便宜。

何时使用

第一个需要登录的用户路由之前。视提供方 1-3 小时。仅对完全公开只读 app 可跳过。

如何做

1. 按需求选提供方,非热度
   - Clerk:最快搭建、最佳 DX,10k MAU 起付费
   - Auth0:企业功能、SSO,7k MAU 起付费
   - NextAuth/Auth.js:免费、自托管、你拥有 DB

2. 为每个环境配置提供方重定向 URL
   - localhost:3000、staging.example.com、example.com——永不用通配符
   - client secret 存平台密钥存储,非 .env

3. 用中间件/会话检查包装受保护路由
   - Next.js:middleware.ts 检查会话 cookie,无则重定向 /login
   - 服务端组件:getServerSession()——永不信任客户端认证状态

4. 用提供方 metadata 实现角色访问,非本地 DB
   - 角色在提供方 user app_metadata;应用每次请求读它
   - 这让角色在登出/登录间一致,且你的 DB 恢复后存活

5. 测试全流程:注册→邮箱验证→登录→登出→密码重置
   - CI 里用一次性邮箱(Mailinator)跑 e2e 测试

示例

// middleware.ts(Next.js + Clerk)
import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";
const isProtected = createRouteMatcher(["/dashboard(.*)", "/api/admin(.*)"]);
export default clerkMiddleware((auth, req) => {
  if (isProtected(req)) auth().protect();
});

// 服务端组件——服务端读会话
import { auth, currentUser } from "@clerk/nextjs";
export default async function Dashboard() {
  const { userId } = auth();
  if (!userId) redirect("/login");
  const user = await currentUser();
  return <h1>欢迎 {user?.firstName}</h1>;
}

常见问题

  • ⚠仅客户端检查认证状态,用户在 DevTools 翻布尔值即看管理面板。
  • ⚠用通配符重定向 URL(localhost:*),攻击者在恶意端口截获认证码。
  • ⚠角色存本地 DB 与提供方失同步,删除用户仍有访问。
  • ⚠不测登出流程,会话残留,共享计算机保持登录数天。

最佳实践

  • ✓总在服务端调会话检查——客户端认证状态用于 UI 提示,非访问控制。
  • ✓每 90 天轮换提供方 client secret,存密钥管理器非仓库。
  • ✓启用提供方异常检测(不可能旅行、暴力破解)——免费且抓撞库。
  • ✓记认证事件(登录、登出、失败)含 IP 和 user-agent 供审计——事件响应时需要。