Pluggable, NextAuth-inspired authentication for FastAPI — providers, adapters, JWT, and RBAC without locking you into an ORM.
FastAuth gives you a complete auth system you can mount on any FastAPI app in a few lines: credentials, magic links, Google and GitHub OAuth, passkeys (WebAuthn), JWT access/refresh pairs, cookie delivery with CSRF, email verification, password reset, RBAC, and an event-hook system. It ships a SQLAlchemy adapter (SQLite, PostgreSQL, MySQL) and a protocol-based extension surface so you can bring your own database.
- Providers — email/password, magic links, Google OAuth, GitHub OAuth, passkeys (WebAuthn)
- OAuth account linking — connect extra providers to an existing, authenticated account
- Pluggable adapters — SQLAlchemy (SQLite, PostgreSQL, MySQL) or implement the protocols yourself
- JWT token pairs — stateless access + refresh tokens with a JTI allowlist for replay detection and revocation
- Cookie delivery —
HttpOnly,Secure,SameSitecookies with a built-in CSRF double-submit scheme - Email flows — verification, password reset, email change, magic links, with pluggable transports and overridable Jinja2 templates
- RBAC — roles and fine-grained permissions, protectable per route
- Event hooks — intercept sign-up/sign-in, modify JWT payloads, gate sign-in
- RS256 / RS512 + JWKS — rotate keys and expose
/.well-known/jwks.jsonfor microservices - CLI — scaffold a project, check installed extras, generate a secret
- Python 3.11+
- FastAPI (installed via the
fastapiextra, included instandard)
pip install "sreekarnv-fastauth[standard]"| Extra | Includes |
|---|---|
standard |
FastAPI, uvicorn, joserfc + cryptography (JWT), SQLAlchemy + aiosqlite, argon2-cffi |
oauth |
httpx (Google, GitHub) |
webauthn |
webauthn (passkeys) |
email |
aiosmtplib, Jinja2 |
redis |
redis-py async |
postgresql |
asyncpg |
cli |
typer, rich |
all |
everything above |
For a typical app with cookie-based auth and email:
pip install "sreekarnv-fastauth[standard,email]"main.py — credentials provider, SQLite, JSON tokens, one protected route:
from contextlib import asynccontextmanager
from fastapi import Depends, FastAPI
from fastauth import FastAuth, FastAuthConfig
from fastauth.adapters.sqlalchemy import SQLAlchemyAdapter
from fastauth.api.deps import require_auth
from fastauth.providers.credentials import CredentialsProvider
from fastauth.types import UserData
adapter = SQLAlchemyAdapter(engine_url="sqlite+aiosqlite:///./auth.db")
auth = FastAuth(FastAuthConfig(
secret="change-me-in-production-min-32-bytes!!", # `fastauth generate-secret`
providers=[CredentialsProvider()],
adapter=adapter.user,
token_adapter=adapter.token,
))
@asynccontextmanager
async def lifespan(app: FastAPI):
await adapter.create_tables()
yield
app = FastAPI(lifespan=lifespan)
auth.mount(app) # mounts /auth/register, /auth/login, /auth/logout, /auth/me, /auth/refresh, ...
@app.get("/dashboard")
async def dashboard(user: UserData = Depends(require_auth)):
return {"hello": user["email"]}Run it:
uvicorn main:app --reload
curl -X POST http://localhost:8000/auth/register \
-H "Content-Type: application/json" \
-d '{"email":"a@b.com","password":"s3cur3-pw!"}'
curl -X POST http://localhost:8000/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"a@b.com","password":"s3cur3-pw!"}' # -> access_token, refresh_token
TOKEN=... # paste access_token
curl http://localhost:8000/dashboard -H "Authorization: Bearer $TOKEN"from fastauth.providers.google import GoogleProvider
from fastauth.providers.github import GitHubProvider
from fastauth.providers.magic_links import MagicLinksProvider
from fastauth.providers.passkey import PasskeyProvider
from fastauth.email_transports.smtp import SMTPTransport
from fastauth.session_backends.memory import MemorySessionBackend
providers=[
CredentialsProvider(),
MagicLinksProvider(), # requires token_adapter; needs [email]
GoogleProvider(client_id=..., client_secret=...), # needs [oauth]
GitHubProvider(client_id=..., client_secret=...), # needs [oauth]
PasskeyProvider(rp_id="example.com", rp_name="My App",
origin="https://example.com"), # needs [webauthn]
]OAuth needs oauth_adapter=adapter.oauth and oauth_state_store=MemorySessionBackend().
Passkeys needs passkey_adapter=adapter.passkey and passkey_state_store=MemorySessionBackend().
config = FastAuthConfig(
...,
roles=[
{"name": "admin", "permissions": ["users:read", "users:write"]},
{"name": "user", "permissions": ["profile:read", "profile:write"]},
],
default_role="user",
)
auth = FastAuth(config)
auth.role_adapter = adapter.role # RBAC endpoints + require_role now work
@asynccontextmanager
async def lifespan(app: FastAPI):
await adapter.create_tables()
await auth.initialize_roles() # seeds roles from config
yieldfrom fastauth.api.deps import require_role, require_permission
@app.get("/admin", dependencies=[Depends(require_role("admin"))])
async def admin(): ...
@app.get("/reports", dependencies=[Depends(require_permission("users:read"))])
async def reports(): ...from fastauth import JWTConfig
config = FastAuthConfig(
...,
jwt=JWTConfig(algorithm="RS256", jwks_enabled=True), # keys auto-generated if PEM not supplied
)
auth = FastAuth(config)
@asynccontextmanager
async def lifespan(app: FastAPI):
await adapter.create_tables()
await auth.initialize_jwks() # required before issuing RS256 tokens
yieldPublic keys are served at GET /.well-known/jwks.json. Resource services fetch and verify with joserfc.
Full docs: sreekarnv.github.io/fastauth
This repo uses uv and a workspace layout. The installable package is packages/fastauth; the root pyproject.toml holds dev dependencies.
git clone https://github.com/sreekarnv/fastauth
cd fastauth
uv sync --all-extras # install runtime + all dev deps
uv run pytest tests/ -v # tests
uv run ruff check . # lint
uv run ruff format --check . # format check
uv run mkdocs build # docs build into ./site
uv run mkdocs serve # live docs at http://localhost:8000Test coverage gate is 95% on CI. No mypy step is configured — type hints are advisory.
Contributions are welcome. There is no formal CONTRIBUTING.md yet — please follow the existing PR template (.github/pull_request_template.md): describe what/why/scope, note breaking changes and migration notes, and confirm tests, lint, formatting, and mkdocs build pass locally. See the production guide for the maintainer release checklist.
Please keep the core DB-agnostic: business logic belongs in core/ / api/, not in adapters/.
MIT — see LICENSE.