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
相关产品推荐
相关产品推荐

