from fastapi import APIRouter, Form, HTTPException from app.auth import ( TokenPair, fetch_account, issue_access_token, issue_refresh_token, rotate_refresh_token, store_refresh_token, touch_last_login, verify_password, ) router = APIRouter(prefix="/api/auth", tags=["auth"]) @router.post("/token", response_model=TokenPair) async def issue_token( username: str = Form(...), password: str = Form(...), ) -> TokenPair: record = await fetch_account(username) if record is None: raise HTTPException(status_code=401, detail="invalid credentials") account_id, password_hash, scopes = record if not verify_password(password, password_hash): raise HTTPException(status_code=401, detail="invalid credentials") access, ttl = issue_access_token(account_id, scopes) refresh, expires_at, refresh_hash = issue_refresh_token(account_id) await store_refresh_token(account_id, refresh_hash, expires_at) await touch_last_login(account_id) return TokenPair( access_token=access, refresh_token=refresh, expires_in=ttl, ) @router.post("/refresh", response_model=TokenPair) async def refresh( refresh_token: str = Form(...), ) -> TokenPair: pair = await rotate_refresh_token(refresh_token) if pair is None: raise HTTPException(status_code=401, detail="invalid or expired refresh token") return pair