"""Auth router for BadNote.""" from datetime import datetime, timezone from uuid import uuid4 from fastapi import APIRouter, Depends, HTTPException, status from ..auth import ( create_access_token, dummy_verify, get_current_user, hash_password, verify_password, ) from ..database import get_db from ..models import TokenResponse, UserCreate, UserLogin router = APIRouter() @router.post("/register", response_model=TokenResponse, status_code=status.HTTP_201_CREATED) async def register(body: UserCreate) -> TokenResponse: """Register a new user.""" db = await get_db() existing = await db.execute( "SELECT id FROM users WHERE username = ?", (body.username,) ) if await existing.fetchone() is not None: raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail="Username already taken", ) user_id = str(uuid4()) now = datetime.now(timezone.utc).isoformat() await db.execute( "INSERT INTO users (id, username, password_hash, created_at) VALUES (?, ?, ?, ?)", (user_id, body.username, hash_password(body.password), now), ) await db.commit() token = create_access_token(user_id) return TokenResponse(token=token, user_id=user_id) @router.post("/login", response_model=TokenResponse) async def login(body: UserLogin) -> TokenResponse: """Authenticate and return a token.""" db = await get_db() row = await ( await db.execute( "SELECT id, password_hash FROM users WHERE username = ?", (body.username,) ) ).fetchone() if row is None: # Spend comparable time hashing so timing does not reveal whether the # username exists. dummy_verify() raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid username or password", ) if not verify_password(body.password, row["password_hash"]): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid username or password", ) token = create_access_token(row["id"]) return TokenResponse(token=token, user_id=row["id"]) @router.post("/refresh", response_model=TokenResponse) async def refresh(user_id: str = Depends(get_current_user)) -> TokenResponse: """Refresh an existing valid token.""" token = create_access_token(user_id) return TokenResponse(token=token, user_id=user_id)