django-clerk-users 0.0.2__tar.gz → 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 (113) hide show
  1. django_clerk_users-0.1.0/.flake8 +17 -0
  2. django_clerk_users-0.1.0/.github/workflows/main.yaml +114 -0
  3. django_clerk_users-0.1.0/.gitignore +185 -0
  4. django_clerk_users-0.1.0/.pre-commit-config.yaml +70 -0
  5. django_clerk_users-0.1.0/.python-version +1 -0
  6. django_clerk_users-0.1.0/CLAUDE.md +116 -0
  7. django_clerk_users-0.1.0/HYBRID_AUTH.md +134 -0
  8. {django_clerk_users-0.0.2/src/django_clerk_users.egg-info → django_clerk_users-0.1.0}/PKG-INFO +92 -9
  9. django_clerk_users-0.0.2/PKG-INFO → django_clerk_users-0.1.0/README.md +87 -28
  10. django_clerk_users-0.1.0/examples/django_react_example/README.md +164 -0
  11. django_clerk_users-0.1.0/examples/django_react_example/TESTING.md +271 -0
  12. django_clerk_users-0.1.0/examples/django_react_example/backend/.env.example +3 -0
  13. django_clerk_users-0.1.0/examples/django_react_example/backend/.gitignore +4 -0
  14. django_clerk_users-0.1.0/examples/django_react_example/backend/api/admin.py +3 -0
  15. django_clerk_users-0.1.0/examples/django_react_example/backend/api/apps.py +6 -0
  16. django_clerk_users-0.1.0/examples/django_react_example/backend/api/models.py +3 -0
  17. django_clerk_users-0.1.0/examples/django_react_example/backend/api/tests.py +278 -0
  18. django_clerk_users-0.1.0/examples/django_react_example/backend/api/urls.py +13 -0
  19. django_clerk_users-0.1.0/examples/django_react_example/backend/api/views.py +68 -0
  20. django_clerk_users-0.1.0/examples/django_react_example/backend/backend/asgi.py +16 -0
  21. django_clerk_users-0.1.0/examples/django_react_example/backend/backend/settings.py +171 -0
  22. django_clerk_users-0.1.0/examples/django_react_example/backend/backend/urls.py +14 -0
  23. django_clerk_users-0.1.0/examples/django_react_example/backend/backend/wsgi.py +16 -0
  24. django_clerk_users-0.1.0/examples/django_react_example/backend/manage.py +22 -0
  25. django_clerk_users-0.1.0/examples/django_react_example/e2e/fixtures/test-users.ts +96 -0
  26. django_clerk_users-0.1.0/examples/django_react_example/e2e/global-setup.ts +28 -0
  27. django_clerk_users-0.1.0/examples/django_react_example/e2e/package.json +20 -0
  28. django_clerk_users-0.1.0/examples/django_react_example/e2e/playwright.config.ts +65 -0
  29. django_clerk_users-0.1.0/examples/django_react_example/e2e/scripts/setup-test-users.ts +113 -0
  30. django_clerk_users-0.1.0/examples/django_react_example/e2e/tests/auth.spec.ts +87 -0
  31. django_clerk_users-0.1.0/examples/django_react_example/e2e/tests/protected-api.spec.ts +82 -0
  32. django_clerk_users-0.1.0/examples/django_react_example/e2e/tsconfig.json +21 -0
  33. django_clerk_users-0.1.0/examples/django_react_example/frontend/.env.example +2 -0
  34. django_clerk_users-0.1.0/examples/django_react_example/frontend/.gitignore +25 -0
  35. django_clerk_users-0.1.0/examples/django_react_example/frontend/.nvmrc +1 -0
  36. django_clerk_users-0.1.0/examples/django_react_example/frontend/README.md +16 -0
  37. django_clerk_users-0.1.0/examples/django_react_example/frontend/eslint.config.js +29 -0
  38. django_clerk_users-0.1.0/examples/django_react_example/frontend/index.html +13 -0
  39. django_clerk_users-0.1.0/examples/django_react_example/frontend/package-lock.json +2813 -0
  40. django_clerk_users-0.1.0/examples/django_react_example/frontend/package.json +28 -0
  41. django_clerk_users-0.1.0/examples/django_react_example/frontend/public/vite.svg +1 -0
  42. django_clerk_users-0.1.0/examples/django_react_example/frontend/src/App.css +100 -0
  43. django_clerk_users-0.1.0/examples/django_react_example/frontend/src/App.jsx +128 -0
  44. django_clerk_users-0.1.0/examples/django_react_example/frontend/src/assets/react.svg +1 -0
  45. django_clerk_users-0.1.0/examples/django_react_example/frontend/src/index.css +68 -0
  46. django_clerk_users-0.1.0/examples/django_react_example/frontend/src/main.jsx +20 -0
  47. django_clerk_users-0.1.0/examples/django_react_example/frontend/vite.config.js +7 -0
  48. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/pyproject.toml +5 -1
  49. django_clerk_users-0.1.0/rav.yaml +50 -0
  50. django_clerk_users-0.1.0/src/django_clerk_users/management/__init__.py +0 -0
  51. django_clerk_users-0.1.0/src/django_clerk_users/management/commands/__init__.py +0 -0
  52. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/src/django_clerk_users/managers.py +14 -15
  53. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/src/django_clerk_users/middleware/auth.py +36 -7
  54. django_clerk_users-0.1.0/src/django_clerk_users/migrations/0002_make_clerk_id_nullable.py +24 -0
  55. django_clerk_users-0.1.0/src/django_clerk_users/migrations/__init__.py +0 -0
  56. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/src/django_clerk_users/models.py +8 -2
  57. django_clerk_users-0.1.0/src/django_clerk_users/organizations/migrations/0001_initial.py +349 -0
  58. django_clerk_users-0.1.0/src/django_clerk_users/organizations/migrations/__init__.py +0 -0
  59. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/src/django_clerk_users/organizations/models.py +1 -3
  60. django_clerk_users-0.1.0/tests/__init__.py +1 -0
  61. django_clerk_users-0.1.0/tests/settings.py +42 -0
  62. django_clerk_users-0.1.0/tests/test_hybrid_auth.py +152 -0
  63. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/tests/test_models.py +9 -6
  64. django_clerk_users-0.1.0/uv.lock +1027 -0
  65. django_clerk_users-0.0.2/README.md +0 -203
  66. django_clerk_users-0.0.2/setup.cfg +0 -4
  67. django_clerk_users-0.0.2/src/django_clerk_users.egg-info/SOURCES.txt +0 -55
  68. django_clerk_users-0.0.2/src/django_clerk_users.egg-info/dependency_links.txt +0 -1
  69. django_clerk_users-0.0.2/src/django_clerk_users.egg-info/requires.txt +0 -6
  70. django_clerk_users-0.0.2/src/django_clerk_users.egg-info/top_level.txt +0 -1
  71. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/LICENSE +0 -0
  72. {django_clerk_users-0.0.2/src/django_clerk_users/management → django_clerk_users-0.1.0/examples/django_react_example/backend/api}/__init__.py +0 -0
  73. {django_clerk_users-0.0.2/src/django_clerk_users/management/commands → django_clerk_users-0.1.0/examples/django_react_example/backend/api/migrations}/__init__.py +0 -0
  74. {django_clerk_users-0.0.2/src/django_clerk_users/migrations → django_clerk_users-0.1.0/examples/django_react_example/backend/backend}/__init__.py +0 -0
  75. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/src/django_clerk_users/__init__.py +0 -0
  76. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/src/django_clerk_users/apps.py +0 -0
  77. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/src/django_clerk_users/authentication/__init__.py +0 -0
  78. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/src/django_clerk_users/authentication/backends.py +0 -0
  79. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/src/django_clerk_users/authentication/drf.py +0 -0
  80. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/src/django_clerk_users/authentication/utils.py +0 -0
  81. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/src/django_clerk_users/caching.py +0 -0
  82. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/src/django_clerk_users/checks.py +0 -0
  83. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/src/django_clerk_users/client.py +0 -0
  84. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/src/django_clerk_users/decorators.py +0 -0
  85. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/src/django_clerk_users/exceptions.py +0 -0
  86. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/src/django_clerk_users/management/commands/migrate_users_to_clerk.py +0 -0
  87. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/src/django_clerk_users/management/commands/sync_clerk_organizations.py +0 -0
  88. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/src/django_clerk_users/management/commands/sync_clerk_users.py +0 -0
  89. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/src/django_clerk_users/middleware/__init__.py +0 -0
  90. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/src/django_clerk_users/migrations/0001_initial.py +0 -0
  91. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/src/django_clerk_users/organizations/__init__.py +0 -0
  92. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/src/django_clerk_users/organizations/admin.py +0 -0
  93. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/src/django_clerk_users/organizations/apps.py +0 -0
  94. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/src/django_clerk_users/organizations/middleware.py +0 -0
  95. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/src/django_clerk_users/organizations/webhooks.py +0 -0
  96. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/src/django_clerk_users/settings.py +0 -0
  97. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/src/django_clerk_users/testing.py +0 -0
  98. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/src/django_clerk_users/utils.py +0 -0
  99. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/src/django_clerk_users/webhooks/__init__.py +0 -0
  100. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/src/django_clerk_users/webhooks/handlers.py +0 -0
  101. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/src/django_clerk_users/webhooks/security.py +0 -0
  102. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/src/django_clerk_users/webhooks/signals.py +0 -0
  103. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/src/django_clerk_users/webhooks/views.py +0 -0
  104. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/tests/test_authentication.py +0 -0
  105. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/tests/test_caching.py +0 -0
  106. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/tests/test_decorators.py +0 -0
  107. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/tests/test_drf.py +0 -0
  108. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/tests/test_import.py +0 -0
  109. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/tests/test_middleware.py +0 -0
  110. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/tests/test_organizations.py +0 -0
  111. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/tests/test_testing.py +0 -0
  112. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/tests/test_utils.py +0 -0
  113. {django_clerk_users-0.0.2 → django_clerk_users-0.1.0}/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)