[PR #17210] [CLOSED] feat: server-side OAuth token management system #24358

Closed
opened 2026-04-20 05:21:43 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/open-webui/open-webui/pull/17210
Author: @Classic298
Created: 9/4/2025
Status: Closed

Base: devHead: dev


📝 Commits (10+)

📊 Changes

10 files changed (+999 additions, -78 deletions)

View changed files

📝 backend/open_webui/config.py (+29 -0)
backend/open_webui/migrations/versions/add_oauth_sessions_table.py (+49 -0)
backend/open_webui/models/oauth_session.py (+205 -0)
📝 backend/open_webui/routers/openai.py (+98 -45)
📝 backend/open_webui/utils/middleware.py (+10 -0)
📝 backend/open_webui/utils/oauth.py (+37 -17)
backend/open_webui/utils/oauth_tokens.py (+329 -0)
backend/open_webui/utils/token_forwarding.py (+224 -0)
📝 backend/open_webui/utils/tools.py (+11 -16)
📝 src/lib/components/AddServerModal.svelte (+7 -0)

📄 Description

Pull Request Checklist

Note to first-time contributors: Please open a discussion post in Discussions and describe your changes before submitting a pull request.

Before submitting, make sure you've checked the following:

  • Target branch: Please verify that the pull request targets the dev branch.
  • Description: Provide a concise description of the changes made in this pull request.
  • Changelog: Ensure a changelog entry following the format of Keep a Changelog is added at the bottom of the PR description.
  • Documentation: Have you updated relevant documentation Open WebUI Docs, or other documentation sources?
  • Dependencies: Are there any new dependencies? Have you updated the dependency versions in the documentation?
  • Testing: Have you written and run sufficient tests to validate the changes?
  • Code review: Have you performed a self-review of your code, addressing any coding standard issues and ensuring adherence to the project's coding standards?
  • Prefix: To clearly categorize this pull request, prefix the pull request title using one of the following:
    • feat: Introduces a new feature or enhancement to the codebase

Changelog Entry

Description

  • Implements a comprehensive, server-side OAuth token management system to resolve multiple interconnected issues related to token storage, refresh, and forwarding. This transforms Open WebUI into a more robust authentication hub, enabling seamless and secure token propagation to downstream services.

Added

  • Server-Side OAuth Session Storage: New oauth_session database table to securely store encrypted OAuth tokens, eliminating browser cookie size limits.
  • Automatic Token Refresh Mechanism: A new OAuthTokenManager service that automatically refreshes expired or near-expiry tokens for providers like Google, Microsoft, and OIDC.
  • Centralized Token Forwarding Service: A single, unified TokenForwardingService to provide valid OAuth tokens to various parts of the application (OpenAI router, Tools, etc.).
  • New Configuration Variables: Added ENABLE_OAUTH_TOKEN_FORWARDING, OAUTH_TOKEN_FORWARDING_SERVICES, OAUTH_TOKEN_ENCRYPTION_KEY, and OAUTH_RESPONSE_MODE for complete control over the new feature.

Changed

  • OAuth Callback Handler: Modified oauth.py to store tokens server-side instead of directly in cookies when the new feature is enabled.
  • OpenAI Router: Updated openai.py to prioritize forwarding the user's OAuth token as the Bearer token, falling back to the static API key.
  • Tool Context: Enhanced middleware.py to inject the __oauth_token__ directly into the tool context, providing a clean access pattern.

Deprecated

  • Legacy Token Handling: The practice of storing the full id_token in the oauth_id_token cookie is now a fallback mechanism and is functionally deprecated by the new server-side session system.

Fixed

  • #17178: Solves large token issues (e.g., AD FS with many groups) by moving storage server-side; adds OAUTH_RESPONSE_MODE for form_post.
  • #11029: Solves the stale token issue by implementing automatic background refreshing of OAuth tokens.
  • #17183: Provides a clean, documented __oauth_token__ parameter for tools, removing the need for fragile cookie scraping.
  • #8957: Allows forwarding user-specific OAuth tokens to OpenAI-compatible endpoints instead of a shared static API key.

Security

  • Encrypted Token Storage: All OAuth tokens are now encrypted at rest in the database using Fernet symmetric encryption via the cryptography library.
  • Reduced Browser Exposure: Only a simple, opaque session ID is stored in browser cookies, minimizing the attack surface.

Breaking Changes

  • None: The entire feature is behind the ENABLE_OAUTH_TOKEN_FORWARDING feature flag and is disabled by default, ensuring full backward compatibility with existing deployments.

Additional Information

Caution

This is an untested prototype developed for @tjbck to realize the architectural vision of "one token to rule them all." It requires thorough testing before being considered for a production merge.

Architectural Rationale & Deep Dive

This pull request addresses a set of deeply interconnected issues stemming from a single architectural flaw: treating dynamic, short-lived OAuth tokens as if they were static, long-lived session cookies. The previous implementation led to problems with token size, expiration, and accessibility.

This new architecture refactors OAuth tokens into what they truly are: managed, secure session credentials.

  1. How It Works:

    • During the OAuth callback, instead of stuffing the large token into a cookie, we now encrypt the entire token payload (including access_token, id_token, and refresh_token) and store it in a new oauth_session database table.
    • A small, secure, httponly cookie containing a unique oauth_session_id is sent to the browser.
    • When a downstream service (like the OpenAI router or a tool) needs a token, it calls the new centralized TokenForwardingService.
    • This service uses the user's ID and the session cookie to retrieve the encrypted tokens from the database, decrypts them, and—critically—checks if they are expired. If they are, it automatically uses the stored refresh_token to get new ones before returning a valid token.
  2. How It Solves the Core Problems:

    • Large Tokens (#17178): The browser only ever sees a tiny session ID. The massive AD FS token with hundreds of group claims now lives securely on the server, completely solving the cookie size limit problem.
    • Stale/Expired Tokens (#11029): The TokenForwardingService acts as a "smart" gatekeeper. By checking the token's expiry on every request and refreshing it just-in-time, it guarantees that any service asking for a token always receives a valid one. This eliminates the "silent failure" scenario where tools would break after the token expired.
    • Clean Token Access (#17183, #8957): Instead of different parts of the code trying to scrape cookies, they now have a single, clean entry point: token_forwarding_service.get_oauth_token_for_service(...). This provides a stable and documented API for accessing tokens, whether for tools (__oauth_token__) or for the OpenAI router (Authorization header). This resolves the core request of both issues and the rejected PR #9673 by providing a clean, backward-compatible, and well-architected solution.

Implementation Notes

Caution

Alembic Migration is a PLACEHOLDER
The Alembic migration file included in this PR (add_oauth_sessions_table.py) is a correctly structured placeholder. However, due to environment limitations, it was not generated directly via alembic revision --autogenerate. It MUST be regenerated or manually validated by a maintainer with a full development environment before this branch is merged into dev to ensure the revision chain is correct.

Caution

The cryptography dependency is already present in pyproject.toml, so no new dependencies are required. However, for multi-replica production deployments (e.g., Kubernetes), the OAUTH_TOKEN_ENCRYPTION_KEY environment variable must be explicitly set and shared across all instances to ensure session continuity. Auto-generation will lead to decryption failures.

Screenshots or Videos

  • N/A

Contributor License Agreement

By submitting this pull request, I confirm that I have read and fully agree to the Contributor License Agreement (CLA), and I am providing my contributions under its terms.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/open-webui/open-webui/pull/17210 **Author:** [@Classic298](https://github.com/Classic298) **Created:** 9/4/2025 **Status:** ❌ Closed **Base:** `dev` ← **Head:** `dev` --- ### 📝 Commits (10+) - [`9289272`](https://github.com/open-webui/open-webui/commit/9289272588839635720380e697a5ffadd17da252) Create oauth_session.py - [`54075b2`](https://github.com/open-webui/open-webui/commit/54075b2e3253f93ccd2fbc30787be27fe6350e17) Update config.py - [`6ccf301`](https://github.com/open-webui/open-webui/commit/6ccf30146591a0951dc71e3e45ee54801b177942) Create oauth_tokens.py - [`78a32c3`](https://github.com/open-webui/open-webui/commit/78a32c331c076711fece2c512a8e0dac5f156740) Create token_forwarding.py - [`e5252a0`](https://github.com/open-webui/open-webui/commit/e5252a07698725fb89743b73a07890f7ee0576d4) Update oauth.py - [`76be7c6`](https://github.com/open-webui/open-webui/commit/76be7c67c3362e029986c9c7490a000aeb90aa53) Update openai.py - [`ac5e01f`](https://github.com/open-webui/open-webui/commit/ac5e01f0073fe28d881cc55000897fe23331b507) Update middleware.py - [`a6f618f`](https://github.com/open-webui/open-webui/commit/a6f618fb17af9dc9e0993144c272e372b6ccdb81) Create add_oauth_sessions_table.py - [`96c826d`](https://github.com/open-webui/open-webui/commit/96c826d7ebfa7e2e3c9087394814fd1e02a2be0c) Update openai.py - [`3a19722`](https://github.com/open-webui/open-webui/commit/3a19722dd9049512de538150c97ba0985204c38d) Merge branch 'open-webui:dev' into dev ### 📊 Changes **10 files changed** (+999 additions, -78 deletions) <details> <summary>View changed files</summary> 📝 `backend/open_webui/config.py` (+29 -0) ➕ `backend/open_webui/migrations/versions/add_oauth_sessions_table.py` (+49 -0) ➕ `backend/open_webui/models/oauth_session.py` (+205 -0) 📝 `backend/open_webui/routers/openai.py` (+98 -45) 📝 `backend/open_webui/utils/middleware.py` (+10 -0) 📝 `backend/open_webui/utils/oauth.py` (+37 -17) ➕ `backend/open_webui/utils/oauth_tokens.py` (+329 -0) ➕ `backend/open_webui/utils/token_forwarding.py` (+224 -0) 📝 `backend/open_webui/utils/tools.py` (+11 -16) 📝 `src/lib/components/AddServerModal.svelte` (+7 -0) </details> ### 📄 Description # Pull Request Checklist ### Note to first-time contributors: Please open a discussion post in [Discussions](https://github.com/open-webui/open-webui/discussions) and describe your changes before submitting a pull request. **Before submitting, make sure you've checked the following:** - [x] **Target branch:** Please verify that the pull request targets the `dev` branch. - [x] **Description:** Provide a concise description of the changes made in this pull request. - [x] **Changelog:** Ensure a changelog entry following the format of [Keep a Changelog](https://keepachangelog.com/) is added at the bottom of the PR description. - [ ] **Documentation:** Have you updated relevant documentation [Open WebUI Docs](https://github.com/open-webui/docs), or other documentation sources? - [x] **Dependencies:** Are there any new dependencies? Have you updated the dependency versions in the documentation? - [ ] **Testing:** Have you written and run sufficient tests to validate the changes? - [x] **Code review:** Have you performed a self-review of your code, addressing any coding standard issues and ensuring adherence to the project's coding standards? - [x] **Prefix:** To clearly categorize this pull request, prefix the pull request title using one of the following: - **feat**: Introduces a new feature or enhancement to the codebase # Changelog Entry ### Description - Implements a comprehensive, server-side OAuth token management system to resolve multiple interconnected issues related to token storage, refresh, and forwarding. This transforms Open WebUI into a more robust authentication hub, enabling seamless and secure token propagation to downstream services. ### Added - **Server-Side OAuth Session Storage**: New `oauth_session` database table to securely store encrypted OAuth tokens, eliminating browser cookie size limits. - **Automatic Token Refresh Mechanism**: A new `OAuthTokenManager` service that automatically refreshes expired or near-expiry tokens for providers like Google, Microsoft, and OIDC. - **Centralized Token Forwarding Service**: A single, unified `TokenForwardingService` to provide valid OAuth tokens to various parts of the application (OpenAI router, Tools, etc.). - **New Configuration Variables**: Added `ENABLE_OAUTH_TOKEN_FORWARDING`, `OAUTH_TOKEN_FORWARDING_SERVICES`, `OAUTH_TOKEN_ENCRYPTION_KEY`, and `OAUTH_RESPONSE_MODE` for complete control over the new feature. ### Changed - **OAuth Callback Handler**: Modified `oauth.py` to store tokens server-side instead of directly in cookies when the new feature is enabled. - **OpenAI Router**: Updated `openai.py` to prioritize forwarding the user's OAuth token as the Bearer token, falling back to the static API key. - **Tool Context**: Enhanced `middleware.py` to inject the `__oauth_token__` directly into the tool context, providing a clean access pattern. ### Deprecated - **Legacy Token Handling**: The practice of storing the full `id_token` in the `oauth_id_token` cookie is now a fallback mechanism and is functionally deprecated by the new server-side session system. ### Fixed - **#17178**: Solves large token issues (e.g., AD FS with many groups) by moving storage server-side; adds `OAUTH_RESPONSE_MODE` for `form_post`. - **#11029**: Solves the stale token issue by implementing automatic background refreshing of OAuth tokens. - **#17183**: Provides a clean, documented `__oauth_token__` parameter for tools, removing the need for fragile cookie scraping. - **#8957**: Allows forwarding user-specific OAuth tokens to OpenAI-compatible endpoints instead of a shared static API key. ### Security - **Encrypted Token Storage**: All OAuth tokens are now encrypted at rest in the database using Fernet symmetric encryption via the `cryptography` library. - **Reduced Browser Exposure**: Only a simple, opaque session ID is stored in browser cookies, minimizing the attack surface. ### Breaking Changes - **None**: The entire feature is behind the `ENABLE_OAUTH_TOKEN_FORWARDING` feature flag and is disabled by default, ensuring full backward compatibility with existing deployments. --- ### Additional Information > [!CAUTION] > This is an **untested prototype** developed for **@tjbck** to realize the architectural vision of "one token to rule them all." It requires thorough testing before being considered for a production merge. #### Architectural Rationale & Deep Dive This pull request addresses a set of deeply interconnected issues stemming from a single architectural flaw: **treating dynamic, short-lived OAuth tokens as if they were static, long-lived session cookies.** The previous implementation led to problems with token size, expiration, and accessibility. This new architecture refactors OAuth tokens into what they truly are: **managed, secure session credentials.** 1. **How It Works:** * During the OAuth callback, instead of stuffing the large token into a cookie, we now encrypt the entire token payload (including `access_token`, `id_token`, and `refresh_token`) and store it in a new `oauth_session` database table. * A small, secure, `httponly` cookie containing a unique `oauth_session_id` is sent to the browser. * When a downstream service (like the OpenAI router or a tool) needs a token, it calls the new centralized `TokenForwardingService`. * This service uses the user's ID and the session cookie to retrieve the encrypted tokens from the database, decrypts them, and—critically—checks if they are expired. If they are, it automatically uses the stored `refresh_token` to get new ones before returning a valid token. 2. **How It Solves the Core Problems:** * **Large Tokens (#17178)**: The browser only ever sees a tiny session ID. The massive AD FS token with hundreds of group claims now lives securely on the server, completely solving the cookie size limit problem. * **Stale/Expired Tokens (#11029)**: The `TokenForwardingService` acts as a "smart" gatekeeper. By checking the token's expiry on every request and refreshing it just-in-time, it guarantees that any service asking for a token always receives a valid one. This eliminates the "silent failure" scenario where tools would break after the token expired. * **Clean Token Access (#17183, #8957)**: Instead of different parts of the code trying to scrape cookies, they now have a single, clean entry point: `token_forwarding_service.get_oauth_token_for_service(...)`. This provides a stable and documented API for accessing tokens, whether for tools (`__oauth_token__`) or for the OpenAI router (`Authorization` header). This resolves the core request of both issues and the rejected PR #9673 by providing a clean, backward-compatible, and well-architected solution. #### Implementation Notes > [!CAUTION] > **Alembic Migration is a PLACEHOLDER** > The Alembic migration file included in this PR (`add_oauth_sessions_table.py`) is a correctly structured placeholder. However, due to environment limitations, it was not generated directly via `alembic revision --autogenerate`. It **MUST be regenerated or manually validated** by a maintainer with a full development environment before this branch is merged into `dev` to ensure the revision chain is correct. > [!CAUTION] > The `cryptography` dependency is already present in `pyproject.toml`, so no new dependencies are required. However, for multi-replica production deployments (e.g., Kubernetes), the `OAUTH_TOKEN_ENCRYPTION_KEY` environment variable **must be explicitly set and shared** across all instances to ensure session continuity. Auto-generation will lead to decryption failures. ### Screenshots or Videos - N/A ### Contributor License Agreement By submitting this pull request, I confirm that I have read and fully agree to the [Contributor License Agreement (CLA)](/CONTRIBUTOR_LICENSE_AGREEMENT), and I am providing my contributions under its terms. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-04-20 05:21:43 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/open-webui#24358