From 8ea4813936e4161c36958d88d0f11f180689b608 Mon Sep 17 00:00:00 2001 From: Tim O'Farrell Date: Wed, 21 Jan 2026 09:17:19 -0700 Subject: [PATCH] fix: remove keycloak_auth cookie requirement for webhook secrets endpoint (#12525) Co-authored-by: openhands --- enterprise/server/middleware.py | 1 + .../live_status_app_conversation_service.py | 11 +---------- .../test_live_status_app_conversation_service.py | 10 +++++----- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/enterprise/server/middleware.py b/enterprise/server/middleware.py index 932fbae7ac..661493d7db 100644 --- a/enterprise/server/middleware.py +++ b/enterprise/server/middleware.py @@ -163,6 +163,7 @@ class SetAuthCookieMiddleware: '/oauth/device/authorize', '/oauth/device/token', '/api/v1/web-client/config', + '/api/v1/webhooks/secrets', ) if path in ignore_paths: return False diff --git a/openhands/app_server/app_conversation/live_status_app_conversation_service.py b/openhands/app_server/app_conversation/live_status_app_conversation_service.py index 0d264812e7..7102f6aeb6 100644 --- a/openhands/app_server/app_conversation/live_status_app_conversation_service.py +++ b/openhands/app_server/app_conversation/live_status_app_conversation_service.py @@ -114,7 +114,6 @@ class LiveStatusAppConversationService(AppConversationServiceBase): openhands_provider_base_url: str | None access_token_hard_timeout: timedelta | None app_mode: str | None = None - keycloak_auth_cookie: str | None = None tavily_api_key: str | None = None async def search_app_conversations( @@ -602,10 +601,6 @@ class LiveStatusAppConversationService(AppConversationServiceBase): ) headers = {'X-Access-Token': access_token} - # Include keycloak_auth cookie in headers if app_mode is SaaS - if self.app_mode == 'saas' and self.keycloak_auth_cookie: - headers['Cookie'] = f'keycloak_auth={self.keycloak_auth_cookie}' - secrets[secret_name] = LookupSecret( url=self.web_url + '/api/v1/webhooks/secrets', headers=headers, @@ -1400,17 +1395,14 @@ class LiveStatusAppConversationServiceInjector(AppConversationServiceInjector): if isinstance(sandbox_service, DockerSandboxService): web_url = f'http://host.docker.internal:{sandbox_service.host_port}' - # Get app_mode and keycloak_auth cookie for SaaS mode + # Get app_mode for SaaS mode app_mode = None - keycloak_auth_cookie = None try: from openhands.server.shared import server_config app_mode = ( server_config.app_mode.value if server_config.app_mode else None ) - if request and server_config.app_mode == AppMode.SAAS: - keycloak_auth_cookie = request.cookies.get('keycloak_auth') except (ImportError, AttributeError): # If server_config is not available (e.g., in tests), continue without it pass @@ -1440,6 +1432,5 @@ class LiveStatusAppConversationServiceInjector(AppConversationServiceInjector): openhands_provider_base_url=config.openhands_provider_base_url, access_token_hard_timeout=access_token_hard_timeout, app_mode=app_mode, - keycloak_auth_cookie=keycloak_auth_cookie, tavily_api_key=tavily_api_key, ) diff --git a/tests/unit/app_server/test_live_status_app_conversation_service.py b/tests/unit/app_server/test_live_status_app_conversation_service.py index f37a7894b8..880244fda4 100644 --- a/tests/unit/app_server/test_live_status_app_conversation_service.py +++ b/tests/unit/app_server/test_live_status_app_conversation_service.py @@ -75,7 +75,6 @@ class TestLiveStatusAppConversationService: openhands_provider_base_url='https://provider.example.com', access_token_hard_timeout=None, app_mode='test', - keycloak_auth_cookie=None, ) # Mock user info @@ -150,10 +149,9 @@ class TestLiveStatusAppConversationService: @pytest.mark.asyncio async def test_setup_secrets_for_git_providers_with_saas_mode(self): - """Test _setup_secrets_for_git_providers with SaaS mode (includes keycloak cookie).""" + """Test _setup_secrets_for_git_providers with SaaS mode uses LookupSecret with X-Access-Token.""" # Arrange self.service.app_mode = 'saas' - self.service.keycloak_auth_cookie = 'test_cookie' base_secrets = {} self.mock_user_context.get_secrets.return_value = base_secrets self.mock_jwt_service.create_jws_token.return_value = 'test_access_token' @@ -173,8 +171,10 @@ class TestLiveStatusAppConversationService: assert 'GITLAB_TOKEN' in result lookup_secret = result['GITLAB_TOKEN'] assert isinstance(lookup_secret, LookupSecret) - assert 'Cookie' in lookup_secret.headers - assert lookup_secret.headers['Cookie'] == 'keycloak_auth=test_cookie' + assert 'X-Access-Token' in lookup_secret.headers + assert lookup_secret.headers['X-Access-Token'] == 'test_access_token' + # Verify no cookie is included (authentication is via X-Access-Token only) + assert 'Cookie' not in lookup_secret.headers # Verify description is included assert lookup_secret.description == 'GITLAB authentication token'