48 lines
1.4 KiB
Python
48 lines
1.4 KiB
Python
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
|