from fastapi import APIRouter, HTTPException, Request, Response, status from app.api.deps import MailerDep, SessionDep, extract_session_token from app.core.config import get_settings from app.schemas.auth import ( LoginRequest, PasswordResetConfirm, PasswordResetRequest, RegisterRequest, SessionRead, TokenRequest, ) from app.schemas.user import UserRead from app.services import auth_service router = APIRouter(prefix="/auth", tags=["auth"]) def _set_session_cookie(response: Response, token: str) -> None: settings = get_settings() response.set_cookie( settings.cookie_name, token, max_age=settings.session_ttl_days * 86400, httponly=True, secure=settings.cookie_secure, samesite="lax", ) @router.post("/register", response_model=SessionRead, status_code=status.HTTP_201_CREATED) async def register( data: RegisterRequest, session: SessionDep, mailer: MailerDep, response: Response ) -> SessionRead: user, token, expires_at = await auth_service.register( session, mailer, email=data.email, password=data.password, display_name=data.display_name, ) _set_session_cookie(response, token) return SessionRead(user=UserRead.model_validate(user), token=token, expires_at=expires_at) @router.post("/login", response_model=SessionRead) async def login(data: LoginRequest, session: SessionDep, response: Response) -> SessionRead: result = await auth_service.login(session, email=data.email, password=data.password) if result is None: raise HTTPException(status.HTTP_401_UNAUTHORIZED, "invalid credentials") user, token, expires_at = result _set_session_cookie(response, token) return SessionRead(user=UserRead.model_validate(user), token=token, expires_at=expires_at) @router.post("/logout", status_code=status.HTTP_204_NO_CONTENT) async def logout(request: Request, session: SessionDep, response: Response) -> None: raw_token = extract_session_token(request) if raw_token: await auth_service.logout(session, raw_token=raw_token) response.delete_cookie(get_settings().cookie_name) @router.post("/verify-email", status_code=status.HTTP_204_NO_CONTENT) async def verify_email(data: TokenRequest, session: SessionDep) -> None: await auth_service.verify_email(session, raw_token=data.token) @router.post("/request-password-reset", status_code=status.HTTP_202_ACCEPTED) async def request_password_reset( data: PasswordResetRequest, session: SessionDep, mailer: MailerDep ) -> dict: await auth_service.request_password_reset(session, mailer, email=data.email) return {"status": "accepted"} @router.post("/reset-password", status_code=status.HTTP_204_NO_CONTENT) async def reset_password(data: PasswordResetConfirm, session: SessionDep) -> None: await auth_service.reset_password( session, raw_token=data.token, new_password=data.new_password )