[GH-ISSUE #19809] MCP OAuth tokens not proactively refreshed, causing session loss after 1 hour #57670

Closed
opened 2026-05-05 21:21:19 -05:00 by GiteaMirror · 3 comments
Owner

Originally created by @jamie-dit on GitHub (Dec 8, 2025).
Original GitHub issue: https://github.com/open-webui/open-webui/issues/19809

Bug Description

MCP OAuth 2.1 tokens (e.g., Notion) expire after ~1 hour but are not proactively refreshed. The current refresh mechanism only triggers when get_oauth_token() is called within 5 minutes of expiration. If the user isn't actively using the MCP tool during that window, the token expires, and the session is deleted on the next access attempt.

Steps to Reproduce

  1. Configure an MCP server with OAuth 2.1 authentication (e.g., Notion)
  2. Complete the OAuth authorization flow
  3. Wait ~1 hour without using the MCP tool
  4. Attempt to use the MCP tool

Expected Behavior

The OAuth token should be automatically refreshed in the background before expiration using the stored refresh_token, maintaining a valid session without user intervention.

Actual Behavior

  • Token expires after 1 hour (Notion's token lifetime)
  • On next use, get_oauth_token() finds the expired session
  • Refresh attempt fails or session lookup fails
  • Session is deleted: "Token refresh failed... deleting session"
  • User must manually re-authorize via Settings → Integrations → Tools

Logs

WARNING | open_webui.utils.oauth:get_oauth_token:572 - No OAuth session found for user XXX, client_id mcp:ntn
INFO    | httpx._client - HTTP Request: POST https://mcp.notion.com/mcp "HTTP/1.1 401 Unauthorized"

Root Cause Analysis

In backend/open_webui/utils/oauth.py, the get_oauth_token() method has proactive refresh logic:

if force_refresh or datetime.now() + timedelta(minutes=5) >= datetime.fromtimestamp(session.expires_at):
    refreshed_token = await self._refresh_token(session)

However, this only runs on-demand when get_oauth_token() is called. There is no background task to refresh tokens before they expire.

The oauth_session table has an index on expires_at but no method to query sessions needing refresh.

Proposed Solution

Add a periodic background task that:

  1. Queries OAuth sessions expiring within the next 10 minutes
  2. Proactively refreshes them using the stored refresh_token
  3. Runs every 5 minutes via asyncio.create_task() in the lifespan handler

Similar to the existing periodic_usage_pool_cleanup() pattern.

Environment

  • Open WebUI version: 0.6.41
  • MCP Server: Notion (mcp.notion.com)
  • OAuth type: OAuth 2.1 with PKCE
  • #18311 - When resuming a chat with an expired MCP OAuth 2.1 token, chat hangs
  • #11029 - OAuth ID token does not refresh
Originally created by @jamie-dit on GitHub (Dec 8, 2025). Original GitHub issue: https://github.com/open-webui/open-webui/issues/19809 ## Bug Description MCP OAuth 2.1 tokens (e.g., Notion) expire after ~1 hour but are not proactively refreshed. The current refresh mechanism only triggers when `get_oauth_token()` is called within 5 minutes of expiration. If the user isn't actively using the MCP tool during that window, the token expires, and the session is deleted on the next access attempt. ## Steps to Reproduce 1. Configure an MCP server with OAuth 2.1 authentication (e.g., Notion) 2. Complete the OAuth authorization flow 3. Wait ~1 hour without using the MCP tool 4. Attempt to use the MCP tool ## Expected Behavior The OAuth token should be automatically refreshed in the background before expiration using the stored `refresh_token`, maintaining a valid session without user intervention. ## Actual Behavior - Token expires after 1 hour (Notion's token lifetime) - On next use, `get_oauth_token()` finds the expired session - Refresh attempt fails or session lookup fails - Session is deleted: `"Token refresh failed... deleting session"` - User must manually re-authorize via Settings → Integrations → Tools ## Logs ``` WARNING | open_webui.utils.oauth:get_oauth_token:572 - No OAuth session found for user XXX, client_id mcp:ntn INFO | httpx._client - HTTP Request: POST https://mcp.notion.com/mcp "HTTP/1.1 401 Unauthorized" ``` ## Root Cause Analysis In `backend/open_webui/utils/oauth.py`, the `get_oauth_token()` method has proactive refresh logic: ```python if force_refresh or datetime.now() + timedelta(minutes=5) >= datetime.fromtimestamp(session.expires_at): refreshed_token = await self._refresh_token(session) ``` However, this only runs **on-demand** when `get_oauth_token()` is called. There is no background task to refresh tokens before they expire. The `oauth_session` table has an index on `expires_at` but no method to query sessions needing refresh. ## Proposed Solution Add a periodic background task that: 1. Queries OAuth sessions expiring within the next 10 minutes 2. Proactively refreshes them using the stored `refresh_token` 3. Runs every 5 minutes via `asyncio.create_task()` in the lifespan handler Similar to the existing `periodic_usage_pool_cleanup()` pattern. ## Environment - Open WebUI version: 0.6.41 - MCP Server: Notion (`mcp.notion.com`) - OAuth type: OAuth 2.1 with PKCE ## Related Issues - #18311 - When resuming a chat with an expired MCP OAuth 2.1 token, chat hangs - #11029 - OAuth ID token does not refresh
Author
Owner

@owui-terminator[bot] commented on GitHub (Dec 8, 2025):

🔍 Similar Issues Found

I found some existing issues that might be related to this one. Please check if any of these are duplicates or contain helpful solutions:

  1. #19794 MCP OAuth 2.1: Not following WWW-Authenticate → Protected Resource → Authorization Server discovery chain
    by jamie-dit • Dec 07, 2025

  2. #18010 issue: MCP OAuth 2.1 flow doesn't match standard (missing code_challenge and resource_url)
    by hsuyuming • Oct 02, 2025 • bug

  3. #14119 feat: Enable per-user authentication in MCP for personalized tool access or consider implementing a proper MCP Client
    by florianchappaz • May 21, 2025


💡 Tips:

  • If this is a duplicate, please consider closing this issue and adding any additional details to the existing one
  • If you found a solution in any of these issues, please share it here to help others

This comment was generated automatically by a bot. Please react with a 👍 if this comment was helpful, or a 👎 if it was not.

<!-- gh-comment-id:3625294730 --> @owui-terminator[bot] commented on GitHub (Dec 8, 2025): 🔍 **Similar Issues Found** I found some existing issues that might be related to this one. Please check if any of these are duplicates or contain helpful solutions: 1. [#19794](https://github.com/open-webui/open-webui/issues/19794) **MCP OAuth 2.1: Not following WWW-Authenticate → Protected Resource → Authorization Server discovery chain** *by jamie-dit • Dec 07, 2025* 2. [#18010](https://github.com/open-webui/open-webui/issues/18010) **issue: MCP OAuth 2.1 flow doesn't match standard (missing code_challenge and resource_url)** *by hsuyuming • Oct 02, 2025 • `bug`* 3. [#14119](https://github.com/open-webui/open-webui/issues/14119) **feat: Enable per-user authentication in MCP for personalized tool access or consider implementing a proper MCP Client** *by florianchappaz • May 21, 2025* --- 💡 **Tips:** - If this is a duplicate, please consider closing this issue and adding any additional details to the existing one - If you found a solution in any of these issues, please share it here to help others *This comment was generated automatically by a bot.* Please react with a 👍 if this comment was helpful, or a 👎 if it was not.
Author
Owner

@jamie-dit commented on GitHub (Dec 8, 2025):

Fix Submitted

A fix has been submitted in PR #19811.

Summary of Fix

The fix adds a background task that proactively refreshes OAuth tokens before they expire:

  1. New method get_expiring_sessions() - Queries OAuth sessions expiring within a specified time window

  2. Enhanced _perform_token_refresh() - Now handles unregistered MCP clients by discovering OAuth info from stored tool server config

  3. Background task periodic_oauth_token_refresh() - Runs every 5 minutes and refreshes tokens expiring within 10 minutes

Testing Confirmed

The fix was tested with a Notion MCP integration:

  • Token refresh triggered automatically before expiration
  • Token expiration updated from 07:42:44 to 08:23:05 (refreshed for another hour)
  • No user re-authentication required
INFO | Found 1 OAuth session(s) expiring soon, attempting refresh...
INFO | Successfully refreshed token for session 9f43ad41-2a6a-4cbc-9cfa-90f228cabba9
INFO | Successfully refreshed MCP OAuth token (provider: mcp:ntn)
<!-- gh-comment-id:3625388275 --> @jamie-dit commented on GitHub (Dec 8, 2025): ## Fix Submitted A fix has been submitted in PR #19811. ### Summary of Fix The fix adds a background task that proactively refreshes OAuth tokens before they expire: 1. **New method `get_expiring_sessions()`** - Queries OAuth sessions expiring within a specified time window 2. **Enhanced `_perform_token_refresh()`** - Now handles unregistered MCP clients by discovering OAuth info from stored tool server config 3. **Background task `periodic_oauth_token_refresh()`** - Runs every 5 minutes and refreshes tokens expiring within 10 minutes ### Testing Confirmed The fix was tested with a Notion MCP integration: - Token refresh triggered automatically before expiration - Token expiration updated from `07:42:44` to `08:23:05` (refreshed for another hour) - No user re-authentication required ``` INFO | Found 1 OAuth session(s) expiring soon, attempting refresh... INFO | Successfully refreshed token for session 9f43ad41-2a6a-4cbc-9cfa-90f228cabba9 INFO | Successfully refreshed MCP OAuth token (provider: mcp:ntn) ```
Author
Owner

@tjbck commented on GitHub (Dec 8, 2025):

Please correct me if I'm wrong, but the current behaviour is working as intended: the access token will be renewed, whether proactively or on demand, as long as the refresh token remains valid. The main issue here seems to be the notion MCP server not providing clients with a lengthy enough refresh token, presumably for valid security reasons.

<!-- gh-comment-id:3627371552 --> @tjbck commented on GitHub (Dec 8, 2025): Please correct me if I'm wrong, but the current behaviour is working as intended: the access token will be renewed, whether proactively or on demand, as long as the refresh token remains valid. The main issue here seems to be the notion MCP server not providing clients with a lengthy enough refresh token, presumably for valid security reasons.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/open-webui#57670