Skip to content

Commit 9e29309

Browse files
fix: ensure custom headers are ignored when using bearer or basic authorization (langgenius#23584)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent e0f0813 commit 9e29309

2 files changed

Lines changed: 178 additions & 1 deletion

File tree

api/core/workflow/nodes/http_request/executor.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,8 @@ def _assembling_headers(self) -> dict[str, Any]:
276276
encoded_credentials = credentials
277277
headers[authorization.config.header] = f"Basic {encoded_credentials}"
278278
elif self.auth.config.type == "custom":
279-
headers[authorization.config.header] = authorization.config.api_key or ""
279+
if authorization.config.header and authorization.config.api_key:
280+
headers[authorization.config.header] = authorization.config.api_key
280281

281282
# Handle Content-Type for multipart/form-data requests
282283
# Fix for issue #23829: Missing boundary when using multipart/form-data

api/tests/integration_tests/workflow/nodes/test_http.py

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,177 @@ def test_custom_authorization_header(setup_http_mock):
160160

161161
assert "?A=b" in data
162162
assert "X-Header: 123" in data
163+
# Custom authorization header should be set (may be masked)
164+
assert "X-Auth:" in data
165+
166+
167+
@pytest.mark.parametrize("setup_http_mock", [["none"]], indirect=True)
168+
def test_custom_auth_with_empty_api_key_does_not_set_header(setup_http_mock):
169+
"""Test: In custom authentication mode, when the api_key is empty, no header should be set."""
170+
from core.workflow.entities.variable_pool import VariablePool
171+
from core.workflow.nodes.http_request.entities import (
172+
HttpRequestNodeAuthorization,
173+
HttpRequestNodeData,
174+
HttpRequestNodeTimeout,
175+
)
176+
from core.workflow.nodes.http_request.executor import Executor
177+
from core.workflow.system_variable import SystemVariable
178+
179+
# Create variable pool
180+
variable_pool = VariablePool(
181+
system_variables=SystemVariable(user_id="test", files=[]),
182+
user_inputs={},
183+
environment_variables=[],
184+
conversation_variables=[],
185+
)
186+
187+
# Create node data with custom auth and empty api_key
188+
node_data = HttpRequestNodeData(
189+
title="http",
190+
desc="",
191+
url="http://example.com",
192+
method="get",
193+
authorization=HttpRequestNodeAuthorization(
194+
type="api-key",
195+
config={
196+
"type": "custom",
197+
"api_key": "", # Empty api_key
198+
"header": "X-Custom-Auth",
199+
},
200+
),
201+
headers="",
202+
params="",
203+
body=None,
204+
ssl_verify=True,
205+
)
206+
207+
# Create executor
208+
executor = Executor(
209+
node_data=node_data, timeout=HttpRequestNodeTimeout(connect=10, read=30, write=10), variable_pool=variable_pool
210+
)
211+
212+
# Get assembled headers
213+
headers = executor._assembling_headers()
214+
215+
# When api_key is empty, the custom header should NOT be set
216+
assert "X-Custom-Auth" not in headers
217+
218+
219+
@pytest.mark.parametrize("setup_http_mock", [["none"]], indirect=True)
220+
def test_bearer_authorization_with_custom_header_ignored(setup_http_mock):
221+
"""
222+
Test that when switching from custom to bearer authorization,
223+
the custom header settings don't interfere with bearer token.
224+
This test verifies the fix for issue #23554.
225+
"""
226+
node = init_http_node(
227+
config={
228+
"id": "1",
229+
"data": {
230+
"title": "http",
231+
"desc": "",
232+
"method": "get",
233+
"url": "http://example.com",
234+
"authorization": {
235+
"type": "api-key",
236+
"config": {
237+
"type": "bearer",
238+
"api_key": "test-token",
239+
"header": "", # Empty header - should default to Authorization
240+
},
241+
},
242+
"headers": "",
243+
"params": "",
244+
"body": None,
245+
},
246+
}
247+
)
248+
249+
result = node._run()
250+
assert result.process_data is not None
251+
data = result.process_data.get("request", "")
252+
253+
# In bearer mode, should use Authorization header (value is masked with *)
254+
assert "Authorization: " in data
255+
# Should contain masked Bearer token
256+
assert "*" in data
257+
258+
259+
@pytest.mark.parametrize("setup_http_mock", [["none"]], indirect=True)
260+
def test_basic_authorization_with_custom_header_ignored(setup_http_mock):
261+
"""
262+
Test that when switching from custom to basic authorization,
263+
the custom header settings don't interfere with basic auth.
264+
This test verifies the fix for issue #23554.
265+
"""
266+
node = init_http_node(
267+
config={
268+
"id": "1",
269+
"data": {
270+
"title": "http",
271+
"desc": "",
272+
"method": "get",
273+
"url": "http://example.com",
274+
"authorization": {
275+
"type": "api-key",
276+
"config": {
277+
"type": "basic",
278+
"api_key": "user:pass",
279+
"header": "", # Empty header - should default to Authorization
280+
},
281+
},
282+
"headers": "",
283+
"params": "",
284+
"body": None,
285+
},
286+
}
287+
)
288+
289+
result = node._run()
290+
assert result.process_data is not None
291+
data = result.process_data.get("request", "")
292+
293+
# In basic mode, should use Authorization header (value is masked with *)
294+
assert "Authorization: " in data
295+
# Should contain masked Basic credentials
296+
assert "*" in data
297+
298+
299+
@pytest.mark.parametrize("setup_http_mock", [["none"]], indirect=True)
300+
def test_custom_authorization_with_empty_api_key(setup_http_mock):
301+
"""
302+
Test that custom authorization doesn't set header when api_key is empty.
303+
This test verifies the fix for issue #23554.
304+
"""
305+
node = init_http_node(
306+
config={
307+
"id": "1",
308+
"data": {
309+
"title": "http",
310+
"desc": "",
311+
"method": "get",
312+
"url": "http://example.com",
313+
"authorization": {
314+
"type": "api-key",
315+
"config": {
316+
"type": "custom",
317+
"api_key": "", # Empty api_key
318+
"header": "X-Custom-Auth",
319+
},
320+
},
321+
"headers": "",
322+
"params": "",
323+
"body": None,
324+
},
325+
}
326+
)
327+
328+
result = node._run()
329+
assert result.process_data is not None
330+
data = result.process_data.get("request", "")
331+
332+
# Custom header should NOT be set when api_key is empty
333+
assert "X-Custom-Auth:" not in data
163334

164335

165336
@pytest.mark.parametrize("setup_http_mock", [["none"]], indirect=True)
@@ -239,6 +410,7 @@ def test_json(setup_http_mock):
239410
assert "X-Header: 123" in data
240411

241412

413+
@pytest.mark.parametrize("setup_http_mock", [["none"]], indirect=True)
242414
def test_x_www_form_urlencoded(setup_http_mock):
243415
node = init_http_node(
244416
config={
@@ -285,6 +457,7 @@ def test_x_www_form_urlencoded(setup_http_mock):
285457
assert "X-Header: 123" in data
286458

287459

460+
@pytest.mark.parametrize("setup_http_mock", [["none"]], indirect=True)
288461
def test_form_data(setup_http_mock):
289462
node = init_http_node(
290463
config={
@@ -334,6 +507,7 @@ def test_form_data(setup_http_mock):
334507
assert "X-Header: 123" in data
335508

336509

510+
@pytest.mark.parametrize("setup_http_mock", [["none"]], indirect=True)
337511
def test_none_data(setup_http_mock):
338512
node = init_http_node(
339513
config={
@@ -366,6 +540,7 @@ def test_none_data(setup_http_mock):
366540
assert "123123123" not in data
367541

368542

543+
@pytest.mark.parametrize("setup_http_mock", [["none"]], indirect=True)
369544
def test_mock_404(setup_http_mock):
370545
node = init_http_node(
371546
config={
@@ -394,6 +569,7 @@ def test_mock_404(setup_http_mock):
394569
assert "Not Found" in resp.get("body", "")
395570

396571

572+
@pytest.mark.parametrize("setup_http_mock", [["none"]], indirect=True)
397573
def test_multi_colons_parse(setup_http_mock):
398574
node = init_http_node(
399575
config={

0 commit comments

Comments
 (0)