kantu-auth 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 (92) hide show
  1. kantu_auth-0.1.0/.gitignore +21 -0
  2. kantu_auth-0.1.0/CHANGELOG.md +52 -0
  3. kantu_auth-0.1.0/LICENSE +21 -0
  4. kantu_auth-0.1.0/PKG-INFO +468 -0
  5. kantu_auth-0.1.0/README.md +350 -0
  6. kantu_auth-0.1.0/SECURITY.md +21 -0
  7. kantu_auth-0.1.0/docs/cli.md +75 -0
  8. kantu_auth-0.1.0/docs/configuration.md +244 -0
  9. kantu_auth-0.1.0/docs/email-adapters.md +291 -0
  10. kantu_auth-0.1.0/docs/routes.md +219 -0
  11. kantu_auth-0.1.0/docs/security.md +122 -0
  12. kantu_auth-0.1.0/pyproject.toml +144 -0
  13. kantu_auth-0.1.0/src/fastapi_auth/__init__.py +35 -0
  14. kantu_auth-0.1.0/src/fastapi_auth/adapters/__init__.py +6 -0
  15. kantu_auth-0.1.0/src/fastapi_auth/adapters/base.py +16 -0
  16. kantu_auth-0.1.0/src/fastapi_auth/adapters/smtp.py +52 -0
  17. kantu_auth-0.1.0/src/fastapi_auth/cli.py +160 -0
  18. kantu_auth-0.1.0/src/fastapi_auth/config.py +315 -0
  19. kantu_auth-0.1.0/src/fastapi_auth/db.py +38 -0
  20. kantu_auth-0.1.0/src/fastapi_auth/dependencies.py +283 -0
  21. kantu_auth-0.1.0/src/fastapi_auth/extras/__init__.py +0 -0
  22. kantu_auth-0.1.0/src/fastapi_auth/extras/celery_app.py +41 -0
  23. kantu_auth-0.1.0/src/fastapi_auth/extras/mailer.py +250 -0
  24. kantu_auth-0.1.0/src/fastapi_auth/extras/qr.py +24 -0
  25. kantu_auth-0.1.0/src/fastapi_auth/extras/templates/_layout.html +40 -0
  26. kantu_auth-0.1.0/src/fastapi_auth/extras/templates/google_account_linked.html +47 -0
  27. kantu_auth-0.1.0/src/fastapi_auth/extras/templates/google_account_unlinked.html +17 -0
  28. kantu_auth-0.1.0/src/fastapi_auth/extras/templates/new_login_alert.html +13 -0
  29. kantu_auth-0.1.0/src/fastapi_auth/extras/templates/password_changed.html +8 -0
  30. kantu_auth-0.1.0/src/fastapi_auth/extras/templates/password_reset.html +17 -0
  31. kantu_auth-0.1.0/src/fastapi_auth/extras/templates/two_factor_otp.html +8 -0
  32. kantu_auth-0.1.0/src/fastapi_auth/extras/templates/two_factor_security.html +13 -0
  33. kantu_auth-0.1.0/src/fastapi_auth/extras/templates/verify_email.html +17 -0
  34. kantu_auth-0.1.0/src/fastapi_auth/extras/templates/welcome.html +6 -0
  35. kantu_auth-0.1.0/src/fastapi_auth/extras/turnstile.py +59 -0
  36. kantu_auth-0.1.0/src/fastapi_auth/jwt.py +298 -0
  37. kantu_auth-0.1.0/src/fastapi_auth/models.py +376 -0
  38. kantu_auth-0.1.0/src/fastapi_auth/rate_limit.py +133 -0
  39. kantu_auth-0.1.0/src/fastapi_auth/redis_client.py +135 -0
  40. kantu_auth-0.1.0/src/fastapi_auth/router.py +116 -0
  41. kantu_auth-0.1.0/src/fastapi_auth/routers/__init__.py +0 -0
  42. kantu_auth-0.1.0/src/fastapi_auth/routers/auth.py +390 -0
  43. kantu_auth-0.1.0/src/fastapi_auth/routers/google.py +588 -0
  44. kantu_auth-0.1.0/src/fastapi_auth/routers/two_factor.py +427 -0
  45. kantu_auth-0.1.0/src/fastapi_auth/routers/users.py +23 -0
  46. kantu_auth-0.1.0/src/fastapi_auth/routers/verification.py +100 -0
  47. kantu_auth-0.1.0/src/fastapi_auth/schemas.py +352 -0
  48. kantu_auth-0.1.0/src/fastapi_auth/schemas_google.py +104 -0
  49. kantu_auth-0.1.0/src/fastapi_auth/security.py +36 -0
  50. kantu_auth-0.1.0/src/fastapi_auth/services/__init__.py +0 -0
  51. kantu_auth-0.1.0/src/fastapi_auth/services/auth_service.py +673 -0
  52. kantu_auth-0.1.0/src/fastapi_auth/services/oauth/__init__.py +0 -0
  53. kantu_auth-0.1.0/src/fastapi_auth/services/oauth/google_service.py +311 -0
  54. kantu_auth-0.1.0/src/fastapi_auth/services/recent_auth_service.py +124 -0
  55. kantu_auth-0.1.0/src/fastapi_auth/services/session_service.py +487 -0
  56. kantu_auth-0.1.0/src/fastapi_auth/services/two_factor_service.py +464 -0
  57. kantu_auth-0.1.0/src/fastapi_auth/services/verification_service.py +130 -0
  58. kantu_auth-0.1.0/src/fastapi_auth/tasks/celery_email.py +39 -0
  59. kantu_auth-0.1.0/src/fastapi_auth/tasks/emails.py +205 -0
  60. kantu_auth-0.1.0/src/fastapi_auth/templates/.env.example +56 -0
  61. kantu_auth-0.1.0/src/fastapi_auth/templates/KANTU_AUTH_SETUP.md +97 -0
  62. kantu_auth-0.1.0/src/fastapi_auth/templates/app/__init__.py +1 -0
  63. kantu_auth-0.1.0/src/fastapi_auth/templates/app/alembic.ini +39 -0
  64. kantu_auth-0.1.0/src/fastapi_auth/templates/app/core/__init__.py +1 -0
  65. kantu_auth-0.1.0/src/fastapi_auth/templates/app/core/celery_app.py +20 -0
  66. kantu_auth-0.1.0/src/fastapi_auth/templates/app/core/config.py +178 -0
  67. kantu_auth-0.1.0/src/fastapi_auth/templates/app/core/db.py +23 -0
  68. kantu_auth-0.1.0/src/fastapi_auth/templates/app/main.py +45 -0
  69. kantu_auth-0.1.0/src/fastapi_auth/templates/app/migrations/README +1 -0
  70. kantu_auth-0.1.0/src/fastapi_auth/templates/app/migrations/env.py +59 -0
  71. kantu_auth-0.1.0/src/fastapi_auth/templates/app/migrations/script.py.mako +24 -0
  72. kantu_auth-0.1.0/src/fastapi_auth/templates/app/migrations/versions/.gitkeep +1 -0
  73. kantu_auth-0.1.0/src/fastapi_auth/utils.py +190 -0
  74. kantu_auth-0.1.0/src/fastapi_auth/validators.py +32 -0
  75. kantu_auth-0.1.0/tests/conftest.py +267 -0
  76. kantu_auth-0.1.0/tests/test_cli.py +66 -0
  77. kantu_auth-0.1.0/tests/test_config_and_schemas.py +218 -0
  78. kantu_auth-0.1.0/tests/test_core_endpoints.py +356 -0
  79. kantu_auth-0.1.0/tests/test_email.py +148 -0
  80. kantu_auth-0.1.0/tests/test_google_endpoints.py +414 -0
  81. kantu_auth-0.1.0/tests/test_google_link_concurrency.py +107 -0
  82. kantu_auth-0.1.0/tests/test_google_security.py +974 -0
  83. kantu_auth-0.1.0/tests/test_password_mutation_locking.py +271 -0
  84. kantu_auth-0.1.0/tests/test_proxy_headers.py +217 -0
  85. kantu_auth-0.1.0/tests/test_rate_limit_and_turnstile.py +219 -0
  86. kantu_auth-0.1.0/tests/test_recent_auth.py +304 -0
  87. kantu_auth-0.1.0/tests/test_router.py +125 -0
  88. kantu_auth-0.1.0/tests/test_session_concurrency.py +361 -0
  89. kantu_auth-0.1.0/tests/test_token_hardening.py +246 -0
  90. kantu_auth-0.1.0/tests/test_two_factor_dependency.py +131 -0
  91. kantu_auth-0.1.0/tests/test_two_factor_endpoints.py +288 -0
  92. kantu_auth-0.1.0/tests/test_verification_endpoints.py +117 -0
