|
1
|
================================================================================================
|
|
2
|
PROPOSAL - [MINOR] functional - p2j.sso_reauth.js handleSsoReauth
|
|
3
|
The expiry path leaves the IdP popup open as an orphan
|
|
4
|
================================================================================================
|
|
5
|
|
|
6
|
REVIEW FINDING (abridged)
|
|
7
|
"The expiry branch no longer calls cleanupReauthUi, which the deleted showSessionExpiredPage did
|
|
8
|
as its first statement (await cleanupReauthUi(true)), so the reauthPopup.close() it performs is
|
|
9
|
skipped [...] popupCloseCheckInterval and reauthMessageListener die with the page but the popup
|
|
10
|
window does not, and the reloaded page holds no reference to it."
|
|
11
|
|
|
12
|
FILES
|
|
13
|
src/com/goldencode/p2j/ui/client/driver/web/res/p2j.sso_reauth.js
|
|
14
|
|
|
15
|
------------------------------------------------------------------------------------------------
|
|
16
|
1. ROOT CAUSE
|
|
17
|
------------------------------------------------------------------------------------------------
|
|
18
|
|
|
19
|
cleanupReauthUi (p2j.sso_reauth.js:436) is the only place that closes the popup:
|
|
20
|
|
|
21
|
if (reauthPopup && !reauthPopup.closed)
|
|
22
|
{
|
|
23
|
reauthPopup.close();
|
|
24
|
}
|
|
25
|
reauthPopup = null;
|
|
26
|
|
|
27
|
The deleted showSessionExpiredPage called it as its first statement, so every pre-change expiry path
|
|
28
|
closed the popup. The replacement branch reaches window.location.reload() with no cleanup call of
|
|
29
|
any kind.
|
|
30
|
|
|
31
|
The grace period does not prevent this. The guard is
|
|
32
|
|
|
33
|
if (reauthInFlight() && (Date.now() - reauthDeadline) < REAUTH_GRACE_MS)
|
|
34
|
|
|
35
|
whose second conjunct is time-bounded, so once 10 s have elapsed past the deadline the branch falls
|
|
36
|
through regardless of reauthInFlight() still being true from an open popup. The tick then reloads
|
|
37
|
with reauthPopup non-null and unclosed.
|
|
38
|
|
|
39
|
A popup opened by window.open is an independent browsing context; reloading the opener does not
|
|
40
|
close it. reauthPopup is module-private and referenced nowhere else, and the reloaded page starts
|
|
41
|
with reauthPopup = null, so nothing can ever close it afterwards. The user is left with a stray
|
|
42
|
window sitting on the IdP page.
|
|
43
|
|
|
44
|
Concrete trigger: the user clicks Re-login, the IdP popup opens, and they abandon it without
|
|
45
|
closing it - a realistic outcome, since the popup is where they would go if they could not remember
|
|
46
|
their credentials.
|
|
47
|
|
|
48
|
------------------------------------------------------------------------------------------------
|
|
49
|
2. PROPOSED FIX
|
|
50
|
------------------------------------------------------------------------------------------------
|
|
51
|
|
|
52
|
2.1 Close the popup before navigating
|
|
53
|
---------------------------------------
|
|
54
|
|
|
55
|
The full cleanupReauthUi is async and removes the overlay, which is not wanted here - the overlay
|
|
56
|
should stay visible until the page actually goes away, and awaiting a dialog close before a
|
|
57
|
navigation invites the same cancelled-navigation problem as
|
|
58
|
proposal-issue-minor-functional-3. A synchronous, targeted close is the right size:
|
|
59
|
|
|
60
|
countdownDiv.textContent = 'Confirming sign-in...';
|
|
61
|
+ // the popup is an independent browsing context and would survive this navigation with
|
|
62
|
+ // nothing left holding a reference to it
|
|
63
|
+ if (reauthPopup && !reauthPopup.closed)
|
|
64
|
+ {
|
|
65
|
+ reauthPopup.close();
|
|
66
|
+ }
|
|
67
|
+ reauthPopup = null;
|
|
68
|
p2j.socket.beginInternalReload();
|
|
69
|
window.location.reload();
|
|
70
|
|
|
71
|
Placed before beginInternalReload so that a cancelled navigation still leaves the popup closed - the
|
|
72
|
popup is dead either way at this point, since the deadline plus grace has passed.
|
|
73
|
|
|
74
|
2.2 Alternative: extract the popup teardown
|
|
75
|
---------------------------------------------
|
|
76
|
|
|
77
|
If the duplication with cleanupReauthUi is unwelcome, extract those five lines:
|
|
78
|
|
|
79
|
+ /** Close the re-login popup if one is open. Synchronous, unlike cleanupReauthUi. */
|
|
80
|
+ function closeReauthPopup()
|
|
81
|
+ {
|
|
82
|
+ if (reauthPopup && !reauthPopup.closed)
|
|
83
|
+ {
|
|
84
|
+ reauthPopup.close();
|
|
85
|
+ }
|
|
86
|
+ reauthPopup = null;
|
|
87
|
+ }
|
|
88
|
|
|
89
|
and call it from both the expiry branch and cleanupReauthUi. There are already two further inline
|
|
90
|
copies of the same three lines at :346 and :360 (the success and failure message handlers), so the
|
|
91
|
extraction pays for itself four times over. This is the better change if the file is being touched
|
|
92
|
anyway.
|
|
93
|
|
|
94
|
------------------------------------------------------------------------------------------------
|
|
95
|
3. RELATIONSHIP TO OTHER PROPOSALS
|
|
96
|
------------------------------------------------------------------------------------------------
|
|
97
|
|
|
98
|
proposal-issue-minor-functional-2 introduces p2j.socket.beginInternalReload, shown in the hunk
|
|
99
|
above. If that proposal is not taken, drop that one line;
|
|
100
|
this fix is independent of it.
|
|
101
|
proposal-issue-minor-functional-3 reorders the same branch (reloadRequested guard). If both
|
|
102
|
are taken, the popup close belongs inside the
|
|
103
|
"first time through" path, not on every tick.
|
|
104
|
|
|
105
|
------------------------------------------------------------------------------------------------
|
|
106
|
4. RISK AND VERIFICATION
|
|
107
|
------------------------------------------------------------------------------------------------
|
|
108
|
|
|
109
|
Risk: negligible. window.close() on an already-closed or never-opened window is guarded, and the
|
|
110
|
popup is unusable at this point by definition.
|
|
111
|
|
|
112
|
One caveat: a popup that navigated cross-origin to the IdP may not be closeable by the opener in
|
|
113
|
every browser (window.close() is honoured for script-opened windows, but a cross-origin document can
|
|
114
|
restrict what the opener may do). The call is best-effort; where it is refused the outcome is
|
|
115
|
today's behaviour, no worse.
|
|
116
|
|
|
117
|
Verification
|
|
118
|
1. Trigger SSO re-auth, click Re-login, and leave the IdP popup open without signing in. Wait out
|
|
119
|
the countdown plus REAUTH_GRACE_MS. Expected: the popup closes as the opener reloads. Pre-fix
|
|
120
|
it remains open on the IdP page.
|
|
121
|
2. Same, but sign in successfully - confirm the existing success path (which closes the popup at
|
|
122
|
:346) is unchanged.
|
|
123
|
3. Same, but close the popup manually before the deadline - confirm no error from closing an
|
|
124
|
already-closed window.
|
|
125
|
4. Confirm the Logout button path still closes the popup via cleanupReauthUi(true).
|