Skip to content

fix(workflows): harden workflow API auth routes#3559

Open
PlaneInABottle wants to merge 24 commits intosimstudioai:stagingfrom
PlaneInABottle:fix/workflow-api-auth-hardening
Open

fix(workflows): harden workflow API auth routes#3559
PlaneInABottle wants to merge 24 commits intosimstudioai:stagingfrom
PlaneInABottle:fix/workflow-api-auth-hardening

Conversation

@PlaneInABottle
Copy link
Contributor

@PlaneInABottle PlaneInABottle commented Mar 13, 2026

Summary

  • Hardens workflow lifecycle and deployment auth after the rebase so session, API-key, and internal callers follow the same access path.
  • Incorporates the latest landed fixes already on the branch: rebased auth-hardening updates, the env mock fix, the duplicate lookup fix, and the archived workspace access fix.
  • Also includes the current follow-up scope in the worktree: API-key audit actor metadata on lifecycle routes, deployment activation reuse of the preloaded workflow, and an English docs clarification.

What changed

  • Landed on the branch
    • Rebases and keeps the workflow API auth-hardening stack aligned with current staging
    • Preserves env mocks / workflow context in the affected workflow route tests to avoid rebase regressions
    • Fixes the duplicate lookup path introduced in the hardened workflow access flow
    • Blocks archived workspace access for workflow routes
  • Current follow-up in the worktree
    • Carries API-key actor name / email into deploy, activate, and revert audit records
    • Reuses the already loaded workflow during deployment activation auth checks instead of performing another lookup
    • Clarifies the English execution API docs to note that lifecycle/deployment endpoints can also be used with session auth or API keys where supported

Validation

  • Targeted workflow auth coverage has been expanded across middleware, deploy, deployment activation, deployment revert, status, and async execution paths
  • Follow-up test updates cover API-key audit actor metadata and preloaded-workflow reuse in deployment activation
  • Docs change is wording-only and does not change runtime behavior

@cursor
Copy link

cursor bot commented Mar 13, 2026

PR Summary

Medium Risk
Touches workflow lifecycle/deployment authorization and audit attribution paths; mistakes could incorrectly allow/deny access or reduce audit fidelity, but changes are localized and test-covered.

Overview
Unifies workflow lifecycle and deployment API routes around the validateWorkflowAccess middleware (replacing direct validateWorkflowPermissions/session-only checks), enabling hybrid auth (session + API key) for read/write/admin actions and tightening edge cases like workspace-scoped API keys and archived/personal workflows.

Improves audit attribution for API-key callers by propagating userName/userEmail from API key auth (via authenticateApiKeyFromHeader + new getAuditActorMetadata) into deploy/undeploy/activate/revert/delete audit records, and avoids redundant workflow lookups by allowing authorizeWorkflowByWorkspacePermission to reuse a preloaded workflow.

Adds targeted Vitest coverage across middleware and workflow endpoints (deploy, deployed state, deployments list/version activation/revert, status, lifecycle, async execute error handling) and updates docs to clarify lifecycle/deployment endpoints can be called programmatically with session auth or API keys where supported.

Written by Cursor Bugbot for commit 6f2e6eb. This will update automatically on new commits. Configure here.

@vercel
Copy link

vercel bot commented Mar 13, 2026

Someone is attempting to deploy a commit to the Sim Team on Vercel.

A member of the Team first needs to authorize it.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 13, 2026

Greptile Summary

This PR hardens the workflow API auth layer by introducing a shared validateWorkflowAccess middleware and migrating seven route surfaces ([id], deploy, deployed, status, deployments, deployments/[version], deployments/[version]/revert) onto it, replacing a patchwork of session-only and ad-hoc auth checks with a consistent session / API-key / internal-secret path.

Key observations:

  • The overall direction is correct and the standardisation is a genuine improvement over the previous divergent per-route auth.
  • Double authorization in route.ts: Both the DELETE and PUT handlers call validateWorkflowAccess (which internally runs authorizeWorkflowByWorkspacePermission) and then immediately call authorizeWorkflowByWorkspacePermission a second time with the same arguments. The redundant second call wastes a DB round-trip on every request and introduces a small race-condition window where the two checks could return different results.
  • Double session validation in deploy/route.ts: validateLifecycleAdminAccess calls validateWorkflowAccess for all callers (which already completes auth + permission checks for session users), and then, for session callers, discards the result and re-runs validateWorkflowPermissions in full. The first check's result is silently thrown away for this auth type.
  • Dead allowInternalSecret: false option in deployed/route.ts: The allowInternalSecret field is only evaluated inside the requireDeployment: true code path in middleware. Specifying it with requireDeployment: false has no effect and misleads readers.
  • The allowInternalSecret default in middleware.ts is coupled to requireDeployment unnecessarily, which reinforces the same misunderstanding.
  • Test coverage is good — every changed route has a corresponding test file, and the tests verify the new middleware is called with the expected options.

