One additional data point that may help confirm this is a deterministic WKWebView limitation rather than intermittent behaviour.
By correlating nginx access logs across multiple failures, I’ve found that the same OIDC state value is repeatedly reused by the in-app browser when users retry login.
Example (sanitised):
• Same state hash seen 14 times
• All requests from:
Snapchat/13.77.0.51 (like Safari…, panda)
• Spread across ~1 hour of repeated login attempts
• Each attempt results in:
/auth/oidc/callback → /auth/failure?message=csrf_detected
This strongly suggests that:
• the browser never successfully stores the session cookie containing the original state
• each retry reuses the same stale state parameter
• Discourse is therefore correctly rejecting the callback every time
In contrast, when the same user opens the link in Safari, a new state is generated and the login succeeds immediately.
So this appears to be a fully deterministic failure mode in certain iOS in-app browsers rather than a timing or race condition.
From Discourse’s perspective, the CSRF protection is behaving exactly as intended - the browser environment simply cannot maintain the required session continuity.
I think this further supports that:
• this is not something a theme component or client JS can mitigate
• and that the only reliable handling is server-side messaging and documentation
Posting in case this data point is useful for confirming expected behaviour.