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 { Skeleton } from "@/components/ui/skeleton";
import { useCredentialsQuery } from "@/routes/workflows/hooks/useCredentialsQuery"; import { useCredentialsQuery } from "@/routes/workflows/hooks/useCredentialsQuery";
import CloudContext from "@/store/CloudContext"; import CloudContext from "@/store/CloudContext";
import { useContext } from "react"; import { useContext, useMemo } from "react";
import { useWorkflowParametersStore } from "@/store/WorkflowParametersStore"; import { useWorkflowParametersStore } from "@/store/WorkflowParametersStore";
import { CredentialsModal } from "@/routes/credentials/CredentialsModal"; import { CredentialsModal } from "@/routes/credentials/CredentialsModal";
import { PlusIcon } from "@radix-ui/react-icons"; import { PlusIcon } from "@radix-ui/react-icons";
@@ -68,6 +68,17 @@ function LoginBlockCredentialSelector({ nodeId, value, onChange }: Props) {
page_size: 100, 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) { if (isCloud && isFetching) {
return <Skeleton className="h-8 w-full" />; return <Skeleton className="h-8 w-full" />;
} }
@@ -78,11 +89,26 @@ function LoginBlockCredentialSelector({ nodeId, value, onChange }: Props) {
type: "credential", type: "credential",
})); }));
const credentialParameterOptions = credentialParameters.map((parameter) => ({ // Get the set of credential IDs that are in the vault
label: parameter.key, const credentialIdsInVault = new Set(credentials.map((c) => c.credential_id));
value: parameter.key,
type: "parameter", // 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( const credentialInputParameterOptions = credentialInputParameters.map(
(parameter) => ({ (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 = [ const options = [
...credentialOptions, ...credentialOptions,
...filteredCredentialParameterOptions, ...filteredCredentialParameterOptions,
@@ -108,7 +127,7 @@ function LoginBlockCredentialSelector({ nodeId, value, onChange }: Props) {
return ( return (
<> <>
<Select <Select
value={value} value={selectedCredentialId ?? value}
onValueChange={(newValue) => { onValueChange={(newValue) => {
if (newValue === "new") { if (newValue === "new") {
setIsOpen(true); setIsOpen(true);
@@ -122,19 +141,20 @@ function LoginBlockCredentialSelector({ nodeId, value, onChange }: Props) {
.filter((node) => node.id !== nodeId) .filter((node) => node.id !== nodeId)
.filter(isLoginNode); .filter(isLoginNode);
const thereIsAParameterWithThisValue = workflowParameters.some( // Check if current value references a Skyvern credential
(parameter) => const currentParameter = workflowParameters.find((parameter) => {
parameter.parameterType === "credential" && if (parameter.parameterType !== "credential") return false;
parameterIsSkyvernCredential(parameter) && if (!parameterIsSkyvernCredential(parameter)) return false;
parameter.credentialId === value, return parameter.key === value;
); });
const isUsedInOtherLoginNodes = const isUsedInOtherLoginNodes =
value && value &&
loginNodes.some((node) => node.data.parameterKeys.includes(value)); loginNodes.some((node) => node.data.parameterKeys.includes(value));
// Only delete old parameter if it's not used elsewhere
const deleteOldParameter = const deleteOldParameter =
thereIsAParameterWithThisValue && !isUsedInOtherLoginNodes; currentParameter && !isUsedInOtherLoginNodes;
if (deleteOldParameter) { if (deleteOldParameter) {
newParameters = newParameters.filter( 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; 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 ( return (
parameter.parameterType === "credential" && parameter.parameterType === "credential" &&
"credentialId" in parameter && parameterIsSkyvernCredential(parameter) &&
parameter.credentialId === newValue parameter.credentialId === newValue
); );
}); });
if (existingCredential) {
// Use the existing parameter's key if (existingParameter) {
parameterKeyToUse = existingCredential.key; // Reuse the existing parameter
parameterKeyToUse = existingParameter.key;
} else { } 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 existingKeys = newParameters.map((param) => param.key);
const newKey = const newKey =
generateDefaultCredentialParameterKey(existingKeys); 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); setWorkflowParameters(newParameters);
onChange?.(parameterKeyToUse);
}} }}
> >
<SelectTrigger className="w-full"> <SelectTrigger className="w-full">
<SelectValue placeholder="Select a credential parameter" /> <SelectValue placeholder="Select a credential" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
{options.map((option) => ( {options.map((option) => (
@@ -202,7 +231,7 @@ function LoginBlockCredentialSelector({ nodeId, value, onChange }: Props) {
const existingKeys = workflowParameters.map((param) => param.key); const existingKeys = workflowParameters.map((param) => param.key);
const newKey = generateDefaultCredentialParameterKey(existingKeys); const newKey = generateDefaultCredentialParameterKey(existingKeys);
onChange?.(newKey); // Update Zustand store first, then call onChange
setWorkflowParameters([ setWorkflowParameters([
...workflowParameters, ...workflowParameters,
{ {
@@ -211,6 +240,7 @@ function LoginBlockCredentialSelector({ nodeId, value, onChange }: Props) {
key: newKey, key: newKey,
}, },
]); ]);
onChange?.(newKey);
}} }}
/> />
</> </>