FastAPI对接Redhat SSO(Keycloak) OIDC授权码模式遇问题求助
FastAPI 与 Keycloak(Redhat SSO)授权码模式认证问题解决方案
问题描述
使用fastapi_third_party_auth库实现FastAPI与Keycloak的OIDC认证时,隐式授权模式可正常运行,但切换到authorization_code模式后出现以下问题:
- 首次报错:
Missing parameter: code_challenge_method - 尝试禁用PKCE后,新报错:
{"error":"invalid_request","error_description":"Missing parameter: username"}
原代码如下:
import uvicorn from fastapi import Depends from fastapi import FastAPI from fastapi import Security from fastapi import status from fastapi.middleware.cors import CORSMiddleware from starlette.responses import RedirectResponse from fastapi_third_party_auth import Auth from fastapi_third_party_auth import KeycloakIDToken auth = Auth( openid_connect_url="https://XXXXX/auth/realms/Sandbox/.well-known/openid-configuration", issuer="https://XXXX/auth/realms/Sandbox", # optional, verification only client_id="devops-tool", # optional, verification only #scopes=["email", "openid"], # optional, verification only #grant_types=[GrantType.IMPLICIT], # optional, docs only grant_types=["authorization_code"], #grant_types=["client_credentials"] #idtoken_model=KeycloakIDToken, # optional, verification only ) app = FastAPI( title="Example", version="dev", dependencies=[Depends(auth)], ) # CORS errors instead of seeing internal exceptions # https://stackoverflow.com/questions/63606055/why-do-i-get-cors-error-reason-cors-request-did-not-succeed #cors = CORSMiddleware( # app=app, # allow_origins=["*"], # allow_credentials=True, # allow_methods=["*"], # allow_headers=["*"], #) @app.get("/", status_code=status.HTTP_303_SEE_OTHER) def redirect_to_docs(): return RedirectResponse(url="/docs") @app.get("/protected") def protected(id_token: KeycloakIDToken = Security(auth.required)): return dict(message=f"You are {id_token}")
解决方案
1. 解决Missing parameter: code_challenge_method错误
该错误源于PKCE(Proof Key for Code Exchange)的强制要求,尤其是当Keycloak客户端为**公共类型(Public)**时,PKCE是授权码模式的必需项,不能禁用。处理步骤:
- 升级
fastapi_third_party_auth库:确保使用最新版本,旧版本可能不支持自动生成PKCE参数(code_challenge和code_challenge_method)。 - 确认Keycloak客户端配置:
- 登录Keycloak管理控制台,进入目标客户端
devops-tool的Settings标签页。 - 将
Access Type设置为public(若为后端服务可设为confidential,但需额外配置客户端密钥)。 - 确保
Standard Flow Enabled(授权码流)处于开启状态。
- 登录Keycloak管理控制台,进入目标客户端
2. 解决Missing parameter: username错误
该错误说明请求未走标准授权码流程(跳转登录页→获取授权码→换取令牌),而是直接向令牌端点发送请求。处理步骤:
- 移除全局认证依赖:将FastApp初始化时的
dependencies=[Depends(auth)]删除,避免在回调路由和文档路由触发认证拦截,导致流程异常。 - 配置回调地址:在
Auth实例中添加redirect_uri参数,并确保该地址已加入Keycloak客户端的Valid Redirect URIs列表:auth = Auth( openid_connect_url="https://XXXXX/auth/realms/Sandbox/.well-known/openid-configuration", issuer="https://XXXX/auth/realms/Sandbox", client_id="devops-tool", grant_types=["authorization_code"], redirect_uri="http://localhost:8000/auth/callback", # 根据实际部署地址调整 ) - 验证Keycloak客户端的重定向URI:在Keycloak客户端
Settings标签页的Valid Redirect URIs中添加上述redirect_uri(支持通配符,如http://localhost:8000/*)。 - (可选)若为Confidential客户端:需在
Auth配置中添加client_secret参数,该密钥可从Keycloak客户端的Credentials标签页获取。
修改后的完整代码示例
import uvicorn from fastapi import FastAPI from fastapi import Security from fastapi import status from starlette.responses import RedirectResponse from fastapi_third_party_auth import Auth from fastapi_third_party_auth import KeycloakIDToken auth = Auth( openid_connect_url="https://XXXXX/auth/realms/Sandbox/.well-known/openid-configuration", issuer="https://XXXX/auth/realms/Sandbox", client_id="devops-tool", grant_types=["authorization_code"], redirect_uri="http://localhost:8000/auth/callback", # 调整为实际地址 ) app = FastAPI( title="Example", version="dev", # 移除全局认证依赖 ) @app.get("/", status_code=status.HTTP_303_SEE_OTHER) def redirect_to_docs(): return RedirectResponse(url="/docs") @app.get("/protected") def protected(id_token: KeycloakIDToken = Security(auth.required)): return dict(message=f"You are {id_token}") if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=8000)
测试流程
- 启动FastAPI服务,访问
http://localhost:8000/protected。 - 页面会自动跳转到Keycloak的登录界面,输入合法账号密码后完成登录。
- 登录成功后会自动跳转回
/protected,并返回包含ID Token的响应。
内容的提问来源于stack exchange,提问作者ruben
相关产品推荐
相关产品推荐

