FastAPI authentication library with JWT access/refresh tokens, sync & async SQLAlchemy support, built-in auth routes (register/login/refresh/me), secure password hashing, and cookie/header token support.
Project description
FastAPI JWT Auth Kit
FastAPI authentication library with JWT access/refresh tokens, sync & async SQLAlchemy support, built-in auth routes (register/login/refresh/me), secure password hashing, and cookie/header token support.
Complete authentication toolkit for FastAPI with JWT, designed for both sync and async SQLAlchemy backends. Provides batteries-included auth services, FastAPI routers, token utilities, and a clean protocol-driven repository interface.
Highlights
- JWT access + refresh tokens with rotation
- Sync and async SQLAlchemy adapters
- Drop-in FastAPI routers for register, login, refresh, logout, and
/me - Cookie and Authorization header support
- Strong typing and protocol-based extensibility
- Works with your own User model
Package Layout
packages/authkit/src/authkit/
fastapi/ # FastAPI router builders + schemas
ext/ # SQLAlchemy protocol adapters (sync/async)
authenticator.py
service.py
tokens.py
hashing.py
settings.py
extractors.py
Installation
From this repo
uv sync
In your project
pip install fastapi-jwt-authkit
The PyPI package name is
fastapi-jwt-authkit, the import name isauthkit.
Typing support
Type information (.pyi + py.typed) is bundled with the package for IDEs and
static type checkers.
Quickstart (Async)
from fastapi import FastAPI
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker
from authkit import AuthSettings
from authkit.fastapi.routers import build_auth_router_async
from your_app.models import Base, User
DATABASE_URL = "sqlite+aiosqlite:///./app.db"
engine = create_async_engine(DATABASE_URL, echo=False)
SessionLocal = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
async def get_session():
async with SessionLocal() as session:
try:
yield session
await session.commit()
except Exception:
await session.rollback()
raise
settings = AuthSettings(secret_key="change-me", cookie_secure=False)
app = FastAPI()
auth_router = build_auth_router_async(
settings=settings,
get_session=get_session,
user_model=User,
)
app.include_router(auth_router, prefix="/auth", tags=["auth"])
Quickstart (Sync)
from fastapi import FastAPI
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from authkit import AuthSettings
from authkit.fastapi.routers import build_auth_router_sync
from your_app.models import Base, User
DATABASE_URL = "sqlite:///./app.db"
engine = create_engine(DATABASE_URL, echo=False)
SessionLocal = sessionmaker(bind=engine, autocommit=False, autoflush=False)
def get_session():
session = SessionLocal()
try:
yield session
session.commit()
except Exception:
session.rollback()
raise
finally:
session.close()
settings = AuthSettings(secret_key="change-me", cookie_secure=False)
app = FastAPI()
auth_router = build_auth_router_sync(
settings=settings,
get_session=get_session,
user_models=User,
)
app.include_router(auth_router, prefix="/auth", tags=["auth"])
Core Concepts
AuthSettings
All auth behavior is configured via AuthSettings:
AuthSettings(
secret_key="your-secret",
algorithm="HS256",
access_minutes=15,
refresh_days=7,
accept_header=True,
accept_cookie=True,
set_cookie_on_login=True,
cookie_secure=True,
cookie_samesite="lax",
)
AuthService / AsyncAuthService
Business logic for creating users, authenticating, and issuing tokens. Uses a repository protocol so you can plug in your own persistence layer.
BaseUser Model (Extendable)
Use BaseUser mixin to automatically get all required fields:
from authkit.fastapi.models import BaseUser
from sqlalchemy.orm import DeclarativeBase, Mapped
from sqlalchemy import String
class Base(DeclarativeBase):
pass
class User(BaseUser, Base):
__tablename__ = "users"
# Required fields inherited from BaseUser:
# id, email, username, password_hash, is_active, is_staff, is_superuser
# Add your custom fields:
phone_number: Mapped[str] = mapped_column(String(20), nullable=True)
avatar_url: Mapped[str] = mapped_column(String(255), nullable=True)
SQLAlchemy Adapters
SQLAlchemySyncUserProtocolSQLAlchemyAsyncUserProtocol
These adapters work with any model that implements the UserProtocol interface
(including models extending BaseUser).
API Endpoints
When you include the auth router with prefix /auth, the following endpoints
are available:
| Method | Path | Description |
|---|---|---|
| POST | /auth/register | Create a new user |
| POST | /auth/login | Authenticate + issue tokens |
| POST | /auth/refresh | Refresh access token (accepts token from header, cookie, or body) |
| POST | /auth/logout | Clear auth cookies |
| GET | /auth/me | Get current user |
Request/Response Schemas
Register
{ "email": "user@example.com", "username": "user", "password": "secret" }
Login
{ "username_or_email": "user", "password": "secret" }
Refresh (token can be in header, cookie, or body)
# Option 1: In request body
{ "refresh_token": "jwt" }
# Option 2: Authorization header
Authorization: Bearer <refresh_token>
# Option 3: Cookie (set automatically on login)
Cookie: refresh_token=<refresh_token>
Token Response
{ "access_token": "jwt", "refresh_token": "jwt" }
Cookies and Headers
AuthSettings controls where tokens are accepted and how they are stored:
accept_header: allowAuthorization: Bearer <token>accept_cookie: allow cookiesset_cookie_on_login: set cookies on successful login
Cookie names and TTL are customizable with:
cookie_name_access, cookie_name_refresh, cookie_max_age_access,
cookie_max_age_refresh.
Refresh Token Sources
The /auth/refresh endpoint accepts refresh tokens from multiple sources (checked in order):
- Authorization header:
Authorization: Bearer <refresh_token> - Cookie:
refresh_tokencookie - Request body:
{"refresh_token": "<token>"}
Example requests:
# Header
curl -X POST /auth/refresh -H "Authorization: Bearer <refresh_token>"
# Cookie (set automatically on login if enabled)
curl -X POST /auth/refresh --cookie "refresh_token=<token>"
# Body
curl -X POST /auth/refresh -d '{"refresh_token": "<token>"}'
Security Considerations
- Use a strong
secret_keyand rotate regularly. - Set
cookie_secure=Truein production (HTTPS only). - Consider
cookie_samesite="strict"for web apps with tight CSRF control. - Short access token TTLs with longer refresh TTLs are recommended.
Production Checklist
-
secret_keystored in a secure secret manager - HTTPS enforced
-
cookie_secure=True,cookie_samesiteset per app policy - Rotate JWT secret or use KMS-backed signing
- Enable logging around login and refresh flows
- Implement account lockout or rate limiting at the API gateway
- Configure backups for the user datastore
Compatibility
- Python: 3.10+
- FastAPI: 0.110+
- SQLAlchemy: 2.x
Observability
This library raises standard HTTPException errors. For production:
- Add structured logging around auth endpoints
- Add tracing/metrics at the FastAPI middleware layer
Versioning
Follows semantic versioning: MAJOR.MINOR.PATCH.
Testing
Run the full test suite:
uv run pytest tests/ -v
All tests are under tests/ and cover tokens, hashing, services, and router
integration (sync + async).
Examples
Working example apps are provided:
examples/async_app.pyexamples/sync_app.py
Run:
uv run python -m examples.async_app
uv run python -m examples.sync_app
Extending the Repo Layer
Implement the repo protocol if you use a different persistence layer:
class MyRepo:
def get_by_id(self, user_id: int): ...
def get_by_email_or_username(self, value: str): ...
def create_user(self, *, email: str, username: str, password: str,
is_staff: bool, is_active: bool, is_superuser: bool): ...
Async version must expose the same methods as async def.
Typing Notes
Type information is bundled with the package (.pyi + py.typed). If your IDE
or type checker does not pick it up, ensure:
- Your tooling supports PEP 561
Troubleshooting
Bcrypt errors on Windows
If you see bcrypt backend errors during hashing, ensure you have bcrypt<5
installed. This repo pins it accordingly in pyproject.toml.
SQLite in-memory tests
In-memory SQLite needs a StaticPool so all sessions share the same DB
connection. The tests already handle this.
License
MIT (see LICENSE).
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fastapi_jwt_authkit-1.0.1.tar.gz.
File metadata
- Download URL: fastapi_jwt_authkit-1.0.1.tar.gz
- Upload date:
- Size: 158.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.15 {"installer":{"name":"uv","version":"0.9.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ed5f036cc0bf698ae89ec0ee92efeeca2b98f9c96bee30361415617e5f2cf205
|
|
| MD5 |
5cdb0a5829d3407f95757c9a5ade072e
|
|
| BLAKE2b-256 |
4df88ad1fa4f9723b55ac7ef857f3c46f4006c19e546869aa6779bd3894862a8
|
File details
Details for the file fastapi_jwt_authkit-1.0.1-py3-none-any.whl.
File metadata
- Download URL: fastapi_jwt_authkit-1.0.1-py3-none-any.whl
- Upload date:
- Size: 19.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.15 {"installer":{"name":"uv","version":"0.9.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bf94bc1b588ed69e563b398552534f847ca0c87bedc2d019655c3eaf1456f2dd
|
|
| MD5 |
3ddebe4800ddecca47c7a1766c4bdd47
|
|
| BLAKE2b-256 |
c0762d28fb6fa0660f5ece01706fe6326ffdb4a401aaf813cdad2e7b96a988a4
|