self.scope['user'] in Django channels keeps showing up as AnonymousUser

Viewed 2917

When I log in to my front end, my self.scope['user'] call in my friends.consumer.py in django channels returns AnonymousUser but when login and call self.scope['user'] in my chat.consumer.py it shows up as the logged in user. For some reason the scope['user'] in one of my apps work and the other one does not. I do not understand what the problem is. I have the routing set up in my django project as this

routing.py

from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
import chat.routing
import friends.routing

application = ProtocolTypeRouter ({
    'websocket': AuthMiddlewareStack(
        URLRouter(
        friends.routing.websocket_urlpatterns + chat.routing.websocket_urlpatterns
        )
    )
})

I structure my second consumer.py similarly to my first consumer.py. This is my consumer.py where the scope['user'] works (I didn't need the scope['user'] in the first consumer but I just wanted to test to see if it works

chat.consumer.py

class ChatConsumer(WebsocketConsumer):
  ...
   def connect(self):
    print(self.scope['user'])
    self.room_name = self.scope['url_route']['kwargs']['room_name']
    self.room_group_name = 'chat_%s' % self.room_name

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

    self.accept()

This code is the consumer where my scope['user'] shows up as anonymous user even after I have logged in.

friends.consumers.py

class FriendRequestConsumer(JsonWebsocketConsumer):
    def connect(self):
        user = self.scope['user']
        grp = 'notifications_{}'.format(user.username)
        self.accept()
        async_to_sync(self.channel_layer.group_add(grp, self.channel_name))

Here are also my routing.py for each app

friends.routing.py

from django.urls import re_path

from . import consumers

websocket_urlpatterns = [
    re_path(r'^ws/friend-request-notification/$', consumers.FriendRequestConsumer),
]

chat.routing.py

from django.urls import re_path

from . import consumers

websocket_urlpatterns = [
    re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer),
]

I was able to connected to the websocket for both of them in my reactjs frontend. I know that the AuthMiddlewareStack allows us to pull the scope['user'], I just don't understand why one works and one does. Can it be that I did not connect to the websocket properly in the frontend or I am missing something on one of my consumers? I appreciate the help and thanks in advance.

For connecting to the websocket in my js, I made a chat.js file and a notifications.js

chat.js

class Chat extends React.Component{
  ... 
   initialiseChat() {
this.waitForSocketConnection(()=> {
  WebSocketInstance.fetchMessages(
    this.props.username,
    this.props.match.params.id
   )
  })
WebSocketInstance.connect(this.props.match.params.id)
}
constructor(props){
  super(props)
  this.initialiseChat()
}

notifications.js

class Notifications extends React.Component{
   ...
  initialiseNotification(){
  this.waitForSocketConnection(() => {
    NotificationWebSocketInstance.fetchFriendRequests(
     this.props.id
  )
  })
  NotificationWebSocketInstance.connect()
}

constructor(props){
  super(props)
  this.initialiseNotification()
}

Here are my websocket actions:

webosocket.js (this connect function gets called in the chat.js)

class WebSocketService {
  ...
  connect(chatUrl) {
const path ='ws://127.0.0.1:8000/ws/chat/'+chatUrl+'/';
console.log(path)
this.socketRef = new WebSocket(path)
this.socketRef.onopen = () => {
  console.log('websocket open')

}
...
const WebSocketInstance = WebSocketService.getInstance();
export default WebSocketInstance;

Here is the websocket for the notification.js

notificationWebsocket.js

class WebSocketNotifications {
 ...
 connect(){
 const path = 'ws://127.0.0.1:8000/ws/friend-request-notification/'
 console.log(path)
 this.socketRef = new WebSocket(path)
 this.socketRef.onopen = () =>{
   console.log('websocket open')
 }
 ...
 const NotificationWebSocketInstance = 
  WebSocketNotifications.getInstance();

 export default NotificationWebSocketInstance;

And here are the routes.js

class BaseRouter extends React.Component {
  <Route exact path = '/chat/:id' render={(props) => <Chat {...props} 
      {...this.props} isAuthenticated={this.props.isAuthenticated} />}  
      />
    <Route exact path = '/notifications/' render={(props) => 
       <Notifications {...props} {...this.props} isAuthenticated= 
       {this.props.isAuthenticated} />}  />
4 Answers

You can see between this two re_path

websocket_urlpatterns = [
    re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer),
]

you used (?P<room_name>\w+) for chatconsumers.

websocket_urlpatterns = [
    re_path(r'^ws/friend-request-notification/$', consumers.FriendRequestConsumer),
]

But in case of FriendRequestConsumer you didn't used. In my case I had same problem and I have added a room name for no reason and can pass any string in that room name . But anyhow i can access self.scope['user'] now.

my asgi.py is like that

ws_patterns=[
    re_path(r'ws/notification/(?P<room_name>\w+)/$', NotificationConsumer.as_asgi()) # NEW CHANGE
    # path('ws/notification/', NotificationConsumer.as_asgi()), # as_PREVIOUS
]
application=ProtocolTypeRouter({
    "http": get_asgi_application(),
    "websocket": AllowedHostsOriginValidator(
        AuthMiddlewareStack(
            URLRouter(ws_patterns)
        ),
    ),
})

There must be some issues with your token middleware

Although this might not be the source of the problem in this instance, this might help somebody stumbling across this question like me.

My token authentication system used django_rest_knox and the authentication and login procedure were working well up until trying to access the user value in self.scope['user'] which was returning AnonymousUser.

Turns out it was as simple as adding the login method from django.

    # api.py

    from django.contrib.auth import login

    class LoginAPI(generics.GenericAPIView):
        permission_classes = (permissions.AllowAny, )
        serializer_class = LoginSerializer

        def post(self, request, *args, **kwargs):

            serializer = self.get_serializer(data=request.data)
            serializer.is_valid(raise_exception=True)
            user = serializer.validated_data
            login(request, user) # <- This was missing
            return Response({
                "user": UserSerializer(user, 
            context=self.get_serializer_context()).data,
                "token": AuthToken.objects.create(user)[1]
            })

consumers.FriendRequestConsumer-> consumers.FriendRequestConsumer.as_asgi()

must

websocket_urlpatterns = [ re_path(r'^ws/friend-request-notification/$', consumers.FriendRequestConsumer.as_asgi()), ]

Related