From 130bcdcdc1456725d03411e0741530d80e7d840f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=92=95=E8=B0=B7=E9=85=B1?= Date: Sat, 26 Jul 2025 23:48:48 +0800 Subject: [PATCH] Add OAuth error return interface format --- app/models/oauth.py | 7 ++++++ app/router/auth.py | 56 +++++++++++++++++++++++++++++++++++++-------- 2 files changed, 54 insertions(+), 9 deletions(-) diff --git a/app/models/oauth.py b/app/models/oauth.py index ae4f903..3776bdd 100644 --- a/app/models/oauth.py +++ b/app/models/oauth.py @@ -27,3 +27,10 @@ class UserCreate(BaseModel): password: str email: str country_code: str = "CN" + + +class OAuthErrorResponse(BaseModel): + error: str + error_description: str + hint: str + message: str diff --git a/app/router/auth.py b/app/router/auth.py index 0a18646..9c7df98 100644 --- a/app/router/auth.py +++ b/app/router/auth.py @@ -11,11 +11,26 @@ from app.auth import ( ) from app.config import settings from app.dependencies import get_db -from app.models.oauth import TokenResponse +from app.models.oauth import TokenResponse, OAuthErrorResponse -from fastapi import APIRouter, Depends, Form, HTTPException +from fastapi import APIRouter, Depends, Form +from fastapi.responses import JSONResponse from sqlmodel.ext.asyncio.session import AsyncSession + +def create_oauth_error_response(error: str, description: str, hint: str, status_code: int = 400): + """创建标准的 OAuth 错误响应""" + error_data = OAuthErrorResponse( + error=error, + error_description=description, + hint=hint, + message=description + ) + return JSONResponse( + status_code=status_code, + content=error_data.model_dump() + ) + router = APIRouter(tags=["osu! OAuth 认证"]) @@ -36,19 +51,30 @@ async def oauth_token( client_id != settings.OSU_CLIENT_ID or client_secret != settings.OSU_CLIENT_SECRET ): - raise HTTPException(status_code=401, detail="Invalid client credentials") + return create_oauth_error_response( + error="invalid_client", + description="Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method).", + hint="Invalid client credentials", + status_code=401 + ) if grant_type == "password": # 密码授权流程 if not username or not password: - raise HTTPException( - status_code=400, detail="Username and password required" + return create_oauth_error_response( + error="invalid_request", + description="The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed.", + hint="Username and password required" ) # 验证用户 user = await authenticate_user(db, username, password) if not user: - raise HTTPException(status_code=401, detail="Invalid username or password") + return create_oauth_error_response( + error="invalid_grant", + description="The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.", + hint="Incorrect sign in" + ) # 生成令牌 access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES) @@ -77,12 +103,20 @@ async def oauth_token( elif grant_type == "refresh_token": # 刷新令牌流程 if not refresh_token: - raise HTTPException(status_code=400, detail="Refresh token required") + return create_oauth_error_response( + error="invalid_request", + description="The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed.", + hint="Refresh token required" + ) # 验证刷新令牌 token_record = await get_token_by_refresh_token(db, refresh_token) if not token_record: - raise HTTPException(status_code=401, detail="Invalid refresh token") + return create_oauth_error_response( + error="invalid_grant", + description="The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.", + hint="Invalid refresh token" + ) # 生成新的访问令牌 access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES) @@ -109,4 +143,8 @@ async def oauth_token( ) else: - raise HTTPException(status_code=400, detail="Unsupported grant type") + return create_oauth_error_response( + error="unsupported_grant_type", + description="The authorization grant type is not supported by the authorization server.", + hint="Unsupported grant type" + )