第三方认证
集成认证提供方(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 供审计——事件响应时需要。