0

I currently have the problem, that I need different authentication mechanisms for different Django Channel consumers. One of them should use a custom token middleware while the other should use the standard session-based authentication. How can it be achieved that middlewares can be applied per path?

In the standard Django views this is achieved with def process_view(self, request, view_func, view_args, view_kwargs): as shown in this answer.

# routing.py

application = ProtocolTypeRouter(
    {
        "http": get_asgi_application(),
        "websocket": TokenAuthMiddleware(
            URLRouter(
                [
                    path(
                        "ws-api/v1/farms/controllers/",
                        ControllerConsumer.as_asgi(),
                        name="ws-controller",
                    ),
                    path("ws-api/v1/graphql/", MyGraphqlWsConsumer.as_asgi()),
                ]
            )
        )
    }
)
Moritz
  • 2,987
  • 3
  • 21
  • 34

1 Answers1

0

I found a solution in where two middleware "chains" are created and selected depending on the request path. It required that the routing.py and middlewares be modified as follows. The PathAuthMiddleware is responsible for selecting the chain of middlewares to be applied.

# routing.py

application = ProtocolTypeRouter(
    {
        "http": get_asgi_application(),
        "websocket": PathAuthMiddleware(
            URLRouter(
                [
                    path(
                        TokenAuthMiddleware.controller_ws_path,
                        ControllerConsumer.as_asgi(),
                        name="ws-controller",
                    ),
                    path(
                        "ws-api/v1/graphql/",
                        MyGraphqlWsConsumer.as_asgi(),
                        name="graphql-subscription",
                    ),
                ]
            )
        ),
    }
)

And the middleware responsible for the routing of the requests and the custom token authentication middleware.

# middleware.py

from channels.auth import AuthMiddlewareStack

class TokenAuthMiddleware:
    controller_ws_path = "ws-api/v1/farms/controllers/"

    def __init__(self, app):
         self.app = app

    async def __call__(self, scope, receive, send):
        ...
        return await self.app(scope, receive, send)

class PathAuthMiddleware:
    def __init__(self, app):
        self.controller_app = TokenAuthMiddleware(app)
        self.other_app = AuthMiddlewareStack(app)

    async def __call__(self, scope, receive, send):
        if scope["path"] == f"/{TokenAuthMiddleware.controller_ws_path}":
            return await self.controller_app(scope, receive, send)
        return await self.other_app(scope, receive, send)
Moritz
  • 2,987
  • 3
  • 21
  • 34