Repository navigation
fix(auth): persist csrf_token cookie for the access_token lifetime - #3872
Merged
WillemJiang merged 1 commit intoJul 2, 2026
Merged
Conversation
The csrf_token double-submit cookie is set without max_age (a session cookie), while the access_token cookie is persistent over HTTPS (max_age = token_expiry_days, see _set_session_cookie). The two cookies represent one session but had different lifetimes. iOS Safari home-screen PWAs evict session cookies when iOS terminates the standalone web app, but keep persistent ones. On reopen the user still appears logged in (the persistent access_token survives and GET /api/v1/auth/me is CSRF-exempt), but the session-only csrf_token is gone, so the frontend's readCsrfCookie() returns null and sends no X-CSRF-Token header. The first state-changing request then fails with 403 "CSRF token missing. Include X-CSRF-Token header." Only re-login restored it. This only manifests over HTTPS, which is why plain-HTTP local dev never sees it. Give csrf_token the same max_age as access_token at both mint sites -- CSRFMiddleware (auth POSTs) and _set_csrf_cookie (OIDC GET callback) -- mirroring _set_session_cookie's `... if is_https else None` guard so the double-submit pair always shares a lifetime: persistent together over HTTPS, session-only together over plain HTTP. Regression tests in tests/test_auth_type_system.py: - test_csrf_cookie_persistent_on_https - test_csrf_cookie_session_only_on_http - test_oidc_callback_csrf_cookie_persistent_on_https Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
WillemJiang
approved these changes
Jul 2, 2026
jtaynl
added a commit
to jtaynl/deer-flow
that referenced
this pull request
Jul 3, 2026
Conflict (1): community/aio_sandbox/aio_sandbox.py — resolved TAKE-UPSTREAM. Upstream bytedance#3871 reworked execute_command to (self, command, env=None, timeout=None), which SUPERSEDES our local timeout= shim (added 2026-06-30 for bytedance#3864) AND adds the new env= for request-scoped skill secrets. Dropping our shim — our fork divergence shrinks by one patch. Normal bash path (env=None) is byte-identical (legacy shell.exec_command); the timeout= call from tools.py no longer TypeErrors. The other 12 auto-merged clean. Notable: bytedance#3904 unified DB config (no-op for us — our checkpointer.type=postgres section takes precedence), bytedance#3903 recursion clamp (ceiling 1000 = our value → no-op), bytedance#3872 csrf cookie lifetime fix (positive), #4fcb subagent step history (persists to existing run_events table, no migration), bytedance#3871 request-scoped secrets. No new alembic migration → head stays 0002. i18n auto-merged, WRI branding preserved. Login/setup pages untouched.
yogyoho
added a commit
to yogyoho/eai-flow-main
that referenced
this pull request
Jul 3, 2026
…n 生命周期 70d53da csrf_middleware import get_auth_config+is_auth_disabled; auth.py OIDC段取eai版(skip bytedance#3506) (c002596 todo counter 清理已前置commit)
ajayr
added a commit
to ajayr/deer-flow
that referenced
this pull request
Jul 15, 2026
Conflict resolutions: - docker/docker-compose.yaml: kept local dir-mount hot-reload hardening (103f179) for config.yaml/extensions_config.json; adopted upstream's new Redis stream-bridge service, depends_on, and env wiring. - backend/.../community/crawl4ai/* + backend/tests/test_crawl4ai_tools.py: took upstream (bytedance#3821/bytedance#3942) — our provider plus SSRF guard, timeout coercion, and expanded tests. Module is now upstream-tracked. - csrf_middleware.py/auth.py auto-merged: our bytedance#3872 fix landed upstream. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
marvin9551
pushed a commit
to marvin9551/deer-flow
that referenced
this pull request
Aug 21, 2026
…ytedance#3872) The csrf_token double-submit cookie is set without max_age (a session cookie), while the access_token cookie is persistent over HTTPS (max_age = token_expiry_days, see _set_session_cookie). The two cookies represent one session but had different lifetimes. iOS Safari home-screen PWAs evict session cookies when iOS terminates the standalone web app, but keep persistent ones. On reopen the user still appears logged in (the persistent access_token survives and GET /api/v1/auth/me is CSRF-exempt), but the session-only csrf_token is gone, so the frontend's readCsrfCookie() returns null and sends no X-CSRF-Token header. The first state-changing request then fails with 403 "CSRF token missing. Include X-CSRF-Token header." Only re-login restored it. This only manifests over HTTPS, which is why plain-HTTP local dev never sees it. Give csrf_token the same max_age as access_token at both mint sites -- CSRFMiddleware (auth POSTs) and _set_csrf_cookie (OIDC GET callback) -- mirroring _set_session_cookie's `... if is_https else None` guard so the double-submit pair always shares a lifetime: persistent together over HTTPS, session-only together over plain HTTP. Regression tests in tests/test_auth_type_system.py: - test_csrf_cookie_persistent_on_https - test_csrf_cookie_session_only_on_http - test_oidc_callback_csrf_cookie_persistent_on_https Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jihtsan
pushed a commit
to jihtsan/dnx-deer-flow
that referenced
this pull request
Aug 29, 2026
…ytedance#3872) The csrf_token double-submit cookie is set without max_age (a session cookie), while the access_token cookie is persistent over HTTPS (max_age = token_expiry_days, see _set_session_cookie). The two cookies represent one session but had different lifetimes. iOS Safari home-screen PWAs evict session cookies when iOS terminates the standalone web app, but keep persistent ones. On reopen the user still appears logged in (the persistent access_token survives and GET /api/v1/auth/me is CSRF-exempt), but the session-only csrf_token is gone, so the frontend's readCsrfCookie() returns null and sends no X-CSRF-Token header. The first state-changing request then fails with 403 "CSRF token missing. Include X-CSRF-Token header." Only re-login restored it. This only manifests over HTTPS, which is why plain-HTTP local dev never sees it. Give csrf_token the same max_age as access_token at both mint sites -- CSRFMiddleware (auth POSTs) and _set_csrf_cookie (OIDC GET callback) -- mirroring _set_session_cookie's `... if is_https else None` guard so the double-submit pair always shares a lifetime: persistent together over HTTPS, session-only together over plain HTTP. Regression tests in tests/test_auth_type_system.py: - test_csrf_cookie_persistent_on_https - test_csrf_cookie_session_only_on_http - test_oidc_callback_csrf_cookie_persistent_on_https Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The
csrf_tokendouble-submit cookie is set as a session cookie (nomax_age), while theaccess_tokencookie is persistent over HTTPS (max_age = token_expiry_days, see_set_session_cookie). The two cookies represent one session but had different lifetimes.This breaks iOS Safari home-screen PWAs (sites added to the Home Screen). When iOS terminates the standalone web app (background memory pressure, reboot, idle), it evicts session cookies but keeps persistent ones. On reopen:
access_token(persistent) survives →GET /api/v1/auth/meis CSRF-exempt → the user still appears logged in.csrf_token(session-only) is gone → the frontend'sreadCsrfCookie()returnsnulland sends noX-CSRF-Tokenheader.Only manifests over HTTPS, which is why plain-HTTP local dev never reproduces it.
Fix
Give
csrf_tokenthe samemax_ageasaccess_tokenat both places it is minted:CSRFMiddleware— auth POST responses (csrf_middleware.py)_set_csrf_cookie— OIDC GET callback (routers/auth.py)Both mirror
_set_session_cookie's... if is_https else Noneguard, so the double-submit pair always shares a lifetime: persistent together over HTTPS, session-only together over plain HTTP. CSRF-token lifetime is not a security boundary (double-submit security comes from same-origin reads), so persisting it is safe.Tests
Added to
backend/tests/test_auth_type_system.py:test_csrf_cookie_persistent_on_https— HTTPS register setscsrf_tokenwithmax_age, paired with the persistentaccess_token.test_csrf_cookie_session_only_on_http— plain HTTP keeps it a session cookie (dev behavior unchanged).test_oidc_callback_csrf_cookie_persistent_on_https— the OIDC-callback helper is persistent over HTTPS too.🤖 Generated with Claude Code