Project

General

Profile

proposal-issue-minor-functional-4.txt

Sergey Ivanovskiy, 08/12/2026 04:15 PM

Download (6.21 KB)

 
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).