Skip to main content

django-channels-jwt-stateless

Stateless JWT authentication middleware for Django Channels. Zero database queries on WebSocket handshake.

Existing Django Channels JWT packages call User.objects.get() on every WebSocket connect. Under burst traffic this saturates the connection pool and causes multi-second stalls. This package verifies the JWT signature and builds a lightweight HandshakeUser from the token claims instead.

Install

pip install django-channels-jwt-stateless

Quick Start

# asgi.py
from channels.routing import ProtocolTypeRouter, URLRouter
from django_channels_jwt_stateless import JWTAuthMiddleware

application = ProtocolTypeRouter({
    "http": django_application,
    "websocket": JWTAuthMiddleware(URLRouter(websocket_urlpatterns)),
})

Works out of the box with djangorestframework-simplejwt defaults (HS256, SECRET_KEY, user_id claim). Pass the token as ?token=<jwt> in the WebSocket URL.

JWTAuthMiddlewareStack

Use JWTAuthMiddlewareStack instead only when your consumers need scope["session"] or scope["cookies"]. Two things to know:

  • The session user never reaches scope["user"]: this middleware always sets it first, so channels' AuthMiddleware only writes into an unread attribute. Session-cookie login is not a fallback.
  • channels' get_user() still runs on every handshake (a database_sync_to_async hop, plus session and user DB queries for clients that carry a sessionid cookie), which partly defeats the purpose of this package. Prefer the bare middleware.

scope["user"]

On a valid token, scope["user"] is a HandshakeUser instance:

Attribute Value
.id / .pk From the JWT user_id claim
.is_anonymous False
.is_authenticated True

On an invalid, expired, or missing token it is AnonymousUser.

HandshakeUser is not a Django model instance: it has no _meta, so channels.auth.login(scope, user) raises AttributeError with it. Non-integer primary keys (e.g. UUID) arrive as strings, exactly as the JWT claim stores them; avoid == comparisons against uuid.UUID.

Configuration

Optional. Add to settings.py:

CHANNELS_JWT_STATELESS = {
    "ALGORITHM": "HS256",              # default
    "SIGNING_KEY": None,               # defaults to SECRET_KEY
    "USER_ID_CLAIM": "user_id",        # default
    "TOKEN_QUERY_PARAM": "token",      # default
    "IS_BLACKLISTED": None,            # dotted path to (jti) -> bool
    "TOKEN_DECODER": None,             # dotted path to (token) -> dict
    "SUBPROTOCOL_PREFIX": None,        # e.g. "access_token"
}

TOKEN_DECODER and IS_BLACKLISTED accept both sync and async callables.

Token Blacklisting

# myapp/auth.py
from django.core.cache import cache

async def is_blacklisted(jti: str) -> bool:
    return await cache.aget(f"blacklist:{jti}") is not None

# settings.py
CHANNELS_JWT_STATELESS = {
    "IS_BLACKLISTED": "myapp.auth.is_blacklisted",
}

Subprotocol Auth

To avoid leaking tokens in query strings:

CHANNELS_JWT_STATELESS = {"SUBPROTOCOL_PREFIX": "access_token"}

Client sends Sec-WebSocket-Protocol: access_token.<jwt>.

Compatibility

Python 3.10+, Django 4.2+, Channels 4+, PyJWT 2+.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_channels_jwt_stateless-0.1.2.tar.gz (8.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_channels_jwt_stateless-0.1.2-py3-none-any.whl (7.7 kB view details)

Uploaded Python 3

File details

Details for the file django_channels_jwt_stateless-0.1.2.tar.gz.

File metadata

File hashes

Hashes for django_channels_jwt_stateless-0.1.2.tar.gz
Algorithm Hash digest
SHA256 d38a7193969715d9bbce039d7ed5017a332f50d01074827b592021a3779f4a5d
MD5 466b6d55167508d33f15dc6532fd20b7
BLAKE2b-256 ea23443dc001f15b7a4a17930aae71f5a62cee1e0027c75aaacae94af57fe816

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_channels_jwt_stateless-0.1.2.tar.gz:

Publisher: publish.yml on zxzinn/django-channels-jwt-stateless

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file django_channels_jwt_stateless-0.1.2-py3-none-any.whl.

File metadata

File hashes

Hashes for django_channels_jwt_stateless-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 687855d6dd72542fecbbd4ace0b25d7858d76fea7700adca95b669937b98565c
MD5 0cb3d0e342e6e771ccf54d357cfaf051
BLAKE2b-256 f98d715b28dd3959e432fd01936821dbc1075713981e03ff79e8dac82a6239ba

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_channels_jwt_stateless-0.1.2-py3-none-any.whl:

Publisher: publish.yml on zxzinn/django-channels-jwt-stateless

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.1.2 This release

2 files

0.1.1

2 files

0.1.0

2 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