Confidence Score: 3/5

  • Auth hardening direction is correct but two routes contain redundant/double authorization logic that should be resolved before merging.
  • The shared middleware and its adoption across routes is well-structured, and the test suite covers the new paths. However, the double authorizeWorkflowByWorkspacePermission in the [id] route and the double session-validation in the deploy route are real logic issues that add unnecessary DB load and create subtle race-condition windows in security-critical code paths.
  • apps/sim/app/api/workflows/[id]/route.ts (DELETE/PUT double authorization) and apps/sim/app/api/workflows/[id]/deploy/route.ts (double session validation in validateLifecycleAdminAccess).

Important Files Changed

Filename Overview
apps/sim/app/api/workflows/middleware.ts New centralized validateWorkflowAccess function with good structure; the allowInternalSecret default coupling to requireDeployment is misleading and should be cleaned up.
apps/sim/app/api/workflows/[id]/route.ts DELETE and PUT handlers perform a redundant second authorizeWorkflowByWorkspacePermission call after validateWorkflowAccess already authorized the user, creating unnecessary DB round-trips and a race-condition window.
apps/sim/app/api/workflows/[id]/deploy/route.ts Session-authenticated callers are double-validated: validateWorkflowAccess completes auth and permission checks, then validateLifecycleAdminAccess discards the result and re-runs validateWorkflowPermissions for session users.
apps/sim/app/api/workflows/[id]/deployed/route.ts Clean internal-caller bypass with JWT verification; the allowInternalSecret: false option passed to validateWorkflowAccess is dead code and should be removed.
apps/sim/app/api/workflows/[id]/status/route.ts Clean migration to shared validateWorkflowAccess; no issues found.
apps/sim/app/api/workflows/[id]/deployments/route.ts Straightforward use of shared middleware for deployment list read access; no issues found.
apps/sim/app/api/workflows/[id]/deployments/[version]/route.ts Good use of per-action permission escalation (read vs admin for activation); validateDeploymentVersionLifecycleAccess wrapper is clean but slightly redundant given it only forwards to validateWorkflowAccess unchanged.
apps/sim/app/api/workflows/[id]/deployments/[version]/revert/route.ts Clean admin-only guard via shared middleware; actor identity checked before mutation; no issues found.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Incoming Request] --> B{Auth Type?}

    B -->|Bearer JWT| C[verifyInternalToken\ndeployed/route only]
    C -->|valid| D[Skip validateWorkflowAccess\nload deployed state directly]
    C -->|invalid| E[validateWorkflowAccess]

    B -->|Session / API Key / Internal Secret| E

    E --> F[getWorkflowById]
    F -->|not found| G[404 Workflow not found]
    F -->|found, no workspaceId| H[403 Personal workflow deprecated]

    F -->|requireDeployment: false| I[checkHybridAuth]
    I -->|fail / no userId| J[401 Unauthorized]
    I -->|success + userId| K[authorizeWorkflowByWorkspacePermission\naction: read / write / admin]
    K -->|denied| L[403 / 404 Access denied]
    K -->|allowed| M[Return workflow + auth]

    F -->|requireDeployment: true| N{isDeployed?}
    N -->|false| O[403 Workflow not deployed]
    N -->|true| P{Internal Secret header?}
    P -->|valid + allowInternalSecret| Q[Return workflow — no userId]
    P -->|missing / invalid| R[Check X-Api-Key header]
    R -->|missing| S[401 API key required]
    R -->|present| T[authenticateApiKeyFromHeader\nworkspace or personal]
    T -->|fail| U[401 Invalid API key]
    T -->|success| V[updateApiKeyLastUsed\nReturn workflow]

    M --> W[Route handler\nDELETE / PUT / POST / PATCH / GET]
    V --> W
    Q --> W
Loading

