# FastAPI JWT Harmony ๐ต
[](https://www.python.org/downloads/)
[](https://fastapi.tiangolo.com/)
[](https://pypi.org/project/fastapi-jwt-harmony/)
[](https://opensource.org/licenses/MIT)
[](https://github.com/astral-sh/ruff)
A modern, type-safe JWT authentication library for FastAPI with **Pydantic integration** - bringing harmony to your auth flow! ๐ถ
## ๐ Key Features
- ๐ **Type-safe JWT authentication** with full Pydantic model support
- ๐ **FastAPI dependency injection** - automatic JWT validation
- ๐ **Multiple token locations** - headers, cookies, or both
- ๐ก๏ธ **CSRF protection** for cookie-based authentication
- ๐ **WebSocket support** with dedicated authentication methods
- ๐ค **User claims as Pydantic models** - strongly typed user data
- ๐ซ **Token denylist/blacklist** support for logout functionality
- ๐ **Asymmetric algorithms** support (RS256, ES256, etc.)
- โ
**100% test coverage** with comprehensive test suite
## ๐ Quick Start
### Installation
```bash
pip install fastapi-jwt-harmony
```
For asymmetric algorithm support:
```bash
pip install fastapi-jwt-harmony[asymmetric]
```
### Basic Example
```python
from fastapi import FastAPI, Depends
from pydantic import BaseModel
from fastapi_jwt_harmony import JWTHarmony, JWTHarmonyDep
app = FastAPI()
# Define your user model
class User(BaseModel):
id: str
username: str
email: str
# Configure JWT (simple way with dict)
JWTHarmony.configure(
User,
{
"secret_key": "your-secret-key", # pragma: allowlist secret
"token_location": {"headers", "cookies"} # Support both
}
)
# Or use JWTHarmonyConfig for advanced configuration
# from fastapi_jwt_harmony import JWTHarmonyConfig
# JWTHarmony.configure(
# User,
# JWTHarmonyConfig(
# secret_key="your-secret-key", # pragma: allowlist secret
# token_location={"headers", "cookies"}
# )
# )
@app.post("/login")
def login(Authorize: JWTHarmony[User] = Depends()):
# Authenticate user (your logic here)
user = User(id="123", username="john", email="john@example.com")
# Create tokens
access_token = Authorize.create_access_token(user_claims=user)
refresh_token = Authorize.create_refresh_token(user_claims=user)
# Set cookies (optional)
Authorize.set_access_cookies(access_token)
Authorize.set_refresh_cookies(refresh_token)
return {"access_token": access_token}
@app.get("/protected")
def protected_route(Authorize: JWTHarmony[User] = Depends(JWTHarmonyDep)):
# JWT automatically validated by JWTHarmonyDep
current_user = Authorize.user_claims # Typed User model!
return {"user": current_user, "message": f"Hello {current_user.username}!"}
```
## ๐ฆ Dependencies Overview
FastAPI JWT Harmony provides several dependency types for different authentication needs:
```python
from fastapi_jwt_harmony import (
JWTHarmonyDep, # Requires valid access token
JWTHarmonyOptional, # Optional JWT validation
JWTHarmonyRefresh, # Requires valid refresh token
JWTHarmonyFresh, # Requires fresh access token
JWTHarmonyBare, # No automatic validation
)
@app.get("/public")
def public_endpoint(Authorize: JWTHarmony[User] = Depends(JWTHarmonyOptional)):
if Authorize.user_claims:
return {"message": f"Hello {Authorize.user_claims.username}!"}
return {"message": "Hello anonymous user!"}
@app.post("/sensitive-action")
def sensitive_action(Authorize: JWTHarmony[User] = Depends(JWTHarmonyFresh)):
# Requires fresh token (just logged in)
return {"message": "Sensitive action performed"}
```
## ๐ช Cookie Authentication
Enable secure cookie-based authentication with CSRF protection:
```python
from fastapi import Response
JWTHarmony.configure(
User,
{
"secret_key": "your-secret-key", # pragma: allowlist secret
"token_location": {"cookies"},
"cookie_csrf_protect": True,
"cookie_secure": True, # HTTPS only
"cookie_samesite": "strict"
}
)
@app.post("/login")
def login(response: Response, Authorize: JWTHarmony[User] = Depends()):
user = User(id="123", username="john", email="john@example.com")
access_token = Authorize.create_access_token(user_claims=user)
# Set secure cookies
Authorize.set_access_cookies(access_token, response)
return {"message": "Logged in successfully"}
@app.post("/logout")
def logout(response: Response, Authorize: JWTHarmony[User] = Depends(JWTHarmonyDep)):
Authorize.unset_jwt_cookies(response)
return {"message": "Logged out successfully"}
```
## ๐ WebSocket Authentication
Authenticate WebSocket connections with dedicated methods:
```python
from fastapi import WebSocket, Query
from fastapi_jwt_harmony import JWTHarmonyWS, JWTHarmonyWebSocket
@app.websocket("/ws")
async def websocket_endpoint(
websocket: WebSocket,
token: str = Query(...),
Authorize: JWTHarmonyWS = Depends(JWTHarmonyWebSocket)
):
await websocket.accept()
try:
# Validate JWT token
Authorize.jwt_required(token)
user = Authorize.user_claims
await websocket.send_text(f"Hello {user.username}!")
except Exception as e:
await websocket.send_text(f"Authentication failed: {str(e)}")
await websocket.close()
```
## ๐ซ Token Denylist (Logout)
Implement secure logout with token blacklisting:
```python
# In-memory denylist (use Redis in production)
denylist = set()
def check_if_token_revoked(jwt_payload: dict) -> bool:
jti = jwt_payload.get("jti")
return jti in denylist
# Configure with denylist callback
JWTHarmony.configure(
User,
{
"secret_key": "your-secret-key", # pragma: allowlist secret
"denylist_enabled": True,
"denylist_token_checks": {"access", "refresh"}
},
denylist_callback=check_if_token_revoked
)
@app.post("/logout")
def logout(Authorize: JWTHarmony[User] = Depends(JWTHarmonyDep)):
jti = Authorize.get_jti()
denylist.add(jti) # Add to denylist
return {"message": "Successfully logged out"}
```
## โ๏ธ Configuration Options
Comprehensive configuration with sensible defaults:
```python
from datetime import timedelta
JWTHarmonyConfig(
# Core settings
secret_key="your-secret-key", # Required for HS256 # pragma: allowlist secret
algorithm="HS256", # JWT algorithm
token_location={"headers"}, # Where to look for tokens
# Token expiration
access_token_expires=timedelta(minutes=15),
refresh_token_expires=timedelta(days=30),
# Headers
header_name="Authorization",
header_type="Bearer",
# Cookies
cookie_secure=False, # Set True for HTTPS
cookie_csrf_protect=True, # CSRF protection
cookie_samesite="strict",
# Asymmetric keys (for RS256, ES256, etc.)
private_key=None, # For signing
public_key=None, # For verification
# Denylist
denylist_enabled=False,
denylist_token_checks={"access", "refresh"},
# Validation
decode_leeway=0, # Clock skew tolerance
decode_audience=None, # Expected audience
decode_issuer=None, # Expected issuer
)
```
## ๐ Asymmetric Algorithms
Support for RS256, ES256, and other asymmetric algorithms:
```python
# Generate keys (example)
private_key = """-----BEGIN PRIVATE KEY----- # pragma: allowlist secret
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC7...
-----END PRIVATE KEY-----""" # pragma: allowlist secret
public_key = """-----BEGIN PUBLIC KEY----- # pragma: allowlist secret
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu7...
-----END PUBLIC KEY-----""" # pragma: allowlist secret
JWTHarmony.configure(
User,
JWTHarmonyConfig(
algorithm="RS256",
private_key=private_key, # For signing tokens # pragma: allowlist secret
public_key=public_key, # For verifying tokens # pragma: allowlist secret
)
)
```
## ๐งช Testing
Run the comprehensive test suite:
```bash
# Install development dependencies
uv sync --dev
# Run tests
uv run pytest
# Run with coverage
uv run pytest --cov=fastapi_jwt_harmony
```
## ๐ ๏ธ Development
```bash
# Clone the repository
git clone https://github.com/ivolnistov/fastapi-jwt-harmony.git
cd fastapi-jwt-harmony
# Install with development dependencies
uv sync --dev
# Run tests
uv run pytest
# Run linting
uv run ruff check src/
uv run mypy src/fastapi_jwt_harmony
uv run pylint src/fastapi_jwt_harmony
```
## ๐ Project Status
- โ
**111 tests passing** - Comprehensive test coverage
- โ
**Type-safe** - Full mypy compatibility
- โ
**Modern Python** - Supports Python 3.11+
- โ
**Production ready** - Used in production applications
## ๐ค Contributing
Contributions are welcome! Please:
1. Fork the repository
2. Create a feature branch
3. Add tests for new functionality
4. Ensure all tests pass
5. Submit a pull request
## ๐ License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## ๐ Acknowledgments
- **FastAPI** for the amazing web framework
- **Pydantic** for data validation and settings management
- **PyJWT** for JWT implementation
- **Original fastapi-jwt-auth** for inspiration
---
**Made with โค๏ธ for the FastAPI community**
Raw data
{
"_id": null,
"home_page": null,
"name": "fastapi-jwt-harmony",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.11",
"maintainer_email": "Ilya Volnistov <i.volnistov@gaijin.team>",
"keywords": "authentication, fastapi, fastapi-jwt, harmony, jwt, pydantic",
"author": null,
"author_email": "Ilya Volnistov <i.volnistov@gaijin.team>",
"download_url": "https://files.pythonhosted.org/packages/93/45/7c9628de5708dd1d24856d994e9d1af35edb0f4feb1cead90c09425781ce/fastapi_jwt_harmony-0.2.0.tar.gz",
"platform": null,
"description": "# FastAPI JWT Harmony \ud83c\udfb5\n\n[](https://www.python.org/downloads/)\n[](https://fastapi.tiangolo.com/)\n[](https://pypi.org/project/fastapi-jwt-harmony/)\n[](https://opensource.org/licenses/MIT)\n[](https://github.com/astral-sh/ruff)\n\nA modern, type-safe JWT authentication library for FastAPI with **Pydantic integration** - bringing harmony to your auth flow! \ud83c\udfb6\n\n## \ud83d\udd11 Key Features\n\n- \ud83d\udd12 **Type-safe JWT authentication** with full Pydantic model support\n- \ud83d\ude80 **FastAPI dependency injection** - automatic JWT validation\n- \ud83d\udccd **Multiple token locations** - headers, cookies, or both\n- \ud83d\udee1\ufe0f **CSRF protection** for cookie-based authentication\n- \ud83c\udf10 **WebSocket support** with dedicated authentication methods\n- \ud83d\udc64 **User claims as Pydantic models** - strongly typed user data\n- \ud83d\udeab **Token denylist/blacklist** support for logout functionality\n- \ud83d\udd10 **Asymmetric algorithms** support (RS256, ES256, etc.)\n- \u2705 **100% test coverage** with comprehensive test suite\n\n## \ud83d\ude80 Quick Start\n\n### Installation\n\n```bash\npip install fastapi-jwt-harmony\n```\n\nFor asymmetric algorithm support:\n```bash\npip install fastapi-jwt-harmony[asymmetric]\n```\n\n### Basic Example\n\n```python\nfrom fastapi import FastAPI, Depends\nfrom pydantic import BaseModel\nfrom fastapi_jwt_harmony import JWTHarmony, JWTHarmonyDep\n\napp = FastAPI()\n\n# Define your user model\nclass User(BaseModel):\n id: str\n username: str\n email: str\n\n# Configure JWT (simple way with dict)\nJWTHarmony.configure(\n User,\n {\n \"secret_key\": \"your-secret-key\", # pragma: allowlist secret\n \"token_location\": {\"headers\", \"cookies\"} # Support both\n }\n)\n\n# Or use JWTHarmonyConfig for advanced configuration\n# from fastapi_jwt_harmony import JWTHarmonyConfig\n# JWTHarmony.configure(\n# User,\n# JWTHarmonyConfig(\n# secret_key=\"your-secret-key\", # pragma: allowlist secret\n# token_location={\"headers\", \"cookies\"}\n# )\n# )\n\n@app.post(\"/login\")\ndef login(Authorize: JWTHarmony[User] = Depends()):\n # Authenticate user (your logic here)\n user = User(id=\"123\", username=\"john\", email=\"john@example.com\")\n\n # Create tokens\n access_token = Authorize.create_access_token(user_claims=user)\n refresh_token = Authorize.create_refresh_token(user_claims=user)\n\n # Set cookies (optional)\n Authorize.set_access_cookies(access_token)\n Authorize.set_refresh_cookies(refresh_token)\n\n return {\"access_token\": access_token}\n\n@app.get(\"/protected\")\ndef protected_route(Authorize: JWTHarmony[User] = Depends(JWTHarmonyDep)):\n # JWT automatically validated by JWTHarmonyDep\n current_user = Authorize.user_claims # Typed User model!\n return {\"user\": current_user, \"message\": f\"Hello {current_user.username}!\"}\n```\n\n## \ud83d\udce6 Dependencies Overview\n\nFastAPI JWT Harmony provides several dependency types for different authentication needs:\n\n```python\nfrom fastapi_jwt_harmony import (\n JWTHarmonyDep, # Requires valid access token\n JWTHarmonyOptional, # Optional JWT validation\n JWTHarmonyRefresh, # Requires valid refresh token\n JWTHarmonyFresh, # Requires fresh access token\n JWTHarmonyBare, # No automatic validation\n)\n\n@app.get(\"/public\")\ndef public_endpoint(Authorize: JWTHarmony[User] = Depends(JWTHarmonyOptional)):\n if Authorize.user_claims:\n return {\"message\": f\"Hello {Authorize.user_claims.username}!\"}\n return {\"message\": \"Hello anonymous user!\"}\n\n@app.post(\"/sensitive-action\")\ndef sensitive_action(Authorize: JWTHarmony[User] = Depends(JWTHarmonyFresh)):\n # Requires fresh token (just logged in)\n return {\"message\": \"Sensitive action performed\"}\n```\n\n## \ud83c\udf6a Cookie Authentication\n\nEnable secure cookie-based authentication with CSRF protection:\n\n```python\nfrom fastapi import Response\n\nJWTHarmony.configure(\n User,\n {\n \"secret_key\": \"your-secret-key\", # pragma: allowlist secret\n \"token_location\": {\"cookies\"},\n \"cookie_csrf_protect\": True,\n \"cookie_secure\": True, # HTTPS only\n \"cookie_samesite\": \"strict\"\n }\n)\n\n@app.post(\"/login\")\ndef login(response: Response, Authorize: JWTHarmony[User] = Depends()):\n user = User(id=\"123\", username=\"john\", email=\"john@example.com\")\n access_token = Authorize.create_access_token(user_claims=user)\n\n # Set secure cookies\n Authorize.set_access_cookies(access_token, response)\n return {\"message\": \"Logged in successfully\"}\n\n@app.post(\"/logout\")\ndef logout(response: Response, Authorize: JWTHarmony[User] = Depends(JWTHarmonyDep)):\n Authorize.unset_jwt_cookies(response)\n return {\"message\": \"Logged out successfully\"}\n```\n\n## \ud83c\udf10 WebSocket Authentication\n\nAuthenticate WebSocket connections with dedicated methods:\n\n```python\nfrom fastapi import WebSocket, Query\nfrom fastapi_jwt_harmony import JWTHarmonyWS, JWTHarmonyWebSocket\n\n@app.websocket(\"/ws\")\nasync def websocket_endpoint(\n websocket: WebSocket,\n token: str = Query(...),\n Authorize: JWTHarmonyWS = Depends(JWTHarmonyWebSocket)\n):\n await websocket.accept()\n try:\n # Validate JWT token\n Authorize.jwt_required(token)\n user = Authorize.user_claims\n\n await websocket.send_text(f\"Hello {user.username}!\")\n except Exception as e:\n await websocket.send_text(f\"Authentication failed: {str(e)}\")\n await websocket.close()\n```\n\n## \ud83d\udeab Token Denylist (Logout)\n\nImplement secure logout with token blacklisting:\n\n```python\n# In-memory denylist (use Redis in production)\ndenylist = set()\n\ndef check_if_token_revoked(jwt_payload: dict) -> bool:\n jti = jwt_payload.get(\"jti\")\n return jti in denylist\n\n# Configure with denylist callback\nJWTHarmony.configure(\n User,\n {\n \"secret_key\": \"your-secret-key\", # pragma: allowlist secret\n \"denylist_enabled\": True,\n \"denylist_token_checks\": {\"access\", \"refresh\"}\n },\n denylist_callback=check_if_token_revoked\n)\n\n@app.post(\"/logout\")\ndef logout(Authorize: JWTHarmony[User] = Depends(JWTHarmonyDep)):\n jti = Authorize.get_jti()\n denylist.add(jti) # Add to denylist\n return {\"message\": \"Successfully logged out\"}\n```\n\n## \u2699\ufe0f Configuration Options\n\nComprehensive configuration with sensible defaults:\n\n```python\nfrom datetime import timedelta\n\nJWTHarmonyConfig(\n # Core settings\n secret_key=\"your-secret-key\", # Required for HS256 # pragma: allowlist secret\n algorithm=\"HS256\", # JWT algorithm\n token_location={\"headers\"}, # Where to look for tokens\n\n # Token expiration\n access_token_expires=timedelta(minutes=15),\n refresh_token_expires=timedelta(days=30),\n\n # Headers\n header_name=\"Authorization\",\n header_type=\"Bearer\",\n\n # Cookies\n cookie_secure=False, # Set True for HTTPS\n cookie_csrf_protect=True, # CSRF protection\n cookie_samesite=\"strict\",\n\n # Asymmetric keys (for RS256, ES256, etc.)\n private_key=None, # For signing\n public_key=None, # For verification\n\n # Denylist\n denylist_enabled=False,\n denylist_token_checks={\"access\", \"refresh\"},\n\n # Validation\n decode_leeway=0, # Clock skew tolerance\n decode_audience=None, # Expected audience\n decode_issuer=None, # Expected issuer\n)\n```\n\n## \ud83d\udd10 Asymmetric Algorithms\n\nSupport for RS256, ES256, and other asymmetric algorithms:\n\n```python\n# Generate keys (example)\nprivate_key = \"\"\"-----BEGIN PRIVATE KEY----- # pragma: allowlist secret\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC7...\n-----END PRIVATE KEY-----\"\"\" # pragma: allowlist secret\n\npublic_key = \"\"\"-----BEGIN PUBLIC KEY----- # pragma: allowlist secret\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu7...\n-----END PUBLIC KEY-----\"\"\" # pragma: allowlist secret\n\nJWTHarmony.configure(\n User,\n JWTHarmonyConfig(\n algorithm=\"RS256\",\n private_key=private_key, # For signing tokens # pragma: allowlist secret\n public_key=public_key, # For verifying tokens # pragma: allowlist secret\n )\n)\n```\n\n## \ud83e\uddea Testing\n\nRun the comprehensive test suite:\n\n```bash\n# Install development dependencies\nuv sync --dev\n\n# Run tests\nuv run pytest\n\n# Run with coverage\nuv run pytest --cov=fastapi_jwt_harmony\n```\n\n## \ud83d\udee0\ufe0f Development\n\n```bash\n# Clone the repository\ngit clone https://github.com/ivolnistov/fastapi-jwt-harmony.git\ncd fastapi-jwt-harmony\n\n# Install with development dependencies\nuv sync --dev\n\n# Run tests\nuv run pytest\n\n# Run linting\nuv run ruff check src/\nuv run mypy src/fastapi_jwt_harmony\nuv run pylint src/fastapi_jwt_harmony\n```\n\n## \ud83d\udcca Project Status\n\n- \u2705 **111 tests passing** - Comprehensive test coverage\n- \u2705 **Type-safe** - Full mypy compatibility\n- \u2705 **Modern Python** - Supports Python 3.11+\n- \u2705 **Production ready** - Used in production applications\n\n## \ud83e\udd1d Contributing\n\nContributions are welcome! Please:\n\n1. Fork the repository\n2. Create a feature branch\n3. Add tests for new functionality\n4. Ensure all tests pass\n5. Submit a pull request\n\n## \ud83d\udcc4 License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n## \ud83d\ude4f Acknowledgments\n\n- **FastAPI** for the amazing web framework\n- **Pydantic** for data validation and settings management\n- **PyJWT** for JWT implementation\n- **Original fastapi-jwt-auth** for inspiration\n\n---\n\n**Made with \u2764\ufe0f for the FastAPI community**\n",
"bugtrack_url": null,
"license": null,
"summary": "FastAPI JWT authentication library with Pydantic integration - bringing harmony to your auth flow",
"version": "0.2.0",
"project_urls": {
"Bug Tracker": "https://github.com/ivolnistov/fastapi-jwt-harmony/issues",
"Changelog": "https://github.com/ivolnistov/fastapi-jwt-harmony/blob/main/CHANGELOG.md",
"Documentation": "https://github.com/ivolnistov/fastapi-jwt-harmony/blob/main/docs/README.md",
"Homepage": "https://github.com/ivolnistov/fastapi-jwt-harmony",
"Repository": "https://github.com/ivolnistov/fastapi-jwt-harmony"
},
"split_keywords": [
"authentication",
" fastapi",
" fastapi-jwt",
" harmony",
" jwt",
" pydantic"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "592b4487802999d3bbe1f7c6dbd1bc939b581e20baff9beac948579dd91114be",
"md5": "fd4b3f3319d299afafb6afa1ed8fa399",
"sha256": "3b3498234af628f373b839fd1750a035e1ebdc6b68cd913886f534e421afb1e9"
},
"downloads": -1,
"filename": "fastapi_jwt_harmony-0.2.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "fd4b3f3319d299afafb6afa1ed8fa399",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.11",
"size": 23361,
"upload_time": "2025-07-18T08:51:07",
"upload_time_iso_8601": "2025-07-18T08:51:07.088743Z",
"url": "https://files.pythonhosted.org/packages/59/2b/4487802999d3bbe1f7c6dbd1bc939b581e20baff9beac948579dd91114be/fastapi_jwt_harmony-0.2.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "93457c9628de5708dd1d24856d994e9d1af35edb0f4feb1cead90c09425781ce",
"md5": "45428c81dac6b018c37361efd369df8f",
"sha256": "c6ca9457925828735c99976eb3499014a02346d353fad32ede54b6c1d4fd92bd"
},
"downloads": -1,
"filename": "fastapi_jwt_harmony-0.2.0.tar.gz",
"has_sig": false,
"md5_digest": "45428c81dac6b018c37361efd369df8f",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.11",
"size": 46001,
"upload_time": "2025-07-18T08:51:08",
"upload_time_iso_8601": "2025-07-18T08:51:08.570045Z",
"url": "https://files.pythonhosted.org/packages/93/45/7c9628de5708dd1d24856d994e9d1af35edb0f4feb1cead90c09425781ce/fastapi_jwt_harmony-0.2.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-18 08:51:08",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "ivolnistov",
"github_project": "fastapi-jwt-harmony",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "fastapi-jwt-harmony"
}