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.
Files changed (79) hide show
  1. fastauth_py-0.1.0/.gitignore +36 -0
  2. fastauth_py-0.1.0/LICENSE +21 -0
  3. fastauth_py-0.1.0/PKG-INFO +326 -0
  4. fastauth_py-0.1.0/README.md +273 -0
  5. fastauth_py-0.1.0/pyproject.toml +106 -0
  6. fastauth_py-0.1.0/src/fastauth/__init__.py +9 -0
  7. fastauth_py-0.1.0/src/fastauth/cli/__init__.py +3 -0
  8. fastauth_py-0.1.0/src/fastauth/cli/main.py +207 -0
  9. fastauth_py-0.1.0/src/fastauth/config.py +270 -0
  10. fastauth_py-0.1.0/src/fastauth/domain/__init__.py +0 -0
  11. fastauth_py-0.1.0/src/fastauth/domain/enums.py +125 -0
  12. fastauth_py-0.1.0/src/fastauth/domain/events.py +217 -0
  13. fastauth_py-0.1.0/src/fastauth/domain/models.py +238 -0
  14. fastauth_py-0.1.0/src/fastauth/exceptions.py +191 -0
  15. fastauth_py-0.1.0/src/fastauth/flows/__init__.py +3 -0
  16. fastauth_py-0.1.0/src/fastauth/flows/change_email.py +203 -0
  17. fastauth_py-0.1.0/src/fastauth/flows/change_password.py +75 -0
  18. fastauth_py-0.1.0/src/fastauth/flows/credentials.py +337 -0
  19. fastauth_py-0.1.0/src/fastauth/flows/email_otp.py +818 -0
  20. fastauth_py-0.1.0/src/fastauth/flows/password_reset.py +163 -0
  21. fastauth_py-0.1.0/src/fastauth/flows/refresh.py +63 -0
  22. fastauth_py-0.1.0/src/fastauth/flows/sessions.py +116 -0
  23. fastauth_py-0.1.0/src/fastauth/flows/user_management.py +304 -0
  24. fastauth_py-0.1.0/src/fastauth/flows/verification.py +147 -0
  25. fastauth_py-0.1.0/src/fastauth/messaging/__init__.py +0 -0
  26. fastauth_py-0.1.0/src/fastauth/messaging/email.py +60 -0
  27. fastauth_py-0.1.0/src/fastauth/messaging/templates/delete_account.html +10 -0
  28. fastauth_py-0.1.0/src/fastauth/messaging/templates/delete_account.txt +8 -0
  29. fastauth_py-0.1.0/src/fastauth/messaging/templates/otp_email_change.html +10 -0
  30. fastauth_py-0.1.0/src/fastauth/messaging/templates/otp_email_change.txt +7 -0
  31. fastauth_py-0.1.0/src/fastauth/messaging/templates/otp_password_reset.html +10 -0
  32. fastauth_py-0.1.0/src/fastauth/messaging/templates/otp_password_reset.txt +7 -0
  33. fastauth_py-0.1.0/src/fastauth/messaging/templates/otp_sign_in.html +10 -0
  34. fastauth_py-0.1.0/src/fastauth/messaging/templates/otp_sign_in.txt +7 -0
  35. fastauth_py-0.1.0/src/fastauth/messaging/templates/otp_verification.html +10 -0
  36. fastauth_py-0.1.0/src/fastauth/messaging/templates/otp_verification.txt +7 -0
  37. fastauth_py-0.1.0/src/fastauth/messaging/templates/reset.html +9 -0
  38. fastauth_py-0.1.0/src/fastauth/messaging/templates/reset.txt +6 -0
  39. fastauth_py-0.1.0/src/fastauth/messaging/templates/verification.html +9 -0
  40. fastauth_py-0.1.0/src/fastauth/messaging/templates/verification.txt +6 -0
  41. fastauth_py-0.1.0/src/fastauth/plugins/__init__.py +3 -0
  42. fastauth_py-0.1.0/src/fastauth/plugins/api_key.py +433 -0
  43. fastauth_py-0.1.0/src/fastauth/plugins/audit_logs.py +195 -0
  44. fastauth_py-0.1.0/src/fastauth/plugins/base.py +210 -0
  45. fastauth_py-0.1.0/src/fastauth/plugins/email_otp.py +336 -0
  46. fastauth_py-0.1.0/src/fastauth/plugins/jwt.py +212 -0
  47. fastauth_py-0.1.0/src/fastauth/plugins/openapi.py +137 -0
  48. fastauth_py-0.1.0/src/fastauth/plugins/test_utils.py +137 -0
  49. fastauth_py-0.1.0/src/fastauth/py.typed +0 -0
  50. fastauth_py-0.1.0/src/fastauth/runtime/__init__.py +0 -0
  51. fastauth_py-0.1.0/src/fastauth/runtime/api.py +432 -0
  52. fastauth_py-0.1.0/src/fastauth/runtime/auth.py +281 -0
  53. fastauth_py-0.1.0/src/fastauth/runtime/context.py +45 -0
  54. fastauth_py-0.1.0/src/fastauth/runtime/event_bus.py +43 -0
  55. fastauth_py-0.1.0/src/fastauth/runtime/hooks.py +59 -0
  56. fastauth_py-0.1.0/src/fastauth/security/__init__.py +0 -0
  57. fastauth_py-0.1.0/src/fastauth/security/jwt.py +371 -0
  58. fastauth_py-0.1.0/src/fastauth/security/lockout.py +94 -0
  59. fastauth_py-0.1.0/src/fastauth/security/otp.py +59 -0
  60. fastauth_py-0.1.0/src/fastauth/security/passwords.py +46 -0
  61. fastauth_py-0.1.0/src/fastauth/security/rate_limit.py +199 -0
  62. fastauth_py-0.1.0/src/fastauth/security/refresh_tokens.py +161 -0
  63. fastauth_py-0.1.0/src/fastauth/security/sessions.py +107 -0
  64. fastauth_py-0.1.0/src/fastauth/security/tokens.py +66 -0
  65. fastauth_py-0.1.0/src/fastauth/storage/__init__.py +1 -0
  66. fastauth_py-0.1.0/src/fastauth/storage/base.py +318 -0
  67. fastauth_py-0.1.0/src/fastauth/storage/beanie/__init__.py +73 -0
  68. fastauth_py-0.1.0/src/fastauth/storage/beanie/adapter.py +578 -0
  69. fastauth_py-0.1.0/src/fastauth/storage/beanie/documents.py +276 -0
  70. fastauth_py-0.1.0/src/fastauth/storage/beanie/helpers.py +52 -0
  71. fastauth_py-0.1.0/src/fastauth/storage/memory.py +404 -0
  72. fastauth_py-0.1.0/src/fastauth/storage/postgres/__init__.py +25 -0
  73. fastauth_py-0.1.0/src/fastauth/storage/postgres/adapter.py +779 -0
  74. fastauth_py-0.1.0/src/fastauth/storage/postgres/migrations.py +57 -0
  75. fastauth_py-0.1.0/src/fastauth/storage/postgres/schema.py +244 -0
  76. fastauth_py-0.1.0/src/fastauth/web/__init__.py +0 -0
  77. fastauth_py-0.1.0/src/fastauth/web/csrf.py +122 -0
  78. fastauth_py-0.1.0/src/fastauth/web/fastapi.py +694 -0
  79. 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).