Search code examples
djangowebsocketdjango-channels

Django Channel Custom Authentication Middleware __call__() missing 2 required positional arguments: 'receive' and 'send'


I am writing a custom authentication middleware for django channels

class TokenAuthMiddleware:
    def __init__(self, inner):
        # Store the ASGI application we were passed
        self.inner = inner

    def __call__(self, scope):

        return TokenAuthMiddlewareInstance(scope, self)


class TokenAuthMiddlewareInstance:

    def __init__(self, scope, middleware):
        self.middleware = middleware
        self.scope = dict(scope)
        self.inner = self.middleware.inner

    async def __call__(self, receive, send):
        ## my logic to get validate user and store the user in user data
        ...
        ...
        ...
        self.scope['user'] = user_data
        inner = self.inner(self.scope)
        return await inner(receive, send)

but on trying to connect to web socket from front end I get the following error

TypeError: __call__() missing 2 required positional arguments: 'receive' and 'send'

Solution

  • For your reference: https://channels.readthedocs.io/en/stable/releases/3.0.0.html

    change from in routing.py

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

    to Consumers now have an as_asgi() class method you need to call when setting up your routing:

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

    then if you need custom authentication https://channels.readthedocs.io/en/stable/topics/authentication.html#custom-authentication

    from channels.auth import AuthMiddlewareStack
    from channels.db import database_sync_to_async
    from django.contrib.auth import get_user_model
    
    User = get_user_model()
    
    @database_sync_to_async
    def get_user(user_id):
        try:
            return User.objects.get(id=user_id)
        except User.DoesNotExist:
            return AnonymousUser()
            
    class QueryAuthMiddleware:
        """
        Custom middleware (insecure) that takes user IDs from the query string.
        """
            
        def __init__(self, app):
            # Store the ASGI application we were passed
            self.app = app
            
        async def __call__(self, scope, receive, send):
            # Look up user from query string (you should also do things like
            # checking if it is a valid user ID, or if scope["user"] is already
            # populated).
            scope['user'] = await get_user(int(scope["query_string"]))
            
            return await self.app(scope, receive, send)
    TokenAuthMiddlewareStack = lambda inner: QueryAuthMiddleware(AuthMiddlewareStack(inner))   
    

    use requirements.txt as following list, and also download package in this order

    Django==3.0.8
    djangorestframework==3.11.0
    websocket-client==0.57.0
    redis==3.5.3
    asgiref==3.2.10
    channels-redis==2.4.2
    channels==3.0.1