refac
This commit is contained in:
@@ -92,9 +92,13 @@ class OAuthClientMetadata(MCPOAuthClientMetadata):
|
||||
pass
|
||||
|
||||
|
||||
OAuthResourceParameterMode = Literal['auto', 'include', 'omit']
|
||||
|
||||
|
||||
class OAuthClientInformationFull(OAuthClientMetadata):
|
||||
issuer: Optional[str] = None # URL of the OAuth server that issued this client
|
||||
resource: Optional[str] = None # RFC 8707 resource indicator for JWT audience
|
||||
oauth_resource_parameter: OAuthResourceParameterMode = 'auto'
|
||||
|
||||
client_id: str
|
||||
client_secret: str | None = None
|
||||
@@ -109,6 +113,8 @@ from open_webui.env import GLOBAL_LOG_LEVEL
|
||||
logging.basicConfig(stream=sys.stdout, level=GLOBAL_LOG_LEVEL)
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
OAUTH_RESOURCE_PARAMETER_MODES = {'auto', 'include', 'omit'}
|
||||
|
||||
OAUTH_RUNTIME_CONFIG = {
|
||||
'DEFAULT_USER_ROLE': ('ui.default_user_role', DEFAULT_USER_ROLE),
|
||||
'ENABLE_OAUTH_SIGNUP': ('oauth.enable_signup', ENABLE_OAUTH_SIGNUP),
|
||||
@@ -665,6 +671,61 @@ def resolve_oauth_client_info(connection: dict) -> dict:
|
||||
return data
|
||||
|
||||
|
||||
def normalize_oauth_resource_parameter(value: str | None) -> OAuthResourceParameterMode:
|
||||
if value in OAUTH_RESOURCE_PARAMETER_MODES:
|
||||
return value
|
||||
return 'auto'
|
||||
|
||||
|
||||
def get_connection_oauth_resource_parameter(connection: dict) -> OAuthResourceParameterMode:
|
||||
info = connection.get('info') or {}
|
||||
config = connection.get('config') or {}
|
||||
return normalize_oauth_resource_parameter(
|
||||
info.get('oauth_resource_parameter') or config.get('oauth_resource_parameter')
|
||||
)
|
||||
|
||||
|
||||
def apply_connection_oauth_options(connection: dict, oauth_client_info: dict) -> dict:
|
||||
return {
|
||||
**oauth_client_info,
|
||||
'oauth_resource_parameter': get_connection_oauth_resource_parameter(connection),
|
||||
}
|
||||
|
||||
|
||||
def scope_has_resource_indicator(scope: str | None) -> bool:
|
||||
if not scope:
|
||||
return False
|
||||
return any(
|
||||
scope_value.startswith(('https://', 'http://', 'api://'))
|
||||
for scope_value in scope.split()
|
||||
)
|
||||
|
||||
|
||||
def should_send_oauth_resource(client_info: OAuthClientInformationFull | None) -> bool:
|
||||
if not client_info or not client_info.resource:
|
||||
return False
|
||||
|
||||
mode = normalize_oauth_resource_parameter(client_info.oauth_resource_parameter)
|
||||
if mode == 'omit':
|
||||
return False
|
||||
if mode == 'include':
|
||||
return True
|
||||
|
||||
return not scope_has_resource_indicator(client_info.scope)
|
||||
|
||||
|
||||
def build_oauth_request_params(client_info: OAuthClientInformationFull | None) -> dict:
|
||||
if not client_info:
|
||||
return {}
|
||||
|
||||
params = {}
|
||||
if client_info.scope:
|
||||
params['scope'] = client_info.scope
|
||||
if should_send_oauth_resource(client_info):
|
||||
params['resource'] = client_info.resource
|
||||
return params
|
||||
|
||||
|
||||
async def recover_static_oauth_client_metadata(connection: dict, oauth_client_info: dict) -> dict:
|
||||
if connection.get('auth_type') != 'oauth_2.1_static':
|
||||
return oauth_client_info
|
||||
@@ -774,6 +835,7 @@ class OAuthClientManager:
|
||||
oauth_client_info = await recover_static_oauth_client_metadata(
|
||||
connection, oauth_client_info
|
||||
)
|
||||
oauth_client_info = apply_connection_oauth_options(connection, oauth_client_info)
|
||||
return self.add_client(expected_client_id, OAuthClientInformationFull(**oauth_client_info))['client']
|
||||
except Exception as e:
|
||||
log.error(f'Failed to lazily add OAuth client {expected_client_id} from config: {e}')
|
||||
@@ -807,12 +869,7 @@ class OAuthClientManager:
|
||||
redirect_uri = str(client_info.redirect_uris[0])
|
||||
|
||||
try:
|
||||
kwargs = {}
|
||||
if client_info.scope:
|
||||
kwargs['scope'] = client_info.scope
|
||||
if client_info.resource:
|
||||
kwargs['resource'] = client_info.resource
|
||||
|
||||
kwargs = build_oauth_request_params(client_info)
|
||||
auth_data = await client.create_authorization_url(redirect_uri=redirect_uri, **kwargs)
|
||||
authorization_url = auth_data.get('url')
|
||||
|
||||
@@ -992,9 +1049,8 @@ class OAuthClientManager:
|
||||
'refresh_token': token_data['refresh_token'],
|
||||
'client_id': client.client_id,
|
||||
}
|
||||
# RFC 8707: include resource indicator so refreshed tokens retain correct audience
|
||||
client_info = await self.get_client_info(client_id)
|
||||
if client_info and client_info.resource:
|
||||
if should_send_oauth_resource(client_info):
|
||||
refresh_data['resource'] = client_info.resource
|
||||
|
||||
if hasattr(client, 'client_secret') and client.client_secret:
|
||||
@@ -1050,11 +1106,7 @@ class OAuthClientManager:
|
||||
redirect_uri = client_info.redirect_uris[0] if client_info.redirect_uris else None
|
||||
redirect_uri_str = str(redirect_uri) if redirect_uri else None
|
||||
# Pass explicit scope/resource parameters for providers that require them.
|
||||
kwargs = {}
|
||||
if client_info.scope:
|
||||
kwargs['scope'] = client_info.scope
|
||||
if client_info.resource:
|
||||
kwargs['resource'] = client_info.resource
|
||||
kwargs = build_oauth_request_params(client_info)
|
||||
return await client.authorize_redirect(request, redirect_uri_str, **kwargs)
|
||||
|
||||
async def handle_callback(self, request, client_id: str, user_id: str, response):
|
||||
@@ -1070,9 +1122,8 @@ class OAuthClientManager:
|
||||
# The Authlib client already has these configured during add_client().
|
||||
# Passing them again causes Authlib to concatenate them (e.g., "ID1,ID1"),
|
||||
# which results in 401 errors from the token endpoint. (Fix for #19823)
|
||||
# RFC 8707: pass resource indicator for correct JWT audience on token exchange
|
||||
token_kwargs = {}
|
||||
if client_info and client_info.resource:
|
||||
if should_send_oauth_resource(client_info):
|
||||
token_kwargs['resource'] = client_info.resource
|
||||
token = await client.authorize_access_token(request, **token_kwargs)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user