AuthKit
Authentication as a package for FastAPI. Install it, point it at a database and a secret key, and get email/password auth, JWT tokens, and Google sign-in — without writing any of it yourself.
from fastapi import FastAPI, Depends
from authkit import AuthKit
app = FastAPI()
auth = AuthKit(app, secret_key="...", database_url="postgresql+psycopg://...")
auth.include_routes()
@app.get("/dashboard")
async def dashboard(user=Depends(auth.login_required)):
return {"user": user.email}
Nine routes, one dependency, zero boilerplate:
/auth/register /auth/login /auth/refresh
/auth/logout /auth/me /auth/password-reset/request
/auth/password-reset/confirm /auth/email/verify/request
/auth/email/verify/confirm /auth/google/login /auth/google/callback
Table of contents
- Features
- Installation
- Quickstart
- Configuration reference
- API reference
- System design
- Data model
- Roadmap
- Development
- Contributing
- License
Features
- Email + password auth — register, login,
is_activegating - JWT access + refresh tokens — short-lived access token, longer-lived refresh token, independent expiry configuration
login_requireddependency — oneDepends()call protects any route and injects the current user- Logout / token revocation — an in-memory blacklist so a logged-out token can't be reused
- Google sign-in (OAuth 2.0) — full authorization-code flow, CSRF-protected state, account linking so a user can have both a password login and a Google login on the same account
- Password reset — request/confirm flow with single-use, hashed, expiring tokens
- Email verification — same token mechanism, separate purpose
- Rate limiting — per-IP sliding window on
/loginand/register - Pluggable side effects — AuthKit never sends an email or decides how to redirect after OAuth; you supply the hook, AuthKit calls it
- Bring your own database — any SQLAlchemy-supported engine; tables are namespaced
(
authkit_*) so they never collide with your app's own schema
Installation
PyPI publication is pending. Until then, install directly from GitHub:
pip install git+https://github.com/<your-username>/authkit.git
Pin to a tag once you have one (recommended for anything beyond local experimentation):
pip install git+https://github.com/<your-username>/authkit.git@v0.1.0
With Poetry, as a git dependency in pyproject.toml:
authkit = { git = "https://github.com/<your-username>/authkit.git", tag = "v0.1.0" }
Once published to PyPI, installation will be:
pip install fastapi-authstack
Quickstart
from fastapi import FastAPI, Depends
from authkit import AuthKit
app = FastAPI()
auth = AuthKit(
app,
secret_key="change-me-to-a-real-secret",
database_url="sqlite:///./app.db",
)
auth.include_routes()
@app.get("/dashboard")
async def dashboard(user=Depends(auth.login_required)):
return {"email": user.email, "verified": user.email_verified}
uvicorn main:app --reload
curl -X POST localhost:8000/auth/register \
-H "Content-Type: application/json" \
-d '{"email": "you@example.com", "password": "supersecret123"}'
curl -X POST localhost:8000/auth/login \
-H "Content-Type: application/json" \
-d '{"email": "you@example.com", "password": "supersecret123"}'
curl localhost:8000/dashboard \
-H "Authorization: Bearer <access_token from login response>"
Configuration reference
auth = AuthKit(
app,
secret_key="...", # required — used to sign JWTs and OAuth state
database_url="postgresql+psycopg://user:pass@host/db",
algorithm="HS256",
access_token_expires_minutes=15,
refresh_token_expires_days=7,
action_token_expires_minutes=30, # password reset / email verification token TTL
auto_create_tables=True, # False if you manage migrations yourself
engine_options=None, # forwarded to sqlalchemy.create_engine
google_oauth={
"client_id": "...",
"client_secret": "...",
"redirect_uri": "https://yourapp.com/auth/google/callback",
},
on_password_reset_requested=lambda user, token: send_email(user, token),
on_email_verification_requested=lambda user, token: send_email(user, token),
on_oauth_login_complete=lambda user, tokens, redirect_after: my_redirect(user, tokens, redirect_after),
)
| Parameter | Required | Default | Notes |
|---|---|---|---|
secret_key |
✅ | — | Signs JWTs and OAuth state tokens |
database_url |
✅ | — | Any SQLAlchemy-compatible URL |
google_oauth |
— | None |
Omit entirely to disable Google sign-in — its routes won't be mounted |
auto_create_tables |
— | True |
Set False and run your own migration if you use Alembic |
on_password_reset_requested |
— | no-op | (user, raw_token) -> None — send the email yourself |
on_email_verification_requested |
— | no-op | Same shape as above |
on_oauth_login_complete |
— | redirect with base64 payload | (user, tokens, redirect_after) -> Response |
API reference
| Method | Path | Auth required | Description |
|---|---|---|---|
POST |
/auth/register |
No | Create an account, returns user + token pair |
POST |
/auth/login |
No | Authenticate, returns user + token pair |
POST |
/auth/refresh |
No (refresh token) | Exchange a refresh token for a new pair |
POST |
/auth/logout |
Yes | Revoke the current access token |
GET |
/auth/me |
Yes | Return the current user |
POST |
/auth/password-reset/request |
No | Issue a password reset token via the configured hook |
POST |
/auth/password-reset/confirm |
No (reset token) | Consume the token, set a new password |
POST |
/auth/email/verify/request |
Yes | Issue an email verification token via the configured hook |
POST |
/auth/email/verify/confirm |
No (verification token) | Consume the token, mark the email verified |
GET |
/auth/google/login |
No | Redirect to Google's consent screen |
GET |
/auth/google/callback |
No | Handle Google's callback, create/link the user, redirect |
All error responses are {"detail": "..."} with the appropriate HTTP status — 401 for bad
credentials/tokens, 403 for inactive accounts, 409 for duplicate registration, 429 for rate
limiting.
System design
AuthKit is a single facade (AuthKit) composing four independent layers. Each layer only
depends on the layers below it — there are no upward or circular imports, which keeps every
piece independently testable and replaceable.
┌───────────────────────────────────────────┐
│ AuthKit (core.py) │
│ the only class an integrator touches │
└───────────────────┬───────────────────────┘
│ wires together
┌─────────────────────┬─────────┼─────────────────┬─────────────────────┐
▼ ▼ ▼ ▼
┌───────────────┐ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ routes/ │ │ services/ │ │ oauth/ │ │ dependencies.py │
│ HTTP contracts │──▶│ business logic │ │ provider adapters │ │ FastAPI Depends() │
│ auth.py │ │ AuthService │ │ base / google / │ │ login_required │
│ google.py │ │ TokenService │ │ state / redirect │ │ get_session │
└───────────────┘ │ ActionTokenService │ └──────────────────┘ └──────────────────┘
└─────────┬──────────┘
│ reads/writes through
▼
┌──────────────────┐
│ repositories/ │
│ UserRepository │ ← the only layer that writes SQL
└─────────┬──────────┘
▼
┌──────────────────┐
│ models.py │ User · UserIdentity · ActionToken
└─────────┬──────────┘
▼
┌──────────────────┐
│ db.py │ SQLAlchemy engine + session factory
└──────────────────┘
cross-cutting, used by routes/services directly:
config.py (typed settings) · security.py (hashing + JWT primitives)
exceptions.py (typed errors → HTTP status) · rate_limit.py · token_blacklist.py
Why layered this way:
- Routes never touch the database. They validate input, call a service, and shape the
HTTP response. All persistence goes through
repositories/. - Services own business rules, not SQL or HTTP.
AuthService.authenticatedoesn't know it's being called from a FastAPI route — it just raises a typed exception on failure. - One exception hierarchy (
AuthKitError) maps to HTTP status codes in one place — a single@app.exception_handler(AuthKitError)registered incore.py. No route handler contains a rawHTTPException. - OAuth providers implement one interface (
oauth/base.py: OAuthProvider). Google is the only implementation today; adding GitHub or Microsoft is a new file inoauth/, not a change tocore.pyor the route layer. - Every side effect the host application needs to control is a hook, not a hardcoded
integration. AuthKit does not send emails or decide how to redirect after OAuth login — it
calls
on_password_reset_requested,on_email_verification_requested, andon_oauth_login_complete, which the integrator supplies. AuthKit's job is auth, not email delivery.
Data model
User— one row per account.hashed_passwordis nullable, because an OAuth-only user has no password.UserIdentity— links aUserto a third-party provider (provider,provider_user_id). A user can have a password login and a Google login on the same account, matching how real products behave.ActionToken— single-use, hashed, expiring tokens for password reset and email verification, keyed by purpose.
Tables are prefixed authkit_* so they never collide with the host application's own schema.
Roadmap
Deliberately out of scope for v1, planned as the next layer on top of this one:
- Two-factor authentication (TOTP)
- API keys (create, scope, rotate, revoke)
- Roles & permissions
- Multi-tenant organizations (teams, invitations, membership)
- Additional OAuth providers (GitHub, Microsoft)
- Persistent token blacklist (Redis-backed, for multi-process deployments)
Development
Requires Python 3.11+.
git clone https://github.com/<your-username>/authkit.git
cd authkit
python3 -m venv venv && source venv/bin/activate
pip install -e .
pip install pytest
pytest
The test suite runs against an in-memory SQLite database — no external services required.
Contributing
Issues and pull requests are welcome. Before opening a PR:
- Run
pytestand make sure everything passes - Keep the layering intact — routes call services, services call repositories, nothing skips a layer
- Match the existing style: no comments, self-explanatory names, typed function signatures
License
MIT — see LICENSE.
Metadata
Release files for fastapi-authstack 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| fastapi_authstack-0.1.0.tar.gz | 19.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| fastapi_authstack-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 41.8 kB
Release files / fastapi_authstack-0.1.0.tar.gz
| Download URL | fastapi_authstack-0.1.0.tar.gz |
|---|---|
| Size | 19.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e53fdb23b5b66e4f724957aacda1d60b4ef1eff190833844df063e88cbb9f461
|
|
BLAKE2b-256 checksum How to use checksums |
31d4a50598eeb21ed466b2d952fe13e69d09a37a5215298f9165f49fcd44d88c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/1.8.2 CPython/3.8.10 Linux/5.15.0-139-generic
|
Release files / fastapi_authstack-0.1.0-py3-none-any.whl
| Download URL | fastapi_authstack-0.1.0-py3-none-any.whl |
|---|---|
| Size | 22.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8f5438182a501d6efbae6636957f231ac13a0ebde2a366d39c083be11bffe25b
|
|
BLAKE2b-256 checksum How to use checksums |
f923f37e74f613693fca841a10246d851efc3d9a1a70cec0181b913911e49dbc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/1.8.2 CPython/3.8.10 Linux/5.15.0-139-generic
|