Standalone FastAPI JWT auth with admin-approved registration, password management, and extensible token claims.
Project description
fastapi-auth-admin
Standalone JWT authentication package for FastAPI applications with admin-approved registration, password management, and extensible token claims (e.g. multi-tenant company_id).
Installation
pip install fastapi-auth-admin
Or with uv:
uv add fastapi-auth-admin
All dependencies (fastapi, sqlalchemy, aiosqlite, bcrypt, pyjwt, pydantic[email], python-multipart) are installed automatically.
Features
- One-call setup —
setup_auth(app)does everything - Fully async — built on SQLAlchemy async + aiosqlite
- Email/password registration and login
- Admin-approved user registration workflow
- JWT tokens with extensible arbitrary claims
- Password change (authenticated, requires current password)
- Async SQLAlchemy user repository (dedicated
users.dbdatabase) - Auto-seeded admin user on first run
- Swagger UI
/docscompatible (OAuth2 password flow)
Quick start
Two touch points — that's it:
1. Entry point — one call to set up auth:
from fastapi import FastAPI
from fastapi_auth import setup_auth
app = FastAPI()
setup_auth(app)
This single call:
- Configures JWT security
- Registers an async startup handler that initializes the
users.dbSQLite database and seeds the default admin user - Registers all
/auth/*endpoints on your app
2. Endpoints — add RequireLogin where needed:
from fastapi_auth import RequireLogin
@router.get("/orders")
async def list_orders(user: RequireLogin):
...
Run with:
uvicorn myapp:app --reload
setup_auth options
setup_auth(
app,
secret_key="your-secret-key-min-32-bytes-long!", # JWT secret (random per process unless set; use a fixed key in production)
algorithm="HS256", # JWT algorithm (default)
expire_minutes=30, # token TTL (default)
db_url="sqlite+aiosqlite:///./users.db", # async database URL (default)
)
Any async SQLAlchemy-compatible URL works:
setup_auth(app, db_url="sqlite+aiosqlite:///./data/auth.db") # custom SQLite path
setup_auth(app, db_url="postgresql+asyncpg://user:pass@host/mydb") # PostgreSQL (requires asyncpg)
Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /auth/register |
public | Register a new user (pending) |
| POST | /auth/login |
public | Login, returns JWT (OAuth2 form) |
| GET | /auth/me |
login | Current user info + token claims |
| POST | /auth/change-password |
login | Change own password |
| GET | /auth/pending |
admin | List unapproved users |
| POST | /auth/approve |
admin | Approve a user by email |
| POST | /auth/reject |
admin | Reject a user by email |
Default admin
On first startup the adapter auto-creates an admin user:
- Email:
admin@admin.com - Password:
changeme
Change the password immediately after first login via /auth/change-password.
Protecting endpoints
Add RequireLogin as a parameter to any endpoint to require a valid JWT:
from fastapi import APIRouter
from fastapi_auth import RequireLogin
router = APIRouter()
@router.get("/dashboard")
async def dashboard(user: RequireLogin):
print(user.sub) # user email
print(user.extra.get("is_admin")) # True/False
If you don't need user info in the function body, it still enforces authentication just by being declared as a parameter.
Multi-tenant / custom token claims
The JWT token supports arbitrary extra claims. Call the login use case directly with extra_claims:
from fastapi_auth.use_cases import login
token = await login(
email="user@example.com",
password="secret",
repo=repo,
extra_claims={"company_id": 42, "role": "manager"},
)
These claims are available in any protected endpoint:
@router.get("/tenant-data")
async def tenant_data(user: RequireLogin):
company_id = user.extra.get("company_id")
role = user.extra.get("role")
Custom user repository
Implement the UserRepositoryPort protocol to use any async storage backend:
from fastapi_auth import UserRepositoryPort, User, set_get_user_repo
class MyUserRepository:
async def find_by_email(self, email: str) -> User | None: ...
async def create(self, user: User) -> None: ...
async def update(self, user: User) -> None: ...
async def list_pending(self) -> list[User]: ...
async def ensure_admin(self, email: str, hashed_password: str) -> None: ...
async def get_user_repo():
yield MyUserRepository()
set_get_user_repo(get_user_repo)
Testing
The test suite uses pytest-asyncio and httpx. Each test gets an isolated in-memory database:
uv sync --group dev
uv run pytest
To add the dev dependencies to your own project:
uv add --group dev pytest pytest-asyncio httpx
Configure pytest-asyncio in pyproject.toml:
[tool.pytest.ini_options]
asyncio_mode = "auto"
Use httpx.AsyncClient with ASGITransport to test endpoints without a running server:
import pytest
from fastapi import FastAPI
from httpx import ASGITransport, AsyncClient
from fastapi_auth import auth_router, configure_security, set_get_user_repo
from fastapi_auth.sqlalchemy_adapter import get_user_repo, init_db
@pytest.fixture
async def client():
configure_security(secret_key="a-secret-key-long-enough-for-hs256")
await init_db("sqlite+aiosqlite:///:memory:")
set_get_user_repo(get_user_repo)
app = FastAPI()
app.include_router(auth_router)
async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as ac:
yield ac
Package structure
fastapi_auth/
__init__.py # setup_auth() + public API exports
models.py # User, TokenPayload dataclasses
ports.py # UserRepositoryPort protocol (async)
errors.py # AuthError, InvalidCredentials, NotAuthorized, ...
security.py # bcrypt password hashing, JWT encode/decode, configure()
use_cases.py # async: register, login, approve, reject, change_password, list_pending
dependencies.py # FastAPI deps: get_current_user, RequireLogin
router.py # APIRouter with all auth endpoints (async handlers)
sqlalchemy_adapter.py # Async SQLAlchemy + aiosqlite user repository (default, recommended)
json_adapter.py # JSON file user repository (dev/prototyping only)
Workflow
- Admin logs in with default credentials
- Users register via
POST /auth/register(created as unapproved) - Admin views pending users via
GET /auth/pending - Admin approves (
POST /auth/approve) or rejects (POST /auth/reject) - Approved users can log in and receive a JWT
- Users change their own password via
POST /auth/change-password
Project details
Release history Release notifications | RSS feed
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_auth_admin-0.2.0.tar.gz.
File metadata
- Download URL: fastapi_auth_admin-0.2.0.tar.gz
- Upload date:
- Size: 37.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","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 |
09d124b0bb8a05c2efdf82dbb0483efdf5349515ee148e4a2ba416e3f4f83b69
|
|
| MD5 |
2504fad471390bfef7523562aa29be86
|
|
| BLAKE2b-256 |
a7900bf56f8b3d144e45d301b9f4d7a1cac9b6af6efa4ae7aed90c9adaa90ceb
|
File details
Details for the file fastapi_auth_admin-0.2.0-py3-none-any.whl.
File metadata
- Download URL: fastapi_auth_admin-0.2.0-py3-none-any.whl
- Upload date:
- Size: 13.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","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 |
18de1d4cf51828a62ee4015704f83e9c7e79bda985e4b65e841c1688d9a1f247
|
|
| MD5 |
8ef262a1bb47fc042005880682235c09
|
|
| BLAKE2b-256 |
af3f49bf91a0ff63d6ad2cddf9393b532113a68d687485d9bcb47e5c5e6162e9
|