-
-
Notifications
You must be signed in to change notification settings - Fork 17.9k
Description
Description
Each OIDC login deletes all existing OAuth sessions for the same user+provider before creating a new one. This means logging in from a second device/browser invalidates the first device's session, causing tool calls to fail with "Missing Authorization header" until the user re-authenticates.
Steps to Reproduce
- Log in on Device A (e.g., desktop browser) — OIDC session created,
oauth_session_idcookie set - Log in on Device B (e.g., mobile/PWA) — new session created, Device A's session deleted from DB
- On Device A, trigger a tool call that requires
system_oauthauthorization - Tool call fails — middleware looks up the session ID from the cookie, but the row no longer exists in
oauth_session
Expected Behavior
Multiple concurrent OIDC sessions should coexist. Logging in on Device B should not invalidate Device A's session.
Root Cause
In backend/open_webui/utils/oauth.py, the handle_callback() method in OAuthManager (around line 1682) deletes all existing sessions for the user+provider before creating a new one:
# Clean up any existing sessions for this user/provider first
sessions = OAuthSessions.get_sessions_by_user_id(user.id, db=db)
for session in sessions:
if session.provider == provider:
OAuthSessions.delete_session_by_id(session.id, db=db)The same pattern exists in OAuthClientManager.handle_callback() (line 879-883) for MCP tool sessions.
Why Refresh Doesn't Help
The OIDC token refresh mechanism works correctly — we confirmed sessions expired for up to 5 days are successfully refreshed using Okta's 7-day refresh token. The problem is that the session row is deleted, so get_session_by_id_and_user_id() returns None before any refresh logic is reached.
Architecture Already Supports Multi-Session
The rest of the codebase is already compatible with multiple concurrent sessions:
- Database schema:
oauth_sessiontable has no unique constraint on(user_id, provider)— multiple rows per user/provider are valid - Cookie handling:
oauth_session_idcookie stores the session UUID, so each device naturally points to its own session - Middleware lookup: Uses
get_session_by_id_and_user_id(session_id, user_id)— looks up by specific UUID, not by provider - Logout flow: Deletes only the current session (by cookie UUID), leaving other sessions intact
- Token refresh: Operates on specific session by ID
Suggested Fix
- Remove the session deletion loop in
OAuthManager.handle_callback()for OIDC sessions (thefor session in sessionsblock) - Optionally add a max session limit (e.g., 10 active OIDC sessions per user) to prevent unbounded growth — prune oldest sessions when the limit is exceeded
- The
OAuthClientManager(MCP tool sessions) may reasonably keep the replace-on-reauth behavior since those tokens are typically one-per-provider
Environment
- Open WebUI version: v0.8.3 (also confirmed in
mainbranch) - OIDC Provider: Okta (access token: 1h, refresh token: 7d)
- Deployment: Google Cloud Run