Files
flowdeck/app/middleware/csrf.py
T
bruno 80f56acf4c
FlowDeck CI / test (push) Failing after 4s
FlowDeck CI / docker (push) Has been skipped
feat(v2.1.0): API publique + Webhooks sortants + PWA
- Public API: /api/v1 (collections, pages, my-tasks) with token auth
- Token generation: POST /api/v1/token
- Outbound webhooks: /workspace/webhooks CRUD + fire_event dispatcher
- PWA: manifest.json endpoint
- CSRF exempt for /api/v1
- 73/73 tests passent (+6 tests v2.1)
- Version 2.0.0 → 2.1.0
- Docs: ROADMAP updated — 7/7 blocs, 52/52 features ✅
2026-07-10 07:28:09 -04:00

50 lines
1.8 KiB
Python

"""FlowDeck — CSRF protection middleware."""
from __future__ import annotations
import secrets
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.responses import JSONResponse
from starlette.requests import Request
class CSRFMiddleware(BaseHTTPMiddleware):
"""Lightweight CSRF protection for state-changing requests.
All POST/PUT/PATCH/DELETE requests must include X-CSRF-Token
header matching the csrf_token cookie.
"""
SAFE_METHODS = {"GET", "HEAD", "OPTIONS"}
EXCLUDED_PATHS = {"/api/webhook", "/api/v1", "/auth/callback", "/board/api/pages", "/db/", "/workspace"}
async def dispatch(self, request: Request, call_next):
# Webhook receiver, OAuth callback, and internal API are exempt
if any(request.url.path.startswith(p) for p in self.EXCLUDED_PATHS):
return await call_next(request)
if request.method in self.SAFE_METHODS:
response = await call_next(request)
# Set CSRF cookie if not present
if "csrf_token" not in request.cookies:
response.set_cookie(
"csrf_token",
secrets.token_hex(32),
httponly=False, # Must be readable by JS
samesite="lax",
max_age=86400,
)
return response
# Validate CSRF for state-changing methods
csrf_cookie = request.cookies.get("csrf_token", "")
csrf_header = request.headers.get("X-CSRF-Token", "")
if not csrf_cookie or not csrf_header or not secrets.compare_digest(csrf_cookie, csrf_header):
return JSONResponse(
{"detail": "CSRF validation failed"},
status_code=403,
)
return await call_next(request)