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.
This message was sent by Atlassian Jira (v9.3.0#930000-sha1:287aeb6)
If image attachments aren't displayed, see this article.