- Frontend: Set MSAL log level to Info, add [MSAL] prefix - Frontend: Add [MSAL Auth] logs for token acquisition - Frontend: Add [MSAL Login] logs for login popup flow - Backend: Add [MSAL Backend] logs for token verification - Backend: Add [MSAL Backend] logs for auth dependency 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
74 lines
2.7 KiB
Python
Executable file
74 lines
2.7 KiB
Python
Executable file
"""
|
|
FastAPI authentication dependencies.
|
|
|
|
Provides dependency functions for securing REST endpoints with Azure AD token verification.
|
|
"""
|
|
import logging
|
|
from typing import Optional
|
|
from fastapi import Header, HTTPException, status
|
|
|
|
from app.config import settings
|
|
from app.services.auth_service import verify_access_token
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
async def get_current_user(authorization: Optional[str] = Header(None)) -> dict:
|
|
"""
|
|
FastAPI dependency to verify the access token and return user claims.
|
|
|
|
Use as a dependency on protected endpoints:
|
|
@app.get("/protected")
|
|
async def protected_route(user: dict = Depends(get_current_user)):
|
|
return {"message": f"Hello {user.get('name')}"}
|
|
|
|
Args:
|
|
authorization: The Authorization header value (Bearer <token>)
|
|
|
|
Returns:
|
|
The token claims dict containing user information
|
|
|
|
Raises:
|
|
HTTPException: 401 if token is missing or invalid
|
|
"""
|
|
logger.info("[MSAL Backend] get_current_user dependency called")
|
|
|
|
# If auth is disabled, return mock user immediately
|
|
if settings.DISABLE_AUTH:
|
|
logger.info("[MSAL Backend] Auth disabled - returning mock user")
|
|
return {"sub": "dev-user", "name": "Development User", "preferred_username": "dev@localhost"}
|
|
|
|
if not authorization:
|
|
logger.warning("[MSAL Backend] Missing authorization header")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail="Missing authorization header",
|
|
headers={"WWW-Authenticate": "Bearer"},
|
|
)
|
|
|
|
logger.info(f"[MSAL Backend] Authorization header present, length: {len(authorization)}")
|
|
|
|
# Extract token from "Bearer <token>" format
|
|
parts = authorization.split()
|
|
if len(parts) != 2 or parts[0].lower() != "bearer":
|
|
logger.warning(f"[MSAL Backend] Invalid auth header format: {parts[0] if parts else 'empty'}")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail="Invalid authorization header format. Expected: Bearer <token>",
|
|
headers={"WWW-Authenticate": "Bearer"},
|
|
)
|
|
|
|
token = parts[1]
|
|
logger.info("[MSAL Backend] Extracted Bearer token, calling verify_access_token...")
|
|
claims = await verify_access_token(token)
|
|
|
|
if not claims:
|
|
logger.warning("[MSAL Backend] Token verification failed - returning 401")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail="Invalid or expired token",
|
|
headers={"WWW-Authenticate": "Bearer"},
|
|
)
|
|
|
|
logger.info(f"[MSAL Backend] Authentication successful for: {claims.get('name', 'unknown')}")
|
|
return claims
|