diff --git a/backend/app/api/auth.py b/backend/app/api/auth.py index 19a114f..9da39ac 100644 --- a/backend/app/api/auth.py +++ b/backend/app/api/auth.py @@ -246,17 +246,18 @@ async def discord_auth_callback( db: DbSession, code: str = Query(..., description="Authorization code from Discord"), state: str = Query(..., description="State parameter for CSRF validation"), -) -> TokenResponse: +) -> RedirectResponse: """Handle Discord OAuth callback. Exchanges the authorization code for tokens and creates/fetches the user. + Redirects to the frontend with tokens in URL fragment. Args: code: Authorization code from Discord. state: State parameter for CSRF validation. Returns: - JWT access and refresh tokens. + Redirect to frontend with access and refresh tokens. Raises: HTTPException: 400 if state is invalid or OAuth fails. @@ -281,13 +282,22 @@ async def discord_auth_callback( await user_service.update_last_login(db, user) # Create tokens - return await _create_tokens_for_user(user.id) + tokens = await _create_tokens_for_user(user.id) + + # Redirect to frontend with tokens in URL fragment (not query params for security) + # Fragment is not sent to server, only accessible by frontend JavaScript + fragment = f"access_token={tokens.access_token}&refresh_token={tokens.refresh_token}&expires_in={tokens.expires_in}" + return RedirectResponse( + url=f"{redirect_uri}#{fragment}", + status_code=status.HTTP_302_FOUND, + ) except DiscordOAuthError as e: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail=f"Discord OAuth failed: {e}", - ) from None + # Redirect to frontend with error + return RedirectResponse( + url=f"{redirect_uri}?error=oauth_failed&message={e}", + status_code=status.HTTP_302_FOUND, + ) # ============================================================================= diff --git a/backend/app/api/deps.py b/backend/app/api/deps.py index 3980b61..743afae 100644 --- a/backend/app/api/deps.py +++ b/backend/app/api/deps.py @@ -26,7 +26,7 @@ from fastapi import Depends, HTTPException, status from fastapi.security import OAuth2PasswordBearer from sqlalchemy.ext.asyncio import AsyncSession -from app.db import get_session_dependency +from app.db import get_session from app.db.models import User from app.services.jwt_service import verify_access_token from app.services.user_service import user_service @@ -55,7 +55,7 @@ async def get_db() -> AsyncSession: async def get_items(db: AsyncSession = Depends(get_db)): ... """ - async with get_session_dependency() as session: + async with get_session() as session: yield session