Skip to content

Commit 00f16e4

Browse files
authored
feat(backend): Add Instance endpoints to Backend API client (#5600)
1 parent bb35660 commit 00f16e4

13 files changed

+280
-0
lines changed

.changeset/heavy-foxes-check.md

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
'@clerk/backend': minor
3+
---
4+
5+
Adds the following functionality for Instances to the Backend API client.
6+
7+
8+
```ts
9+
import { createClerkClient } from '@clerk/backend';
10+
11+
const clerkClient = createClerkClient(...);
12+
13+
await clerkClient.instance.get();
14+
await clerkClient.instance.update({...});
15+
await clerkClient.instance.updateRestrictions({...});
16+
await clerkClient.instance.updateOrganizationSettings({...});
17+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { joinPaths } from '../../util/path';
2+
import type { Instance } from '../resources/Instance';
3+
import type { InstanceRestrictions } from '../resources/InstanceRestrictions';
4+
import type { OrganizationSettings } from '../resources/OrganizationSettings';
5+
import { AbstractAPI } from './AbstractApi';
6+
7+
const basePath = '/instance';
8+
9+
type UpdateParams = {
10+
/**
11+
* Toggles test mode for this instance, allowing the use of test email addresses and phone numbers.
12+
*
13+
* @remarks Defaults to true for development instances.
14+
*/
15+
testMode?: boolean | null | undefined;
16+
/**
17+
* Whether the instance should be using the HIBP service to check passwords for breaches
18+
*/
19+
hibp?: boolean | null | undefined;
20+
/**
21+
* The "enhanced_email_deliverability" feature will send emails from "[email protected]" instead of your domain.
22+
*
23+
* @remarks This can be helpful if you do not have a high domain reputation.
24+
*/
25+
enhancedEmailDeliverability?: boolean | null | undefined;
26+
supportEmail?: string | null | undefined;
27+
clerkJsVersion?: string | null | undefined;
28+
developmentOrigin?: string | null | undefined;
29+
/**
30+
* For browser-like stacks such as browser extensions, Electron, or Capacitor.js the instance allowed origins need to be updated with the request origin value.
31+
*
32+
* @remarks For Chrome extensions popup, background, or service worker pages the origin is chrome-extension://extension_uiid. For Electron apps the default origin is http://localhost:3000. For Capacitor, the origin is capacitor://localhost.
33+
*/
34+
allowedOrigins?: Array<string> | undefined;
35+
/**
36+
* Whether the instance should use URL-based session syncing in development mode (i.e. without third-party cookies).
37+
*/
38+
urlBasedSessionSyncing?: boolean | null | undefined;
39+
};
40+
41+
type UpdateRestrictionsParams = {
42+
allowlist?: boolean | null | undefined;
43+
blocklist?: boolean | null | undefined;
44+
blockEmailSubaddresses?: boolean | null | undefined;
45+
blockDisposableEmailDomains?: boolean | null | undefined;
46+
ignoreDotsForGmailAddresses?: boolean | null | undefined;
47+
};
48+
49+
type UpdateOrganizationSettingsParams = {
50+
enabled?: boolean | null | undefined;
51+
maxAllowedMemberships?: number | null | undefined;
52+
adminDeleteEnabled?: boolean | null | undefined;
53+
domainsEnabled?: boolean | null | undefined;
54+
/**
55+
* Specify which enrollment modes to enable for your Organization Domains.
56+
*
57+
* @remarks Supported modes are 'automatic_invitation' & 'automatic_suggestion'.
58+
*/
59+
domainsEnrollmentModes?: Array<string> | undefined;
60+
/**
61+
* Specify what the default organization role is for an organization creator.
62+
*/
63+
creatorRoleId?: string | null | undefined;
64+
/**
65+
* Specify what the default organization role is for the organization domains.
66+
*/
67+
domainsDefaultRoleId?: string | null | undefined;
68+
};
69+
70+
export class InstanceAPI extends AbstractAPI {
71+
public async get() {
72+
return this.request<Instance>({
73+
method: 'GET',
74+
path: basePath,
75+
});
76+
}
77+
78+
public async update(params: UpdateParams) {
79+
return this.request<void>({
80+
method: 'PATCH',
81+
path: basePath,
82+
bodyParams: params,
83+
});
84+
}
85+
86+
public async updateRestrictions(params: UpdateRestrictionsParams) {
87+
return this.request<InstanceRestrictions>({
88+
method: 'PATCH',
89+
path: joinPaths(basePath, 'restrictions'),
90+
bodyParams: params,
91+
});
92+
}
93+
94+
public async updateOrganizationSettings(params: UpdateOrganizationSettingsParams) {
95+
return this.request<OrganizationSettings>({
96+
method: 'PATCH',
97+
path: joinPaths(basePath, 'organization_settings'),
98+
bodyParams: params,
99+
});
100+
}
101+
}

packages/backend/src/api/endpoints/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export * from './BlocklistIdentifierApi';
66
export * from './ClientApi';
77
export * from './DomainApi';
88
export * from './EmailAddressApi';
9+
export * from './InstanceApi';
910
export * from './InvitationApi';
1011
export * from './JwksApi';
1112
export * from './JwtTemplatesApi';

packages/backend/src/api/factory.ts

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
ClientAPI,
77
DomainAPI,
88
EmailAddressAPI,
9+
InstanceAPI,
910
InvitationAPI,
1011
JwksAPI,
1112
JwtTemplatesApi,
@@ -40,6 +41,7 @@ export function createBackendApiClient(options: CreateBackendApiOptions) {
4041
clients: new ClientAPI(request),
4142
domains: new DomainAPI(request),
4243
emailAddresses: new EmailAddressAPI(request),
44+
instance: new InstanceAPI(request),
4345
invitations: new InvitationAPI(request),
4446
jwks: new JwksAPI(request),
4547
jwtTemplates: new JwtTemplatesApi(request),

packages/backend/src/api/resources/Deserializer.ts

+12
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,17 @@ import {
88
Domain,
99
Email,
1010
EmailAddress,
11+
Instance,
12+
InstanceRestrictions,
13+
InstanceSettings,
1114
Invitation,
1215
JwtTemplate,
1316
OauthAccessToken,
1417
OAuthApplication,
1518
Organization,
1619
OrganizationInvitation,
1720
OrganizationMembership,
21+
OrganizationSettings,
1822
PhoneNumber,
1923
ProxyCheck,
2024
RedirectUrl,
@@ -91,6 +95,12 @@ function jsonToObject(item: any): any {
9195
return EmailAddress.fromJSON(item);
9296
case ObjectType.Email:
9397
return Email.fromJSON(item);
98+
case ObjectType.Instance:
99+
return Instance.fromJSON(item);
100+
case ObjectType.InstanceRestrictions:
101+
return InstanceRestrictions.fromJSON(item);
102+
case ObjectType.InstanceSettings:
103+
return InstanceSettings.fromJSON(item);
94104
case ObjectType.Invitation:
95105
return Invitation.fromJSON(item);
96106
case ObjectType.JwtTemplate:
@@ -105,6 +115,8 @@ function jsonToObject(item: any): any {
105115
return OrganizationInvitation.fromJSON(item);
106116
case ObjectType.OrganizationMembership:
107117
return OrganizationMembership.fromJSON(item);
118+
case ObjectType.OrganizationSettings:
119+
return OrganizationSettings.fromJSON(item);
108120
case ObjectType.PhoneNumber:
109121
return PhoneNumber.fromJSON(item);
110122
case ObjectType.ProxyCheck:

packages/backend/src/api/resources/Enums.ts

+7
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ export type SignUpStatus = 'missing_requirements' | 'complete' | 'abandoned';
3737

3838
export type InvitationStatus = 'pending' | 'accepted' | 'revoked' | 'expired';
3939

40+
export const DomainsEnrollmentModes = {
41+
ManualInvitation: 'manual_invitation',
42+
AutomaticInvitation: 'automatic_invitation',
43+
AutomaticSuggestion: 'automatic_suggestion',
44+
} as const;
45+
export type DomainsEnrollmentModes = (typeof DomainsEnrollmentModes)[keyof typeof DomainsEnrollmentModes];
46+
4047
export const ActorTokenStatus = {
4148
Pending: 'pending',
4249
Accepted: 'accepted',
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import type { InstanceJSON } from './JSON';
2+
3+
export class Instance {
4+
constructor(
5+
readonly id: string,
6+
readonly environmentType: string,
7+
readonly allowedOrigins: Array<string> | null,
8+
) {}
9+
10+
static fromJSON(data: InstanceJSON): Instance {
11+
return new Instance(data.id, data.environment_type, data.allowed_origins);
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import type { InstanceRestrictionsJSON } from './JSON';
2+
3+
export class InstanceRestrictions {
4+
constructor(
5+
readonly allowlist: boolean,
6+
readonly blocklist: boolean,
7+
readonly blockEmailSubaddresses: boolean,
8+
readonly blockDisposableEmailDomains: boolean,
9+
readonly ignoreDotsForGmailAddresses: boolean,
10+
) {}
11+
12+
static fromJSON(data: InstanceRestrictionsJSON): InstanceRestrictions {
13+
return new InstanceRestrictions(
14+
data.allowlist,
15+
data.blocklist,
16+
data.block_email_subaddresses,
17+
data.block_disposable_email_domains,
18+
data.ignore_dots_for_gmail_addresses,
19+
);
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import type { InstanceSettingsJSON } from './JSON';
2+
3+
export class InstanceSettings {
4+
constructor(
5+
readonly id?: string | undefined,
6+
readonly restrictedToAllowlist?: boolean | undefined,
7+
readonly fromEmailAddress?: string | undefined,
8+
readonly progressiveSignUp?: boolean | undefined,
9+
readonly enhancedEmailDeliverability?: boolean | undefined,
10+
) {}
11+
12+
static fromJSON(data: InstanceSettingsJSON): InstanceSettings {
13+
return new InstanceSettings(
14+
data.id,
15+
data.restricted_to_allowlist,
16+
data.from_email_address,
17+
data.progressive_sign_up,
18+
data.enhanced_email_deliverability,
19+
);
20+
}
21+
}

packages/backend/src/api/resources/JSON.ts

+43
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type {
22
ActorTokenStatus,
33
AllowlistIdentifierType,
44
BlocklistIdentifierType,
5+
DomainsEnrollmentModes,
56
InvitationStatus,
67
OrganizationDomainVerificationStatus,
78
OrganizationDomainVerificationStrategy,
@@ -25,6 +26,9 @@ export const ObjectType = {
2526
ExternalAccount: 'external_account',
2627
FacebookAccount: 'facebook_account',
2728
GoogleAccount: 'google_account',
29+
Instance: 'instance',
30+
InstanceRestrictions: 'instance_restrictions',
31+
InstanceSettings: 'instance_settings',
2832
Invitation: 'invitation',
2933
JwtTemplate: 'jwt_template',
3034
OauthAccessToken: 'oauth_access_token',
@@ -33,6 +37,7 @@ export const ObjectType = {
3337
OrganizationDomain: 'organization_domain',
3438
OrganizationInvitation: 'organization_invitation',
3539
OrganizationMembership: 'organization_membership',
40+
OrganizationSettings: 'organization_settings',
3641
PhoneNumber: 'phone_number',
3742
ProxyCheck: 'proxy_check',
3843
RedirectUrl: 'redirect_url',
@@ -224,6 +229,44 @@ export interface IdentificationLinkJSON extends ClerkResourceJSON {
224229
type: string;
225230
}
226231

232+
export interface OrganizationSettingsJSON extends ClerkResourceJSON {
233+
object: typeof ObjectType.OrganizationSettings;
234+
enabled: boolean;
235+
max_allowed_memberships: number;
236+
max_allowed_roles: number;
237+
max_allowed_permissions: number;
238+
creator_role: string;
239+
admin_delete_enabled: boolean;
240+
domains_enabled: boolean;
241+
domains_enrollment_modes: Array<DomainsEnrollmentModes>;
242+
domains_default_role: string;
243+
}
244+
245+
export interface InstanceJSON extends ClerkResourceJSON {
246+
object: typeof ObjectType.Instance;
247+
id: string;
248+
environment_type: string;
249+
allowed_origins: Array<string> | null;
250+
}
251+
252+
export interface InstanceRestrictionsJSON extends ClerkResourceJSON {
253+
object: typeof ObjectType.InstanceRestrictions;
254+
allowlist: boolean;
255+
blocklist: boolean;
256+
block_email_subaddresses: boolean;
257+
block_disposable_email_domains: boolean;
258+
ignore_dots_for_gmail_addresses: boolean;
259+
}
260+
261+
export interface InstanceSettingsJSON extends ClerkResourceJSON {
262+
object: typeof ObjectType.InstanceSettings;
263+
id: string;
264+
restricted_to_allowlist: boolean;
265+
from_email_address: string;
266+
progressive_sign_up: boolean;
267+
enhanced_email_deliverability: boolean;
268+
}
269+
227270
export interface InvitationJSON extends ClerkResourceJSON {
228271
object: typeof ObjectType.Invitation;
229272
email_address: string;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import type { DomainsEnrollmentModes } from './Enums';
2+
import type { OrganizationSettingsJSON } from './JSON';
3+
4+
export class OrganizationSettings {
5+
constructor(
6+
readonly enabled: boolean,
7+
readonly maxAllowedMemberships: number,
8+
readonly maxAllowedRoles: number,
9+
readonly maxAllowedPermissions: number,
10+
readonly creatorRole: string,
11+
readonly adminDeleteEnabled: boolean,
12+
readonly domainsEnabled: boolean,
13+
readonly domainsEnrollmentModes: Array<DomainsEnrollmentModes>,
14+
readonly domainsDefaultRole: string,
15+
) {}
16+
17+
static fromJSON(data: OrganizationSettingsJSON): OrganizationSettings {
18+
return new OrganizationSettings(
19+
data.enabled,
20+
data.max_allowed_memberships,
21+
data.max_allowed_roles,
22+
data.max_allowed_permissions,
23+
data.creator_role,
24+
data.admin_delete_enabled,
25+
data.domains_enabled,
26+
data.domains_enrollment_modes,
27+
data.domains_default_role,
28+
);
29+
}
30+
}

packages/backend/src/api/resources/index.ts

+4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ export type {
2222

2323
export * from './ExternalAccount';
2424
export * from './IdentificationLink';
25+
export * from './Instance';
26+
export * from './InstanceRestrictions';
27+
export * from './InstanceSettings';
2528
export * from './Invitation';
2629
export * from './JSON';
2730
export * from './JwtTemplate';
@@ -30,6 +33,7 @@ export * from './OAuthApplication';
3033
export * from './Organization';
3134
export * from './OrganizationInvitation';
3235
export * from './OrganizationMembership';
36+
export * from './OrganizationSettings';
3337
export * from './PhoneNumber';
3438
export * from './ProxyCheck';
3539
export * from './RedirectUrl';

0 commit comments

Comments
 (0)