Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/clerk-js/src/core/resources/EnterpriseAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ export class EnterpriseAccount extends BaseResource implements EnterpriseAccount
return this;
}

destroy = (): Promise<void> => this._baseDelete();

public __internal_toSnapshot(): EnterpriseAccountJSONSnapshot {
return {
object: 'enterprise_account',
Expand Down Expand Up @@ -93,6 +95,7 @@ export class EnterpriseAccountConnection extends BaseResource implements Enterpr
protocol!: EnterpriseAccountResource['protocol'];
provider!: EnterpriseAccountResource['provider'];
syncUserAttributes!: boolean;
allowAccountLinking!: boolean;
createdAt!: Date;
updatedAt!: Date;
enterpriseConnectionId: string | null = '';
Expand All @@ -114,6 +117,7 @@ export class EnterpriseAccountConnection extends BaseResource implements Enterpr
this.allowSubdomains = data.allow_subdomains;
this.allowIdpInitiated = data.allow_idp_initiated;
this.disableAdditionalIdentifications = data.disable_additional_identifications;
this.allowAccountLinking = data.allow_account_linking;
this.createdAt = unixEpochToDate(data.created_at);
this.updatedAt = unixEpochToDate(data.updated_at);
this.enterpriseConnectionId = data.enterprise_connection_id;
Expand All @@ -136,6 +140,7 @@ export class EnterpriseAccountConnection extends BaseResource implements Enterpr
allow_subdomains: this.allowSubdomains,
allow_idp_initiated: this.allowIdpInitiated,
disable_additional_identifications: this.disableAdditionalIdentifications,
allow_account_linking: this.allowAccountLinking,
enterprise_connection_id: this.enterpriseConnectionId,
created_at: this.createdAt.getTime(),
updated_at: this.updatedAt.getTime(),
Expand Down
17 changes: 16 additions & 1 deletion packages/clerk-js/src/core/resources/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import type {
DeletedObjectJSON,
DeletedObjectResource,
EmailAddressResource,
EnterpriseAccountConnectionJSON,
EnterpriseAccountConnectionResource,
EnterpriseAccountResource,
ExternalAccountJSON,
ExternalAccountResource,
Expand Down Expand Up @@ -42,6 +44,7 @@ import {
DeletedObject,
EmailAddress,
EnterpriseAccount,
EnterpriseAccountConnection,
ExternalAccount,
Image,
OrganizationMembership,
Expand Down Expand Up @@ -156,7 +159,7 @@ export class User extends BaseResource implements UserResource {
};

createExternalAccount = async (params: CreateExternalAccountParams): Promise<ExternalAccountResource> => {
const { strategy, redirectUrl, additionalScopes } = params || {};
const { strategy, redirectUrl, additionalScopes, enterpriseConnectionId } = params || {};

const json = (
await BaseResource._fetch<ExternalAccountJSON>({
Expand All @@ -166,6 +169,7 @@ export class User extends BaseResource implements UserResource {
strategy,
redirect_url: redirectUrl,
additional_scope: additionalScopes,
enterprise_connection_id: enterpriseConnectionId,
} as any,
})
)?.response as unknown as ExternalAccountJSON;
Expand Down Expand Up @@ -289,6 +293,17 @@ export class User extends BaseResource implements UserResource {
return new DeletedObject(json);
};

getEnterpriseConnections = async (): Promise<EnterpriseAccountConnectionResource[]> => {
const json = (
await BaseResource._fetch({
path: '/me/enterprise_connections',
method: 'GET',
})
)?.response as unknown as EnterpriseAccountConnectionJSON[];

return (json || []).map(connection => new EnterpriseAccountConnection(connection));
};

initializePaymentMethod: typeof initializePaymentMethod = params => {
return initializePaymentMethod(params);
};
Expand Down
82 changes: 82 additions & 0 deletions packages/clerk-js/src/core/resources/__tests__/User.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,88 @@ describe('User', () => {
});
});

it('creates an external account with enterprise connection id', async () => {
const externalAccountJSON = {
object: 'external_account',
provider: 'saml_okta',
verification: {
external_verification_redirect_url: 'https://www.example.com',
},
};

// @ts-ignore
BaseResource._fetch = vi.fn().mockReturnValue(Promise.resolve({ response: externalAccountJSON }));

const user = new User({
email_addresses: [],
phone_numbers: [],
web3_wallets: [],
external_accounts: [],
} as unknown as UserJSON);

await user.createExternalAccount({
enterpriseConnectionId: 'ec_123',
redirectUrl: 'https://www.example.com',
});

// @ts-ignore
expect(BaseResource._fetch).toHaveBeenCalledWith({
method: 'POST',
path: '/me/external_accounts',
body: {
strategy: undefined,
redirect_url: 'https://www.example.com',
additional_scope: undefined,
enterprise_connection_id: 'ec_123',
},
});
});

it('fetches enterprise connections', async () => {
const enterpriseConnectionsJSON = [
{
id: 'ec_123',
object: 'enterprise_account_connection',
name: 'Acme Corp SSO',
active: true,
allow_account_linking: true,
domain: 'acme.com',
protocol: 'saml',
provider: 'saml_okta',
logo_public_url: null,
sync_user_attributes: true,
allow_subdomains: false,
allow_idp_initiated: false,
disable_additional_identifications: false,
enterprise_connection_id: 'ec_123',
created_at: 1234567890,
updated_at: 1234567890,
},
];

// @ts-ignore
BaseResource._fetch = vi.fn().mockReturnValue(Promise.resolve({ response: enterpriseConnectionsJSON }));

const user = new User({
email_addresses: [],
phone_numbers: [],
web3_wallets: [],
external_accounts: [],
} as unknown as UserJSON);

const connections = await user.getEnterpriseConnections();

// @ts-ignore
expect(BaseResource._fetch).toHaveBeenCalledWith({
method: 'GET',
path: '/me/enterprise_connections',
});

expect(connections).toHaveLength(1);
expect(connections[0].name).toBe('Acme Corp SSO');
expect(connections[0].allowAccountLinking).toBe(true);
});

it('creates a web3 wallet', async () => {
const targetWeb3Wallet = '0x0000000000000000000000000000000000000000';
const web3WalletJSON = {
Expand Down
11 changes: 11 additions & 0 deletions packages/localizations/src/en-US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1188,6 +1188,15 @@ export const enUS: LocalizationResource = {
successMessage: 'The provider has been added to your account',
title: 'Add connected account',
},
enterpriseAccountPage: {
removeResource: {
messageLine1: '{{identifier}} will be removed from this account.',
messageLine2:
'You will no longer be able to use this enterprise account and any dependent features will no longer work.',
successMessage: '{{enterpriseAccount}} has been removed from your account.',
title: 'Remove enterprise account',
},
},
deletePage: {
actionDescription: 'Type "Delete account" below to continue.',
confirm: 'Delete account',
Expand Down Expand Up @@ -1360,6 +1369,8 @@ export const enUS: LocalizationResource = {
title: 'Email addresses',
},
enterpriseAccountsSection: {
destructiveActionTitle: 'Remove',
primaryButton: 'Connect account',
title: 'Enterprise accounts',
},
headerTitle__account: 'Profile details',
Expand Down
2 changes: 2 additions & 0 deletions packages/shared/src/types/enterpriseAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export interface EnterpriseAccountResource extends ClerkResource {
publicMetadata: Record<string, unknown> | null;
verification: VerificationResource | null;
lastAuthenticatedAt: Date | null;
destroy: () => Promise<void>;
__internal_toSnapshot: () => EnterpriseAccountJSONSnapshot;
}

Expand All @@ -35,6 +36,7 @@ export interface EnterpriseAccountConnectionResource extends ClerkResource {
protocol: EnterpriseProtocol;
provider: EnterpriseProvider;
syncUserAttributes: boolean;
allowAccountLinking: boolean;
enterpriseConnectionId: string | null;
__internal_toSnapshot: () => EnterpriseAccountConnectionJSONSnapshot;
}
1 change: 1 addition & 0 deletions packages/shared/src/types/json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ export interface EnterpriseAccountConnectionJSON extends ClerkResourceJSON {
protocol: EnterpriseProtocol;
provider: EnterpriseProvider;
sync_user_attributes: boolean;
allow_account_linking: boolean;
created_at: number;
updated_at: number;
enterprise_connection_id: string | null;
Expand Down
10 changes: 10 additions & 0 deletions packages/shared/src/types/localization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,8 @@ export type __internal_LocalizationResource = {
};
enterpriseAccountsSection: {
title: LocalizationValue;
primaryButton: LocalizationValue;
destructiveActionTitle: LocalizationValue;
};
passwordSection: {
title: LocalizationValue;
Expand Down Expand Up @@ -819,6 +821,14 @@ export type __internal_LocalizationResource = {
successMessage: LocalizationValue<'connectedAccount'>;
};
};
enterpriseAccountPage: {
removeResource: {
title: LocalizationValue;
messageLine1: LocalizationValue<'identifier'>;
messageLine2: LocalizationValue;
successMessage: LocalizationValue<'enterpriseAccount'>;
};
};
web3WalletPage: {
title: LocalizationValue;
subtitle__availableWallets: LocalizationValue;
Expand Down
6 changes: 4 additions & 2 deletions packages/shared/src/types/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { BackupCodeResource } from './backupCode';
import type { BillingPayerMethods } from './billing';
import type { DeletedObjectResource } from './deletedObject';
import type { EmailAddressResource } from './emailAddress';
import type { EnterpriseAccountResource } from './enterpriseAccount';
import type { EnterpriseAccountConnectionResource, EnterpriseAccountResource } from './enterpriseAccount';
import type { ExternalAccountResource } from './externalAccount';
import type { ImageResource } from './image';
import type { UserJSON } from './json';
Expand Down Expand Up @@ -118,6 +118,7 @@ export interface UserResource extends ClerkResource, BillingPayerMethods {
) => Promise<ClerkPaginatedResponse<OrganizationSuggestionResource>>;
getOrganizationCreationDefaults: () => Promise<OrganizationCreationDefaultsResource>;
leaveOrganization: (organizationId: string) => Promise<DeletedObjectResource>;
getEnterpriseConnections: () => Promise<EnterpriseAccountConnectionResource[]>;
createTOTP: () => Promise<TOTPResource>;
verifyTOTP: (params: VerifyTOTPParams) => Promise<TOTPResource>;
disableTOTP: () => Promise<DeletedObjectResource>;
Expand All @@ -141,7 +142,8 @@ export type CreatePhoneNumberParams = { phoneNumber: string };
export type CreateWeb3WalletParams = { web3Wallet: string };
export type SetProfileImageParams = { file: Blob | File | string | null };
export type CreateExternalAccountParams = {
strategy: OAuthStrategy;
strategy?: OAuthStrategy;
enterpriseConnectionId?: string;
redirectUrl?: string;
additionalScopes?: OAuthScope[];
oidcPrompt?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const ConnectMenuButton = (props: { strategy: OAuthStrategy; onClick?: () => voi
card.setLoading(strategy);
return createExternalAccount()
.then(res => {
if (res && res.verification?.externalVerificationRedirectURL) {
if (res?.verification?.externalVerificationRedirectURL) {
void sleep(2000).then(() => card.setIdle(strategy));
void navigate(res.verification.externalVerificationRedirectURL.href);
}
Expand Down
Loading
Loading