{"id":8985,"library":"fastapi-csrf-protect","title":"FastAPI CSRF Protect","description":"FastAPI CSRF Protect is a FastAPI extension providing stateless Cross-Site Request Forgery (XSRF) protection. It implements the Double Submit Cookie mitigation pattern, similar to `flask-wtf` and inspired by `fastapi-jwt-auth`. The library is designed to be lightweight and easy to use. The current version is 1.0.7, requiring Python 3.9 or newer, and it maintains an active release cadence.","status":"active","version":"1.0.7","language":"en","source_language":"en","source_url":"https://github.com/aekasitt/fastapi-csrf-protect","tags":["fastapi","csrf","security","xsrf","double-submit-cookie","web-security"],"install":[{"cmd":"pip install fastapi-csrf-protect","lang":"bash","label":"Install stable version"}],"dependencies":[{"reason":"Core web framework integration.","package":"fastapi","optional":false},{"reason":"Used for configuration management (e.g., CsrfSettings).","package":"pydantic-settings","optional":false}],"imports":[{"symbol":"CsrfProtect","correct":"from fastapi_csrf_protect import CsrfProtect"},{"symbol":"CsrfProtectError","correct":"from fastapi_csrf_protect.exceptions import CsrfProtectError"},{"note":"Use this import for hybrid applications that need to accept CSRF tokens from both headers and form bodies.","symbol":"CsrfProtect (flexible mode)","correct":"from fastapi_csrf_protect.flexible import CsrfProtect"}],"quickstart":{"code":"import os\nfrom fastapi import FastAPI, Request, Depends\nfrom fastapi.responses import JSONResponse, HTMLResponse\nfrom fastapi.templating import Jinja2Templates\nfrom fastapi_csrf_protect import CsrfProtect\nfrom fastapi_csrf_protect.exceptions import CsrfProtectError\nfrom pydantic_settings import BaseSettings\n\napp = FastAPI()\ntemplates = Jinja2Templates(directory=\"templates\") # Ensure you have a 'templates' directory with 'form.html'\n\nclass CsrfSettings(BaseSettings):\n    secret_key: str = os.environ.get('CSRF_SECRET_KEY', 'a_super_secret_key_for_csrf_protection')\n    cookie_samesite: str = \"lax\"\n\n@CsrfProtect.load_config\ndef get_csrf_config():\n    return CsrfSettings()\n\n@app.exception_handler(CsrfProtectError)\nasync def csrf_protect_exception_handler(request: Request, exc: CsrfProtectError):\n    return JSONResponse(status_code=exc.status_code, content={'detail': exc.message})\n\n@app.get(\"/login\", response_class=HTMLResponse)\nasync def login_form(request: Request, csrf_protect: CsrfProtect = Depends()):\n    csrf_token, signed_token = csrf_protect.generate_csrf_tokens()\n    response = templates.TemplateResponse(\n        \"form.html\", {\"request\": request, \"csrf_token\": csrf_token}\n    )\n    csrf_protect.set_csrf_cookie(signed_token, response)\n    return response\n\n@app.post(\"/login\", response_class=JSONResponse)\nasync def process_login(request: Request, csrf_protect: CsrfProtect = Depends()):\n    await csrf_protect.validate_csrf(request)\n    # Your login logic here\n    response: JSONResponse = JSONResponse(status_code=200, content={\"detail\": \"Login successful\"})\n    csrf_protect.unset_csrf_cookie(response) # Optional: prevent token reuse\n    return response\n\n# To run this, you'll need a 'templates/form.html' file like:\n# <form method=\"post\" action=\"/login\">\n#     <input type=\"hidden\" name=\"csrf-token\" value=\"{{ csrf_token }}\">\n#     <label for=\"username\">Username:</label>\n#     <input type=\"text\" id=\"username\" name=\"username\"><br><br>\n#     <label for=\"password\">Password:</label>\n#     <input type=\"password\" id=\"password\" name=\"password\"><br><br>\n#     <input type=\"submit\" value=\"Submit\">\n# </form>\n","lang":"python","description":"This quickstart demonstrates how to integrate `fastapi-csrf-protect` into a FastAPI application for a login form scenario. It shows how to load CSRF settings, generate and set CSRF cookies for a GET request, and validate the CSRF token on a POST request. It includes basic error handling for `CsrfProtectError`."},"warnings":[{"fix":"Replace `csrf_protect.generate_csrf()` with `csrf_protect.generate_csrf_tokens()` and use the returned `signed_token` for setting the cookie.","message":"The `generate_csrf` function was deprecated in version 0.3.1. It was replaced by `generate_csrf_tokens` which returns both the plain and signed tokens.","severity":"breaking","affected_versions":"0.3.0 -> 0.3.1"},{"fix":"Ensure that `validate_csrf` is always `await`ed, e.g., `await csrf_protect.validate_csrf(request)`.","message":"The `validate_csrf` method became an `async` function.","severity":"breaking","affected_versions":"0.3.1 -> 0.3.2"},{"fix":"Always include `await csrf_protect.validate_csrf(request)` in any endpoint you intend to protect against CSRF.","message":"The library relies on explicit calls to `validate_csrf`. Simply injecting `CsrfProtect = Depends()` into an endpoint does NOT automatically secure it; you must explicitly call `await csrf_protect.validate_csrf(request)`.","severity":"gotcha","affected_versions":"All versions"},{"fix":"For hybrid applications, use `from fastapi_csrf_protect.flexible import CsrfProtect`. This sub-package always accepts tokens from either the header (`X-CSRFToken`) or the form body (`token_key`), prioritizing the header if both are present.","message":"The main `fastapi-csrf-protect` package is opinionated and expects the CSRF token in either the header or the body, but not both simultaneously by default. For applications combining Server-Side Rendering (SSR) with API endpoints (hybrid apps), this can be inconvenient.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Consider documenting how to manually add the CSRF header in tools like Swagger for testing, or use the `flexible` module which handles various token locations, potentially simplifying testing.","message":"When using cookie-based JWT authentication and sending CSRF tokens as headers, Swagger UI (and other API documentation tools) might fail to send the CSRF header correctly, breaking API documentation for protected endpoints.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-04-16T00:00:00.000Z","next_check":"2026-07-15T00:00:00.000Z","problems":[{"fix":"Ensure the client-side code correctly sends the CSRF token (from `generate_csrf_tokens`) in the `X-CSRFToken` header or a form field named `csrf-token`. Verify the `CsrfSettings` (especially `secret_key` and `cookie_samesite`) are correctly configured and that `await csrf_protect.validate_csrf(request)` is present in your endpoint.","cause":"The CSRF token provided in the request (either in the header or form body) does not match the token in the signed cookie, or the cookie is missing/expired. This could also happen if `validate_csrf` was not called.","error":"CsrfProtectError: CSRF token invalid"},{"fix":"Make sure you have a `BaseSettings` subclass (e.g., `CsrfSettings`) defining `secret_key` and decorated with `@CsrfProtect.load_config` in your application's startup phase.","cause":"The `CsrfSettings` class or the `@CsrfProtect.load_config` decorator was not properly defined or loaded before attempting to use `CsrfProtect`.","error":"AttributeError: 'CsrfProtect' object has no attribute 'secret_key'"},{"fix":"Change the call to `await csrf_protect.validate_csrf(request)`.","cause":"You are calling `csrf_protect.validate_csrf(request)` without `await`. This is common after upgrading from versions prior to 0.3.2.","error":"RuntimeError: `validate_csrf` must be awaited"},{"fix":"Ensure that `CSRF_SECRET_KEY` environment variable is set in production, or provide a robust default value in your `CsrfSettings` definition, e.g., `secret_key: str = os.environ.get('CSRF_SECRET_KEY', 'a_strong_default_key')`.","cause":"The `secret_key` in your `CsrfSettings` is not being loaded correctly, potentially due to missing environment variable or an empty string being passed.","error":"PydanticValidationError: 1 validation error for CsrfSettings.secret_key"}]}