django-rest-frameworkdjango-channelsauth-token

How do you authenticate a websocket with token authentication on django channels?


We want to use django-channels for our websockets but we need to authenticate as well. We have a rest api running with django-rest-framework and there we use tokens to authenticate a user, but the same functionality does not seem to be built into django-channels.


Solution

  • If you are using Django Channels 3 you can use this code: https://gist.github.com/AliRn76/1fb99688315bedb2bf32fc4af0e50157

    middleware.py

    from django.contrib.auth.models import AnonymousUser
    from rest_framework.authtoken.models import Token
    from channels.db import database_sync_to_async
    from channels.middleware import BaseMiddleware
    
    @database_sync_to_async
    def get_user(token_key):
        try:
            token = Token.objects.get(key=token_key)
            return token.user
        except Token.DoesNotExist:
            return AnonymousUser()
    
    class TokenAuthMiddleware(BaseMiddleware):
        def __init__(self, inner):
            super().__init__(inner)
    
        async def __call__(self, scope, receive, send):
            try:
                token_key = (dict((x.split('=') for x in scope['query_string'].decode().split("&")))).get('token', None)
            except ValueError:
                token_key = None
            scope['user'] = AnonymousUser() if token_key is None else await get_user(token_key)
            return await super().__call__(scope, receive, send)
    

    routing.py

    from channels.security.websocket import AllowedHostsOriginValidator
    from channels.routing import ProtocolTypeRouter, URLRouter
    from .middleware import TokenAuthMiddleware
    from main.consumers import MainConsumer
    from django.conf.urls import url
    
    application = ProtocolTypeRouter({
            'websocket': AllowedHostsOriginValidator(
                TokenAuthMiddleware(
                    URLRouter(
                        [
                            url(r"^main/$", MainConsumer.as_asgi()),
                        ]
                    )
                )
            )
        })