django-clerk-users 0.0.2__tar.gz → 0.1.1__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.
- django_clerk_users-0.1.1/.flake8 +17 -0
- django_clerk_users-0.1.1/.github/workflows/main.yaml +114 -0
- django_clerk_users-0.1.1/.gitignore +185 -0
- django_clerk_users-0.1.1/.pre-commit-config.yaml +70 -0
- django_clerk_users-0.1.1/.python-version +1 -0
- django_clerk_users-0.1.1/CLAUDE.md +116 -0
- django_clerk_users-0.1.1/HYBRID_AUTH.md +134 -0
- {django_clerk_users-0.0.2/src/django_clerk_users.egg-info → django_clerk_users-0.1.1}/PKG-INFO +92 -9
- django_clerk_users-0.0.2/PKG-INFO → django_clerk_users-0.1.1/README.md +87 -28
- django_clerk_users-0.1.1/examples/django_react_example/README.md +164 -0
- django_clerk_users-0.1.1/examples/django_react_example/TESTING.md +271 -0
- django_clerk_users-0.1.1/examples/django_react_example/backend/.env.example +3 -0
- django_clerk_users-0.1.1/examples/django_react_example/backend/.gitignore +4 -0
- django_clerk_users-0.1.1/examples/django_react_example/backend/api/admin.py +3 -0
- django_clerk_users-0.1.1/examples/django_react_example/backend/api/apps.py +6 -0
- django_clerk_users-0.1.1/examples/django_react_example/backend/api/models.py +3 -0
- django_clerk_users-0.1.1/examples/django_react_example/backend/api/tests.py +278 -0
- django_clerk_users-0.1.1/examples/django_react_example/backend/api/urls.py +13 -0
- django_clerk_users-0.1.1/examples/django_react_example/backend/api/views.py +68 -0
- django_clerk_users-0.1.1/examples/django_react_example/backend/backend/asgi.py +16 -0
- django_clerk_users-0.1.1/examples/django_react_example/backend/backend/settings.py +171 -0
- django_clerk_users-0.1.1/examples/django_react_example/backend/backend/urls.py +14 -0
- django_clerk_users-0.1.1/examples/django_react_example/backend/backend/wsgi.py +16 -0
- django_clerk_users-0.1.1/examples/django_react_example/backend/manage.py +22 -0
- django_clerk_users-0.1.1/examples/django_react_example/e2e/fixtures/test-users.ts +96 -0
- django_clerk_users-0.1.1/examples/django_react_example/e2e/global-setup.ts +28 -0
- django_clerk_users-0.1.1/examples/django_react_example/e2e/package.json +20 -0
- django_clerk_users-0.1.1/examples/django_react_example/e2e/playwright.config.ts +65 -0
- django_clerk_users-0.1.1/examples/django_react_example/e2e/scripts/setup-test-users.ts +113 -0
- django_clerk_users-0.1.1/examples/django_react_example/e2e/tests/auth.spec.ts +87 -0
- django_clerk_users-0.1.1/examples/django_react_example/e2e/tests/protected-api.spec.ts +82 -0
- django_clerk_users-0.1.1/examples/django_react_example/e2e/tsconfig.json +21 -0
- django_clerk_users-0.1.1/examples/django_react_example/frontend/.env.example +2 -0
- django_clerk_users-0.1.1/examples/django_react_example/frontend/.gitignore +25 -0
- django_clerk_users-0.1.1/examples/django_react_example/frontend/.nvmrc +1 -0
- django_clerk_users-0.1.1/examples/django_react_example/frontend/README.md +16 -0
- django_clerk_users-0.1.1/examples/django_react_example/frontend/eslint.config.js +29 -0
- django_clerk_users-0.1.1/examples/django_react_example/frontend/index.html +13 -0
- django_clerk_users-0.1.1/examples/django_react_example/frontend/package-lock.json +2813 -0
- django_clerk_users-0.1.1/examples/django_react_example/frontend/package.json +28 -0
- django_clerk_users-0.1.1/examples/django_react_example/frontend/public/vite.svg +1 -0
- django_clerk_users-0.1.1/examples/django_react_example/frontend/src/App.css +100 -0
- django_clerk_users-0.1.1/examples/django_react_example/frontend/src/App.jsx +128 -0
- django_clerk_users-0.1.1/examples/django_react_example/frontend/src/assets/react.svg +1 -0
- django_clerk_users-0.1.1/examples/django_react_example/frontend/src/index.css +68 -0
- django_clerk_users-0.1.1/examples/django_react_example/frontend/src/main.jsx +20 -0
- django_clerk_users-0.1.1/examples/django_react_example/frontend/vite.config.js +7 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/pyproject.toml +5 -1
- django_clerk_users-0.1.1/rav.yaml +50 -0
- django_clerk_users-0.1.1/src/django_clerk_users/authentication/backends.py +83 -0
- django_clerk_users-0.1.1/src/django_clerk_users/management/__init__.py +0 -0
- django_clerk_users-0.1.1/src/django_clerk_users/management/commands/__init__.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/src/django_clerk_users/managers.py +14 -15
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/src/django_clerk_users/middleware/auth.py +36 -7
- django_clerk_users-0.1.1/src/django_clerk_users/migrations/0002_make_clerk_id_nullable.py +24 -0
- django_clerk_users-0.1.1/src/django_clerk_users/migrations/__init__.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/src/django_clerk_users/models.py +8 -2
- django_clerk_users-0.1.1/src/django_clerk_users/organizations/migrations/0001_initial.py +349 -0
- django_clerk_users-0.1.1/src/django_clerk_users/organizations/migrations/__init__.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/src/django_clerk_users/organizations/models.py +1 -3
- django_clerk_users-0.1.1/tests/__init__.py +1 -0
- django_clerk_users-0.1.1/tests/settings.py +42 -0
- django_clerk_users-0.1.1/tests/test_hybrid_auth.py +152 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/tests/test_models.py +9 -6
- django_clerk_users-0.1.1/uv.lock +1027 -0
- django_clerk_users-0.0.2/README.md +0 -203
- django_clerk_users-0.0.2/setup.cfg +0 -4
- django_clerk_users-0.0.2/src/django_clerk_users/authentication/backends.py +0 -89
- django_clerk_users-0.0.2/src/django_clerk_users.egg-info/SOURCES.txt +0 -55
- django_clerk_users-0.0.2/src/django_clerk_users.egg-info/dependency_links.txt +0 -1
- django_clerk_users-0.0.2/src/django_clerk_users.egg-info/requires.txt +0 -6
- django_clerk_users-0.0.2/src/django_clerk_users.egg-info/top_level.txt +0 -1
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/LICENSE +0 -0
- {django_clerk_users-0.0.2/src/django_clerk_users/management → django_clerk_users-0.1.1/examples/django_react_example/backend/api}/__init__.py +0 -0
- {django_clerk_users-0.0.2/src/django_clerk_users/management/commands → django_clerk_users-0.1.1/examples/django_react_example/backend/api/migrations}/__init__.py +0 -0
- {django_clerk_users-0.0.2/src/django_clerk_users/migrations → django_clerk_users-0.1.1/examples/django_react_example/backend/backend}/__init__.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/src/django_clerk_users/__init__.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/src/django_clerk_users/apps.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/src/django_clerk_users/authentication/__init__.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/src/django_clerk_users/authentication/drf.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/src/django_clerk_users/authentication/utils.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/src/django_clerk_users/caching.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/src/django_clerk_users/checks.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/src/django_clerk_users/client.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/src/django_clerk_users/decorators.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/src/django_clerk_users/exceptions.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/src/django_clerk_users/management/commands/migrate_users_to_clerk.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/src/django_clerk_users/management/commands/sync_clerk_organizations.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/src/django_clerk_users/management/commands/sync_clerk_users.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/src/django_clerk_users/middleware/__init__.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/src/django_clerk_users/migrations/0001_initial.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/src/django_clerk_users/organizations/__init__.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/src/django_clerk_users/organizations/admin.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/src/django_clerk_users/organizations/apps.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/src/django_clerk_users/organizations/middleware.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/src/django_clerk_users/organizations/webhooks.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/src/django_clerk_users/settings.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/src/django_clerk_users/testing.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/src/django_clerk_users/utils.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/src/django_clerk_users/webhooks/__init__.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/src/django_clerk_users/webhooks/handlers.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/src/django_clerk_users/webhooks/security.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/src/django_clerk_users/webhooks/signals.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/src/django_clerk_users/webhooks/views.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/tests/test_authentication.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/tests/test_caching.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/tests/test_decorators.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/tests/test_drf.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/tests/test_import.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/tests/test_middleware.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/tests/test_organizations.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/tests/test_testing.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/tests/test_utils.py +0 -0
- {django_clerk_users-0.0.2 → django_clerk_users-0.1.1}/tests/test_webhooks.py +0 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
[flake8]
|
|
2
|
+
max-line-length = 88
|
|
3
|
+
extend-ignore = E203, W503, LOG011, LOG005
|
|
4
|
+
exclude =
|
|
5
|
+
.git,
|
|
6
|
+
__pycache__,
|
|
7
|
+
example_project,
|
|
8
|
+
.venv,
|
|
9
|
+
.tox,
|
|
10
|
+
build,
|
|
11
|
+
dist,
|
|
12
|
+
*.egg-info
|
|
13
|
+
per-file-ignores =
|
|
14
|
+
# Imported but unused in __init__ files
|
|
15
|
+
__init__.py:F401
|
|
16
|
+
# Docstring examples can be long
|
|
17
|
+
*/views.py:E501
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
# workflow_dispatch:
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
tags:
|
|
9
|
+
- '**'
|
|
10
|
+
pull_request:
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: ${{ github.head_ref || github.run_id }}
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
tests:
|
|
18
|
+
name: Python ${{ matrix.python-version }}
|
|
19
|
+
runs-on: ubuntu-24.04
|
|
20
|
+
|
|
21
|
+
strategy:
|
|
22
|
+
matrix:
|
|
23
|
+
python-version:
|
|
24
|
+
- '3.12'
|
|
25
|
+
- '3.13'
|
|
26
|
+
- '3.14'
|
|
27
|
+
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/checkout@v4
|
|
30
|
+
|
|
31
|
+
- uses: actions/setup-python@v5
|
|
32
|
+
with:
|
|
33
|
+
python-version: ${{ matrix.python-version }}
|
|
34
|
+
allow-prereleases: true
|
|
35
|
+
|
|
36
|
+
- name: Install uv
|
|
37
|
+
uses: astral-sh/setup-uv@v4
|
|
38
|
+
with:
|
|
39
|
+
enable-cache: true
|
|
40
|
+
cache-dependency-glob: uv.lock
|
|
41
|
+
|
|
42
|
+
- name: Install dependencies
|
|
43
|
+
run: uv pip install --system tox tox-uv
|
|
44
|
+
|
|
45
|
+
- name: Run tox targets for ${{ matrix.python-version }}
|
|
46
|
+
run: tox run -f py$(echo ${{ matrix.python-version }} | tr -d .)
|
|
47
|
+
|
|
48
|
+
- name: Upload coverage data
|
|
49
|
+
uses: actions/upload-artifact@v4
|
|
50
|
+
with:
|
|
51
|
+
name: coverage-data-${{ matrix.python-version }}
|
|
52
|
+
path: '${{ github.workspace }}/.coverage.*'
|
|
53
|
+
include-hidden-files: true
|
|
54
|
+
if-no-files-found: warn
|
|
55
|
+
|
|
56
|
+
coverage:
|
|
57
|
+
name: Coverage
|
|
58
|
+
runs-on: ubuntu-24.04
|
|
59
|
+
needs: tests
|
|
60
|
+
steps:
|
|
61
|
+
- uses: actions/checkout@v4
|
|
62
|
+
|
|
63
|
+
- uses: actions/setup-python@v5
|
|
64
|
+
with:
|
|
65
|
+
python-version: '3.12'
|
|
66
|
+
|
|
67
|
+
- name: Install uv
|
|
68
|
+
uses: astral-sh/setup-uv@v4
|
|
69
|
+
|
|
70
|
+
- name: Install dependencies
|
|
71
|
+
run: uv pip install --system coverage[toml]
|
|
72
|
+
|
|
73
|
+
- name: Download data
|
|
74
|
+
uses: actions/download-artifact@v4
|
|
75
|
+
with:
|
|
76
|
+
path: ${{ github.workspace }}
|
|
77
|
+
pattern: coverage-data-*
|
|
78
|
+
merge-multiple: true
|
|
79
|
+
|
|
80
|
+
- name: Combine coverage and fail if it's <100%
|
|
81
|
+
run: |
|
|
82
|
+
python -m coverage combine
|
|
83
|
+
python -m coverage html --skip-covered --skip-empty
|
|
84
|
+
python -m coverage report --fail-under=100
|
|
85
|
+
echo "## Coverage summary" >> $GITHUB_STEP_SUMMARY
|
|
86
|
+
python -m coverage report --format=markdown >> $GITHUB_STEP_SUMMARY
|
|
87
|
+
continue-on-error: true
|
|
88
|
+
|
|
89
|
+
- name: Upload HTML report
|
|
90
|
+
if: ${{ failure() }}
|
|
91
|
+
uses: actions/upload-artifact@v4
|
|
92
|
+
with:
|
|
93
|
+
name: html-report
|
|
94
|
+
path: htmlcov
|
|
95
|
+
|
|
96
|
+
release:
|
|
97
|
+
needs: [coverage]
|
|
98
|
+
if: success() && startsWith(github.ref, 'refs/tags/')
|
|
99
|
+
runs-on: ubuntu-24.04
|
|
100
|
+
environment: release
|
|
101
|
+
|
|
102
|
+
permissions:
|
|
103
|
+
contents: read
|
|
104
|
+
id-token: write
|
|
105
|
+
|
|
106
|
+
steps:
|
|
107
|
+
- uses: actions/checkout@v4
|
|
108
|
+
|
|
109
|
+
- uses: astral-sh/setup-uv@v4
|
|
110
|
+
|
|
111
|
+
- name: Build
|
|
112
|
+
run: uv build
|
|
113
|
+
|
|
114
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
example/
|
|
2
|
+
|
|
3
|
+
.DS_Store
|
|
4
|
+
|
|
5
|
+
# Byte-compiled / optimized / DLL files
|
|
6
|
+
__pycache__/
|
|
7
|
+
*.py[cod]
|
|
8
|
+
*$py.class
|
|
9
|
+
|
|
10
|
+
# C extensions
|
|
11
|
+
*.so
|
|
12
|
+
|
|
13
|
+
# Distribution / packaging
|
|
14
|
+
.Python
|
|
15
|
+
build/
|
|
16
|
+
develop-eggs/
|
|
17
|
+
dist/
|
|
18
|
+
downloads/
|
|
19
|
+
eggs/
|
|
20
|
+
.eggs/
|
|
21
|
+
lib/
|
|
22
|
+
lib64/
|
|
23
|
+
parts/
|
|
24
|
+
sdist/
|
|
25
|
+
var/
|
|
26
|
+
wheels/
|
|
27
|
+
share/python-wheels/
|
|
28
|
+
*.egg-info/
|
|
29
|
+
.installed.cfg
|
|
30
|
+
*.egg
|
|
31
|
+
MANIFEST
|
|
32
|
+
|
|
33
|
+
# PyInstaller
|
|
34
|
+
# Usually these files are written by a python script from a template
|
|
35
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
36
|
+
*.manifest
|
|
37
|
+
*.spec
|
|
38
|
+
|
|
39
|
+
# Installer logs
|
|
40
|
+
pip-log.txt
|
|
41
|
+
pip-delete-this-directory.txt
|
|
42
|
+
|
|
43
|
+
# Unit test / coverage reports
|
|
44
|
+
htmlcov/
|
|
45
|
+
.tox/
|
|
46
|
+
.nox/
|
|
47
|
+
.coverage
|
|
48
|
+
.coverage.*
|
|
49
|
+
.cache
|
|
50
|
+
nosetests.xml
|
|
51
|
+
coverage.xml
|
|
52
|
+
*.cover
|
|
53
|
+
*.py,cover
|
|
54
|
+
.hypothesis/
|
|
55
|
+
.pytest_cache/
|
|
56
|
+
cover/
|
|
57
|
+
|
|
58
|
+
# Translations
|
|
59
|
+
*.mo
|
|
60
|
+
*.pot
|
|
61
|
+
|
|
62
|
+
# Django stuff:
|
|
63
|
+
*.log
|
|
64
|
+
local_settings.py
|
|
65
|
+
db.sqlite3
|
|
66
|
+
db.sqlite3-journal
|
|
67
|
+
|
|
68
|
+
# Flask stuff:
|
|
69
|
+
instance/
|
|
70
|
+
.webassets-cache
|
|
71
|
+
|
|
72
|
+
# Scrapy stuff:
|
|
73
|
+
.scrapy
|
|
74
|
+
|
|
75
|
+
# Sphinx documentation
|
|
76
|
+
docs/_build/
|
|
77
|
+
|
|
78
|
+
# PyBuilder
|
|
79
|
+
.pybuilder/
|
|
80
|
+
target/
|
|
81
|
+
|
|
82
|
+
# Jupyter Notebook
|
|
83
|
+
.ipynb_checkpoints
|
|
84
|
+
|
|
85
|
+
# IPython
|
|
86
|
+
profile_default/
|
|
87
|
+
ipython_config.py
|
|
88
|
+
|
|
89
|
+
# pyenv
|
|
90
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
91
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
92
|
+
# .python-version
|
|
93
|
+
|
|
94
|
+
# pipenv
|
|
95
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
96
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
97
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
98
|
+
# install all needed dependencies.
|
|
99
|
+
#Pipfile.lock
|
|
100
|
+
|
|
101
|
+
# UV
|
|
102
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
103
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
104
|
+
# commonly ignored for libraries.
|
|
105
|
+
#uv.lock
|
|
106
|
+
|
|
107
|
+
# poetry
|
|
108
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
109
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
110
|
+
# commonly ignored for libraries.
|
|
111
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
112
|
+
#poetry.lock
|
|
113
|
+
|
|
114
|
+
# pdm
|
|
115
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
116
|
+
#pdm.lock
|
|
117
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
118
|
+
# in version control.
|
|
119
|
+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
|
120
|
+
.pdm.toml
|
|
121
|
+
.pdm-python
|
|
122
|
+
.pdm-build/
|
|
123
|
+
|
|
124
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
125
|
+
__pypackages__/
|
|
126
|
+
|
|
127
|
+
# Celery stuff
|
|
128
|
+
celerybeat-schedule
|
|
129
|
+
celerybeat.pid
|
|
130
|
+
|
|
131
|
+
# SageMath parsed files
|
|
132
|
+
*.sage.py
|
|
133
|
+
|
|
134
|
+
# Environments
|
|
135
|
+
.env
|
|
136
|
+
.venv
|
|
137
|
+
env/
|
|
138
|
+
venv/
|
|
139
|
+
ENV/
|
|
140
|
+
env.bak/
|
|
141
|
+
venv.bak/
|
|
142
|
+
|
|
143
|
+
# Spyder project settings
|
|
144
|
+
.spyderproject
|
|
145
|
+
.spyproject
|
|
146
|
+
|
|
147
|
+
# Rope project settings
|
|
148
|
+
.ropeproject
|
|
149
|
+
|
|
150
|
+
# mkdocs documentation
|
|
151
|
+
/site
|
|
152
|
+
|
|
153
|
+
# mypy
|
|
154
|
+
.mypy_cache/
|
|
155
|
+
.dmypy.json
|
|
156
|
+
dmypy.json
|
|
157
|
+
|
|
158
|
+
# Pyre type checker
|
|
159
|
+
.pyre/
|
|
160
|
+
|
|
161
|
+
# pytype static type analyzer
|
|
162
|
+
.pytype/
|
|
163
|
+
|
|
164
|
+
# Cython debug symbols
|
|
165
|
+
cython_debug/
|
|
166
|
+
|
|
167
|
+
# PyCharm
|
|
168
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
169
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
170
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
171
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
172
|
+
#.idea/
|
|
173
|
+
|
|
174
|
+
# PyPI configuration file
|
|
175
|
+
.pypirc
|
|
176
|
+
|
|
177
|
+
# Playwright MCP cache
|
|
178
|
+
.playwright-mcp/
|
|
179
|
+
|
|
180
|
+
# Development notes
|
|
181
|
+
issues.md
|
|
182
|
+
|
|
183
|
+
# Test artifacts
|
|
184
|
+
tests/ui_*.py
|
|
185
|
+
tests/*.sqlite3
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
ci:
|
|
2
|
+
autoupdate_schedule: monthly
|
|
3
|
+
|
|
4
|
+
default_language_version:
|
|
5
|
+
python: python3.12
|
|
6
|
+
|
|
7
|
+
repos:
|
|
8
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
9
|
+
rev: v5.0.0
|
|
10
|
+
hooks:
|
|
11
|
+
- id: check-added-large-files
|
|
12
|
+
- id: check-case-conflict
|
|
13
|
+
- id: check-json
|
|
14
|
+
- id: check-merge-conflict
|
|
15
|
+
- id: check-symlinks
|
|
16
|
+
- id: check-toml
|
|
17
|
+
- id: end-of-file-fixer
|
|
18
|
+
- id: trailing-whitespace
|
|
19
|
+
- repo: https://github.com/tox-dev/pyproject-fmt
|
|
20
|
+
rev: v2.5.0
|
|
21
|
+
hooks:
|
|
22
|
+
- id: pyproject-fmt
|
|
23
|
+
- repo: https://github.com/tox-dev/tox-ini-fmt
|
|
24
|
+
rev: 1.4.1
|
|
25
|
+
hooks:
|
|
26
|
+
- id: tox-ini-fmt
|
|
27
|
+
- repo: https://github.com/rstcheck/rstcheck
|
|
28
|
+
rev: v6.2.4
|
|
29
|
+
hooks:
|
|
30
|
+
- id: rstcheck
|
|
31
|
+
additional_dependencies:
|
|
32
|
+
- sphinx==6.1.3
|
|
33
|
+
- tomli==2.0.1
|
|
34
|
+
- repo: https://github.com/sphinx-contrib/sphinx-lint
|
|
35
|
+
rev: v1.0.0
|
|
36
|
+
hooks:
|
|
37
|
+
- id: sphinx-lint
|
|
38
|
+
- repo: https://github.com/asottile/pyupgrade
|
|
39
|
+
rev: v3.19.0
|
|
40
|
+
hooks:
|
|
41
|
+
- id: pyupgrade
|
|
42
|
+
args: [--py39-plus]
|
|
43
|
+
- repo: https://github.com/adamchainz/django-upgrade
|
|
44
|
+
rev: 1.22.2
|
|
45
|
+
hooks:
|
|
46
|
+
- id: django-upgrade
|
|
47
|
+
args: [--target-version, '4.2']
|
|
48
|
+
- repo: https://github.com/psf/black-pre-commit-mirror
|
|
49
|
+
rev: 24.10.0
|
|
50
|
+
hooks:
|
|
51
|
+
- id: black
|
|
52
|
+
- repo: https://github.com/adamchainz/blacken-docs
|
|
53
|
+
rev: 1.19.1
|
|
54
|
+
hooks:
|
|
55
|
+
- id: blacken-docs
|
|
56
|
+
additional_dependencies:
|
|
57
|
+
- black==23.1.0
|
|
58
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
59
|
+
# Ruff version.
|
|
60
|
+
rev: v0.14.1
|
|
61
|
+
hooks:
|
|
62
|
+
# Run the linter.
|
|
63
|
+
- id: ruff-check
|
|
64
|
+
args: [ --fix ]
|
|
65
|
+
# Run the formatter.
|
|
66
|
+
- id: ruff-format
|
|
67
|
+
- repo: https://github.com/adamchainz/djade-pre-commit
|
|
68
|
+
rev: 1.6.0
|
|
69
|
+
hooks:
|
|
70
|
+
- id: djade
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
django-clerk-users is a Django package for integrating Clerk authentication with Django. It provides:
|
|
8
|
+
|
|
9
|
+
- Custom user model (`ClerkUser`) with Clerk integration
|
|
10
|
+
- JWT token validation via Clerk SDK
|
|
11
|
+
- Session-based authentication middleware (validates once, caches in session)
|
|
12
|
+
- Webhook handling with Svix signature verification
|
|
13
|
+
- Optional organizations support (separate sub-app)
|
|
14
|
+
- Django REST Framework authentication (optional)
|
|
15
|
+
|
|
16
|
+
## Development Commands
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# Install dependencies
|
|
20
|
+
uv sync
|
|
21
|
+
|
|
22
|
+
# Run tests
|
|
23
|
+
uv run python -m pytest
|
|
24
|
+
|
|
25
|
+
# Run a single test
|
|
26
|
+
uv run python -m pytest tests/test_import.py::TestModels::test_clerk_user_model -v
|
|
27
|
+
|
|
28
|
+
# Run tests with coverage across Python versions (3.12, 3.13, 3.14)
|
|
29
|
+
uv run tox
|
|
30
|
+
|
|
31
|
+
# Run pre-commit hooks
|
|
32
|
+
uv run pre-commit run --all-files
|
|
33
|
+
|
|
34
|
+
# Install package in development mode
|
|
35
|
+
uv pip install -e .
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Code Architecture
|
|
39
|
+
|
|
40
|
+
### Package Structure
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
src/django_clerk_users/
|
|
44
|
+
├── models.py # AbstractClerkUser, ClerkUser, ClerkUserManager
|
|
45
|
+
├── settings.py # Package settings (CLERK_* settings)
|
|
46
|
+
├── client.py # Clerk SDK client singleton
|
|
47
|
+
├── authentication/ # Auth backends and token utilities
|
|
48
|
+
│ ├── backends.py # ClerkBackend (Django auth)
|
|
49
|
+
│ ├── drf.py # ClerkAuthentication (DRF, optional)
|
|
50
|
+
│ └── utils.py # Token validation, user creation
|
|
51
|
+
├── middleware/
|
|
52
|
+
│ └── auth.py # ClerkAuthMiddleware
|
|
53
|
+
├── webhooks/
|
|
54
|
+
│ ├── views.py # Webhook endpoint
|
|
55
|
+
│ ├── security.py # Svix verification
|
|
56
|
+
│ ├── handlers.py # Event handlers
|
|
57
|
+
│ └── signals.py # Django signals for extensibility
|
|
58
|
+
├── organizations/ # Optional sub-app
|
|
59
|
+
│ ├── models.py # Organization, OrganizationMember, OrganizationInvitation
|
|
60
|
+
│ ├── middleware.py # ClerkOrganizationMiddleware
|
|
61
|
+
│ └── webhooks.py # Organization event handlers
|
|
62
|
+
├── decorators.py # @clerk_user_required, @clerk_org_required
|
|
63
|
+
├── caching.py # User/org caching utilities
|
|
64
|
+
├── utils.py # update_or_create_clerk_user, etc.
|
|
65
|
+
└── management/commands/
|
|
66
|
+
├── sync_clerk_users.py
|
|
67
|
+
├── sync_clerk_organizations.py
|
|
68
|
+
└── migrate_users_to_clerk.py
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Key Patterns
|
|
72
|
+
|
|
73
|
+
1. **Session-based optimization**: JWT validated once, then cached in Django session. Re-validates every 5 minutes.
|
|
74
|
+
|
|
75
|
+
2. **Lazy imports**: `__init__.py` uses `__getattr__` to avoid loading models before Django apps are ready.
|
|
76
|
+
|
|
77
|
+
3. **Swappable user model**: Provides both `AbstractClerkUser` (for custom models) and `ClerkUser` (concrete).
|
|
78
|
+
|
|
79
|
+
4. **Signal-based webhooks**: Handlers emit signals (`clerk_user_created`, etc.) for extensibility.
|
|
80
|
+
|
|
81
|
+
5. **Optional DRF**: Install with `uv pip install django-clerk-users[drf]` for DRF authentication.
|
|
82
|
+
|
|
83
|
+
## Configuration
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
# Required settings
|
|
87
|
+
CLERK_SECRET_KEY = env("CLERK_SECRET_KEY")
|
|
88
|
+
CLERK_WEBHOOK_SIGNING_KEY = env("CLERK_WEBHOOK_SIGNING_KEY")
|
|
89
|
+
CLERK_FRONTEND_HOSTS = ["https://myapp.com"]
|
|
90
|
+
|
|
91
|
+
# Optional settings
|
|
92
|
+
CLERK_SESSION_REVALIDATION_SECONDS = 300 # 5 minutes
|
|
93
|
+
CLERK_CACHE_TIMEOUT = 300 # 5 minutes
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Testing
|
|
97
|
+
|
|
98
|
+
- Tests use pytest-django with settings in `tests/settings.py`
|
|
99
|
+
- Tests use mock Clerk credentials (no actual API calls)
|
|
100
|
+
- Run `uv run python -m pytest -v` for verbose output
|
|
101
|
+
|
|
102
|
+
## Releasing
|
|
103
|
+
|
|
104
|
+
Releases are automated via GitHub Actions (`.github/workflows/main.yaml`).
|
|
105
|
+
|
|
106
|
+
To release a new version:
|
|
107
|
+
|
|
108
|
+
1. Update version in `pyproject.toml`
|
|
109
|
+
2. Commit changes: `git commit -am "Release vX.Y.Z"`
|
|
110
|
+
3. Push to main: `git push origin main`
|
|
111
|
+
4. Create and push a tag: `git tag vX.Y.Z && git push origin vX.Y.Z`
|
|
112
|
+
|
|
113
|
+
The workflow will:
|
|
114
|
+
- Run tests on Python 3.12, 3.13, 3.14
|
|
115
|
+
- Build the package
|
|
116
|
+
- Publish to PyPI (using trusted publishing)
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Hybrid Authentication Feature
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This feature adds support for hybrid authentication, allowing django-clerk-users to work alongside Django's traditional admin authentication. This is particularly useful when your admin panel is on a different domain than your frontend.
|
|
6
|
+
|
|
7
|
+
## Changes Made
|
|
8
|
+
|
|
9
|
+
### 1. Middleware Updates (`src/django_clerk_users/middleware/auth.py`)
|
|
10
|
+
|
|
11
|
+
- Added `_is_clerk_session()` method to distinguish between Clerk sessions and Django admin sessions
|
|
12
|
+
- Updated `process_request()` to respect existing Django admin sessions without interference
|
|
13
|
+
- Clerk sessions are identified by the presence of `last_clerk_check` in the session
|
|
14
|
+
- Django admin sessions (created by `ModelBackend`) are preserved and not validated against Clerk
|
|
15
|
+
|
|
16
|
+
### 2. Model Updates (`src/django_clerk_users/models.py`)
|
|
17
|
+
|
|
18
|
+
- Made `clerk_id` field nullable (`null=True`, `blank=True`)
|
|
19
|
+
- Updated help text to clarify that `clerk_id` can be null for Django admin users
|
|
20
|
+
- Changed `REQUIRED_FIELDS` from `["clerk_id"]` to `[]` to support admin user creation
|
|
21
|
+
- Password field (inherited from `AbstractBaseUser`) is now functional for admin users
|
|
22
|
+
|
|
23
|
+
### 3. Migration (`src/django_clerk_users/migrations/0002_make_clerk_id_nullable.py`)
|
|
24
|
+
|
|
25
|
+
- New migration to alter `clerk_id` field to allow NULL values
|
|
26
|
+
- Maintains unique constraint while allowing NULL
|
|
27
|
+
|
|
28
|
+
### 4. Documentation Updates (`README.md`)
|
|
29
|
+
|
|
30
|
+
- Added "Hybrid Authentication" section explaining the feature
|
|
31
|
+
- Updated authentication backend configuration to show both options:
|
|
32
|
+
- Clerk-only: `[ClerkBackend]`
|
|
33
|
+
- Hybrid: `[ModelBackend, ClerkBackend]`
|
|
34
|
+
- Added instructions for creating admin users via `createsuperuser`
|
|
35
|
+
- Explained session handling differences
|
|
36
|
+
- Added use cases for hybrid authentication
|
|
37
|
+
|
|
38
|
+
### 5. Tests (`tests/test_hybrid_auth.py`)
|
|
39
|
+
|
|
40
|
+
- New test file for hybrid authentication scenarios
|
|
41
|
+
- Tests for Django admin session preservation
|
|
42
|
+
- Tests for Clerk session handling
|
|
43
|
+
- Tests for middleware behavior with both authentication types
|
|
44
|
+
|
|
45
|
+
## Configuration
|
|
46
|
+
|
|
47
|
+
### For Clerk-only authentication (existing behavior):
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
AUTHENTICATION_BACKENDS = [
|
|
51
|
+
"django_clerk_users.authentication.ClerkBackend",
|
|
52
|
+
]
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### For hybrid authentication (new feature):
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
AUTHENTICATION_BACKENDS = [
|
|
59
|
+
"django.contrib.auth.backends.ModelBackend", # For Django admin
|
|
60
|
+
"django_clerk_users.authentication.ClerkBackend", # For Clerk JWT
|
|
61
|
+
]
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## How It Works
|
|
65
|
+
|
|
66
|
+
1. **Clerk Users**: Authenticated via JWT tokens
|
|
67
|
+
- JWT validated on first request
|
|
68
|
+
- Session created with `last_clerk_check` marker
|
|
69
|
+
- Re-validated every 5 minutes (configurable)
|
|
70
|
+
- Has `clerk_id` field populated
|
|
71
|
+
|
|
72
|
+
2. **Admin Users**: Authenticated via username/password
|
|
73
|
+
- Traditional Django session (no `last_clerk_check` marker)
|
|
74
|
+
- Created via `createsuperuser` command
|
|
75
|
+
- No `clerk_id` (NULL)
|
|
76
|
+
- Can access Django admin panel
|
|
77
|
+
|
|
78
|
+
3. **Middleware Behavior**:
|
|
79
|
+
- Checks if user is already authenticated
|
|
80
|
+
- If `last_clerk_check` exists → Clerk session (validate/revalidate)
|
|
81
|
+
- If no `last_clerk_check` → Django admin session (preserve as-is)
|
|
82
|
+
- Never interferes with Django admin sessions
|
|
83
|
+
|
|
84
|
+
## Use Cases
|
|
85
|
+
|
|
86
|
+
1. **Cross-domain admin access**: Admin on different domain than frontend
|
|
87
|
+
2. **Internal staff access**: Staff use Django admin without Clerk accounts
|
|
88
|
+
3. **Gradual migration**: Migrate from Django auth to Clerk incrementally
|
|
89
|
+
4. **Mixed user types**: Frontend users via Clerk, internal users via Django
|
|
90
|
+
|
|
91
|
+
## Breaking Changes
|
|
92
|
+
|
|
93
|
+
- `clerk_id` is now nullable (requires migration)
|
|
94
|
+
- `REQUIRED_FIELDS` changed from `["clerk_id"]` to `[]`
|
|
95
|
+
|
|
96
|
+
Existing deployments will need to run the migration:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
python manage.py migrate django_clerk_users
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Testing
|
|
103
|
+
|
|
104
|
+
Run the new hybrid authentication tests:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
python -m pytest tests/test_hybrid_auth.py -v
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Example: Creating Admin User
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# Create superuser (no Clerk account needed)
|
|
114
|
+
python manage.py createsuperuser
|
|
115
|
+
|
|
116
|
+
# Login to Django admin
|
|
117
|
+
# Visit: http://your-domain.com/admin/
|
|
118
|
+
# Use the email/password you just created
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Example: User Table After Migration
|
|
122
|
+
|
|
123
|
+
| id | email | clerk_id | is_staff | is_superuser | Created via |
|
|
124
|
+
|----|-------|----------|----------|--------------|-------------|
|
|
125
|
+
| 1 | admin@example.com | NULL | True | True | createsuperuser |
|
|
126
|
+
| 2 | user@example.com | user_2abc123 | False | False | Clerk webhook |
|
|
127
|
+
| 3 | staff@example.com | user_2def456 | True | False | Clerk webhook + manual flag |
|
|
128
|
+
|
|
129
|
+
## Notes
|
|
130
|
+
|
|
131
|
+
- Django admin users (NULL clerk_id) cannot authenticate via Clerk JWT
|
|
132
|
+
- Clerk users (with clerk_id) can authenticate via either method if they have a password set
|
|
133
|
+
- The middleware automatically detects which auth method was used
|
|
134
|
+
- No changes needed to existing Clerk-only deployments (backward compatible)
|