Skip to content

fix: scope cookies to CHAINLIT_ROOT_PATH for multi-app deployments#2825

Open
gonzalo123 wants to merge 4 commits intoChainlit:mainfrom
gonzalo123:fix/cookie-path
Open

fix: scope cookies to CHAINLIT_ROOT_PATH for multi-app deployments#2825
gonzalo123 wants to merge 4 commits intoChainlit:mainfrom
gonzalo123:fix/cookie-path

Conversation

@gonzalo123
Copy link

@gonzalo123 gonzalo123 commented Mar 5, 2026

Summary

  • Fix bug where _cookie_path used CHAINLIT_ROOT_PATH value as an env var name instead of using it directly as the cookie path
  • Add path=_cookie_path to all set_cookie calls that were missing it (access_token, oauth_state, X-Chainlit-Session-id)
  • Add path=_cookie_path to clear_oauth_state_cookie's delete_cookie call

Problem

When multiple Chainlit apps are mounted on the same domain under different subpaths (e.g., /app1 and /app2), their cookies collide because all cookies are set with path="/". This causes 401 Unauthorized errors when switching between browser tabs running different apps.

The root cause is twofold:

  1. _cookie_path resolution has a bug: os.environ.get(_cookie_root_path, "/") treats the value (e.g., /app1) as an env var name
  2. Most set_cookie calls never pass path=, so cookies always default to path="/"

Changes

  • backend/chainlit/auth/cookie.py: Fix _cookie_path resolution and add path=_cookie_path to all set_cookie/delete_cookie calls
  • backend/chainlit/server.py: Import _cookie_path and use it for the X-Chainlit-Session-id cookie
  • backend/tests/auth/test_cookie.py: Add 3 tests for _cookie_path resolution logic

Backwards Compatibility

No breaking changes. When CHAINLIT_ROOT_PATH is not set, _cookie_path remains "/" — identical to current behavior.


Summary by cubic

Scope all Chainlit cookies to the app’s root path to prevent cross-app collisions on the same domain. Also delete legacy root-path cookies to avoid stale auth after upgrading.

  • Bug Fixes
    • Fix _cookie_path: use CHAINLIT_ROOT_PATH directly; CHAINLIT_AUTH_COOKIE_PATH can override; default is "/".
    • Apply path=_cookie_path to all cookies (auth chunk/single, oauth_state set/delete, X-Chainlit-Session-id) and delete legacy Path="/" copies, including leftover chunks.
    • Add tests for path defaults/overrides, legacy cleanup (exact Path=/ assertion), and session cookie path.

Written for commit 7247fe4. Summary will update on new commits.

Fix bug where _cookie_path used CHAINLIT_ROOT_PATH value as an env var
name instead of as the path itself. Add path=_cookie_path to all
set_cookie/delete_cookie calls that were missing it.
@dosubot dosubot bot added size:M This PR changes 30-99 lines, ignoring generated files. auth Pertaining to authentication. unit-tests Has unit tests. labels Mar 5, 2026
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 3 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="backend/chainlit/auth/cookie.py">

<violation number="1" location="backend/chainlit/auth/cookie.py:135">
P2: Legacy auth/state cookies set at `path='/'` are never deleted after scoping to `_cookie_path`, so older root-path cookies can continue to be sent alongside new scoped cookies and be picked up instead, causing stale auth/state after upgrade.</violation>
</file>

Since this is your first cubic review, here's how it works:

  • cubic automatically reviews your code and comments on bugs and improvements
  • Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
  • Add one-off context when rerunning by tagging @cubic-dev-ai with guidance or docs links (including llms.txt)
  • Ask questions if you need clarification on any suggestion

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

…OT_PATH

When upgrading from pre-scoped versions, browsers may still hold old
cookies at path="/". Add _delete_legacy_cookies helper that explicitly
deletes them when _cookie_path != "/" to prevent stale auth behavior.
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 3 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="backend/tests/auth/test_cookie.py">

<violation number="1" location="backend/tests/auth/test_cookie.py:196">
P2: Substring check for `Path=/` will also match `Path=/app1`, so the test can pass even if deletion happens at the scoped path. This weak assertion can miss regressions in legacy cookie deletion behavior.</violation>
</file>

<file name="backend/chainlit/auth/cookie.py">

<violation number="1" location="backend/chainlit/auth/cookie.py:47">
P2: Unconditional root-path legacy cookie deletion can remove same-named cookies from other apps on the same host, causing cross-app session/logout disruption in multi-app deployments.</violation>

<violation number="2" location="backend/chainlit/auth/cookie.py:155">
P2: Legacy root-path cleanup is incomplete: stale removed chunk cookies are only deleted at `_cookie_path`, allowing mixed old/new chunk token reconstruction.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

cookie_module._delete_legacy_cookies(response, "access_token")
set_cookie_header = response.headers.get("set-cookie", "")
assert "access_token" in set_cookie_header
assert 'Path=/' in set_cookie_header
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Substring check for Path=/ will also match Path=/app1, so the test can pass even if deletion happens at the scoped path. This weak assertion can miss regressions in legacy cookie deletion behavior.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/tests/auth/test_cookie.py, line 196:

<comment>Substring check for `Path=/` will also match `Path=/app1`, so the test can pass even if deletion happens at the scoped path. This weak assertion can miss regressions in legacy cookie deletion behavior.</comment>

<file context>
@@ -171,6 +171,32 @@ def test_cookie_path_explicit_overrides_root_path(monkeypatch):
+    cookie_module._delete_legacy_cookies(response, "access_token")
+    set_cookie_header = response.headers.get("set-cookie", "")
+    assert "access_token" in set_cookie_header
+    assert 'Path=/' in set_cookie_header
+    assert 'Max-Age=0' in set_cookie_header
+
</file context>
Suggested change
assert 'Path=/' in set_cookie_header
parts = [part.strip() for part in set_cookie_header.split(";")]
assert "Path=/" in parts
Fix with Cubic

return
for name in names:
response.delete_cookie(
key=name, path="/", secure=_cookie_secure, samesite=_cookie_samesite
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Unconditional root-path legacy cookie deletion can remove same-named cookies from other apps on the same host, causing cross-app session/logout disruption in multi-app deployments.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/chainlit/auth/cookie.py, line 47:

<comment>Unconditional root-path legacy cookie deletion can remove same-named cookies from other apps on the same host, causing cross-app session/logout disruption in multi-app deployments.</comment>

<file context>
@@ -34,6 +34,20 @@
+        return
+    for name in names:
+        response.delete_cookie(
+            key=name, path="/", secure=_cookie_secure, samesite=_cookie_samesite
+        )
+
</file context>
Fix with Cubic

…assertion

- Add _delete_legacy_cookies call when deleting leftover chunk cookies
- Fix weak Path=/ substring assertion in test to use exact match
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

auth Pertaining to authentication. size:M This PR changes 30-99 lines, ignoring generated files. unit-tests Has unit tests.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant