feat(ui,clerk-js,shared): Allow to link external accounts to enterprise accounts#8091
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🦋 Changeset detectedLatest commit: 6d6d5dc The changes in this PR will be included in the next version bump. This PR includes changesets to release 21 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
packages/ui/src/components/UserProfile/EnterpriseAccountsSection.tsx
Outdated
Show resolved
Hide resolved
packages/ui/src/components/UserProfile/EnterpriseAccountsSection.tsx
Outdated
Show resolved
Hide resolved
packages/ui/src/components/UserProfile/EnterpriseAccountsSection.tsx
Outdated
Show resolved
Hide resolved
|
Todo: filter enterprise connections that the user already have an account with. Also only display the "connect enterprise account" button if there's at least one available connection to connect |
b3579e5 to
5ae8252
Compare
5ae8252 to
a76363d
Compare
c97b99e to
609631b
Compare
120dab9 to
e2f89fd
Compare
@clerk/agent-toolkit
@clerk/astro
@clerk/backend
@clerk/chrome-extension
@clerk/clerk-js
@clerk/dev-cli
@clerk/expo
@clerk/expo-passkeys
@clerk/express
@clerk/fastify
@clerk/hono
@clerk/localizations
@clerk/nextjs
@clerk/nuxt
@clerk/react
@clerk/react-router
@clerk/shared
@clerk/tanstack-react-start
@clerk/testing
@clerk/ui
@clerk/upgrade
@clerk/vue
commit: |
519aff7 to
239b897
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/ui/src/components/UserProfile/EnterpriseAccountsSection.tsx`:
- Around line 130-135: The current filter for linkableEnterpriseConnections
deduplicates against activeEnterpriseAccounts (a display subset), causing
already-linked connections that are omitted from that subset to be offered again
and break createExternalAccount; change the dedupe target to the full
linked-account set instead of activeEnterpriseAccounts — i.e., when computing
linkableEnterpriseConnections, compare c.id against the complete list of the
user's enterprise/external accounts (the canonical linked-account collection
returned by your user data hook or state) rather than activeEnterpriseAccounts
so duplicates are correctly excluded before calling createExternalAccount.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Organization UI (inherited)
Review profile: ASSERTIVE
Plan: Pro
Run ID: 387b1b9b-a838-4467-a544-d2db9c9c039b
📒 Files selected for processing (50)
packages/localizations/src/ar-SA.tspackages/localizations/src/be-BY.tspackages/localizations/src/bg-BG.tspackages/localizations/src/bn-IN.tspackages/localizations/src/ca-ES.tspackages/localizations/src/cs-CZ.tspackages/localizations/src/da-DK.tspackages/localizations/src/de-DE.tspackages/localizations/src/el-GR.tspackages/localizations/src/en-GB.tspackages/localizations/src/es-CR.tspackages/localizations/src/es-ES.tspackages/localizations/src/es-MX.tspackages/localizations/src/es-UY.tspackages/localizations/src/fa-IR.tspackages/localizations/src/fi-FI.tspackages/localizations/src/fr-FR.tspackages/localizations/src/he-IL.tspackages/localizations/src/hi-IN.tspackages/localizations/src/hr-HR.tspackages/localizations/src/hu-HU.tspackages/localizations/src/id-ID.tspackages/localizations/src/is-IS.tspackages/localizations/src/it-IT.tspackages/localizations/src/ja-JP.tspackages/localizations/src/kk-KZ.tspackages/localizations/src/ko-KR.tspackages/localizations/src/mn-MN.tspackages/localizations/src/ms-MY.tspackages/localizations/src/nb-NO.tspackages/localizations/src/nl-BE.tspackages/localizations/src/nl-NL.tspackages/localizations/src/pl-PL.tspackages/localizations/src/pt-BR.tspackages/localizations/src/pt-PT.tspackages/localizations/src/ro-RO.tspackages/localizations/src/ru-RU.tspackages/localizations/src/sk-SK.tspackages/localizations/src/sr-RS.tspackages/localizations/src/sv-SE.tspackages/localizations/src/ta-IN.tspackages/localizations/src/te-IN.tspackages/localizations/src/th-TH.tspackages/localizations/src/tr-TR.tspackages/localizations/src/uk-UA.tspackages/localizations/src/utils/enUS_v4.tspackages/localizations/src/vi-VN.tspackages/localizations/src/zh-CN.tspackages/localizations/src/zh-TW.tspackages/ui/src/components/UserProfile/EnterpriseAccountsSection.tsx
| const loadingKey = `enterprise_${connection.id}`; | ||
|
|
||
| const createExternalAccount = useReverification(() => { | ||
| const redirectUrl = isModal ? appendModalState({ url: window.location.href, componentName }) : window.location.href; |
There was a problem hiding this comment.
I wonder if we shouldn't abstract this logic somehow into a hook, I would personally forget all the time to check for the modal mode to append the modal state 😅
| return user?.createExternalAccount({ | ||
| enterpriseConnectionId: connection.id, | ||
| redirectUrl, | ||
| }); |
There was a problem hiding this comment.
❓ A few questions here around the data fetching:
- When we use the
user.createExternalAccountis that using the User resource directly? - Does that handles invalidating the
/me/enterprise_connectionsquery? - I think in this case it doesn't matter because we always redirect to the IdP to sign in and connect the account right?
- In this case, would be worth making this call a hook in the react package? I wonder where do we draw the line between create a custom hook or the resource directly.
There was a problem hiding this comment.
I think in this case it doesn't matter because we always redirect to the IdP to sign in and connect the account right?
Correct, so the IdP would redirect back to the app, with the UserProfile opened, where it'll trigger /me/enterprise_connections again
There was a problem hiding this comment.
In this case, would be worth making this call a hook in the react package? I wonder where do we draw the line between create a custom hook or the resource directly.
Since this logic is specific for the enterprise account section, so makes sense to have it in there, and only rely on the mutation + query
We could extract this to a hook within the same module at least for semantics
| return createExternalAccount() | ||
| .then(res => { | ||
| if (res?.verification?.externalVerificationRedirectURL) { | ||
| void sleep(2000).then(() => card.setIdle(loadingKey)); |
There was a problem hiding this comment.
Is the sleep just cosmetic to make the windowNavigate smoother?
There was a problem hiding this comment.
Mostly presentational, I've kept it consistent with the same logic we have to connect to external accounts
If navigation is slow, loading eventually clears after 2s instead of staying stuck forever
239b897 to
cd6ca61
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/ui/src/components/UserProfile/ConnectedAccountsSection.tsx`:
- Around line 70-72: The early return in ConnectedAccountsSection that returns
null when accounts.length === 0 hides the AddConnectedAccount UI; change the
logic so the component only returns null when there are no accounts AND creation
is not allowed. Specifically, update the conditional around accounts to check
shouldAllowCreation (or the prop/name used) and render the AddConnectedAccount
component when shouldAllowCreation is true even if accounts.length === 0; keep
the existing rendering path for when accounts exist (map over accounts) and only
skip rendering the whole section if no accounts AND shouldAllowCreation is
false.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Organization UI (inherited)
Review profile: ASSERTIVE
Plan: Pro
Run ID: d0d40689-640c-4223-bd5f-984cd849dcbe
📒 Files selected for processing (51)
packages/localizations/src/ar-SA.tspackages/localizations/src/be-BY.tspackages/localizations/src/bg-BG.tspackages/localizations/src/bn-IN.tspackages/localizations/src/ca-ES.tspackages/localizations/src/cs-CZ.tspackages/localizations/src/da-DK.tspackages/localizations/src/de-DE.tspackages/localizations/src/el-GR.tspackages/localizations/src/en-GB.tspackages/localizations/src/es-CR.tspackages/localizations/src/es-ES.tspackages/localizations/src/es-MX.tspackages/localizations/src/es-UY.tspackages/localizations/src/fa-IR.tspackages/localizations/src/fi-FI.tspackages/localizations/src/fr-FR.tspackages/localizations/src/he-IL.tspackages/localizations/src/hi-IN.tspackages/localizations/src/hr-HR.tspackages/localizations/src/hu-HU.tspackages/localizations/src/id-ID.tspackages/localizations/src/is-IS.tspackages/localizations/src/it-IT.tspackages/localizations/src/ja-JP.tspackages/localizations/src/kk-KZ.tspackages/localizations/src/ko-KR.tspackages/localizations/src/mn-MN.tspackages/localizations/src/ms-MY.tspackages/localizations/src/nb-NO.tspackages/localizations/src/nl-BE.tspackages/localizations/src/nl-NL.tspackages/localizations/src/pl-PL.tspackages/localizations/src/pt-BR.tspackages/localizations/src/pt-PT.tspackages/localizations/src/ro-RO.tspackages/localizations/src/ru-RU.tspackages/localizations/src/sk-SK.tspackages/localizations/src/sr-RS.tspackages/localizations/src/sv-SE.tspackages/localizations/src/ta-IN.tspackages/localizations/src/te-IN.tspackages/localizations/src/th-TH.tspackages/localizations/src/tr-TR.tspackages/localizations/src/uk-UA.tspackages/localizations/src/utils/enUS_v4.tspackages/localizations/src/vi-VN.tspackages/localizations/src/zh-CN.tspackages/localizations/src/zh-TW.tspackages/ui/src/components/UserProfile/ConnectedAccountsSection.tsxpackages/ui/src/components/UserProfile/EnterpriseAccountsSection.tsx
| if (accounts.length === 0) { | ||
| return null; | ||
| } |
There was a problem hiding this comment.
Blocking: early return hides “Add connected account” for first-time users
Returning null when accounts.length === 0 prevents rendering AddConnectedAccount when shouldAllowCreation is true, so users with no existing accounts cannot connect one from this section.
Suggested fix
- if (accounts.length === 0) {
- return null;
- }
+ if (accounts.length === 0 && !shouldAllowCreation) {
+ return null;
+ }As per coding guidelines: "Only comment on issues that would block merging... focus strictly on merge-blocking concerns."
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (accounts.length === 0) { | |
| return null; | |
| } | |
| if (accounts.length === 0 && !shouldAllowCreation) { | |
| return null; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/ui/src/components/UserProfile/ConnectedAccountsSection.tsx` around
lines 70 - 72, The early return in ConnectedAccountsSection that returns null
when accounts.length === 0 hides the AddConnectedAccount UI; change the logic so
the component only returns null when there are no accounts AND creation is not
allowed. Specifically, update the conditional around accounts to check
shouldAllowCreation (or the prop/name used) and render the AddConnectedAccount
component when shouldAllowCreation is true even if accounts.length === 0; keep
the existing rendering path for when accounts exist (map over accounts) and only
skip rendering the whole section if no accounts AND shouldAllowCreation is
false.
cd6ca61 to
e10e17f
Compare
e10e17f to
82f1fe4
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/clerk-js/src/core/resources/EnterpriseAccount.ts (1)
108-123:⚠️ Potential issue | 🔴 CriticalPopulate
protocolwhen hydrating enterprise connections.
User.getEnterpriseConnections()now returnsEnterpriseAccountConnectiondirectly, but this mapper never copiesdata.protocol. Every fetched connection will exposeprotocolasundefined, and__internal_toSnapshot()will persist that broken value back out.Suggested fix
this.name = data.name; this.domain = data.domain; this.active = data.active; + this.protocol = data.protocol; this.provider = data.provider; this.logoPublicUrl = data.logo_public_url;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/clerk-js/src/core/resources/EnterpriseAccount.ts` around lines 108 - 123, The fromJSON mapper in EnterpriseAccountConnection (method protected fromJSON(...)) never copies the protocol field, so hydrated EnterpriseAccountConnection instances end up with protocol undefined and __internal_toSnapshot() will persist that bad value; fix by assigning this.protocol = data.protocol inside fromJSON alongside the other property mappings so protocol is populated when hydrating and subsequently serialized.
♻️ Duplicate comments (1)
packages/ui/src/components/UserProfile/ConnectedAccountsSection.tsx (1)
70-72:⚠️ Potential issue | 🔴 CriticalBlocking: early return hides account creation flow.
At Line 70, returning
nullwhenaccounts.length === 0also hidesAddConnectedAccounteven whenshouldAllowCreationis true, so users with no current accounts cannot connect one.Suggested fix
- if (accounts.length === 0) { + if (accounts.length === 0 && !shouldAllowCreation) { return null; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/ui/src/components/UserProfile/ConnectedAccountsSection.tsx` around lines 70 - 72, ConnectedAccountsSection currently returns early when accounts.length === 0 which hides the AddConnectedAccount flow; remove the unconditional early return and instead render the section container even when accounts is empty, conditionally showing either the list of accounts (when accounts.length > 0) or an empty state that includes AddConnectedAccount when shouldAllowCreation is true; update the rendering logic in ConnectedAccountsSection (references: accounts, shouldAllowCreation, AddConnectedAccount) so AddConnectedAccount is rendered whenever shouldAllowCreation is true regardless of accounts.length.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/shared/src/react/hooks/useUserEnterpriseConnections.tsx`:
- Around line 34-50: The cache key for the query currently only uses userId, so
toggling withOrganizationAccountLinking still hits the same cache; update the
cache key generation by passing withOrganizationAccountLinking into
useUserEnterpriseConnectionsCacheKeys (so the returned queryKey/stableKey
include that flag) and then keep using those keys in useClerkQuery and
useClearQueriesOnSignOut; modify the call site where
useUserEnterpriseConnectionsCacheKeys is invoked and the cache key factory (the
implementation behind useUserEnterpriseConnectionsCacheKeys) to include
withOrganizationAccountLinking as part of the key.
---
Outside diff comments:
In `@packages/clerk-js/src/core/resources/EnterpriseAccount.ts`:
- Around line 108-123: The fromJSON mapper in EnterpriseAccountConnection
(method protected fromJSON(...)) never copies the protocol field, so hydrated
EnterpriseAccountConnection instances end up with protocol undefined and
__internal_toSnapshot() will persist that bad value; fix by assigning
this.protocol = data.protocol inside fromJSON alongside the other property
mappings so protocol is populated when hydrating and subsequently serialized.
---
Duplicate comments:
In `@packages/ui/src/components/UserProfile/ConnectedAccountsSection.tsx`:
- Around line 70-72: ConnectedAccountsSection currently returns early when
accounts.length === 0 which hides the AddConnectedAccount flow; remove the
unconditional early return and instead render the section container even when
accounts is empty, conditionally showing either the list of accounts (when
accounts.length > 0) or an empty state that includes AddConnectedAccount when
shouldAllowCreation is true; update the rendering logic in
ConnectedAccountsSection (references: accounts, shouldAllowCreation,
AddConnectedAccount) so AddConnectedAccount is rendered whenever
shouldAllowCreation is true regardless of accounts.length.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Organization UI (inherited)
Review profile: ASSERTIVE
Plan: Pro
Run ID: 8580e5a6-d514-4c36-995d-f1c44126fa9a
📒 Files selected for processing (64)
.changeset/large-cameras-talk.mdpackages/clerk-js/src/core/resources/EnterpriseAccount.tspackages/clerk-js/src/core/resources/User.tspackages/clerk-js/src/core/resources/__tests__/User.test.tspackages/localizations/src/ar-SA.tspackages/localizations/src/be-BY.tspackages/localizations/src/bg-BG.tspackages/localizations/src/bn-IN.tspackages/localizations/src/ca-ES.tspackages/localizations/src/cs-CZ.tspackages/localizations/src/da-DK.tspackages/localizations/src/de-DE.tspackages/localizations/src/el-GR.tspackages/localizations/src/en-GB.tspackages/localizations/src/en-US.tspackages/localizations/src/es-CR.tspackages/localizations/src/es-ES.tspackages/localizations/src/es-MX.tspackages/localizations/src/es-UY.tspackages/localizations/src/fa-IR.tspackages/localizations/src/fi-FI.tspackages/localizations/src/fr-FR.tspackages/localizations/src/he-IL.tspackages/localizations/src/hi-IN.tspackages/localizations/src/hr-HR.tspackages/localizations/src/hu-HU.tspackages/localizations/src/id-ID.tspackages/localizations/src/is-IS.tspackages/localizations/src/it-IT.tspackages/localizations/src/ja-JP.tspackages/localizations/src/kk-KZ.tspackages/localizations/src/ko-KR.tspackages/localizations/src/mn-MN.tspackages/localizations/src/ms-MY.tspackages/localizations/src/nb-NO.tspackages/localizations/src/nl-BE.tspackages/localizations/src/nl-NL.tspackages/localizations/src/pl-PL.tspackages/localizations/src/pt-BR.tspackages/localizations/src/pt-PT.tspackages/localizations/src/ro-RO.tspackages/localizations/src/ru-RU.tspackages/localizations/src/sk-SK.tspackages/localizations/src/sr-RS.tspackages/localizations/src/sv-SE.tspackages/localizations/src/ta-IN.tspackages/localizations/src/te-IN.tspackages/localizations/src/th-TH.tspackages/localizations/src/tr-TR.tspackages/localizations/src/uk-UA.tspackages/localizations/src/utils/enUS_v4.tspackages/localizations/src/vi-VN.tspackages/localizations/src/zh-CN.tspackages/localizations/src/zh-TW.tspackages/shared/src/react/hooks/index.tspackages/shared/src/react/hooks/useUserEnterpriseConnections.shared.tspackages/shared/src/react/hooks/useUserEnterpriseConnections.tsxpackages/shared/src/react/stable-keys.tspackages/shared/src/types/enterpriseAccount.tspackages/shared/src/types/json.tspackages/shared/src/types/localization.tspackages/shared/src/types/user.tspackages/ui/src/components/UserProfile/ConnectedAccountsSection.tsxpackages/ui/src/components/UserProfile/EnterpriseAccountsSection.tsx
82f1fe4 to
4ced24e
Compare
Description
This PR adds a new feature for the enterprise accounts section in
UserProfile- it allows to connect to enterprise accounts based on existing enterprise connections for the current user's active organization.This is only possible if the enterprise connection has an
allowOrganizationAccountLinkingflag.CleanShot.2026-03-23.at.23.27.45.mp4
Checklist
pnpm testruns as expected.pnpm buildruns as expected.Type of change
Summary by CodeRabbit
Release Notes
New Features
Localization