Console redirects to sign-in during concurrent session refresh
还没有人认领这个 Issue。
评估
- 难度
- 5/5
- 预计耗时
- 一周以上
- 新手友好度
- 45/100
- Issue 类型
- 缺陷
- 描述清晰度
- 基本清楚
- 活跃度
- 活跃
- 技术栈
- nextjs, python, typescript
调研方向
Read hydration-boundary.tsx, web/app/auth/refresh/route.ts, web/service/refresh-token.ts, account_login_service.py, and account_login_adapters.py to trace concurrent refresh handling. Reproduce overlapping refresh requests and inspect navigation plus refresh responses. Done means valid shared sessions converge without unnecessary sign-in redirects, while expired or revoked sessions still recover in a bounded way without loops.
由索引模型根据 Issue 内容生成。
描述
Self Checks
- I have read the Contributing Guide and Language Policy.
- This is only for bug report, if you would like to ask a question, please head to Discussions.
- I have searched for existing issues search for existing issues, including closed ones.
- I confirm that I am using English to submit this report, otherwise it will be closed.
- 【中文用户 & Non English User】请使用英语提交,否则会被关闭 :)
- Please do not modify this template :) and fill in all the required fields.
Dify version
1.17.1 on Dify Cloud, observed on September 18, 2026. API and Web image tags were hotfix-1.17.1-fix.0; the API trace's service.version was dify-1.17.1-9f6153c3d55aaa60e0e7e46078da54f9765f9376.
The source paths discussed below were rechecked against main at 5568aa0ea57d3a2c1cec03bd747b975494f094e3 on September 19. The refresh route, client refresh helper, and backend session refresh/rotation files are unchanged between these two revisions. Runtime reproduction was on Cloud, not a build of the latest main.
Cloud or Self Hosted
Cloud
Steps to reproduce
Concurrent Console session refresh can navigate an authenticated browser to /signin, even though a usable session remains available. This report tracks that recovery behavior separately from the login-page cache loop in #38457.
- Sign in to
https://cloud.dify.ai/and verify that the Console home page is accessible. - In the same browser profile/context, create a set of eight test tabs so that they share authentication cookies with the signed-in tab.
- Dispatch navigation in these eight test tabs concurrently to
https://cloud.dify.ai/auth/refresh?redirect_url=%2F(the test used a parallel batch of browser navigation calls). - Inspect navigation history as well as final URLs. A final Home page alone can hide a preceding sign-in redirect.
- Correlate this window with complete API access logs for
POST /console/api/refresh-token; successful and rejected refresh requests can be absent from sampled traces.
This was a targeted concurrency test in the Codex in-app browser. It deliberately invokes the existing refresh route with a logged-in session; it does not reproduce the full natural access-token-expiry sequence. A preceding two-tab attempt ended at Home in both tabs. The eight-tab attempt also eventually ended at Home, but its history recorded:
2026-09-18T11:30:01.343Z
Sign in - Dify
https://cloud.dify.ai/signin?redirect_url=%2F
The issue is timing-dependent; one observed eight-tab run is not a measured reproduction rate. No cookies were manually deleted, no logout endpoint was invoked, and no application data was changed during this test.
✔️ Expected Behavior
Concurrent refresh attempts for a shared browser session should converge on a usable renewed session without unnecessarily navigating an already-authenticated user to sign-in.
A regression test should cover overlapping refresh-route requests, browser API refresh overlapping with server-side refresh, and out-of-order responses. Genuinely expired or revoked sessions should still require authentication, with bounded recovery and no redirect loop.
❌ Actual Behavior
At least one navigation passed through /signin during the concurrent test before all test tabs returned to Home. This demonstrates an unnecessary authentication redirect; it does not by itself demonstrate permanent session revocation.
Additional production evidence
The investigation began with a user reporting unexpected logout. The associated Console request sequence included:
2026-09-18 09:57:41.398 UTC GET /console/api/explore/apps 401 (server span)
2026-09-18 09:57:41.756 UTC POST /console/api/refresh-token 200 (access log)
2026-09-18 09:57:42.791 UTC GET /console/api/explore/apps 200 (access log)
The account associated with the successful retry subsequently had 35 authenticated API requests returning 200 through 09:59:14 UTC. Separately, complete access logs contain a server-side refresh from a Web pod returning 401 at 09:57:46.149 UTC; it has no corresponding sampled trace. That internal request lacks a session correlation identifier, so it cannot be conclusively attributed to the original user.
Refresh 200s and 401s were also present in the concurrent browser test window. Those log queries covered shared production traffic: exact per-test success/failure counts are not claimed without a unique correlation key. Private account identifiers, cookie values, internal addresses, and observability links are omitted.
Relevant implementation and candidate mechanism
- Console SSR profile admission redirects a profile 401 to
/auth/refresh. - The refresh route forwards the incoming Cookie header to the API, then redirects to sign-in on any non-OK result or exception. It does not recheck whether another request has already renewed the browser session.
- Backend refresh resolves the refresh token before rotation deletes the old mapping and issues new tokens. These are separate operations; a request carrying the old cookie can resolve after another request has deleted its mapping and receive 401.
- Browser refresh coordination uses client memory/localStorage and does not coordinate requests handled by the server-side refresh route.
This is a source-supported candidate explanation for the observed redirect, not a claim that every request in the original incident used the same refresh token. The full original logout and its precise interleaving still need request-level correlation. Simply serializing rotation may still reject callers already carrying a superseded token; recovery semantics need to handle that case without weakening revocation.
Related work
- #38457 and draft PR #38458 cover the sign-in profile-cache redirect loop. The PR changes the login-page probe, not backend refresh rotation or server/client refresh coordination.
- #30827 / merged PR #30828 address a browser refresh-lock deadlock.
- PR #41914 proposes dead-token/sign-in-loop handling, but was closed without merging and does not address backend rotation.
From Codex
- 主要语言
- TypeScript
- 星标
- 157k
- 派生
- 24.7k
- 平均合并
- 22 小时 32 分钟
- 30 天内合并 PR
- 611
贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
langgenius/dify 的其他 Issue
-
Annotation Reply: a stored score threshold of 0.0 is silently replaced with 1, disabling the feature 未关闭
难度 2/5 1-3 小时 新手友好度 88/100
langgenius/dify#42639 · 1 条评论 · 1 个 reaction ·
-
难度 2/5 1-3 小时 新手友好度 70/100
langgenius/dify#42468 · 1 条评论 · 1 个 reaction ·
-
🐞 bug
难度 2/5 1-3 小时 新手友好度 86/100
langgenius/dify#42446 · 1 个 reaction ·
-
难度 2/5 1-3 小时 新手友好度 88/100
langgenius/dify#42355 · 1 条评论 · 1 个 reaction ·
-
难度 2/5 1-3 小时 新手友好度 88/100
langgenius/dify#42350 · 1 条评论 · 1 个 reaction ·
相似的 Issue
-
calcite-components needs triage refactor
难度 2/5 1-3 小时 新手友好度 75/100
Esri/calcite-design-system#15203 ·
-
难度 2/5 1-3 小时 新手友好度 91/100
-
community first-timers-only good first issue hacktoberfest help wanted low hanging fruit up-for-grabs
难度 1/5 1 小时以内 新手友好度 95/100
-
难度 2/5 1-3 小时 新手友好度 78/100
Automattic/studio#4908 ·
-
难度 2/5 1-3 小时 新手友好度 90/100