self.scope['user'] always returns Anonymous User

self.scope[‘user’] always returns Anonymous User even the user is authenticated

Middlewares included

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

asgi.py

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Chat_App.settings')

application =  ProtocolTypeRouter({
    "http" : get_asgi_application(),
    "websocket": AllowedHostsOriginValidator(
        AuthMiddlewareStack(
            URLRouter(
                Chat.routings.websocket_urlpatterns
            )
        )
    ),  
})

I am using AsyncJsonWebsocketConsumer
I did all the configuration like adding daphne in INSTALLED_APPS, changing to as an application, and adding the channels layer.

I used the Django pre-built authentication class view to authenticate the user.

django==4.0.0
channels==4.0.0
daphne==4.0.0

Please help!

Are you sure you’re authenticating before the websocket gets opened? (That’s a common mistake. You don’t want to open the websocket on your initial page or the login page itself. You want to open the websocket on the landing page retreived after the login has been completed.)

Are you using a JavaScript framework for the front end?

Please provide more details about the specifics of how you’re opening the websocket and doing the authentication.

I even thought the same that maybe I am not authenticated while calling the consumer so I tried 2 methods to check whether I am authenticated or not

  1. Checked whether the user is authenticated from the view that called just before the WebSocket connection
@login_required
def ChatRoom(request,grpname):
    group = Group.objects.get(name=grpname)
    chats = Chat.objects.filter(group=group)
    profile = UserProfile.objects.get(user=request.user)
    print('You user: ',request.user.username)
    return render(request,'chat/chatroom.html',{'group':group,'chats':chats,'profile':profile})

Output

HTTP GET /accounts/login/?next=/ 200 [0.16, 127.0.0.1:60223]
HTTP POST /accounts/login/?next=/ 302 [0.92, 127.0.0.1:60223]
HTTP GET / 200 [0.17, 127.0.0.1:60223]
You user:  ugthesep

This means that the user is authenticated before the consumer

  1. Checked whether the user is authenticated inside the consumer by accessing the user through the session key(session key is obtained by the Chrome inspect mode application)

So, I created a get user method inside the AsyncJsonWebsocket and passed the session key obtained through the chrome browser current tab

def get_user(self):
        session_key = 'zdpagvtfetm7h4gsk996c5tl8ecmoc11'
        session =  Session.objects.get(session_key=session_key)
        session_data = session.get_decoded()
        uid =  session_data.get('_auth_user_id')
        user = User.objects.get(id=uid)
        print('User obtained through session key',user)
        return user

Screenshot of the session key obtained from chrome browser localhost tab

The output of the code:

HTTP GET /chat/india/ 200 [0.13, 127.0.0.1:60586]
WebSocket HANDSHAKING /ws/chat/india [127.0.0.1:60589]
Websocket connect!
User obtained through session key ugthesep
WebSocket CONNECT /ws/chat/india [127.0.0.1:60589]

That means the user is still login in the consumer also

Yes, I am using Javascript in frontend

Let me share all the codes

routing.py

from .consumers import ChatConsumer
from django.urls import path

websocket_urlpatterns = [
    path('ws/chat/<str:grpname>',ChatConsumer.as_asgi())
]

consumers.py

class ChatConsumer(AsyncJsonWebsocketConsumer):
    
    async def connect(self):
        print("Websocket connect!")
        await database_sync_to_async(self.get_user)()
        self.grpname = self.scope['url_route']['kwargs']['grpname']
        await self.channel_layer.group_add(self.grpname,self.channel_name)
        await self.accept()
    
    async def receive_json(self,content,**kwargs):
        if content['msg'] != None:
            await database_sync_to_async(save_to_database)(self,content)
            await self.channel_layer.group_send(self.grpname,
              {
                    'type' : 'chat.message',
                    'msg': content['msg']
              })
    
    async def disconnect(self, close_code):
        await self.channel_layer.group_discard(self.grpname,self.channel_name)
        self.close()
        raise StopConsumer()
    
    async def chat_message(self,event):
        msg = json.dumps({'msg':event['msg']})
        await self.send(msg)
    
    def get_user(self):
        session_key = 'zdpagvtfetm7h4gsk996c5tl8ecmoc11'
        session =  Session.objects.get(session_key=session_key)
        session_data = session.get_decoded()
        uid =  session_data.get('_auth_user_id')
        user = User.objects.get(id=uid)
        print('User obtained through session key',user)
        return user


# Outside the consumer class
def save_to_database(self,content):
        group = Group.objects.get(name=self.grpname)
        profile = UserProfile.objects.get(user=self.user)
        chat=Chat.objects.create(message=content['msg'],group=group,send_by=profile)
        chat.save()
    

javascript used for connection building

