django-clerk-users 0.0.1__tar.gz → 0.0.2__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 (65) hide show
  1. django_clerk_users-0.0.2/PKG-INFO +228 -0
  2. django_clerk_users-0.0.2/README.md +203 -0
  3. {django_clerk_users-0.0.1 → django_clerk_users-0.0.2}/pyproject.toml +12 -5
  4. django_clerk_users-0.0.2/src/django_clerk_users/__init__.py +93 -0
  5. django_clerk_users-0.0.2/src/django_clerk_users/apps.py +20 -0
  6. django_clerk_users-0.0.2/src/django_clerk_users/authentication/__init__.py +24 -0
  7. django_clerk_users-0.0.2/src/django_clerk_users/authentication/backends.py +89 -0
  8. django_clerk_users-0.0.2/src/django_clerk_users/authentication/drf.py +111 -0
  9. django_clerk_users-0.0.2/src/django_clerk_users/authentication/utils.py +171 -0
  10. django_clerk_users-0.0.2/src/django_clerk_users/caching.py +161 -0
  11. django_clerk_users-0.0.2/src/django_clerk_users/checks.py +127 -0
  12. django_clerk_users-0.0.2/src/django_clerk_users/client.py +32 -0
  13. django_clerk_users-0.0.2/src/django_clerk_users/decorators.py +181 -0
  14. django_clerk_users-0.0.2/src/django_clerk_users/exceptions.py +51 -0
  15. django_clerk_users-0.0.2/src/django_clerk_users/management/__init__.py +0 -0
  16. django_clerk_users-0.0.2/src/django_clerk_users/management/commands/__init__.py +0 -0
  17. django_clerk_users-0.0.2/src/django_clerk_users/management/commands/migrate_users_to_clerk.py +223 -0
  18. django_clerk_users-0.0.2/src/django_clerk_users/management/commands/sync_clerk_organizations.py +191 -0
  19. django_clerk_users-0.0.2/src/django_clerk_users/management/commands/sync_clerk_users.py +114 -0
  20. django_clerk_users-0.0.2/src/django_clerk_users/managers.py +121 -0
  21. django_clerk_users-0.0.2/src/django_clerk_users/middleware/__init__.py +9 -0
  22. django_clerk_users-0.0.2/src/django_clerk_users/middleware/auth.py +201 -0
  23. django_clerk_users-0.0.2/src/django_clerk_users/migrations/0001_initial.py +174 -0
  24. django_clerk_users-0.0.2/src/django_clerk_users/migrations/__init__.py +0 -0
  25. django_clerk_users-0.0.2/src/django_clerk_users/models.py +174 -0
  26. django_clerk_users-0.0.2/src/django_clerk_users/organizations/__init__.py +8 -0
  27. django_clerk_users-0.0.2/src/django_clerk_users/organizations/admin.py +81 -0
  28. django_clerk_users-0.0.2/src/django_clerk_users/organizations/apps.py +8 -0
  29. django_clerk_users-0.0.2/src/django_clerk_users/organizations/middleware.py +130 -0
  30. django_clerk_users-0.0.2/src/django_clerk_users/organizations/models.py +316 -0
  31. django_clerk_users-0.0.2/src/django_clerk_users/organizations/webhooks.py +417 -0
  32. django_clerk_users-0.0.2/src/django_clerk_users/settings.py +37 -0
  33. django_clerk_users-0.0.2/src/django_clerk_users/testing.py +381 -0
  34. django_clerk_users-0.0.2/src/django_clerk_users/utils.py +210 -0
  35. django_clerk_users-0.0.2/src/django_clerk_users/webhooks/__init__.py +26 -0
  36. django_clerk_users-0.0.2/src/django_clerk_users/webhooks/handlers.py +346 -0
  37. django_clerk_users-0.0.2/src/django_clerk_users/webhooks/security.py +108 -0
  38. django_clerk_users-0.0.2/src/django_clerk_users/webhooks/signals.py +42 -0
  39. django_clerk_users-0.0.2/src/django_clerk_users/webhooks/views.py +76 -0
  40. django_clerk_users-0.0.2/src/django_clerk_users.egg-info/PKG-INFO +228 -0
  41. django_clerk_users-0.0.2/src/django_clerk_users.egg-info/SOURCES.txt +55 -0
  42. django_clerk_users-0.0.2/src/django_clerk_users.egg-info/requires.txt +6 -0
  43. django_clerk_users-0.0.2/tests/test_authentication.py +134 -0
  44. django_clerk_users-0.0.2/tests/test_caching.py +181 -0
  45. django_clerk_users-0.0.2/tests/test_decorators.py +274 -0
  46. django_clerk_users-0.0.2/tests/test_drf.py +194 -0
  47. django_clerk_users-0.0.2/tests/test_import.py +221 -0
  48. django_clerk_users-0.0.2/tests/test_middleware.py +307 -0
  49. django_clerk_users-0.0.2/tests/test_models.py +224 -0
  50. django_clerk_users-0.0.2/tests/test_organizations.py +343 -0
  51. django_clerk_users-0.0.2/tests/test_testing.py +234 -0
  52. django_clerk_users-0.0.2/tests/test_utils.py +343 -0
  53. django_clerk_users-0.0.2/tests/test_webhooks.py +363 -0
  54. django_clerk_users-0.0.1/PKG-INFO +0 -24
  55. django_clerk_users-0.0.1/README.md +0 -3
  56. django_clerk_users-0.0.1/src/django_clerk_users/__init__.py +0 -22
  57. django_clerk_users-0.0.1/src/django_clerk_users/main.py +0 -2
  58. django_clerk_users-0.0.1/src/django_clerk_users.egg-info/PKG-INFO +0 -24
  59. django_clerk_users-0.0.1/src/django_clerk_users.egg-info/SOURCES.txt +0 -11
  60. django_clerk_users-0.0.1/src/django_clerk_users.egg-info/requires.txt +0 -1
  61. django_clerk_users-0.0.1/tests/test_import.py +0 -19
  62. {django_clerk_users-0.0.1 → django_clerk_users-0.0.2}/LICENSE +0 -0
  63. {django_clerk_users-0.0.1 → django_clerk_users-0.0.2}/setup.cfg +0 -0
  64. {django_clerk_users-0.0.1 → django_clerk_users-0.0.2}/src/django_clerk_users.egg-info/dependency_links.txt +0 -0
  65. {django_clerk_users-0.0.1 → django_clerk_users-0.0.2}/src/django_clerk_users.egg-info/top_level.txt +0 -0
