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.
This step assumes that -
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)
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)
)
}
)
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