# FastAPI Zitadel Auth
Simplify OAuth2 authentication in FastAPI apps using [**Zitadel**](https://zitadel.com/) as the identity service,
including token validation, role-based access control, and Swagger UI integration.
<a href="https://github.com/cleanenergyexchange/fastapi-zitadel-auth/actions/workflows/test.yml" target="_blank">
<img src="https://github.com/cleanenergyexchange/fastapi-zitadel-auth/actions/workflows/test.yml/badge.svg" alt="Test status">
</a>
<a href="https://codecov.io/gh/cleanenergyexchange/fastapi-zitadel-auth">
<img src="https://codecov.io/gh/cleanenergyexchange/fastapi-zitadel-auth/graph/badge.svg?token=A3TSXDVLQT" alt="Code coverage"/>
</a>
<a href="https://pypi.org/pypi/fastapi-zitadel-auth">
<img src="https://img.shields.io/pypi/v/fastapi-zitadel-auth.svg?logo=pypi&logoColor=white&label=pypi" alt="Package version">
</a>
<a href="https://python.org">
<img src="https://img.shields.io/badge/python-v3.10+-blue.svg?logo=python&logoColor=white&label=python" alt="Python versions">
</a>
<a href="https://github.com/cleanenergyexchange/fastapi-zitadel-auth/blob/main/LICENSE">
<img src="https://badgen.net/github/license/cleanenergyexchange/fastapi-zitadel-auth/" alt="License"/>
</a>
## Features
* Authorization Code flow with PKCE
* JWT validation using Zitadel JWKS
* Role-based access control using Zitadel roles
* Service user authentication (JWT Profile)
* Swagger UI integration
* Type-safe token validation
> [!NOTE]
> This library implements JWT, locally validated using JWKS, as it prioritizes performance,
> see [Zitadel docs on Opaque tokens vs JWT](https://zitadel.com/docs/concepts/knowledge/opaque-tokens#use-cases-and-trade-offs).
> If you need to validate opaque tokens using Introspection, please open an issue – PRs are welcome!
## Installation and quick start
```bash
pip install fastapi-zitadel-auth
```
```python
from fastapi import FastAPI, Security
from fastapi_zitadel_auth import ZitadelAuth, AuthConfig
auth = ZitadelAuth(AuthConfig(
client_id="your-client-id",
project_id="your-project-id",
zitadel_host="https://your-instance.zitadel.cloud"
))
app = FastAPI(
swagger_ui_init_oauth={
"usePkceWithAuthorizationCodeGrant": True,
"clientId": 'your-client-id',
"scopes": "openid profile email urn:zitadel:iam:org:project:id:zitadel:aud urn:zitadel:iam:org:projects:roles"
}
)
@app.get("/protected", dependencies=[Security(auth)])
def protected_route():
return {"message": "Access granted!"}
```
See the [Usage](#usage) section for more details.
## Usage
### Configuration
#### Zitadel
Set up a project in Zitadel according to [docs/ZITADEL_SETUP.md](docs/ZITADEL_SETUP.md).
#### FastAPI
```python
from fastapi import FastAPI, Request, Security
from fastapi_zitadel_auth import ZitadelAuth, AuthConfig
# Your Zitadel configuration
CLIENT_ID = 'your-zitadel-client-id'
PROJECT_ID = 'your-zitadel-project-id'
BASE_URL = 'https://your-instance-xyz.zitadel.cloud'
# Create an AuthConfig object with your Zitadel configuration
config = AuthConfig(
client_id=CLIENT_ID,
project_id=PROJECT_ID,
zitadel_host=BASE_URL,
scopes={
"openid": "OpenID Connect",
"email": "Email",
"profile": "Profile",
"urn:zitadel:iam:org:project:id:zitadel:aud": "Audience",
"urn:zitadel:iam:org:projects:roles": "Roles",
},
)
# Create a ZitadelAuth object with the AuthConfig usable as a FastAPI dependency
auth = ZitadelAuth(config)
# Create a FastAPI app and configure Swagger UI
app = FastAPI(
title="fastapi-zitadel-auth demo",
swagger_ui_oauth2_redirect_url="/oauth2-redirect",
swagger_ui_init_oauth={
"usePkceWithAuthorizationCodeGrant": True,
"clientId": CLIENT_ID,
"scopes": " ".join(
[
"openid",
"email",
"profile",
"urn:zitadel:iam:org:project:id:zitadel:aud",
"urn:zitadel:iam:org:projects:roles",
]
),
},
)
# Create an endpoint and protect it with the ZitadelAuth dependency
@app.get(
"/api/private",
summary="Private endpoint, requiring a valid token with `system` scope",
dependencies=[Security(auth, scopes=["system"])],
)
def private(request: Request):
return {
"message": f"Hello, protected world! Here is Zitadel user {request.state.user.user_id}"
}
```
## Demo app
See `demo_project` for a complete example, including service user login. To run the demo app:
```bash
uv run demo_project/server.py
```
Then navigate to `http://localhost:8001/docs` to see the Swagger UI.
### Service user
Service users are "machine users" in Zitadel.
To log in as a service user, change the config in `demo_project/service_user.py`, then
```bash
uv run demo_project/service_user.py
```
Make sure you have a running server at `http://localhost:8001`.
## Development
See [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md) for development instructions.
Raw data
{
"_id": null,
"home_page": null,
"name": "fastapi-zitadel-auth",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": null,
"keywords": "async, asyncio, authentication, fastapi, oauth, oidc, zitadel",
"author": null,
"author_email": "Clean Energy Exchange <info@ceex.ch>",
"download_url": "https://files.pythonhosted.org/packages/ef/d3/daca50aa1c99d4931c89f11397fd8305ff0e4c1ceef4e6db9853fc5cdaee/fastapi_zitadel_auth-0.1.1.tar.gz",
"platform": null,
"description": "# FastAPI Zitadel Auth\n\nSimplify OAuth2 authentication in FastAPI apps using [**Zitadel**](https://zitadel.com/) as the identity service, \nincluding token validation, role-based access control, and Swagger UI integration.\n\n\n<a href=\"https://github.com/cleanenergyexchange/fastapi-zitadel-auth/actions/workflows/test.yml\" target=\"_blank\">\n <img src=\"https://github.com/cleanenergyexchange/fastapi-zitadel-auth/actions/workflows/test.yml/badge.svg\" alt=\"Test status\">\n</a>\n<a href=\"https://codecov.io/gh/cleanenergyexchange/fastapi-zitadel-auth\">\n <img src=\"https://codecov.io/gh/cleanenergyexchange/fastapi-zitadel-auth/graph/badge.svg?token=A3TSXDVLQT\" alt=\"Code coverage\"/> \n</a>\n<a href=\"https://pypi.org/pypi/fastapi-zitadel-auth\">\n <img src=\"https://img.shields.io/pypi/v/fastapi-zitadel-auth.svg?logo=pypi&logoColor=white&label=pypi\" alt=\"Package version\">\n</a>\n<a href=\"https://python.org\">\n <img src=\"https://img.shields.io/badge/python-v3.10+-blue.svg?logo=python&logoColor=white&label=python\" alt=\"Python versions\">\n</a>\n<a href=\"https://github.com/cleanenergyexchange/fastapi-zitadel-auth/blob/main/LICENSE\">\n <img src=\"https://badgen.net/github/license/cleanenergyexchange/fastapi-zitadel-auth/\" alt=\"License\"/>\n</a>\n\n\n## Features\n\n* Authorization Code flow with PKCE\n* JWT validation using Zitadel JWKS\n* Role-based access control using Zitadel roles\n* Service user authentication (JWT Profile)\n* Swagger UI integration\n* Type-safe token validation\n\n\n> [!NOTE]\n> This library implements JWT, locally validated using JWKS, as it prioritizes performance, \n> see [Zitadel docs on Opaque tokens vs JWT](https://zitadel.com/docs/concepts/knowledge/opaque-tokens#use-cases-and-trade-offs).\n> If you need to validate opaque tokens using Introspection, please open an issue \u2013 PRs are welcome!\n\n\n## Installation and quick start\n\n```bash\npip install fastapi-zitadel-auth\n```\n\n```python\nfrom fastapi import FastAPI, Security\nfrom fastapi_zitadel_auth import ZitadelAuth, AuthConfig\n\nauth = ZitadelAuth(AuthConfig(\n client_id=\"your-client-id\",\n project_id=\"your-project-id\",\n zitadel_host=\"https://your-instance.zitadel.cloud\"\n))\n\napp = FastAPI(\n swagger_ui_init_oauth={\n \"usePkceWithAuthorizationCodeGrant\": True,\n \"clientId\": 'your-client-id',\n \"scopes\": \"openid profile email urn:zitadel:iam:org:project:id:zitadel:aud urn:zitadel:iam:org:projects:roles\"\n }\n)\n\n\n@app.get(\"/protected\", dependencies=[Security(auth)])\ndef protected_route():\n return {\"message\": \"Access granted!\"}\n```\n\nSee the [Usage](#usage) section for more details.\n\n## Usage\n\n### Configuration\n\n#### Zitadel\n\nSet up a project in Zitadel according to [docs/ZITADEL_SETUP.md](docs/ZITADEL_SETUP.md).\n\n#### FastAPI\n\n```python\nfrom fastapi import FastAPI, Request, Security\nfrom fastapi_zitadel_auth import ZitadelAuth, AuthConfig\n\n# Your Zitadel configuration\nCLIENT_ID = 'your-zitadel-client-id'\nPROJECT_ID = 'your-zitadel-project-id'\nBASE_URL = 'https://your-instance-xyz.zitadel.cloud'\n\n# Create an AuthConfig object with your Zitadel configuration\nconfig = AuthConfig(\n client_id=CLIENT_ID,\n project_id=PROJECT_ID,\n zitadel_host=BASE_URL,\n scopes={\n \"openid\": \"OpenID Connect\",\n \"email\": \"Email\",\n \"profile\": \"Profile\",\n \"urn:zitadel:iam:org:project:id:zitadel:aud\": \"Audience\",\n \"urn:zitadel:iam:org:projects:roles\": \"Roles\",\n },\n)\n\n# Create a ZitadelAuth object with the AuthConfig usable as a FastAPI dependency\nauth = ZitadelAuth(config)\n\n# Create a FastAPI app and configure Swagger UI\napp = FastAPI(\n title=\"fastapi-zitadel-auth demo\",\n swagger_ui_oauth2_redirect_url=\"/oauth2-redirect\",\n swagger_ui_init_oauth={\n \"usePkceWithAuthorizationCodeGrant\": True,\n \"clientId\": CLIENT_ID,\n \"scopes\": \" \".join(\n [\n \"openid\",\n \"email\",\n \"profile\",\n \"urn:zitadel:iam:org:project:id:zitadel:aud\",\n \"urn:zitadel:iam:org:projects:roles\",\n ]\n ),\n },\n)\n\n\n# Create an endpoint and protect it with the ZitadelAuth dependency\n@app.get(\n \"/api/private\",\n summary=\"Private endpoint, requiring a valid token with `system` scope\",\n dependencies=[Security(auth, scopes=[\"system\"])],\n)\ndef private(request: Request):\n return {\n \"message\": f\"Hello, protected world! Here is Zitadel user {request.state.user.user_id}\"\n }\n\n```\n\n## Demo app\n\nSee `demo_project` for a complete example, including service user login. To run the demo app:\n\n```bash\nuv run demo_project/server.py\n```\n\nThen navigate to `http://localhost:8001/docs` to see the Swagger UI.\n\n\n### Service user\n\nService users are \"machine users\" in Zitadel.\n\nTo log in as a service user, change the config in `demo_project/service_user.py`, then\n\n```bash\nuv run demo_project/service_user.py\n```\n\nMake sure you have a running server at `http://localhost:8001`.\n\n## Development\n\nSee [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md) for development instructions.",
"bugtrack_url": null,
"license": null,
"summary": "Zitadel authentication for FastAPI",
"version": "0.1.1",
"project_urls": null,
"split_keywords": [
"async",
" asyncio",
" authentication",
" fastapi",
" oauth",
" oidc",
" zitadel"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "9095d10813faed0658e20c62d7d416b6725553f7a4e2a13e5a462679ea7f9a7a",
"md5": "17c09d0731ef5ece04bd4b95de52148b",
"sha256": "f651acd270cdf5937bcf111c81fbeace1327f96c22aee305f67cb409eea7e2c9"
},
"downloads": -1,
"filename": "fastapi_zitadel_auth-0.1.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "17c09d0731ef5ece04bd4b95de52148b",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 9604,
"upload_time": "2024-12-05T15:48:29",
"upload_time_iso_8601": "2024-12-05T15:48:29.193669Z",
"url": "https://files.pythonhosted.org/packages/90/95/d10813faed0658e20c62d7d416b6725553f7a4e2a13e5a462679ea7f9a7a/fastapi_zitadel_auth-0.1.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "efd3daca50aa1c99d4931c89f11397fd8305ff0e4c1ceef4e6db9853fc5cdaee",
"md5": "1f33aef0e5ffe8fb69c8d0d175c6fdc2",
"sha256": "2920d1d2af6a10fcba9b84edc91ff5592e07b7d0f1cef1989d22326dca23f0ef"
},
"downloads": -1,
"filename": "fastapi_zitadel_auth-0.1.1.tar.gz",
"has_sig": false,
"md5_digest": "1f33aef0e5ffe8fb69c8d0d175c6fdc2",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 57226,
"upload_time": "2024-12-05T15:48:30",
"upload_time_iso_8601": "2024-12-05T15:48:30.220231Z",
"url": "https://files.pythonhosted.org/packages/ef/d3/daca50aa1c99d4931c89f11397fd8305ff0e4c1ceef4e6db9853fc5cdaee/fastapi_zitadel_auth-0.1.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-12-05 15:48:30",
"github": false,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"lcname": "fastapi-zitadel-auth"
}