2025-12-20 22:18:14 +01:00
|
|
|
"""Authentication routes for register, login, logout, and current user."""
|
2025-12-21 21:54:26 +01:00
|
|
|
|
2025-12-25 18:42:46 +01:00
|
|
|
from fastapi import APIRouter, Depends, Response
|
2025-12-20 22:18:14 +01:00
|
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
|
|
|
|
|
|
from auth import (
|
|
|
|
|
ACCESS_TOKEN_EXPIRE_MINUTES,
|
|
|
|
|
COOKIE_NAME,
|
2025-12-20 22:38:39 +01:00
|
|
|
COOKIE_SECURE,
|
2025-12-21 21:54:26 +01:00
|
|
|
build_user_response,
|
2025-12-20 22:18:14 +01:00
|
|
|
get_current_user,
|
|
|
|
|
)
|
|
|
|
|
from database import get_db
|
2025-12-25 18:42:46 +01:00
|
|
|
from models import User
|
2025-12-21 21:54:26 +01:00
|
|
|
from schemas import RegisterWithInvite, UserLogin, UserResponse
|
2025-12-25 18:42:46 +01:00
|
|
|
from services.auth import AuthService
|
2025-12-20 22:18:14 +01:00
|
|
|
|
|
|
|
|
router = APIRouter(prefix="/api/auth", tags=["auth"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def set_auth_cookie(response: Response, token: str) -> None:
|
|
|
|
|
"""Set the authentication cookie on the response."""
|
|
|
|
|
response.set_cookie(
|
|
|
|
|
key=COOKIE_NAME,
|
|
|
|
|
value=token,
|
|
|
|
|
httponly=True,
|
2025-12-20 22:38:39 +01:00
|
|
|
secure=COOKIE_SECURE,
|
2025-12-20 22:18:14 +01:00
|
|
|
samesite="lax",
|
|
|
|
|
max_age=ACCESS_TOKEN_EXPIRE_MINUTES * 60,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/register", response_model=UserResponse)
|
|
|
|
|
async def register(
|
|
|
|
|
user_data: RegisterWithInvite,
|
|
|
|
|
response: Response,
|
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
|
) -> UserResponse:
|
|
|
|
|
"""Register a new user using an invite code."""
|
2025-12-25 18:42:46 +01:00
|
|
|
service = AuthService(db)
|
|
|
|
|
user, access_token = await service.register_user(
|
2025-12-20 22:18:14 +01:00
|
|
|
email=user_data.email,
|
2025-12-25 18:42:46 +01:00
|
|
|
password=user_data.password,
|
|
|
|
|
invite_identifier=user_data.invite_identifier,
|
2025-12-20 22:18:14 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
set_auth_cookie(response, access_token)
|
|
|
|
|
return await build_user_response(user, db)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/login", response_model=UserResponse)
|
|
|
|
|
async def login(
|
|
|
|
|
user_data: UserLogin,
|
|
|
|
|
response: Response,
|
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
|
) -> UserResponse:
|
|
|
|
|
"""Authenticate a user and return their info with an auth cookie."""
|
2025-12-25 18:42:46 +01:00
|
|
|
service = AuthService(db)
|
|
|
|
|
user, access_token = await service.login_user(
|
|
|
|
|
email=user_data.email, password=user_data.password
|
|
|
|
|
)
|
2025-12-20 22:18:14 +01:00
|
|
|
|
|
|
|
|
set_auth_cookie(response, access_token)
|
|
|
|
|
return await build_user_response(user, db)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/logout")
|
|
|
|
|
async def logout(response: Response) -> dict[str, bool]:
|
|
|
|
|
"""Log out the current user by clearing their auth cookie."""
|
|
|
|
|
response.delete_cookie(key=COOKIE_NAME)
|
|
|
|
|
return {"ok": True}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/me", response_model=UserResponse)
|
|
|
|
|
async def get_me(
|
|
|
|
|
current_user: User = Depends(get_current_user),
|
|
|
|
|
db: AsyncSession = Depends(get_db),
|
|
|
|
|
) -> UserResponse:
|
|
|
|
|
"""Get the current authenticated user's info."""
|
|
|
|
|
return await build_user_response(current_user, db)
|