Project

General

Profile

issue_9.txt

Sergey Ivanovskiy, 05/22/2026 03:56 PM

Download (2.04 KB)

 
1
ISSUE DESCRIPTION:
2
*[MAJOR]* _functional_ @SsoReauth@.@btnRelogin.onclick@: After @sso-reauth-failed@ the user is invited to click Re-login again, but the captured @reauthUrl@ (sso_reauth.js:137) contains the same OIDC @state@ parameter; @OidcSsoAuthenticator.processReauthCallback@ at line 347 calls @lookupAndRemoveReauthState(state)@ BEFORE the token exchange, consuming the state on the first @/reauthcb@ hit. Any retry returns "Invalid or expired re-auth state". The client must request a fresh re-auth URL (or the server must keep the state until exchange succeeds).
3

    
4
PROPOSED FIX:
5
Move the lookupAndRemoveReauthState() call so it executes only on a successful token exchange. The state remains valid for the user's second Re-login attempt if the first OIDC exchange fails; on success the state is consumed exactly once. This is preferred over (a) and (b) because it requires no extra round trip or new message type and keeps the existing reauthUrl reusable until success.
6

    
7
CHANGES:
8
src/com/goldencode/p2j/security/OidcSsoAuthenticator.java (around line 347, in processReauthCallback)
9

    
10
Before (illustrative):
11
   public Result processReauthCallback(String code, String state)
12
   {
13
      if (lookupAndRemoveReauthState(state) == null)
14
      {
15
         return Result.failure("Invalid or expired re-auth state");
16
      }
17
      // ... exchange code for tokens ...
18
   }
19

    
20
After:
21
   public Result processReauthCallback(String code, String state)
22
   {
23
      // Validate the state exists but do NOT consume it yet; otherwise a failed
24
      // token exchange would invalidate the state and prevent the user from retrying
25
      // Re-login from the same overlay (the captured reauthUrl still carries this state).
26
      if (peekReauthState(state) == null)
27
      {
28
         return Result.failure("Invalid or expired re-auth state");
29
      }
30
      Result result = exchangeCodeForTokens(code, state);
31
      if (result != null && result.isSuccess())
32
      {
33
         // Only consume the state after a successful exchange.
34
         lookupAndRemoveReauthState(state);
35
      }
36
      return result;
37
   }