self.scope['user'] always returns AnonymousUser in websocket

Viewed 28

I have researched similar questions but can't find an answer that works for me.

I would like to get the username or user_id from a session when a user connects to a websocket.

This is what I have in consumers.py:

class PracticeConsumer(AsyncConsumer):
    async def websocket_connect(self, event):
        print('session data', self.scope['user'])
        await self.send({"type": "websocket.accept", })
    ...
    @database_sync_to_async
    def get_user(self, user_id):
        try:
            return User.objects.get(username=user_id).pk
        except User.DoesNotExist:
            return AnonymousUser()

This is my asgi.py:

""" ASGI config for restapi project.

It exposes the ASGI callable as a module-level variable named application.

For more information on this file, see https://docs.djangoproject.com/en/4.0/howto/deployment/asgi/

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

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

and the user login function + token sent when user logs in:

class CustomTokenObtainPairSerializer(TokenObtainPairSerializer):

    def validate(self, attrs):
        authenticate_kwargs = {
            self.username_field: attrs[self.username_field],
            "password": attrs["password"],
        }
        try:
            authenticate_kwargs["request"] = self.context["request"]
        except KeyError:
            pass
        user = authenticate(**authenticate_kwargs)
        if not user:
            return {
                'user': 'Username or password is incorrect',
            }
        token = RefreshToken.for_user(user)
        # customizing token payload
        token['username'] = user.username
        token['first_name'] = user.first_name
        token['last_name'] = user.last_name
        token['country'] = user.profile.country
        token['city'] = user.profile.city
        token['bio'] = user.profile.bio
        token['photo'] = json.dumps(str(user.profile.profile_pic))

        user_logged_in.send(sender=user.__class__, request=self.context['request'], user=user)

        if not api_settings.USER_AUTHENTICATION_RULE(user):
            raise exceptions.AuthenticationFailed(
                self.error_messages["no_active_account"],
                "no_active_account",
            )

        return {
            'refresh': str(token),
            'access': str(token.access_token),
        }

Whenever I print out self.scope['user'] upon connecting, I get AnonymousUser

UPDATE

I tried writing some custom middleware to handle simple JWT authentication:

@database_sync_to_async
def get_user(validated_token):
    try:
        user = get_user_model().objects.get(id=validated_token["user_id"])
        print(f"{user}")
        return user

    except User.DoesNotExist:
        return AnonymousUser()


class JwtAuthMiddleware(BaseMiddleware):
    def __init__(self, inner):
        self.inner = inner

    async def __call__(self, scope, receive, send):
        close_old_connections()
        token = parse_qs(scope["query_string"].decode("utf8"))["token"][0]
        try:
            UntypedToken(token)
        except (InvalidToken, TokenError) as e:
            print(e)
            return None
        else:
            decoded_data = jwt_decode(token, settings.SECRET_KEY, algorithms=["HS256"])
            print(decoded_data)
            scope["user"] = await get_user(validated_token=decoded_data)
        return await super().__call__(scope, receive, send)


def JwtAuthMiddlewareStack(inner):
    return JwtAuthMiddleware(AuthMiddlewareStack(inner))

However, this gives me this error:

  File "C:\Users\15512\Desktop\django-project\peerplatform\signup\middleware.py", line 37, in __call__
    token = parse_qs(scope["query_string"].decode("utf8"))["token"][0]
KeyError: 'token'
0 Answers
Related