Skip to content

feat(core): Add validation on workflow with dynamic credentials#26423

Open
guillaumejacquart wants to merge 5 commits intomasterfrom
iam-354-add-validation-on-workflow-with-dynamic-credentials
Open

feat(core): Add validation on workflow with dynamic credentials#26423
guillaumejacquart wants to merge 5 commits intomasterfrom
iam-354-add-validation-on-workflow-with-dynamic-credentials

Conversation

@guillaumejacquart
Copy link
Contributor

@guillaumejacquart guillaumejacquart commented Mar 2, 2026

Summary

Adds pre-publication validation for workflows that use dynamic (resolvable) credentials, blocking activation when the required runtime dependencies are not configured.

Two checks are enforced:

  • Resolver check — each dynamic credential must have a resolver configured. The resolver can be
    set either on the credential itself (resolverId) or as a workflow-level fallback
    (workflowSettings.credentialResolverId). If neither is present, activation is blocked with a
    clear error naming the affected credentials.
  • Identity extractor check — at least one trigger node must have context establishment hooks
    configured. These hooks extract user identity from incoming requests (e.g. bearer tokens from
    HTTP headers), which is required for the resolver to determine which credential values to return.
    Triggers without hooks (e.g. schedule, polling) cannot establish this context.

Both checks run at workflow activation time, before the workflow goes live, preventing silent
runtime failures when dynamic credentials cannot be resolved.

Related Linear tickets, Github issues, and Community forum posts

https://linear.app/n8n/issue/IAM-354/add-validation-on-workflow-with-dynamic-credentials

Review / Merge checklist

  • PR title and summary are descriptive. (conventions)
  • Docs updated or follow-up ticket created.
  • Tests included.
  • PR Labeled with release/backport (if the PR is an urgent fix that needs to be backported)

guillaumejacquart and others added 2 commits March 2, 2026 13:53
…tivation

Block workflow activation when dynamic (resolvable) credentials are used but
no webhook trigger is present. Schedule and polling triggers cannot establish
the user identity context required for dynamic credential resolution.

Ref: https://linear.app/n8n/issue/IAM-149

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@codecov
Copy link

codecov bot commented Mar 2, 2026

Codecov Report

❌ Patch coverage is 87.17949% with 5 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
packages/cli/src/workflows/workflow.service.ts 50.00% 3 Missing ⚠️
...s/cli/src/workflows/workflow-validation.service.ts 93.93% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@n8n-assistant n8n-assistant bot added core Enhancement outside /nodes-base and /editor-ui n8n team Authored by the n8n team labels Mar 2, 2026
@guillaumejacquart guillaumejacquart marked this pull request as ready for review March 2, 2026 13:38
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cubic analysis

No issues found across 5 files

Linked issue analysis

Linked issue: IAM-354: Add validation on workflow with dynamic credentials

Status Acceptance criteria Notes
When a dynamic credential is configured and the workflow resolver is not set, publishing fails and a clear error indicates the dynamic credential requires a resolver in production. Checks credentialsWithoutResolver and errors if no workflow resolver
When a dynamic credential is configured and workflow triggers have no identity extractors, publishing fails and a clear error indicates the dynamic credential requires an identity extractor in production. Checks for extractor hooks on triggers and errors when none found
Resolver can be set either on the credential itself (resolverId) or as a workflow-level fallback (workflowSettings.credentialResolverId). Uses c.resolverId and workflowSettings.credentialResolverId as fallback
Both checks run at workflow activation time, before the workflow goes live (pre-publication validation). workflow.service calls _validateDynamicCredentials during activation and throws on failure
Disabled nodes are skipped when collecting credentials (disabled nodes should not cause validation to run for their credentials). Collects credentialIds skipping node.disabled nodes
Only resolvable credentials used by nodes are checked; non-resolvable credentials don't block activation. Queries credentialsRepository for isResolvable:true and returns valid if none
Errors surfaced should name the affected credentials (clear error messages indicating which credentials are problematic). Constructs credNames from c.name and includes in error strings
At least one trigger node must have contextEstablishmentHooks configured (identity extractor) for dynamic credential resolution. Uses toExecutionContextEstablishmentHookParameter and checks hooks.length > 0 on trigger nodes
Architecture diagram
sequenceDiagram
    participant UI as Client / API
    participant WS as WorkflowService
    participant V as WorkflowValidationService
    participant DB as CredentialsRepository
    participant NT as NodeTypes

    Note over UI,NT: Workflow Activation Flow (Pre-publication)

    UI->>WS: activateWorkflow(workflowId)
    WS->>WS: _detectWebhookConflicts()
    WS->>WS: _validateNodes()

    rect rgb(23, 37, 84)
    WS->>V: NEW: validateDynamicCredentials(nodes, settings)
    
    V->>V: Filter active nodes for credential IDs
    
    alt Workflow contains credentials
        V->>DB: CHANGED: find resolvable credentials (isResolvable=true)
        DB-->>V: List of dynamic credentials (id, name, resolverId)
        
        opt Has dynamic credentials
            V->>V: NEW: Check for Resolver (cred.resolverId OR settings.fallback)
            alt No Resolver Configured
                V-->>WS: Return isValid: false (Missing Resolver)
            else Resolver Valid
                V->>NT: Get node type definitions for triggers
                V->>V: NEW: Validate contextEstablishmentHooks (Identity Extractor)
                alt No triggers have extractor hooks
                    V-->>WS: Return isValid: false (Missing Identity Extractor)
                else Extractor Valid
                    V-->>WS: Return isValid: true
                end
            end
        end
    else No credentials
        V-->>WS: Return isValid: true
    end
    end

    alt Validation Failed
        WS->>WS: Log warning & throw WorkflowValidationError
        WS-->>UI: 400 Bad Request (Validation Error)
    else Validation Passed
        WS->>WS: Continue activation (Sub-workflow checks, etc.)
        WS-->>UI: 200 OK / Workflow Active
    end
Loading

@blacksmith-sh

This comment has been minimized.

@guillaumejacquart guillaumejacquart requested review from a team, BGZStephen, afitzek, cstuncsik and phyllis-noester and removed request for a team March 2, 2026 16:11

const resolvableCredentials = await this.credentialsRepository.find({
where: { id: In([...credentialIds]), isResolvable: true },
select: ['id', 'name', 'resolverId'],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think i missed this: did we move the resolver to the credential itself?

}

// A credential is covered if it has its own resolver OR the workflow has a defined resolver
const workflowResolverId = workflowSettings?.credentialResolverId;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i guess we only need to get const credentialsWithoutResolver = resolvableCredentials.filter((c) => !c.resolverId); if no workflow ersolver exists no?

if (!workflowResolverId && credentialsWithoutResolver.length > 0) {
const credNames = credentialsWithoutResolver.map((c) => `"${c.name}"`).join(', ');
return {
isValid: false,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we should also check for extractor hook before returning so it's easier to debug for the workflow developer as they get all problems back

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core Enhancement outside /nodes-base and /editor-ui n8n team Authored by the n8n team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants