If a user opens the OAuth popup and closes it without finishing login, the promise in loginWithRocketChatOAuth never settles. No resolve, no reject.
The Promise constructor on line 45 only takes resolve. There's no reject. The only path to resolve() is inside the onMessage handler, which fires when the popup posts back an rc-oauth-callback message.
There IS code that detects when the popup closes (the setInterval on line 60). It cleans up the interval and the event listener, which is good. But then it just... stops. Nobody calls resolve() or reject(), so the promise sits there indefinitely:
const checkInterval = setInterval(() => {
if (popup.closed) {
clearInterval(checkInterval);
window.removeEventListener("message", onMessage);
// nothing here — promise hangs
}
}, 1000);
What happens in practice:
The caller in ChatInput.js (line 241) does:
try {
await RCInstance.auth.loginWithRocketChatOAuth();
} catch (e) {
console.error(e);
dispatchToastMessage({ type: 'error', message: e.message });
}
Since the promise never rejects, the catch block never runs. The user closes the popup and gets zero feedback — no error toast, no "login cancelled" message, nothing. The onJoin async function just silently stays stuck at the await forever.
Each time the user tries this (click JOIN, popup opens, close popup), another unresolved promise stacks up in memory. They're never garbage collected because the promise internals still hold references to the closure.
If a user opens the OAuth popup and closes it without finishing login, the promise in
loginWithRocketChatOAuthnever settles. No resolve, no reject.The Promise constructor on line 45 only takes
resolve. There's noreject. The only path toresolve()is inside theonMessagehandler, which fires when the popup posts back anrc-oauth-callbackmessage.There IS code that detects when the popup closes (the
setIntervalon line 60). It cleans up the interval and the event listener, which is good. But then it just... stops. Nobody callsresolve()orreject(), so the promise sits there indefinitely:What happens in practice:
The caller in
ChatInput.js(line 241) does:Since the promise never rejects, the
catchblock never runs. The user closes the popup and gets zero feedback — no error toast, no "login cancelled" message, nothing. TheonJoinasync function just silently stays stuck at theawaitforever.Each time the user tries this (click JOIN, popup opens, close popup), another unresolved promise stacks up in memory. They're never garbage collected because the promise internals still hold references to the closure.