from fastapi import APIRouter, Request, HTTPException from authlib.integrations.starlette_client import OAuth from app.core.config import settings router = APIRouter() # Initialize OAuth oauth = OAuth() # Register Microsoft Provider # We use the tenant-specific endpoint for better security and internal organization CONF_URL = f"https://login.microsoftonline.com/{settings.MS_TENANT_ID}/v2.0/.well-known/openid-configuration" oauth.register( name='microsoft', client_id=settings.MS_CLIENT_ID, client_secret=settings.MS_CLIENT_SECRET, server_metadata_url=CONF_URL, client_kwargs={ 'scope': 'openid email profile User.Read' } ) @router.get("/login") async def login(request: Request): """ Redirects the user to the Microsoft Login page. """ redirect_uri = settings.MS_REDIRECT_URI return await oauth.microsoft.authorize_redirect(request, redirect_uri) @router.get("/callback") async def auth_callback(request: Request): """ Handles the callback from Microsoft after successful login. Exchanges the authorization code for an access token and user info. """ try: token = await oauth.microsoft.authorize_access_token(request) user = token.get('userinfo') if not user: # Sometimes userinfo is not directly in the token depending on claims, # allow fetch via userinfo endpoint if configured, or parse id_token user = await oauth.microsoft.userinfo(token=token) # Store user info in session (cookie) # In a real app, you might issue your own JWT here instead request.session['user'] = dict(user) return {"message": "Login successful", "user": user} except Exception as e: # Log the error in production raise HTTPException(status_code=400, detail=f"SSO Login Failed: {str(e)}") @router.get("/logout") async def logout(request: Request): """ Clears the local session. """ request.session.pop('user', None) return {"message": "Logged out successfully"}