Skip to content

CSRF

bluefox_auth.csrf

Plain double-submit cookie CSRF protection.

How it works

  1. On login/refresh, a random CSRF token is set as a non-HttpOnly cookie
  2. For mutating requests using cookie transport, the client reads the cookie and sends the value in the X-CSRF-Token header
  3. The server compares both values using secrets.compare_digest (timing-safe)

Functions

generate_csrf_token

def generate_csrf_token() -> str

Returns a 32-byte URL-safe random token via secrets.token_urlsafe(32).

validate_csrf

def validate_csrf(request: Any, cookie_name: str) -> None

Validates CSRF for the given request. Behavior:

  • Safe methods (GET, HEAD, OPTIONS): skipped, returns immediately
  • Missing cookie or header: raises HTTPException(403, "Missing CSRF token")
  • Mismatch: raises HTTPException(403, "CSRF token mismatch")

When CSRF is checked

CSRF validation is triggered in two places:

  1. get_current_user dependency — for cookie-based mutating requests (POST/PUT/PATCH/DELETE)
  2. /refresh endpoint — when the refresh token comes from a cookie

Bearer requests skip CSRF — this is enforced at the call site (get_current_user and the /refresh route) by only calling validate_csrf when the token came from a cookie. The validate_csrf function itself has no Bearer awareness.

Client implementation

// Read the CSRF cookie
const csrfToken = document.cookie
  .split("; ")
  .find(row => row.startsWith("bf_csrf_token="))
  ?.split("=")[1];

// Include it in mutating requests
fetch("/auth/refresh", {
  method: "POST",
  credentials: "include",
  headers: { "X-CSRF-Token": csrfToken },
});