Reference credential in HTTP request block (#4218)

This commit is contained in:
Marc Kelechava
2025-12-05 17:21:15 -08:00
committed by GitHub
parent b7d08fe906
commit 33d4d87102
2 changed files with 50 additions and 6 deletions

View File

@@ -398,6 +398,10 @@ function HttpRequestNode({ id, data }: NodeProps<HttpRequestNodeType>) {
Use "Quick Headers" in the headers section to add common Use "Quick Headers" in the headers section to add common
authentication and content headers authentication and content headers
</li> </li>
<li>
Pass a credential/secret parameter and reference it in headers
or body with {"{{ my_credential.password }}"}
</li>
<li> <li>
The request will return response data including status, headers, The request will return response data including status, headers,
and body and body

View File

@@ -206,25 +206,50 @@ class Block(BaseModel, abc.ABC):
) )
def format_block_parameter_template_from_workflow_run_context( def format_block_parameter_template_from_workflow_run_context(
self, potential_template: str, workflow_run_context: WorkflowRunContext self,
potential_template: str,
workflow_run_context: WorkflowRunContext,
*,
force_include_secrets: bool = False,
) -> str: ) -> str:
"""
Format a template string using the workflow run context.
Security Note:
Real secret values are ONLY resolved for blocks that do NOT expose data to the LLM
(like HttpRequestBlock, CodeBlock), as determined by is_safe_block_for_secrets.
"""
if not potential_template: if not potential_template:
return potential_template return potential_template
# Security: only allow real secret values for non-LLM blocks (HttpRequestBlock, CodeBlock)
is_safe_block_for_secrets = self.block_type in [BlockType.CODE, BlockType.HTTP_REQUEST]
template = jinja_sandbox_env.from_string(potential_template) template = jinja_sandbox_env.from_string(potential_template)
block_reference_data: dict[str, Any] = workflow_run_context.get_block_metadata(self.label) block_reference_data: dict[str, Any] = workflow_run_context.get_block_metadata(self.label)
template_data = workflow_run_context.values.copy() template_data = workflow_run_context.values.copy()
if workflow_run_context.include_secrets_in_templates:
include_secrets = workflow_run_context.include_secrets_in_templates or force_include_secrets
# FORCE DISABLE if block is not safe (sends data to LLM)
if include_secrets and not is_safe_block_for_secrets:
include_secrets = False
if include_secrets:
template_data.update(workflow_run_context.secrets) template_data.update(workflow_run_context.secrets)
# Create easier-to-access entries for credentials # Create easier-to-access entries for credentials
# Look for credential parameters and create real_username/real_password entries # Look for credential parameters and create real_username/real_password entries
# First collect all credential parameters to avoid modifying dict during iteration # First collect all credential parameters to avoid modifying dict during iteration
credential_params = [] credential_params = []
for key, value in template_data.items(): for key, value in list(template_data.items()):
if isinstance(value, dict) and "context" in value and "username" in value and "password" in value: if isinstance(value, dict) and "context" in value and "username" in value and "password" in value:
credential_params.append((key, value)) credential_params.append((key, value))
elif is_safe_block_for_secrets and isinstance(value, str):
secret_value = workflow_run_context.get_original_secret_value_or_none(value)
if secret_value is not None:
template_data[key] = secret_value
# Now add the real_username/real_password entries # Now add the real_username/real_password entries
for key, value in credential_params: for key, value in credential_params:
@@ -239,6 +264,17 @@ class Block(BaseModel, abc.ABC):
template_data[f"{key}_real_username"] = real_username template_data[f"{key}_real_username"] = real_username
template_data[f"{key}_real_password"] = real_password template_data[f"{key}_real_password"] = real_password
if is_safe_block_for_secrets:
resolved_credential = value.copy()
for credential_field, credential_placeholder in value.items():
if credential_field == "context":
continue
secret_value = workflow_run_context.get_original_secret_value_or_none(credential_placeholder)
if secret_value is not None:
resolved_credential[credential_field] = secret_value
resolved_credential.pop("context", None)
template_data[key] = resolved_credential
if self.label in template_data: if self.label in template_data:
current_value = template_data[self.label] current_value = template_data[self.label]
if isinstance(current_value, dict): if isinstance(current_value, dict):
@@ -3757,21 +3793,25 @@ class HttpRequestBlock(Block):
def format_potential_template_parameters(self, workflow_run_context: WorkflowRunContext) -> None: def format_potential_template_parameters(self, workflow_run_context: WorkflowRunContext) -> None:
"""Format template parameters in the block fields""" """Format template parameters in the block fields"""
template_kwargs = {"force_include_secrets": True}
if self.url: if self.url:
self.url = self.format_block_parameter_template_from_workflow_run_context(self.url, workflow_run_context) self.url = self.format_block_parameter_template_from_workflow_run_context(
self.url, workflow_run_context, **template_kwargs
)
if self.body: if self.body:
# If body is provided as a template string, try to parse it as JSON # If body is provided as a template string, try to parse it as JSON
for key, value in self.body.items(): for key, value in self.body.items():
if isinstance(value, str): if isinstance(value, str):
self.body[key] = self.format_block_parameter_template_from_workflow_run_context( self.body[key] = self.format_block_parameter_template_from_workflow_run_context(
value, workflow_run_context value, workflow_run_context, **template_kwargs
) )
if self.headers: if self.headers:
for key, value in self.headers.items(): for key, value in self.headers.items():
self.headers[key] = self.format_block_parameter_template_from_workflow_run_context( self.headers[key] = self.format_block_parameter_template_from_workflow_run_context(
value, workflow_run_context value, workflow_run_context, **template_kwargs
) )
def validate_url(self, url: str) -> bool: def validate_url(self, url: str) -> bool: