"""Authentication routes for admin login and logout. Handles the login form, credential verification, session cookie management, and logout. """ import structlog from fastapi import APIRouter, Depends, Request from fastapi.responses import HTMLResponse, RedirectResponse from sqlmodel import Session from app.database import get_db_session from app.services.auth_service import AuthService from app.utils.auth import SESSION_COOKIE_NAME logger = structlog.get_logger(__name__) router = APIRouter(tags=["auth"]) @router.get("/login", response_class=HTMLResponse) async def login_page(request: Request): """Render the login form. Args: request: The incoming HTTP request. Returns: Rendered login page HTML. """ templates = request.app.state.templates return templates.TemplateResponse("pages/login.html", { "request": request, "error": None, }) @router.post("/login") async def login_submit( request: Request, session: Session = Depends(get_db_session), ): """Process login form submission. Verifies credentials and sets a session cookie on success. Re-renders the login page with an error on failure. Args: request: The incoming HTTP request. session: Database session. Returns: Redirect to home on success, or login page with error. """ form = await request.form() username = form.get("username", "") password = form.get("password", "") secret_key = request.app.state.secret_key auth_service = AuthService(session, secret_key=secret_key) user = auth_service.authenticate(username, password) if user is None: templates = request.app.state.templates return templates.TemplateResponse("pages/login.html", { "request": request, "error": "Invalid username or password.", }, status_code=200) # Create session token and set cookie — httponly and samesite for security token = auth_service.create_session_token(user_id=user.id) response = RedirectResponse(url="/", status_code=303) response.set_cookie( key=SESSION_COOKIE_NAME, value=token, httponly=True, samesite="lax", max_age=86400, # 24 hours ) logger.info("login_success", username=username) return response @router.get("/logout") async def logout(request: Request): """Clear the session cookie and redirect to login. Args: request: The incoming HTTP request. Returns: Redirect to login page. """ response = RedirectResponse(url="/login", status_code=303) response.delete_cookie(key=SESSION_COOKIE_NAME) response.delete_cookie(key="active_profile_id") logger.info("logout") return response