Hi everyone,
while trying to explain and fix flickering integration tests, I found that there is a true race condition in XWiki when logging out while an editor is open.
Basically, there are two requests that race:
- The actual logout request.
- The request for unlocking, triggered when the editor page is unloaded, which continues in the background.
There are two possible risks here:
- The unlocking of the page might not succeed, as the user’s session has already been invalidated.
- The unlocking request might mess with the session or persistent login cookies that are set/invalidated by the logout.
Solution 1: Unauthenticated unlock
One idea I had to clean up this mess is to make the unlock request unauthenticated, using credentials: 'omit'. Instead, the request should be authenticated by a random token that is unique to each lock and stored together with the lock, created when the lock is created. That way, unlock requests always succeed and don’t interfere with a logout.
There are several issues with this approach, though:
- Some authenticators might be placed on a layer in front of XWiki such that XWiki in fact isn’t accessible when using
credentials: 'omit'. As a way to mitigate that, we could detect that situation client-side once and store in a cookie if XWiki can be reached unauthenticated. - Unlocking currently also deletes the temporary uploads for the current session, which wouldn’t be available in an unauthenticated request.
- Quite some added complexity for the tokens.
Solution 2: Unlock early on logout
We could add a JavaScript event handler on the logout link that performs the unlock request early, waiting for the request to finish before redirecting to the logout action. Not my favorite in terms of coupling between logout link and unlocking, and a bit ugly in the sense that logout might seemingly stop working - but we could minimize the impact when not in an editor, and display some notification that locks are released, so the user knows what’s going on.
In integration tests, we normally don’t use the logout link so we would need to decide what to do there, if we start using the actual logout link or if we do something else (like trigger the same mechanism as the logout link uses if we can make it re-usable).
Solution 3: Don’t care about unlock
We could decide to not care about unlock during logout and just ensure that the unlock request doesn’t interfere with the logout request by being careful with server-side steps/adding a little bit of server-side synchronization.
Do you have any opinions? I think solution 2 would be best, solution 3 being a good fallback as I would consider the use case unlikely. So +1 for 2 and 3, and -0 for 1 as it feels too complex and fragile.