<script>
        const grpname = JSON.parse(document.getElementById('grpname').textContent)
        ws = new WebSocket('ws://127.0.0.1:8000/ws/chat/' + String(grpname))

        ws.onopen = function(event){
            console.log("Connection opens",event)
        }
        ws.onmessage = function(event){
            data = JSON.parse(event.data)
            msg_html = ''
            document.getElementById('chat-box').innerHTML += data.msg + '\n'
        }
        ws.onerror = function(event){
            console.log("Error occurred: ",event)
        }
        
        ws.onclose = function(event){
            console.log("Connection Closed: ",event)
        }

        document.getElementById('msg-send').onclick = 
        function(event){
            console.log("msg-send")
            const msg = 
            JSON.stringify({'type':'message','msg':document.getElementById('msg').value,
            'user':JSON.parse(document.getElementById('user').textContent)})
            document.getElementById('msg').value = ''
            ws.send(msg)
        }

    </script>

I hope this helped you to understand the problem more deeply.

This JavaScript script at the bottom of your post - in which page is it being loaded? (What template is it in? What view is rendering it and sending it as a response?)

You posted this:

What was happening prior to this? (What requests were made before the login?)

Before login user enters on the home page that is localhost:8000 which shows all the current group available for chatting

Here the code of the page appear before login
view.py

class Home(ListView):
    template_name = 'Home/home.html'
    model = Group
    context_object_name = 'groups'
    paginate_by = 4
    paginate_orphans = 2
    paginator_class = Paginator

    def get_queryset(self):
        return super().get_queryset().all().order_by('name')

home.html

{% extends 'navigation.html' %}
{% load group_tags %}
{% block title %}GupShup Groups{% endblock %}

{% block content %}
    <div class="p-3 mb-1">
        <div class="d-flex flex-row justify-content-around bg-dark bg-gradient text-light p-3 rounded" style="--bs-bg-opacity: .8;">
            <div style="display: inline-block;" title="Name of the group">GupShup Name</div> 
                <div style="display: inline-block;" title="Total members of the group">GupShup Members</div> 
                <div style="display: inline-block;" title="Type of Group">GupShup Type</div> 
            </div>
            
    </div>
        {% for group in groups %}
        <div class="p-3 mb-2">
            <div class="d-flex flex-row justify-content-around bg-info text-dark p-3 rounded">
                <div class="border border-info" style="display: inline-block;"><a href="{% url 'chat-room' group.name %}" style="text-decoration: none; color: black;">{{group.name|title}}</a></div> 
                    <div class="border border-info" style="display: inline-block;">{{group.name|count}}</div> 
                    <div class="border border-info" style="display: inline-block;">{{group.type}}</div> 
                    {% if group.name|is_joined:user %}
                    
                    <div><a class="btn btn-success disabled" href="{% url 'join-group' group.name %}" role="button">Joined</a></div>
                    {% else %}
                    <div><a class="btn btn-primary" href="{% url 'join-group' group.name %}?next={{request.get_full_path}}" role="button">Join</a></div>
                    {% endif %}
                </div>
                
        </div>
        {% endfor %}
        <ul class="pagination justify-content-center">
            {% if page_obj.has_previous %}
            <a class="page-link" href="?page={{page_obj.previous_page_number}}">Previous</a>
            {% else %}
            <li class="page-item disabled">
              <a class="page-link">Previous</a>
            </li>
            {% endif %}
            <li class="page-item"><a class="page-link active" >{{page_obj.number}}</a></li>
            {% if page_obj.has_next %}
            <a class="page-link" href="?page={{page_obj.next_page_number}}">Next</a>
            {% else %}
            <li class="page-item disabled">
              <a class="page-link">Next</a>
            </li>
            {% endif %}
          </ul>
{% endblock %}

Now let’s talk about this output the view and the template that was used are what I told you that I checked whether user is login or not from the view which called just before the consumer.

So this actually calls a view which fetch the passed group chats and establish the javascript connection with the consumer to talk real-time

Here the code is

views.py

@login_required
def ChatRoom(request,grpname):
    group = Group.objects.get(name=grpname)
    chats = Chat.objects.filter(group=group)
    profile = UserProfile.objects.get(user=request.user)
    return render(request,'chat/chatroom.html',{'group':group,'chats':chats,'profile':profile})

chatroom.html

