oauth 2.0 仅负责授权,openid connect(oidc)才是基于其扩展的认证协议,必须显式添加 openid scope 才能获取 id_token 并完成用户身份验证。

OAuth 2.0 和 OpenID Connect 不是同一个东西,别混着配
很多人一上来就照抄 oauthlib 或 authlib 的 OAuth 示例,结果发现拿不到用户邮箱、无法验证身份——因为 OAuth 2.0 只管授权(access token),不负责认证;OpenID Connect(OIDC)才是基于 OAuth 2.0 扩展出来的认证协议,必须显式启用 openid scope 才能拿到 id_token。
常见错误现象:token_response 里只有 access_token,没有 id_token;调用 /userinfo 接口返回 401 或 “invalid_scope”。
- 必须在 authorization request 中带上
scope=openid%20profile%20email(URL 编码后) - 必须指定
response_type=code(推荐)或code id_token,不能只用token - OIDC Provider(如 Auth0、Okta、Google)的
.well-known/openid-configuration必须可访问,客户端要从中读取jwks_uri、issuer、userinfo_endpoint - 验签
id_token时,要用 Provider 提供的 JWKS(不是你自己生成的密钥),且必须校验iss、aud、exp、iat
用 authlib 配 OIDC 时,register_client 的参数容易写错
authlib 的 OAuth.register 看似简单,但几个关键字段一旦填错,就会导致重定向失败、token 解析报错或签名验证不过。
使用场景:Django/Flask 中集成 Google 或 Keycloak 登录。
立即学习“Python免费学习笔记(深入)”;
-
client_id和client_secret是你在 OIDC Provider 后台创建应用时分配的,不是自己随便写的字符串 -
server_metadata_url必须是完整 URL,例如https://accounts.google.com/.well-known/openid-configuration,少个/或协议头都会加载失败 -
client_kwargs里要明确写{"scope": "openid profile email"},不能依赖全局默认值 - 如果 Provider 要求 PKCE(比如 GitHub OIDC),得额外加
code_challenge_method="S256",否则authorization_code流会卡在 callback
id_token 解析失败?先看 JWT header 和 signature 验证逻辑
拿到 id_token 后直接 jwt.decode(...) 很危险——多数人漏掉 issuer 校验、audience 校验,或者用错了公钥。
性能影响:每次请求都远程拉一次 JWKS(jwks_uri)很慢,应该缓存并定期刷新;本地硬编码公钥则无法应对 Provider 密钥轮换。
- 别用
PyJWT直接decode,优先用authlib的IdToken类或jwt.decode配合algorithms=["RS256"]和key参数 -
key必须是从jwks_uri拿到的对应kid的 RSA 公钥,不是整个 JWKS JSON - 必须传
issuer(Provider 的 issuer 字符串,比如https://google.com)和audience(你的client_id),否则验签通过也可能是伪造 token - 出错时常见提示:
InvalidAlgorithmError(算法不匹配)、InvalidAudienceError(aud不对)、InvalidIssuerError(iss域不一致)
Flask/Django 里 session 存 token 容易被忽略的兼容性问题
把 access_token 或 id_token 存进 session 看似没问题,但实际部署时经常出现 token 丢失、过期不刷新、跨域失效等问题。
兼容性影响:某些 OIDC Provider(如 Azure AD)返回的 access_token 是 opaque token,不能解析,也不能当认证凭证复用;而 id_token 有固定有效期(通常 1 小时),不能长期存 session。
- 不要把原始
id_token当长期登录凭证——它只用于首次登录认证,后续应靠 session + server-side state 维持登录态 - 如果需要调用下游 API,
access_token应按需获取、即用即弃;长期保存需加密且设短过期时间(比如 15 分钟) - Flask 的
session默认用 signed cookie,大小有限(~4KB),id_token本身可能就 2KB+,加上其他字段容易溢出,触发 silent fail - Django 用户注意:
SESSION_COOKIE_SAMESITE设为Lax或Strict时,跨站 redirect 回 callback URL 可能导致 session 丢失,需配合CSRF_TRUSTED_ORIGINS










