"""Authentication endpoints.""" from fastapi import APIRouter, HTTPException, status, Request from datetime import timedelta import logging from app.core.config import get_settings from app.core.security import authenticate_user, create_access_token, check_rate_limit from app.schemas import LoginRequest, LoginResponse logger = logging.getLogger(__name__) router = APIRouter(prefix="/auth", tags=["Authentication"]) @router.post("/login", response_model=LoginResponse) async def login(request: Request, credentials: LoginRequest): """ Authenticate user and return JWT token. - **username**: Username - **password**: Password Returns JWT access token with expiration time. """ settings = get_settings() # Get client IP for rate limiting client_ip = request.client.host if request.client else "unknown" # Check rate limit if check_rate_limit(client_ip): logger.warning(f"Rate limit exceeded for IP: {client_ip}") raise HTTPException( status_code=status.HTTP_429_TOO_MANY_REQUESTS, detail="Too many login attempts. Please try again later." ) # Authenticate (never log the password) logger.info(f"Login attempt for user: {credentials.username} from IP: {client_ip}") if not authenticate_user(credentials.username, credentials.password): logger.warning(f"Failed login attempt for user: {credentials.username}") raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials", headers={"WWW-Authenticate": "Bearer"}, ) # Create access token access_token_expires = timedelta(minutes=settings.jwt_expires_minutes) access_token = create_access_token( data={"sub": credentials.username}, expires_delta=access_token_expires ) logger.info(f"Successful login for user: {credentials.username}") return LoginResponse( access_token=access_token, token_type="bearer", expires_in=settings.jwt_expires_minutes * 60 # Convert to seconds )