"""OAuth2 authentication endpoints.""" from typing import Annotated from fastapi import APIRouter, Depends, HTTPException, Query, Response, status from fastapi.responses import RedirectResponse from app.core.config import settings from app.core.dependencies import get_auth_service, get_oauth_service from app.core.logging import get_logger from app.services.auth import AuthService from app.services.oauth import OAuthService router = APIRouter() logger = get_logger(__name__) @router.get("/{provider}/authorize") async def oauth_authorize( provider: str, oauth_service: Annotated[OAuthService, Depends(get_oauth_service)], ) -> dict[str, str]: """Get OAuth authorization URL.""" try: # Generate secure state parameter state = oauth_service.generate_state() # Get authorization URL auth_url = oauth_service.get_authorization_url(provider, state) except HTTPException: raise except Exception as e: logger.exception("OAuth authorization failed for provider: %s", provider) raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="OAuth authorization failed", ) from e else: return { "authorization_url": auth_url, "state": state, } @router.get("/{provider}/callback") async def oauth_callback( provider: str, response: Response, code: Annotated[str, Query()], oauth_service: Annotated[OAuthService, Depends(get_oauth_service)], auth_service: Annotated[AuthService, Depends(get_auth_service)], ) -> RedirectResponse: """Handle OAuth callback.""" try: # Handle OAuth callback and get user info oauth_user_info = await oauth_service.handle_callback(provider, code) # Perform OAuth login (link or create user) auth_response = await auth_service.oauth_login(oauth_user_info) # Create and store refresh token user = await auth_service.get_current_user(auth_response.user.id) refresh_token = await auth_service.create_and_store_refresh_token(user) # Set HTTP-only cookies for both tokens response.set_cookie( key="access_token", value=auth_response.token.access_token, max_age=auth_response.token.expires_in, httponly=True, secure=settings.COOKIE_SECURE, samesite=settings.COOKIE_SAMESITE, ) response.set_cookie( key="refresh_token", value=refresh_token, max_age=settings.JWT_REFRESH_TOKEN_EXPIRE_DAYS * 24 * 60 * 60, httponly=True, secure=settings.COOKIE_SECURE, samesite=settings.COOKIE_SAMESITE, ) logger.info( "OAuth login successful for user: %s via %s", auth_response.user.email, provider, ) # Redirect back to frontend after successful authentication return RedirectResponse( url="http://localhost:8001/?auth=success", status_code=302, ) except HTTPException: raise except Exception as e: logger.exception("OAuth callback failed for provider: %s", provider) raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="OAuth callback failed", ) from e @router.get("/providers") async def get_oauth_providers() -> dict[str, list[str]]: """Get list of available OAuth providers.""" return { "providers": ["google", "github"], }