Skip to content

Commit 1827b50

Browse files
feat(ui,clerk-js,shared): Allow to link external accounts to enterprise accounts (#8091)
Co-authored-by: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com>
1 parent 59c4278 commit 1827b50

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+465
-50
lines changed

.changeset/large-cameras-talk.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
'@clerk/localizations': minor
3+
'@clerk/clerk-js': minor
4+
'@clerk/shared': minor
5+
'@clerk/ui': minor
6+
---
7+
8+
Allow to link external accounts to enterprise accounts via `UserProfile`

packages/clerk-js/src/core/resources/EnterpriseAccount.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ export class EnterpriseAccount extends BaseResource implements EnterpriseAccount
6161
return this;
6262
}
6363

64+
destroy = (): Promise<void> => this._baseDelete();
65+
6466
public __internal_toSnapshot(): EnterpriseAccountJSONSnapshot {
6567
return {
6668
object: 'enterprise_account',
@@ -93,6 +95,7 @@ export class EnterpriseAccountConnection extends BaseResource implements Enterpr
9395
protocol!: EnterpriseAccountResource['protocol'];
9496
provider!: EnterpriseAccountResource['provider'];
9597
syncUserAttributes!: boolean;
98+
allowOrganizationAccountLinking!: boolean;
9699
createdAt!: Date;
97100
updatedAt!: Date;
98101
enterpriseConnectionId: string | null = '';
@@ -114,6 +117,7 @@ export class EnterpriseAccountConnection extends BaseResource implements Enterpr
114117
this.allowSubdomains = data.allow_subdomains;
115118
this.allowIdpInitiated = data.allow_idp_initiated;
116119
this.disableAdditionalIdentifications = data.disable_additional_identifications;
120+
this.allowOrganizationAccountLinking = data.allow_organization_account_linking;
117121
this.createdAt = unixEpochToDate(data.created_at);
118122
this.updatedAt = unixEpochToDate(data.updated_at);
119123
this.enterpriseConnectionId = data.enterprise_connection_id;
@@ -136,6 +140,7 @@ export class EnterpriseAccountConnection extends BaseResource implements Enterpr
136140
allow_subdomains: this.allowSubdomains,
137141
allow_idp_initiated: this.allowIdpInitiated,
138142
disable_additional_identifications: this.disableAdditionalIdentifications,
143+
allow_organization_account_linking: this.allowOrganizationAccountLinking,
139144
enterprise_connection_id: this.enterpriseConnectionId,
140145
created_at: this.createdAt.getTime(),
141146
updated_at: this.updatedAt.getTime(),

packages/clerk-js/src/core/resources/User.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@ import type {
99
DeletedObjectJSON,
1010
DeletedObjectResource,
1111
EmailAddressResource,
12+
EnterpriseAccountConnectionJSON,
13+
EnterpriseAccountConnectionResource,
1214
EnterpriseAccountResource,
1315
ExternalAccountJSON,
1416
ExternalAccountResource,
17+
GetEnterpriseConnectionsParams,
1518
GetOrganizationMemberships,
1619
GetUserOrganizationInvitationsParams,
1720
GetUserOrganizationSuggestionsParams,
@@ -42,6 +45,7 @@ import {
4245
DeletedObject,
4346
EmailAddress,
4447
EnterpriseAccount,
48+
EnterpriseAccountConnection,
4549
ExternalAccount,
4650
Image,
4751
OrganizationMembership,
@@ -156,7 +160,7 @@ export class User extends BaseResource implements UserResource {
156160
};
157161

158162
createExternalAccount = async (params: CreateExternalAccountParams): Promise<ExternalAccountResource> => {
159-
const { strategy, redirectUrl, additionalScopes } = params || {};
163+
const { strategy, redirectUrl, additionalScopes, enterpriseConnectionId } = params || {};
160164

161165
const json = (
162166
await BaseResource._fetch<ExternalAccountJSON>({
@@ -166,6 +170,7 @@ export class User extends BaseResource implements UserResource {
166170
strategy,
167171
redirect_url: redirectUrl,
168172
additional_scope: additionalScopes,
173+
enterprise_connection_id: enterpriseConnectionId,
169174
} as any,
170175
})
171176
)?.response as unknown as ExternalAccountJSON;
@@ -289,6 +294,28 @@ export class User extends BaseResource implements UserResource {
289294
return new DeletedObject(json);
290295
};
291296

297+
getEnterpriseConnections = async (
298+
params?: GetEnterpriseConnectionsParams,
299+
): Promise<EnterpriseAccountConnectionResource[]> => {
300+
const { withOrganizationAccountLinking } = params || {};
301+
302+
const json = (
303+
await BaseResource._fetch({
304+
path: '/me/enterprise_connections',
305+
method: 'GET',
306+
...(withOrganizationAccountLinking !== undefined
307+
? {
308+
search: {
309+
with_organization_account_linking: String(withOrganizationAccountLinking),
310+
},
311+
}
312+
: {}),
313+
})
314+
)?.response as unknown as EnterpriseAccountConnectionJSON[];
315+
316+
return (json || []).map(connection => new EnterpriseAccountConnection(connection));
317+
};
318+
292319
initializePaymentMethod: typeof initializePaymentMethod = params => {
293320
return initializePaymentMethod(params);
294321
};

packages/clerk-js/src/core/resources/__tests__/User.test.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,88 @@ describe('User', () => {
4242
});
4343
});
4444

45+
it('creates an external account with enterprise connection id', async () => {
46+
const externalAccountJSON = {
47+
object: 'external_account',
48+
provider: 'saml_okta',
49+
verification: {
50+
external_verification_redirect_url: 'https://www.example.com',
51+
},
52+
};
53+
54+
// @ts-ignore
55+
BaseResource._fetch = vi.fn().mockReturnValue(Promise.resolve({ response: externalAccountJSON }));
56+
57+
const user = new User({
58+
email_addresses: [],
59+
phone_numbers: [],
60+
web3_wallets: [],
61+
external_accounts: [],
62+
} as unknown as UserJSON);
63+
64+
await user.createExternalAccount({
65+
enterpriseConnectionId: 'ec_123',
66+
redirectUrl: 'https://www.example.com',
67+
});
68+
69+
// @ts-ignore
70+
expect(BaseResource._fetch).toHaveBeenCalledWith({
71+
method: 'POST',
72+
path: '/me/external_accounts',
73+
body: {
74+
strategy: undefined,
75+
redirect_url: 'https://www.example.com',
76+
additional_scope: undefined,
77+
enterprise_connection_id: 'ec_123',
78+
},
79+
});
80+
});
81+
82+
it('fetches enterprise connections', async () => {
83+
const enterpriseConnectionsJSON = [
84+
{
85+
id: 'ec_123',
86+
object: 'enterprise_account_connection',
87+
name: 'Acme Corp SSO',
88+
active: true,
89+
allow_organization_account_linking: true,
90+
domain: 'acme.com',
91+
protocol: 'saml',
92+
provider: 'saml_okta',
93+
logo_public_url: null,
94+
sync_user_attributes: true,
95+
allow_subdomains: false,
96+
allow_idp_initiated: false,
97+
disable_additional_identifications: false,
98+
enterprise_connection_id: 'ec_123',
99+
created_at: 1234567890,
100+
updated_at: 1234567890,
101+
},
102+
];
103+
104+
// @ts-ignore
105+
BaseResource._fetch = vi.fn().mockReturnValue(Promise.resolve({ response: enterpriseConnectionsJSON }));
106+
107+
const user = new User({
108+
email_addresses: [],
109+
phone_numbers: [],
110+
web3_wallets: [],
111+
external_accounts: [],
112+
} as unknown as UserJSON);
113+
114+
const connections = await user.getEnterpriseConnections();
115+
116+
// @ts-ignore
117+
expect(BaseResource._fetch).toHaveBeenCalledWith({
118+
method: 'GET',
119+
path: '/me/enterprise_connections',
120+
});
121+
122+
expect(connections).toHaveLength(1);
123+
expect(connections[0].name).toBe('Acme Corp SSO');
124+
expect(connections[0].allowOrganizationAccountLinking).toBe(true);
125+
});
126+
45127
it('creates a web3 wallet', async () => {
46128
const targetWeb3Wallet = '0x0000000000000000000000000000000000000000';
47129
const web3WalletJSON = {

packages/localizations/src/ar-SA.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,6 +1338,7 @@ export const arSA: LocalizationResource = {
13381338
title: 'العنوان الإلكتروني',
13391339
},
13401340
enterpriseAccountsSection: {
1341+
primaryButton: 'ربط حساب',
13411342
title: 'حساب المؤسسات',
13421343
},
13431344
headerTitle__account: 'الحساب',

packages/localizations/src/be-BY.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1357,6 +1357,7 @@ export const beBY: LocalizationResource = {
13571357
title: 'Адрасы электроннай пошты',
13581358
},
13591359
enterpriseAccountsSection: {
1360+
primaryButton: 'Падключыць уліковы запіс',
13601361
title: 'Enterprise accounts',
13611362
},
13621363
headerTitle__account: 'Уліковы запіс',

packages/localizations/src/bg-BG.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1348,6 +1348,7 @@ export const bgBG: LocalizationResource = {
13481348
title: 'Имейл адреси',
13491349
},
13501350
enterpriseAccountsSection: {
1351+
primaryButton: 'Свържи акаунт',
13511352
title: 'Корпоративни акаунти',
13521353
},
13531354
headerTitle__account: 'Профил',

packages/localizations/src/bn-IN.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1359,6 +1359,7 @@ export const bnIN: LocalizationResource = {
13591359
title: 'ইমেইল ঠিকানা',
13601360
},
13611361
enterpriseAccountsSection: {
1362+
primaryButton: 'অ্যাকাউন্ট সংযুক্ত করুন',
13621363
title: 'এন্টারপ্রাইজ অ্যাকাউন্ট',
13631364
},
13641365
headerTitle__account: 'প্রোফাইল বিবরণ',

packages/localizations/src/ca-ES.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1354,6 +1354,7 @@ export const caES: LocalizationResource = {
13541354
title: 'Adreces de correu electrònic',
13551355
},
13561356
enterpriseAccountsSection: {
1357+
primaryButton: 'Connecta compte',
13571358
title: "Comptes d'empresa",
13581359
},
13591360
headerTitle__account: 'Detalls del perfil',

packages/localizations/src/cs-CZ.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1361,6 +1361,7 @@ export const csCZ: LocalizationResource = {
13611361
title: 'E-mailové adresy',
13621362
},
13631363
enterpriseAccountsSection: {
1364+
primaryButton: 'Připojit účet',
13641365
title: 'Podnikové účty',
13651366
},
13661367
headerTitle__account: 'Podrobnosti profilu',

0 commit comments

Comments
 (0)