from fastapi import APIRouter, Depends, HTTPException, Request from fastapi.responses import JSONResponse from pydantic import BaseModel from app.auth.jwt_provider import JWTAuthProvider from app.auth.rate_limiter import LoginRateLimiter, get_client_ip from app.dependencies import get_jwt_auth router = APIRouter(tags=["auth"]) class LoginRequest(BaseModel): username: str password: str class TokenResponse(BaseModel): access_token: str token_type: str = "bearer" expires_in: int @router.post("/auth/token", response_model=TokenResponse) async def login( request: Request, body: LoginRequest, auth: JWTAuthProvider = Depends(get_jwt_auth), ): limiter: LoginRateLimiter = request.app.state.login_rate_limiter ip: str = get_client_ip(request, request.app.state.login_trusted_networks) if limiter.is_blocked(ip): return JSONResponse( status_code=429, content={ "detail": "Too many failed login attempts. Please try again later.", "code": "login_rate_limited", }, headers={"Retry-After": str(limiter.cooldown_seconds)}, ) if not auth.verify_credentials(body.username, body.password): limiter.record_failure(ip) raise HTTPException( status_code=401, detail={"detail": "Invalid credentials", "code": "invalid_credentials"}, ) limiter.record_success(ip) token = auth.create_token() return TokenResponse( access_token=token, token_type="bearer", expires_in=auth._expiry_seconds, )