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

FastAPI Azure Auth跨域授权码兑换需PKCE问题排查求助

解决FastAPI Azure Auth的AADSTS9002325错误问题

问题描述

完全按照官方示例实现FastAPI Azure Auth功能,却触发错误:
AADSTS9002325: Proof Key for Code Exchange is required for cross-origin authorization code redemption
已确认FastAPI应用注册的重定向URI一致,求解决或排查思路。

排查与解决思路

  • 修复Swagger UI的PKCE配置丢失问题:你的代码中先初始化了带swagger_ui_init_oauth配置的app,但随后又执行app = FastAPI()覆盖了原有实例,导致PKCE配置未生效。必须保留包含OAuth配置的FastAPI初始化代码。
  • 确认Azure应用注册的重定向URI类型:将重定向URI设置为**单页应用(SPA)**类型,而非"Web"类型。Web类型会要求客户端密钥,而PKCE是SPA跨域场景的标准验证方案,类型不匹配会触发该错误。
  • 验证CORS配置完整性:确保BACKEND_CORS_ORIGINS包含实际访问的域名(如Swagger UI的http://localhost:8000),且allow_credentials设为True,跨域场景下必须允许凭证传递。
  • 检查Scope参数格式:SingleTenantAzureAuthorizationCodeBearer的scopes参数需传入正确的键值对格式,比如{"api://<客户端ID>/user_impersonation": "user_impersonation"},错误的Scope会导致授权流程异常。
  • 调整初始化顺序:azure_scheme需在lifespan之前初始化,否则启动时加载OpenID配置会报错;同时要将lifespan关联到正确的app实例。

修正后的代码示例

from fastapi import FastAPI, Security
from fastapi.middleware.cors import CORSMiddleware
from fastapi_azure_auth import SingleTenantAzureAuthorizationCodeBearer
from pydantic import AnyHttpUrl, computed_field
from pydantic_settings import BaseSettings
from contextlib import asynccontextmanager
from typing import AsyncGenerator
import uvicorn


class Settings(BaseSettings):
    BACKEND_CORS_ORIGINS: list[str | AnyHttpUrl] = ['http://localhost:8000']
    APP_CLIENT_ID: str = ""  # 替换为你的应用客户端ID
    TENANT_ID: str = ""      # 替换为你的租户ID
    API_CLIENT_ID: str = ""  # 替换为你的API客户端ID
    SCOPE_DESCRIPTION: str = "user_impersonation"

    @computed_field
    @property
    def SCOPE_NAME(self) -> str:
        return f'api://{self.API_CLIENT_ID}/{self.SCOPE_DESCRIPTION}'

    @computed_field
    @property
    def SCOPES(self) -> dict:
        return {
            self.SCOPE_NAME: self.SCOPE_DESCRIPTION,
        }

    class Config:
        env_file = '.env'
        env_file_encoding = 'utf-8'
        case_sensitive = True


settings = Settings()

# 先初始化认证方案,确保生命周期钩子能引用
azure_scheme = SingleTenantAzureAuthorizationCodeBearer(
    app_client_id=settings.APP_CLIENT_ID,
    tenant_id=settings.TENANT_ID,
    scopes=settings.SCOPES,
)


@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
    """启动时预加载OpenID配置"""
    await azure_scheme.openid_config.load_config()
    yield


# 初始化FastAPI应用,保留Swagger OAuth配置并关联生命周期钩子
app = FastAPI(
    swagger_ui_oauth2_redirect_url='/oauth2-redirect',
    swagger_ui_init_oauth={
        'usePkceWithAuthorizationCodeGrant': True,
        'clientId': settings.APP_CLIENT_ID,
    },
    lifespan=lifespan
)

# 配置CORS中间件
if settings.BACKEND_CORS_ORIGINS:
    app.add_middleware(
        CORSMiddleware,
        allow_origins=[str(origin) for origin in settings.BACKEND_CORS_ORIGINS],
        allow_credentials=True,
        allow_methods=['*'],
        allow_headers=['*'],
    )


@app.get("/", dependencies=[Security(azure_scheme)])
async def root():
    return {"whoIsTheBest": "xxTeam is"}


if __name__ == '__main__':
    uvicorn.run('main:app', reload=True)

内容的提问来源于stack exchange,提问作者Thomas Segato

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 19:54:50