django-rest-knox-redis
A bounded Redis lookup layer for
django-rest-knox. It preserves
Knox's AuthToken request contract, makes revocation database-authoritative,
and offers an optional synchronous
django-modern-rest
adapter.
Security and consistency model
Redis is an acceleration hint, never the source of truth:
- A cache hit verifies the raw token digest against the cached digest.
- Authentication then selects the exact token and its user from the database.
request.authandrequest._authreceive the real KnoxAuthTokenmodel, so Knox logout and application code can call model methods normally.- Token deletion invalidates Redis in
pre_delete. If Redis cannot confirm invalidation, deletion fails closed and the surrounding database operation is rolled back. A best-efforton_commitdeletion closes the invalidate-before-commit race.
This deliberately trades revocation availability for consistency: logout,
logout-all, admin deletion, ORM deletion, expiry cleanup, or a user cascade can
return/raise a 503-class CacheInvalidationError while Redis is unavailable.
Ordinary authentication can still fall back to Knox's database path when a
Redis read or cache population fails. A stale entry left by a failed
post-commit cleanup cannot authenticate because every hit rechecks the token
row.
All cache entries are bounded:
- finite Knox tokens use
min(remaining token lifetime, MAX_TOKEN_CACHE_TTL); TOKEN_TTL=NoneusesMAX_TOKEN_CACHE_TTLas the mandatory revalidation bound;- entries with less than one whole second remaining are not cached;
- the default maximum is 300 seconds.
When Knox AUTO_REFRESH=True, token caching is disabled and authentication is
delegated to Knox. This preserves MIN_REFRESH_INTERVAL and
AUTO_REFRESH_MAX_TTL semantics without maintaining a competing Redis expiry.
Measured authentication operations
These counts are regression-tested with Django 6.0, django-rest-knox 5.1, one
valid token, and AUTO_REFRESH=False. They count all SQL, not only token-model
manager calls.
| Path | Redis commands / round trips | SQL queries | Result |
|---|---|---|---|
| Cache hit | GET / 1 |
1 | Joined exact token + user row |
| Cache miss | GET, then pipelined SET, SADD, EXPIREAT / 2 |
2 | Knox lookup + Knox expired-sibling cleanup query, then cache population |
AUTO_REFRESH=True |
none | Knox-owned | Two reads for the simple valid-token case, plus an UPDATE only when Knox's refresh interval permits |
The cache therefore reduces the tested valid-token path from two SQL queries to one; it does not eliminate database authentication queries. Previous unreproducible claims such as “22x faster”, “95% fewer queries”, and “7x more throughput” are intentionally not claimed. Measure end-to-end behavior against your database, Redis topology, user model, and token population.
Compatibility
The base package supports:
| Python | Django |
|---|---|
| 3.10–3.14 | 5.2 |
| 3.12–3.14 | 6.0 |
Runtime bounds are DRF 3.16.1–3.17, django-rest-knox 5.0.4–5.1, and django-redis 6–7. The CI matrix installs each supported Python/Django combination into a fresh environment, includes an explicit lower-bound job for DRF 3.16.1, Knox 5.0.4, and django-redis 6.0.0, and asserts the resolved versions. The optional DMR extra requires Python 3.11 or newer through django-modern-rest.
Installation
DRF only:
python -m pip install django-rest-knox-redis
With optional DMR integration:
python -m pip install 'django-rest-knox-redis[dmr]'
The extra is exactly django-modern-rest>=0.12; importing the base package
does not import or require DMR. The example below uses DMR's optional msgspec
serializer plugin, so install its backend too:
python -m pip install 'django-rest-knox-redis[dmr]' 'django-modern-rest[msgspec]'
DRF configuration
Add the applications and a django-redis cache:
# settings.py
from datetime import timedelta
INSTALLED_APPS = [
# ...
"rest_framework",
"knox",
"knox_redis",
]
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
},
},
}
REST_KNOX = {
"TOKEN_TTL": timedelta(hours=10),
"AUTO_REFRESH": False,
}
REST_KNOX_REDIS = {
"CACHE_ALIAS": "default",
"REDIS_KEY_PREFIX": "knox",
"CACHE_ENABLED": True,
"MAX_TOKEN_CACHE_TTL": 300,
}
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
"knox_redis.auth.TokenAuthentication",
],
}
Use the Redis-aware Knox views so their full request authentication flow uses the same authenticator:
# urls.py
from django.urls import path
from knox_redis.views import LoginView, LogoutAllView, LogoutView
urlpatterns = [
path("api/auth/login/", LoginView.as_view(), name="knox_login"),
path("api/auth/logout/", LogoutView.as_view(), name="knox_logout"),
path("api/auth/logout-all/", LogoutAllView.as_view(), name="knox_logout_all"),
]
The authenticator is also available per view:
from knox_redis.auth import TokenAuthentication
from rest_framework.response import Response
from rest_framework.views import APIView
class ProtectedView(APIView):
authentication_classes = (TokenAuthentication,)
def get(self, request):
return Response({"username": request.user.get_username()})
Settings
| Setting | Type | Default | Meaning |
|---|---|---|---|
CACHE_ALIAS |
str |
"default" |
Django cache alias backed by django-redis |
REDIS_KEY_PREFIX |
str |
"knox" |
Prefix for token and user-index keys |
CACHE_ENABLED |
bool |
True |
Enables Redis reads, population, and invalidation |
MAX_TOKEN_CACHE_TTL |
positive int |
300 |
Maximum seconds before database revalidation, including non-expiring Knox tokens |
Settings are reloadable with Django's override_settings and preserve the
0.1.x names and defaults, except that cache entries are now always bounded.
Redis keys
knox:token:{token_key} JSON schema v2: digest, user_id, created, expiry, token_key
knox:user:{user_id}:tokens bounded set of cached token keys
Unknown, old, malformed, or mismatched cache payloads are treated as misses. The prefix is configurable for multi-application Redis deployments.
django-modern-rest integration
The public adapters implement DMR SyncAuth:
knox_redis.dmr.KnoxRedisSyncAuthuses the hardened Redis-aware authenticator;knox_redis.dmr.KnoxDatabaseSyncAuthalways uses Knox's database authenticator.
Both return a real Knox AuthToken and populate request.user,
request.auth, request._auth, and request.auser. They preserve Knox header
parsing for Authorization: Token <token> and translate DRF authentication
errors into DMR responses with WWW-Authenticate: Token instead of leaking a
500.
Required authentication:
from dmr import Controller
from dmr.plugins.msgspec import MsgspecSerializer
from knox_redis.dmr import KnoxRedisSyncAuth
class ProtectedController(Controller[MsgspecSerializer]):
auth = (KnoxRedisSyncAuth(required=True),)
Optional authentication keeps DMR alternative order. With required=False
(the default), missing credentials or another scheme returns None so the
next authenticator can run; recognized malformed or invalid Knox credentials
remain terminal 401 responses:
class OptionalController(Controller[MsgspecSerializer]):
auth = (KnoxRedisSyncAuth(), MyAnonymousOrOtherSyncAuth())
OpenAPI describes Knox as type: apiKey, name: Authorization, in: header;
it is not represented as a Bearer scheme.
Native DMR AsyncAuth is not implemented. The adapters call synchronous Knox
and Django ORM APIs and must be used through DMR's synchronous authentication
path.
Known limitations
- Cache hits validate the presented token but do not run Knox's opportunistic cleanup loop for other expired tokens owned by the same user. Those rows are removed when presented, by logout/admin/ORM cleanup, or by an application maintenance task.
- DRF and DMR translate
CacheInvalidationErrorto 503. Direct ORM callers receive the exception, while uncustomized Django admin error handling renders it as a 500 even though the deletion transaction is safely rolled back. - There is no native DMR async authenticator.
Operations
- Monitor Redis
keyspace_hits,keyspace_misses, command latency, and the application'sCacheInvalidationError/503 rate. - Alert on invalidation failures: they intentionally block revocation.
- Do not use
KEYSin production. Inspect a prefix withSCAN, for exampleredis-cli --scan --pattern 'knox:token:*'. - Keep Redis access restricted; cached data includes token digests and user identifiers, never raw tokens.
Development and release checks
uv sync --locked --all-extras
uv run --locked --all-extras pytest
uv run --locked --all-extras ruff check .
uv run --locked --all-extras ruff format --check .
uv run --locked --all-extras mypy knox_redis
DJANGO_SETTINGS_MODULE=tests.settings uv run --locked --all-extras django-admin check
uv build --no-build-isolation
uv run --locked --all-extras twine check dist/*
See CHANGELOG.md for release notes. The package is licensed under the MIT License.
Metadata
Release files for django-rest-knox-redis 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| django_rest_knox_redis-0.2.0.tar.gz | 25.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| django_rest_knox_redis-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 42.1 kB
Release files / django_rest_knox_redis-0.2.0.tar.gz
| Download URL | django_rest_knox_redis-0.2.0.tar.gz |
|---|---|
| Size | 25.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d2691fc2c866bf7f3b1b2a8e96639ba24758e5d53ab879d874889d47c45818cf
|
|
BLAKE2b-256 checksum How to use checksums |
fe956e16ac7f53733874e337bc9e41fd888b6fea12fdaa22e82e3c5800cb3c38
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.
Transparency logRelease files / django_rest_knox_redis-0.2.0-py3-none-any.whl
| Download URL | django_rest_knox_redis-0.2.0-py3-none-any.whl |
|---|---|
| Size | 16.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2dde1061ff55babbfb1787cd7ffda4d67314994e3392ce73df34f85ba4fb8cbc
|
|
BLAKE2b-256 checksum How to use checksums |
2a57e50a1dd5feb0557f71673360b1016d931a1d221febd9ff9ee90489be034b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.
Transparency log