epok-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 (61) hide show
  1. epok_auth-0.1.0/.env.secret.example +4 -0
  2. epok_auth-0.1.0/.github/workflows/ci.yml +247 -0
  3. epok_auth-0.1.0/.github/workflows/codeql.yml +23 -0
  4. epok_auth-0.1.0/.gitignore +43 -0
  5. epok_auth-0.1.0/CHANGELOG.md +38 -0
  6. epok_auth-0.1.0/CONTRIBUTING.md +23 -0
  7. epok_auth-0.1.0/LICENSE +21 -0
  8. epok_auth-0.1.0/PKG-INFO +260 -0
  9. epok_auth-0.1.0/README.md +215 -0
  10. epok_auth-0.1.0/ROADMAP.md +31 -0
  11. epok_auth-0.1.0/SECURITY.md +33 -0
  12. epok_auth-0.1.0/docs/PUBLISHING.md +244 -0
  13. epok_auth-0.1.0/docs/SECURITY_ASSURANCE.md +45 -0
  14. epok_auth-0.1.0/docs/THREAT_MODEL.md +63 -0
  15. epok_auth-0.1.0/docs/USAGE_ES.md +737 -0
  16. epok_auth-0.1.0/examples/minimal/main.py +21 -0
  17. epok_auth-0.1.0/examples/nuxt-bff/README.md +7 -0
  18. epok_auth-0.1.0/examples/nuxt-bff/server/api/auth/login.post.ts +13 -0
  19. epok_auth-0.1.0/examples/nuxt-bff/server/api/auth/session.get.ts +6 -0
  20. epok_auth-0.1.0/examples/nuxt-bff/server/utils/authSession.ts +65 -0
  21. epok_auth-0.1.0/pyproject.toml +129 -0
  22. epok_auth-0.1.0/scripts/publish.py +846 -0
  23. epok_auth-0.1.0/scripts/publish.sh +11 -0
  24. epok_auth-0.1.0/src/epok_auth/__init__.py +23 -0
  25. epok_auth-0.1.0/src/epok_auth/cli.py +140 -0
  26. epok_auth-0.1.0/src/epok_auth/config.py +226 -0
  27. epok_auth-0.1.0/src/epok_auth/errors.py +58 -0
  28. epok_auth-0.1.0/src/epok_auth/fastapi/__init__.py +3 -0
  29. epok_auth-0.1.0/src/epok_auth/fastapi/integration.py +463 -0
  30. epok_auth-0.1.0/src/epok_auth/fastapi/schemas.py +145 -0
  31. epok_auth-0.1.0/src/epok_auth/migrate.py +40 -0
  32. epok_auth-0.1.0/src/epok_auth/migrations/__init__.py +1 -0
  33. epok_auth-0.1.0/src/epok_auth/migrations/env.py +98 -0
  34. epok_auth-0.1.0/src/epok_auth/migrations/script.py.mako +26 -0
  35. epok_auth-0.1.0/src/epok_auth/migrations/versions/0001_initial.py +240 -0
  36. epok_auth-0.1.0/src/epok_auth/models.py +143 -0
  37. epok_auth-0.1.0/src/epok_auth/passwords.py +140 -0
  38. epok_auth-0.1.0/src/epok_auth/postgres/__init__.py +4 -0
  39. epok_auth-0.1.0/src/epok_auth/postgres/store.py +288 -0
  40. epok_auth-0.1.0/src/epok_auth/postgres/tables.py +131 -0
  41. epok_auth-0.1.0/src/epok_auth/py.typed +0 -0
  42. epok_auth-0.1.0/src/epok_auth/service.py +810 -0
  43. epok_auth-0.1.0/src/epok_auth/store.py +42 -0
  44. epok_auth-0.1.0/src/epok_auth/testing/__init__.py +3 -0
  45. epok_auth-0.1.0/src/epok_auth/testing/memory.py +145 -0
  46. epok_auth-0.1.0/src/epok_auth/tokens.py +154 -0
  47. epok_auth-0.1.0/tests/__init__.py +0 -0
  48. epok_auth-0.1.0/tests/conftest.py +65 -0
  49. epok_auth-0.1.0/tests/test_cli.py +165 -0
  50. epok_auth-0.1.0/tests/test_config.py +166 -0
  51. epok_auth-0.1.0/tests/test_fastapi.py +356 -0
  52. epok_auth-0.1.0/tests/test_migrate.py +48 -0
  53. epok_auth-0.1.0/tests/test_models_and_memory.py +122 -0
  54. epok_auth-0.1.0/tests/test_package_metadata.py +7 -0
  55. epok_auth-0.1.0/tests/test_passwords.py +81 -0
  56. epok_auth-0.1.0/tests/test_postgres_integration.py +213 -0
  57. epok_auth-0.1.0/tests/test_publish_script.py +46 -0
  58. epok_auth-0.1.0/tests/test_service_sessions.py +338 -0
  59. epok_auth-0.1.0/tests/test_service_users.py +237 -0
  60. epok_auth-0.1.0/tests/test_tokens.py +142 -0
  61. epok_auth-0.1.0/uv.lock +1416 -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,247 @@
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 and CLI
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
+ uv run scripts/publish.py --help
42
+ - name: Install all dependencies
43
+ run: uv sync --locked --all-extras --group dev --python 3.12
44
+ - name: Formatting
45
+ run: uv run ruff format --check .
46
+ - name: Lint and security rules
47
+ run: uv run ruff check .
48
+ - name: Strict typing
49
+ run: uv run pyright
50
+ - name: Compile all modules
51
+ run: uv run python -m compileall -q src tests examples/minimal scripts/publish.py
52
+ - name: Dependency vulnerability audit
53
+ run: uv run pip-audit --local --progress-spinner off
54
+
55
+ tests:
56
+ name: tests-py${{ matrix.python-version }}
57
+ runs-on: ubuntu-latest
58
+ timeout-minutes: 15
59
+ strategy:
60
+ fail-fast: false
61
+ matrix:
62
+ python-version: ["3.12", "3.13", "3.14"]
63
+ steps:
64
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
65
+ - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b
66
+ with:
67
+ version: "0.12.1"
68
+ enable-cache: true
69
+ - run: uv python install ${{ matrix.python-version }}
70
+ - run: uv sync --locked --all-extras --group dev --python ${{ matrix.python-version }}
71
+ - name: Unit, HTTP and adversarial suite
72
+ run: uv run --python ${{ matrix.python-version }} pytest -m "not integration" -q
73
+
74
+ postgres:
75
+ name: postgres-17
76
+ runs-on: ubuntu-latest
77
+ timeout-minutes: 20
78
+ env:
79
+ TEST_DATABASE_URL: postgresql+psycopg://epok_auth:epok_auth@127.0.0.1:5432/epok_auth_test
80
+ services:
81
+ postgres:
82
+ image: postgres:17-alpine
83
+ env:
84
+ POSTGRES_USER: epok_auth
85
+ POSTGRES_PASSWORD: epok_auth
86
+ POSTGRES_DB: epok_auth_test
87
+ ports: ["5432:5432"]
88
+ options: >-
89
+ --health-cmd "pg_isready -U epok_auth -d epok_auth_test"
90
+ --health-interval 2s
91
+ --health-timeout 3s
92
+ --health-retries 30
93
+ steps:
94
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
95
+ - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b
96
+ with:
97
+ version: "0.12.1"
98
+ enable-cache: true
99
+ - run: uv python install 3.12
100
+ - run: uv sync --locked --all-extras --group dev --python 3.12
101
+ - name: Migrate from an empty database
102
+ run: |
103
+ uv run python - <<'PY'
104
+ import os
105
+ from epok_auth.migrate import upgrade_database
106
+
107
+ upgrade_database(os.environ["TEST_DATABASE_URL"])
108
+ PY
109
+ - name: Assert metadata has no migration drift
110
+ run: |
111
+ uv run python - <<'PY'
112
+ import os
113
+ from epok_auth.migrate import check_database
114
+
115
+ check_database(os.environ["TEST_DATABASE_URL"])
116
+ PY
117
+ - name: PostgreSQL integration and concurrency suite
118
+ run: uv run pytest -m integration -q
119
+
120
+ coverage:
121
+ name: coverage
122
+ runs-on: ubuntu-latest
123
+ timeout-minutes: 20
124
+ env:
125
+ TEST_DATABASE_URL: postgresql+psycopg://epok_auth:epok_auth@127.0.0.1:5432/epok_auth_test
126
+ services:
127
+ postgres:
128
+ image: postgres:17-alpine
129
+ env:
130
+ POSTGRES_USER: epok_auth
131
+ POSTGRES_PASSWORD: epok_auth
132
+ POSTGRES_DB: epok_auth_test
133
+ ports: ["5432:5432"]
134
+ options: >-
135
+ --health-cmd "pg_isready -U epok_auth -d epok_auth_test"
136
+ --health-interval 2s
137
+ --health-timeout 3s
138
+ --health-retries 30
139
+ steps:
140
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
141
+ - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b
142
+ with:
143
+ version: "0.12.1"
144
+ enable-cache: true
145
+ - run: uv python install 3.12
146
+ - run: uv sync --locked --all-extras --group dev --python 3.12
147
+ - name: Migrate from an empty database
148
+ run: |
149
+ uv run python - <<'PY'
150
+ import os
151
+ from epok_auth.migrate import upgrade_database
152
+
153
+ upgrade_database(os.environ["TEST_DATABASE_URL"])
154
+ PY
155
+ - name: Enforce branch coverage across unit and PostgreSQL paths
156
+ run: >-
157
+ uv run pytest
158
+ --cov=epok_auth
159
+ --cov-branch
160
+ --cov-report=term-missing
161
+ --cov-report=xml
162
+ --cov-fail-under=90
163
+
164
+ package:
165
+ name: package
166
+ runs-on: ubuntu-latest
167
+ timeout-minutes: 18
168
+ steps:
169
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
170
+ - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b
171
+ with:
172
+ version: "0.12.1"
173
+ enable-cache: true
174
+ - run: uv python install 3.12
175
+ - name: Build wheel and source distribution without local sources
176
+ run: uv build --no-sources
177
+ - name: Verify packaged files
178
+ shell: bash
179
+ run: |
180
+ set -euo pipefail
181
+ wheel="$(find dist -maxdepth 1 -name '*.whl' -print -quit)"
182
+ sdist="$(find dist -maxdepth 1 -name '*.tar.gz' -print -quit)"
183
+ test -n "$wheel"
184
+ test -n "$sdist"
185
+ test "$(find dist -maxdepth 1 -name '*.whl' | wc -l)" -eq 1
186
+ test "$(find dist -maxdepth 1 -name '*.tar.gz' | wc -l)" -eq 1
187
+ python -m zipfile -l "$wheel" | grep 'epok_auth/migrations/versions/0001_initial.py'
188
+ python -m zipfile -l "$wheel" | grep 'epok_auth/py.typed'
189
+ tar -tzf "$sdist" | grep 'src/epok_auth/migrations/versions/0001_initial.py'
190
+ tar -tzf "$sdist" | grep 'src/epok_auth/py.typed'
191
+ - name: Isolated wheel and sdist smoke tests
192
+ shell: bash
193
+ run: |
194
+ set -euo pipefail
195
+ expected_version="$(uv version --short)"
196
+ wheel="$(find dist -maxdepth 1 -name '*.whl' -print -quit)"
197
+ sdist="$(find dist -maxdepth 1 -name '*.tar.gz' -print -quit)"
198
+
199
+ smoke_test() {
200
+ artifact="$1"
201
+ environment="$2"
202
+ uv venv "$environment" --python 3.12
203
+ uv pip install --python "$environment/bin/python" "$artifact"
204
+ EPOK_AUTH_EXPECTED_VERSION="$expected_version" \
205
+ "$environment/bin/python" - <<'PY'
206
+ import os
207
+ from importlib.metadata import version
208
+
209
+ import epok_auth
210
+ from epok_auth import AuthSettings, EpokAuth
211
+
212
+ expected = os.environ["EPOK_AUTH_EXPECTED_VERSION"]
213
+ assert version("epok-auth") == expected
214
+ assert epok_auth.__version__ == expected
215
+ assert AuthSettings is not None
216
+ assert EpokAuth is not None
217
+ PY
218
+ "$environment/bin/epok-auth" --help >/dev/null
219
+ "$environment/bin/epok-auth" generate-secret --bytes 32 | grep -E '.{40,}'
220
+ }
221
+
222
+ smoke_test "$wheel" /tmp/epok-auth-wheel-smoke
223
+ smoke_test "$sdist" /tmp/epok-auth-sdist-smoke
224
+
225
+ merge-gate:
226
+ name: merge-gate
227
+ if: always()
228
+ needs: [quality, tests, postgres, coverage, package]
229
+ runs-on: ubuntu-latest
230
+ timeout-minutes: 5
231
+ steps:
232
+ - name: Require every beta gate to pass
233
+ shell: bash
234
+ env:
235
+ QUALITY: ${{ needs.quality.result }}
236
+ TESTS: ${{ needs.tests.result }}
237
+ POSTGRES: ${{ needs.postgres.result }}
238
+ COVERAGE: ${{ needs.coverage.result }}
239
+ PACKAGE: ${{ needs.package.result }}
240
+ run: |
241
+ set -euo pipefail
242
+ printf '%-12s %s\n' quality "$QUALITY" tests "$TESTS" postgres "$POSTGRES" coverage "$COVERAGE" package "$PACKAGE"
243
+ test "$QUALITY" = success
244
+ test "$TESTS" = success
245
+ test "$POSTGRES" = success
246
+ test "$COVERAGE" = success
247
+ 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,38 @@
1
+ # Changelog
2
+
3
+ ## Unreleased
4
+
5
+ - replace the duplicated shell release implementation with a Rich/Typer Python orchestrator;
6
+ - run the release CLI through isolated PEP 723 dependencies;
7
+ - validate Python 3.12, 3.13 and 3.14 locally without replacing the project `.venv`;
8
+ - execute PostgreSQL 17 migrations, drift, integration, concurrency and coverage locally;
9
+ - build and smoke-test both wheel and source distribution;
10
+ - publish, create the annotated tag and verify the public PyPI installation in one command;
11
+ - add regression tests for `.env.secret` parsing and Docker port discovery;
12
+ - retain `scripts/publish.sh` as a compatibility alias.
13
+
14
+ ## 0.1.0b1 — 2026-08-04
15
+
16
+ First public beta and production-candidate foundation for the Colors integration:
17
+
18
+ - local user administration;
19
+ - Argon2id credentials with dummy verification and rehash support;
20
+ - strict short-lived access JWTs;
21
+ - opaque rotating refresh sessions;
22
+ - session families, reuse detection and immediate revocation;
23
+ - idle and absolute session deadlines;
24
+ - CSRF correlation and Origin protection;
25
+ - PostgreSQL-authoritative sessions and security events;
26
+ - packaged Alembic migrations with drift checking;
27
+ - FastAPI routers, dependencies and administrative endpoints;
28
+ - operational CLI and Nuxt BFF reference;
29
+ - reproducible `uv.lock` and isolated package installation;
30
+ - single-source versioning through `pyproject.toml`, `uv version` and `importlib.metadata`;
31
+ - guarded local publication through `uv build --no-sources` and `uv publish`;
32
+ - ignored local PyPI credentials with repository safeguards;
33
+ - CodeQL, dependency audit, strict typing and Ruff security rules;
34
+ - branch-aware coverage above 90%;
35
+ - Python 3.12, 3.13 and 3.14 validation;
36
+ - PostgreSQL 17 integration and concurrency validation.
37
+
38
+ The standalone beta is published on PyPI and tagged as `v0.1.0b1`. 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,260 @@
1
+ Metadata-Version: 2.4
2
+ Name: epok-auth
3
+ Version: 0.1.0
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: greenlet<4,>=3.0; extra == 'all'
37
+ Requires-Dist: psycopg[binary,pool]<4,>=3.2; extra == 'all'
38
+ Requires-Dist: sqlalchemy<2.1,>=2.0.45; extra == 'all'
39
+ Provides-Extra: postgres
40
+ Requires-Dist: alembic<2,>=1.17; extra == 'postgres'
41
+ Requires-Dist: greenlet<4,>=3.0; extra == 'postgres'
42
+ Requires-Dist: psycopg[binary,pool]<4,>=3.2; extra == 'postgres'
43
+ Requires-Dist: sqlalchemy<2.1,>=2.0.45; extra == 'postgres'
44
+ Description-Content-Type: text/markdown
45
+
46
+ # epok-auth
47
+
48
+ **FastAPI-first authentication with PostgreSQL-authoritative, revocable sessions.**
49
+
50
+ `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.
51
+
52
+ > **Status:** `0.1.0b1` public beta on PyPI. 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`.
53
+ >
54
+ > **Practical testing:** see the Spanish step-by-step guide in [`docs/USAGE_ES.md`](docs/USAGE_ES.md).
55
+
56
+ ## Validated beta gate
57
+
58
+ The clean beta tree is continuously validated by GitHub Actions. The current candidate has passed:
59
+
60
+ | Gate | Evidence |
61
+ |---|---|
62
+ | Functional and adversarial tests | 101/101 passing |
63
+ | Branch coverage | 94.80% |
64
+ | Python compatibility | 3.12, 3.13 and 3.14 |
65
+ | PostgreSQL | PostgreSQL 17 migration, zero Alembic drift, integration and concurrency tests |
66
+ | Static quality | Ruff formatting/lint/security rules and Pyright strict on production source |
67
+ | Dependencies | Reproducible `uv.lock` and `pip-audit` |
68
+ | Distribution | Wheel and sdist build, packaged migrations, `py.typed`, isolated install and CLI smoke test |
69
+ | Code scanning | CodeQL `security-extended` |
70
+
71
+ The repository does not claim that vulnerabilities are impossible. The green gate establishes reproducible evidence for the defined beta threat model and invariants.
72
+
73
+ ## What the beta includes
74
+
75
+ - Argon2id password hashing through `pwdlib`, with rehash support and dummy verification;
76
+ - local users, active/disabled state, roles, scopes, administrative provisioning and reset;
77
+ - short-lived access JWTs with strict issuer, audience, algorithm, type and time validation;
78
+ - opaque refresh credentials stored only as SHA-256 hashes;
79
+ - refresh rotation, session families and reuse detection;
80
+ - immediate access revocation through authoritative PostgreSQL session state;
81
+ - inactivity and absolute session deadlines;
82
+ - secure cookies, CSRF correlation and strict Origin allowlists;
83
+ - account lockout, uniform login failures and security-event persistence;
84
+ - plug-and-play FastAPI routers and dependencies;
85
+ - packaged Alembic migrations and an operational CLI;
86
+ - a Nuxt/Nitro BFF reference where Vue never receives access or refresh tokens.
87
+
88
+ Google OIDC, TOTP/MFA, passkeys, Redis coordination, multi-tenancy and service-to-service authentication remain outside this beta. See [ROADMAP.md](ROADMAP.md).
89
+
90
+ ## Installation
91
+
92
+ ```bash
93
+ uv add "epok-auth[postgres]==0.1.0b1"
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
+ `uv version --short` prints the current project version, not the installed version of `uv`.
125
+
126
+ Local PyPI credentials belong in an ignored `.env.secret` file. The complete release pipeline is a single Python command:
127
+
128
+ ```bash
129
+ cp .env.secret.example .env.secret
130
+ uv run scripts/publish.py --validate-only
131
+ uv run scripts/publish.py --dry-run
132
+ uv run scripts/publish.py
133
+ ```
134
+
135
+ The normal command validates Python 3.12–3.14, launches disposable PostgreSQL 17, runs migrations, drift checks, integration, concurrency and coverage, builds and installs wheel/sdist, simulates the upload, publishes after an exact-version confirmation, pushes the tag and verifies the public PyPI installation.
136
+
137
+ The script uses inline PEP 723 dependencies and `uv run --isolated`, so it does not replace the developer's project `.venv`. The legacy `bash scripts/publish.sh` command remains as a thin alias. See [docs/PUBLISHING.md](docs/PUBLISHING.md) for the complete procedure.
138
+
139
+ ## Database and initial administrator
140
+
141
+ ```bash
142
+ uv run epok-auth check-config
143
+ uv run epok-auth upgrade-db
144
+ uv run epok-auth check-db
145
+ uv run epok-auth create-admin
146
+ ```
147
+
148
+ The first administrator is serialized transactionally. A second initial-admin creation attempt fails rather than racing.
149
+
150
+ ## FastAPI integration
151
+
152
+ ```python
153
+ from fastapi import Depends, FastAPI
154
+
155
+ from epok_auth import AuthSettings, EpokAuth, Principal
156
+
157
+ settings = AuthSettings()
158
+ auth = EpokAuth.postgres(settings=settings)
159
+
160
+ app = FastAPI()
161
+ auth.install(
162
+ app,
163
+ prefix="/api/v1/auth",
164
+ include_admin=True,
165
+ )
166
+
167
+ catalog = auth.protected_router(prefix="/api/v1/catalog")
168
+
169
+
170
+ @catalog.get("")
171
+ async def get_catalog(
172
+ principal: Principal = Depends(auth.authenticated),
173
+ ) -> dict[str, str]:
174
+ return {"viewer": principal.email}
175
+
176
+
177
+ @catalog.post("")
178
+ async def update_catalog(
179
+ principal: Principal = Depends(auth.require_scopes("catalog:write")),
180
+ ) -> dict[str, str]:
181
+ return {"editor": principal.email}
182
+
183
+
184
+ app.include_router(catalog)
185
+ ```
186
+
187
+ `auth.install()` exposes:
188
+
189
+ ```text
190
+ POST /api/v1/auth/login
191
+ POST /api/v1/auth/refresh
192
+ POST /api/v1/auth/logout
193
+ POST /api/v1/auth/change-password
194
+ GET /api/v1/auth/me
195
+ ```
196
+
197
+ With `include_admin=True` it also exposes protected user administration under `/api/v1/auth/users`.
198
+
199
+ ## Application boundary
200
+
201
+ `epok-auth` owns authentication capabilities:
202
+
203
+ - credentials and account state;
204
+ - sessions, rotation and revocation;
205
+ - generic roles/scopes;
206
+ - browser transport protections;
207
+ - authentication audit events.
208
+
209
+ The consuming product still owns:
210
+
211
+ - tenants and memberships;
212
+ - domain permissions;
213
+ - resource-level authorization;
214
+ - business profiles and data;
215
+ - frontend UI and infrastructure.
216
+
217
+ A role named `editor` has no meaning until Colors decides what an editor can do.
218
+
219
+ ## Nuxt BFF
220
+
221
+ 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:
222
+
223
+ ```text
224
+ Browser ── HttpOnly opaque session cookie ──> Nuxt/Nitro
225
+ Nuxt/Nitro ── protected access/refresh ──> FastAPI + epok-auth
226
+ ```
227
+
228
+ Vue receives only safe user/session state. Access and refresh credentials remain server-side.
229
+
230
+ ## Documentation
231
+
232
+ - [Minimal usage and test guide in Spanish](docs/USAGE_ES.md)
233
+ - [Publishing and versioning](docs/PUBLISHING.md)
234
+ - [Threat model](docs/THREAT_MODEL.md)
235
+ - [Security assurance](docs/SECURITY_ASSURANCE.md)
236
+ - [Security policy](SECURITY.md)
237
+
238
+ ## Security model
239
+
240
+ The beta is designed around these invariants:
241
+
242
+ - knowledge of the source code does not grant access;
243
+ - PostgreSQL is the authority for session validity;
244
+ - refresh credentials are one-time, opaque and hashed at rest;
245
+ - replay revokes the whole session family;
246
+ - changing a password, disabling or locking a user revokes sessions;
247
+ - unsafe production configuration fails before serving traffic;
248
+ - authentication errors do not echo secrets or distinguish unknown users.
249
+
250
+ ## Development
251
+
252
+ ```bash
253
+ uv sync --locked --all-extras --group dev
254
+ uv run ruff format --check .
255
+ uv run ruff check .
256
+ uv run pyright
257
+ uv run pytest
258
+ ```
259
+
260
+ 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.