fauth 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.
- fauth-0.1.0/.github/dependabot.yml +6 -0
- fauth-0.1.0/.github/workflows/cicd.yml +181 -0
- fauth-0.1.0/.github/workflows/pre-commit-autoupdate.yml +53 -0
- fauth-0.1.0/.gitignore +60 -0
- fauth-0.1.0/.pre-commit-config.yaml +58 -0
- fauth-0.1.0/CHANGELOG.md +74 -0
- fauth-0.1.0/LICENSE +21 -0
- fauth-0.1.0/PKG-INFO +218 -0
- fauth-0.1.0/README.md +188 -0
- fauth-0.1.0/fauth/__init__.py +44 -0
- fauth-0.1.0/fauth/api/__init__.py +3 -0
- fauth-0.1.0/fauth/api/router.py +24 -0
- fauth-0.1.0/fauth/core/__init__.py +12 -0
- fauth-0.1.0/fauth/core/config.py +11 -0
- fauth-0.1.0/fauth/core/exceptions.py +19 -0
- fauth-0.1.0/fauth/core/schemas.py +24 -0
- fauth-0.1.0/fauth/crypto/__init__.py +16 -0
- fauth-0.1.0/fauth/crypto/jwt.py +81 -0
- fauth-0.1.0/fauth/crypto/password.py +14 -0
- fauth-0.1.0/fauth/providers/__init__.py +4 -0
- fauth-0.1.0/fauth/providers/protocols.py +11 -0
- fauth-0.1.0/fauth/providers/provider.py +161 -0
- fauth-0.1.0/fauth/testing/__init__.py +9 -0
- fauth-0.1.0/fauth/testing/config.py +16 -0
- fauth-0.1.0/fauth/testing/fakes.py +18 -0
- fauth-0.1.0/fauth/testing/provider.py +22 -0
- fauth-0.1.0/fauth/transports/__init__.py +4 -0
- fauth-0.1.0/fauth/transports/base.py +19 -0
- fauth-0.1.0/fauth/transports/bearer.py +20 -0
- fauth-0.1.0/pyproject.toml +198 -0
- fauth-0.1.0/pytest.ini +2 -0
- fauth-0.1.0/requirements.txt +52 -0
- fauth-0.1.0/tests/__init__.py +0 -0
- fauth-0.1.0/tests/api/__init__.py +0 -0
- fauth-0.1.0/tests/api/conftest.py +85 -0
- fauth-0.1.0/tests/api/test_router.py +32 -0
- fauth-0.1.0/tests/conftest.py +13 -0
- fauth-0.1.0/tests/core/__init__.py +0 -0
- fauth-0.1.0/tests/core/conftest.py +18 -0
- fauth-0.1.0/tests/core/test_config.py +16 -0
- fauth-0.1.0/tests/core/test_exceptions.py +19 -0
- fauth-0.1.0/tests/crypto/__init__.py +0 -0
- fauth-0.1.0/tests/crypto/conftest.py +22 -0
- fauth-0.1.0/tests/crypto/test_jwt.py +76 -0
- fauth-0.1.0/tests/crypto/test_password.py +25 -0
- fauth-0.1.0/tests/providers/__init__.py +0 -0
- fauth-0.1.0/tests/providers/conftest.py +147 -0
- fauth-0.1.0/tests/providers/test_provider.py +87 -0
- fauth-0.1.0/tests/testing/__init__.py +0 -0
- fauth-0.1.0/tests/testing/test_testing.py +65 -0
- fauth-0.1.0/uv.lock +881 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ['main']
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: ['main']
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
id-token: write
|
|
11
|
+
contents: write
|
|
12
|
+
actions: write
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
lint:
|
|
16
|
+
name: Lint
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v6
|
|
20
|
+
env:
|
|
21
|
+
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
|
22
|
+
with:
|
|
23
|
+
token: ${{ env.GH_TOKEN }}
|
|
24
|
+
fetch-depth: 0
|
|
25
|
+
|
|
26
|
+
- name: Set up Python 3.13
|
|
27
|
+
uses: actions/setup-python@v6
|
|
28
|
+
with:
|
|
29
|
+
python-version: '3.13'
|
|
30
|
+
|
|
31
|
+
- name: Install uv
|
|
32
|
+
uses: astral-sh/setup-uv@v7
|
|
33
|
+
with:
|
|
34
|
+
version: '0.9.11'
|
|
35
|
+
python-version: '3.13'
|
|
36
|
+
|
|
37
|
+
- name: Install dependencies
|
|
38
|
+
run: |
|
|
39
|
+
uv sync --all-groups --frozen
|
|
40
|
+
|
|
41
|
+
- name: Run lint and format checks
|
|
42
|
+
id: format
|
|
43
|
+
run: |
|
|
44
|
+
uv run poe format
|
|
45
|
+
|
|
46
|
+
test:
|
|
47
|
+
name: Test
|
|
48
|
+
runs-on: ubuntu-latest
|
|
49
|
+
steps:
|
|
50
|
+
- uses: actions/checkout@v6
|
|
51
|
+
with:
|
|
52
|
+
token: ${{ secrets.GH_TOKEN }}
|
|
53
|
+
fetch-depth: 0
|
|
54
|
+
|
|
55
|
+
- name: Set up Python 3.13
|
|
56
|
+
uses: actions/setup-python@v6
|
|
57
|
+
with:
|
|
58
|
+
python-version: '3.13'
|
|
59
|
+
|
|
60
|
+
- name: Install uv
|
|
61
|
+
uses: astral-sh/setup-uv@v7
|
|
62
|
+
with:
|
|
63
|
+
version: '0.9.11'
|
|
64
|
+
python-version: '3.13'
|
|
65
|
+
|
|
66
|
+
- name: Install dependencies
|
|
67
|
+
run: |
|
|
68
|
+
uv sync --all-groups --frozen
|
|
69
|
+
|
|
70
|
+
- name: Run unit tests
|
|
71
|
+
env:
|
|
72
|
+
SECRET_KEY: ${{ secrets.SECRET_KEY }}
|
|
73
|
+
run: |
|
|
74
|
+
uv run poe test
|
|
75
|
+
|
|
76
|
+
requirements:
|
|
77
|
+
name: Requirements
|
|
78
|
+
if: github.ref != 'refs/heads/main'
|
|
79
|
+
runs-on: ubuntu-latest
|
|
80
|
+
steps:
|
|
81
|
+
- uses: actions/checkout@v6
|
|
82
|
+
with:
|
|
83
|
+
token: ${{ secrets.GH_TOKEN }}
|
|
84
|
+
fetch-depth: 0
|
|
85
|
+
ref: ${{ github.head_ref }}
|
|
86
|
+
|
|
87
|
+
- name: Set up Python 3.13
|
|
88
|
+
uses: actions/setup-python@v6
|
|
89
|
+
with:
|
|
90
|
+
python-version: '3.13'
|
|
91
|
+
|
|
92
|
+
- name: Install uv
|
|
93
|
+
uses: astral-sh/setup-uv@v7
|
|
94
|
+
with:
|
|
95
|
+
version: '0.9.11'
|
|
96
|
+
python-version: '3.13'
|
|
97
|
+
|
|
98
|
+
- name: Install dependencies
|
|
99
|
+
run: |
|
|
100
|
+
uv sync --all-groups --frozen
|
|
101
|
+
|
|
102
|
+
- name: Generate requirements.txt
|
|
103
|
+
run: |
|
|
104
|
+
uv pip compile pyproject.toml -o requirements.txt
|
|
105
|
+
|
|
106
|
+
- name: Commit and push requirements.txt
|
|
107
|
+
run: |
|
|
108
|
+
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
|
109
|
+
git config --local user.name "github-actions[bot]"
|
|
110
|
+
git add requirements.txt
|
|
111
|
+
git diff --staged --quiet || git commit -m "chore(config): update requirements.txt"
|
|
112
|
+
git push origin HEAD:${{ github.head_ref }}
|
|
113
|
+
|
|
114
|
+
versioning:
|
|
115
|
+
name: Versioning
|
|
116
|
+
runs-on: ubuntu-latest
|
|
117
|
+
if: github.ref == 'refs/heads/main'
|
|
118
|
+
needs: [lint, test]
|
|
119
|
+
steps:
|
|
120
|
+
- uses: actions/checkout@v6
|
|
121
|
+
env:
|
|
122
|
+
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
|
123
|
+
with:
|
|
124
|
+
token: ${{ env.GH_TOKEN }}
|
|
125
|
+
fetch-depth: 0
|
|
126
|
+
|
|
127
|
+
- name: Set up Python 3.13
|
|
128
|
+
uses: actions/setup-python@v6
|
|
129
|
+
with:
|
|
130
|
+
python-version: '3.13'
|
|
131
|
+
|
|
132
|
+
- name: Install uv
|
|
133
|
+
uses: astral-sh/setup-uv@v7
|
|
134
|
+
with:
|
|
135
|
+
version: '0.9.11'
|
|
136
|
+
python-version: '3.13'
|
|
137
|
+
|
|
138
|
+
- name: Install dependencies
|
|
139
|
+
run: |
|
|
140
|
+
uv sync --all-groups --frozen
|
|
141
|
+
|
|
142
|
+
- name: Bump version and update changelog
|
|
143
|
+
id: release
|
|
144
|
+
uses: python-semantic-release/python-semantic-release@v9.21.1
|
|
145
|
+
with:
|
|
146
|
+
github_token: ${{ secrets.GH_TOKEN }}
|
|
147
|
+
|
|
148
|
+
outputs:
|
|
149
|
+
released: ${{ steps.release.outputs.released }}
|
|
150
|
+
|
|
151
|
+
publish:
|
|
152
|
+
name: Publish to PyPI
|
|
153
|
+
runs-on: ubuntu-latest
|
|
154
|
+
if: needs.versioning.outputs.released == 'true'
|
|
155
|
+
needs: [versioning]
|
|
156
|
+
environment:
|
|
157
|
+
name: pypi
|
|
158
|
+
url: https://pypi.org/p/fauth
|
|
159
|
+
steps:
|
|
160
|
+
- uses: actions/checkout@v6
|
|
161
|
+
with:
|
|
162
|
+
token: ${{ secrets.GH_TOKEN }}
|
|
163
|
+
ref: main
|
|
164
|
+
fetch-depth: 0
|
|
165
|
+
|
|
166
|
+
- name: Set up Python 3.13
|
|
167
|
+
uses: actions/setup-python@v6
|
|
168
|
+
with:
|
|
169
|
+
python-version: '3.13'
|
|
170
|
+
|
|
171
|
+
- name: Install uv
|
|
172
|
+
uses: astral-sh/setup-uv@v7
|
|
173
|
+
with:
|
|
174
|
+
version: '0.9.11'
|
|
175
|
+
python-version: '3.13'
|
|
176
|
+
|
|
177
|
+
- name: Build package
|
|
178
|
+
run: uv build
|
|
179
|
+
|
|
180
|
+
- name: Publish to PyPI
|
|
181
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# The workflow checks out the repository, installs pre-commit, and runs the autoupdate command.
|
|
2
|
+
# If there are any updates, it creates a pull request with the changes.
|
|
3
|
+
|
|
4
|
+
name: Pre-commit Update
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
schedule:
|
|
8
|
+
- cron: '0 2 * * *'
|
|
9
|
+
workflow_dispatch: # Allows manual triggering of the workflow
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
auto-update:
|
|
16
|
+
permissions:
|
|
17
|
+
contents: write
|
|
18
|
+
pull-requests: write
|
|
19
|
+
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v6
|
|
23
|
+
- uses: actions/setup-python@v6
|
|
24
|
+
with:
|
|
25
|
+
python-version: '3.13'
|
|
26
|
+
|
|
27
|
+
- name: Install uv
|
|
28
|
+
uses: astral-sh/setup-uv@v7
|
|
29
|
+
with:
|
|
30
|
+
version: '0.9.11'
|
|
31
|
+
python-version: '3.13'
|
|
32
|
+
|
|
33
|
+
- name: Install dependencies
|
|
34
|
+
run: |
|
|
35
|
+
uv sync --all-groups --frozen
|
|
36
|
+
|
|
37
|
+
- name: Autoupdate pre-commit hooks
|
|
38
|
+
working-directory: '.'
|
|
39
|
+
run: uv run pre-commit autoupdate
|
|
40
|
+
|
|
41
|
+
- name: Create Pull Request
|
|
42
|
+
uses: peter-evans/create-pull-request@v8
|
|
43
|
+
env:
|
|
44
|
+
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
|
45
|
+
with:
|
|
46
|
+
token: ${{ env.GH_TOKEN }}
|
|
47
|
+
branch: chore/pre-commit-autoupdate
|
|
48
|
+
title: Update pre-commit hooks
|
|
49
|
+
commit-message: 'chore(config): update pre-commit hooks'
|
|
50
|
+
body: |
|
|
51
|
+
This PR was created automatically by the pre-commit autoupdate workflow.
|
|
52
|
+
It updates the pre-commit hooks to their latest versions.
|
|
53
|
+
labels: update
|
fauth-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
*$py.class
|
|
4
|
+
|
|
5
|
+
*.so
|
|
6
|
+
|
|
7
|
+
.Python
|
|
8
|
+
build/
|
|
9
|
+
develop-eggs/
|
|
10
|
+
dist/
|
|
11
|
+
downloads/
|
|
12
|
+
eggs/
|
|
13
|
+
.eggs/
|
|
14
|
+
lib/
|
|
15
|
+
lib64/
|
|
16
|
+
parts/
|
|
17
|
+
sdist/
|
|
18
|
+
var/
|
|
19
|
+
wheels/
|
|
20
|
+
*.egg-info/
|
|
21
|
+
*.egg
|
|
22
|
+
MANIFEST
|
|
23
|
+
|
|
24
|
+
.venv/
|
|
25
|
+
venv/
|
|
26
|
+
ENV/
|
|
27
|
+
env/
|
|
28
|
+
|
|
29
|
+
pip-log.txt
|
|
30
|
+
pip-delete-this-directory.txt
|
|
31
|
+
|
|
32
|
+
htmlcov/
|
|
33
|
+
.tox/
|
|
34
|
+
.nox/
|
|
35
|
+
.coverage
|
|
36
|
+
.coverage.*
|
|
37
|
+
.cache
|
|
38
|
+
nosetests.xml
|
|
39
|
+
coverage.xml
|
|
40
|
+
*.cover
|
|
41
|
+
*.py,cover
|
|
42
|
+
.hypothesis/
|
|
43
|
+
.pytest_cache/
|
|
44
|
+
|
|
45
|
+
.mypy_cache/
|
|
46
|
+
dmypy.json
|
|
47
|
+
dmypy.txt
|
|
48
|
+
.ruff_cache/
|
|
49
|
+
|
|
50
|
+
.vscode/
|
|
51
|
+
.idea/
|
|
52
|
+
*.swp
|
|
53
|
+
*.swo
|
|
54
|
+
*~
|
|
55
|
+
|
|
56
|
+
.DS_Store
|
|
57
|
+
Thumbs.db
|
|
58
|
+
|
|
59
|
+
.env
|
|
60
|
+
.env.*
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/abravalheri/validate-pyproject
|
|
3
|
+
rev: v0.25
|
|
4
|
+
hooks:
|
|
5
|
+
- id: validate-pyproject
|
|
6
|
+
|
|
7
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
8
|
+
rev: v6.0.0
|
|
9
|
+
hooks:
|
|
10
|
+
- id: trailing-whitespace
|
|
11
|
+
- id: end-of-file-fixer
|
|
12
|
+
- id: detect-private-key
|
|
13
|
+
|
|
14
|
+
- repo: https://github.com/gitleaks/gitleaks
|
|
15
|
+
rev: v8.30.1
|
|
16
|
+
hooks:
|
|
17
|
+
- id: gitleaks
|
|
18
|
+
|
|
19
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
20
|
+
rev: 'v0.15.6'
|
|
21
|
+
hooks:
|
|
22
|
+
- id: ruff-check
|
|
23
|
+
name: ruff
|
|
24
|
+
args: ['--fix']
|
|
25
|
+
|
|
26
|
+
- repo: https://github.com/PyCQA/bandit
|
|
27
|
+
rev: 1.9.4
|
|
28
|
+
hooks:
|
|
29
|
+
- id: bandit
|
|
30
|
+
args: ['-r', '-lll']
|
|
31
|
+
|
|
32
|
+
- repo: https://github.com/pylint-dev/pylint
|
|
33
|
+
rev: v4.0.5
|
|
34
|
+
hooks:
|
|
35
|
+
- id: pylint
|
|
36
|
+
types: [python]
|
|
37
|
+
additional_dependencies:
|
|
38
|
+
- loguru==0.7.3
|
|
39
|
+
- pydantic==2.12.4
|
|
40
|
+
- pydantic-settings==2.12
|
|
41
|
+
- uvicorn==0.38.0
|
|
42
|
+
- pytest==9.0.2
|
|
43
|
+
- polyfactory==3.2.0
|
|
44
|
+
- fastapi==0.135.1
|
|
45
|
+
|
|
46
|
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
47
|
+
rev: v1.19.1
|
|
48
|
+
hooks:
|
|
49
|
+
- id: mypy
|
|
50
|
+
additional_dependencies:
|
|
51
|
+
- loguru==0.7.3
|
|
52
|
+
- pydantic==2.12.4
|
|
53
|
+
- pydantic-settings==2.12
|
|
54
|
+
- pytest==9.0.2
|
|
55
|
+
- polyfactory==3.2.0
|
|
56
|
+
- fastapi==0.135.1
|
|
57
|
+
- pyjwt==2.11.0
|
|
58
|
+
- pwdlib==0.3.0
|
fauth-0.1.0/CHANGELOG.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# CHANGELOG
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
## v0.1.0 (2026-03-29)
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
- Add tests for core and api modules
|
|
9
|
+
([`cb6ed8a`](https://github.com/justmatias/fauth/commit/cb6ed8aabca0e4de41054cfd1857e1f41973d55c))
|
|
10
|
+
|
|
11
|
+
- Add tests for create_access_token
|
|
12
|
+
([`eae2201`](https://github.com/justmatias/fauth/commit/eae22013a94a56e931fbd2ba21f1800b90a4fd29))
|
|
13
|
+
|
|
14
|
+
- Add tests for jwt utilities and password utilities.
|
|
15
|
+
([`23f6345`](https://github.com/justmatias/fauth/commit/23f6345412be42eb6377f3bd008e4c0700b29ccb))
|
|
16
|
+
|
|
17
|
+
- Add unit tests and fixtures for the authentication provider.
|
|
18
|
+
([`e81e447`](https://github.com/justmatias/fauth/commit/e81e447391307df431a419439b30c46ab6f3f0cd))
|
|
19
|
+
|
|
20
|
+
### Chores
|
|
21
|
+
|
|
22
|
+
- Add polyfactory as new development dependencies.
|
|
23
|
+
([`e36e895`](https://github.com/justmatias/fauth/commit/e36e895fff0843218efcf425c75bb6ed66e29ad6))
|
|
24
|
+
|
|
25
|
+
- Add pypi publishing job
|
|
26
|
+
([`e8f5ba0`](https://github.com/justmatias/fauth/commit/e8f5ba07326dd058ed7df1f3881a7831ba563392))
|
|
27
|
+
|
|
28
|
+
- Fix lint issues
|
|
29
|
+
([`8e18b2f`](https://github.com/justmatias/fauth/commit/8e18b2f4d093a05f897033c97241158dab727100))
|
|
30
|
+
|
|
31
|
+
- Improve type hints, update dependencies, and apply minor stylistic adjustments across several
|
|
32
|
+
modules
|
|
33
|
+
([`1d55d31`](https://github.com/justmatias/fauth/commit/1d55d31cb7bd143dc353a44628c6f3575e571420))
|
|
34
|
+
|
|
35
|
+
- Restructure main package
|
|
36
|
+
([`f0d7236`](https://github.com/justmatias/fauth/commit/f0d723698da27d74cb7db9c7ca3a0ef2b1e129a2))
|
|
37
|
+
|
|
38
|
+
- **config**: Merge with main
|
|
39
|
+
([`c66d1eb`](https://github.com/justmatias/fauth/commit/c66d1eb612a2caebe790d20ce4a7f162ee49b6a4))
|
|
40
|
+
|
|
41
|
+
- **config**: Update pre-commit hooks
|
|
42
|
+
([`77a8f95`](https://github.com/justmatias/fauth/commit/77a8f95d330ea0570a04ca8bd5bbccc72554e5e4))
|
|
43
|
+
|
|
44
|
+
- **config**: Update requirements.txt
|
|
45
|
+
([`13b718a`](https://github.com/justmatias/fauth/commit/13b718aae048d817191fe26a96b6fd964f59e82a))
|
|
46
|
+
|
|
47
|
+
- **config**: Update requirements.txt
|
|
48
|
+
([`ec11b90`](https://github.com/justmatias/fauth/commit/ec11b90eb30ab24a5b384c5b9662077fea46d903))
|
|
49
|
+
|
|
50
|
+
- **config**: Update requirements.txt
|
|
51
|
+
([`08fed86`](https://github.com/justmatias/fauth/commit/08fed86e03f8bd6e3ec902dfc1d13225406be4a6))
|
|
52
|
+
|
|
53
|
+
### Features
|
|
54
|
+
|
|
55
|
+
- Implement core authentication logic, transports, and testing utilities
|
|
56
|
+
([`7abbd34`](https://github.com/justmatias/fauth/commit/7abbd343405c93fc86de1c454b51c94a25b04774))
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
## v0.0.0 (2026-03-10)
|
|
60
|
+
|
|
61
|
+
### Chores
|
|
62
|
+
|
|
63
|
+
- Add initial project readme and detailed design plan
|
|
64
|
+
([`161b2e0`](https://github.com/justmatias/fauth/commit/161b2e0fa87b5b1d04d83496e50076362e9874f6))
|
|
65
|
+
|
|
66
|
+
- Introduce package and document new testing utilities including fakes and factories.
|
|
67
|
+
([`52f3299`](https://github.com/justmatias/fauth/commit/52f32998d9095d825d7a1059be535dd7121814e6))
|
|
68
|
+
|
|
69
|
+
- Update readme
|
|
70
|
+
([`1e50ee3`](https://github.com/justmatias/fauth/commit/1e50ee3d59747f19d9c54a85858c786d4a95cfbe))
|
|
71
|
+
|
|
72
|
+
- **config**: Initialize new fauth project with core structure, dependency management, CI/CD, and
|
|
73
|
+
code quality tooling
|
|
74
|
+
([`0fe70cf`](https://github.com/justmatias/fauth/commit/0fe70cf7e453e404db44b64828ce5dc5bc440f73))
|
fauth-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Matías Giménez
|
|
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.
|
fauth-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fauth
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Ergonomic, lightweight JWT authentication for FastAPI. Secure your routes instantly with plug-and-play dependency injection
|
|
5
|
+
Project-URL: Homepage, https://github.com/justmatias/fauth
|
|
6
|
+
Project-URL: Repository, https://github.com/justmatias/fauth
|
|
7
|
+
Project-URL: Issues, https://github.com/justmatias/fauth/issues
|
|
8
|
+
Author-email: Matias Gimenez <matiasgimenez.dev@gmail.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: authentication,fastapi,jwt,rbac,security
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Framework :: FastAPI
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
21
|
+
Classifier: Topic :: Security
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.12
|
|
24
|
+
Requires-Dist: fastapi>=0.100
|
|
25
|
+
Requires-Dist: pwdlib[argon2]>=0.2
|
|
26
|
+
Requires-Dist: pydantic-settings>=2.0
|
|
27
|
+
Requires-Dist: pydantic>=2.0
|
|
28
|
+
Requires-Dist: pyjwt[crypto]>=2.8
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# FAuth
|
|
32
|
+
|
|
33
|
+
An ergonomic, plug-and-play authentication library for FastAPI.
|
|
34
|
+
|
|
35
|
+
`fauth` eliminates boilerplate around JWT, password hashing, user fetching, and Role-Based Access Control (RBAC) by leveraging FastAPI's Dependency Injection (`Depends`), Pydantic models, and Python Protocols.
|
|
36
|
+
|
|
37
|
+
## Features
|
|
38
|
+
|
|
39
|
+
- **Protocol-Based User Fetching**: Complete inversion of control. You implement a simple `UserLoader` protocol to define how to fetch a user from a token payload.
|
|
40
|
+
- **Plug-and-Play Configuration**: Centralized settings via Pydantic (`AuthConfig`). Configure once, inject everywhere.
|
|
41
|
+
- **Pluggable Transports**: Extensible `Transport` protocol with a built-in `BearerTransport` for Authorization header tokens.
|
|
42
|
+
- **Built-in Password Hashing**: Uses modern Argon2 via `pwdlib`.
|
|
43
|
+
- **RBAC**: Flexible `require_roles` and `require_permissions` dependencies for endpoint authorization.
|
|
44
|
+
- **Secure Router**: `SecureAPIRouter` applies authentication as a router-level dependency, securing all its routes automatically.
|
|
45
|
+
- **Testing Utilities**: Ships fake implementations (`FakeUserLoader`) and a `build_fake_auth_provider()` factory so consumers can write unit tests with zero boilerplate.
|
|
46
|
+
- **Type Safety**: Fully annotated for MyPy and IDE integration.
|
|
47
|
+
|
|
48
|
+
## Quick Start
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pip install fauth
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
uv add fauth
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## How to use FAuth
|
|
59
|
+
|
|
60
|
+
To adopt FAuth, define an async user lookup function (implementing the `UserLoader` protocol), supply an `AuthConfig`, and instantiate the `AuthProvider`. You can then inject the provider directly into FastAPI endpoints.
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from fastapi import FastAPI, Depends
|
|
64
|
+
from pydantic import BaseModel
|
|
65
|
+
from fauth import AuthConfig, AuthProvider, TokenPayload, SecureAPIRouter
|
|
66
|
+
|
|
67
|
+
app = FastAPI()
|
|
68
|
+
|
|
69
|
+
# 1. Define your internal user model
|
|
70
|
+
class User(BaseModel):
|
|
71
|
+
id: str
|
|
72
|
+
username: str
|
|
73
|
+
is_active: bool = True
|
|
74
|
+
roles: list[str] = []
|
|
75
|
+
|
|
76
|
+
# Mock database
|
|
77
|
+
DB: dict[str, User] = {
|
|
78
|
+
"user-123": User(id="user-123", username="alice", roles=["admin"])
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# 2. Define the callback that retrieves a user from the decoded JWT
|
|
82
|
+
async def load_user(payload: TokenPayload) -> User | None:
|
|
83
|
+
return DB.get(payload.sub)
|
|
84
|
+
|
|
85
|
+
# 3. Instantiate the auth component (ideally wired in DI or at module-level)
|
|
86
|
+
config = AuthConfig(secret_key="my-super-secret-key", algorithm="HS256")
|
|
87
|
+
auth: AuthProvider[User] = AuthProvider(config=config, user_loader=load_user)
|
|
88
|
+
|
|
89
|
+
# --- Routes ---
|
|
90
|
+
|
|
91
|
+
@app.post("/login")
|
|
92
|
+
async def login():
|
|
93
|
+
# Example logic: password hashing checks omitted for brevity
|
|
94
|
+
# 4. Use `auth.login` to issue tokens
|
|
95
|
+
return await auth.login(sub="user-123")
|
|
96
|
+
|
|
97
|
+
@app.get("/me")
|
|
98
|
+
async def get_me(user: User = Depends(auth.require_user())):
|
|
99
|
+
# 5. `auth.require_user()` secures the endpoint automatically
|
|
100
|
+
return {"message": f"Hello {user.username}"}
|
|
101
|
+
|
|
102
|
+
@app.get("/admin")
|
|
103
|
+
async def get_admin_data(user: User = Depends(auth.require_roles("admin"))):
|
|
104
|
+
# 6. `auth.require_roles()` enforces RBAC implicitly
|
|
105
|
+
return {"secret_data": "Top secret admin info"}
|
|
106
|
+
|
|
107
|
+
# --- Securing Multiple Routes ---
|
|
108
|
+
|
|
109
|
+
# 7. Use `SecureAPIRouter` to protect an entire group of routes.
|
|
110
|
+
# Any route added to this router will require an active user automatically.
|
|
111
|
+
secure_router = SecureAPIRouter(auth_provider=auth, prefix="/internal", tags=["Protected"])
|
|
112
|
+
|
|
113
|
+
@secure_router.get("/dashboard")
|
|
114
|
+
async def get_dashboard():
|
|
115
|
+
# This endpoint is secured by FAuth without needing Depends in the signature!
|
|
116
|
+
return {"data": "Secure dashboard"}
|
|
117
|
+
|
|
118
|
+
app.include_router(secure_router)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Custom Token Payload
|
|
122
|
+
|
|
123
|
+
By default, FAuth decodes JWTs into its built-in `TokenPayload` schema. If you need custom claims in your tokens (e.g., `tenant_id`, `organization_id`), subclass `TokenPayload` and pass it to `AuthProvider`:
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
from fauth import AuthConfig, AuthProvider, TokenPayload
|
|
127
|
+
|
|
128
|
+
class MyTokenPayload(TokenPayload):
|
|
129
|
+
tenant_id: str
|
|
130
|
+
plan: str = "free"
|
|
131
|
+
|
|
132
|
+
auth = AuthProvider(
|
|
133
|
+
config=AuthConfig(secret_key="my-secret"),
|
|
134
|
+
user_loader=load_user,
|
|
135
|
+
token_payload_schema=MyTokenPayload, # JWTs will be decoded into MyTokenPayload
|
|
136
|
+
)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
When issuing tokens, use the `extra` parameter to encode the custom claims into the JWT:
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
await auth.login(sub="user-123", extra={"tenant_id": "acme", "plan": "pro"})
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
On the decoding side, your `user_loader` will receive a `MyTokenPayload` instance with fully typed access to `payload.tenant_id` and `payload.plan`.
|
|
146
|
+
|
|
147
|
+
## Testing your endpoints with FAuth Fakes
|
|
148
|
+
|
|
149
|
+
FAuth ships with a `fauth.testing` module specifically to simplify testing. No complex JWT mocks or real database dependencies needed — just use `build_fake_auth_provider` to wire up an in-memory auth provider.
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
import asyncio
|
|
153
|
+
import pytest
|
|
154
|
+
from fastapi.testclient import TestClient
|
|
155
|
+
from pydantic import BaseModel
|
|
156
|
+
|
|
157
|
+
from myapp.main import app, auth # Import your FastAPI app and FAuth instance
|
|
158
|
+
from fauth.testing import build_fake_auth_provider
|
|
159
|
+
|
|
160
|
+
class User(BaseModel):
|
|
161
|
+
id: str
|
|
162
|
+
username: str
|
|
163
|
+
is_active: bool = True
|
|
164
|
+
roles: list[str] = []
|
|
165
|
+
|
|
166
|
+
@pytest.fixture
|
|
167
|
+
def test_client() -> TestClient:
|
|
168
|
+
# 1. Provide a mock test user
|
|
169
|
+
mock_user = User(id="user-123", username="test_user", roles=["admin"])
|
|
170
|
+
|
|
171
|
+
# 2. Wire the fake provider with an in-memory user store
|
|
172
|
+
fake = build_fake_auth_provider(users={"user-123": mock_user})
|
|
173
|
+
|
|
174
|
+
# 3. Override the internal cached dependency functions.
|
|
175
|
+
# These are the actual callables that FastAPI resolves inside Depends().
|
|
176
|
+
app.dependency_overrides[auth._require_user] = fake._require_user
|
|
177
|
+
app.dependency_overrides[auth._require_active_user] = fake._require_active_user
|
|
178
|
+
|
|
179
|
+
yield TestClient(app)
|
|
180
|
+
|
|
181
|
+
# Clean up overrides
|
|
182
|
+
app.dependency_overrides.clear()
|
|
183
|
+
|
|
184
|
+
def test_secure_route(test_client):
|
|
185
|
+
response = test_client.get("/me")
|
|
186
|
+
assert response.status_code == 200
|
|
187
|
+
assert response.json() == {"message": "Hello test_user"}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Alternative: End-to-end testing with real JWT tokens
|
|
191
|
+
|
|
192
|
+
If you prefer issuing real tokens in tests, `build_fake_auth_provider` uses safe test defaults (a fixed secret key, short expiry) so you can generate tokens without any extra setup.
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
def test_secure_me_endpoint():
|
|
196
|
+
user = User(id="user-999", username="fake_alice", roles=[])
|
|
197
|
+
|
|
198
|
+
# FAuth supplies a pre-made test provider with safe defaults
|
|
199
|
+
test_auth = build_fake_auth_provider(users={"user-999": user})
|
|
200
|
+
|
|
201
|
+
# Generate a real JWT token via the test provider
|
|
202
|
+
token_response = asyncio.run(test_auth.login(sub="user-999"))
|
|
203
|
+
|
|
204
|
+
# Override the dependency so the app uses the test user store
|
|
205
|
+
app.dependency_overrides[auth._require_user] = test_auth._require_user
|
|
206
|
+
|
|
207
|
+
# Apply Bearer token
|
|
208
|
+
client = TestClient(app)
|
|
209
|
+
response = client.get(
|
|
210
|
+
"/me",
|
|
211
|
+
headers={"Authorization": f"Bearer {token_response.access_token}"}
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
assert response.status_code == 200
|
|
215
|
+
assert response.json() == {"message": "Hello fake_alice"}
|
|
216
|
+
|
|
217
|
+
app.dependency_overrides.clear()
|
|
218
|
+
```
|