1

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.

Sumeth Sathnindu
  • 323
  • 4
  • 15
  • Which version of OS, Edge and Chrome are you using? Could you please use [`WebSocket.onerror`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/onerror) to check the error details? According to [this answer](https://stackoverflow.com/questions/19304157/getting-the-reason-why-websockets-closed-with-close-code-1006/19305172#19305172), code 1006 is a low level error with WebSocket itself which is in your code and implementation. – Yu Zhou Dec 07 '21 at 08:47
  • My os is `Windows 10 Pro OS build 19042.1288` and Edge browser version is `96.0.1054.43` (latest). I tried printing the error with `WebSocket.onerror` but it doesn't give much information. @YuZhou – Sumeth Sathnindu Dec 07 '21 at 17:12
  • I made a simple sample using [NodeJS WebSocket server ws](https://www.npmjs.com/package/ws#sending-and-receiving-text-data), and it works well in Edge and Chrome with localhost. I think the issue might be related to django-channels. You can refer to [this thread](https://github.com/django/channels/issues/1407) to see if it helps. Besides, does your domain use `http` or `https`? – Yu Zhou Dec 08 '21 at 08:59
  • @YuZhou Thanks for the sample. My domain uses `http`. – Sumeth Sathnindu Dec 08 '21 at 09:11
  • If you're using the simple NodeJS WebSocket sample, will it work in Edge and Chrome? Does localhost domain work well in Edge and Chrome? It's hard to find the issue without a reproducible sample. If possible, you can provide the steps and a minimal code snippet which can reproduce the issue. – Yu Zhou Dec 10 '21 at 03:07
  • @YuZhou I found the cause of the failure. Please check out the update. – Sumeth Sathnindu Dec 10 '21 at 17:26
  • @YuZhou I found the answer, check below. Thank you for your help. :-) – Sumeth Sathnindu Dec 12 '21 at 16:29

1 Answers1

0

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()
Nimantha
  • 6,405
  • 6
  • 28
  • 69
Sumeth Sathnindu
  • 323
  • 4
  • 15