A django application for API key management with rest-framework support
Project description
django-keysmith
Production-oriented API token management for Django and Django REST Framework.
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.
Why Keysmith
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:
- secure token generation and hashed storage
- explicit lifecycle APIs (
create,rotate,revoke) - plain Django and DRF integration on one validation pipeline
- structured audit records for auth and lifecycle events
Features
- No plaintext token storage (PBKDF2-SHA512 by default)
- Deterministic lookup path (
prefixindex + hash verify) - Token states: active, expired, revoked, purged
- Scope model backed by Django
auth.Permissioncodenames - Middleware + decorators for plain Django
- Authentication + permission classes for DRF
- One-time raw token display in Django admin create flow
- Extensibility hooks for rate limiting and DRF throttling
- Swappable token and audit models with system-contract checks
Requirements
- Python 3.9+
- Django 4.0+
Optional:
- Django REST Framework 3.15.2+ (
django-keysmith[drf])
Installation
pip install django-keysmith
With DRF support:
pip install "django-keysmith[drf]"
Quick Start (Plain Django)
- Add Keysmith to your app and middleware stack.
# settings.py
INSTALLED_APPS = [
# ...
"keysmith",
]
MIDDLEWARE = [
# ...
"django.contrib.auth.middleware.AuthenticationMiddleware",
"keysmith.django.middleware.KeysmithAuthenticationMiddleware",
]
- Run migrations.
python manage.py migrate
- Create a token and keep the raw value safe.
from django.contrib.auth import get_user_model
from keysmith.services.tokens import create_token
User = get_user_model()
user = User.objects.get(username="api-user")
token, raw_token = create_token(
name="local-dev",
user=user, # recommended for plain Django decorators
)
print(token.prefix)
print(raw_token) # store immediately; raw secret is not recoverable from DB
- Protect views.
from django.http import JsonResponse
from keysmith.django.decorator import keysmith_required
@keysmith_required
def secure_view(request):
return JsonResponse({"ok": True, "token_prefix": request.keysmith_token.prefix})
- Call with header:
curl -H "X-KEYSMITH-TOKEN: <raw-token>" http://localhost:8000/api/secure/
Quick Start (DRF)
# settings.py
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
"keysmith.drf.auth.KeysmithAuthentication",
],
"DEFAULT_PERMISSION_CLASSES": [
"keysmith.drf.permissions.RequireKeysmithToken",
],
}
from rest_framework.response import Response
from rest_framework.views import APIView
class StatusView(APIView):
def get(self, request):
return Response({
"authenticated": True,
"token_prefix": request.auth.prefix,
})
Token Lifecycle API
from keysmith.services.tokens import create_token, rotate_token, revoke_token
# create
token, raw = create_token(name="billing-worker", user=user)
# rotate (invalidates previous raw token immediately)
new_raw = rotate_token(token, actor=request.user, request=request)
# revoke (optionally purge)
revoke_token(token, purge=True, actor=request.user, request=request)
Authentication Flow
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.
Public Token Format
Example:
tok_ab12CD34:SecretValueHere123456
Structure:
<namespace>_<identifier>: indexed prefix used for lookup:<secret>: verified against hashedkeyin DB- trailing
6-digit CRC: format integrity check
Configuration
Configure through KEYSMITH in settings.py.
KEYSMITH = {
"DEFAULT_EXPIRY_DAYS": 90,
"ENABLE_AUDIT_LOGGING": True,
"HEADER_NAME": "HTTP_X_KEYSMITH_TOKEN",
"ALLOW_QUERY_PARAM": False,
}
Common options:
| Setting | Default | Purpose |
|---|---|---|
HASH_BACKEND |
keysmith.hashers.PBKDF2SHA512TokenHasher |
Token hashing backend |
HASH_ITERATIONS |
100_000 |
PBKDF2 cost factor |
DEFAULT_EXPIRY_DAYS |
90 |
Default token expiry window |
TOKEN_MODEL |
keysmith.Token |
Swappable token model |
AUDIT_LOG_MODEL |
keysmith.TokenAuditLog |
Swappable audit model |
HEADER_NAME |
HTTP_X_KEYSMITH_TOKEN |
Header key in request.META |
ALLOW_QUERY_PARAM |
False |
Accept token via query string |
QUERY_PARAM_NAME |
keysmith_token |
Query parameter name |
ENABLE_AUDIT_LOGGING |
True |
Enable audit row creation |
TOKEN_PREFIX |
tok |
Prefix namespace |
TOKEN_SECRET_LENGTH |
32 |
Generated secret length |
AVAILABLE_SCOPES |
[] |
Allowed permission codenames |
DEFAULT_SCOPES |
[] |
Auto-assigned codenames on token create |
RATE_LIMIT_HOOK |
None |
Callable: hook(request, raw_token=None) |
DRF_THROTTLE_HOOK |
None |
Callable: hook(request, token=None) |
Scopes and Permissions
Scopes are Django permission codenames attached to each token.
- Plain Django: use
@keysmith_scopes("write") - DRF: use
HasKeysmithScopesorScopedPermission("write")
from keysmith.django.decorator import keysmith_required
from keysmith.django.permissions import keysmith_scopes
@keysmith_required
@keysmith_scopes("write")
def create_resource(request):
...
Admin Experience
Keysmith registers Token and TokenAuditLog in Django admin:
- create tokens from admin UI
- see raw token once immediately after creation
- revoke/purge selected tokens via admin actions
- inspect audit events by token, action, path, status, and timestamp
Extending Keysmith
Swappable models
KEYSMITH = {
"TOKEN_MODEL": "myapp.MyToken",
"AUDIT_LOG_MODEL": "myapp.MyAuditLog",
}
Custom models must satisfy required field/method contracts (validated by Django system checks).
Custom hash backend
KEYSMITH = {
"HASH_BACKEND": "myapp.security.MyTokenHasher",
}
Hasher must implement BaseTokenHasher.hash() and BaseTokenHasher.verify().
Rate limit / throttle hooks
KEYSMITH = {
"RATE_LIMIT_HOOK": "myapp.auth.rate_limit_hook",
"DRF_THROTTLE_HOOK": "myapp.auth.drf_throttle_hook",
}
Development
make setup
make lint
make test
make migration-check
Build or preview docs:
make docs-build
make docs-serve
Contributing
See CONTRIBUTING.md and CODE_OF_CONDUCT.md.
Security
Please report vulnerabilities privately as described in SECURITY.md.
License
MIT. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_keysmith-0.1.1.tar.gz.
File metadata
- Download URL: django_keysmith-0.1.1.tar.gz
- Upload date:
- Size: 20.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c955de4d2b899d1a4010dd2004d3bf5f8f01ba7706e1cf0724fc733d453d231b
|
|
| MD5 |
c774f8a9060d551a59d8b10f8d6341d0
|
|
| BLAKE2b-256 |
348ed747d0784ba0c344e5608602a967a9ea74d5e35e31e3fcdb8f6861bbdc48
|
Provenance
The following attestation bundles were made for django_keysmith-0.1.1.tar.gz:
Publisher:
publish.yml on thekodeking/django-keysmith
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_keysmith-0.1.1.tar.gz -
Subject digest:
c955de4d2b899d1a4010dd2004d3bf5f8f01ba7706e1cf0724fc733d453d231b - Sigstore transparency entry: 999415616
- Sigstore integration time:
-
Permalink:
thekodeking/django-keysmith@9136d4700c70112116bf41251ca96a753fb042d2 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/thekodeking
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9136d4700c70112116bf41251ca96a753fb042d2 -
Trigger Event:
release
-
Statement type:
File details
Details for the file django_keysmith-0.1.1-py3-none-any.whl.
File metadata
- Download URL: django_keysmith-0.1.1-py3-none-any.whl
- Upload date:
- Size: 31.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
160e763fee4b69af6be53450aec4aaa54c906f7487a6772504ac4bd452e26018
|
|
| MD5 |
5d6d5f25ec1a0ba9e0639f79740c7f3e
|
|
| BLAKE2b-256 |
d569b781d5062b306fb224177826edadb0a494c68509ba996a245d81e14c351b
|
Provenance
The following attestation bundles were made for django_keysmith-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on thekodeking/django-keysmith
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_keysmith-0.1.1-py3-none-any.whl -
Subject digest:
160e763fee4b69af6be53450aec4aaa54c906f7487a6772504ac4bd452e26018 - Sigstore transparency entry: 999415722
- Sigstore integration time:
-
Permalink:
thekodeking/django-keysmith@9136d4700c70112116bf41251ca96a753fb042d2 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/thekodeking
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9136d4700c70112116bf41251ca96a753fb042d2 -
Trigger Event:
release
-
Statement type: