Files
BadNote/server/badnote_server/routers/auth_router.py
T

78 lines
2.4 KiB
Python
Raw Normal View History

"""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)