fix: add timeout to CAS popup login to prevent infinite hang#40161
fix: add timeout to CAS popup login to prevent infinite hang#40161Kgupta62 wants to merge 1 commit intoRocketChat:developfrom
Conversation
|
Looks like this PR is not ready to merge, because of the following issues:
Please fix the issues and try again If you have any trouble, please check the PR guidelines |
|
|
|
WalkthroughAdded a timeout mechanism to CAS popup login to prevent indefinite waiting. The Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Suggested labels
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/meteor/client/lib/openCASLoginPopup.ts`:
- Line 60: Remove the inline comment inside the catch block in
apps/meteor/client/lib/openCASLoginPopup.ts (the catch handling around the
popup/window check in function openCASLoginPopup), i.e., delete the comment text
"// popup may already be closed or cross-origin" so the catch block contains no
implementation comment and matches repository style; leave the catch body empty
or add a minimal TODO or proper error handling if required by project policy.
- Around line 46-67: The polling interval and timeout are not cleared in both
completion paths; update the logic in openCASLoginPopup (the pollClosed Promise
and timeout Promise using checkPopupOpen interval and the setTimeout with
CAS_POPUP_TIMEOUT_MS) so that when either path completes you clear the other
timer: store the interval id (checkPopupOpen) and timeout id, call
clearInterval(checkPopupOpen) and clearTimeout(timeoutId) inside the pollClosed
resolve branch and likewise clearInterval/checkPopupOpen before rejecting in the
timeout branch (ensure popup.close() remains wrapped in try/catch).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 7719af75-046c-4d40-add0-14ce44381d94
📒 Files selected for processing (1)
apps/meteor/client/lib/openCASLoginPopup.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js}
📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)
**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation
Files:
apps/meteor/client/lib/openCASLoginPopup.ts
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Use `page.waitFor()` with specific conditions instead of hardcoded timeouts in Playwright tests
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Use `page.waitFor()` with specific conditions instead of hardcoded timeouts in Playwright tests
Applied to files:
apps/meteor/client/lib/openCASLoginPopup.ts
📚 Learning: 2026-02-10T16:32:42.586Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 38528
File: apps/meteor/client/startup/roles.ts:14-14
Timestamp: 2026-02-10T16:32:42.586Z
Learning: In Rocket.Chat's Meteor client code, DDP streams use EJSON and Date fields arrive as Date objects; do not manually construct new Date() in stream handlers (for example, in sdk.stream()). Only REST API responses return plain JSON where dates are strings, so implement explicit conversion there if needed. Apply this guidance to all TypeScript files under apps/meteor/client to ensure consistent date handling in DDP streams and REST responses.
Applied to files:
apps/meteor/client/lib/openCASLoginPopup.ts
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In the Rocket.Chat repository, do not reference Biome lint rules in code review feedback. Biome is not used even if biome.json exists; only reference Biome rules if there is explicit, project-wide usage documented. For TypeScript files, review lint implications without Biome guidance unless the project enables Biome rules.
Applied to files:
apps/meteor/client/lib/openCASLoginPopup.ts
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In this repository (RocketChat/Rocket.Chat), Biome lint rules are not used even if a biome.json exists. When reviewing TypeScript files (e.g., packages/ui-voip/src/providers/useMediaSession.ts), ensure lint suggestions do not reference Biome-specific rules. Rely on general ESLint/TypeScript lint rules and project conventions instead.
Applied to files:
apps/meteor/client/lib/openCASLoginPopup.ts
🔇 Additional comments (1)
apps/meteor/client/lib/openCASLoginPopup.ts (1)
43-44: Nice extraction of popup timeout constant.Good call centralizing the timeout value; this keeps the flow readable and easier to adjust later.
| const pollClosed = new Promise<void>((resolve) => { | ||
| const checkPopupOpen = setInterval(() => { | ||
| if (popup.closed || popup.closed === undefined) { | ||
| clearInterval(checkPopupOpen); | ||
| resolve(); | ||
| } | ||
| }, 100); | ||
| }); | ||
|
|
||
| const timeout = new Promise<never>((_, reject) => { | ||
| setTimeout(() => { | ||
| try { | ||
| popup.close(); | ||
| } catch { | ||
| // popup may already be closed or cross-origin | ||
| } | ||
| reject(new Error('CAS login timed out. Please try again.')); | ||
| }, CAS_POPUP_TIMEOUT_MS); | ||
| }); | ||
|
|
||
| return Promise.race([pollClosed, timeout]); | ||
| }; |
There was a problem hiding this comment.
Clear timer handles on both completion paths.
On Line 46 and Line 55, interval/timeout are created independently but never jointly cleaned up. If the timeout path hits Line 57 and popup.close() fails, polling may continue forever; if polling wins first, the timeout still executes later.
Proposed fix
const waitForPopupClose = (popup: Window) => {
- const pollClosed = new Promise<void>((resolve) => {
- const checkPopupOpen = setInterval(() => {
- if (popup.closed || popup.closed === undefined) {
- clearInterval(checkPopupOpen);
- resolve();
- }
- }, 100);
- });
-
- const timeout = new Promise<never>((_, reject) => {
- setTimeout(() => {
- try {
- popup.close();
- } catch {
- // popup may already be closed or cross-origin
- }
- reject(new Error('CAS login timed out. Please try again.'));
- }, CAS_POPUP_TIMEOUT_MS);
- });
-
- return Promise.race([pollClosed, timeout]);
+ return new Promise<void>((resolve, reject) => {
+ const checkPopupOpen = window.setInterval(() => {
+ if (popup.closed || popup.closed === undefined) {
+ cleanup();
+ resolve();
+ }
+ }, 100);
+
+ const timeoutId = window.setTimeout(() => {
+ cleanup();
+ try {
+ popup.close();
+ } catch {}
+ reject(new Error('CAS login timed out. Please try again.'));
+ }, CAS_POPUP_TIMEOUT_MS);
+
+ const cleanup = (): void => {
+ window.clearInterval(checkPopupOpen);
+ window.clearTimeout(timeoutId);
+ };
+ });
};🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/meteor/client/lib/openCASLoginPopup.ts` around lines 46 - 67, The
polling interval and timeout are not cleared in both completion paths; update
the logic in openCASLoginPopup (the pollClosed Promise and timeout Promise using
checkPopupOpen interval and the setTimeout with CAS_POPUP_TIMEOUT_MS) so that
when either path completes you clear the other timer: store the interval id
(checkPopupOpen) and timeout id, call clearInterval(checkPopupOpen) and
clearTimeout(timeoutId) inside the pollClosed resolve branch and likewise
clearInterval/checkPopupOpen before rejecting in the timeout branch (ensure
popup.close() remains wrapped in try/catch).
| try { | ||
| popup.close(); | ||
| } catch { | ||
| // popup may already be closed or cross-origin |
There was a problem hiding this comment.
Remove inline catch comment to match repository style.
Line 60 adds an implementation comment in production code; this should be removed to follow project conventions.
As per coding guidelines "Avoid code comments in the implementation".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/meteor/client/lib/openCASLoginPopup.ts` at line 60, Remove the inline
comment inside the catch block in apps/meteor/client/lib/openCASLoginPopup.ts
(the catch handling around the popup/window check in function
openCASLoginPopup), i.e., delete the comment text "// popup may already be
closed or cross-origin" so the catch block contains no implementation comment
and matches repository style; leave the catch body empty or add a minimal TODO
or proper error handling if required by project policy.
Summary
Closes #40153
openCASLoginPopup()pollspopup.closedevery 100ms with no timeout, causing the login flow to hang indefinitely if the popup never closesPromise.race()that rejects with a user-friendly error message and attempts to close the stale popupChanges
apps/meteor/client/lib/openCASLoginPopup.ts: Wrapped the polling promise inPromise.race()alongside a 120-second timeout promise that rejects with"CAS login timed out. Please try again."Test plan
Note
This PR was authored with assistance from an AI coding tool (Claude Code by Anthropic).
Summary by CodeRabbit
Bug Fixes