Well I had the same problem. First of all you can't do JWT authentication with django channels because the only thing that you can send through your channels is query string
and you can't set header parameters or such thing like http
protocol (especially if your using JavaScript
as your client side). I didn't want to send my token as query string because of security purpose (because every one can see it). So I explain my solution here and maybe it can solve your problem too. I created an API for registering in my socket and in that API I returned a ticket (uuid type) as a response and in the same API I cached this ticket based on a user:
class RegisterFilterAPIView(APIView):
"""
get:
API view for retrieving ticket uuid.
"""
authentication_classes = (JWTAuthentication,)
permission_classes = (IsAuthenticatedOrReadOnly,)
def get(self, request, *args, **kwargs):
ticket_uuid = str(uuid4())
if request.user.is_anonymous:
cache.set(ticket_uuid, False, TICKET_EXPIRE_TIME)
else:
# You can set any condition based on logged in user here
cache.set(ticket_uuid, some_conditions, TICKET_EXPIRE_TIME)
return Response({'ticket_uuid': ticket_uuid})
After this part I sent this ticket as a query string to my socket like:
var endpoint = 'ws://your/socket/endpoint/?ticket_uuid=some_ticket';
var newSocket = new WebSocket(endpoint);
newSocket.onmessage = function (e) {
console.log("message", e)
};
newSocket.onopen = function (e) {
console.log("open", e);
};
newSocket.onerror = function (e) {
console.log("error", e)
};
newSocket.onclose = function (e) {
console.log("close", e)
};
Note that the above codes are written in JS
so you should change it to something else based on your requirements. And finally in my consumer I handled this ticket which is created in my register API:
from urllib.parse import parse_qsl
from django.core.cache import cache
from channels.generic.websocket import AsyncJsonWebsocketConsumer
class FilterConsumer(AsyncJsonWebsocketConsumer):
async def websocket_connect(self, event):
try:
query_string = self.scope['query_string'].decode('utf-8')
query_params = dict(parse_qsl(query_string))
ticket_uuid = query_params.get('ticket_uuid')
self.scope['has_ticket'] = cache.get(ticket_uuid)
if not cache.delete(ticket_uuid): # I destroyed ticket for performance and security purposes
raise Exception('ticket not found')
except:
await self.close()
return
await self.accept()
So now you have a register API (like obtain token API) which is safe and you can generate a token based on your JWT token but make sure your server supports a cache backend service. You can also set self.scope['user']
in your websocket connect method based on your ticket value. I hope this can solve your problem.