From 911deb86db1b354841e8b8c6f29f77498451453d Mon Sep 17 00:00:00 2001 From: Celal Zamanoglu <95054566+celalzamanoglu@users.noreply.github.com> Date: Thu, 15 Jan 2026 16:47:23 +0300 Subject: [PATCH] fix login block credential selection (#4458) --- .../LoginBlockCredentialSelector.tsx | 102 +++++++++++------- 1 file changed, 66 insertions(+), 36 deletions(-) diff --git a/skyvern-frontend/src/routes/workflows/editor/nodes/LoginNode/LoginBlockCredentialSelector.tsx b/skyvern-frontend/src/routes/workflows/editor/nodes/LoginNode/LoginBlockCredentialSelector.tsx index 855501a6..ee367c89 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/LoginNode/LoginBlockCredentialSelector.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/LoginNode/LoginBlockCredentialSelector.tsx @@ -8,7 +8,7 @@ import { import { Skeleton } from "@/components/ui/skeleton"; import { useCredentialsQuery } from "@/routes/workflows/hooks/useCredentialsQuery"; import CloudContext from "@/store/CloudContext"; -import { useContext } from "react"; +import { useContext, useMemo } from "react"; import { useWorkflowParametersStore } from "@/store/WorkflowParametersStore"; import { CredentialsModal } from "@/routes/credentials/CredentialsModal"; import { PlusIcon } from "@radix-ui/react-icons"; @@ -68,6 +68,17 @@ function LoginBlockCredentialSelector({ nodeId, value, onChange }: Props) { page_size: 100, }); + // Determine which credential is currently selected (by credential_id) + // This must be before the early return to comply with React hooks rules + const selectedCredentialId = useMemo(() => { + if (!value) return undefined; + const parameter = credentialParameters.find((p) => p.key === value); + if (parameter && parameterIsSkyvernCredential(parameter)) { + return parameter.credentialId; + } + return undefined; + }, [value, credentialParameters]); + if (isCloud && isFetching) { return ; } @@ -78,11 +89,26 @@ function LoginBlockCredentialSelector({ nodeId, value, onChange }: Props) { type: "credential", })); - const credentialParameterOptions = credentialParameters.map((parameter) => ({ - label: parameter.key, - value: parameter.key, - type: "parameter", - })); + // Get the set of credential IDs that are in the vault + const credentialIdsInVault = new Set(credentials.map((c) => c.credential_id)); + + // Filter credential parameters to only show those that reference credentials + // NOT in the vault (e.g., Bitwarden, 1Password, Azure Vault credentials) + // Skyvern credential parameters are excluded because the actual credential is already shown + const filteredCredentialParameterOptions = credentialParameters + .filter((parameter) => { + if (parameterIsSkyvernCredential(parameter)) { + // Don't show Skyvern credential parameters if the credential is in the vault + return !credentialIdsInVault.has(parameter.credentialId); + } + // Show non-Skyvern credential parameters (Bitwarden, 1Password, etc.) + return true; + }) + .map((parameter) => ({ + label: parameter.key, + value: parameter.key, + type: "parameter", + })); const credentialInputParameterOptions = credentialInputParameters.map( (parameter) => ({ @@ -92,13 +118,6 @@ function LoginBlockCredentialSelector({ nodeId, value, onChange }: Props) { }), ); - const filteredCredentialParameterOptions = credentialParameterOptions.filter( - (option) => - !credentialOptions.some( - (credential) => credential.value === option.value, - ), - ); - const options = [ ...credentialOptions, ...filteredCredentialParameterOptions, @@ -108,7 +127,7 @@ function LoginBlockCredentialSelector({ nodeId, value, onChange }: Props) { return ( <>