feat(canvas): auto-switch workspace when opening a flow URL from another member workspace#6582
feat(canvas): auto-switch workspace when opening a flow URL from another member workspace#6582dkindlund wants to merge 5 commits into
Conversation
…her member workspace
In multi-user mode, chatflows/agentflows are scoped to the user's *active*
workspace, and the canvas load is scoped to that active workspace. So when a
user opens a flow URL (`/canvas/:id`, `/agentcanvas/:id`, `/v2/agentcanvas/:id`)
whose flow lives in a workspace they are a *member of* but which isn't their
active workspace, the canvas dead-ends on:
Failed to retrieve Chatflow ... Chatflow <id> not found in the database!
and the user has to manually figure out which workspace owns it and switch.
Since `chat_flow.id` is a globally-unique UUID, it maps to exactly one owning
workspace, so the UI can resolve it and switch automatically.
Backend: add a membership-gated `GET /chatflows/resolve-workspace/:id` resolver
that returns `{ workspaceId }` only when the requesting user is a member of the
flow's workspace. Every other branch (missing flow, non-member, API-key caller
with no user) returns the *identical* 404 as `getChatflowById`, so this
preserves the existing "Protect Against Cross-Workspace Chatflow Disclosure"
hardening and never reveals that a flow exists in a workspace the caller cannot
access.
Frontend: on the canvas load 404, a shared `useFlowWorkspaceAutoSwitch` hook
resolves the owning workspace and — only if it's in the user's
`assignedWorkspaces` and isn't already active — switches to it and re-fetches
the flow, staying on the same canvas URL. Falls back to the normal error for a
genuine 404 / non-member. Guarded to attempt at most once per flow id.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request introduces a mechanism to automatically switch the active workspace when a user opens a chatflow URL belonging to a workspace they are a member of, but which is not currently active. It adds a secure server-side endpoint to resolve the workspace and a custom React hook useFlowWorkspaceAutoSwitch to handle the transition on the client side. The review feedback suggests defensive checks for workspaceId on the server, wrapping the auto-switch function in useCallback for performance, and using optional chaining on error responses in the UI to prevent potential crashes.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
- service: also treat a chatflow with a missing workspaceId as not-found before the membership lookup (fail-fast, avoids querying WorkspaceUser with a null workspaceId). - useFlowWorkspaceAutoSwitch: memoize tryAutoSwitch with useCallback so it isn't recreated each render; defensively optional-chain w?.id and switchRes?.data. - canvas/index.jsx, agentflowsv2/Canvas.jsx: optional-chain the fallback error message so a transport-level failure (no error.response) can't throw a TypeError; fall back to error.message / 'Unknown error'. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@gemini-code-assist, please re-review based on the latest commit. |
There was a problem hiding this comment.
Code Review
This pull request introduces a mechanism to automatically switch the active workspace in the UI when a user opens a chatflow URL belonging to another workspace they are a member of. This is achieved by adding a new /resolve-workspace/:id endpoint on the backend and a corresponding useFlowWorkspaceAutoSwitch hook on the frontend. The review feedback suggests a security improvement to ensure that the user's membership status is explicitly checked as active before resolving the workspace, preventing unauthorized access or disclosure for disabled or pending users.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Gate the membership lookup in resolveChatflowWorkspace on WorkspaceUserStatus.ACTIVE so a user who is only INVITED (invited but not yet accepted) or DISABLEd cannot resolve a flow's workspace or auto-switch into it. Without this, the default membership row (status INVITED) would satisfy the check and leak/grant cross-workspace access via a direct API call. Non-active members now get the same 404 as non-members. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@gemini-code-assist, please re-review based on the latest commit. |
There was a problem hiding this comment.
Code Review
This pull request introduces an automatic workspace-switching mechanism in the UI when a user opens a chatflow URL belonging to another workspace they are a member of. This is supported by a new backend endpoint /resolve-workspace/:id that safely resolves the workspace ID while preventing cross-workspace information disclosure. Feedback highlights a critical issue in the custom hook useFlowWorkspaceAutoSwitch where a stale closure of currentUser can occur due to missing dependencies in the canvas components' useEffect hooks. Additionally, the hook prematurely marks a flow ID as attempted before verifying if the user is loaded. A ref-based solution is suggested to resolve these issues.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
…try before user loads Adopt the latest-ref pattern in useFlowWorkspaceAutoSwitch: - Store currentUser in a ref kept current via useEffect, and read it inside tryAutoSwitch from the ref. tryAutoSwitch now only depends on [dispatch], so its identity is stable — callers that omit it from their useEffect deps no longer capture a stale callback that closes over an old/null user. - Check that the user is loaded BEFORE adding the flow id to attemptedRef, so a call made before currentUser is hydrated doesn't permanently prevent a later, valid auto-switch attempt for that flow. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@gemini-code-assist, please re-review based on the latest commit. |
There was a problem hiding this comment.
Code Review
This pull request introduces a workspace auto-switch feature that automatically switches the active workspace when a user opens a flow URL belonging to another workspace they are a member of, preventing a dead-end 404 error. This is implemented via a new server-side endpoint /resolve-workspace/:id that safely resolves the owning workspace without leaking information to non-members, and a custom React hook useFlowWorkspaceAutoSwitch integrated into the canvas views. Feedback suggests improving the hook's error handling by clearing the flow ID from the attempted set during transient (non-404) errors to allow subsequent retries.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
In useFlowWorkspaceAutoSwitch, only a 404 from the resolver/switch is definitive (not a member / missing flow) — keep it marked in attemptedRef so we don't retry. For transient failures (network error, 5xx, etc.) delete the flow id from attemptedRef so a later re-fetch can attempt the auto-switch again instead of being permanently blocked until a full page reload. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@gemini-code-assist, please re-review based on the latest commit. |
There was a problem hiding this comment.
Code Review
This pull request introduces a workspace auto-switching feature for chatflows. It adds a new backend endpoint /chatflows/resolve-workspace/:id and a corresponding service method resolveChatflowWorkspace to resolve the owning workspace of a chatflow, strictly gating access to active members of that workspace to prevent unauthorized information disclosure. On the frontend, a new React hook useFlowWorkspaceAutoSwitch is introduced and integrated into the canvas views. When a user attempts to load a chatflow that belongs to another workspace they are a member of, the UI automatically switches to that workspace and re-fetches the flow instead of displaying a 404 error. I have no additional feedback to provide as there are no review comments.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Closes #6581
What this does
In multi-user / enterprise mode, chatflows/agentflows are scoped to the user's active workspace, and the canvas load is scoped to that active workspace. So when a user opens a flow URL (
/canvas/:id,/agentcanvas/:id,/v2/agentcanvas/:id) whose flow lives in a workspace they are a member of but which isn't active, the canvas dead-ends on:and they have to manually find and switch workspaces. Since
chat_flow.idis a globally-unique UUID it maps to exactly one owning workspace, so the UI can resolve it and switch automatically — only when the user is a member.Changes
Backend
services/chatflows/index.ts: newresolveChatflowWorkspace(chatflowId, userId)— looks the flow up by id (no workspace filter), then gates strictly on a directworkspace_usermembership check, returning{ workspaceId }. Every failure mode (bad UUID aside) returns the identical404 "Chatflow <id> not found in the database!"asgetChatflowById.controllers/chatflows/index.ts: newgetChatflowWorkspacecontroller (usesreq.user?.id; API-key callers have no user → 404).routes/chatflows/index.ts: newGET /chatflows/resolve-workspace/:id(two-segment path so it never collides with the['/', '/:id']matcher; samecheckAnyPermissionas the read route).Frontend
api/chatflows.js:getChatflowWorkspace(id).hooks/useFlowWorkspaceAutoSwitch.jsx(new): shared hook used by both canvases. On a 404, resolves the owning workspace and — only if it's in the user'sassignedWorkspacesand isn't already active — calls the existingworkspaceApi.switchWorkspace, dispatchesworkspaceSwitchSuccess, and re-fetches the flow. Attempts at most once per flow id; falls back to the normal error otherwise. Stays on the same canvas URL (nonavigate('/'); navigate(0)).views/canvas/index.jsxandviews/agentflowsv2/Canvas.jsx: wire the hook into the existinggetSpecificChatflowerror branch.Security
This preserves the existing "Protect Against Cross-Workspace Chatflow Disclosure" behavior:
assignedWorkspacesbefore switching, so it can never switch into an unauthorized workspace.How it was tested
Manually against a local multi-user instance:
/canvas,/agentcanvas) and v2 (/v2/agentcanvas).The backend resolver was also exercised directly: 404 repro, 200 member-resolve, identical-404 for non-member (no leak), 400 for a malformed UUID.