@@ -0,0 +1,228 @@
1
+ Metadata-Version: 2.4
2
+ Name: django-clerk-users
3
+ Version: 0.0.2
4
+ Summary: Integrate Clerk with Django
5
+ Project-URL: Changelog, https://github.com/jmitchel3/django-clerk-users
6
+ Project-URL: Documentation, https://github.com/jmitchel3/django-clerk-users
7
+ Project-URL: Funding, https://github.com/jmitchel3/django-clerk-users
8
+ Project-URL: Repository, https://github.com/jmitchel3/django-clerk-users
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Framework :: Django
11
+ Classifier: Framework :: Django :: 4.2
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Requires-Python: >=3.12
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: django>=4.2
20
+ Requires-Dist: clerk-backend-api>=1.0.0
21
+ Requires-Dist: svix>=1.0.0
22
+ Provides-Extra: drf
23
+ Requires-Dist: djangorestframework>=3.14; extra == "drf"
24
+ Dynamic: license-file
25
+
26
+ # Django Clerk Users
27
+
28
+ Integrate [Clerk](https://clerk.com) authentication with Django.
29
+
30
+ > **Note:** This package is in early development (v0.0.2). APIs may change.
31
+
32
+ ## Features
33
+
34
+ - Custom user model (`ClerkUser`) with Clerk integration
35
+ - JWT token validation via Clerk SDK
36
+ - Session-based authentication middleware (validates once, caches in session)
37
+ - Webhook handling with Svix signature verification
38
+ - Optional organizations support (separate sub-app)
39
+ - Django REST Framework authentication (optional)
40
+
41
+ ## Installation
42
+
43
+ ```bash
44
+ pip install django-clerk-users
45
+ ```
46
+
47
+ For Django REST Framework support:
48
+
49
+ ```bash
50
+ pip install django-clerk-users[drf]
51
+ ```
52
+
53
+ ## Quick Start
54
+
55
+ ### 1. Add to installed apps
56
+
57
+ ```python
58
+ INSTALLED_APPS = [
59
+ # ...
60
+ "django_clerk_users",
61
+ # Optional: for organization support
62
+ # "django_clerk_users.organizations",
63
+ ]
64
+ ```
65
+
66
+ ### 2. Configure settings
67
+
68
+ ```python
69
+ # Required
70
+ CLERK_SECRET_KEY = "sk_live_..." # From Clerk Dashboard
71
+ CLERK_WEBHOOK_SIGNING_KEY = "whsec_..." # From Clerk Webhooks
72
+ CLERK_FRONTEND_HOSTS = ["https://your-app.com"] # Your frontend URLs
73
+
74
+ # Optional
75
+ CLERK_SESSION_REVALIDATION_SECONDS = 300 # Re-validate JWT every 5 minutes
76
+ CLERK_CACHE_TIMEOUT = 300 # Cache timeout for user lookups
77
+ ```
78
+
79
+ ### 3. Set the user model
80
+
81
+ ```python
82
+ AUTH_USER_MODEL = "django_clerk_users.ClerkUser"
83
+ ```
84
+
85
+ Or extend the abstract model for custom fields:
86
+
87
+ ```python
88
+ # myapp/models.py
89
+ from django_clerk_users.models import AbstractClerkUser
90
+
91
+ class CustomUser(AbstractClerkUser):
92
+ company = models.CharField(max_length=255, blank=True)
93
+
94
+ class Meta(AbstractClerkUser.Meta):
95
+ swappable = "AUTH_USER_MODEL"
96
+
97
+ # settings.py
98
+ AUTH_USER_MODEL = "myapp.CustomUser"
99
+ ```
100
+
101
+ ### 4. Add middleware
102
+
103
+ ```python
104
+ MIDDLEWARE = [
105
+ "django.middleware.security.SecurityMiddleware",
106
+ "django.contrib.sessions.middleware.SessionMiddleware",
107
+ "django.middleware.common.CommonMiddleware",
108
+ "django.middleware.csrf.CsrfViewMiddleware",
109
+ "django.contrib.auth.middleware.AuthenticationMiddleware",
110
+ "django_clerk_users.middleware.ClerkAuthMiddleware", # Add after AuthenticationMiddleware
111
+ # ...
112
+ ]
113
+ ```
114
+
115
+ ### 5. Add authentication backend
116
+
117
+ ```python
118
+ AUTHENTICATION_BACKENDS = [
119
+ "django_clerk_users.authentication.ClerkBackend",
120
+ ]
121
+ ```
122
+
123
+ ### 6. Run migrations
124
+
125
+ ```bash
126
+ python manage.py migrate
127
+ ```
128
+
129
+ ### 7. Configure webhooks
130
+
131
+ Add the webhook URL to your `urls.py`:
132
+
133
+ ```python
134
+ from django_clerk_users.webhooks import clerk_webhook_view
135
+
136
+ urlpatterns = [
137
+ # ...
138
+ path("webhooks/clerk/", clerk_webhook_view, name="clerk_webhook"),
139
+ ]
140
+ ```
141
+
142
+ Then configure your Clerk Dashboard to send webhooks to `https://your-app.com/webhooks/clerk/`.
143
+
144
+ ## Usage
145
+
146
+ ### Accessing the user in views
147
+
148
+ ```python
149
+ def my_view(request):
150
+ if request.user.is_authenticated:
151
+ # Access Clerk user attributes
152
+ print(request.user.clerk_id)
153
+ print(request.user.email)
154
+ print(request.user.full_name)
155
+
156
+ # Access organization (if using organizations)
157
+ print(request.org) # Organization ID from JWT
158
+ ```
159
+
160
+ ### Decorators
161
+
162
+ ```python
163
+ from django_clerk_users.decorators import clerk_user_required
164
+
165
+ @clerk_user_required
166
+ def protected_view(request):
167
+ # Only authenticated Clerk users can access
168
+ return HttpResponse(f"Hello, {request.user.email}")
169
+ ```
170
+
171
+ ### Django REST Framework
172
+
173
+ ```python
174
+ # settings.py
175
+ REST_FRAMEWORK = {
176
+ "DEFAULT_AUTHENTICATION_CLASSES": [
177
+ "django_clerk_users.authentication.ClerkAuthentication",
178
+ ],
179
+ }
180
+ ```
181
+
182
+ ## Organizations (Optional)
183
+
184
+ For Clerk organization support:
185
+
186
+ ```python
187
+ # settings.py
188
+ INSTALLED_APPS = [
189
+ # ...
190
+ "django_clerk_users",
191
+ "django_clerk_users.organizations",
192
+ ]
193
+
194
+ MIDDLEWARE = [
195
+ # ...
196
+ "django_clerk_users.middleware.ClerkAuthMiddleware",
197
+ "django_clerk_users.organizations.middleware.ClerkOrganizationMiddleware",
198
+ ]
199
+ ```
200
+
201
+ ## Management Commands
202
+
203
+ ```bash
204
+ # Sync users from Clerk
205
+ python manage.py sync_clerk_users
206
+
207
+ # Sync organizations from Clerk
208
+ python manage.py sync_clerk_organizations
209
+ ```
210
+
211
+ ## Configuration Reference
212
+
213
+ | Setting | Required | Default | Description |
214
+ |---------|----------|---------|-------------|
215
+ | `CLERK_SECRET_KEY` | Yes | - | Your Clerk secret key |
216
+ | `CLERK_WEBHOOK_SIGNING_KEY` | Yes* | - | Webhook signing secret (*required for webhooks) |
217
+ | `CLERK_FRONTEND_HOSTS` | Yes | `[]` | Authorized frontend URLs |
218
+ | `CLERK_SESSION_REVALIDATION_SECONDS` | No | `300` | JWT revalidation interval |
219
+ | `CLERK_CACHE_TIMEOUT` | No | `300` | User cache timeout |
220
+ | `CLERK_ORG_CACHE_TIMEOUT` | No | `900` | Organization cache timeout |
221
+
222
+ ## License
223
+
224
+ MIT
225
+
226
+ ## Contributing
227
+
228
+ Contributions are welcome! Please open an issue or PR on [GitHub](https://github.com/jmitchel3/django-clerk-users).
@@ -0,0 +1,203 @@
1
+ # Django Clerk Users
2
+
3
+ Integrate [Clerk](https://clerk.com) authentication with Django.
4
+
5
+ > **Note:** This package is in early development (v0.0.2). APIs may change.
6
+
7
+ ## Features
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
+ ## Installation
17
+
18
+ ```bash
19
+ pip install django-clerk-users
20
+ ```
21
+
22
+ For Django REST Framework support:
23
+
24
+ ```bash
25
+ pip install django-clerk-users[drf]
26
+ ```
27
+
28
+ ## Quick Start
29
+
30
+ ### 1. Add to installed apps
31
+
32
+ ```python
33
+ INSTALLED_APPS = [
34
+ # ...
35
+ "django_clerk_users",
36
+ # Optional: for organization support
37
+ # "django_clerk_users.organizations",
38
+ ]
39
+ ```
40
+
41
+ ### 2. Configure settings
42
+
43
+ ```python
44
+ # Required
45
+ CLERK_SECRET_KEY = "sk_live_..." # From Clerk Dashboard
46
+ CLERK_WEBHOOK_SIGNING_KEY = "whsec_..." # From Clerk Webhooks
47
+ CLERK_FRONTEND_HOSTS = ["https://your-app.com"] # Your frontend URLs
48
+
49
+ # Optional
50
+ CLERK_SESSION_REVALIDATION_SECONDS = 300 # Re-validate JWT every 5 minutes
51
+ CLERK_CACHE_TIMEOUT = 300 # Cache timeout for user lookups
52
+ ```
53
+
54
+ ### 3. Set the user model
55
+
56
+ ```python
57
+ AUTH_USER_MODEL = "django_clerk_users.ClerkUser"
58
+ ```
59
+
60
+ Or extend the abstract model for custom fields:
61
+
62
+ ```python
63
+ # myapp/models.py
64
+ from django_clerk_users.models import AbstractClerkUser
65
+
66
+ class CustomUser(AbstractClerkUser):
67
+ company = models.CharField(max_length=255, blank=True)
68
+
69
+ class Meta(AbstractClerkUser.Meta):
70
+ swappable = "AUTH_USER_MODEL"
71
+
72
+ # settings.py
73
+ AUTH_USER_MODEL = "myapp.CustomUser"
74
+ ```
75
+
76
+ ### 4. Add middleware
77
+
78
+ ```python
79
+ MIDDLEWARE = [
80
+ "django.middleware.security.SecurityMiddleware",
81
+ "django.contrib.sessions.middleware.SessionMiddleware",
82
+ "django.middleware.common.CommonMiddleware",
83
+ "django.middleware.csrf.CsrfViewMiddleware",
84
+ "django.contrib.auth.middleware.AuthenticationMiddleware",
85
+ "django_clerk_users.middleware.ClerkAuthMiddleware", # Add after AuthenticationMiddleware
86
+ # ...
87
+ ]
88
+ ```
89
+
90
+ ### 5. Add authentication backend
91
+
92
+ ```python
93
+ AUTHENTICATION_BACKENDS = [
94
+ "django_clerk_users.authentication.ClerkBackend",
95
+ ]
96
+ ```
97
+
98
+ ### 6. Run migrations
99
+
100
+ ```bash
101
+ python manage.py migrate
102
+ ```
103
+
104
+ ### 7. Configure webhooks
105
+
106
+ Add the webhook URL to your `urls.py`:
107
+
108
+ ```python
109
+ from django_clerk_users.webhooks import clerk_webhook_view
110
+
111
+ urlpatterns = [
112
+ # ...
113
+ path("webhooks/clerk/", clerk_webhook_view, name="clerk_webhook"),
114
+ ]
115
+ ```
116
+
117
+ Then configure your Clerk Dashboard to send webhooks to `https://your-app.com/webhooks/clerk/`.
118
+
119
+ ## Usage
120
+
121
+ ### Accessing the user in views
122
+
123
+ ```python
124
+ def my_view(request):
125
+ if request.user.is_authenticated:
126
+ # Access Clerk user attributes
127
+ print(request.user.clerk_id)
128
+ print(request.user.email)
129
+ print(request.user.full_name)
130
+
131
+ # Access organization (if using organizations)
132
+ print(request.org) # Organization ID from JWT
133
+ ```
134
+
135
+ ### Decorators
136
+
137
+ ```python
138
+ from django_clerk_users.decorators import clerk_user_required
139
+
140
+ @clerk_user_required
141
+ def protected_view(request):
142
+ # Only authenticated Clerk users can access
143
+ return HttpResponse(f"Hello, {request.user.email}")
144
+ ```
145
+
146
+ ### Django REST Framework
147
+
148
+ ```python
149
+ # settings.py
150
+ REST_FRAMEWORK = {
151
+ "DEFAULT_AUTHENTICATION_CLASSES": [
152
+ "django_clerk_users.authentication.ClerkAuthentication",
153
+ ],
154
+ }
155
+ ```
156
+
157
+ ## Organizations (Optional)
158
+
159
+ For Clerk organization support:
160
+
161
+ ```python
162
+ # settings.py
163
+ INSTALLED_APPS = [
164
+ # ...
165
+ "django_clerk_users",
166
+ "django_clerk_users.organizations",
167
+ ]
168
+
169
+ MIDDLEWARE = [
170
+ # ...
171
+ "django_clerk_users.middleware.ClerkAuthMiddleware",
172
+ "django_clerk_users.organizations.middleware.ClerkOrganizationMiddleware",
173
+ ]
174
+ ```
175
+
176
+ ## Management Commands
177
+
178
+ ```bash
179
+ # Sync users from Clerk
180
+ python manage.py sync_clerk_users
181
+
182
+ # Sync organizations from Clerk
183
+ python manage.py sync_clerk_organizations
184
+ ```
185
+
186
+ ## Configuration Reference
187
+
188
+ | Setting | Required | Default | Description |
189
+ |---------|----------|---------|-------------|
190
+ | `CLERK_SECRET_KEY` | Yes | - | Your Clerk secret key |
191
+ | `CLERK_WEBHOOK_SIGNING_KEY` | Yes* | - | Webhook signing secret (*required for webhooks) |
192
+ | `CLERK_FRONTEND_HOSTS` | Yes | `[]` | Authorized frontend URLs |
193
+ | `CLERK_SESSION_REVALIDATION_SECONDS` | No | `300` | JWT revalidation interval |
194
+ | `CLERK_CACHE_TIMEOUT` | No | `300` | User cache timeout |
195
+ | `CLERK_ORG_CACHE_TIMEOUT` | No | `900` | Organization cache timeout |
196
+
197
+ ## License
198
+
199
+ MIT
200
+
201
+ ## Contributing
202
+
203
+ Contributions are welcome! Please open an issue or PR on [GitHub](https://github.com/jmitchel3/django-clerk-users).
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "django-clerk-users"
3
- version = "0.0.1"
3
+ version = "0.0.2"
4
4
  description = "Integrate Clerk with Django"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -15,19 +15,26 @@ classifiers = [
15
15
  ]
16
16
  dependencies = [
17
17
  "django>=4.2",
18
+ "clerk-backend-api>=1.0.0",
19
+ "svix>=1.0.0",
18
20
  ]
19
21
 
22
+ [project.optional-dependencies]
23
+ drf = ["djangorestframework>=3.14"]
20
24
 
21
- urls.Changelog = "https://github.com/jmitchel3/django-clerk-users"
22
- urls.Documentation = "https://github.com/jmitchel3/django-clerk-users"
23
- urls.Funding = "https://github.com/jmitchel3/django-clerk-users"
24
- urls.Repository = "https://github.com/jmitchel3/django-clerk-users"
25
+
26
+ [project.urls]
27
+ Changelog = "https://github.com/jmitchel3/django-clerk-users"
28
+ Documentation = "https://github.com/jmitchel3/django-clerk-users"
29
+ Funding = "https://github.com/jmitchel3/django-clerk-users"
30
+ Repository = "https://github.com/jmitchel3/django-clerk-users"
25
31
 
26
32
  [dependency-groups]
27
33
  dev = [
28
34
  "pip>=25.3",
29
35
  "pre-commit>=4.4.0",
30
36
  "pytest>=9.0.0",
37
+ "pytest-django>=4.9",
31
38
  "rav>=0.1.1",
32
39
  "tox>=4.32.0",
33
40
  "tox-uv>=1.29.0",
@@ -0,0 +1,93 @@
1
+ """
2
+ django-clerk-users: Integrate Clerk authentication with Django.
3
+ """
4
+
5
+ from importlib.metadata import PackageNotFoundError, version
6
+
7
+ try:
8
+ __version__ = version("django-clerk-users")
9
+ except PackageNotFoundError:
10
+ __version__ = "unknown"
11
+
12
+ # Re-export default app config
13
+ default_app_config = "django_clerk_users.apps.DjangoClerkUsersConfig"
14
+
15
+
16
+ def __getattr__(name: str):
17
+ """Lazy import to avoid loading Django models before apps are ready."""
18
+ # Models
19
+ if name == "AbstractClerkUser":
20
+ from django_clerk_users.models import AbstractClerkUser
21
+
22
+ return AbstractClerkUser
23
+ if name == "ClerkUser":
24
+ from django_clerk_users.models import ClerkUser
25
+
26
+ return ClerkUser
27
+ if name == "ClerkUserManager":
28
+ from django_clerk_users.models import ClerkUserManager
29
+
30
+ return ClerkUserManager
31
+
32
+ # Client
33
+ if name == "get_clerk_client":
34
+ from django_clerk_users.client import get_clerk_client
35
+
36
+ return get_clerk_client
37
+
38
+ # Exceptions
39
+ if name in (
40
+ "ClerkError",
41
+ "ClerkConfigurationError",
42
+ "ClerkAuthenticationError",
43
+ "ClerkTokenError",
44
+ "ClerkWebhookError",
45
+ "ClerkAPIError",
46
+ "ClerkUserNotFoundError",
47
+ "ClerkOrganizationNotFoundError",
48
+ ):
49
+ from django_clerk_users import exceptions
50
+
51
+ return getattr(exceptions, name)
52
+
53
+ # Testing utilities
54
+ if name in (
55
+ "ClerkTestClient",
56
+ "ClerkTestMixin",
57
+ "TestUserData",
58
+ "make_test_email",
59
+ "make_test_phone",
60
+ "TEST_OTP_CODE",
61
+ ):
62
+ from django_clerk_users import testing
63
+
64
+ return getattr(testing, name)
65
+
66
+ raise AttributeError(f"Module 'django_clerk_users' has no attribute '{name}'")
67
+
68
+
69
+ __all__ = [
70
+ "__version__",
71
+ # Models
72
+ "AbstractClerkUser",
73
+ "ClerkUser",
74
+ "ClerkUserManager",
75
+ # Client
76
+ "get_clerk_client",
77
+ # Exceptions
78
+ "ClerkError",
79
+ "ClerkConfigurationError",
80
+ "ClerkAuthenticationError",
81
+ "ClerkTokenError",
82
+ "ClerkWebhookError",
83
+ "ClerkAPIError",
84
+ "ClerkUserNotFoundError",
85
+ "ClerkOrganizationNotFoundError",
86
+ # Testing utilities
87
+ "ClerkTestClient",
88
+ "ClerkTestMixin",
89
+ "TestUserData",
90
+ "make_test_email",
91
+ "make_test_phone",
92
+ "TEST_OTP_CODE",
93
+ ]
@@ -0,0 +1,20 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class DjangoClerkUsersConfig(AppConfig):
5
+ name = "django_clerk_users"
6
+ verbose_name = "Django Clerk Users"
7
+ default_auto_field = "django.db.models.BigAutoField"
8
+
9
+ def ready(self):
10
+ # Import checks to register them with Django
11
+ from django_clerk_users import checks # noqa: F401
12
+
13
+ # Disconnect Django's update_last_login signal
14
+ # Clerk manages authentication externally
15
+ from django.contrib.auth import get_user_model
16
+ from django.contrib.auth.models import update_last_login
17
+ from django.contrib.auth.signals import user_logged_in
18
+
19
+ User = get_user_model()
20
+ user_logged_in.disconnect(update_last_login, sender=User)
@@ -0,0 +1,24 @@
1
+ """
2
+ Authentication backends and utilities for django-clerk-users.
3
+ """
4
+
5
+ from django_clerk_users.authentication.backends import ClerkBackend
6
+ from django_clerk_users.authentication.utils import (
7
+ get_clerk_payload_from_request,
8
+ get_or_create_user_from_payload,
9
+ )
10
+
11
+ __all__ = [
12
+ "ClerkBackend",
13
+ "get_clerk_payload_from_request",
14
+ "get_or_create_user_from_payload",
15
+ ]
16
+
17
+ # Conditionally export DRF authentication if available
18
+ try:
19
+ from django_clerk_users.authentication.drf import ClerkAuthentication
20
+
21
+ __all__.append("ClerkAuthentication")
22
+ except ImportError:
23
+ # DRF is not installed
24
+ pass
@@ -0,0 +1,89 @@
1
+ """
2
+ Django authentication backend for Clerk.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import logging
8
+ from typing import TYPE_CHECKING, Any
9
+
10
+ from django.contrib.auth import get_user_model
11
+ from django.contrib.auth.backends import BaseBackend
12
+
13
+ if TYPE_CHECKING:
14
+ from django.http import HttpRequest
15
+
16
+ from django_clerk_users.models import AbstractClerkUser
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ class ClerkBackend(BaseBackend):
22
+ """
23
+ Django authentication backend for Clerk.
24
+
25
+ This backend authenticates users by their Clerk ID rather than
26
+ username/password. It's designed to work with Clerk's JWT-based
27
+ authentication.
28
+
29
+ To use this backend, add it to AUTHENTICATION_BACKENDS in settings:
30
+
31
+ AUTHENTICATION_BACKENDS = [
32
+ 'django_clerk_users.authentication.ClerkBackend',
33
+ ]
34
+ """
35
+
36
+ def authenticate(
37
+ self,
38
+ request: "HttpRequest | None" = None,
39
+ clerk_id: str | None = None,
40
+ **kwargs: Any,
41
+ ) -> "AbstractClerkUser | None":
42
+ """
43
+ Authenticate a user by their Clerk ID.
44
+
45
+ Args:
46
+ request: The current HTTP request (optional).
47
+ clerk_id: The Clerk user ID to authenticate.
48
+ **kwargs: Additional keyword arguments (ignored).
49
+
50
+ Returns:
51
+ The authenticated user or None if authentication fails.
52
+ """
53
+ if not clerk_id:
54
+ return None
55
+
56
+ User = get_user_model()
57
+
58
+ try:
59
+ user = User.objects.get(clerk_id=clerk_id)
60
+ if user.is_active:
61
+ return user
62
+ logger.debug(f"User {clerk_id} is inactive")
63
+ return None
64
+ except User.DoesNotExist:
65
+ logger.debug(f"No user found with clerk_id: {clerk_id}")
66
+ return None
67
+
68
+ def get_user(self, user_id: int) -> "AbstractClerkUser | None":
69
+ """
70
+ Get a user by their Django primary key.
71
+
72
+ This method is called by Django's authentication middleware
73
+ to restore the user from the session.
74
+
75
+ Args:
76
+ user_id: The user's primary key.
77
+
78
+ Returns:
79
+ The user instance or None if not found.
80
+ """
81
+ User = get_user_model()
82
+
83
+ try:
84
+ user = User.objects.get(pk=user_id)
85
+ if user.is_active:
86
+ return user
87
+ return None
88
+ except User.DoesNotExist:
89
+ return None