Files
sentinel/app/api/auth.py

50 lines
1.7 KiB
Python
Raw Normal View History

from __future__ import annotations
import logging
from fastapi import APIRouter, Depends, HTTPException, Request, status
from redis.asyncio import Redis
from app.config import Settings
from app.core.ip_utils import extract_client_ip
from app.core.security import (
clear_login_failures,
create_admin_jwt,
ensure_login_allowed,
register_login_failure,
verify_admin_password,
)
from app.dependencies import get_redis, get_settings
from app.schemas.auth import LoginRequest, TokenResponse
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/admin/api", tags=["auth"])
@router.post("/login", response_model=TokenResponse)
async def login(
payload: LoginRequest,
request: Request,
settings: Settings = Depends(get_settings),
redis: Redis | None = Depends(get_redis),
) -> TokenResponse:
if redis is None:
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail="Login service is unavailable because Redis is offline.",
)
client_ip = extract_client_ip(request, settings)
await ensure_login_allowed(redis, client_ip, settings)
if not verify_admin_password(payload.password, settings):
await register_login_failure(redis, client_ip, settings)
logger.warning("Admin login failed.", extra={"client_ip": client_ip})
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid admin password.")
await clear_login_failures(redis, client_ip)
token, expires_in = create_admin_jwt(settings)
logger.info("Admin login succeeded.", extra={"client_ip": client_ip})
return TokenResponse(access_token=token, expires_in=expires_in)