[CONNECTED_ACCOUNT_BREAKING_CHANGE] Unify connected account permissions (#20732)

# Introduction
This PR is a followup of https://github.com/twentyhq/twenty/pull/20673

It aims to unify the authentication/permissions layer with all the
connectedAccount interactions across the application

## Deprecate
- findAll
- findById

## Email sync
An user can only sync the message of his own connected account

## Workflow email
- Related https://github.com/twentyhq/private-issues/issues/478
- Only reauthorize owned account
This commit is contained in:
Paul Rastoin
2026-05-20 13:36:58 +02:00
committed by GitHub
parent b454ad2aea
commit 3d49c17e34
21 changed files with 258 additions and 297 deletions
@@ -0,0 +1,13 @@
import { gql } from '@apollo/client';
export const WORKFLOW_STEP_CONNECTED_ACCOUNT_HANDLE = gql`
query WorkflowStepConnectedAccountHandle($connectedAccountId: UUID!) {
workflowStepConnectedAccountHandle(
connectedAccountId: $connectedAccountId
) {
id
handle
provider
}
}
`;
@@ -1,13 +1,13 @@
import { type ConnectedAccount } from '@/accounts/types/ConnectedAccount';
import { getMissingDraftEmailScopes } from '@/accounts/utils/hasMissingDraftEmailScopes';
import { WorkflowSendEmailAttachments } from '@/advanced-text-editor/components/WorkflowSendEmailAttachments';
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
import { FormAdvancedTextFieldInput } from '@/object-record/record-field/ui/form-types/components/FormAdvancedTextFieldInput';
import { FormMultiTextFieldInput } from '@/object-record/record-field/ui/form-types/components/FormMultiTextFieldInput';
import { FormTextFieldInput } from '@/object-record/record-field/ui/form-types/components/FormTextFieldInput';
import { GET_CONNECTED_ACCOUNT_BY_ID } from '@/settings/accounts/graphql/queries/getConnectedAccountById';
import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient';
import { useMyConnectedAccounts } from '@/settings/accounts/hooks/useMyConnectedAccounts';
import { useTriggerApisOAuth } from '@/settings/accounts/hooks/useTriggerApiOAuth';
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
import { Select } from '@/ui/input/components/Select';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
@@ -15,6 +15,7 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import { WORKFLOW_STEP_CONNECTED_ACCOUNT_HANDLE } from '@/workflow/graphql/queries/workflowStepConnectedAccountHandle';
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState';
import { type WorkflowEmailAction } from '@/workflow/types/WorkflowEmailAction';
@@ -109,6 +110,8 @@ export const WorkflowEditActionEmailBase = ({
handleFieldChange('connectedAccountId', connectedAccountId);
};
const apolloCoreClient = useApolloCoreClient();
const { accounts: myAccounts, loading: myAccountsLoading } =
useMyConnectedAccounts();
@@ -118,17 +121,13 @@ export const WorkflowEditActionEmailBase = ({
);
const { data: otherAccountData, loading: otherAccountLoading } = useQuery<{
connectedAccountById: Pick<
workflowStepConnectedAccountHandle: Pick<
ConnectedAccount,
| 'id'
| 'handle'
| 'provider'
| 'scopes'
| 'userWorkspaceId'
| 'connectionParameters'
'id' | 'handle' | 'provider'
> | null;
}>(GET_CONNECTED_ACCOUNT_BY_ID, {
variables: { id: configuredAccountId },
}>(WORKFLOW_STEP_CONNECTED_ACCOUNT_HANDLE, {
client: apolloCoreClient,
variables: { connectedAccountId: configuredAccountId },
skip:
!isDefined(configuredAccountId) ||
configuredAccountId === '' ||
@@ -137,25 +136,25 @@ export const WorkflowEditActionEmailBase = ({
const loading = myAccountsLoading || otherAccountLoading;
const otherAccount = otherAccountData?.connectedAccountById ?? null;
const otherAccount =
otherAccountData?.workflowStepConnectedAccountHandle ?? null;
const selectedAccount =
myAccounts.find((account) => account.id === configuredAccountId) ??
otherAccount ??
undefined;
const ownAccount = myAccounts.find(
(account) => account.id === configuredAccountId,
);
const missingDraftScopes =
action.type === 'DRAFT_EMAIL' && isDefined(selectedAccount)
? getMissingDraftEmailScopes(selectedAccount)
action.type === 'DRAFT_EMAIL' && isDefined(ownAccount)
? getMissingDraftEmailScopes(ownAccount)
: [];
const missingScopes =
isDefined(selectedAccount) &&
selectedAccount.provider !== ConnectedAccountProvider.IMAP_SMTP_CALDAV &&
isDefined(ownAccount) &&
ownAccount.provider !== ConnectedAccountProvider.IMAP_SMTP_CALDAV &&
missingDraftScopes.length > 0
? {
provider: selectedAccount.provider,
loginHint: selectedAccount.handle,
provider: ownAccount.provider,
loginHint: ownAccount.handle,
}
: null;
@@ -179,13 +178,7 @@ export const WorkflowEditActionEmailBase = ({
});
});
if (
isDefined(otherAccount) &&
!(
otherAccount.provider === ConnectedAccountProvider.IMAP_SMTP_CALDAV &&
!isDefined(otherAccount.connectionParameters?.SMTP)
)
) {
if (isDefined(otherAccount)) {
emptyOption = {
label: otherAccount.handle,
value: otherAccount.id,