[GH-ISSUE #21647] bug: OAuth sessions deleted on re-login, breaking multi-device usage #35074

Closed
opened 2026-04-25 09:16:32 -05:00 by GiteaMirror · 2 comments
Owner

Originally created by @gboston on GitHub (Feb 20, 2026).
Original GitHub issue: https://github.com/open-webui/open-webui/issues/21647

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

  1. Log in on Device A (e.g., desktop browser) — OIDC session created, oauth_session_id cookie set
  2. Log in on Device B (e.g., mobile/PWA) — new session created, Device A's session deleted from DB
  3. On Device A, trigger a tool call that requires system_oauth authorization
  4. 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_session table has no unique constraint on (user_id, provider) — multiple rows per user/provider are valid
  • Cookie handling: oauth_session_id cookie 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

  1. Remove the session deletion loop in OAuthManager.handle_callback() for OIDC sessions (the for session in sessions block)
  2. 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
  3. 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 main branch)
  • OIDC Provider: Okta (access token: 1h, refresh token: 7d)
  • Deployment: Google Cloud Run
Originally created by @gboston on GitHub (Feb 20, 2026). Original GitHub issue: https://github.com/open-webui/open-webui/issues/21647 ### 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 1. Log in on **Device A** (e.g., desktop browser) — OIDC session created, `oauth_session_id` cookie set 2. Log in on **Device B** (e.g., mobile/PWA) — new session created, **Device A's session deleted from DB** 3. On Device A, trigger a tool call that requires `system_oauth` authorization 4. 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: ```python # 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_session` table has no unique constraint on `(user_id, provider)` — multiple rows per user/provider are valid - **Cookie handling**: `oauth_session_id` cookie 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 1. **Remove the session deletion loop** in `OAuthManager.handle_callback()` for OIDC sessions (the `for session in sessions` block) 2. 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 3. 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 `main` branch) - OIDC Provider: Okta (access token: 1h, refresh token: 7d) - Deployment: Google Cloud Run
Author
Owner

@pr-validator-bot commented on GitHub (Feb 20, 2026):

⚠️ Missing Issue Title Prefix

@gboston, your issue title is missing a prefix (e.g., bug:, feat:, docs:).

Please update your issue title to include one of the following prefixes:

  • bug: Bug report or error you've encountered
  • feat: Feature request or enhancement suggestion
  • docs: Documentation issue or improvement request
  • question: Question about usage or functionality
  • help: Request for help or support

Example: bug: Login fails when using special characters in password

<!-- gh-comment-id:3933993534 --> @pr-validator-bot commented on GitHub (Feb 20, 2026): # ⚠️ Missing Issue Title Prefix @gboston, your issue title is missing a prefix (e.g., `bug:`, `feat:`, `docs:`). Please update your issue title to include one of the following prefixes: - **bug**: Bug report or error you've encountered - **feat**: Feature request or enhancement suggestion - **docs**: Documentation issue or improvement request - **question**: Question about usage or functionality - **help**: Request for help or support Example: `bug: Login fails when using special characters in password`
Author
Owner

@tjbck commented on GitHub (Feb 20, 2026):

ae05586fda

<!-- gh-comment-id:3937521566 --> @tjbck commented on GitHub (Feb 20, 2026): ae05586fdabf318d551b53ede41575355d3b9e2b
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/open-webui#35074