fix login block credential selection (#4458)

This commit is contained in:
Celal Zamanoglu
2026-01-15 16:47:23 +03:00
committed by GitHub
parent 55c71e7e3e
commit 911deb86db

View File

@@ -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 <Skeleton className="h-8 w-full" />;
}
@@ -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 (
<>
<Select
value={value}
value={selectedCredentialId ?? value}
onValueChange={(newValue) => {
if (newValue === "new") {
setIsOpen(true);
@@ -122,19 +141,20 @@ function LoginBlockCredentialSelector({ nodeId, value, onChange }: Props) {
.filter((node) => node.id !== nodeId)
.filter(isLoginNode);
const thereIsAParameterWithThisValue = workflowParameters.some(
(parameter) =>
parameter.parameterType === "credential" &&
parameterIsSkyvernCredential(parameter) &&
parameter.credentialId === value,
);
// Check if current value references a Skyvern credential
const currentParameter = workflowParameters.find((parameter) => {
if (parameter.parameterType !== "credential") return false;
if (!parameterIsSkyvernCredential(parameter)) return false;
return parameter.key === value;
});
const isUsedInOtherLoginNodes =
value &&
loginNodes.some((node) => node.data.parameterKeys.includes(value));
// Only delete old parameter if it's not used elsewhere
const deleteOldParameter =
thereIsAParameterWithThisValue && !isUsedInOtherLoginNodes;
currentParameter && !isUsedInOtherLoginNodes;
if (deleteOldParameter) {
newParameters = newParameters.filter(
@@ -142,21 +162,28 @@ function LoginBlockCredentialSelector({ nodeId, value, onChange }: Props) {
);
}
const option = options.find((option) => option.value === newValue);
// Check if user selected an actual credential (by credential_id)
const selectedCredential = credentialOptions.find(
(option) => option.value === newValue,
);
let parameterKeyToUse = newValue;
if (option?.type === "credential") {
const existingCredential = workflowParameters.find((parameter) => {
if (selectedCredential) {
// User selected an actual credential
const existingParameter = newParameters.find((parameter) => {
return (
parameter.parameterType === "credential" &&
"credentialId" in parameter &&
parameterIsSkyvernCredential(parameter) &&
parameter.credentialId === newValue
);
});
if (existingCredential) {
// Use the existing parameter's key
parameterKeyToUse = existingCredential.key;
if (existingParameter) {
// Reuse the existing parameter
parameterKeyToUse = existingParameter.key;
} else {
// Generate a new parameter key based on existing keys
// Create a new parameter for this credential
const existingKeys = newParameters.map((param) => param.key);
const newKey =
generateDefaultCredentialParameterKey(existingKeys);
@@ -171,17 +198,19 @@ function LoginBlockCredentialSelector({ nodeId, value, onChange }: Props) {
},
];
}
} else if (deleteOldParameter) {
newParameters = newParameters.filter(
(parameter) => parameter.key !== value,
);
}
onChange?.(parameterKeyToUse);
// If user selected a parameter (non-Skyvern credential or input parameter)
// just use it directly (parameterKeyToUse is already set to newValue)
// Update Zustand store first, then call onChange
// This ensures workflowParameters is updated before the parent re-renders
// with the new value, so selectedCredentialId computes correctly
setWorkflowParameters(newParameters);
onChange?.(parameterKeyToUse);
}}
>
<SelectTrigger className="w-full">
<SelectValue placeholder="Select a credential parameter" />
<SelectValue placeholder="Select a credential" />
</SelectTrigger>
<SelectContent>
{options.map((option) => (
@@ -202,7 +231,7 @@ function LoginBlockCredentialSelector({ nodeId, value, onChange }: Props) {
const existingKeys = workflowParameters.map((param) => param.key);
const newKey = generateDefaultCredentialParameterKey(existingKeys);
onChange?.(newKey);
// Update Zustand store first, then call onChange
setWorkflowParameters([
...workflowParameters,
{
@@ -211,6 +240,7 @@ function LoginBlockCredentialSelector({ nodeId, value, onChange }: Props) {
key: newKey,
},
]);
onChange?.(newKey);
}}
/>
</>