fastauth-py 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fastauth_py-0.1.0/.gitignore +36 -0
- fastauth_py-0.1.0/LICENSE +21 -0
- fastauth_py-0.1.0/PKG-INFO +326 -0
- fastauth_py-0.1.0/README.md +273 -0
- fastauth_py-0.1.0/pyproject.toml +106 -0
- fastauth_py-0.1.0/src/fastauth/__init__.py +9 -0
- fastauth_py-0.1.0/src/fastauth/cli/__init__.py +3 -0
- fastauth_py-0.1.0/src/fastauth/cli/main.py +207 -0
- fastauth_py-0.1.0/src/fastauth/config.py +270 -0
- fastauth_py-0.1.0/src/fastauth/domain/__init__.py +0 -0
- fastauth_py-0.1.0/src/fastauth/domain/enums.py +125 -0
- fastauth_py-0.1.0/src/fastauth/domain/events.py +217 -0
- fastauth_py-0.1.0/src/fastauth/domain/models.py +238 -0
- fastauth_py-0.1.0/src/fastauth/exceptions.py +191 -0
- fastauth_py-0.1.0/src/fastauth/flows/__init__.py +3 -0
- fastauth_py-0.1.0/src/fastauth/flows/change_email.py +203 -0
- fastauth_py-0.1.0/src/fastauth/flows/change_password.py +75 -0
- fastauth_py-0.1.0/src/fastauth/flows/credentials.py +337 -0
- fastauth_py-0.1.0/src/fastauth/flows/email_otp.py +818 -0
- fastauth_py-0.1.0/src/fastauth/flows/password_reset.py +163 -0
- fastauth_py-0.1.0/src/fastauth/flows/refresh.py +63 -0
- fastauth_py-0.1.0/src/fastauth/flows/sessions.py +116 -0
- fastauth_py-0.1.0/src/fastauth/flows/user_management.py +304 -0
- fastauth_py-0.1.0/src/fastauth/flows/verification.py +147 -0
- fastauth_py-0.1.0/src/fastauth/messaging/__init__.py +0 -0
- fastauth_py-0.1.0/src/fastauth/messaging/email.py +60 -0
- fastauth_py-0.1.0/src/fastauth/messaging/templates/delete_account.html +10 -0
- fastauth_py-0.1.0/src/fastauth/messaging/templates/delete_account.txt +8 -0
- fastauth_py-0.1.0/src/fastauth/messaging/templates/otp_email_change.html +10 -0
- fastauth_py-0.1.0/src/fastauth/messaging/templates/otp_email_change.txt +7 -0
- fastauth_py-0.1.0/src/fastauth/messaging/templates/otp_password_reset.html +10 -0
- fastauth_py-0.1.0/src/fastauth/messaging/templates/otp_password_reset.txt +7 -0
- fastauth_py-0.1.0/src/fastauth/messaging/templates/otp_sign_in.html +10 -0
- fastauth_py-0.1.0/src/fastauth/messaging/templates/otp_sign_in.txt +7 -0
- fastauth_py-0.1.0/src/fastauth/messaging/templates/otp_verification.html +10 -0
- fastauth_py-0.1.0/src/fastauth/messaging/templates/otp_verification.txt +7 -0
- fastauth_py-0.1.0/src/fastauth/messaging/templates/reset.html +9 -0
- fastauth_py-0.1.0/src/fastauth/messaging/templates/reset.txt +6 -0
- fastauth_py-0.1.0/src/fastauth/messaging/templates/verification.html +9 -0
- fastauth_py-0.1.0/src/fastauth/messaging/templates/verification.txt +6 -0
- fastauth_py-0.1.0/src/fastauth/plugins/__init__.py +3 -0
- fastauth_py-0.1.0/src/fastauth/plugins/api_key.py +433 -0
- fastauth_py-0.1.0/src/fastauth/plugins/audit_logs.py +195 -0
- fastauth_py-0.1.0/src/fastauth/plugins/base.py +210 -0
- fastauth_py-0.1.0/src/fastauth/plugins/email_otp.py +336 -0
- fastauth_py-0.1.0/src/fastauth/plugins/jwt.py +212 -0
- fastauth_py-0.1.0/src/fastauth/plugins/openapi.py +137 -0
- fastauth_py-0.1.0/src/fastauth/plugins/test_utils.py +137 -0
- fastauth_py-0.1.0/src/fastauth/py.typed +0 -0
- fastauth_py-0.1.0/src/fastauth/runtime/__init__.py +0 -0
- fastauth_py-0.1.0/src/fastauth/runtime/api.py +432 -0
- fastauth_py-0.1.0/src/fastauth/runtime/auth.py +281 -0
- fastauth_py-0.1.0/src/fastauth/runtime/context.py +45 -0
- fastauth_py-0.1.0/src/fastauth/runtime/event_bus.py +43 -0
- fastauth_py-0.1.0/src/fastauth/runtime/hooks.py +59 -0
- fastauth_py-0.1.0/src/fastauth/security/__init__.py +0 -0
- fastauth_py-0.1.0/src/fastauth/security/jwt.py +371 -0
- fastauth_py-0.1.0/src/fastauth/security/lockout.py +94 -0
- fastauth_py-0.1.0/src/fastauth/security/otp.py +59 -0
- fastauth_py-0.1.0/src/fastauth/security/passwords.py +46 -0
- fastauth_py-0.1.0/src/fastauth/security/rate_limit.py +199 -0
- fastauth_py-0.1.0/src/fastauth/security/refresh_tokens.py +161 -0
- fastauth_py-0.1.0/src/fastauth/security/sessions.py +107 -0
- fastauth_py-0.1.0/src/fastauth/security/tokens.py +66 -0
- fastauth_py-0.1.0/src/fastauth/storage/__init__.py +1 -0
- fastauth_py-0.1.0/src/fastauth/storage/base.py +318 -0
- fastauth_py-0.1.0/src/fastauth/storage/beanie/__init__.py +73 -0
- fastauth_py-0.1.0/src/fastauth/storage/beanie/adapter.py +578 -0
- fastauth_py-0.1.0/src/fastauth/storage/beanie/documents.py +276 -0
- fastauth_py-0.1.0/src/fastauth/storage/beanie/helpers.py +52 -0
- fastauth_py-0.1.0/src/fastauth/storage/memory.py +404 -0
- fastauth_py-0.1.0/src/fastauth/storage/postgres/__init__.py +25 -0
- fastauth_py-0.1.0/src/fastauth/storage/postgres/adapter.py +779 -0
- fastauth_py-0.1.0/src/fastauth/storage/postgres/migrations.py +57 -0
- fastauth_py-0.1.0/src/fastauth/storage/postgres/schema.py +244 -0
- fastauth_py-0.1.0/src/fastauth/web/__init__.py +0 -0
- fastauth_py-0.1.0/src/fastauth/web/csrf.py +122 -0
- fastauth_py-0.1.0/src/fastauth/web/fastapi.py +694 -0
- fastauth_py-0.1.0/src/fastauth/web/security_headers.py +82 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Python bytecode and caches
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# Virtual environments
|
|
7
|
+
.venv/
|
|
8
|
+
venv/
|
|
9
|
+
env/
|
|
10
|
+
|
|
11
|
+
# Build and packaging output
|
|
12
|
+
build/
|
|
13
|
+
dist/
|
|
14
|
+
*.egg-info/
|
|
15
|
+
|
|
16
|
+
# Test, lint, type-check, and coverage caches
|
|
17
|
+
.coverage
|
|
18
|
+
coverage.xml
|
|
19
|
+
htmlcov/
|
|
20
|
+
.pytest_cache/
|
|
21
|
+
.ruff_cache/
|
|
22
|
+
.mypy_cache/
|
|
23
|
+
.pyright/
|
|
24
|
+
.uv-cache/
|
|
25
|
+
|
|
26
|
+
# Generated docs
|
|
27
|
+
site/
|
|
28
|
+
|
|
29
|
+
# OS/editor noise
|
|
30
|
+
.DS_Store
|
|
31
|
+
.idea/
|
|
32
|
+
.vscode/
|
|
33
|
+
|
|
34
|
+
# Local environment files
|
|
35
|
+
.env
|
|
36
|
+
.env.*
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 FastAuth contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fastauth-py
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A modular, Pydantic-native, async-only authentication library for FastAPI.
|
|
5
|
+
Project-URL: Homepage, https://github.com/bhargavandhe2310/fastauth
|
|
6
|
+
Project-URL: Documentation, https://github.com/bhargavandhe2310/fastauth#readme
|
|
7
|
+
Project-URL: Repository, https://github.com/bhargavandhe2310/fastauth
|
|
8
|
+
Project-URL: Issues, https://github.com/bhargavandhe2310/fastauth/issues
|
|
9
|
+
Author-email: Bhargav Andhe <bhargavandhe2310@gmail.com>
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: auth,authentication,beanie,fastapi,jwt,mongodb,postgres,pydantic,security,sqlalchemy
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Framework :: FastAPI
|
|
15
|
+
Classifier: Framework :: Pydantic :: 2
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Requires-Dist: argon2-cffi>=23.1
|
|
21
|
+
Requires-Dist: fastapi>=0.115
|
|
22
|
+
Requires-Dist: itsdangerous>=2.2
|
|
23
|
+
Requires-Dist: jinja2>=3.1
|
|
24
|
+
Requires-Dist: pydantic[email]>=2.8
|
|
25
|
+
Requires-Dist: starlette>=0.40
|
|
26
|
+
Requires-Dist: typing-extensions>=4.12
|
|
27
|
+
Provides-Extra: beanie
|
|
28
|
+
Requires-Dist: beanie<2,>=1.27; extra == 'beanie'
|
|
29
|
+
Requires-Dist: motor>=3.6; extra == 'beanie'
|
|
30
|
+
Provides-Extra: cli
|
|
31
|
+
Requires-Dist: rich>=13.7; extra == 'cli'
|
|
32
|
+
Requires-Dist: typer>=0.12; extra == 'cli'
|
|
33
|
+
Provides-Extra: dev
|
|
34
|
+
Requires-Dist: httpx>=0.27; extra == 'dev'
|
|
35
|
+
Requires-Dist: pre-commit>=3.8; extra == 'dev'
|
|
36
|
+
Requires-Dist: pyright>=1.1.380; extra == 'dev'
|
|
37
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
|
|
38
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
39
|
+
Requires-Dist: pytest>=8.3; extra == 'dev'
|
|
40
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
41
|
+
Requires-Dist: testcontainers[mongodb,postgres]>=4.8; extra == 'dev'
|
|
42
|
+
Requires-Dist: uvicorn[standard]>=0.30; extra == 'dev'
|
|
43
|
+
Provides-Extra: docs
|
|
44
|
+
Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
|
|
45
|
+
Requires-Dist: mkdocstrings[python]>=0.26; extra == 'docs'
|
|
46
|
+
Provides-Extra: jwt
|
|
47
|
+
Requires-Dist: cryptography>=43.0; extra == 'jwt'
|
|
48
|
+
Requires-Dist: joserfc>=1.0; extra == 'jwt'
|
|
49
|
+
Provides-Extra: postgres
|
|
50
|
+
Requires-Dist: asyncpg>=0.29; extra == 'postgres'
|
|
51
|
+
Requires-Dist: sqlalchemy[asyncio]<3,>=2.0; extra == 'postgres'
|
|
52
|
+
Description-Content-Type: text/markdown
|
|
53
|
+
|
|
54
|
+
# fastauth
|
|
55
|
+
|
|
56
|
+
A modular, Pydantic-native, async-only authentication library for FastAPI.
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install fastauth-py
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from fastapi import FastAPI
|
|
64
|
+
from pydantic import SecretStr
|
|
65
|
+
|
|
66
|
+
from fastauth import FastAuth, FastAuthConfig
|
|
67
|
+
|
|
68
|
+
app_secret = "replace-me-with-a-secret-from-your-application-config"
|
|
69
|
+
|
|
70
|
+
config = FastAuthConfig(secret_key=SecretStr(app_secret))
|
|
71
|
+
auth = FastAuth(config)
|
|
72
|
+
|
|
73
|
+
app = FastAPI(lifespan=auth.lifespan)
|
|
74
|
+
auth.install(app)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
That's it. You now have `/auth/sign-up/email`, `/auth/sign-in/email`,
|
|
78
|
+
`/auth/sign-out`, `/auth/get-session`, `/auth/verify-email`,
|
|
79
|
+
`/auth/forgot-password`, `/auth/reset-password`, `/auth/change-password`,
|
|
80
|
+
`/auth/set-password`, `/auth/verify-password`, `/auth/user`,
|
|
81
|
+
`/auth/delete-account`, `/auth/delete-account/request`,
|
|
82
|
+
`/auth/delete-account/confirm`,
|
|
83
|
+
`/auth/change-email/{request,confirm}`, `/auth/sessions` (list / revoke /
|
|
84
|
+
revoke-others), `/auth/refresh`, and `/auth/health` wired into your FastAPI
|
|
85
|
+
application. Rate-limiting, account-lockout, and refresh tokens are part of
|
|
86
|
+
the router. CSRF and security headers are ASGI middleware installed by
|
|
87
|
+
`auth.install(app)`. If you use `auth.as_asgi()` instead, fastauth returns a
|
|
88
|
+
standalone app with the same routes and middleware already installed.
|
|
89
|
+
|
|
90
|
+
## Why fastauth
|
|
91
|
+
|
|
92
|
+
Built deliberately for the **modern Python web stack** — FastAPI + Pydantic
|
|
93
|
+
v2 + async-only + MongoDB or Postgres persistence:
|
|
94
|
+
|
|
95
|
+
- **Pydantic v2 everywhere.** Every domain model, every request body, every
|
|
96
|
+
response is a `BaseModel`. No `dataclass`/`NamedTuple`/`TypedDict` smuggled
|
|
97
|
+
in. Four documented carve-outs for plain dicts (OpenAPI schema, JWK,
|
|
98
|
+
JWT payload, HTTP headers); everything else is typed.
|
|
99
|
+
- **Async-only.** No sync wrappers, no thread-pool shims. Your event loop
|
|
100
|
+
doesn't get hijacked.
|
|
101
|
+
- **Strict-typed.** `pyright --strict` passes with **0 errors, 0 warnings**.
|
|
102
|
+
`py.typed` marker ships with the wheel — your IDE and your CI get full
|
|
103
|
+
type information.
|
|
104
|
+
- **Source-agnostic config.** `FastAuthConfig` is a plain `BaseModel`. The
|
|
105
|
+
framework **never reads process-level configuration**. You build config
|
|
106
|
+
from your application settings object, vault client, parameter store, or
|
|
107
|
+
test fixture and pass it in explicitly.
|
|
108
|
+
- **Plugins as first-class extension points.** Six built-in plugins
|
|
109
|
+
(`ApiKeyPlugin`, `JwtPlugin`, `EmailOtpPlugin`, `AuditLogsPlugin`,
|
|
110
|
+
`OpenApiPlugin`, `TestUtilsPlugin`) — each contributes endpoints,
|
|
111
|
+
event handlers, lifecycle hooks, and rate-limit policies through a
|
|
112
|
+
tight `Plugin` ABC. Write your own for OAuth providers, webhooks,
|
|
113
|
+
custom MFA — whatever your app needs.
|
|
114
|
+
- **Capability-based storage protocols.** `DatabaseAdapter` covers the core
|
|
115
|
+
auth flows. Optional surfaces (`ApiKeyStore`, `JwksKeyStore`,
|
|
116
|
+
`AuditLogStore`, `RateLimitStore`) are only required when you enable the
|
|
117
|
+
matching plugin or database-backed feature.
|
|
118
|
+
- **Pure events.** A single typed `EventBus` carries 19 concrete
|
|
119
|
+
`AuthEvent` subclasses (`UserSignedUp`, `UserEmailVerified`,
|
|
120
|
+
`PasswordChanged`, `AccountLockedOut`, …). Subscribe and react —
|
|
121
|
+
send emails, ping Slack, write audit rows, whatever.
|
|
122
|
+
|
|
123
|
+
## What's in the box
|
|
124
|
+
|
|
125
|
+
### Auth flows
|
|
126
|
+
- Sign-up / sign-in / sign-out by email or username
|
|
127
|
+
- Email verification with anti-enumeration
|
|
128
|
+
- Password reset with anti-enumeration and session-wide revoke
|
|
129
|
+
- Authenticated change-password (keeps current session, revokes others)
|
|
130
|
+
- Authenticated profile update, set-password, verify-password, and
|
|
131
|
+
account deletion with password or email-token verification
|
|
132
|
+
- Authenticated change-email with re-verification
|
|
133
|
+
- Refresh tokens with **one-time-use rotation** and
|
|
134
|
+
**family-revocation on reuse** (OAuth 2.1-style theft detection)
|
|
135
|
+
- Multi-session management: list, revoke one, revoke-all-except-current
|
|
136
|
+
|
|
137
|
+
### Sessions
|
|
138
|
+
- Database-backed sessions (revocable, IP/UA bound) **or** JWT sessions
|
|
139
|
+
(stateless, JWKS-signed). One config flag flips between them.
|
|
140
|
+
- JWKS with auto-generated keys, AES-GCM at-rest encryption with master-key
|
|
141
|
+
rotation support, and an opt-in `set-auth-jwt` response header that
|
|
142
|
+
attaches a JWT to every authenticated response.
|
|
143
|
+
- Local key signing **or** plug in your own `KmsSigner` (HSM, AWS KMS,
|
|
144
|
+
GCP KMS, …) via a tiny Protocol.
|
|
145
|
+
|
|
146
|
+
### Security
|
|
147
|
+
- **Argon2id** password hashing (configurable cost).
|
|
148
|
+
- **Account lockout** — HTTP 423 + `Retry-After` after 5 failed sign-ins in 15 min.
|
|
149
|
+
- **CSRF middleware** — Origin/Referer validation on state-changing methods,
|
|
150
|
+
bearer-only requests are exempt.
|
|
151
|
+
- **Rate limiting** with /64 IPv6-subnet bucketing, per-(IP, path) windowing,
|
|
152
|
+
pluggable storage (memory, MongoDB, Postgres).
|
|
153
|
+
- **Security headers** — HSTS, `X-Frame-Options`, `X-Content-Type-Options`,
|
|
154
|
+
`Referrer-Policy` on by default; opt-in `Permissions-Policy` and CSP.
|
|
155
|
+
|
|
156
|
+
### Plugins (each optional)
|
|
157
|
+
- **ApiKeyPlugin** — create/verify/list/update/delete API keys with optional
|
|
158
|
+
refilling quotas and per-key rate limits.
|
|
159
|
+
- **JwtPlugin** — `/auth/token` to mint a JWT from a session, `/auth/jwks`
|
|
160
|
+
for the public key set.
|
|
161
|
+
- **EmailOtpPlugin** — passwordless sign-in, email verification, password
|
|
162
|
+
reset, and (optional) email change via 6-digit OTPs delivered to email.
|
|
163
|
+
Hashed storage, per-OTP attempt cap, lockout-coupled.
|
|
164
|
+
- **AuditLogsPlugin** — auto-captures every `AuthEvent` into a paginated
|
|
165
|
+
audit-log collection.
|
|
166
|
+
- **OpenApiPlugin** — Scalar UI at `/auth/reference`, OpenAPI 3.1 schema
|
|
167
|
+
at `/auth/openapi.json`.
|
|
168
|
+
- **TestUtilsPlugin** — factories, login helpers, OTP capture for tests.
|
|
169
|
+
|
|
170
|
+
### Developer experience
|
|
171
|
+
- **`CurrentUser` / `CurrentSession` FastAPI dependencies** with optional
|
|
172
|
+
variants, both `Depends(...)` and `Annotated[...]` calling styles
|
|
173
|
+
documented.
|
|
174
|
+
- **`FastAuth(config)`** — uses the in-memory adapter by default when
|
|
175
|
+
`DatabaseConfig.backend == "memory"` for compact tests and first-run demos.
|
|
176
|
+
- **`auth.install(app)`** — install routes, CSRF, and security headers on your
|
|
177
|
+
FastAPI app in one call. `FastAuth.as_asgi()` still returns a standalone app
|
|
178
|
+
when you want fastauth mounted separately.
|
|
179
|
+
- **Typer CLI** — `fastauth init --backend memory|mongo|postgres`,
|
|
180
|
+
`fastauth migrate`, `fastauth generate-secret`.
|
|
181
|
+
- **mkdocs-material docs** + quickstart example app with its own test suite.
|
|
182
|
+
|
|
183
|
+
## Installation
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
# Core (in-memory adapter only — useful for tests and local dev)
|
|
187
|
+
pip install fastauth-py
|
|
188
|
+
|
|
189
|
+
# MongoDB-backed production
|
|
190
|
+
pip install fastauth-py[beanie,jwt]
|
|
191
|
+
|
|
192
|
+
# Postgres-backed production
|
|
193
|
+
pip install fastauth-py[postgres,jwt]
|
|
194
|
+
|
|
195
|
+
# All implemented optional extras
|
|
196
|
+
pip install fastauth-py[beanie,postgres,jwt,cli,docs]
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Extras: `beanie` (MongoDB), `postgres` (SQLAlchemy async + asyncpg), `jwt`
|
|
200
|
+
(JOSE signing + crypto for at-rest JWK encryption), `cli` (Typer CLI),
|
|
201
|
+
`docs` (mkdocs-material toolchain).
|
|
202
|
+
|
|
203
|
+
Python 3.11+ required. FastAPI 0.115+, Pydantic 2.8+.
|
|
204
|
+
|
|
205
|
+
## Protecting routes
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
from fastapi import Depends
|
|
209
|
+
from fastauth.domain.models import User
|
|
210
|
+
|
|
211
|
+
@app.get("/me")
|
|
212
|
+
async def me(user: User = Depends(auth.get_current_user)) -> User:
|
|
213
|
+
return user
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Or with the `Annotated` style (FastAPI's idiom):
|
|
217
|
+
|
|
218
|
+
```python
|
|
219
|
+
from typing import Annotated
|
|
220
|
+
from fastapi import Depends
|
|
221
|
+
from fastauth.domain.models import User
|
|
222
|
+
|
|
223
|
+
CurrentUser = Annotated[User, Depends(auth.get_current_user)]
|
|
224
|
+
|
|
225
|
+
@app.get("/me")
|
|
226
|
+
async def me(user: CurrentUser) -> User:
|
|
227
|
+
return user
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Cookie auth and `Authorization: Bearer …` both work — fastauth handles either
|
|
231
|
+
transparently. Use `auth.get_optional_current_user` if anonymous requests
|
|
232
|
+
are allowed.
|
|
233
|
+
|
|
234
|
+
## Configuration
|
|
235
|
+
|
|
236
|
+
`FastAuthConfig` is a plain `pydantic.BaseModel`. Every field has a sensible
|
|
237
|
+
default; pass only what you want to override:
|
|
238
|
+
|
|
239
|
+
```python
|
|
240
|
+
from fastauth import FastAuthConfig
|
|
241
|
+
from fastauth.config import (
|
|
242
|
+
AppConfig, CookieConfig, CsrfConfig,
|
|
243
|
+
LockoutConfig, RefreshTokenConfig, SecurityHeadersConfig,
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
config = FastAuthConfig(
|
|
247
|
+
secret_key=SecretStr("…"),
|
|
248
|
+
app=AppConfig(name="My App", base_url="https://myapp.com"),
|
|
249
|
+
cookie=CookieConfig(secure=True, same_site="strict"),
|
|
250
|
+
csrf=CsrfConfig(trusted_origins=["https://myapp.com"]),
|
|
251
|
+
lockout=LockoutConfig(max_failures=10, window_seconds=300),
|
|
252
|
+
refresh_token=RefreshTokenConfig(max_age_seconds=14 * 24 * 3600),
|
|
253
|
+
security_headers=SecurityHeadersConfig(
|
|
254
|
+
content_security_policy="default-src 'self'",
|
|
255
|
+
),
|
|
256
|
+
)
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
16 sub-configs cover `app`, `session`, `cookie`, `password`, `email`,
|
|
260
|
+
`email_verification`, `password_reset`, `email_change`, `delete_account`,
|
|
261
|
+
`rate_limit`, `csrf`, `lockout`, `refresh_token`, `security_headers`,
|
|
262
|
+
`database`, `advanced`.
|
|
263
|
+
|
|
264
|
+
See [docs/concepts/config.md](docs/concepts/config.md) for the full reference.
|
|
265
|
+
|
|
266
|
+
## Documentation
|
|
267
|
+
|
|
268
|
+
Full docs site: `mkdocs serve` from a checkout.
|
|
269
|
+
|
|
270
|
+
- [Quickstart](docs/quickstart.md)
|
|
271
|
+
- [Config](docs/concepts/config.md) · [Sessions](docs/concepts/sessions.md) ·
|
|
272
|
+
[Plugins](docs/concepts/plugins.md) · [Adapters](docs/concepts/adapters.md) ·
|
|
273
|
+
[CSRF](docs/concepts/csrf.md) · [Events](docs/concepts/events.md) ·
|
|
274
|
+
[Hooks](docs/concepts/hooks.md)
|
|
275
|
+
- [Email verification guide](docs/guides/email-verification.md) ·
|
|
276
|
+
[Password reset guide](docs/guides/password-reset.md) ·
|
|
277
|
+
[User management guide](docs/guides/user-management.md) ·
|
|
278
|
+
[KMS signing guide](docs/guides/kms-signing.md)
|
|
279
|
+
|
|
280
|
+
## Project layout
|
|
281
|
+
|
|
282
|
+
```
|
|
283
|
+
fastauth/
|
|
284
|
+
├── config.py / exceptions.py # top-level
|
|
285
|
+
├── domain/ # pure data: enums, models, events
|
|
286
|
+
├── security/ # auth primitives: passwords, tokens, sessions, jwt,
|
|
287
|
+
│ # refresh_tokens, lockout, rate_limit
|
|
288
|
+
├── storage/ # Core/optional adapter protocols + InMemory/Beanie/Postgres backends
|
|
289
|
+
├── messaging/ # email + Jinja2 templates
|
|
290
|
+
├── flows/ # sign-up, sign-in, verification, refresh, …
|
|
291
|
+
├── plugins/ # api_key, jwt, audit_logs, openapi, test_utils
|
|
292
|
+
├── runtime/ # FastAuth, AuthContext, AuthApi, EventBus, hooks
|
|
293
|
+
├── web/ # FastAPI integration + CSRF + security headers
|
|
294
|
+
└── cli/ # Typer CLI
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
## Status
|
|
298
|
+
|
|
299
|
+
**v0.1.0** — first release. Coverage spans unit tests, adapter-contract tests,
|
|
300
|
+
integration flows, CLI behavior, and the quickstart example. `pyright --strict`
|
|
301
|
+
is clean. See [CHANGELOG.md](CHANGELOG.md) for the detailed feature list.
|
|
302
|
+
|
|
303
|
+
**Roadmap** (v0.2+):
|
|
304
|
+
- OAuth providers (Google → GitHub → Apple → Microsoft)
|
|
305
|
+
- 2FA / TOTP
|
|
306
|
+
- Webhooks
|
|
307
|
+
- HIBP password breach check
|
|
308
|
+
- Audit-log enrichment (geo-IP, UA parsing)
|
|
309
|
+
|
|
310
|
+
## Contributing
|
|
311
|
+
|
|
312
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for the project-wide rules
|
|
313
|
+
(no leading-underscore names, async-only, Pydantic-everywhere, …).
|
|
314
|
+
Quick development loop:
|
|
315
|
+
|
|
316
|
+
```bash
|
|
317
|
+
uv sync --all-extras
|
|
318
|
+
uv run ruff check
|
|
319
|
+
uv run pyright
|
|
320
|
+
uv run pytest
|
|
321
|
+
uv run mkdocs serve
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
## License
|
|
325
|
+
|
|
326
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
# fastauth
|
|
2
|
+
|
|
3
|
+
A modular, Pydantic-native, async-only authentication library for FastAPI.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install fastauth-py
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
from fastapi import FastAPI
|
|
11
|
+
from pydantic import SecretStr
|
|
12
|
+
|
|
13
|
+
from fastauth import FastAuth, FastAuthConfig
|
|
14
|
+
|
|
15
|
+
app_secret = "replace-me-with-a-secret-from-your-application-config"
|
|
16
|
+
|
|
17
|
+
config = FastAuthConfig(secret_key=SecretStr(app_secret))
|
|
18
|
+
auth = FastAuth(config)
|
|
19
|
+
|
|
20
|
+
app = FastAPI(lifespan=auth.lifespan)
|
|
21
|
+
auth.install(app)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
That's it. You now have `/auth/sign-up/email`, `/auth/sign-in/email`,
|
|
25
|
+
`/auth/sign-out`, `/auth/get-session`, `/auth/verify-email`,
|
|
26
|
+
`/auth/forgot-password`, `/auth/reset-password`, `/auth/change-password`,
|
|
27
|
+
`/auth/set-password`, `/auth/verify-password`, `/auth/user`,
|
|
28
|
+
`/auth/delete-account`, `/auth/delete-account/request`,
|
|
29
|
+
`/auth/delete-account/confirm`,
|
|
30
|
+
`/auth/change-email/{request,confirm}`, `/auth/sessions` (list / revoke /
|
|
31
|
+
revoke-others), `/auth/refresh`, and `/auth/health` wired into your FastAPI
|
|
32
|
+
application. Rate-limiting, account-lockout, and refresh tokens are part of
|
|
33
|
+
the router. CSRF and security headers are ASGI middleware installed by
|
|
34
|
+
`auth.install(app)`. If you use `auth.as_asgi()` instead, fastauth returns a
|
|
35
|
+
standalone app with the same routes and middleware already installed.
|
|
36
|
+
|
|
37
|
+
## Why fastauth
|
|
38
|
+
|
|
39
|
+
Built deliberately for the **modern Python web stack** — FastAPI + Pydantic
|
|
40
|
+
v2 + async-only + MongoDB or Postgres persistence:
|
|
41
|
+
|
|
42
|
+
- **Pydantic v2 everywhere.** Every domain model, every request body, every
|
|
43
|
+
response is a `BaseModel`. No `dataclass`/`NamedTuple`/`TypedDict` smuggled
|
|
44
|
+
in. Four documented carve-outs for plain dicts (OpenAPI schema, JWK,
|
|
45
|
+
JWT payload, HTTP headers); everything else is typed.
|
|
46
|
+
- **Async-only.** No sync wrappers, no thread-pool shims. Your event loop
|
|
47
|
+
doesn't get hijacked.
|
|
48
|
+
- **Strict-typed.** `pyright --strict` passes with **0 errors, 0 warnings**.
|
|
49
|
+
`py.typed` marker ships with the wheel — your IDE and your CI get full
|
|
50
|
+
type information.
|
|
51
|
+
- **Source-agnostic config.** `FastAuthConfig` is a plain `BaseModel`. The
|
|
52
|
+
framework **never reads process-level configuration**. You build config
|
|
53
|
+
from your application settings object, vault client, parameter store, or
|
|
54
|
+
test fixture and pass it in explicitly.
|
|
55
|
+
- **Plugins as first-class extension points.** Six built-in plugins
|
|
56
|
+
(`ApiKeyPlugin`, `JwtPlugin`, `EmailOtpPlugin`, `AuditLogsPlugin`,
|
|
57
|
+
`OpenApiPlugin`, `TestUtilsPlugin`) — each contributes endpoints,
|
|
58
|
+
event handlers, lifecycle hooks, and rate-limit policies through a
|
|
59
|
+
tight `Plugin` ABC. Write your own for OAuth providers, webhooks,
|
|
60
|
+
custom MFA — whatever your app needs.
|
|
61
|
+
- **Capability-based storage protocols.** `DatabaseAdapter` covers the core
|
|
62
|
+
auth flows. Optional surfaces (`ApiKeyStore`, `JwksKeyStore`,
|
|
63
|
+
`AuditLogStore`, `RateLimitStore`) are only required when you enable the
|
|
64
|
+
matching plugin or database-backed feature.
|
|
65
|
+
- **Pure events.** A single typed `EventBus` carries 19 concrete
|
|
66
|
+
`AuthEvent` subclasses (`UserSignedUp`, `UserEmailVerified`,
|
|
67
|
+
`PasswordChanged`, `AccountLockedOut`, …). Subscribe and react —
|
|
68
|
+
send emails, ping Slack, write audit rows, whatever.
|
|
69
|
+
|
|
70
|
+
## What's in the box
|
|
71
|
+
|
|
72
|
+
### Auth flows
|
|
73
|
+
- Sign-up / sign-in / sign-out by email or username
|
|
74
|
+
- Email verification with anti-enumeration
|
|
75
|
+
- Password reset with anti-enumeration and session-wide revoke
|
|
76
|
+
- Authenticated change-password (keeps current session, revokes others)
|
|
77
|
+
- Authenticated profile update, set-password, verify-password, and
|
|
78
|
+
account deletion with password or email-token verification
|
|
79
|
+
- Authenticated change-email with re-verification
|
|
80
|
+
- Refresh tokens with **one-time-use rotation** and
|
|
81
|
+
**family-revocation on reuse** (OAuth 2.1-style theft detection)
|
|
82
|
+
- Multi-session management: list, revoke one, revoke-all-except-current
|
|
83
|
+
|
|
84
|
+
### Sessions
|
|
85
|
+
- Database-backed sessions (revocable, IP/UA bound) **or** JWT sessions
|
|
86
|
+
(stateless, JWKS-signed). One config flag flips between them.
|
|
87
|
+
- JWKS with auto-generated keys, AES-GCM at-rest encryption with master-key
|
|
88
|
+
rotation support, and an opt-in `set-auth-jwt` response header that
|
|
89
|
+
attaches a JWT to every authenticated response.
|
|
90
|
+
- Local key signing **or** plug in your own `KmsSigner` (HSM, AWS KMS,
|
|
91
|
+
GCP KMS, …) via a tiny Protocol.
|
|
92
|
+
|
|
93
|
+
### Security
|
|
94
|
+
- **Argon2id** password hashing (configurable cost).
|
|
95
|
+
- **Account lockout** — HTTP 423 + `Retry-After` after 5 failed sign-ins in 15 min.
|
|
96
|
+
- **CSRF middleware** — Origin/Referer validation on state-changing methods,
|
|
97
|
+
bearer-only requests are exempt.
|
|
98
|
+
- **Rate limiting** with /64 IPv6-subnet bucketing, per-(IP, path) windowing,
|
|
99
|
+
pluggable storage (memory, MongoDB, Postgres).
|
|
100
|
+
- **Security headers** — HSTS, `X-Frame-Options`, `X-Content-Type-Options`,
|
|
101
|
+
`Referrer-Policy` on by default; opt-in `Permissions-Policy` and CSP.
|
|
102
|
+
|
|
103
|
+
### Plugins (each optional)
|
|
104
|
+
- **ApiKeyPlugin** — create/verify/list/update/delete API keys with optional
|
|
105
|
+
refilling quotas and per-key rate limits.
|
|
106
|
+
- **JwtPlugin** — `/auth/token` to mint a JWT from a session, `/auth/jwks`
|
|
107
|
+
for the public key set.
|
|
108
|
+
- **EmailOtpPlugin** — passwordless sign-in, email verification, password
|
|
109
|
+
reset, and (optional) email change via 6-digit OTPs delivered to email.
|
|
110
|
+
Hashed storage, per-OTP attempt cap, lockout-coupled.
|
|
111
|
+
- **AuditLogsPlugin** — auto-captures every `AuthEvent` into a paginated
|
|
112
|
+
audit-log collection.
|
|
113
|
+
- **OpenApiPlugin** — Scalar UI at `/auth/reference`, OpenAPI 3.1 schema
|
|
114
|
+
at `/auth/openapi.json`.
|
|
115
|
+
- **TestUtilsPlugin** — factories, login helpers, OTP capture for tests.
|
|
116
|
+
|
|
117
|
+
### Developer experience
|
|
118
|
+
- **`CurrentUser` / `CurrentSession` FastAPI dependencies** with optional
|
|
119
|
+
variants, both `Depends(...)` and `Annotated[...]` calling styles
|
|
120
|
+
documented.
|
|
121
|
+
- **`FastAuth(config)`** — uses the in-memory adapter by default when
|
|
122
|
+
`DatabaseConfig.backend == "memory"` for compact tests and first-run demos.
|
|
123
|
+
- **`auth.install(app)`** — install routes, CSRF, and security headers on your
|
|
124
|
+
FastAPI app in one call. `FastAuth.as_asgi()` still returns a standalone app
|
|
125
|
+
when you want fastauth mounted separately.
|
|
126
|
+
- **Typer CLI** — `fastauth init --backend memory|mongo|postgres`,
|
|
127
|
+
`fastauth migrate`, `fastauth generate-secret`.
|
|
128
|
+
- **mkdocs-material docs** + quickstart example app with its own test suite.
|
|
129
|
+
|
|
130
|
+
## Installation
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
# Core (in-memory adapter only — useful for tests and local dev)
|
|
134
|
+
pip install fastauth-py
|
|
135
|
+
|
|
136
|
+
# MongoDB-backed production
|
|
137
|
+
pip install fastauth-py[beanie,jwt]
|
|
138
|
+
|
|
139
|
+
# Postgres-backed production
|
|
140
|
+
pip install fastauth-py[postgres,jwt]
|
|
141
|
+
|
|
142
|
+
# All implemented optional extras
|
|
143
|
+
pip install fastauth-py[beanie,postgres,jwt,cli,docs]
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Extras: `beanie` (MongoDB), `postgres` (SQLAlchemy async + asyncpg), `jwt`
|
|
147
|
+
(JOSE signing + crypto for at-rest JWK encryption), `cli` (Typer CLI),
|
|
148
|
+
`docs` (mkdocs-material toolchain).
|
|
149
|
+
|
|
150
|
+
Python 3.11+ required. FastAPI 0.115+, Pydantic 2.8+.
|
|
151
|
+
|
|
152
|
+
## Protecting routes
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
from fastapi import Depends
|
|
156
|
+
from fastauth.domain.models import User
|
|
157
|
+
|
|
158
|
+
@app.get("/me")
|
|
159
|
+
async def me(user: User = Depends(auth.get_current_user)) -> User:
|
|
160
|
+
return user
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Or with the `Annotated` style (FastAPI's idiom):
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
from typing import Annotated
|
|
167
|
+
from fastapi import Depends
|
|
168
|
+
from fastauth.domain.models import User
|
|
169
|
+
|
|
170
|
+
CurrentUser = Annotated[User, Depends(auth.get_current_user)]
|
|
171
|
+
|
|
172
|
+
@app.get("/me")
|
|
173
|
+
async def me(user: CurrentUser) -> User:
|
|
174
|
+
return user
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Cookie auth and `Authorization: Bearer …` both work — fastauth handles either
|
|
178
|
+
transparently. Use `auth.get_optional_current_user` if anonymous requests
|
|
179
|
+
are allowed.
|
|
180
|
+
|
|
181
|
+
## Configuration
|
|
182
|
+
|
|
183
|
+
`FastAuthConfig` is a plain `pydantic.BaseModel`. Every field has a sensible
|
|
184
|
+
default; pass only what you want to override:
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
from fastauth import FastAuthConfig
|
|
188
|
+
from fastauth.config import (
|
|
189
|
+
AppConfig, CookieConfig, CsrfConfig,
|
|
190
|
+
LockoutConfig, RefreshTokenConfig, SecurityHeadersConfig,
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
config = FastAuthConfig(
|
|
194
|
+
secret_key=SecretStr("…"),
|
|
195
|
+
app=AppConfig(name="My App", base_url="https://myapp.com"),
|
|
196
|
+
cookie=CookieConfig(secure=True, same_site="strict"),
|
|
197
|
+
csrf=CsrfConfig(trusted_origins=["https://myapp.com"]),
|
|
198
|
+
lockout=LockoutConfig(max_failures=10, window_seconds=300),
|
|
199
|
+
refresh_token=RefreshTokenConfig(max_age_seconds=14 * 24 * 3600),
|
|
200
|
+
security_headers=SecurityHeadersConfig(
|
|
201
|
+
content_security_policy="default-src 'self'",
|
|
202
|
+
),
|
|
203
|
+
)
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
16 sub-configs cover `app`, `session`, `cookie`, `password`, `email`,
|
|
207
|
+
`email_verification`, `password_reset`, `email_change`, `delete_account`,
|
|
208
|
+
`rate_limit`, `csrf`, `lockout`, `refresh_token`, `security_headers`,
|
|
209
|
+
`database`, `advanced`.
|
|
210
|
+
|
|
211
|
+
See [docs/concepts/config.md](docs/concepts/config.md) for the full reference.
|
|
212
|
+
|
|
213
|
+
## Documentation
|
|
214
|
+
|
|
215
|
+
Full docs site: `mkdocs serve` from a checkout.
|
|
216
|
+
|
|
217
|
+
- [Quickstart](docs/quickstart.md)
|
|
218
|
+
- [Config](docs/concepts/config.md) · [Sessions](docs/concepts/sessions.md) ·
|
|
219
|
+
[Plugins](docs/concepts/plugins.md) · [Adapters](docs/concepts/adapters.md) ·
|
|
220
|
+
[CSRF](docs/concepts/csrf.md) · [Events](docs/concepts/events.md) ·
|
|
221
|
+
[Hooks](docs/concepts/hooks.md)
|
|
222
|
+
- [Email verification guide](docs/guides/email-verification.md) ·
|
|
223
|
+
[Password reset guide](docs/guides/password-reset.md) ·
|
|
224
|
+
[User management guide](docs/guides/user-management.md) ·
|
|
225
|
+
[KMS signing guide](docs/guides/kms-signing.md)
|
|
226
|
+
|
|
227
|
+
## Project layout
|
|
228
|
+
|
|
229
|
+
```
|
|
230
|
+
fastauth/
|
|
231
|
+
├── config.py / exceptions.py # top-level
|
|
232
|
+
├── domain/ # pure data: enums, models, events
|
|
233
|
+
├── security/ # auth primitives: passwords, tokens, sessions, jwt,
|
|
234
|
+
│ # refresh_tokens, lockout, rate_limit
|
|
235
|
+
├── storage/ # Core/optional adapter protocols + InMemory/Beanie/Postgres backends
|
|
236
|
+
├── messaging/ # email + Jinja2 templates
|
|
237
|
+
├── flows/ # sign-up, sign-in, verification, refresh, …
|
|
238
|
+
├── plugins/ # api_key, jwt, audit_logs, openapi, test_utils
|
|
239
|
+
├── runtime/ # FastAuth, AuthContext, AuthApi, EventBus, hooks
|
|
240
|
+
├── web/ # FastAPI integration + CSRF + security headers
|
|
241
|
+
└── cli/ # Typer CLI
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Status
|
|
245
|
+
|
|
246
|
+
**v0.1.0** — first release. Coverage spans unit tests, adapter-contract tests,
|
|
247
|
+
integration flows, CLI behavior, and the quickstart example. `pyright --strict`
|
|
248
|
+
is clean. See [CHANGELOG.md](CHANGELOG.md) for the detailed feature list.
|
|
249
|
+
|
|
250
|
+
**Roadmap** (v0.2+):
|
|
251
|
+
- OAuth providers (Google → GitHub → Apple → Microsoft)
|
|
252
|
+
- 2FA / TOTP
|
|
253
|
+
- Webhooks
|
|
254
|
+
- HIBP password breach check
|
|
255
|
+
- Audit-log enrichment (geo-IP, UA parsing)
|
|
256
|
+
|
|
257
|
+
## Contributing
|
|
258
|
+
|
|
259
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for the project-wide rules
|
|
260
|
+
(no leading-underscore names, async-only, Pydantic-everywhere, …).
|
|
261
|
+
Quick development loop:
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
uv sync --all-extras
|
|
265
|
+
uv run ruff check
|
|
266
|
+
uv run pyright
|
|
267
|
+
uv run pytest
|
|
268
|
+
uv run mkdocs serve
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## License
|
|
272
|
+
|
|
273
|
+
MIT — see [LICENSE](LICENSE).
|