fix(gui): use fastapi-csrf-protect native body-token validation

The library supports form-data tokens via token_location="body" and
token_key config options, which we missed in the initial integration.
Removed hand-rolled _validate_csrf_form helper in favor of the
library's validate_csrf method.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Ubuntu 2026-05-17 07:00:57 +00:00
commit 17dd653bd8
2 changed files with 6 additions and 17 deletions

View file

@ -37,6 +37,8 @@ def _configure_csrf() -> None:
class CsrfSettings(BaseModel): class CsrfSettings(BaseModel):
secret_key: str secret_key: str
token_location: str = "body"
token_key: str = "csrf_token"
@CsrfProtect.load_config @CsrfProtect.load_config
def get_csrf_config(): def get_csrf_config():

View file

@ -24,19 +24,6 @@ from central.gui.db import get_pool
router = APIRouter() router = APIRouter()
async def _validate_csrf_form(request, csrf_protect):
"""Validate CSRF token from form data."""
form = await request.form()
csrf_token = form.get("csrf_token")
if csrf_token:
cookie_token = request.cookies.get("fastapi-csrf-token")
if not cookie_token or cookie_token != csrf_token:
from fastapi_csrf_protect.exceptions import TokenValidationError
raise TokenValidationError("CSRF token mismatch")
else:
from fastapi_csrf_protect.exceptions import MissingTokenError
raise MissingTokenError("Missing CSRF token in form")
def _get_templates(): def _get_templates():
"""Get templates instance (deferred import to avoid circular).""" """Get templates instance (deferred import to avoid circular)."""
from central.gui import templates from central.gui import templates
@ -119,7 +106,7 @@ async def setup_submit(
pool = get_pool() pool = get_pool()
# Validate CSRF # Validate CSRF
await _validate_csrf_form(request, csrf_protect) await csrf_protect.validate_csrf(request)
# Validate input # Validate input
error = None error = None
@ -213,7 +200,7 @@ async def login_submit(
pool = get_pool() pool = get_pool()
# Validate CSRF # Validate CSRF
await _validate_csrf_form(request, csrf_protect) await csrf_protect.validate_csrf(request)
# Look up operator # Look up operator
async with pool.acquire() as conn: async with pool.acquire() as conn:
@ -279,7 +266,7 @@ async def logout(
pool = get_pool() pool = get_pool()
# Validate CSRF # Validate CSRF
await _validate_csrf_form(request, csrf_protect) await csrf_protect.validate_csrf(request)
# Get current session # Get current session
session_token = request.cookies.get("central_session") session_token = request.cookies.get("central_session")
@ -328,7 +315,7 @@ async def change_password_submit(
operator = request.state.operator operator = request.state.operator
# Validate CSRF # Validate CSRF
await _validate_csrf_form(request, csrf_protect) await csrf_protect.validate_csrf(request)
# Get current password hash # Get current password hash
async with pool.acquire() as conn: async with pool.acquire() as conn: