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 (
<>