Search code examples
google-chromewebsocketmicrosoft-edgedjango-channels

Websockets to localhost fails in chromium based browsers


I'm trying to build a chat website that uses vue.js as the frontend and django as the backend. It works fine in Firefox but in MS edge and Google Chrome, Websocket is failing. I get this message in the browser console.

WebSocket connection to 'ws://127.0.0.1:8000/inbox/' failed

I use django-channels so in the python console it prints

WebSocket CONNECT /inbox/ [127.0.0.1:4965]
WebSocket DISCONNECT /inbox/ [127.0.0.1:4965]

When I print out the error code I get 1006

Close Code 1006 is a special code that means the connection was closed abnormally (locally) by the browser implementation.

My WebSocket code

new WebSocket(url, authToken) // I use sec-websocket-protocol to transfer the authentication token

What am I doing wrong or is it a problem with the browser?

-- Updated

new WebSocket("ws://127.0.0.1:8000/inbox/", "authtoken");

So, I'm sending the authentication token in the second Websocket protocol and authenticating the user in the backend using middleware. When I remove that protocol and accept unauthenticated users in the backend ->

new WebSocket("ws://127.0.0.1:8000/inbox/");

-> the WebSocket connects just fine. The problem is when sending the second Websocket protocol.


Solution

  • I added 'Token' as a subprotocol to the WebSocket and authToken along with it. Then in the backend, I accepted it with the same subprotocol name:

    My Websocket

    new WebSocket(url, ["Token", authToken]) // Sending 'Token' and authToken as subprotocols
    

    Token Auth Middleware

    from asgiref.sync import sync_to_async
    from django.contrib.auth.models import AnonymousUser
    
    from rest_framework.authtoken.models import Token
    
    
    @sync_to_async
    def get_user(headers):
        try:
            token_key = headers[b"sec-websocket-protocol"].decode().split(', ')[1]
            token = Token.objects.get(key=token_key)
    
            return token.user
        except Token.DoesNotExist:
            return AnonymousUser()
    
    
    class TokenAuthMiddleware:
        def __init__(self, app):
            self.app = app
    
        async def __call__(self, scope, receive, send):
            headers = dict(scope["headers"])
    
            if b"sec-websocket-protocol" in headers:
                scope['user'] = await get_user(headers)
    
            return await self.app(scope, receive, send)
    

    Consumers.py

    from channels.generic.websocket import AsyncWebsocketConsumer
    
    class Consumer(AsyncWebsocketConsumer):
        async def connect(self):
            self.user = self.scope['user']
            if self.user.is_authenticated:
                self.room_name = await self.get_user_id(self.user)
                self.room_group_name = self.room_name
    
                await self.channel_layer.group_add(self.room_group_name, self.channel_name)
                await self.accept('Token')  # Here I'm giving a subprotocol to the self.accept function
    
            else:
                await self.close()