A reusable FastAPI authentication and authorization extension.
Project description
UsrAK
Reusable authentication and authorization for FastAPI applications built on top of FastAPI, SQLModel, JWT cookies, and API tokens.
UsrAK is aimed at backend developers who want to plug a working auth surface into an existing FastAPI project without giving up control over models, schemas, or infrastructure choices.
Table Of Contents
- Why UsrAK
- Features
- Quick Start
- Built-In Routes
- How To Extend
- Project Layout
- Current Limitations
- Development
- Changelog
Why UsrAK
- Bring auth into an existing FastAPI codebase instead of generating a whole starter app.
- Keep your own
SQLModeltables and response schemas. - Support cookie-based session auth and header-based API tokens in the same package.
- Toggle features per project with
AppConfigandRouterConfig. - Swap storage and delivery backends without rewriting routes.
Features
| Capability | Status | Notes |
|---|---|---|
| Email/password sign-in | Yes | Sets access_token and refresh_token cookies |
| Logout and token refresh | Yes | Cookie-based session flow |
| Current user/profile endpoint | Yes | Works with mounted auth app |
| Optional signup flow | Yes | Email registration can be enabled via config |
| Signup verification links | Yes | Driven by one-time tokens |
| Password reset via email | Yes | Link-based reset flow |
| API tokens | Yes | Includes create/list/delete endpoints |
| API token IP allowlist | Yes | whitelisted_ip_addresses on token model |
| Optional user resolution | Yes | Access cookie, API token, or both |
| Role-based protection | Yes | require_roles(...) dependency |
| Google OAuth | Yes | Redirect/callback flow |
| Telegram auth | Yes | Signed Telegram login payload |
| Admin user registration | Yes | Protected by admin role |
| Pluggable KV store | Yes | In-memory, Redis, LMDB, or custom class |
| Pluggable notification service | Yes | No-op or SMTP-backed |
| Redis rate limiter backend | Not yet | Config surface exists, implementation is incomplete |
Quick Start
1. Install
python -m pip install -e .[test]
Minimum runtime requirements from pyproject.toml:
- Python
>=3.10 fastapi>=0.115.0sqlmodel>=0.0.24
2. Define your models and read schemas
UsrAK is designed to work with your own SQLModel tables. You extend the provided abstract bases and point the router config at your concrete classes.
from typing import Optional
from pydantic import BaseModel
from sqlmodel import Field
from usrak import TokensModelBase, UserModelBase
class User(UserModelBase, table=True):
__tablename__ = "users"
id: Optional[int] = Field(default=None, primary_key=True)
class ApiToken(TokensModelBase, table=True):
__tablename__ = "api_tokens"
id: Optional[int] = Field(default=None, primary_key=True)
user_id: Optional[int] = Field(default=None, foreign_key="users.id", index=True)
class UserRead(BaseModel):
id: int | None = None
email: str
auth_provider: str
is_active: bool
is_verified: bool
user_name: str | None = None
model_config = {"from_attributes": True}
class ApiTokenRead(BaseModel):
id: int | None = None
token: str
token_type: str
name: str | None = None
whitelisted_ip_addresses: list[str] | None = None
is_deleted: bool
expires_at: int | None = None
model_config = {"from_attributes": True}
3. Configure the extension
from usrak import AppConfig, RouterConfig
app_config = AppConfig(
DATABASE_URL="postgresql+psycopg://postgres:postgres@localhost:5432/app",
REDIS_URL="redis://localhost:6379/0",
JWT_ACCESS_TOKEN_SECRET_KEY="change-me",
JWT_REFRESH_TOKEN_SECRET_KEY="change-me-too",
JWT_ONETIME_TOKEN_SECRET_KEY="change-me-three",
JWT_API_TOKEN_SECRET_KEY="change-me-four",
CODE_HASH_SALT="change-me-five",
FERNET_KEY="Y8RFpaIxSaAFNsB352tpLXl5znUw5anEKIZgclOezak=",
COOKIE_SECURE=False,
REDIRECT_AFTER_AUTH_URL="http://localhost:3000/auth/callback",
)
router_config = RouterConfig(
USER_MODEL=User,
USER_READ_SCHEMA=UserRead,
TOKENS_MODEL=ApiToken,
TOKENS_READ_SCHEMA=ApiTokenRead,
ENABLE_EMAIL_REGISTRATION=True,
ENABLE_PASSWORD_RESET_VIA_EMAIL=True,
USE_VERIFICATION_LINKS_FOR_SIGNUP=True,
)
4. Mount AuthApp
from fastapi import FastAPI
from usrak import AuthApp
app = FastAPI(title="My Product API")
auth_app = AuthApp(app_config=app_config, router_config=router_config)
app.mount("/auth", auth_app)
With that setup, the default auth routes are available under /auth/..., for example:
POST /auth/sign-inPOST /auth/logoutPOST /auth/refreshGET /auth/profilePOST /auth/check-auth
Create your SQLModel tables and migrations separately. UsrAK does not create or migrate database schema for you.
5. Protect your own routes
UsrAK exposes dependencies for access-cookie auth, API-key auth, optional auth, and role checks.
from fastapi import APIRouter, Depends
from usrak.core.dependencies.role import require_roles
from usrak.core.dependencies.user import get_user_access_only, get_user_api_only
from usrak.core.enums import DefaultRoles
router = APIRouter()
@router.get("/me")
async def get_me(user=Depends(get_user_access_only)):
return {"email": user.email, "role": user.role}
@router.get("/service-token")
async def get_service_token_user(user=Depends(get_user_api_only)):
return {"user_identifier": user.user_identifier}
@router.post("/admin-only")
async def admin_only(_admin=Depends(require_roles(DefaultRoles.ADMIN))):
return {"ok": True}
For API-token authenticated requests, send the token in the X-API-Key header.
Built-In Routes
Routes are enabled conditionally from RouterConfig.
| Route | Method | Purpose | Config flag |
|---|---|---|---|
/profile |
GET |
Return current authenticated user profile | Always on |
/sign-in |
POST |
Email/password login | Always on |
/logout |
POST |
Clear auth cookies | Always on |
/check-auth |
POST |
Verify current auth state | Always on |
/refresh |
POST |
Rotate access/refresh cookies | Always on |
/api-tokens |
GET |
List current user's API tokens | Always on |
/api-tokens |
POST |
Create a new API token | Always on |
/api-tokens/{token_identifier} |
DELETE |
Soft-delete API token | Always on |
/signup |
POST |
Register by email | ENABLE_EMAIL_REGISTRATION |
/signup/send_link |
POST |
Send signup verification link | ENABLE_EMAIL_REGISTRATION + USE_VERIFICATION_LINKS_FOR_SIGNUP |
/signup/verify |
POST |
Verify signup token | ENABLE_EMAIL_REGISTRATION + USE_VERIFICATION_LINKS_FOR_SIGNUP |
/password/forgot |
POST |
Start password reset | ENABLE_PASSWORD_RESET_VIA_EMAIL |
/password/change |
POST |
Request password change flow | ENABLE_PASSWORD_RESET_VIA_EMAIL |
/password/verify_token |
POST |
Verify password reset token | ENABLE_PASSWORD_RESET_VIA_EMAIL |
/password/reset |
POST |
Complete password reset | ENABLE_PASSWORD_RESET_VIA_EMAIL |
/oauth/google |
POST |
Start Google OAuth | ENABLE_OAUTH + ENABLE_GOOGLE_OAUTH |
/oauth/google/callback |
GET |
Finish Google OAuth | ENABLE_OAUTH + ENABLE_GOOGLE_OAUTH |
/oauth/telegram |
POST |
Telegram auth | ENABLE_OAUTH + ENABLE_TELEGRAM_OAUTH |
/admin/register_user |
POST |
Admin-only user creation | ENABLE_ADMIN_PANEL |
How To Extend
Override model identity rules
If your primary key is not named id, point UsrAK at the correct field:
router_config = RouterConfig(
USER_MODEL=User,
USER_READ_SCHEMA=UserRead,
USER_IDENTIFIER_FIELD_NAME="external_pk",
TOKENS_MODEL=ApiToken,
TOKENS_READ_SCHEMA=ApiTokenRead,
TOKENS_IDENTIFIER_FIELD_NAME="token_pk",
TOKENS_OWNER_FIELD_NAME="owner_id",
TOKENS_OWNER_RELATION_FIELD_NAME="owner",
)
This is one of the package's strongest extension points: it does not force a hardcoded internal user ID convention.
Override roles
RouterConfig.DEFAULT_ROLES_ENUM lets you replace the default admin/user pair with your own string enum, as long as the enum still contains ADMIN and USER.
from enum import Enum
class Roles(str, Enum):
ADMIN = "superuser"
USER = "member"
AUDITOR = "auditor"
Swap infrastructure backends
UsrAK accepts either concrete classes or string shortcuts for several backends:
KEY_VALUE_STORE:"in_memory","redis","lmdb", or a customKeyValueStoreABSNOTIFICATION_SERVICE:"smtp","no_op", or a customNotificationServiceABSSMTP_CLIENT:"default","no_op", or a customSMTPClientABSFAST_API_RATE_LIMITER:"no_op"today, custom implementation if you have one
The abstract interfaces live in:
usrak/core/managers/key_value_store/base.pyusrak/core/managers/notification/base.pyusrak/core/managers/rate_limiter/interface.pyusrak/core/smtp/base.py
Add your own protected routers
The package is best treated as an auth module, not as the whole application. Mount it once, then build your product routes around its dependencies.
Good patterns:
- use
get_user_access_onlyfor browser/session routes - use
get_user_api_onlyfor machine-to-machine calls - use
get_optional_user_anywhen auth should enrich, but not block, a request - use
require_roles(...)for admin or staff-only endpoints
Customize responses and schemas
UsrAK already wraps most responses in typed CommonResponse and CommonDataResponse[...] models. You control the user and token payload shapes by passing custom USER_READ_SCHEMA and TOKENS_READ_SCHEMA.
That means you can:
- keep internal DB fields private
- expose only public-safe attributes
- version your outward response contract without forking the auth logic
Project Layout
usrak/
auth_app.py # FastAPI app wrapper
auth_router.py # Route registration
core/
config_schemas.py # AppConfig and RouterConfig
db.py # Async DB session factory
dependencies/ # User, role, config, manager providers
managers/ # Tokens, signup, password reset, KV store, notification
middleware/ # Request body and trusted proxy middleware
models/ # Base SQLModel classes
schemas/ # Request/response payloads
security.py # JWT, hashing, token helpers
routes/ # Feature routers
tests/
fixtures/ # Example models/config used by the test suite
Current Limitations
- The project is still marked
Development Status :: 3 - Alpha. FAST_API_RATE_LIMITER="redis"currently raisesNotImplementedError.- The package is PostgreSQL-first today:
AppConfig.DATABASE_URLis typed asPostgresDsn, andTokensModelBaseuses PostgreSQLJSONB. - DB migrations are not managed by UsrAK. You own table creation, migrations, and lifecycle.
- Global config is stored in module-level state and several providers are cached with
lru_cache(). Running multiple differently configured auth apps in the same process is risky. - Parent config flags do not strictly enforce child flags.
Example: passing
ENABLE_OAUTH=FalseandENABLE_GOOGLE_OAUTH=Truestill leaves Google OAuth enabled at the field level. - OAuth support is currently focused on Google and Telegram only.
- Some implementation areas still contain debug prints and TODOs, so production hardening is not finished.
Development
Run the usual checks before opening a PR:
python -m pytest
ruff check usrak tests
mypy usrak tests
Useful local targets:
python -m pytest -m "not docker_required"
python -m pytest tests/test_default_endpoints.py
For disposable infra during higher-level scenarios, use docker-compose.tests.yaml.
Changelog
This section is derived from git tags and the current HEAD.
0.3.0 - 2026-03-16
Current HEAD version in pyproject.toml and not tagged yet in git.
- Added
RoleModelBaseto make role-based extension more explicit. - Updated packaging metadata and project versioning.
v0.2.3 - 2025-10-10
- Reworked common response schemas and response typing.
- Cleaned response-model structure around status/data payloads.
v0.2.2 - 2025-10-02
- Added full API token management flow.
- Added API-token-specific dependencies and auth modes.
- Added
TOKENS_OWNER_RELATION_FIELD_NAME. - Added IP allowlists for API tokens with
whitelisted_ip_addresses. - Improved request handling by switching some auth resolution paths to
HTTPConnection. - Added secret-token creation helpers and token resolver improvements.
v0.2.1 - 2025-08-13
- Added optional user dependency support via
get_optional_user.
v0.2.0 - 2025-08-07
- Removed the old
internal_idassumption. - Introduced dynamic user identification based on configurable model fields.
v0.1.1 - 2025-07-29
- Fixed LMDB/KV-store caching behavior.
- Increased LMDB reader limits.
- Introduced singleton helpers around shared services.
v0.1.0 - 2025-06-25
- Initial project foundation.
License
MIT.
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 usrak-0.3.0.tar.gz.
File metadata
- Download URL: usrak-0.3.0.tar.gz
- Upload date:
- Size: 65.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
680711ed69351c0b55368b087c834fbd3ad13ed8daf3b38328f65683086c0373
|
|
| MD5 |
dfc072643bd147584c408601477d7f72
|
|
| BLAKE2b-256 |
7a578777beb76cdf175b05b4db8a984372f555a303b27459b7782912ef988b87
|
File details
Details for the file usrak-0.3.0-py3-none-any.whl.
File metadata
- Download URL: usrak-0.3.0-py3-none-any.whl
- Upload date:
- Size: 78.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
27c6d16076b44a40396330f0ab9a515a50682e250c1dce964cceedfe851f93aa
|
|
| MD5 |
5cca93c221f0ae4b15c2280c5ddedff3
|
|
| BLAKE2b-256 |
d97a81796bfe5596947ead47164d4e3b3a1539bcbbe7377e0a3380977a93bd90
|