Django Channels won't ever set scope['user'].username to non null... -_-

I’ve gone through this tutorial twice:

And I’ve gone through the official docs of Django Channels at least enough to get started. Now granted the basic exampled did work on the docs. However, the enhanced version with rooms and online user list from the above-mentioned article is not working.

When the channel consumer calls user = self.scope['user'] it does grab something, however the username is always None (null).

I have the authentication middleware in use:

core/asgi.py

import os

from channels.auth import AuthMiddlewareStack # new import
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application

import chat.routing

os.environ.setdefault(‘DJANGO_SETTINGS_MODULE’, ‘core.settings’)

application = ProtocolTypeRouter({
  'http': get_asgi_application(),
  'websocket': AuthMiddlewareStack(  # new
        URLRouter(
            chat.routing.websocket_urlpatterns
        )
    ),  # new
})

I have the following Consumer code:

import json

from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer

from .models import Room, Message

class ChatConsumer(WebsocketConsumer):

    def __init__(self, *args, **kwargs):
        super().__init__(args, kwargs)
        self.room_name = None
        self.room_group_name = None
        self.room = None
        self.user = None
        self.user_inbox = None

    def connect(self):
        self.room_name = self.scope['url_route']['kwargs']['room_name']
        self.room_group_name = f'chat_{self.room_name}'
        self.room = Room.objects.get(name=self.room_name)
        self.user = self.scope['user']
        self.user_inbox = f'inbox_{self.user.username}'

        # connection has to be accepted
        self.accept()

        # join the room group
        async_to_sync(self.channel_layer.group_add)(
            self.room_group_name,
            self.channel_name,
        )

        users = [user.username for user in self.room.online.all()]
        
        # send the user list to the newly joined user
        self.send(json.dumps({
            'type': 'user_list',
            'users': users,
        }))

        if self.user.is_authenticated:
            # create a user inbox for private messages
            async_to_sync(self.channel_layer.group_add)(
                self.user_inbox,
                self.channel_name,
            )

            # send the join event to the room
            async_to_sync(self.channel_layer.group_send)(
                self.room_group_name,
                {
                    'type': 'user_join',
                    'user': self.user.username,
                }
            )
            self.room.online.add(self.user)

    def disconnect(self, close_code):
        async_to_sync(self.channel_layer.group_discard)(
            self.room_group_name,
            self.channel_name,
        )

        if self.user.is_authenticated:
            # delete the user inbox for private messages
            async_to_sync(self.channel_layer.group_add)(
                self.user_inbox,
                self.channel_name,
            )

            # send the leave event to the room
            async_to_sync(self.channel_layer.group_send)(
                self.room_group_name,
                {
                    'type': 'user_leave',
                    'user': self.user.username,
                }
            )
            self.room.online.remove(self.user)

    def receive(self, text_data=None, bytes_data=None):
        text_data_json = json.loads(text_data)
        message = text_data_json['message']

        if not self.user.is_authenticated:
            return
        if message.startswith('/pm '):
            split = message.split(' ', 2)
            target = split[1]
            target_msg = split[2]

            # send private message to the target
            async_to_sync(self.channel_layer.group_send)(
                f'inbox_{target}',
                {
                    'type': 'private_message',
                    'user': self.user.username,
                    'message': target_msg,
                }
            )
            # send private message delivered to the user
            self.send(json.dumps({
                'type': 'private_message_delivered',
                'target': target,
                'message': target_msg,
            }))
            return

        # send chat message event to the room
        async_to_sync(self.channel_layer.group_send)(
            self.room_group_name,
            {
                'type': 'chat_message',
                'user': self.user.username,
                'message': message,
            }
        )
        Message.objects.create(user=self.user, room=self.room, content=message)

    def chat_message(self, event):
        self.send(text_data=json.dumps(event))

    def user_join(self, event):
        self.send(text_data=json.dumps(event))

    def user_leave(self, event):
        self.send(text_data=json.dumps(event))

    def private_message(self, event):
        self.send(text_data=json.dumps(event))

    def private_message_delivered(self, event):
        self.send(text_data=json.dumps(event))

And my Authentication backends look like this:

AUTHENTICATION_BACKENDS = (
    "social_core.backends.github.GithubOAuth2",
    "social_core.backends.twitter.TwitterOAuth",
    "social_core.backends.facebook.FacebookOAuth2",
    "social_core.backends.linkedin.LinkedinOAuth2",
    "social_core.backends.google.GoogleOAuth2",
    "graphql_jwt.backends.JSONWebTokenBackend",
    "django.contrib.auth.backends.ModelBackend",  
)

I’m currently thinking maybe User.username is null and I should use email. Checking that now.

Yes, switching from user.username to user.email seems to work for me for now.

Note, for future reference here, please don’t post images of code. They’re not legible on many devices, and they can’t be quoted or extracted for comments.

When you have a question involving middleware, it’s helpful if you include your middleware settings as the order of middleware is important.

Finally, when you have questions about authentication, it helps to know if you’re using a custom user object or the system default object.

1 Like

@KenWhitesell Please guide me how to insert an image with a smallar avatar. Then the image is optional, I just think it brings a lot more info to the discussion should anyone choose to read it. Everything is highlighted and as I see it no less! I will not post any more images of code until you show me how to do it properly. Thanks.

It really doesn’t.

  • They’re not readable on all devices.

  • Information in an image can’t be searched when someone is here looking for questions and answers previously asked.

  • People trying to answer questions can’t copy/paste code from an image into their response to address specific lines of code.

  • If you are copying the code into the body of your message, then it’s just duplicated effort on your part.

Images are worthwhile if you’re describing an issue concerning a layout of a page, or if you’ve providing a diagram or picture that adds information to the code being posted.

1 Like