oauth2 token换取失败主因是授权服务器对client_id、client_secret、redirect_uri和code四者一致性校验严格,尤其redirect_uri须完全匹配(含斜杠、协议、端口),且pkce、请求格式、响应处理、authorization头空格、scope权限、audience限制及redirect_uri白名单配置等均需精准符合要求。

拿到 authorization code 后,token_url 请求失败 401 或 400
绝大多数卡在第一步 token 换取环节,不是代码写错,而是授权服务器对 client_id、client_secret、redirect_uri 和 code 四者一致性校验极严。
-
redirect_uri必须和申请code时传的一模一样(包括末尾斜杠、协议、端口),哪怕只是http://localhost:8000/callback和http://localhost:8000/callback/都算不匹配 - 部分平台(如 GitHub)要求
client_secret只能用在后端,前端 JS 直接调token_url会被拒绝——这不是 CORS 问题,是服务端主动拦截 - 有些 OAuth2 提供方(如 Microsoft Entra ID)默认关闭 PKCE,但若你在授权请求里加了
code_challenge,却没在 token 请求里带code_verifier,就会返回"invalid_grant" - 用
requests.post发 token 请求时,别把参数塞进 URL 或 body dict 里乱传;标准做法是用data传 form-url-encoded,且必须设headers={"Content-Type": "application/x-www-form-urlencoded"}
用 requests 手动发 token 请求,json() 报 JSONDecodeError
这不是 token 接口挂了,大概率是你没处理好响应体类型。OAuth2 的 token 响应本该是 JSON,但出错时很多 provider 返回 HTML 错误页(比如 404 页面)或纯文本错误信息,response.json() 一读就崩。
- 永远先检查
response.status_code,非 200 就别急着.json() - 出错时打印
response.headers.get("Content-Type")和response.text[:200],一眼能看出是不是 HTML 或 plain text - 正确写法:
if response.status_code == 200: token_data = response.json() else: print("Token request failed:", response.status_code, response.text)
拿到 access_token 后调 API 总是 401 Unauthorized
token 没过期、格式看着也对,但 API 就是不认——问题常出在 Authorization 头的拼写、空格或 scope 权限上。
- Header 必须是
Authorization: Bearer <token></token>,注意Bearer后面**有一个空格**,少这个空格就是 401 - 确认 token 对应的
scope包含你要访问的 API 权限(例如 GitHub 的user:email才能读邮箱,只申请了read:user就不行) - 某些平台(如 Google)的 token 有“受众”(
audience)限制,比如你用一个面向https://www.googleapis.com的 token 去调https://oauth2.googleapis.com,也会被拒 - 别把
refresh_token当access_token用——虽然都是长字符串,但用途完全不同
本地开发时 http://localhost 被拒绝,提示 “redirect_uri not allowed”
不是你的代码有问题,是 OAuth2 提供方后台没配白名单。几乎所有主流平台都强制要求提前注册合法的 redirect_uri,且不允许通配符。
立即学习“Python免费学习笔记(深入)”;
- GitHub:进 Settings → Developer settings → OAuth Apps → Edit → “Authorization callback URL”,填死
http://localhost:8000/callback - Google Cloud Console:API & Services → Credentials → Edit OAuth client → “Authorized redirect URIs”,一行一个,不能写
http://localhost:*/callback - 如果用
127.0.0.1替代localhost,要连同注册的 URI 一起换,二者不等价 - 开发中临时绕过?别试。有些平台允许
urn:ietf:wg:oauth:2.0:oob(out-of-band),但已逐步弃用,且不适用于 Web 流程
redirect_uri 多一个斜杠、Authorization 头少一个空格、后台没点保存按钮,都会让整个 flow 在某个看似无关的点上静默失败。










