import secrets from datetime import datetime, timezone from fastapi import APIRouter, HTTPException, status from ..crud import NO_PASSWORD from ..db import get_users from ..models import LoginRequest, LoginResponse router = APIRouter(prefix="/auth", tags=["auth"]) @router.post("/login", response_model=LoginResponse, summary="Verify credentials") async def login(payload: LoginRequest): """Credential check against ighub.users. NOTE: this collection stores passwords in plaintext, so verification is a constant-time string comparison. Once the stored values are migrated to a real hash, replace the comparison below with the hash verify call. """ users = get_users() doc = await users.find_one( {"$or": [{"user_id": payload.username}, {"email": payload.username}]} ) invalid = HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials" ) if not doc: raise invalid stored = doc.get("password") or "" if not secrets.compare_digest(stored, payload.password): raise invalid if doc.get("status") != 1: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Account is inactive" ) await users.update_one( {"_id": doc["_id"]}, {"$set": {"last_login": datetime.now(timezone.utc)}} ) fresh = await users.find_one({"_id": doc["_id"]}, NO_PASSWORD) return LoginResponse(authenticated=True, user=fresh)