Search code examples
djangodjango-channels

Django Channels 2: two authentication methods


how i can implement multiple authentication methods for my django channels app? I mean, i have a Token based auth and a AuthMiddlewareStack.

How i can mantain these two authentication in order to manage the in session django auth for my internal application websocket data consumer/notify/etc... and adopt the websocket token authentication for the third-party apps?

Here is my routing.py:

application = ProtocolTypeRouter({
    # (http->django views is added by default)
    'websocket': AuthMiddlewareStack(
        URLRouter(
            app.routing.websocket_test
        )
    ),
    "websocket": TokenAuthMiddlewareStack(
        URLRouter([
            app.routing.websocket_test
        ]),
    )
})

Thanks.


Solution

  • I've solved the problem implementing an Universal Authentication Middleware stack, the docs of Django Channels, lacks about these examples.

    In any case, i've implemented a hybrid authentication stack that supports: Session, Token header and Token urlpath Authentication methods, works flawlessly:

    WSTokenAuthMiddleware.py:

    from urllib.parse import parse_qs
    
    from channels.auth import AuthMiddlewareStack
    from django.utils import timezone
    
    from rest_framework.authtoken.models import Token
    from django.contrib.auth.models import AnonymousUser
    from django.db import close_old_connections
    
    
    class WSTokenAuthMiddleware:
        """
        Token [Querystring/Header] authorization middleware for Django Channels 2
        """
    
        def __init__(self, inner):
            self.inner = inner
    
        def __call__(self, scope):
            query_string = parse_qs(scope['query_string']) #Used for querystring token url auth
            headers = dict(scope['headers']) #Used for headers token url auth
            if b'token' in query_string:
                try:
                    token_key = query_string[b'token'][0].decode()
                    token = Token.objects.get(key=token_key)
                    scope['user'] = token.user
                    close_old_connections()
                except ApiToken.DoesNotExist:
                    scope['user'] = AnonymousUser()
            elif b'authorization' in headers:
                try:
                    token_name, token_key = headers[b'authorization'].decode().split()
                    if token_name == 'Token':
                        token = Token.objects.get(key=token_key)
                        scope['user'] = token.user
                        close_old_connections()
                except ApiToken.DoesNotExist:
                    scope['user'] = AnonymousUser()
            else:
                pass #Session auth or anonymus
    
            return self.inner(scope)
    
    UniversalAuthMiddlewareStack = lambda inner: WSTokenAuthMiddleware(AuthMiddlewareStack(inner))
    

    Routing.py:

    application = ProtocolTypeRouter({
        "websocket": UniversalAuthMiddlewareStack(
            URLRouter(
                ...
            ),
        ),
    })