集成 Supabase Auth 到 FastAPI 的三个坑
· 阅读需 4 分钟
在为客户构建 SaaS 认证系统时遇到此问题,记录根因与解法。
TL;DR
Supabase Auth + FastAPI 集成有三个常见坑:JWKS 路径不是标准路径、ES256 签名需转换为 DER 格式、用户首次登录时本地数据库无记录。本文提供完整解决方案。
问题现象
坑 1:JWKS 路径 404
GET https://xxx.supabase.co/.well-known/jwks.json
# 404 Not Found
所有 JWT 验证请求返回 401 Invalid Token。
坑 2:ES256 签名验证失败
from jose import jwt
payload = jwt.decode(token, key, algorithms=["ES256"])
# JWTError: Signature verification failed
明明公钥是对的,但签名验证总是失败。
坑 3:用户首次登录无本地记录
# 创建 Agent 时
agent = Agent(user_id=current_user["user_id"], ...)
db.add(agent)
# ForeignKeyViolation: user_id 不存在
Supabase Auth 用户通过了 JWT 验证,但本地 agent_users 表没有该用户记录。
根因
坑 1:Supabase 非标准 JWKS 路径
标准 OAuth/OIDC 服务器 JWKS 在 /.well-known/jwks.json,但 Supabase 把认证服务放在 /auth/v1/ 子路径下:
| 标准路径 | Supabase 路径 |
|---|---|
/.well-known/jwks.json | /auth/v1/.well-known/jwks.json |
坑 2:ES256 原始签名 vs DER 格式
Supabase JWT 使用 ES256(P-256 曲线)签名。JWT 中的签名是 raw 格式(r || s 拼接,64 字节),但 Python cryptography 库的 verify() 方法需要 DER-encoded ASN.1 格式。
Raw: r (32 bytes) || s (32 bytes) = 64 bytes
DER: 0x30 <len> 0x02 <r_len> <r> 0x02 <s_len> <s>
python-jose 的 jwt.decode() 在处理 ES256 时有兼容性问题,需要手动验证签名。
坑 3:认证与数据分离
Supabase Auth 是独立服务,用户注册/登录后只存在于 Supabase 的 auth.users 表。本地数据库的 agent_users 表需要手动同步。