@@ -0,0 +1,21 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+
12
+ # Local dev artifacts
13
+ *.db
14
+ .env
15
+ .env.*
16
+ !.env.example
17
+
18
+ # Runtime service state
19
+ dump.rdb
20
+
21
+ frontend
@@ -0,0 +1,52 @@
1
+ # Changelog
2
+
3
+ All notable changes to Kantu Auth are documented here. The project follows
4
+ [Semantic Versioning](https://semver.org/) while its public API is pre-1.0.
5
+
6
+ ## [Unreleased]
7
+
8
+ ### Security
9
+
10
+ - Validate proxy peers and forwarded IP chains with explicit trusted CIDRs.
11
+ - Prevent account enumeration through duplicate registration responses.
12
+ - Prevent implicit same-email Google account linking and linked-identity replacement.
13
+ - Bind Google account-link state to the authorizing active session and make
14
+ concurrent link completion first-writer-wins.
15
+ - Require authenticated, step-up-protected Google-only password setup.
16
+ - Invalidate undelivered Google password-setup tokens and return a controlled
17
+ `503` for email import, configuration, delivery, or queueing failures.
18
+ - Refresh verified provider emails only for passwordless Google-created
19
+ accounts, rejecting collisions without merging local identities.
20
+ - Bind Google session-exchange tokens to the provider identity.
21
+ - Revoke sessions and notify the account after Google unlinking.
22
+ - Issue a fresh session ID for every authenticated login and serialize
23
+ same-user session mutations so revoked access tokens cannot be reactivated.
24
+ - Rotate refresh tokens with a conditional database update so concurrent reuse
25
+ cannot produce two successful rotations.
26
+ - Retry only SQLite lock conflicts during concurrent same-device login while
27
+ preserving fresh session IDs and surfacing unrelated database failures.
28
+ - Use a consistent `User` → reset-token → session lock order for password
29
+ changes and resets, with conditional one-time reset-token consumption.
30
+ - Require a recent, session-bound password or Google proof before unenrolled
31
+ users can enroll 2FA or perform sensitive Google account actions; enrolled
32
+ users must continue to use their 2FA step-up.
33
+ - Fail closed for enrolled two-factor users when 2FA or Redis is unavailable.
34
+ - Send verification and reset secrets in frontend URL fragments and support
35
+ body-based email-verification confirmation.
36
+
37
+ ### Added
38
+
39
+ - Initial alpha version of the reusable FastAPI authentication package and
40
+ `kantu-auth init` application scaffold.
41
+ - Dedicated Google login, exchange, password-setup, and linking rate limits.
42
+ - Public `require_two_factor`, `require_sensitive_auth`, and
43
+ `require_google_only_user` dependencies.
44
+ - Safe `google_linked` account state in user responses.
45
+ - Python 3.11–3.14 continuous-integration matrix and distribution checks.
46
+
47
+ ### Removed
48
+
49
+ - Removed the deprecated misspelled `fastapi_auth.adpters` compatibility
50
+ namespace; migrate imports to `fastapi_auth.adapters`.
51
+ - Removed the undocumented `/auth/google/auth/add-password/request` alias; use
52
+ authenticated `POST /auth/google/password/request` with an empty JSON body.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ukasha-hacks
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 all
13
+ 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 THE
21
+ SOFTWARE.
@@ -0,0 +1,468 @@
1
+ Metadata-Version: 2.5
2
+ Name: kantu-auth
3
+ Version: 0.1.0
4
+ Summary: Reusable authentication for FastAPI with secure sessions, email verification, two-factor authentication, and optional OAuth.
5
+ Project-URL: Repository, https://github.com/ukasha-hacks/fastapi_auth
6
+ Project-URL: Documentation, https://github.com/ukasha-hacks/fastapi_auth#readme
7
+ Author: ukasha-hacks
8
+ License: MIT License
9
+
10
+ Copyright (c) 2026 ukasha-hacks
11
+
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ of this software and associated documentation files (the "Software"), to deal
14
+ in the Software without restriction, including without limitation the rights
15
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ copies of the Software, and to permit persons to whom the Software is
17
+ furnished to do so, subject to the following conditions:
18
+
19
+ The above copyright notice and this permission notice shall be included in all
20
+ copies or substantial portions of the Software.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
+ SOFTWARE.
29
+ License-File: LICENSE
30
+ Keywords: authentication,fastapi,jwt,oauth,two-factor
31
+ Classifier: Development Status :: 3 - Alpha
32
+ Classifier: Framework :: FastAPI
33
+ Classifier: Intended Audience :: Developers
34
+ Classifier: License :: OSI Approved :: MIT License
35
+ Classifier: Operating System :: OS Independent
36
+ Classifier: Programming Language :: Python :: 3 :: Only
37
+ Classifier: Programming Language :: Python :: 3.11
38
+ Classifier: Programming Language :: Python :: 3.12
39
+ Classifier: Programming Language :: Python :: 3.13
40
+ Classifier: Programming Language :: Python :: 3.14
41
+ Classifier: Topic :: Internet :: WWW/HTTP
42
+ Classifier: Topic :: Security
43
+ Requires-Python: >=3.11
44
+ Requires-Dist: alembic>=1.18.5
45
+ Requires-Dist: bcrypt<4.1
46
+ Requires-Dist: fastapi>=0.110
47
+ Requires-Dist: passlib[bcrypt]>=1.7.4
48
+ Requires-Dist: pydantic-settings>=2.4
49
+ Requires-Dist: pydantic[email]>=2.7
50
+ Requires-Dist: pyjwt>=2.8.0
51
+ Requires-Dist: sqlalchemy[asyncio]>=2.0.30
52
+ Requires-Dist: sqlmodel>=0.0.21
53
+ Requires-Dist: typer>=0.15
54
+ Provides-Extra: all
55
+ Requires-Dist: aiosmtplib>=3.0; extra == 'all'
56
+ Requires-Dist: aiosqlite>=0.20; extra == 'all'
57
+ Requires-Dist: asyncpg>=0.29; extra == 'all'
58
+ Requires-Dist: celery>=5.3; extra == 'all'
59
+ Requires-Dist: cryptography>=42; extra == 'all'
60
+ Requires-Dist: google-auth>=2.30; extra == 'all'
61
+ Requires-Dist: httpx>=0.27; extra == 'all'
62
+ Requires-Dist: jinja2>=3.1; extra == 'all'
63
+ Requires-Dist: pyotp>=2.9.0; extra == 'all'
64
+ Requires-Dist: qrcode[pil]>=7.4; extra == 'all'
65
+ Requires-Dist: redis>=5.0; extra == 'all'
66
+ Requires-Dist: urllib3>=2.0; extra == 'all'
67
+ Provides-Extra: celery
68
+ Requires-Dist: aiosmtplib>=3.0; extra == 'celery'
69
+ Requires-Dist: celery>=5.3; extra == 'celery'
70
+ Requires-Dist: jinja2>=3.1; extra == 'celery'
71
+ Provides-Extra: dev
72
+ Requires-Dist: aiosmtplib>=3.0; extra == 'dev'
73
+ Requires-Dist: aiosqlite>=0.20; extra == 'dev'
74
+ Requires-Dist: build>=1.2; extra == 'dev'
75
+ Requires-Dist: celery>=5.3; extra == 'dev'
76
+ Requires-Dist: cryptography>=42; extra == 'dev'
77
+ Requires-Dist: fakeredis>=2.23; extra == 'dev'
78
+ Requires-Dist: google-auth>=2.30; extra == 'dev'
79
+ Requires-Dist: httpx>=0.27; extra == 'dev'
80
+ Requires-Dist: jinja2>=3.1; extra == 'dev'
81
+ Requires-Dist: pyotp>=2.9.0; extra == 'dev'
82
+ Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
83
+ Requires-Dist: pytest-cov>=5; extra == 'dev'
84
+ Requires-Dist: pytest>=8; extra == 'dev'
85
+ Requires-Dist: qrcode[pil]>=7.4; extra == 'dev'
86
+ Requires-Dist: redis>=5.0; extra == 'dev'
87
+ Requires-Dist: ruff>=0.8; extra == 'dev'
88
+ Requires-Dist: twine>=6.1; extra == 'dev'
89
+ Requires-Dist: urllib3>=2.0; extra == 'dev'
90
+ Provides-Extra: email
91
+ Requires-Dist: aiosmtplib>=3.0; extra == 'email'
92
+ Requires-Dist: jinja2>=3.1; extra == 'email'
93
+ Provides-Extra: encryption
94
+ Requires-Dist: cryptography>=42; extra == 'encryption'
95
+ Provides-Extra: google
96
+ Requires-Dist: aiosmtplib>=3.0; extra == 'google'
97
+ Requires-Dist: google-auth>=2.30; extra == 'google'
98
+ Requires-Dist: httpx>=0.27; extra == 'google'
99
+ Requires-Dist: jinja2>=3.1; extra == 'google'
100
+ Requires-Dist: redis>=5.0; extra == 'google'
101
+ Requires-Dist: urllib3>=2.0; extra == 'google'
102
+ Provides-Extra: postgres
103
+ Requires-Dist: asyncpg>=0.29; extra == 'postgres'
104
+ Provides-Extra: qr
105
+ Requires-Dist: qrcode[pil]>=7.4; extra == 'qr'
106
+ Provides-Extra: redis
107
+ Requires-Dist: redis>=5.0; extra == 'redis'
108
+ Provides-Extra: sqlite
109
+ Requires-Dist: aiosqlite>=0.20; extra == 'sqlite'
110
+ Provides-Extra: turnstile
111
+ Requires-Dist: httpx>=0.27; extra == 'turnstile'
112
+ Provides-Extra: two-factor
113
+ Requires-Dist: cryptography>=42; extra == 'two-factor'
114
+ Requires-Dist: pyotp>=2.9.0; extra == 'two-factor'
115
+ Requires-Dist: qrcode[pil]>=7.4; extra == 'two-factor'
116
+ Requires-Dist: redis>=5.0; extra == 'two-factor'
117
+ Description-Content-Type: text/markdown
118
+
119
+ # Kantu Auth
120
+
121
+ Kantu Auth is a reusable authentication package for FastAPI. It owns the
122
+ authentication models, services, dependencies, and routers; your application
123
+ owns its database engine, settings, migrations, and deployment.
124
+
125
+ The package currently provides:
126
+
127
+ - email/password registration and login;
128
+ - short-lived access tokens and rotating refresh-token sessions;
129
+ - session listing, per-session revocation, and logout-all;
130
+ - password changes and one-time password-reset tokens;
131
+ - optional email verification;
132
+ - optional TOTP two-factor authentication, recovery codes, and email OTP;
133
+ - optional Google OAuth login and account linking;
134
+ - optional Redis-backed rate limiting;
135
+ - optional Cloudflare Turnstile checks;
136
+ - SMTP or custom email delivery, inline or through Celery; and
137
+ - an application scaffold generated by `kantu-auth init`.
138
+
139
+ > Kantu Auth is currently pre-1.0. Review the [security checklist](docs/security.md),
140
+ > run your own threat model, and test the integration before using it in
141
+ > production.
142
+
143
+ ## Install
144
+
145
+ Install the package with only the features your application uses:
146
+
147
+ ```bash
148
+ pip install "kantu-auth[sqlite,email]" uvicorn
149
+ ```
150
+
151
+ For development from this repository:
152
+
153
+ ```bash
154
+ git clone https://github.com/ukasha-hacks/fastapi_auth.git
155
+ cd fastapi_auth
156
+ python -m venv .venv
157
+ . .venv/bin/activate
158
+ pip install -e ".[dev]"
159
+ ```
160
+
161
+ The distribution and command are named `kantu-auth`. The Python import remains
162
+ `fastapi_auth` for compatibility during the pre-1.0 rename.
163
+
164
+ ### Optional dependencies
165
+
166
+ | Extra | Use it for |
167
+ | --- | --- |
168
+ | `sqlite` | Async SQLite through `aiosqlite` |
169
+ | `postgres` | Async PostgreSQL through `asyncpg` |
170
+ | `redis` | Rate limiting and other Redis-backed features |
171
+ | `email` | Jinja email templates and async SMTP |
172
+ | `celery` | Queued email delivery; includes the email dependencies |
173
+ | `qr` | QR data URLs returned by TOTP setup |
174
+ | `encryption` | Encryption of stored TOTP secrets |
175
+ | `two-factor` | Redis, QR generation, and TOTP-secret encryption; add `email` for emailed OTPs |
176
+ | `turnstile` | Cloudflare Turnstile verification |
177
+ | `google` | Google OAuth with PKCE; includes Redis and password-setup email support |
178
+ | `all` | Every runtime integration |
179
+ | `dev` | The local test toolchain |
180
+
181
+ ## Initialize an application
182
+
183
+ From the root of a FastAPI project, run:
184
+
185
+ ```bash
186
+ kantu-auth init
187
+ ```
188
+
189
+ The command creates an integration you can edit and own:
190
+
191
+ ```text
192
+ .
193
+ ├── .env.example
194
+ ├── KANTU_AUTH_SETUP.md
195
+ └── app
196
+ ├── __init__.py
197
+ ├── main.py
198
+ ├── alembic.ini
199
+ ├── core
200
+ │ ├── __init__.py
201
+ │ ├── celery_app.py
202
+ │ ├── config.py
203
+ │ └── db.py
204
+ └── migrations
205
+ ├── README
206
+ ├── env.py
207
+ ├── script.py.mako
208
+ └── versions
209
+ ```
210
+
211
+ Existing files are skipped. Use `--dest PATH` to target another directory and
212
+ use `--force` only when you intentionally want to overwrite matching files:
213
+
214
+ ```bash
215
+ kantu-auth init --dest ./my-api
216
+ kantu-auth init --dest ./my-api --force
217
+ ```
218
+
219
+ Then configure and start the generated app:
220
+
221
+ ```bash
222
+ cp .env.example .env
223
+ # Replace JWT_SECRET_KEY and review every enabled feature.
224
+
225
+ alembic -c app/alembic.ini revision --autogenerate -m "init auth"
226
+ alembic -c app/alembic.ini upgrade head
227
+ uvicorn app.main:app --reload
228
+ ```
229
+
230
+ The generated `.env` is read by the generated host `AppSettings`, so it uses
231
+ ordinary names such as `DATABASE_URL` and `JWT_SECRET_KEY`. The host validates
232
+ them and passes the relevant values to Kantu Auth.
233
+
234
+ See [CLI and scaffold](docs/cli.md) for the generated-file contract and upgrade
235
+ guidance.
236
+
237
+ ## Integrate without the scaffold
238
+
239
+ Kantu Auth does not create or own a global database engine. Build your engine
240
+ and session factory, configure the package, and inject the factory during app
241
+ startup:
242
+
243
+ ```python
244
+ import os
245
+ from contextlib import asynccontextmanager
246
+
247
+ from fastapi import FastAPI
248
+ from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
249
+ from sqlmodel.ext.asyncio.session import AsyncSession
250
+
251
+ from fastapi_auth import configure, get_auth_router, set_session_factory
252
+
253
+ engine = create_async_engine("sqlite+aiosqlite:///./kantu.db")
254
+ session_factory = async_sessionmaker(
255
+ engine,
256
+ class_=AsyncSession,
257
+ expire_on_commit=False,
258
+ )
259
+
260
+ configure(
261
+ APP_NAME="My API",
262
+ JWT_SECRET_KEY=os.environ["JWT_SECRET_KEY"],
263
+ FRONTEND_URL="http://localhost:3000",
264
+ )
265
+
266
+
267
+ @asynccontextmanager
268
+ async def lifespan(app: FastAPI):
269
+ set_session_factory(session_factory)
270
+ try:
271
+ yield
272
+ finally:
273
+ from fastapi_auth.redis_client import close_redis_client
274
+
275
+ await close_redis_client()
276
+ await engine.dispose()
277
+
278
+
279
+ app = FastAPI(lifespan=lifespan)
280
+ app.include_router(get_auth_router(), prefix="/api/v1")
281
+ ```
282
+
283
+ Use Alembic migrations in deployed environments. Import the models in the
284
+ Alembic environment before assigning `SQLModel.metadata` so autogeneration can
285
+ see every auth table; the generated scaffold already does this.
286
+
287
+ ### Direct environment configuration
288
+
289
+ If you do not call `configure()`, Kantu Auth reads its own values lazily from
290
+ the environment with a `KANTU_AUTH_` prefix:
291
+
292
+ ```bash
293
+ export KANTU_AUTH_JWT_SECRET_KEY="$(openssl rand -hex 32)"
294
+ KANTU_AUTH_FRONTEND_URL=https://app.example.com
295
+ KANTU_AUTH_REDIS_URL=redis://localhost:6379/0
296
+ KANTU_AUTH_ENABLE_TWO_FACTOR=true
297
+ ```
298
+
299
+ Programmatic `configure()` rejects unknown names, which catches misspelled
300
+ security settings. See [configuration](docs/configuration.md) for every setting,
301
+ unit, dependency, and feature prerequisite.
302
+
303
+ ## Authentication flow
304
+
305
+ 1. Register with `POST /auth/register`.
306
+ 2. If email verification is enabled, confirm the one-time link before login.
307
+ 3. Log in with `POST /auth/login`.
308
+ 4. If 2FA is disabled, the response contains access and refresh tokens. If 2FA
309
+ is enabled, it contains a short-lived challenge for
310
+ `POST /auth/two-factor/verify`.
311
+ 5. Send the access token as `Authorization: Bearer <token>`.
312
+ 6. Exchange the refresh token at `POST /auth/refresh`. Successful refreshes
313
+ rotate the token; replaying an older token revokes the session.
314
+
315
+ Registration deliberately returns the same `201` response for a new or an
316
+ existing email address. Do not make your frontend replace that neutral message
317
+ with an account-existence error.
318
+
319
+ All documented request bodies reject unknown fields. Passwords must be 8–128
320
+ characters and satisfy the rules implemented in `fastapi_auth.validators`.
321
+
322
+ The exact route catalogue and optional-router requirements are in
323
+ [routes](docs/routes.md).
324
+
325
+ ### Protect a host-application route with recent authentication
326
+
327
+ Kantu Auth exports its sensitive-action dependency for routes owned by your API:
328
+
329
+ ```python
330
+ from typing import Annotated
331
+
332
+ from fastapi import Depends
333
+ from fastapi_auth import require_sensitive_auth
334
+ from fastapi_auth.models import User
335
+
336
+
337
+ @app.delete("/billing/account")
338
+ async def delete_billing_account(
339
+ user: Annotated[User, Depends(require_sensitive_auth)],
340
+ ):
341
+ return {"deleted_for": user.id}
342
+ ```
343
+
344
+ Enrolled users must first call `POST /auth/two-factor/validate`. Unenrolled
345
+ users prove their current password at `POST /auth/reauth/password`, while
346
+ Google-only users use `GET /auth/google/reauth/request`. Every proof is bound
347
+ to the current session and expires quickly. Redis failures and contradictory
348
+ disabled-feature configuration fail closed with `503`.
349
+
350
+ The separate `require_two_factor` dependency remains available when a route
351
+ should let unenrolled users pass without a recent primary-credential proof.
352
+
353
+ ## Optional features
354
+
355
+ Optional routers are disabled by default and must be both installed and
356
+ configured:
357
+
358
+ ```python
359
+ import os
360
+
361
+ configure(
362
+ JWT_SECRET_KEY=os.environ["JWT_SECRET_KEY"],
363
+ REDIS_URL="redis://localhost:6379/0",
364
+ ENABLE_TWO_FACTOR=True,
365
+ ENABLE_EMAIL_VERIFICATION=True,
366
+ )
367
+
368
+ app.include_router(get_auth_router())
369
+ ```
370
+
371
+ Two-factor authentication, rate limiting, and Google OAuth rely on Redis for
372
+ security state. Google OAuth also requires its client ID, client secret, and
373
+ redirect URI. Turnstile requires its secret. Invalid feature combinations fail
374
+ during settings validation or router construction rather than silently running
375
+ partially configured.
376
+
377
+ TOTP and recovery-code 2FA come from the `two-factor` extra. The optional
378
+ `/auth/two-factor/send-otp` email path also needs the `email` extra, a sender,
379
+ and working SMTP or a custom adapter.
380
+
381
+ ### Google login, linking, and password setup
382
+
383
+ Install `kantu-auth[google]`, enable Google, configure email delivery, and set
384
+ separate login and link callback URIs. Begin login with
385
+ `POST /auth/google/login`, send the user to the returned URL, then exchange the
386
+ callback's short `google_token` exactly once at `POST /auth/google/session`.
387
+
388
+ Kantu Auth never links accounts merely because Google returns the same email.
389
+ An existing user signs in locally and starts `GET /auth/google/link/request`
390
+ with a Bearer token after satisfying the appropriate session-bound recent-auth
391
+ proof. A linked identity cannot be silently replaced. Link state is bound to
392
+ that active session, and concurrent callbacks cannot replace the first identity
393
+ to complete.
394
+
395
+ A passwordless Google user can request password setup only for their own
396
+ authenticated account:
397
+
398
+ ```http
399
+ POST /auth/google/password/request
400
+ Authorization: Bearer <access-token>
401
+ Content-Type: application/json
402
+
403
+ {}
404
+ ```
405
+
406
+ There is no target email field. The route is separately rate-limited and uses
407
+ the authenticated account's email. If delivery or queueing fails, the setup
408
+ token is invalidated and the route returns `503`. Unlinking Google requires a
409
+ local password, revokes every session, and sends a security notification.
410
+
411
+ ### Reverse proxies
412
+
413
+ Do not trust `X-Forwarded-For` from the public internet. Either leave
414
+ `TRUST_PROXY_HEADERS=false` and configure Uvicorn's trusted forwarding peers,
415
+ or set `TRUST_PROXY_HEADERS=true`, list exact `TRUSTED_PROXY_CIDRS`, and run
416
+ Uvicorn with `--no-proxy-headers` so Kantu Auth can verify the socket peer.
417
+ See [configuration](docs/configuration.md#reverse-proxies-and-client-ips).
418
+
419
+ ## Email delivery
420
+
421
+ For SMTP delivery, install the email extra and configure a sender:
422
+
423
+ ```bash
424
+ pip install "kantu-auth[email]"
425
+
426
+ SMTP_HOST=smtp.example.com
427
+ SMTP_PORT=587
428
+ SMTP_USERNAME=mailer
429
+ SMTP_PASSWORD=replace-me
430
+ SMTP_FROM_EMAIL=no-reply@example.com
431
+ SMTP_FROM_NAME=My API
432
+ SMTP_START_TLS=true
433
+ SMTP_USE_TLS=false
434
+ ```
435
+
436
+ Without a Celery broker, sending is awaited inline. With
437
+ `CELERY_BROKER_URL`, messages are queued through the stable
438
+ `fastapi_auth.send_email` task. Custom transports can implement the small
439
+ `EmailAdapter` protocol; built-in templates can be selectively overridden.
440
+
441
+ See [email adapters and templates](docs/email-adapters.md) for examples and
442
+ worker setup.
443
+
444
+ ## Documentation
445
+
446
+ - [CLI and generated scaffold](docs/cli.md)
447
+ - [Configuration reference](docs/configuration.md)
448
+ - [Routes and authentication flows](docs/routes.md)
449
+ - [Email adapters and templates](docs/email-adapters.md)
450
+ - [Security and production checklist](docs/security.md)
451
+
452
+ ## Development
453
+
454
+ ```bash
455
+ pip install -e ".[dev]"
456
+ pytest
457
+ python -m compileall -q src app
458
+ python -m build
459
+ python -m twine check dist/*
460
+ ```
461
+
462
+ When changing a route or setting, update its focused document and tests in the
463
+ same change. Do not commit `.env`, `.env.local`, database dumps, or generated
464
+ tokens.
465
+
466
+ ## License
467
+
468
+ Kantu Auth is available under the MIT License. See [LICENSE](LICENSE).