feat: Implement session management with middleware and update authentication flow

This commit is contained in:
2025-11-09 23:14:41 +01:00
parent 3601c2e422
commit 27262bdfa3
9 changed files with 804 additions and 83 deletions

View File

@@ -9,7 +9,13 @@ from fastapi.templating import Jinja2Templates
from pydantic import ValidationError
from starlette.datastructures import FormData
from dependencies import get_jwt_settings, get_unit_of_work
from dependencies import (
get_auth_session,
get_jwt_settings,
get_session_strategy,
get_unit_of_work,
require_current_user,
)
from models import Role, User
from schemas.auth import (
LoginForm,
@@ -29,6 +35,12 @@ from services.security import (
hash_password,
verify_password,
)
from services.session import (
AuthSession,
SessionStrategy,
clear_session_cookies,
set_session_cookies,
)
from services.repositories import RoleRepository, UserRepository
from services.unit_of_work import UnitOfWork
@@ -103,6 +115,7 @@ async def login_submit(
request: Request,
uow: UnitOfWork = Depends(get_unit_of_work),
jwt_settings: JWTSettings = Depends(get_jwt_settings),
session_strategy: SessionStrategy = Depends(get_session_strategy),
):
form_data = _normalise_form_data(await request.form())
try:
@@ -158,7 +171,31 @@ async def login_submit(
request.url_for("dashboard.home"),
status_code=status.HTTP_303_SEE_OTHER,
)
_set_auth_cookies(response, access_token, refresh_token, jwt_settings)
set_session_cookies(
response,
access_token=access_token,
refresh_token=refresh_token,
strategy=session_strategy,
jwt_settings=jwt_settings,
)
return response
@router.get("/logout", include_in_schema=False, name="auth.logout")
async def logout(
request: Request,
_: User = Depends(require_current_user),
session: AuthSession = Depends(get_auth_session),
session_strategy: SessionStrategy = Depends(get_session_strategy),
) -> RedirectResponse:
session.mark_cleared()
redirect_url = request.url_for(
"auth.login_form").include_query_params(logout="1")
response = RedirectResponse(
redirect_url,
status_code=status.HTTP_303_SEE_OTHER,
)
clear_session_cookies(response, session_strategy)
return response
@@ -168,32 +205,6 @@ def _lookup_user(users_repo: UserRepository, identifier: str) -> User | None:
return users_repo.get_by_username(identifier, with_roles=True)
def _set_auth_cookies(
response: RedirectResponse,
access_token: str,
refresh_token: str,
jwt_settings: JWTSettings,
) -> None:
access_ttl = int(jwt_settings.access_token_ttl.total_seconds())
refresh_ttl = int(jwt_settings.refresh_token_ttl.total_seconds())
response.set_cookie(
"calminer_access_token",
access_token,
httponly=True,
secure=False,
samesite="lax",
max_age=max(access_ttl, 0) or None,
)
response.set_cookie(
"calminer_refresh_token",
refresh_token,
httponly=True,
secure=False,
samesite="lax",
max_age=max(refresh_ttl, 0) or None,
)
@router.get("/register", response_class=HTMLResponse, include_in_schema=False, name="auth.register_form")
def register_form(request: Request) -> HTMLResponse:
return _template(