epok-auth 0.1.0b1__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 (59) hide show
  1. epok_auth-0.1.0b1/.env.secret.example +4 -0
  2. epok_auth-0.1.0b1/.github/workflows/ci.yml +246 -0
  3. epok_auth-0.1.0b1/.github/workflows/codeql.yml +23 -0
  4. epok_auth-0.1.0b1/.gitignore +43 -0
  5. epok_auth-0.1.0b1/CHANGELOG.md +27 -0
  6. epok_auth-0.1.0b1/CONTRIBUTING.md +23 -0
  7. epok_auth-0.1.0b1/LICENSE +21 -0
  8. epok_auth-0.1.0b1/PKG-INFO +255 -0
  9. epok_auth-0.1.0b1/README.md +212 -0
  10. epok_auth-0.1.0b1/ROADMAP.md +31 -0
  11. epok_auth-0.1.0b1/SECURITY.md +33 -0
  12. epok_auth-0.1.0b1/docs/PUBLISHING.md +178 -0
  13. epok_auth-0.1.0b1/docs/SECURITY_ASSURANCE.md +45 -0
  14. epok_auth-0.1.0b1/docs/THREAT_MODEL.md +63 -0
  15. epok_auth-0.1.0b1/docs/USAGE_ES.md +737 -0
  16. epok_auth-0.1.0b1/examples/minimal/main.py +21 -0
  17. epok_auth-0.1.0b1/examples/nuxt-bff/README.md +7 -0
  18. epok_auth-0.1.0b1/examples/nuxt-bff/server/api/auth/login.post.ts +13 -0
  19. epok_auth-0.1.0b1/examples/nuxt-bff/server/api/auth/session.get.ts +6 -0
  20. epok_auth-0.1.0b1/examples/nuxt-bff/server/utils/authSession.ts +65 -0
  21. epok_auth-0.1.0b1/pyproject.toml +126 -0
  22. epok_auth-0.1.0b1/scripts/publish.sh +187 -0
  23. epok_auth-0.1.0b1/src/epok_auth/__init__.py +23 -0
  24. epok_auth-0.1.0b1/src/epok_auth/cli.py +140 -0
  25. epok_auth-0.1.0b1/src/epok_auth/config.py +226 -0
  26. epok_auth-0.1.0b1/src/epok_auth/errors.py +58 -0
  27. epok_auth-0.1.0b1/src/epok_auth/fastapi/__init__.py +3 -0
  28. epok_auth-0.1.0b1/src/epok_auth/fastapi/integration.py +463 -0
  29. epok_auth-0.1.0b1/src/epok_auth/fastapi/schemas.py +145 -0
  30. epok_auth-0.1.0b1/src/epok_auth/migrate.py +40 -0
  31. epok_auth-0.1.0b1/src/epok_auth/migrations/__init__.py +1 -0
  32. epok_auth-0.1.0b1/src/epok_auth/migrations/env.py +98 -0
  33. epok_auth-0.1.0b1/src/epok_auth/migrations/script.py.mako +26 -0
  34. epok_auth-0.1.0b1/src/epok_auth/migrations/versions/0001_initial.py +240 -0
  35. epok_auth-0.1.0b1/src/epok_auth/models.py +143 -0
  36. epok_auth-0.1.0b1/src/epok_auth/passwords.py +140 -0
  37. epok_auth-0.1.0b1/src/epok_auth/postgres/__init__.py +4 -0
  38. epok_auth-0.1.0b1/src/epok_auth/postgres/store.py +288 -0
  39. epok_auth-0.1.0b1/src/epok_auth/postgres/tables.py +131 -0
  40. epok_auth-0.1.0b1/src/epok_auth/py.typed +0 -0
  41. epok_auth-0.1.0b1/src/epok_auth/service.py +810 -0
  42. epok_auth-0.1.0b1/src/epok_auth/store.py +42 -0
  43. epok_auth-0.1.0b1/src/epok_auth/testing/__init__.py +3 -0
  44. epok_auth-0.1.0b1/src/epok_auth/testing/memory.py +145 -0
  45. epok_auth-0.1.0b1/src/epok_auth/tokens.py +154 -0
  46. epok_auth-0.1.0b1/tests/__init__.py +0 -0
  47. epok_auth-0.1.0b1/tests/conftest.py +65 -0
  48. epok_auth-0.1.0b1/tests/test_cli.py +165 -0
  49. epok_auth-0.1.0b1/tests/test_config.py +166 -0
  50. epok_auth-0.1.0b1/tests/test_fastapi.py +356 -0
  51. epok_auth-0.1.0b1/tests/test_migrate.py +48 -0
  52. epok_auth-0.1.0b1/tests/test_models_and_memory.py +122 -0
  53. epok_auth-0.1.0b1/tests/test_package_metadata.py +7 -0
  54. epok_auth-0.1.0b1/tests/test_passwords.py +81 -0
  55. epok_auth-0.1.0b1/tests/test_postgres_integration.py +213 -0
  56. epok_auth-0.1.0b1/tests/test_service_sessions.py +338 -0
  57. epok_auth-0.1.0b1/tests/test_service_users.py +237 -0
  58. epok_auth-0.1.0b1/tests/test_tokens.py +142 -0
  59. epok_auth-0.1.0b1/uv.lock +1400 -0