{% extends 'navigation.html' %}
{% block title %}GupShup Chats | {{grpname}}{% endblock %}
{% load static %}
{% block content %

{{group.name|json_script:"grpname"}}
    <div>Group name : {{group.name}}</div>

 <textarea name="chatbox" id="chat-box" cols="30" rows="10">
                {% for chat in chats %}
                {{chat.message}}
                {% endfor %}
            </textarea>
            <input type="text" name="msg" id="msg">
            <input type="button" value="send" id="msg-send">
<script>
        const grpname = JSON.parse(document.getElementById('grpname').textContent)
        ws = new WebSocket('ws://127.0.0.1:8000/ws/chat/' + String(grpname))

        ws.onopen = function(event){
            console.log("Connection opens",event)
        }
        ws.onmessage = function(event){
            data = JSON.parse(event.data)
            msg_html = ''
            document.getElementById('chat-box').innerHTML += data.msg + '\n'
        }
        ws.onerror = function(event){
            console.log("Error occurred: ",event)
        }
        
        ws.onclose = function(event){
            console.log("Connection Closed: ",event)
        }

        document.getElementById('msg-send').onclick = 
        function(event){
            console.log("msg-send")
            const msg = 
            JSON.stringify({'type':'message','msg':document.getElementById('msg').value,
            'user':JSON.parse(document.getElementById('user').textContent)})
            document.getElementById('msg').value = ''
            ws.send(msg)
        }

    </script>

navigation.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4" crossorigin="anonymous"></script>
    <link rel="shortcut icon" type="image/png" href="https://bit.ly/3BVcGAU"/>
    <title>{% block title %} {% endblock %}</title>
</head>
<body>
    <nav class="navbar bg-light">
        <div class="container-fluid">
            <a class="navbar-brand" href="{% url 'home' %}">
                <img src="https://bit.ly/3BVcGAU" alt="Logo" width="30" height="28" class="d-inline-block align-text-top">
                GupShup
              </a>
              <div class="d-flex flex-row">
                <form class="d-flex" role="search" style="margin-right: 4rem;" action="{% url 'search-group' %}" method="get">
                    <input class="form-control me-2" type="search" name='expression' placeholder="Search" aria-label="Search" value="{{expression}}">
                    <button class="btn btn-outline-success" type="submit">Search</button>
                  </form>
                 {% if user.is_authenticated %}
                 <a href="{% url 'profile' %}" style="margin-right: 2rem;">{{user.username}}</a>
                 {% else %}
                 <form action="{% url 'signup' %}" method="get"><button type="submit" class="btn btn-outline-dark" style="margin-right: 0.5rem;">Sign Up</button></form>
                 <form action="{% url 'login' %}" method="get"><button type="submit" class="btn btn-outline-dark">Login</button></form>
                 {% endif %}
              </div>
        </div>
      </nav>
      {% block content %}
      {% endblock %}
</body>
</html>

In summary:
Javascript use chatroom.html whose url is localhost:8000/chat/str:grpname which loads after the user click on the group from home page and uses ChatRoom view available in Chat application inside the project.

In addition let me provide you the authentication code

So, actually all authentication code are in account app inside the project

Accounts/views.py

from django.shortcuts import render
from .forms import SignupForm
from django.views.generic.edit import CreateView

# Create your views here.

class SignUpView(CreateView):
    form_class = SignupForm
    success_url='/'
    template_name = 'registration/signup.html'

urls.py

from django.urls import path,include
from django.contrib.auth.views import LoginView,LogoutView
from .forms import LoginForm
from .views import SignUpView
urlpatterns = [
    path('login/',LoginView.as_view(template_name = 'registration/login.html',authentication_form=LoginForm),name='login'),
    path('logout/',LogoutView.as_view(),name='logout'),
    path('signup/',SignUpView.as_view(),name='signup')

]

forrms.py

from django import forms
from django.contrib.auth.forms import UserCreationForm,AuthenticationForm
class LoginForm(AuthenticationForm):
    username = forms.CharField(widget=forms.TextInput(
        attrs={'class': 'form-control', 'placeholder': 'Enter your username'}),label_suffix='')
    password = forms.CharField(widget=forms.PasswordInput(
        attrs={
            'class': 'form-control','placeholder': 'Enter your password'}),label_suffix='')

class SignupForm(UserCreationForm):
    username = forms.CharField(widget=forms.TextInput(
        attrs={'class': 'form-control', 'placeholder': 'Enter your username'}),label_suffix='')
    password1 = forms.CharField(widget=forms.PasswordInput(
        attrs={
            'class': 'form-control','placeholder': 'Enter your password'}),label_suffix='',label='Password')
    password2 = forms.CharField(widget=forms.PasswordInput(
        attrs={
            'class': 'form-control','placeholder': 'Enter your password'}),label_suffix='',label='Confirm Password')

That helps explain a lot - first thing that jumps out at me is:

You’re showing that the user is entering localhost.

However, within your javascript, you have:

Which is actually not the same host domain from the perspective of the browser. Cookies being returned to you from localhost will not be shared with 127.0.0.1. (Yes, they may generally refer to the same IP address, but the browser doesn’t know that.)

2 Likes

Thanks a lot, sir.

It worked :slight_smile:

Today I learned one more thing and I will always keep this in my mind.