fix(auth): use CHAINLIT_ROOT_PATH value as cookie path; constant-time OAuth state check#2934
Conversation
Two bugs in `backend/chainlit/auth/cookie.py`:
1. **Cookie path computed incorrectly when CHAINLIT_ROOT_PATH is set**
The module-level initialisation read:
if _cookie_root_path := os.environ.get("CHAINLIT_ROOT_PATH", None):
_cookie_path = os.environ.get(_cookie_root_path, "/")
`os.environ.get(_cookie_root_path, "/")` used the *value* of
`CHAINLIT_ROOT_PATH` (e.g. `"/myapp"`) as a *key* to look up yet
another environment variable. Since `"/myapp"` is never a valid env
var name the lookup always returned the default `"/"`. Cookie deletion
in `set_auth_cookie` and `clear_auth_cookie` therefore used path `"/"`
instead of the intended root path, leaving stale cookies on clients
after logout or token rotation when the app is deployed under a
sub-path.
Fix: assign `_cookie_path = _cookie_root_path` directly.
2. **Non-constant-time OAuth state comparison**
`validate_oauth_state_cookie` used `oauth_state != state` (regular
string equality), which is not constant-time and can leak the expected
state value via timing side-channels. Replace with
`hmac.compare_digest`, which is the standard Python idiom for
constant-time secret comparison. Also added an explicit `None` guard
so a missing state cookie is rejected before the digest comparison.
Tests added:
- test_cookie_path_uses_chainlit_root_path_value
- test_validate_oauth_state_cookie_rejects_missing_state
- test_validate_oauth_state_cookie_accepts_correct_state
There was a problem hiding this comment.
1 issue found across 2 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:28">
P1: Cookie path mismatch: cookies are set at `/` but deleted at `_cookie_path` (`CHAINLIT_ROOT_PATH`), so auth cookies/chunks cannot be cleaned up under a sub-path.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| @@ -1,3 +1,4 @@ | |||
| import hmac | |||
There was a problem hiding this comment.
P1: Cookie path mismatch: cookies are set at / but deleted at _cookie_path (CHAINLIT_ROOT_PATH), so auth cookies/chunks cannot be cleaned up under a sub-path.
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 28:
<comment>Cookie path mismatch: cookies are set at `/` but deleted at `_cookie_path` (`CHAINLIT_ROOT_PATH`), so auth cookies/chunks cannot be cleaned up under a sub-path.</comment>
<file context>
@@ -24,7 +25,7 @@
_cookie_secure = _cookie_samesite == "none"
if _cookie_root_path := os.environ.get("CHAINLIT_ROOT_PATH", None):
- _cookie_path = os.environ.get(_cookie_root_path, "/")
+ _cookie_path = _cookie_root_path
else:
_cookie_path = os.environ.get("CHAINLIT_AUTH_COOKIE_PATH", "/")
</file context>
|
@devteamaegis Code looks good, just some linting nitpicks. Nice to finally have the cookie path fixed! |
There was a problem hiding this comment.
Pull request overview
This PR fixes two issues in Chainlit’s backend auth cookie handling: (1) correctly deriving the cookie deletion path from CHAINLIT_ROOT_PATH, and (2) hardening OAuth state validation by using a constant-time comparison to reduce timing side-channel risk.
Changes:
- Fix module initialization so
_cookie_pathuses the value ofCHAINLIT_ROOT_PATH(instead of treating it as an env var key). - Update
validate_oauth_state_cookieto usehmac.compare_digest(and reject missing state cookies) for constant-time comparison. - Add unit tests covering the root-path cookie behavior and OAuth state validation.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
backend/chainlit/auth/cookie.py |
Fixes cookie path derivation and makes OAuth state comparison constant-time. |
backend/tests/auth/test_cookie.py |
Adds tests for CHAINLIT_ROOT_PATH cookie path derivation and OAuth state validation behaviors. |
| monkeypatch.setenv("CHAINLIT_ROOT_PATH", "/myapp") | ||
| monkeypatch.delenv("CHAINLIT_AUTH_COOKIE_PATH", raising=False) | ||
| importlib.reload(cookie_module) | ||
| assert cookie_module._cookie_path == "/myapp", ( | ||
| f"Expected _cookie_path to be '/myapp' but got '{cookie_module._cookie_path}'. " | ||
| "CHAINLIT_ROOT_PATH value should be used directly as the cookie path." | ||
| ) |
| _cookie_secure = _cookie_samesite == "none" | ||
| if _cookie_root_path := os.environ.get("CHAINLIT_ROOT_PATH", None): | ||
| _cookie_path = os.environ.get(_cookie_root_path, "/") | ||
| _cookie_path = _cookie_root_path | ||
| else: | ||
| _cookie_path = os.environ.get("CHAINLIT_AUTH_COOKIE_PATH", "/") |
dokterbob
left a comment
There was a problem hiding this comment.
Good find but some quite significant fixups suggested by copilot!
Summary
Two bugs in
backend/chainlit/auth/cookie.py.Bug 1 — Cookie path always
"/"whenCHAINLIT_ROOT_PATHis setRoot cause: Module-level initialisation used the value of
CHAINLIT_ROOT_PATHas an environment variable key:Impact:
set_auth_cookieandclear_auth_cookiedelete stale cookie chunks usingpath=_cookie_path. When an app is deployed under a sub-path andCHAINLIT_ROOT_PATHis configured, the path used for deletion is"/"instead of the intended path, so old chunks are never cleared after logout or token rotation.Fix:
Bug 2 — Non-constant-time OAuth state comparison
validate_oauth_state_cookieused a regular!=comparison:String equality (
!=) is not constant-time and can leak information about the expected state value through timing side-channels. While network jitter limits practical exploitability, the fix is trivial: usehmac.compare_digest, which is the standard Python idiom for comparing secrets. An explicitNoneguard was also added so a missing cookie is rejected before the digest call.Tests
Three new tests in
tests/auth/test_cookie.py:test_cookie_path_uses_chainlit_root_path_value_cookie_pathequalsCHAINLIT_ROOT_PATHvalue after reloadtest_validate_oauth_state_cookie_rejects_missing_statetest_validate_oauth_state_cookie_accepts_correct_stateSummary by cubic
Fixes cookie path handling to honor
CHAINLIT_ROOT_PATH, so auth cookie cleanup works when the app runs under a sub-path. Adds a constant-time OAuth state comparison to harden against timing attacks.CHAINLIT_ROOT_PATHvalue directly as the cookiepath, ensuringset_auth_cookie/clear_auth_cookieremove stale chunks under sub-paths.hmac.compare_digestand reject missing cookies.Written for commit fa04ea9. Summary will update on new commits. Review in cubic