Architecture¶
Overview¶
Authority is layered so that the core is completely framework-agnostic:
- Core layer —
AuthManager(sync) andAsyncAuthManagerimplement all business logic. They depend only onAuthConfig, anEventBus, and a storage backend. - Storage layer —
StorageInterface/AsyncStorageInterfaceabstract persistence.SQLiteStorageandAsyncSQLiteStorageare the built-in implementations; any database can be plugged in by implementing the interface. - Integration layer — thin adapters (
fastapi.py,flask.py,django.py,starlette.py) and generic middleware (asgi.py,wsgi.py) that translate framework request objects into calls against the manager.
flowchart LR
App[Your Application] --> Integrations
subgraph Integrations[Framework Integrations]
FastAPI["authority.fastapi"]
Flask["authority.flask"]
Django["authority.django"]
Starlette["authority.starlette"]
ASGI["authority.asgi"]
WSGI["authority.wsgi"]
end
subgraph Core[Core]
AM[AuthManager]
AAM[AsyncAuthManager]
Cfg[AuthConfig]
Events[EventBus / Event]
Exc[Exceptions]
end
subgraph Storage[Storage]
SI[StorageInterface]
ASI[AsyncStorageInterface]
SQL[SQLiteStorage]
ASQL[AsyncSQLiteStorage]
end
Integrations --> AM & AAM
Cfg --> AM & AAM
AM --> SQL & SI
AAM --> ASQL & ASI
AM -.emit.-> Events
AAM -.emit.-> Events
AM & AAM -.raise.-> Exc
Module map¶
| Module | Responsibility |
|---|---|
config.py |
AuthConfig dataclass; env-var resolution |
core.py |
AuthManager — sync implementation of all flows |
async_core.py |
AsyncAuthManager — async twin with the same surface |
events.py |
Event enum (22 events), EventBus with sync/async handlers |
exceptions.py |
24-class AuthError hierarchy |
utils.py |
Token generation/hashing, Fernet encryption, HIBP, email validation |
storage/base.py |
StorageInterface, AsyncStorageInterface |
storage/sqlite.py |
Sync SQLite storage |
storage/aiosqlite.py |
Async SQLite storage |
fastapi.py |
FastAPI dependencies (get_current_user, require_permission, …) |
flask.py |
FlaskAuth + module helpers |
django.py |
AuthorityBackend + decorators/helpers |
starlette.py |
StarletteAuth decorators |
asgi.py |
Generic ASGI middleware + state helpers |
wsgi.py |
Generic WSGI middleware + state helpers |
Design decisions¶
Sync and async are separate classes¶
There is no "sync wrapper around async" or vice versa: AuthManager and
AsyncAuthManager are independent implementations that share the same public
surface, AuthConfig, storage interfaces, and exceptions. This keeps blocking
I/O out of the event loop and avoids surprise thread-pool round-trips.
Storage is behind an interface¶
The manager never touches SQL directly — it calls the storage backend. This is what makes the database pluggable and keeps the core unit-testable with fakes.
Framework adapters are thin¶
Adapters only translate between the framework's request/response model and the manager API. All policy lives in the core, so behavior is identical across FastAPI, Flask, Django, Starlette, and raw ASGI/WSGI.
Security properties in the core¶
Token family tracking, reuse detection, audit chain hashing, MFA secret encryption, and password policies are implemented once, in the core, so every integration inherits them.
Data flow: token-based login¶
- Client sends
POST /loginwith email/password. - The framework adapter parses the body and calls
manager.login(...). - The core verifies credentials, checks lockout state, optionally requires MFA
(returning
mfa_required), then creates an access token and a hashed, rotated refresh token via the storage layer. - The adapter serializes the tokens back to the client.
- On a protected route, the adapter extracts the
Authorization: Bearerheader and callsmanager.verify_access_token(...), then passes the user to the handler — or the generic middleware verifies the token and stores the result inscope["authority"]/environ["authority"].