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

Metadata

Release files for django-channels-jwt-stateless 0.1.2

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-channels-jwt-stateless 0.1.2
File Size Uploaded Provenance Actions
django_channels_jwt_stateless-0.1.2.tar.gz 8.1 kB With provenance Details for django_channels_jwt_stateless-0.1.2.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for django-channels-jwt-stateless 0.1.2
File Interpreter ABI Platform Actions
django_channels_jwt_stateless-0.1.2-py3-none-any.whl Python 3 none any Details for django_channels_jwt_stateless-0.1.2-py3-none-any.whl

Total release size: 15.8 kB

Release files / django_channels_jwt_stateless-0.1.2.tar.gz

Download URL django_channels_jwt_stateless-0.1.2.tar.gz
Size 8.1 kB
Tags Source
SHA-256 checksum
How to use checksums
d38a7193969715d9bbce039d7ed5017a332f50d01074827b592021a3779f4a5d
BLAKE2b-256 checksum
How to use checksums
ea23443dc001f15b7a4a17930aae71f5a62cee1e0027c75aaacae94af57fe816
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 Aug 12, 2026.

Transparency log

Release files / django_channels_jwt_stateless-0.1.2-py3-none-any.whl

Download URL django_channels_jwt_stateless-0.1.2-py3-none-any.whl
Size 7.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
687855d6dd72542fecbbd4ace0b25d7858d76fea7700adca95b669937b98565c
BLAKE2b-256 checksum
How to use checksums
f98d715b28dd3959e432fd01936821dbc1075713981e03ff79e8dac82a6239ba
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 Aug 12, 2026.

Transparency log

Security for django-channels-jwt-stateless 0.1.2

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.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for django_channels_jwt_stateless-0.1.2.tar.gz

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 Aug 12, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.2 This release

2 release files Full provenance

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