Python Aiohttp: cookies behaviour on same domain

537 views Asked by At

Hopefully this is not a too stupid question, but I am having trouble with aiohttp cookie processing.

Aiohttp's CookieJar class mentions it implements cookie storage adhering to RFC 6265, which states that:

  • cookies for a given host are shared across all the ports on that host
  • Cookies do not provide isolation by port. If a cookie is readable by a service running on one port, the cookie is also readable by a service running on another port of the same server.

But if I create two aiohttp servers, one that makes you "login" and gives you a cookie back, and another one with an endpoint that expects you to have a cookie, both hosted on localhost (two different ports I guess), the cookie will not be processed.

Here's a set of 4 tests using aiohttp, pytest, pytest and pytest-aiohttp to explain:

import functools

import pytest
from aiohttp import web


pytestmark = pytest.mark.asyncio


def attach_session(f):
    @functools.wraps(f)
    async def wrapper(request: web.Request):
        session_id = request.cookies.get("testcookie")
        request["mysession"] = session_id

        response = await f(request)
        response.set_cookie("testcookie", session_id)
        return response

    return wrapper


def is_logged_in(f):
    @functools.wraps(f)
    @attach_session
    async def wrapper(request: web.Request):
        session = request["mysession"]
        if not session:
            raise web.HTTPUnauthorized
        return await f(request)

    return wrapper


async def login(_: web.Request):
    response = web.Response()
    response.set_cookie("testcookie", "somerandomstring")
    return response


@is_logged_in
async def some_endpoint(request: web.Request):
    return web.Response(text="sweet")


@pytest.fixture
def auth_client(event_loop, aiohttp_client):
    app = web.Application()
    app.router.add_post("/login", login)
    return event_loop.run_until_complete(aiohttp_client(app))


@pytest.fixture
def core_client(event_loop, aiohttp_client):
    app = web.Application()
    app.router.add_get("/some_endpoint", some_endpoint)
    return event_loop.run_until_complete(aiohttp_client(app))


async def test_login(auth_client):
    resp = await auth_client.post("/login")
    assert resp.status == 200
    assert resp.cookies.get("testcookie").value == "somerandomstring"


async def test_some_endpoint_anonymous(core_client):
    resp = await core_client.get("/some_endpoint")
    assert resp.status == 401


async def test_some_endpoint_as_logged_in(auth_client, core_client):
    resp1 = await auth_client.post("/login")
    resp2 = await core_client.get("/some_endpoint", cookies=resp1.cookies)
    assert resp2.status == 401


async def test_some_endpoint_as_logged_in_again(auth_client, core_client):
    resp1 = await auth_client.post("/login")

    _cookie = list(resp1.cookies.values())[0]
    resp2 = await core_client.get(
        "/some_endpoint", cookies={_cookie.key: _cookie.value}
    )
    assert resp2.status == 200

But from my understanding, the "test_some_endpoint_as_logged_in" test should work. Why is it returning 401, while the same thing but with sending the cookie as a dict returns 200?

1

There are 1 answers

2
Tom Dierckx On

I think the correct way of sharing the cookies between clients would be loading the SimpleCookie object of the resp1 to the core_client.session.cookie_jar.

Changing the code of the test_some_endpoint_as_logged_in to should fix it:

async def test_some_endpoint_as_logged_in(auth_client, core_client):
    resp1 = await auth_client.post("/login")
    core_client.session.cookie_jar.update_cookies(resp1.cookies)
    resp2 = await core_client.get("/some_endpoint")
    assert resp2.status == 401

Cookie data is kept in the session object as the auth_client and core_client are different sessions with there own data cookie data is not shared. It is comparable to using a different browser with each there own cookie_jar.