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
Metadata
Release files for fastapi-auth-admin 0.2.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_auth_admin-0.2.0.tar.gz | 37.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| fastapi_auth_admin-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 50.1 kB
Release files / fastapi_auth_admin-0.2.0.tar.gz
| Download URL | fastapi_auth_admin-0.2.0.tar.gz |
|---|---|
| Size | 37.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
09d124b0bb8a05c2efdf82dbb0483efdf5349515ee148e4a2ba416e3f4f83b69
|
|
BLAKE2b-256 checksum How to use checksums |
a7900bf56f8b3d144e45d301b9f4d7a1cac9b6af6efa4ae7aed90c9adaa90ceb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is 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}
|
Release files / fastapi_auth_admin-0.2.0-py3-none-any.whl
| Download URL | fastapi_auth_admin-0.2.0-py3-none-any.whl |
|---|---|
| Size | 13.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
18de1d4cf51828a62ee4015704f83e9c7e79bda985e4b65e841c1688d9a1f247
|
|
BLAKE2b-256 checksum How to use checksums |
af3f49bf91a0ff63d6ad2cddf9393b532113a68d687485d9bcb47e5c5e6162e9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is 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}
|