You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用requests-oauthlib获取API令牌失败,手动请求正常

OAuth2授权码模式:requests-oauthlib触发AccessDeniedError的排查提示

问题背景

使用requests-oauthlib实现OAuth2授权码流程时,跳转至授权页后回调触发oauthlib.oauth2.rfc6749.errors.AccessDeniedError: (access_denied)错误,但手动构造HTTP请求实现相同逻辑可成功获取令牌。

requests-oauthlib实现代码(报错版本)

from flask import Flask, request, redirect
from requests_oauthlib import OAuth2Session

# 配置常量(省略)
app = Flask(__name__)
app.config["SECRET_KEY"] = SECRET_KEY

@app.route("/login/")
def login():
    api_session = OAuth2Session(API_CLIENT_ID, redirect_uri=API_REDIRECT_URI)
    authorization_url, state = api_session.authorization_url(API_AUTHORIZATION_BASE_URL)
    session["oauth_state"] = state
    return redirect(authorization_url)

@app.route("/callback/")
def callback():
    api_session = OAuth2Session(API_CLIENT_ID, state=session["oauth_state"])
    token = api_session.fetch_token(
        API_TOKEN_URL,
        client_secret=API_CLIENT_SECRET,
        authorization_response=request.url,
        include_client_id=True,
        code=request.args.get("code"),
    )
    return "Tokens received."

手动请求实现代码(成功版本)

import requests
from flask import Flask, request, redirect

# 配置常量(省略)
app = Flask(__name__)
app.config["SECRET_KEY"] = SECRET_KEY

@app.route("/login/")
def login():
    return redirect(
        f"{API_AUTHORIZATION_BASE_URL}?client_id={API_CLIENT_ID}&redirect_uri={API_REDIRECT_URI}&response_type=code"
    )

@app.route("/callback/")
def callback():
    data = {
        "client_secret": API_CLIENT_SECRET,
        "grant_type": "authorization_code",
        "code": request.args.get("code"),
        "client_id": API_CLIENT_ID,
        "redirect_uri": API_REDIRECT_URI,
    }
    response = requests.post(API_TOKEN_URL, data=data)
    return "Tokens received."

排查方向

  • 对比授权请求URL参数差异
    requests-oauthlib的authorization_url()默认会自动添加state参数,若初始化时未指定scope还会附加空值的scope=参数。部分不符合OAuth2规范的第三方API会拒绝包含未约定参数的请求。
    操作:打印authorization_url的值,和手动构造的URL对比,若存在额外参数,可通过以下方式移除:

    # 初始化时禁用state
    api_session = OAuth2Session(API_CLIENT_ID, redirect_uri=API_REDIRECT_URI, state=None)
    # 或者指定空scope(如果API不需要scope)
    authorization_url, state = api_session.authorization_url(API_AUTHORIZATION_BASE_URL, scope='')
    
  • 修正fetch_token参数传递
    代码中同时传入了authorization_response=request.url和code=request.args.get("code"),前者会自动从回调URL中提取code,重复传递会导致参数冲突。
    操作:移除code=request.args.get("code")参数,仅保留authorization_response=request.url。

  • 检查令牌请求的编码与头信息
    requests-oauthlib的fetch_token请求可能与手动请求的Content-Type、编码方式存在差异。
    操作:开启requests-oauthlib的debug日志,或用抓包工具对比两次令牌请求的参数、请求头、编码格式,确保与手动请求一致。

  • 验证redirect_uri的编码一致性
    requests-oauthlib会自动对redirect_uri进行URL编码,若第三方API仅识别未编码或特定编码格式的URI,会导致授权失败。
    操作:对比authorization_url中的redirect_uri部分与手动构造的URI,确保编码格式一致。

内容的提问来源于stack exchange,提问作者Ralf Zosel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 23:47:47