open-webui/backend/open_webui
Classic298 20c8aaed76 fix: only accept token exchange tokens minted for trusted OAuth clients
The OAuth token exchange endpoint accepts an access token in the request body and validates it by calling the provider's userinfo endpoint. That endpoint confirms a token is valid but never reveals which OAuth client it was issued to, so a token minted for any client of the same provider was accepted. Anyone able to obtain an access token for a user, for instance by having them sign in to an unrelated OAuth application, could exchange it for an Open WebUI session as that user. The account's role was carried over unchanged, so a targeted admin yielded an admin session.

`OAUTH_ALLOWED_DOMAINS` does not cover this. It filters which user identities may sign in, and an impersonated user is a legitimate member of an allowed domain, so the check passes. The missing question is not who the user is but which client vouched for them.

Resolve the issuing client through RFC 7662 introspection and require it to appear in the new `OAUTH_TOKEN_EXCHANGE_ALLOWED_AUDIENCES` allowlist. Only the introspection response's `client_id` is trusted, never `aud`: `client_id` identifies the client the token was issued to, while `aud` names intended resource servers and several identity providers let any client place another client's id there.

The allowlist is a deploy-time environment variable rather than a database setting, so the endpoint's only authentication boundary cannot be widened at runtime from an admin session. It has no default, and an unset allowlist rejects every exchange. That is a breaking change for existing deployments, which must now name the clients they trust, but the previous behaviour trusted every client of the provider and the endpoint is off by default.

This requires a provider that implements RFC 7662. Google, Microsoft and GitHub do not, so token exchange no longer works against them and the rejection is logged. Supporting those providers needs an audience-bound assertion the caller can supply, such as an OIDC ID token validated against the provider's JWKS, which can be added without changing the current request contract.
2026-07-26 21:46:12 +02:00
..
data refac: mv backend files to /open_webui dir 2024-09-04 16:54:48 +02:00
internal perf: cut per-request database session overhead (#27385) 2026-07-23 17:49:48 -05:00
migrations refac 2026-07-24 01:54:36 -04:00
models fix: don't seed non-persistent config keys (oauth.* with flag off) (#26928) 2026-07-24 00:58:30 -05:00
retrieval fix: honor Admin UI web loader settings in get_web_loader (#26749) 2026-07-24 01:30:47 -05:00
routers fix: only accept token exchange tokens minted for trusted OAuth clients 2026-07-26 21:46:12 +02:00
socket refac 2026-07-23 22:20:50 -04:00
static refac 2026-07-20 22:27:13 -04:00
storage refac: modernize type annotations (PEP 604 / PEP 585) 2026-05-12 17:10:15 +09:00
tools fix: detect bare pipe alternation as regex in grep_knowledge_files (#26795) 2026-07-23 23:33:35 -04:00
utils fix: deny chained access to unregistered base models for non-admins (#26905) 2026-07-24 01:32:32 -05:00
__init__.py refac 2026-06-17 03:01:11 +02:00
alembic.ini fix: Alembic CLI commands from failing 2025-08-15 04:17:47 -04:00
config.py refac 2026-07-24 01:19:28 -04:00
constants.py refac 2026-06-29 05:46:51 -05:00
env.py fix: only accept token exchange tokens minted for trusted OAuth clients 2026-07-26 21:46:12 +02:00
events.py Log upstream provider rejections at warn/error level (#27238) 2026-07-23 03:42:48 -04:00
functions.py refac 2026-07-23 03:39:56 -04:00
main.py refac 2026-07-24 00:47:12 -04:00
tasks.py refac 2026-07-16 21:57:43 -04:00