@@ -0,0 +1,4 @@
1
+ # Copy this file to `.env.secret` and replace the placeholder locally.
2
+ # `.env.secret` is ignored by Git and must never be committed.
3
+ # The release script accepts only this variable and comments; it never executes the file as shell code.
4
+ UV_PUBLISH_TOKEN="pypi-REPLACE_WITH_YOUR_TOKEN"
@@ -0,0 +1,246 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [main]
6
+ push:
7
+ branches: [main]
8
+ workflow_dispatch:
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ concurrency:
14
+ group: epok-auth-beta-${{ github.event.pull_request.number || github.ref }}
15
+ cancel-in-progress: true
16
+
17
+ env:
18
+ PYTHONUNBUFFERED: "1"
19
+
20
+ jobs:
21
+ quality:
22
+ name: quality
23
+ runs-on: ubuntu-latest
24
+ timeout-minutes: 12
25
+ steps:
26
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
27
+ - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b
28
+ with:
29
+ version: "0.12.1"
30
+ enable-cache: true
31
+ - run: uv python install 3.12
32
+ - name: Require reproducible lockfile
33
+ run: |
34
+ test -f uv.lock
35
+ uv lock --check
36
+ - name: Validate release safeguards
37
+ run: |
38
+ git check-ignore -q .env.secret
39
+ ! git ls-files --error-unmatch .env.secret 2>/dev/null
40
+ bash -n scripts/publish.sh
41
+ - name: Install all dependencies
42
+ run: uv sync --locked --all-extras --group dev --python 3.12
43
+ - name: Formatting
44
+ run: uv run ruff format --check .
45
+ - name: Lint and security rules
46
+ run: uv run ruff check .
47
+ - name: Strict typing
48
+ run: uv run pyright
49
+ - name: Compile all modules
50
+ run: uv run python -m compileall -q src tests examples/minimal
51
+ - name: Dependency vulnerability audit
52
+ run: uv run pip-audit --local --progress-spinner off
53
+
54
+ tests:
55
+ name: tests-py${{ matrix.python-version }}
56
+ runs-on: ubuntu-latest
57
+ timeout-minutes: 15
58
+ strategy:
59
+ fail-fast: false
60
+ matrix:
61
+ python-version: ["3.12", "3.13", "3.14"]
62
+ steps:
63
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
64
+ - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b
65
+ with:
66
+ version: "0.12.1"
67
+ enable-cache: true
68
+ - run: uv python install ${{ matrix.python-version }}
69
+ - run: uv sync --locked --all-extras --group dev --python ${{ matrix.python-version }}
70
+ - name: Unit, HTTP and adversarial suite
71
+ run: uv run --python ${{ matrix.python-version }} pytest -m "not integration" -q
72
+
73
+ postgres:
74
+ name: postgres-17
75
+ runs-on: ubuntu-latest
76
+ timeout-minutes: 20
77
+ env:
78
+ TEST_DATABASE_URL: postgresql+psycopg://epok_auth:epok_auth@127.0.0.1:5432/epok_auth_test
79
+ services:
80
+ postgres:
81
+ image: postgres:17-alpine
82
+ env:
83
+ POSTGRES_USER: epok_auth
84
+ POSTGRES_PASSWORD: epok_auth
85
+ POSTGRES_DB: epok_auth_test
86
+ ports: ["5432:5432"]
87
+ options: >-
88
+ --health-cmd "pg_isready -U epok_auth -d epok_auth_test"
89
+ --health-interval 2s
90
+ --health-timeout 3s
91
+ --health-retries 30
92
+ steps:
93
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
94
+ - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b
95
+ with:
96
+ version: "0.12.1"
97
+ enable-cache: true
98
+ - run: uv python install 3.12
99
+ - run: uv sync --locked --all-extras --group dev --python 3.12
100
+ - name: Migrate from an empty database
101
+ run: |
102
+ uv run python - <<'PY'
103
+ import os
104
+ from epok_auth.migrate import upgrade_database
105
+
106
+ upgrade_database(os.environ["TEST_DATABASE_URL"])
107
+ PY
108
+ - name: Assert metadata has no migration drift
109
+ run: |
110
+ uv run python - <<'PY'
111
+ import os
112
+ from epok_auth.migrate import check_database
113
+
114
+ check_database(os.environ["TEST_DATABASE_URL"])
115
+ PY
116
+ - name: PostgreSQL integration and concurrency suite
117
+ run: uv run pytest -m integration -q
118
+
119
+ coverage:
120
+ name: coverage
121
+ runs-on: ubuntu-latest
122
+ timeout-minutes: 20
123
+ env:
124
+ TEST_DATABASE_URL: postgresql+psycopg://epok_auth:epok_auth@127.0.0.1:5432/epok_auth_test
125
+ services:
126
+ postgres:
127
+ image: postgres:17-alpine
128
+ env:
129
+ POSTGRES_USER: epok_auth
130
+ POSTGRES_PASSWORD: epok_auth
131
+ POSTGRES_DB: epok_auth_test
132
+ ports: ["5432:5432"]
133
+ options: >-
134
+ --health-cmd "pg_isready -U epok_auth -d epok_auth_test"
135
+ --health-interval 2s
136
+ --health-timeout 3s
137
+ --health-retries 30
138
+ steps:
139
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
140
+ - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b
141
+ with:
142
+ version: "0.12.1"
143
+ enable-cache: true
144
+ - run: uv python install 3.12
145
+ - run: uv sync --locked --all-extras --group dev --python 3.12
146
+ - name: Migrate from an empty database
147
+ run: |
148
+ uv run python - <<'PY'
149
+ import os
150
+ from epok_auth.migrate import upgrade_database
151
+
152
+ upgrade_database(os.environ["TEST_DATABASE_URL"])
153
+ PY
154
+ - name: Enforce branch coverage across unit and PostgreSQL paths
155
+ run: >-
156
+ uv run pytest
157
+ --cov=epok_auth
158
+ --cov-branch
159
+ --cov-report=term-missing
160
+ --cov-report=xml
161
+ --cov-fail-under=90
162
+
163
+ package:
164
+ name: package
165
+ runs-on: ubuntu-latest
166
+ timeout-minutes: 18
167
+ steps:
168
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
169
+ - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b
170
+ with:
171
+ version: "0.12.1"
172
+ enable-cache: true
173
+ - run: uv python install 3.12
174
+ - name: Build wheel and source distribution without local sources
175
+ run: uv build --no-sources
176
+ - name: Verify packaged files
177
+ shell: bash
178
+ run: |
179
+ set -euo pipefail
180
+ wheel="$(find dist -maxdepth 1 -name '*.whl' -print -quit)"
181
+ sdist="$(find dist -maxdepth 1 -name '*.tar.gz' -print -quit)"
182
+ test -n "$wheel"
183
+ test -n "$sdist"
184
+ test "$(find dist -maxdepth 1 -name '*.whl' | wc -l)" -eq 1
185
+ test "$(find dist -maxdepth 1 -name '*.tar.gz' | wc -l)" -eq 1
186
+ python -m zipfile -l "$wheel" | grep 'epok_auth/migrations/versions/0001_initial.py'
187
+ python -m zipfile -l "$wheel" | grep 'epok_auth/py.typed'
188
+ tar -tzf "$sdist" | grep 'src/epok_auth/migrations/versions/0001_initial.py'
189
+ tar -tzf "$sdist" | grep 'src/epok_auth/py.typed'
190
+ - name: Isolated wheel and sdist smoke tests
191
+ shell: bash
192
+ run: |
193
+ set -euo pipefail
194
+ expected_version="$(uv version --short)"
195
+ wheel="$(find dist -maxdepth 1 -name '*.whl' -print -quit)"
196
+ sdist="$(find dist -maxdepth 1 -name '*.tar.gz' -print -quit)"
197
+
198
+ smoke_test() {
199
+ artifact="$1"
200
+ environment="$2"
201
+ uv venv "$environment" --python 3.12
202
+ uv pip install --python "$environment/bin/python" "$artifact"
203
+ EPOK_AUTH_EXPECTED_VERSION="$expected_version" \
204
+ "$environment/bin/python" - <<'PY'
205
+ import os
206
+ from importlib.metadata import version
207
+
208
+ import epok_auth
209
+ from epok_auth import AuthSettings, EpokAuth
210
+
211
+ expected = os.environ["EPOK_AUTH_EXPECTED_VERSION"]
212
+ assert version("epok-auth") == expected
213
+ assert epok_auth.__version__ == expected
214
+ assert AuthSettings is not None
215
+ assert EpokAuth is not None
216
+ PY
217
+ "$environment/bin/epok-auth" --help >/dev/null
218
+ "$environment/bin/epok-auth" generate-secret --bytes 32 | grep -E '.{40,}'
219
+ }
220
+
221
+ smoke_test "$wheel" /tmp/epok-auth-wheel-smoke
222
+ smoke_test "$sdist" /tmp/epok-auth-sdist-smoke
223
+
224
+ merge-gate:
225
+ name: merge-gate
226
+ if: always()
227
+ needs: [quality, tests, postgres, coverage, package]
228
+ runs-on: ubuntu-latest
229
+ timeout-minutes: 5
230
+ steps:
231
+ - name: Require every beta gate to pass
232
+ shell: bash
233
+ env:
234
+ QUALITY: ${{ needs.quality.result }}
235
+ TESTS: ${{ needs.tests.result }}
236
+ POSTGRES: ${{ needs.postgres.result }}
237
+ COVERAGE: ${{ needs.coverage.result }}
238
+ PACKAGE: ${{ needs.package.result }}
239
+ run: |
240
+ set -euo pipefail
241
+ printf '%-12s %s\n' quality "$QUALITY" tests "$TESTS" postgres "$POSTGRES" coverage "$COVERAGE" package "$PACKAGE"
242
+ test "$QUALITY" = success
243
+ test "$TESTS" = success
244
+ test "$POSTGRES" = success
245
+ test "$COVERAGE" = success
246
+ test "$PACKAGE" = success
@@ -0,0 +1,23 @@
1
+ name: CodeQL
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [main]
6
+ push:
7
+ branches: [main]
8
+
9
+ permissions:
10
+ contents: read
11
+ security-events: write
12
+
13
+ jobs:
14
+ analyze:
15
+ runs-on: ubuntu-latest
16
+ timeout-minutes: 15
17
+ steps:
18
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
19
+ - uses: github/codeql-action/init@0daab03d71ff584ef619d027a3fd9146679c5d84
20
+ with:
21
+ languages: python
22
+ queries: security-extended
23
+ - uses: github/codeql-action/analyze@0daab03d71ff584ef619d027a3fd9146679c5d84
@@ -0,0 +1,43 @@
1
+ # Local environments and secrets
2
+ .env
3
+ .env.*
4
+ !.env.example
5
+ !.env.secret.example
6
+ .envrc
7
+ .secrets/
8
+ *credentials*.json
9
+ *service-account*.json
10
+ application_default_credentials.json
11
+ adc.json
12
+ token*.json
13
+ *.pem
14
+ *.key
15
+ *.p12
16
+ *.pfx
17
+ *.jks
18
+ *.keystore
19
+
20
+ # Python and tooling
21
+ .venv/
22
+ __pycache__/
23
+ *.py[cod]
24
+ .pytest_cache/
25
+ .ruff_cache/
26
+ .pyright/
27
+ .coverage
28
+ coverage.xml
29
+ htmlcov/
30
+
31
+ # Build and local data
32
+ dist/
33
+ build/
34
+ *.egg-info/
35
+ *.sqlite
36
+ *.sqlite3
37
+ *.db
38
+ *.log
39
+
40
+ # Operating systems and editors
41
+ .DS_Store
42
+ .idea/
43
+ .vscode/
@@ -0,0 +1,27 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0b1 — unreleased beta candidate
4
+
5
+ First production-candidate beta for the Colors integration:
6
+
7
+ - local user administration;
8
+ - Argon2id credentials with dummy verification and rehash support;
9
+ - strict short-lived access JWTs;
10
+ - opaque rotating refresh sessions;
11
+ - session families, reuse detection and immediate revocation;
12
+ - idle and absolute session deadlines;
13
+ - CSRF correlation and Origin protection;
14
+ - PostgreSQL-authoritative sessions and security events;
15
+ - packaged Alembic migrations with drift checking;
16
+ - FastAPI routers, dependencies and administrative endpoints;
17
+ - operational CLI and Nuxt BFF reference;
18
+ - reproducible `uv.lock` and isolated package installation;
19
+ - single-source versioning through `pyproject.toml`, `uv version` and `importlib.metadata`;
20
+ - guarded local publication through `uv build --no-sources` and `uv publish`;
21
+ - ignored local PyPI credentials with repository safeguards;
22
+ - CodeQL, dependency audit, strict typing and Ruff security rules;
23
+ - 102 passing tests with branch-aware coverage above 90%;
24
+ - Python 3.12, 3.13 and 3.14 validation;
25
+ - PostgreSQL 17 integration and concurrency validation.
26
+
27
+ The standalone beta gate is green. Colors backend/frontend parity, deployment configuration and application authorization remain required before product rollout.
@@ -0,0 +1,23 @@
1
+ # Contributing
2
+
3
+ All changes enter through a pull request. Direct pushes to `main` are not part of the supported workflow.
4
+
5
+ Authentication, cryptography, cookie, CSRF, migration and concurrency changes require:
6
+
7
+ 1. a stated security invariant;
8
+ 2. a functional test;
9
+ 3. an adversarial or regression test;
10
+ 4. PostgreSQL coverage when persistence semantics change;
11
+ 5. an update to the assurance manifest when the threat model changes.
12
+
13
+ Run:
14
+
15
+ ```bash
16
+ uv sync --all-extras --group dev
17
+ uv run ruff format .
18
+ uv run ruff check .
19
+ uv run pyright
20
+ uv run pytest
21
+ ```
22
+
23
+ No contribution may introduce custom cryptographic algorithms or log credential material.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 EPOK
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,255 @@
1
+ Metadata-Version: 2.4
2
+ Name: epok-auth
3
+ Version: 0.1.0b1
4
+ Summary: FastAPI-first authentication with PostgreSQL-authoritative, revocable sessions.
5
+ Project-URL: Documentation, https://github.com/epok200/epok_auth/blob/main/docs/USAGE_ES.md
6
+ Project-URL: Changelog, https://github.com/epok200/epok_auth/blob/main/CHANGELOG.md
7
+ Project-URL: Repository, https://github.com/epok200/epok_auth
8
+ Project-URL: Issues, https://github.com/epok200/epok_auth/issues
9
+ Project-URL: Security, https://github.com/epok200/epok_auth/security/policy
10
+ Author: EPOK
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: authentication,fastapi,jwt,postgresql,security,sessions
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Framework :: FastAPI
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Topic :: Security
24
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
+ Classifier: Typing :: Typed
26
+ Requires-Python: >=3.12
27
+ Requires-Dist: email-validator<3,>=2.2
28
+ Requires-Dist: fastapi<1,>=0.128
29
+ Requires-Dist: pwdlib[argon2]<0.4,>=0.3
30
+ Requires-Dist: pydantic-settings<3,>=2.11
31
+ Requires-Dist: pydantic<3,>=2.12
32
+ Requires-Dist: pyjwt<3,>=2.10
33
+ Requires-Dist: typer<1,>=0.20
34
+ Provides-Extra: all
35
+ Requires-Dist: alembic<2,>=1.17; extra == 'all'
36
+ Requires-Dist: psycopg[binary,pool]<4,>=3.2; extra == 'all'
37
+ Requires-Dist: sqlalchemy<2.1,>=2.0.45; extra == 'all'
38
+ Provides-Extra: postgres
39
+ Requires-Dist: alembic<2,>=1.17; extra == 'postgres'
40
+ Requires-Dist: psycopg[binary,pool]<4,>=3.2; extra == 'postgres'
41
+ Requires-Dist: sqlalchemy<2.1,>=2.0.45; extra == 'postgres'
42
+ Description-Content-Type: text/markdown
43
+
44
+ # epok-auth
45
+
46
+ **FastAPI-first authentication with PostgreSQL-authoritative, revocable sessions.**
47
+
48
+ `epok-auth` is designed for private B2B web applications that need secure local accounts without rebuilding password handling, session rotation, revocation, CSRF protection, administration, and FastAPI dependencies for every product.
49
+
50
+ > **Status:** `0.1.0b1` beta candidate. The standalone library gate is green. Colors integration and application-level parity remain required before using this beta in that product. Public APIs may still change before `1.0`.
51
+ >
52
+ > **Practical testing:** see the Spanish step-by-step guide in [`docs/USAGE_ES.md`](docs/USAGE_ES.md).
53
+
54
+ ## Validated beta gate
55
+
56
+ The clean beta tree is continuously validated by GitHub Actions. The current candidate has passed:
57
+
58
+ | Gate | Evidence |
59
+ |---|---|
60
+ | Functional and adversarial tests | 101/101 passing |
61
+ | Branch coverage | 94.80% |
62
+ | Python compatibility | 3.12, 3.13 and 3.14 |
63
+ | PostgreSQL | PostgreSQL 17 migration, zero Alembic drift, integration and concurrency tests |
64
+ | Static quality | Ruff formatting/lint/security rules and Pyright strict on production source |
65
+ | Dependencies | Reproducible `uv.lock` and `pip-audit` |
66
+ | Distribution | Wheel and sdist build, packaged migrations, `py.typed`, isolated install and CLI smoke test |
67
+ | Code scanning | CodeQL `security-extended` |
68
+
69
+ The repository does not claim that vulnerabilities are impossible. The green gate establishes reproducible evidence for the defined beta threat model and invariants.
70
+
71
+ ## What the beta includes
72
+
73
+ - Argon2id password hashing through `pwdlib`, with rehash support and dummy verification;
74
+ - local users, active/disabled state, roles, scopes, administrative provisioning and reset;
75
+ - short-lived access JWTs with strict issuer, audience, algorithm, type and time validation;
76
+ - opaque refresh credentials stored only as SHA-256 hashes;
77
+ - refresh rotation, session families and reuse detection;
78
+ - immediate access revocation through authoritative PostgreSQL session state;
79
+ - inactivity and absolute session deadlines;
80
+ - secure cookies, CSRF correlation and strict Origin allowlists;
81
+ - account lockout, uniform login failures and security-event persistence;
82
+ - plug-and-play FastAPI routers and dependencies;
83
+ - packaged Alembic migrations and an operational CLI;
84
+ - a Nuxt/Nitro BFF reference where Vue never receives access or refresh tokens.
85
+
86
+ Google OIDC, TOTP/MFA, passkeys, Redis coordination, multi-tenancy and service-to-service authentication remain outside this beta. See [ROADMAP.md](ROADMAP.md).
87
+
88
+ ## Installation
89
+
90
+ Until the beta is published to PyPI, consume the reviewed commit or Git tag explicitly. After publication:
91
+
92
+ ```bash
93
+ uv add "epok-auth[postgres]"
94
+ ```
95
+
96
+ Generate a secret and configure the application:
97
+
98
+ ```bash
99
+ uv run epok-auth generate-secret
100
+ ```
101
+
102
+ ```dotenv
103
+ EPOK_AUTH_ENVIRONMENT=production
104
+ EPOK_AUTH_DATABASE_URL=postgresql://colors:password@postgres/colors
105
+ EPOK_AUTH_JWT_SECRET=<generated-secret>
106
+ EPOK_AUTH_ISSUER=colors-auth
107
+ EPOK_AUTH_AUDIENCE=colors-api
108
+ EPOK_AUTH_TRUSTED_ORIGINS=https://colors.example.com
109
+ ```
110
+
111
+ Production configuration is **fail-closed**: weak secrets, insecure cookies, generic issuer/audience values, missing PostgreSQL, and ambiguous origins prevent startup.
112
+
113
+ ## Publication
114
+
115
+ The version has one source of truth in `pyproject.toml` and is exposed at runtime through `importlib.metadata`:
116
+
117
+ ```bash
118
+ uv version --short
119
+ uv version --bump beta
120
+ uv version --bump stable
121
+ uv version --bump patch
122
+ ```
123
+
124
+ Local PyPI credentials belong in an ignored `.env.secret` file. Validate or publish only from a clean `main` checkout:
125
+
126
+ ```bash
127
+ cp .env.secret.example .env.secret
128
+ bash scripts/publish.sh --dry-run
129
+ bash scripts/publish.sh
130
+ ```
131
+
132
+ The script runs the release checks, builds with `uv build --no-sources`, installs the wheel in an isolated environment and asks for the exact version before uploading. See [docs/PUBLISHING.md](docs/PUBLISHING.md) for the complete procedure.
133
+
134
+ ## Database and initial administrator
135
+
136
+ ```bash
137
+ uv run epok-auth check-config
138
+ uv run epok-auth upgrade-db
139
+ uv run epok-auth check-db
140
+ uv run epok-auth create-admin
141
+ ```
142
+
143
+ The first administrator is serialized transactionally. A second initial-admin creation attempt fails rather than racing.
144
+
145
+ ## FastAPI integration
146
+
147
+ ```python
148
+ from fastapi import Depends, FastAPI
149
+
150
+ from epok_auth import AuthSettings, EpokAuth, Principal
151
+
152
+ settings = AuthSettings()
153
+ auth = EpokAuth.postgres(settings=settings)
154
+
155
+ app = FastAPI()
156
+ auth.install(
157
+ app,
158
+ prefix="/api/v1/auth",
159
+ include_admin=True,
160
+ )
161
+
162
+ catalog = auth.protected_router(prefix="/api/v1/catalog")
163
+
164
+
165
+ @catalog.get("")
166
+ async def get_catalog(
167
+ principal: Principal = Depends(auth.authenticated),
168
+ ) -> dict[str, str]:
169
+ return {"viewer": principal.email}
170
+
171
+
172
+ @catalog.post("")
173
+ async def update_catalog(
174
+ principal: Principal = Depends(auth.require_scopes("catalog:write")),
175
+ ) -> dict[str, str]:
176
+ return {"editor": principal.email}
177
+
178
+
179
+ app.include_router(catalog)
180
+ ```
181
+
182
+ `auth.install()` exposes:
183
+
184
+ ```text
185
+ POST /api/v1/auth/login
186
+ POST /api/v1/auth/refresh
187
+ POST /api/v1/auth/logout
188
+ POST /api/v1/auth/change-password
189
+ GET /api/v1/auth/me
190
+ ```
191
+
192
+ With `include_admin=True` it also exposes protected user administration under `/api/v1/auth/users`.
193
+
194
+ ## Application boundary
195
+
196
+ `epok-auth` owns authentication capabilities:
197
+
198
+ - credentials and account state;
199
+ - sessions, rotation and revocation;
200
+ - generic roles/scopes;
201
+ - browser transport protections;
202
+ - authentication audit events.
203
+
204
+ The consuming product still owns:
205
+
206
+ - tenants and memberships;
207
+ - domain permissions;
208
+ - resource-level authorization;
209
+ - business profiles and data;
210
+ - frontend UI and infrastructure.
211
+
212
+ A role named `editor` has no meaning until Colors decides what an editor can do.
213
+
214
+ ## Nuxt BFF
215
+
216
+ The BFF is **not implemented inside the Python library**. The repository includes a reference integration under [`examples/nuxt-bff`](examples/nuxt-bff) that demonstrates this boundary:
217
+
218
+ ```text
219
+ Browser ── HttpOnly opaque session cookie ──> Nuxt/Nitro
220
+ Nuxt/Nitro ── protected access/refresh ──> FastAPI + epok-auth
221
+ ```
222
+
223
+ Vue receives only safe user/session state. Access and refresh credentials remain server-side.
224
+
225
+ ## Documentation
226
+
227
+ - [Minimal usage and test guide in Spanish](docs/USAGE_ES.md)
228
+ - [Publishing and versioning](docs/PUBLISHING.md)
229
+ - [Threat model](docs/THREAT_MODEL.md)
230
+ - [Security assurance](docs/SECURITY_ASSURANCE.md)
231
+ - [Security policy](SECURITY.md)
232
+
233
+ ## Security model
234
+
235
+ The beta is designed around these invariants:
236
+
237
+ - knowledge of the source code does not grant access;
238
+ - PostgreSQL is the authority for session validity;
239
+ - refresh credentials are one-time, opaque and hashed at rest;
240
+ - replay revokes the whole session family;
241
+ - changing a password, disabling or locking a user revokes sessions;
242
+ - unsafe production configuration fails before serving traffic;
243
+ - authentication errors do not echo secrets or distinguish unknown users.
244
+
245
+ ## Development
246
+
247
+ ```bash
248
+ uv sync --locked --all-extras --group dev
249
+ uv run ruff format --check .
250
+ uv run ruff check .
251
+ uv run pyright
252
+ uv run pytest
253
+ ```
254
+
255
+ Pull requests must pass the GitHub Actions `CI / merge-gate`, including PostgreSQL 17, Python 3.12–3.14, branch coverage, dependency auditing, packaging and isolated installation. `CodeQL` must also pass.