Comments Outside Diff (1)

  1. apps/sim/app/api/workflows/[id]/route.ts, line 170-192 (link)

    Redundant double authorization check

    validateWorkflowAccess at line 150 already calls checkHybridAuth followed by authorizeWorkflowByWorkspacePermission with the same action: 'admin'. If that first check passes, the userId is guaranteed to have workspace admin permission. Re-running authorizeWorkflowByWorkspacePermission here with the same userId and action creates:

    1. A redundant DB round-trip on every request.
    2. A window for a race condition — if the workspace membership changes between the two calls, the second check could return a different result, leading to inconsistent behaviour (e.g. the first check passes but the second returns authorization.allowed = false, causing a 403 even though the user was legitimately authorised).

    The workflow object is already available via validation.workflow and the access is already verified, so the second authorizeWorkflowByWorkspacePermission call (and the canDelete guard that depends on it) should be removed in favour of using validation.workflow directly:

    const workflowData = validation.workflow
    if (!workflowData) {
      return NextResponse.json({ error: 'Workflow not found' }, { status: 404 })
    }
    

    The same pattern applies to the PUT handler at lines 403–424.

Last reviewed commit: 73c1328

@PlaneInABottle PlaneInABottle force-pushed the fix/workflow-api-auth-hardening branch 2 times, most recently from 888a246 to aab58cb Compare March 13, 2026 08:27
@vercel
Copy link

vercel bot commented Mar 13, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Mar 19, 2026 0:32am

Request Review

@icecrasher321 icecrasher321 self-assigned this Mar 13, 2026
@icecrasher321
Copy link
Collaborator

@PlaneInABottle this PR might have to wait till our v0.6 release. Lot of auth changes there that might overwrite this potentially

@icecrasher321
Copy link
Collaborator

@PlaneInABottle can you rebase against staging

PlaneInABottle and others added 8 commits March 14, 2026 21:50
Co-authored-by: GitHub Copilot <github-copilot[bot]@users.noreply.github.com>
Co-authored-by: GitHub Copilot <github-copilot[bot]@users.noreply.github.com>
Co-authored-by: GitHub Copilot <github-copilot[bot]@users.noreply.github.com>
Use the shared audit actor helper consistently so workflow deletion matches deploy behavior and remove the redundant deploy wrapper raised in review.
Call validateWorkflowAccess directly in workflow deployment lifecycle routes and clean up the related test helper formatting raised in review.
@PlaneInABottle PlaneInABottle force-pushed the fix/workflow-api-auth-hardening branch from a264ee9 to 2c267c0 Compare March 14, 2026 19:00
@PlaneInABottle
Copy link
Contributor Author

@icecrasher321 rebased

@icecrasher321
Copy link
Collaborator

@PlaneInABottle looks like one more conflict sorry.

One thing -- I see you changed routes like revert to share the same auth as deploy. That's a bit confusing to me, since those are currently only UI actions. Shouldn't we keep more deliberate / as narrowly scoped auth as possible. We don't intend things like revert to be API key accessible.

@PlaneInABottle
Copy link
Contributor Author

@PlaneInABottle looks like one more conflict sorry.

One thing -- I see you changed routes like revert to share the same auth as deploy. That's a bit confusing to me, since those are currently only UI actions. Shouldn't we keep more deliberate / as narrowly scoped auth as possible. We don't intend things like revert to be API key accessible.

@icecrasher321 I did it for to control sim with ai. I am using sim generally with ai, not with ui at all. Only to check the workflows' blocks, orders etc. I mean it is of course a design choice. I could remove it but I do think sim would be better with it.

You could also advertise sim with this feature :D . For example you can create, deploy and use workflows with openclaw or something like that. It is one of the things I want at the end of this year. I think you guys should aim and design in this way too, it would be cool to see ai native workflow builder.

I do believe also it could attract not only developers because it is opensource, but it could also attract vibecoders against n8n.

@icecrasher321
Copy link
Collaborator

icecrasher321 commented Mar 18, 2026

@PlaneInABottle That's interesting, this makes sense. I'm not against making these publicly hittable endpoints. Can you fix the merge conflict and also add this to the docs so it's not a hidden feature. Only add it to the english docs it'll auto translate.

@PlaneInABottle PlaneInABottle force-pushed the fix/workflow-api-auth-hardening branch from ce2ed79 to 1f61db8 Compare March 18, 2026 23:07
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants