There are 4 updates, 1 comment.
 
 
OpenId Connect / cid:jira-generated-image-avatar-e57565bf-dbf2-4bf7-9980-e32462489991 OIDC-299 Closed

Race Condition in Parallel OIDC Auth Flows

 
View issue   ยท   Add comment
 

4 updates

 
cid:jira-generated-image-avatar-cc66811c-e252-4d3d-a31e-54e3b26b9902 Changes by Thomas Mortagne on 15/Oct/26 19:30
 
Fix Version: 2.29.0
Assignee: Thomas Mortagne
Resolution: Fixed
Status: Open Closed
 
 

1 comment

 
cid:jira-generated-image-avatar-cc66811c-e252-4d3d-a31e-54e3b26b9902 Thomas Mortagne on 15/Oct/26 19:30
 

Thanks for the analysis and the patch, the diagnosis was right: the state was stored only once per session, so only the last started authentication could complete.

The attached patch could not be applied as is, mainly because:

  • each authentication start clears the whole OIDC session, which would also remove the state stored by the other windows
  • the state is not the only information specific to an authentication: the nonce, the PKCE code verifier, the URL to go back to and the provider are also stored in the session, and they would still be the ones of the last started authentication (the code exchange would fail with the wrong PKCE code verifier, for example)

The fix takes a different approach:

  • each authentication start saves its own copy of what it stored in the session (state, nonce, PKCE code verifier, return URL, etc.), indexed by state, and the callback restores the copy matching the state sent back by the provider
  • at most 10 authentications in progress are remembered per session, the oldest ones being forgotten first
  • when the callback of an authentication arrives after the user was already authenticated by another one, it just redirects to the page where that authentication started, without asking the provider for a token again
  • the callbacks and authentication starts of a same session are now handled one at a time, so that windows coming back at the same time (which is the most common case when the user is still authenticated on the provider side) don't mix their information
  • since a callback can now wait for another one, the requests sent to the provider now have a connection timeout (10s by default) and a read timeout (30s by default), configurable with the oidc.http.connectTimeout and oidc.http.readTimeout properties in xwiki.properties (in milliseconds, 0 for no limit)

PS: This post was co-written with Claude Code.