[Bug] Browser Back after an OIDC login re-submits the spent state; auth_openid answers 500 (MismatchingStateError) instead of restarting login
AI assistance disclosure
Per CONTRIBUTING.md: this issue was written by Claude Code (Anthropic) from measurements it took under my direction on my own RomM instance; I reviewed the report and the evidence. If replies are written with AI assistance I will say so.
RomM version
5.2.0. backend/endpoints/auth.py line 290 (auth_openid: token = await oauth.openid.authorize_access_token(request)) has no handler for authlib's MismatchingStateError; unchanged on master at 14fc8d9.
Describe the bug
After a successful OIDC login, one press of the browser's Back button on the home page re-enters the provider's authorization page, which still carries the original state. The provider (Authelia here) re-authenticates and redirects to /api/oauth/openid with a new code and the old, already-consumed state. RomM raises MismatchingStateError and the user is left on a bare Internal Server Error page instead of being sent back to login.
A second way in: the v2 UI's gamepad navigation treats Standard button 1 (B, ○ on a DualShock 4) as back, so a pad player meets the same 500 on their first ○ at the home page.
To Reproduce
- Sign in with OIDC (Authelia, with 2FA). Land on
/. - Press Back once. The history entry is
https://auth.example/?rd=…/api/oidc/authorization?…&state=<original>. - Complete the provider's prompt. It redirects to
/api/oauth/openid?code=<new>&state=<original>. - RomM answers 500. Log:
ERROR: [RomM][httptools_impl][2026-09-16 12:01:18] Exception in ASGI application
File "/backend/endpoints/auth.py", line 290, in auth_openid
token = await oauth.openid.authorize_access_token(request)
raise MismatchingStateError()
authlib.integrations.base_client.errors.MismatchingStateError: mismatching_state: CSRF Warning! State not equal in request and response.
GET /api/oauth/openid?code=…&state=wrkRHIxoasz9gaxLJljVxPrBFvNO28 500The same state had been accepted (307) four minutes earlier for the real login.
Expected behavior
authlib's rejection of a stale state is correct; the response is not. Suggestion, as a suggestion: catch MismatchingStateError (or authlib.integrations.base_client.errors.OAuthError) around line 290 and redirect to /login so a fresh flow starts, rather than a 500. If the user already has a valid session cookie, redirecting to / would be even kinder.
Desktop
- OS: macOS 27.0
- Browser: Chromium 149.0.7827.55; first seen with a gamepad in the v2 UI, reproduced with the plain Back button
- Provider: Authelia (OIDC), 2FA on
Source: rommapp/romm