django-keysmith 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.
Files changed (39) hide show
  1. django_keysmith-0.1.1/LICENSE +9 -0
  2. django_keysmith-0.1.1/PKG-INFO +342 -0
  3. django_keysmith-0.1.1/README.md +291 -0
  4. django_keysmith-0.1.1/keysmith/__init__.py +0 -0
  5. django_keysmith-0.1.1/keysmith/admin.py +306 -0
  6. django_keysmith-0.1.1/keysmith/apps.py +9 -0
  7. django_keysmith-0.1.1/keysmith/audit/logger.py +67 -0
  8. django_keysmith-0.1.1/keysmith/auth/__init__.py +1 -0
  9. django_keysmith-0.1.1/keysmith/auth/base.py +59 -0
  10. django_keysmith-0.1.1/keysmith/auth/exceptions.py +14 -0
  11. django_keysmith-0.1.1/keysmith/auth/utils.py +6 -0
  12. django_keysmith-0.1.1/keysmith/checks.py +108 -0
  13. django_keysmith-0.1.1/keysmith/django/__init__.py +1 -0
  14. django_keysmith-0.1.1/keysmith/django/decorator.py +39 -0
  15. django_keysmith-0.1.1/keysmith/django/http.py +5 -0
  16. django_keysmith-0.1.1/keysmith/django/middleware.py +87 -0
  17. django_keysmith-0.1.1/keysmith/django/permissions.py +45 -0
  18. django_keysmith-0.1.1/keysmith/drf/__init__.py +1 -0
  19. django_keysmith-0.1.1/keysmith/drf/auth.py +77 -0
  20. django_keysmith-0.1.1/keysmith/drf/permissions.py +75 -0
  21. django_keysmith-0.1.1/keysmith/hashers/__init__.py +3 -0
  22. django_keysmith-0.1.1/keysmith/hashers/base.py +21 -0
  23. django_keysmith-0.1.1/keysmith/hashers/pbkdf2.py +30 -0
  24. django_keysmith-0.1.1/keysmith/hashers/registry.py +9 -0
  25. django_keysmith-0.1.1/keysmith/hooks.py +10 -0
  26. django_keysmith-0.1.1/keysmith/migrations/0001_initial.py +158 -0
  27. django_keysmith-0.1.1/keysmith/migrations/0002_remove_token_hint_and_add_created_action.py +30 -0
  28. django_keysmith-0.1.1/keysmith/migrations/__init__.py +0 -0
  29. django_keysmith-0.1.1/keysmith/models/__init__.py +4 -0
  30. django_keysmith-0.1.1/keysmith/models/audit.py +10 -0
  31. django_keysmith-0.1.1/keysmith/models/base.py +173 -0
  32. django_keysmith-0.1.1/keysmith/models/token.py +10 -0
  33. django_keysmith-0.1.1/keysmith/models/utils.py +30 -0
  34. django_keysmith-0.1.1/keysmith/services/tokens.py +245 -0
  35. django_keysmith-0.1.1/keysmith/settings.py +73 -0
  36. django_keysmith-0.1.1/keysmith/templates/admin/keysmith/token/token_created.html +33 -0
  37. django_keysmith-0.1.1/keysmith/utils/tokens.py +84 -0
  38. django_keysmith-0.1.1/keysmith/views.py +0 -0
  39. django_keysmith-0.1.1/pyproject.toml +86 -0
