From 7be009649a0a94008484335c492ab0af18fde41f Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 29 Jun 2026 02:57:58 -0500 Subject: [PATCH] refac --- backend/open_webui/main.py | 3 ++ backend/open_webui/routers/configs.py | 4 ++- backend/open_webui/utils/oauth.py | 21 +++++++++-- src/lib/apis/configs/index.ts | 1 + src/lib/components/AddToolServerModal.svelte | 37 ++++++++++++++++++-- 5 files changed, 60 insertions(+), 6 deletions(-) diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index 9c28e54fca..166ee55b3d 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -2265,6 +2265,7 @@ async def register_client(request, client_id: str) -> bool: server_url = connection.get('url') auth_type = connection.get('auth_type', 'none') + oauth_scope = (connection.get('info') or {}).get('oauth_scope') or (connection.get('config') or {}).get('oauth_scope') oauth_server_key = (connection.get('config') or {}).get('oauth_server_key') try: @@ -2288,6 +2289,7 @@ async def register_client(request, client_id: str) -> bool: server_url, oauth_client_id=oauth_client_id, oauth_client_secret=oauth_client_secret, + oauth_scope=oauth_scope, ) else: oauth_client_info = await get_oauth_client_info_with_dynamic_client_registration( @@ -2295,6 +2297,7 @@ async def register_client(request, client_id: str) -> bool: client_id, server_url, oauth_server_key, + oauth_scope=oauth_scope, ) except Exception as e: log.error(f'OAuth client re-registration failed for {client_id}: {e}') diff --git a/backend/open_webui/routers/configs.py b/backend/open_webui/routers/configs.py index f8fa412cce..83b1bb921c 100644 --- a/backend/open_webui/routers/configs.py +++ b/backend/open_webui/routers/configs.py @@ -155,6 +155,7 @@ class OAuthClientRegistrationForm(BaseModel): client_name: str | None = None client_secret: str | None = None oauth_server_url: str | None = None + oauth_scope: str | None = None @router.post('/oauth/clients/register') @@ -179,10 +180,11 @@ async def register_oauth_client( oauth_server_url, oauth_client_id=form_data.client_id, oauth_client_secret=form_data.client_secret, + oauth_scope=form_data.oauth_scope, ) else: oauth_client_info = await get_oauth_client_info_with_dynamic_client_registration( - request, oauth_client_id, oauth_server_url + request, oauth_client_id, oauth_server_url, oauth_scope=form_data.oauth_scope ) return { 'status': True, diff --git a/backend/open_webui/utils/oauth.py b/backend/open_webui/utils/oauth.py index 8e2b5e1825..cceb3408a7 100644 --- a/backend/open_webui/utils/oauth.py +++ b/backend/open_webui/utils/oauth.py @@ -465,6 +465,7 @@ async def get_oauth_client_info_with_dynamic_client_registration( client_id: str, oauth_server_url: str, oauth_server_key: Optional[str] = None, + oauth_scope: str | None = None, ) -> OAuthClientInformationFull: try: oauth_server_metadata = None @@ -487,7 +488,10 @@ async def get_oauth_client_info_with_dynamic_client_registration( # Prefer the resource-specific scopes from the Protected Resource Metadata # (RFC 9728) over the AS's full scopes_supported catalog, for least # privilege. Mirrors the static-credentials flow (#24690). - if resource_metadata.scopes_supported: + scope_override = ' '.join(oauth_scope.replace(',', ' ').split()) if oauth_scope else None + if scope_override: + oauth_client_metadata.scope = scope_override + elif resource_metadata.scopes_supported: oauth_client_metadata.scope = ' '.join(resource_metadata.scopes_supported) discovery_urls = resource_metadata.get_discovery_urls(oauth_server_url) @@ -587,6 +591,7 @@ async def get_oauth_client_info_with_static_credentials( oauth_server_url: str, oauth_client_id: str, oauth_client_secret: str, + oauth_scope: str | None = None, ) -> OAuthClientInformationFull: """ Build an OAuthClientInformationFull from user-provided static credentials. @@ -621,7 +626,9 @@ async def get_oauth_client_info_with_static_credentials( # Unlike the Authorization Server's scopes_supported (which is a full catalog # of every scope the server can grant), the PRM scopes_supported represents # what this specific resource requires — making it safe to request them all. - scope = ' '.join(resource_metadata.scopes_supported) if resource_metadata.scopes_supported else None + scope = (' '.join(oauth_scope.replace(',', ' ').split()) if oauth_scope else None) or ( + ' '.join(resource_metadata.scopes_supported) if resource_metadata.scopes_supported else None + ) # Determine token_endpoint_auth_method token_endpoint_auth_method = 'client_secret_post' @@ -687,10 +694,18 @@ def get_connection_oauth_resource_parameter(connection: dict) -> OAuthResourcePa def apply_connection_oauth_options(connection: dict, oauth_client_info: dict) -> dict: - return { + info = connection.get('info') or {} + config = connection.get('config') or {} + oauth_scope = info.get('oauth_scope') or config.get('oauth_scope') + oauth_scope = ' '.join(oauth_scope.replace(',', ' ').split()) if oauth_scope else None + + options = { **oauth_client_info, 'oauth_resource_parameter': get_connection_oauth_resource_parameter(connection), } + if oauth_scope: + options['scope'] = oauth_scope + return options def scope_has_resource_indicator(scope: str | None) -> bool: diff --git a/src/lib/apis/configs/index.ts b/src/lib/apis/configs/index.ts index f890b9edf2..15f32e94ac 100644 --- a/src/lib/apis/configs/index.ts +++ b/src/lib/apis/configs/index.ts @@ -466,6 +466,7 @@ type RegisterOAuthClientForm = { client_name?: string; client_secret?: string; oauth_server_url?: string; + oauth_scope?: string; }; export const registerOAuthClient = async ( diff --git a/src/lib/components/AddToolServerModal.svelte b/src/lib/components/AddToolServerModal.svelte index 2cd17ed5be..1267007214 100644 --- a/src/lib/components/AddToolServerModal.svelte +++ b/src/lib/components/AddToolServerModal.svelte @@ -61,6 +61,7 @@ let oauthClientId = ''; let oauthClientSecret = ''; let oauthServerUrl = ''; + let oauthScope = ''; let oauthResourceParameter = 'auto'; let enable = true; @@ -93,9 +94,11 @@ client_id: string; client_secret?: string; oauth_server_url?: string; + oauth_scope?: string; } = { url: url, client_id: id, + ...(oauthScope ? { oauth_scope: oauthScope } : {}), ...(auth_type === 'oauth_2.1_static' ? { client_secret: oauthClientSecret, oauth_server_url: oauthServerUrl } : {}) @@ -226,6 +229,7 @@ id = data.info.id ?? ''; name = data.info.name ?? ''; description = data.info.description ?? ''; + oauthScope = data.info.oauth_scope ?? ''; oauthResourceParameter = data.info.oauth_resource_parameter ?? 'auto'; } @@ -262,7 +266,10 @@ name: name, description: description, ...(type === 'mcp' && ['oauth_2.1', 'oauth_2.1_static'].includes(auth_type) - ? { oauth_resource_parameter: oauthResourceParameter } + ? { + ...(oauthScope ? { oauth_scope: oauthScope } : {}), + oauth_resource_parameter: oauthResourceParameter + } : {}) } } @@ -348,7 +355,10 @@ name: name, description: description, ...(type === 'mcp' && ['oauth_2.1', 'oauth_2.1_static'].includes(auth_type) - ? { oauth_resource_parameter: oauthResourceParameter } + ? { + ...(oauthScope ? { oauth_scope: oauthScope } : {}), + oauth_resource_parameter: oauthResourceParameter + } : {}), ...(oauthClientInfo ? { oauth_client_info: oauthClientInfo } : {}), ...(auth_type === 'oauth_2.1_static' @@ -385,6 +395,7 @@ oauthClientId = ''; oauthClientSecret = ''; oauthServerUrl = ''; + oauthScope = ''; oauthResourceParameter = 'auto'; enable = true; @@ -413,6 +424,7 @@ oauthClientId = connection.info?.oauth_client_id ?? ''; oauthClientSecret = connection.info?.oauth_client_secret ?? ''; oauthServerUrl = connection.info?.oauth_server_url ?? ''; + oauthScope = connection.info?.oauth_scope ?? ''; oauthResourceParameter = connection.info?.oauth_resource_parameter ?? 'auto'; enable = connection.config?.enable ?? true; @@ -885,6 +897,27 @@ {/if} {#if type === 'mcp' && ['oauth_2.1', 'oauth_2.1_static'].includes(auth_type)} +
+
+ + +
+ +
+
+
+