Search code examples
authenticationcookieswebsocketdjango-channels

Django Channels retrieve the cookies from the HTTP handshake request (upgrade request)


I'm trying to retrieve the cookies at the start of the connection request in Django Channels. According to the normal flow, to establish a connection via websocket, a handshake is performed through the HTTP protocol which should inject, in its headers, the cookies that the browser has (that matches with the server domain). But it doesn't receive them.

To retrieve the server side cookies I'm using the channels cookie middleware.

from channels.sessions import CookieMiddleware

In Chrome tools you can see that it sends cookies.

enter image description here

enter image description here

But in Django don't.

class CookieMiddleware:
    """
    Extracts cookies from HTTP or WebSocket-style scopes and adds them as a
    scope["cookies"] entry with the same format as Django's request.COOKIES.
    """

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

    async def __call__(self, scope, receive, send):
        print("Call coockie middleware")
        print(scope) # print request
        # Check this actually has headers. They're a required scope key for HTTP and WS.
        if "headers" not in scope:
            raise ValueError(
                "CookieMiddleware was passed a scope that did not have a headers key "
                + "(make sure it is only passed HTTP or WebSocket connections)"
            )
        # Go through headers to find the cookie one
        for name, value in scope.get("headers", []):
            if name == b"cookie":
                cookies = parse_cookie(value.decode("latin1"))
                break
        else:
            # No cookie header found - add an empty default.
            cookies = {}
        # Return inner application
        print(cookies) # print cookies
        return await self.inner(dict(scope, cookies=cookies), receive, send)

Result print.

Call coockie middleware
{'type': 'websocket', 'path': '/app-test/', 'raw_path': b'/app-test/', 'headers': [(b'host', b'127.0.0.1:8000'), (b'connection', b'Upgrade'), (b'pragma', b'no-cache'), (b'cache-control', b'no-cache'), (b'user-agent', b'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36'), (b'upgrade', b'websocket'), (b'origin', b'http://localhost:4200'), (b'sec-websocket-version', b'13'), (b'accept-encoding', b'gzip, deflate, br'), (b'accept-language', b'es-ES,es;q=0.9'), (b'sec-websocket-key', b'MncLnxEO1beQemafXpMt4g=='), (b'sec-websocket-extensions', b'permessage-deflate; client_max_window_bits')], 'query_string': b'', 'client': ['127.0.0.1', 55272], 'server': ['127.0.0.1', 8000], 'subprotocols': [], 'asgi': {'version': '3.0'}}
{}

Dependencies

django = "==3.1.5"
channels = "==3.0.3"

Thanks for reading and for any help or guidance you can offer me.


Solution

  • Nevermind, it's not fetching the cookies because it only sends them when the domain matches. Therefore, it's not capable of recognizing IPs (number) nor can a cookie be defined at the IP level. This means that when I make the connection via websocket, the URL must have the domain (no IP) defined for the cookie.

    Before:

    ws://127.0.0.1:8000/app-test/
    

    After:

    ws://localhost:8000/app-test/
    

    Lastly, the image above doesn't represent the true request to the websocket server, so even though it's sending the cookies it's not sending them to my server (it's a different request).