Last active
April 10, 2025 16:38
-
-
Save rluts/22e05ed8f53f97bdd02eafdf38f3d60a to your computer and use it in GitHub Desktop.
Token authorization middleware for Django Channels 2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from channels.auth import AuthMiddlewareStack | |
from rest_framework.authtoken.models import Token | |
from django.contrib.auth.models import AnonymousUser | |
from django.db import close_old_connections | |
class TokenAuthMiddleware: | |
""" | |
Token authorization middleware for Django Channels 2 | |
""" | |
def __init__(self, inner): | |
self.inner = inner | |
def __call__(self, scope): | |
headers = dict(scope['headers']) | |
if b'authorization' in headers: | |
try: | |
token_name, token_key = headers[b'authorization'].decode().split() | |
if token_name == 'Token': | |
token = Token.objects.get(key=token_key) | |
scope['user'] = token.user | |
close_old_connections() | |
except Token.DoesNotExist: | |
scope['user'] = AnonymousUser() | |
return self.inner(scope) | |
TokenAuthMiddlewareStack = lambda inner: TokenAuthMiddleware(AuthMiddlewareStack(inner)) |
If anyone else needs it, here is a version of @dmwyatt gist in which Channels 3.0 are supported with rest_framework_simplejwt:
https://gist.github.com/alex-pobeditel-2004/5098bac720c4eeb79052b7234346f52d
For those who are stuck with client side code to be written and how this middleware connects to the url configuration, following blog can help --> https://hashnode.com/post/using-django-drf-jwt-authentication-with-django-channels-cjzy5ffqs0013rus1yb9huxvl
To support headers Authorization
and token
from query string, and also session as well.
from urllib import parse
from rest_framework.authtoken.models import Token
from channels.db import database_sync_to_async
from channels.auth import AuthMiddlewareStack
@database_sync_to_async
def get_user_from_headers_or_queries(scope):
"""
function to get the `User` object
from his headers or queries as well.
:return object of `User` or None
"""
try:
headers = dict(scope["headers"])
except KeyError as error:
headers = {}
logger.error(error)
try:
params = dict(parse.parse_qsl(scope["query_string"].decode("utf8")))
except KeyError as error:
params = {}
logger.warning(error)
token_key = None
token_is_found = False
if b"authorization" in headers:
# 1. get from authorization headers
token_name, token_key = headers[b"authorization"].decode().split()
if token_name == "Token": # nosec: B105 (just checking the token name)
token_is_found = True
else:
# 2. get from token params
token_key = params.get("token")
token_is_found = True if token_key else False
if token_is_found:
try:
token = Token.objects.get(key=token_key)
return token.user
except Token.DoesNotExist:
pass # AnonymousUser
return None
class TokenAuthMiddleware:
def __init__(self, app):
# Store the ASGI application we were passed
self.app = app
async def __call__(self, scope, receive, send):
user = await get_user_from_headers_or_queries(scope)
if user is not None:
scope["user"] = user
return await self.app(scope, receive, send)
# Handy shortcut for applying all three layers at once
def TokenAuthMiddlewareStack(inner):
"""
middleware to support websocket ssh connection
from both session or by queries
"""
return TokenAuthMiddleware(AuthMiddlewareStack(inner))
urls.py
;
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.security.websocket import AllowedHostsOriginValidator
from yourproject.utils.middleware import TokenAuthMiddlewareStack
application = ProtocolTypeRouter({
'websocket': AllowedHostsOriginValidator(
TokenAuthMiddlewareStack(
URLRouter(...)
)
)
})
To support headers
Authorization
andtoken
from query string, and also session as well.from urllib import parse from rest_framework.authtoken.models import Token from channels.db import database_sync_to_async from channels.auth import AuthMiddlewareStack @database_sync_to_async def get_user_from_headers_or_queries(scope): """ function to get the `User` object from his headers or queries as well. :return object of `User` or None """ try: headers = dict(scope["headers"]) except KeyError as error: headers = {} logger.error(error) try: params = dict(parse.parse_qsl(scope["query_string"].decode("utf8"))) except KeyError as error: params = {} logger.warning(error) token_key = None token_is_found = False if b"authorization" in headers: # 1. get from authorization headers token_name, token_key = headers[b"authorization"].decode().split() if token_name == "Token": # nosec: B105 (just checking the token name) token_is_found = True else: # 2. get from token params token_key = params.get("token") token_is_found = True if token_key else False if token_is_found: try: token = Token.objects.get(key=token_key) return token.user except Token.DoesNotExist: pass # AnonymousUser return None class TokenAuthMiddleware: def __init__(self, app): # Store the ASGI application we were passed self.app = app async def __call__(self, scope, receive, send): user = await get_user_from_headers_or_queries(scope) if user is not None: scope["user"] = user return await self.app(scope, receive, send) # Handy shortcut for applying all three layers at once def TokenAuthMiddlewareStack(inner): """ middleware to support websocket ssh connection from both session or by queries """ return TokenAuthMiddleware(AuthMiddlewareStack(inner))
urls.py
;from channels.routing import ProtocolTypeRouter, URLRouter from channels.security.websocket import AllowedHostsOriginValidator from yourproject.utils.middleware import TokenAuthMiddlewareStack application = ProtocolTypeRouter({ 'websocket': AllowedHostsOriginValidator( TokenAuthMiddlewareStack( URLRouter(...) ) ) })
first you need to change the order.
# Handy shortcut for applying all three layers at once
def TokenAuthMiddlewareStack(inner):
"""
middleware to support websocket ssh connection
from both session or by queries
"""
return AuthMiddlewareStack(TokenAuthMiddleware(inner)) #<---------- need to change the order.
and second getting token from a query string is not a good idea.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
this is the code that worked for me in jwt auth in channels 3
cheers I didn't remove the useless imports I just found the solution and sharing it with you