5

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} />}  />
hafkacc
  • 51
  • 1
  • 3
  • Can you share the js where you are connecting to the web sockets? – Iain Shelvington Apr 07 '20 at 07:52
  • Yea of course, I connect to the websocket in my chat.js file and notifications.js file. I edited my question on top and include the files. I have a login function in redux that connects to the rest-auth backend as well. I think that is how I got the self.scope['user'] to work for the chat.consumers.py but for some reason it does not work for the friends.consumers.py. Thank you for your help and correct me if I am wrong about anything. – hafkacc Apr 07 '20 at 16:51

4 Answers4

1

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)
        ),
    ),
})
0

There must be some issues with your token middleware

0

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]
            })
Jamie T
  • 56
  • 1
  • 6
0

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

must

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

  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jun 16 '22 at 19:55