Using Django DRF JWT Authentication with Django Channels

In this tutorial, you will learn how to use Django DRF JWT authentication with Django Channels. You will learn how to configure Django DRF to use JWT authentication, and how to use Django Channels to send and receive JWT tokens over WebSockets.

This is for people who are already using django-rest-framework-simplejwt for Django REST Framework user authentication and want to use the same JWT token generated by django-rest-framework-simplejwt to authenticate users with Channels.

Sending the token over WebSocket from client to server

This step assumes that -

  • User is already authenticated to use Django DRF using JWT
  • A token is present on client side

We can only make use of querystring to send the token while opening the socket.

Create a new WebSocket using the endpoint -

// Retrieve your token on client side
let token = retrieveToken()
let endpoint = "ws://yourwebsite.com/path/"

// Create new WebSocket
let socket = new WebSocket(endpoint + "?token=" + token)

Handling the token and authenticating the user

To authenticate the user in Channels using JWT, we will need to create a custom authentication middleware for Channels. Make a file yourproject/channelsmiddleware.py -

from django.db import close_old_connections
from rest_framework_simplejwt.tokens import UntypedToken
from rest_framework_simplejwt.exceptions import InvalidToken, TokenError
from jwt import decode as jwt_decode
from django.conf import settings
from django.contrib.auth import get_user_model
from urllib.parse import parse_qs


class TokenAuthMiddleware:
    """
    Custom token auth middleware
    """

    def __init__(self, inner):
        # Store the ASGI application we were passed
        self.inner = inner

    def __call__(self, scope):

        # Close old database connections to prevent usage of timed out connections
        close_old_connections()

        # Get the token
        token = parse_qs(scope["query_string"].decode("utf8"))["token"][0]

        # Try to authenticate the user
        try:
            # This will automatically validate the token and raise an error if token is invalid
            UntypedToken(token)
        except (InvalidToken, TokenError) as e:
            # Token is invalid
            print(e)
            return None
        else:
            #  Then token is valid, decode it
            decoded_data = jwt_decode(token, settings.SECRET_KEY, algorithms=["HS256"])
            print(decoded_data)
            # Will return a dictionary like -
            # {
            #     "token_type": "access",
            #     "exp": 1568770772,
            #     "jti": "5c15e80d65b04c20ad34d77b6703251b",
            #     "user_id": 6
            # }

            # Get the user using ID
            user = get_user_model().objects.get(id=decoded_data["user_id"])

        # Return the inner application directly and let it run everything else
        return self.inner(dict(scope, user=user))

Now use this middleware in yourproject/routing.py file -

from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
import yourapp.routing
from .channelsmiddleware import TokenAuthMiddleware

application = ProtocolTypeRouter(
    {
        # (http->django views is added by default)
        "websocket": TokenAuthMiddleware(
            URLRouter(yourapp.routing.websocket_urlpatterns)
        )
    }
)

Accessing the User in consumer

You can now access the user in yourapp/consumers.py like -

from channels.generic.websocket import AsyncWebsocketConsumer

class FooConsumer(AsyncWebsocketConsumer):
    async def websocket_connect(self, event):
        user = self.scope["user"]
        await self.accept()


Happy Coding !!!

Originally published by Code Gabru at hashnode.com

#django #python

Using Django DRF JWT Authentication with Django Channels
4 Likes136.80 GEEK