Skip to main content

django-rest-knox-redis

PyPI Python Django Tests License: MIT

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:

  1. A cache hit verifies the raw token digest against the cached digest.
  2. Authentication then selects the exact token and its user from the database.
  3. request.auth and request._auth receive the real Knox AuthToken model, so Knox logout and application code can call model methods normally.
  4. 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-effort on_commit deletion 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=None uses MAX_TOKEN_CACHE_TTL as 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.KnoxRedisSyncAuth uses the hardened Redis-aware authenticator;
  • knox_redis.dmr.KnoxDatabaseSyncAuth always 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 CacheInvalidationError to 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's CacheInvalidationError/503 rate.
  • Alert on invalidation failures: they intentionally block revocation.
  • Do not use KEYS in production. Inspect a prefix with SCAN, for example redis-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)

Source distribution for django-rest-knox-redis 0.2.0
File Size Uploaded
django_rest_knox_redis-0.2.0.tar.gz 25.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for django-rest-knox-redis 0.2.0
File Interpreter ABI Platform
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 log

Release 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

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.3

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page