# FastAPI CSRF Protect
[](https://pypi.org/project/fastapi-csrf-protect)
[](https://pypi.org/project/fastapi-csrf-protect)
[](https://pypi.org/project/fastapi-csrf-protect)
[](https://pypi.org/project/fastapi-csrf-protect)
[](.)
[](.)
[](.)
[](.)
[](https://aekasitt.github.io/fastapi-csrf-protect)
[](https://github.com/aekasitt/fastapi-csrf-protect/blob/master/static/protect-banner.svg)
## Features
FastAPI extension that provides stateless Cross-Site Request Forgery (XSRF) Protection support.
Aimed to be easy to use and lightweight, we adopt [Double Submit Cookie](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#double-submit-cookie) mitigation pattern.
If you were familiar with `flask-wtf` library this extension suitable for you.
This extension inspired by `fastapi-jwt-auth` 😀
- Storing `fastapi-csrf-token` in cookies or serve it in template's context
## Installation
The easiest way to start working with this extension with pip
```bash
pip install fastapi-csrf-protect
# or
uv add fastapi-csrf-protect
```
## Getting Started
The following examples show you how to integrate this extension to a FastAPI App
### Example Login Form
```python
from fastapi import FastAPI, Request, Depends
from fastapi.responses import JSONResponse
from fastapi.templating import Jinja2Templates
from fastapi_csrf_protect import CsrfProtect
from fastapi_csrf_protect.exceptions import CsrfProtectError
from pydantic_settings import BaseSettings
app = FastAPI()
templates = Jinja2Templates(directory="templates")
class CsrfSettings(BaseSettings):
secret_key: str = "asecrettoeverybody"
cookie_samesite: str = "none"
@CsrfProtect.load_config
def get_csrf_config():
return CsrfSettings()
@app.get("/login")
def form(request: Request, csrf_protect: CsrfProtect = Depends()):
"""
Returns form template.
"""
csrf_token, signed_token = csrf_protect.generate_csrf_tokens()
response = templates.TemplateResponse(
"form.html", {"request": request, "csrf_token": csrf_token}
)
csrf_protect.set_csrf_cookie(signed_token, response)
return response
@app.post("/login", response_class=JSONResponse)
async def create_post(request: Request, csrf_protect: CsrfProtect = Depends()):
"""
Creates a new Post
"""
await csrf_protect.validate_csrf(request)
response: JSONResponse = JSONResponse(status_code=200, content={"detail": "OK"})
csrf_protect.unset_csrf_cookie(response) # prevent token reuse
return response
@app.exception_handler(CsrfProtectError)
def csrf_protect_exception_handler(request: Request, exc: CsrfProtectError):
return JSONResponse(status_code=exc.status_code, content={"detail": exc.message})
```
## Contributions
### Prerequisites
* [python](https://www.python.org) version 3.9 and above
* [uv](https://docs.astral.sh/uv)
### Setting up
The following guide walks through setting up your local working environment using `pyenv`
as Python version manager and `uv` as Python package manager. If you do not have `pyenv`
installed, run the following command.
<details>
<summary> Install using Homebrew (Darwin) </summary>
```sh
brew install pyenv --head
```
</details>
<details>
<summary> Install using standalone installer (Darwin and Linux) </summary>
```sh
curl https://pyenv.run | bash
```
</details>
If you do not have `uv` installed, run the following command.
<details>
<summary> Install using Homebrew (Darwin) </summary>
```sh
brew install uv
```
</details>
<details>
<summary> Install using standalone installer (Darwin and Linux) </summary>
```sh
curl -LsSf https://astral.sh/uv/install.sh | sh
```
</details>
Once you have `pyenv` Python version manager installed, you can
install any version of Python above version 3.9 for this project.
The following commands help you set up and activate a Python virtual
environment where `uv` can download project dependencies from the `PyPI`
open-sourced registry defined under `pyproject.toml` file.
<details>
<summary> Set up environment and synchronize project dependencies </summary>
```sh
pyenv shell 3.11.9
uv venv --python-preference system
source .venv/bin/activate
```
</details>
### Getting started
To contribute to the project, fork the repository and clone to your local device
and install preferred testing dependency [pytest](https://github.com/pytest-dev/pytest)
Alternatively, run the following command on your terminal to do so:
```bash
uv sync --dev
```
Testing can be done by the following command post-installation:
```bash
uv sync --dev --extras test
pytest
```
## Changelog
### 🚧 Breaking Changes (0.3.0 -> 0.3.1) The double submit update
* The `generate_csrf` method has now been marked for deprecation
* The recommended method is now `generate_csrf_tokens` which returns a tuple of tokens, first unsigned
and the latter signed
* Recommended pattern is for the first token is aimed for returning as part of context
* Recommended pattern is for the signed token to be set in client's cookie completing [Double Submit Cookie](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#double-submit-cookie)
* To prevent token reuse, protected endpoint can unset the signed CSRF Token in client's cookies as
per example code and recommended pattern.
### 🚧 Breaking Changes (0.3.1 -> 0.3.2) The anti-JavaScript update
* New keys are added at setup `token_location` (either `body` or `header`) and `token_key` is key
where form-encoded keeps the csrf token stored, cross-checked with csrf secret in cookies.
* Asynchronous `validate_csrf` method now needs to be awaited therefore protected endpoints need to
be asynchronous as well.
### Error in version 0.3.5 after updating to Pydantic V2
* Made a blunder when updating from Pydantic V1 to Pydantic V2 and caused an error to occur when
setting `cookie_samesite` in settings
* Fixed in version `0.3.6`
### Version 1.0
* Remove deprecated method `generate_csrf`, please use `generate_csrf_tokens`.
* Validate `FormData` value received for given `token_key` is in fact a string, not `UploadFile`
### Version 1.0.1
* Fix cookie unsetting when configuring library with cookie `Secure` and / or `SameSite=None`
* Test cookie settings covering `SameSite` options and `Secure` usage
* Bypass `https` tests using manual `test_client.base_url = 'https://testserver'`
### Run Examples
To run the provided examples, first you must install extra dependencies [uvicorn](https://github.com/encode/uvicorn) and [jinja2](https://github.com/pallets/jinja/)
Alternatively, run the following command on your terminal to do so
```bash
uv sync --extras examples
```
Running the example utilizing form submission
```bash
uvicorn examples.body:app
```
Running the example utilizing headers via JavaScript
```bash
uvicorn examples.header:app
```
## License
This project is licensed under the terms of the MIT license.
Raw data
{
"_id": null,
"home_page": null,
"name": "fastapi-csrf-protect",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.9",
"maintainer_email": null,
"keywords": "asynchronous, cross-site request forgery, csrf, fastapi, samesite, starlette, xsrf",
"author": null,
"author_email": "Sitt Guruvanich <aekazitt+github@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/31/f1/fd4f22038e806703ed1b009cde5ab1a77458cf6207b89c16cbeb34bacb6f/fastapi_csrf_protect-1.0.2.tar.gz",
"platform": null,
"description": "# FastAPI CSRF Protect\n\n[](https://pypi.org/project/fastapi-csrf-protect)\n[](https://pypi.org/project/fastapi-csrf-protect)\n[](https://pypi.org/project/fastapi-csrf-protect)\n[](https://pypi.org/project/fastapi-csrf-protect)\n[](.)\n[](.)\n[](.)\n[](.)\n[](https://aekasitt.github.io/fastapi-csrf-protect)\n\n[](https://github.com/aekasitt/fastapi-csrf-protect/blob/master/static/protect-banner.svg)\n\n## Features\n\nFastAPI extension that provides stateless Cross-Site Request Forgery (XSRF) Protection support.\nAimed to be easy to use and lightweight, we adopt [Double Submit Cookie](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#double-submit-cookie) mitigation pattern.\nIf you were familiar with `flask-wtf` library this extension suitable for you.\nThis extension inspired by `fastapi-jwt-auth` \ud83d\ude00\n\n- Storing `fastapi-csrf-token` in cookies or serve it in template's context\n\n## Installation\n\nThe easiest way to start working with this extension with pip\n\n```bash\npip install fastapi-csrf-protect\n# or\nuv add fastapi-csrf-protect\n```\n\n## Getting Started\n\nThe following examples show you how to integrate this extension to a FastAPI App\n\n### Example Login Form\n\n```python\nfrom fastapi import FastAPI, Request, Depends\nfrom fastapi.responses import JSONResponse\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\")\n\nclass CsrfSettings(BaseSettings):\n secret_key: str = \"asecrettoeverybody\"\n cookie_samesite: str = \"none\"\n\n@CsrfProtect.load_config\ndef get_csrf_config():\n return CsrfSettings()\n\n@app.get(\"/login\")\ndef form(request: Request, csrf_protect: CsrfProtect = Depends()):\n \"\"\"\n Returns form template.\n \"\"\"\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 create_post(request: Request, csrf_protect: CsrfProtect = Depends()):\n \"\"\"\n Creates a new Post\n \"\"\"\n await csrf_protect.validate_csrf(request)\n response: JSONResponse = JSONResponse(status_code=200, content={\"detail\": \"OK\"})\n csrf_protect.unset_csrf_cookie(response) # prevent token reuse\n return response\n\n@app.exception_handler(CsrfProtectError)\ndef csrf_protect_exception_handler(request: Request, exc: CsrfProtectError):\n return JSONResponse(status_code=exc.status_code, content={\"detail\": exc.message})\n\n```\n\n## Contributions\n\n### Prerequisites\n\n\n* [python](https://www.python.org) version 3.9 and above\n* [uv](https://docs.astral.sh/uv)\n\n### Setting up\n\nThe following guide walks through setting up your local working environment using `pyenv`\nas Python version manager and `uv` as Python package manager. If you do not have `pyenv`\ninstalled, run the following command.\n\n<details>\n <summary> Install using Homebrew (Darwin) </summary>\n \n ```sh\n brew install pyenv --head\n ```\n</details>\n\n<details>\n <summary> Install using standalone installer (Darwin and Linux) </summary>\n \n ```sh\n curl https://pyenv.run | bash\n ```\n</details>\n\nIf you do not have `uv` installed, run the following command.\n\n<details>\n <summary> Install using Homebrew (Darwin) </summary>\n\n ```sh\n brew install uv\n ```\n</details>\n\n<details>\n <summary> Install using standalone installer (Darwin and Linux) </summary>\n\n ```sh\n curl -LsSf https://astral.sh/uv/install.sh | sh\n ```\n</details>\n\nOnce you have `pyenv` Python version manager installed, you can\ninstall any version of Python above version 3.9 for this project.\nThe following commands help you set up and activate a Python virtual\nenvironment where `uv` can download project dependencies from the `PyPI`\nopen-sourced registry defined under `pyproject.toml` file.\n\n<details>\n <summary> Set up environment and synchronize project dependencies </summary>\n\n ```sh\n pyenv shell 3.11.9\n uv venv --python-preference system\n source .venv/bin/activate\n ```\n</details>\n\n### Getting started\n\nTo contribute to the project, fork the repository and clone to your local device\nand install preferred testing dependency [pytest](https://github.com/pytest-dev/pytest)\nAlternatively, run the following command on your terminal to do so:\n\n```bash\nuv sync --dev\n```\n\nTesting can be done by the following command post-installation:\n\n```bash\nuv sync --dev --extras test\npytest\n```\n\n## Changelog\n\n### \ud83d\udea7 Breaking Changes (0.3.0 -> 0.3.1) The double submit update\n\n* The `generate_csrf` method has now been marked for deprecation\n* The recommended method is now `generate_csrf_tokens` which returns a tuple of tokens, first unsigned\n and the latter signed\n* Recommended pattern is for the first token is aimed for returning as part of context\n* Recommended pattern is for the signed token to be set in client's cookie completing [Double Submit Cookie](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#double-submit-cookie)\n* To prevent token reuse, protected endpoint can unset the signed CSRF Token in client's cookies as\n per example code and recommended pattern.\n\n### \ud83d\udea7 Breaking Changes (0.3.1 -> 0.3.2) The anti-JavaScript update\n\n* New keys are added at setup `token_location` (either `body` or `header`) and `token_key` is key\n where form-encoded keeps the csrf token stored, cross-checked with csrf secret in cookies.\n* Asynchronous `validate_csrf` method now needs to be awaited therefore protected endpoints need to\n be asynchronous as well.\n\n### Error in version 0.3.5 after updating to Pydantic V2\n\n* Made a blunder when updating from Pydantic V1 to Pydantic V2 and caused an error to occur when\n setting `cookie_samesite` in settings\n* Fixed in version `0.3.6`\n\n### Version 1.0\n\n* Remove deprecated method `generate_csrf`, please use `generate_csrf_tokens`.\n* Validate `FormData` value received for given `token_key` is in fact a string, not `UploadFile`\n\n### Version 1.0.1\n\n* Fix cookie unsetting when configuring library with cookie `Secure` and / or `SameSite=None`\n* Test cookie settings covering `SameSite` options and `Secure` usage\n* Bypass `https` tests using manual `test_client.base_url = 'https://testserver'`\n\n\n### Run Examples\n\nTo run the provided examples, first you must install extra dependencies [uvicorn](https://github.com/encode/uvicorn) and [jinja2](https://github.com/pallets/jinja/)\nAlternatively, run the following command on your terminal to do so\n\n```bash\nuv sync --extras examples\n```\n\nRunning the example utilizing form submission\n\n```bash\nuvicorn examples.body:app\n```\n\nRunning the example utilizing headers via JavaScript\n\n```bash\nuvicorn examples.header:app\n```\n\n## License\n\nThis project is licensed under the terms of the MIT license.\n",
"bugtrack_url": null,
"license": null,
"summary": "Stateless implementation of Cross-Site Request Forgery (XSRF) Protection by using Double Submit Cookie mitigation pattern",
"version": "1.0.2",
"project_urls": null,
"split_keywords": [
"asynchronous",
" cross-site request forgery",
" csrf",
" fastapi",
" samesite",
" starlette",
" xsrf"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "64a047d6f15b605b3e7644ed1803c12f2484c8df11f5a487872112ace2396159",
"md5": "614c5c371e65dcd52d1e88a1e44ce550",
"sha256": "65b34bcb53654b9386416e9c10c4aad3646d8ee86df6dcbae1f3d8a0aed772ad"
},
"downloads": -1,
"filename": "fastapi_csrf_protect-1.0.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "614c5c371e65dcd52d1e88a1e44ce550",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9",
"size": 11609,
"upload_time": "2025-02-10T19:37:49",
"upload_time_iso_8601": "2025-02-10T19:37:49.429325Z",
"url": "https://files.pythonhosted.org/packages/64/a0/47d6f15b605b3e7644ed1803c12f2484c8df11f5a487872112ace2396159/fastapi_csrf_protect-1.0.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "31f1fd4f22038e806703ed1b009cde5ab1a77458cf6207b89c16cbeb34bacb6f",
"md5": "8fda1f302a1c99c649027e329b0bddb4",
"sha256": "70a1c022d345431dc39a7bc5d05df4bd0ddaf1bfb1eeb69a807c6d09c3fbfdfe"
},
"downloads": -1,
"filename": "fastapi_csrf_protect-1.0.2.tar.gz",
"has_sig": false,
"md5_digest": "8fda1f302a1c99c649027e329b0bddb4",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9",
"size": 179653,
"upload_time": "2025-02-10T19:37:51",
"upload_time_iso_8601": "2025-02-10T19:37:51.272667Z",
"url": "https://files.pythonhosted.org/packages/31/f1/fd4f22038e806703ed1b009cde5ab1a77458cf6207b89c16cbeb34bacb6f/fastapi_csrf_protect-1.0.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-02-10 19:37:51",
"github": false,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"lcname": "fastapi-csrf-protect"
}