@@ -0,0 +1,9 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-present thekodeking <nkrishnaraj.developer@gmail.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,342 @@
1
+ Metadata-Version: 2.3
2
+ Name: django-keysmith
3
+ Version: 0.1.1
4
+ Summary: A django application for API key management with rest-framework support
5
+ Keywords: django,keys,management
6
+ Author: thekodeking
7
+ Author-email: thekodeking <nkrishnaraj.developer@gmail.com>
8
+ License: MIT License
9
+
10
+ Copyright (c) 2025-present thekodeking <nkrishnaraj.developer@gmail.com>
11
+
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17
+ Classifier: Development Status :: 3 - Alpha
18
+ Classifier: Environment :: Web Environment
19
+ Classifier: Framework :: Django
20
+ Classifier: Framework :: Django :: 4.0
21
+ Classifier: Framework :: Django :: 5.0
22
+ Classifier: Intended Audience :: Developers
23
+ Classifier: License :: OSI Approved :: MIT License
24
+ Classifier: Programming Language :: Python
25
+ Classifier: Programming Language :: Python :: 3
26
+ Classifier: Programming Language :: Python :: 3.9
27
+ Classifier: Programming Language :: Python :: 3.10
28
+ Classifier: Programming Language :: Python :: 3.11
29
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
30
+ Requires-Dist: django>=4.2.28
31
+ Requires-Dist: black>=22.3.0 ; extra == 'dev'
32
+ Requires-Dist: django-stubs>=5.1.3 ; extra == 'dev'
33
+ Requires-Dist: djangorestframework-stubs>=3.15.1 ; extra == 'dev'
34
+ Requires-Dist: flake8>=4.0.1 ; extra == 'dev'
35
+ Requires-Dist: isort>=5.10.1 ; extra == 'dev'
36
+ Requires-Dist: mypy>=0.941 ; extra == 'dev'
37
+ Requires-Dist: pytest>=7.0.0 ; extra == 'dev'
38
+ Requires-Dist: pytest-django>=4.5.2 ; extra == 'dev'
39
+ Requires-Dist: ruff>=0.15.2 ; extra == 'dev'
40
+ Requires-Dist: zensical ; extra == 'docs'
41
+ Requires-Dist: djangorestframework>=3.15.2 ; extra == 'drf'
42
+ Requires-Python: >=3.9
43
+ Project-URL: Homepage, https://github.com/thekodeking/django-keysmith
44
+ Project-URL: Issues, https://github.com/thekodeking/django-keysmith/issues
45
+ Project-URL: Repository, https://github.com/thekodeking/django-keysmith
46
+ Project-URL: Documentation, https://thekodeking.github.io/django-keysmith/
47
+ Provides-Extra: dev
48
+ Provides-Extra: docs
49
+ Provides-Extra: drf
50
+ Description-Content-Type: text/markdown
51
+
52
+ # django-keysmith
53
+
54
+ <p align="center">
55
+ <img src="https://img.shields.io/pypi/v/django-keysmith.svg" alt="PyPI version">
56
+ <img src="https://img.shields.io/pypi/pyversions/django-keysmith.svg" alt="Python versions">
57
+ <img src="https://img.shields.io/badge/django-4.0%2B-blue.svg" alt="Django versions">
58
+ <img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License">
59
+ </p>
60
+
61
+ Production-oriented API token management for Django and Django REST Framework.
62
+
63
+ `django-keysmith` gives you a lifecycle-first token system with secure secret storage, rotation and revocation primitives, scope-based authorization, and built-in audit trails.
64
+
65
+ ## Why Keysmith
66
+
67
+ Many token solutions cover basic request authentication but leave lifecycle operations, observability, and extension points to app-level glue code. Keysmith provides those pieces as a cohesive package:
68
+
69
+ - secure token generation and hashed storage
70
+ - explicit lifecycle APIs (`create`, `rotate`, `revoke`)
71
+ - plain Django and DRF integration on one validation pipeline
72
+ - structured audit records for auth and lifecycle events
73
+
74
+ ## Features
75
+
76
+ - No plaintext token storage (PBKDF2-SHA512 by default)
77
+ - Deterministic lookup path (`prefix` index + hash verify)
78
+ - Token states: active, expired, revoked, purged
79
+ - Scope model backed by Django `auth.Permission` codenames
80
+ - Middleware + decorators for plain Django
81
+ - Authentication + permission classes for DRF
82
+ - One-time raw token display in Django admin create flow
83
+ - Extensibility hooks for rate limiting and DRF throttling
84
+ - Swappable token and audit models with system-contract checks
85
+
86
+ ## Requirements
87
+
88
+ - Python 3.9+
89
+ - Django 4.0+
90
+
91
+ Optional:
92
+
93
+ - Django REST Framework 3.15.2+ (`django-keysmith[drf]`)
94
+
95
+ ## Installation
96
+
97
+ ```bash
98
+ pip install django-keysmith
99
+ ```
100
+
101
+ With DRF support:
102
+
103
+ ```bash
104
+ pip install "django-keysmith[drf]"
105
+ ```
106
+
107
+ ## Quick Start (Plain Django)
108
+
109
+ 1. Add Keysmith to your app and middleware stack.
110
+
111
+ ```python
112
+ # settings.py
113
+ INSTALLED_APPS = [
114
+ # ...
115
+ "keysmith",
116
+ ]
117
+
118
+ MIDDLEWARE = [
119
+ # ...
120
+ "django.contrib.auth.middleware.AuthenticationMiddleware",
121
+ "keysmith.django.middleware.KeysmithAuthenticationMiddleware",
122
+ ]
123
+ ```
124
+
125
+ 2. Run migrations.
126
+
127
+ ```bash
128
+ python manage.py migrate
129
+ ```
130
+
131
+ 3. Create a token and keep the raw value safe.
132
+
133
+ ```python
134
+ from django.contrib.auth import get_user_model
135
+ from keysmith.services.tokens import create_token
136
+
137
+ User = get_user_model()
138
+ user = User.objects.get(username="api-user")
139
+
140
+ token, raw_token = create_token(
141
+ name="local-dev",
142
+ user=user, # recommended for plain Django decorators
143
+ )
144
+
145
+ print(token.prefix)
146
+ print(raw_token) # store immediately; raw secret is not recoverable from DB
147
+ ```
148
+
149
+ 4. Protect views.
150
+
151
+ ```python
152
+ from django.http import JsonResponse
153
+ from keysmith.django.decorator import keysmith_required
154
+
155
+ @keysmith_required
156
+ def secure_view(request):
157
+ return JsonResponse({"ok": True, "token_prefix": request.keysmith_token.prefix})
158
+ ```
159
+
160
+ 5. Call with header:
161
+
162
+ ```bash
163
+ curl -H "X-KEYSMITH-TOKEN: <raw-token>" http://localhost:8000/api/secure/
164
+ ```
165
+
166
+ ## Quick Start (DRF)
167
+
168
+ ```python
169
+ # settings.py
170
+ REST_FRAMEWORK = {
171
+ "DEFAULT_AUTHENTICATION_CLASSES": [
172
+ "keysmith.drf.auth.KeysmithAuthentication",
173
+ ],
174
+ "DEFAULT_PERMISSION_CLASSES": [
175
+ "keysmith.drf.permissions.RequireKeysmithToken",
176
+ ],
177
+ }
178
+ ```
179
+
180
+ ```python
181
+ from rest_framework.response import Response
182
+ from rest_framework.views import APIView
183
+
184
+ class StatusView(APIView):
185
+ def get(self, request):
186
+ return Response({
187
+ "authenticated": True,
188
+ "token_prefix": request.auth.prefix,
189
+ })
190
+ ```
191
+
192
+ ## Token Lifecycle API
193
+
194
+ ```python
195
+ from keysmith.services.tokens import create_token, rotate_token, revoke_token
196
+
197
+ # create
198
+ token, raw = create_token(name="billing-worker", user=user)
199
+
200
+ # rotate (invalidates previous raw token immediately)
201
+ new_raw = rotate_token(token, actor=request.user, request=request)
202
+
203
+ # revoke (optionally purge)
204
+ revoke_token(token, purge=True, actor=request.user, request=request)
205
+ ```
206
+
207
+ ## Authentication Flow
208
+
209
+ For each request, Keysmith reads the token from the configured header, validates format and checksum, resolves the token by prefix, enforces lifecycle checks (revoked, purged, expired), verifies the secret hash, marks the token as used, attaches auth context to the request, and writes the corresponding audit event.
210
+
211
+ ## Public Token Format
212
+
213
+ Example:
214
+
215
+ ```text
216
+ tok_ab12CD34:SecretValueHere123456
217
+ ```
218
+
219
+ Structure:
220
+
221
+ - `<namespace>_<identifier>`: indexed prefix used for lookup
222
+ - `:<secret>`: verified against hashed `key` in DB
223
+ - trailing `6-digit CRC`: format integrity check
224
+
225
+ ## Configuration
226
+
227
+ Configure through `KEYSMITH` in `settings.py`.
228
+
229
+ ```python
230
+ KEYSMITH = {
231
+ "DEFAULT_EXPIRY_DAYS": 90,
232
+ "ENABLE_AUDIT_LOGGING": True,
233
+ "HEADER_NAME": "HTTP_X_KEYSMITH_TOKEN",
234
+ "ALLOW_QUERY_PARAM": False,
235
+ }
236
+ ```
237
+
238
+ Common options:
239
+
240
+ | Setting | Default | Purpose |
241
+ | --- | --- | --- |
242
+ | `HASH_BACKEND` | `keysmith.hashers.PBKDF2SHA512TokenHasher` | Token hashing backend |
243
+ | `HASH_ITERATIONS` | `100_000` | PBKDF2 cost factor |
244
+ | `DEFAULT_EXPIRY_DAYS` | `90` | Default token expiry window |
245
+ | `TOKEN_MODEL` | `keysmith.Token` | Swappable token model |
246
+ | `AUDIT_LOG_MODEL` | `keysmith.TokenAuditLog` | Swappable audit model |
247
+ | `HEADER_NAME` | `HTTP_X_KEYSMITH_TOKEN` | Header key in `request.META` |
248
+ | `ALLOW_QUERY_PARAM` | `False` | Accept token via query string |
249
+ | `QUERY_PARAM_NAME` | `keysmith_token` | Query parameter name |
250
+ | `ENABLE_AUDIT_LOGGING` | `True` | Enable audit row creation |
251
+ | `TOKEN_PREFIX` | `tok` | Prefix namespace |
252
+ | `TOKEN_SECRET_LENGTH` | `32` | Generated secret length |
253
+ | `AVAILABLE_SCOPES` | `[]` | Allowed permission codenames |
254
+ | `DEFAULT_SCOPES` | `[]` | Auto-assigned codenames on token create |
255
+ | `RATE_LIMIT_HOOK` | `None` | Callable: `hook(request, raw_token=None)` |
256
+ | `DRF_THROTTLE_HOOK` | `None` | Callable: `hook(request, token=None)` |
257
+
258
+ ## Scopes and Permissions
259
+
260
+ Scopes are Django permission codenames attached to each token.
261
+
262
+ - Plain Django: use `@keysmith_scopes("write")`
263
+ - DRF: use `HasKeysmithScopes` or `ScopedPermission("write")`
264
+
265
+ ```python
266
+ from keysmith.django.decorator import keysmith_required
267
+ from keysmith.django.permissions import keysmith_scopes
268
+
269
+ @keysmith_required
270
+ @keysmith_scopes("write")
271
+ def create_resource(request):
272
+ ...
273
+ ```
274
+
275
+ ## Admin Experience
276
+
277
+ Keysmith registers `Token` and `TokenAuditLog` in Django admin:
278
+
279
+ - create tokens from admin UI
280
+ - see raw token once immediately after creation
281
+ - revoke/purge selected tokens via admin actions
282
+ - inspect audit events by token, action, path, status, and timestamp
283
+
284
+ ## Extending Keysmith
285
+
286
+ ### Swappable models
287
+
288
+ ```python
289
+ KEYSMITH = {
290
+ "TOKEN_MODEL": "myapp.MyToken",
291
+ "AUDIT_LOG_MODEL": "myapp.MyAuditLog",
292
+ }
293
+ ```
294
+
295
+ Custom models must satisfy required field/method contracts (validated by Django system checks).
296
+
297
+ ### Custom hash backend
298
+
299
+ ```python
300
+ KEYSMITH = {
301
+ "HASH_BACKEND": "myapp.security.MyTokenHasher",
302
+ }
303
+ ```
304
+
305
+ Hasher must implement `BaseTokenHasher.hash()` and `BaseTokenHasher.verify()`.
306
+
307
+ ### Rate limit / throttle hooks
308
+
309
+ ```python
310
+ KEYSMITH = {
311
+ "RATE_LIMIT_HOOK": "myapp.auth.rate_limit_hook",
312
+ "DRF_THROTTLE_HOOK": "myapp.auth.drf_throttle_hook",
313
+ }
314
+ ```
315
+
316
+ ## Development
317
+
318
+ ```bash
319
+ make setup
320
+ make lint
321
+ make test
322
+ make migration-check
323
+ ```
324
+
325
+ Build or preview docs:
326
+
327
+ ```bash
328
+ make docs-build
329
+ make docs-serve
330
+ ```
331
+
332
+ ## Contributing
333
+
334
+ See [CONTRIBUTING.md](CONTRIBUTING.md) and [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md).
335
+
336
+ ## Security
337
+
338
+ Please report vulnerabilities privately as described in [SECURITY.md](SECURITY.md).
339
+
340
+ ## License
341
+
342
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,291 @@
1
+ # django-keysmith
2
+
3
+ <p align="center">
4
+ <img src="https://img.shields.io/pypi/v/django-keysmith.svg" alt="PyPI version">
5
+ <img src="https://img.shields.io/pypi/pyversions/django-keysmith.svg" alt="Python versions">
6
+ <img src="https://img.shields.io/badge/django-4.0%2B-blue.svg" alt="Django versions">
7
+ <img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License">
8
+ </p>
9
+
10
+ Production-oriented API token management for Django and Django REST Framework.
11
+
12
+ `django-keysmith` gives you a lifecycle-first token system with secure secret storage, rotation and revocation primitives, scope-based authorization, and built-in audit trails.
13
+
14
+ ## Why Keysmith
15
+
16
+ Many token solutions cover basic request authentication but leave lifecycle operations, observability, and extension points to app-level glue code. Keysmith provides those pieces as a cohesive package:
17
+
18
+ - secure token generation and hashed storage
19
+ - explicit lifecycle APIs (`create`, `rotate`, `revoke`)
20
+ - plain Django and DRF integration on one validation pipeline
21
+ - structured audit records for auth and lifecycle events
22
+
23
+ ## Features
24
+
25
+ - No plaintext token storage (PBKDF2-SHA512 by default)
26
+ - Deterministic lookup path (`prefix` index + hash verify)
27
+ - Token states: active, expired, revoked, purged
28
+ - Scope model backed by Django `auth.Permission` codenames
29
+ - Middleware + decorators for plain Django
30
+ - Authentication + permission classes for DRF
31
+ - One-time raw token display in Django admin create flow
32
+ - Extensibility hooks for rate limiting and DRF throttling
33
+ - Swappable token and audit models with system-contract checks
34
+
35
+ ## Requirements
36
+
37
+ - Python 3.9+
38
+ - Django 4.0+
39
+
40
+ Optional:
41
+
42
+ - Django REST Framework 3.15.2+ (`django-keysmith[drf]`)
43
+
44
+ ## Installation
45
+
46
+ ```bash
47
+ pip install django-keysmith
48
+ ```
49
+
50
+ With DRF support:
51
+
52
+ ```bash
53
+ pip install "django-keysmith[drf]"
54
+ ```
55
+
56
+ ## Quick Start (Plain Django)
57
+
58
+ 1. Add Keysmith to your app and middleware stack.
59
+
60
+ ```python
61
+ # settings.py
62
+ INSTALLED_APPS = [
63
+ # ...
64
+ "keysmith",
65
+ ]
66
+
67
+ MIDDLEWARE = [
68
+ # ...
69
+ "django.contrib.auth.middleware.AuthenticationMiddleware",
70
+ "keysmith.django.middleware.KeysmithAuthenticationMiddleware",
71
+ ]
72
+ ```
73
+
74
+ 2. Run migrations.
75
+
76
+ ```bash
77
+ python manage.py migrate
78
+ ```
79
+
80
+ 3. Create a token and keep the raw value safe.
81
+
82
+ ```python
83
+ from django.contrib.auth import get_user_model
84
+ from keysmith.services.tokens import create_token
85
+
86
+ User = get_user_model()
87
+ user = User.objects.get(username="api-user")
88
+
89
+ token, raw_token = create_token(
90
+ name="local-dev",
91
+ user=user, # recommended for plain Django decorators
92
+ )
93
+
94
+ print(token.prefix)
95
+ print(raw_token) # store immediately; raw secret is not recoverable from DB
96
+ ```
97
+
98
+ 4. Protect views.
99
+
100
+ ```python
101
+ from django.http import JsonResponse
102
+ from keysmith.django.decorator import keysmith_required
103
+
104
+ @keysmith_required
105
+ def secure_view(request):
106
+ return JsonResponse({"ok": True, "token_prefix": request.keysmith_token.prefix})
107
+ ```
108
+
109
+ 5. Call with header:
110
+
111
+ ```bash
112
+ curl -H "X-KEYSMITH-TOKEN: <raw-token>" http://localhost:8000/api/secure/
113
+ ```
114
+
115
+ ## Quick Start (DRF)
116
+
117
+ ```python
118
+ # settings.py
119
+ REST_FRAMEWORK = {
120
+ "DEFAULT_AUTHENTICATION_CLASSES": [
121
+ "keysmith.drf.auth.KeysmithAuthentication",
122
+ ],
123
+ "DEFAULT_PERMISSION_CLASSES": [
124
+ "keysmith.drf.permissions.RequireKeysmithToken",
125
+ ],
126
+ }
127
+ ```
128
+
129
+ ```python
130
+ from rest_framework.response import Response
131
+ from rest_framework.views import APIView
132
+
133
+ class StatusView(APIView):
134
+ def get(self, request):
135
+ return Response({
136
+ "authenticated": True,
137
+ "token_prefix": request.auth.prefix,
138
+ })
139
+ ```
140
+
141
+ ## Token Lifecycle API
142
+
143
+ ```python
144
+ from keysmith.services.tokens import create_token, rotate_token, revoke_token
145
+
146
+ # create
147
+ token, raw = create_token(name="billing-worker", user=user)
148
+
149
+ # rotate (invalidates previous raw token immediately)
150
+ new_raw = rotate_token(token, actor=request.user, request=request)
151
+
152
+ # revoke (optionally purge)
153
+ revoke_token(token, purge=True, actor=request.user, request=request)
154
+ ```
155
+
156
+ ## Authentication Flow
157
+
158
+ For each request, Keysmith reads the token from the configured header, validates format and checksum, resolves the token by prefix, enforces lifecycle checks (revoked, purged, expired), verifies the secret hash, marks the token as used, attaches auth context to the request, and writes the corresponding audit event.
159
+
160
+ ## Public Token Format
161
+
162
+ Example:
163
+
164
+ ```text
165
+ tok_ab12CD34:SecretValueHere123456
166
+ ```
167
+
168
+ Structure:
169
+
170
+ - `<namespace>_<identifier>`: indexed prefix used for lookup
171
+ - `:<secret>`: verified against hashed `key` in DB
172
+ - trailing `6-digit CRC`: format integrity check
173
+
174
+ ## Configuration
175
+
176
+ Configure through `KEYSMITH` in `settings.py`.
177
+
178
+ ```python
179
+ KEYSMITH = {
180
+ "DEFAULT_EXPIRY_DAYS": 90,
181
+ "ENABLE_AUDIT_LOGGING": True,
182
+ "HEADER_NAME": "HTTP_X_KEYSMITH_TOKEN",
183
+ "ALLOW_QUERY_PARAM": False,
184
+ }
185
+ ```
186
+
187
+ Common options:
188
+
189
+ | Setting | Default | Purpose |
190
+ | --- | --- | --- |
191
+ | `HASH_BACKEND` | `keysmith.hashers.PBKDF2SHA512TokenHasher` | Token hashing backend |
192
+ | `HASH_ITERATIONS` | `100_000` | PBKDF2 cost factor |
193
+ | `DEFAULT_EXPIRY_DAYS` | `90` | Default token expiry window |
194
+ | `TOKEN_MODEL` | `keysmith.Token` | Swappable token model |
195
+ | `AUDIT_LOG_MODEL` | `keysmith.TokenAuditLog` | Swappable audit model |
196
+ | `HEADER_NAME` | `HTTP_X_KEYSMITH_TOKEN` | Header key in `request.META` |
197
+ | `ALLOW_QUERY_PARAM` | `False` | Accept token via query string |
198
+ | `QUERY_PARAM_NAME` | `keysmith_token` | Query parameter name |
199
+ | `ENABLE_AUDIT_LOGGING` | `True` | Enable audit row creation |
200
+ | `TOKEN_PREFIX` | `tok` | Prefix namespace |
201
+ | `TOKEN_SECRET_LENGTH` | `32` | Generated secret length |
202
+ | `AVAILABLE_SCOPES` | `[]` | Allowed permission codenames |
203
+ | `DEFAULT_SCOPES` | `[]` | Auto-assigned codenames on token create |
204
+ | `RATE_LIMIT_HOOK` | `None` | Callable: `hook(request, raw_token=None)` |
205
+ | `DRF_THROTTLE_HOOK` | `None` | Callable: `hook(request, token=None)` |
206
+
207
+ ## Scopes and Permissions
208
+
209
+ Scopes are Django permission codenames attached to each token.
210
+
211
+ - Plain Django: use `@keysmith_scopes("write")`
212
+ - DRF: use `HasKeysmithScopes` or `ScopedPermission("write")`
213
+
214
+ ```python
215
+ from keysmith.django.decorator import keysmith_required
216
+ from keysmith.django.permissions import keysmith_scopes
217
+
218
+ @keysmith_required
219
+ @keysmith_scopes("write")
220
+ def create_resource(request):
221
+ ...
222
+ ```
223
+
224
+ ## Admin Experience
225
+
226
+ Keysmith registers `Token` and `TokenAuditLog` in Django admin:
227
+
228
+ - create tokens from admin UI
229
+ - see raw token once immediately after creation
230
+ - revoke/purge selected tokens via admin actions
231
+ - inspect audit events by token, action, path, status, and timestamp
232
+
233
+ ## Extending Keysmith
234
+
235
+ ### Swappable models
236
+
237
+ ```python
238
+ KEYSMITH = {
239
+ "TOKEN_MODEL": "myapp.MyToken",
240
+ "AUDIT_LOG_MODEL": "myapp.MyAuditLog",
241
+ }
242
+ ```
243
+
244
+ Custom models must satisfy required field/method contracts (validated by Django system checks).
245
+
246
+ ### Custom hash backend
247
+
248
+ ```python
249
+ KEYSMITH = {
250
+ "HASH_BACKEND": "myapp.security.MyTokenHasher",
251
+ }
252
+ ```
253
+
254
+ Hasher must implement `BaseTokenHasher.hash()` and `BaseTokenHasher.verify()`.
255
+
256
+ ### Rate limit / throttle hooks
257
+
258
+ ```python
259
+ KEYSMITH = {
260
+ "RATE_LIMIT_HOOK": "myapp.auth.rate_limit_hook",
261
+ "DRF_THROTTLE_HOOK": "myapp.auth.drf_throttle_hook",
262
+ }
263
+ ```
264
+
265
+ ## Development
266
+
267
+ ```bash
268
+ make setup
269
+ make lint
270
+ make test
271
+ make migration-check
272
+ ```
273
+
274
+ Build or preview docs:
275
+
276
+ ```bash
277
+ make docs-build
278
+ make docs-serve
279
+ ```
280
+
281
+ ## Contributing
282
+
283
+ See [CONTRIBUTING.md](CONTRIBUTING.md) and [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md).
284
+
285
+ ## Security
286
+
287
+ Please report vulnerabilities privately as described in [SECURITY.md](SECURITY.md).
288
+
289
+ ## License
290
+
291
+ MIT. See [LICENSE](LICENSE).
File without changes