62 lines
2.1 KiB
Python
62 lines
2.1 KiB
Python
"""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
|
|
)
|