Skip to content

feat(backend): Add Instance endpoints to Backend API client #5600

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 15, 2025
Merged
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
17 changes: 17 additions & 0 deletions .changeset/heavy-foxes-check.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
'@clerk/backend': minor
---

Adds the following functionality for Instances to the Backend API client.


```ts
import { createClerkClient } from '@clerk/backend';

const clerkClient = createClerkClient(...);

await clerkClient.instance.get();
await clerkClient.instance.update({...});
await clerkClient.instance.updateRestrictions({...});
await clerkClient.instance.updateOrganizationSettings({...});
```
101 changes: 101 additions & 0 deletions packages/backend/src/api/endpoints/InstanceApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { joinPaths } from '../../util/path';
import type { Instance } from '../resources/Instance';
import type { InstanceRestrictions } from '../resources/InstanceRestrictions';
import type { OrganizationSettings } from '../resources/OrganizationSettings';
import { AbstractAPI } from './AbstractApi';

const basePath = '/instance';

type UpdateParams = {
/**
* Toggles test mode for this instance, allowing the use of test email addresses and phone numbers.
*
* @remarks Defaults to true for development instances.
*/
testMode?: boolean | null | undefined;
/**
* Whether the instance should be using the HIBP service to check passwords for breaches
*/
hibp?: boolean | null | undefined;
/**
* The "enhanced_email_deliverability" feature will send emails from "[email protected]" instead of your domain.
*
* @remarks This can be helpful if you do not have a high domain reputation.
*/
enhancedEmailDeliverability?: boolean | null | undefined;
supportEmail?: string | null | undefined;
clerkJsVersion?: string | null | undefined;
developmentOrigin?: string | null | undefined;
/**
* 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.
*
* @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.
*/
allowedOrigins?: Array<string> | undefined;
/**
* Whether the instance should use URL-based session syncing in development mode (i.e. without third-party cookies).
*/
urlBasedSessionSyncing?: boolean | null | undefined;
};

type UpdateRestrictionsParams = {
allowlist?: boolean | null | undefined;
blocklist?: boolean | null | undefined;
blockEmailSubaddresses?: boolean | null | undefined;
blockDisposableEmailDomains?: boolean | null | undefined;
ignoreDotsForGmailAddresses?: boolean | null | undefined;
};

type UpdateOrganizationSettingsParams = {
enabled?: boolean | null | undefined;
maxAllowedMemberships?: number | null | undefined;
adminDeleteEnabled?: boolean | null | undefined;
domainsEnabled?: boolean | null | undefined;
/**
* Specify which enrollment modes to enable for your Organization Domains.
*
* @remarks Supported modes are 'automatic_invitation' & 'automatic_suggestion'.
*/
domainsEnrollmentModes?: Array<string> | undefined;
/**
* Specify what the default organization role is for an organization creator.
*/
creatorRoleId?: string | null | undefined;
/**
* Specify what the default organization role is for the organization domains.
*/
domainsDefaultRoleId?: string | null | undefined;
};

export class InstanceAPI extends AbstractAPI {
public async get() {
return this.request<Instance>({
method: 'GET',
path: basePath,
});
}

public async update(params: UpdateParams) {
return this.request<void>({
method: 'PATCH',
path: basePath,
bodyParams: params,
});
}

public async updateRestrictions(params: UpdateRestrictionsParams) {
return this.request<InstanceRestrictions>({
method: 'PATCH',
path: joinPaths(basePath, 'restrictions'),
bodyParams: params,
});
}

public async updateOrganizationSettings(params: UpdateOrganizationSettingsParams) {
return this.request<OrganizationSettings>({
method: 'PATCH',
path: joinPaths(basePath, 'organization_settings'),
bodyParams: params,
});
}
}
1 change: 1 addition & 0 deletions packages/backend/src/api/endpoints/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export * from './BlocklistIdentifierApi';
export * from './ClientApi';
export * from './DomainApi';
export * from './EmailAddressApi';
export * from './InstanceApi';
export * from './InvitationApi';
export * from './JwksApi';
export * from './JwtTemplatesApi';
Expand Down
2 changes: 2 additions & 0 deletions packages/backend/src/api/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
ClientAPI,
DomainAPI,
EmailAddressAPI,
InstanceAPI,
InvitationAPI,
JwksAPI,
JwtTemplatesApi,
Expand Down Expand Up @@ -40,6 +41,7 @@ export function createBackendApiClient(options: CreateBackendApiOptions) {
clients: new ClientAPI(request),
domains: new DomainAPI(request),
emailAddresses: new EmailAddressAPI(request),
instance: new InstanceAPI(request),
invitations: new InvitationAPI(request),
jwks: new JwksAPI(request),
jwtTemplates: new JwtTemplatesApi(request),
Expand Down
12 changes: 12 additions & 0 deletions packages/backend/src/api/resources/Deserializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,17 @@ import {
Domain,
Email,
EmailAddress,
Instance,
InstanceRestrictions,
InstanceSettings,
Invitation,
JwtTemplate,
OauthAccessToken,
OAuthApplication,
Organization,
OrganizationInvitation,
OrganizationMembership,
OrganizationSettings,
PhoneNumber,
ProxyCheck,
RedirectUrl,
Expand Down Expand Up @@ -91,6 +95,12 @@ function jsonToObject(item: any): any {
return EmailAddress.fromJSON(item);
case ObjectType.Email:
return Email.fromJSON(item);
case ObjectType.Instance:
return Instance.fromJSON(item);
case ObjectType.InstanceRestrictions:
return InstanceRestrictions.fromJSON(item);
case ObjectType.InstanceSettings:
return InstanceSettings.fromJSON(item);
case ObjectType.Invitation:
return Invitation.fromJSON(item);
case ObjectType.JwtTemplate:
Expand All @@ -105,6 +115,8 @@ function jsonToObject(item: any): any {
return OrganizationInvitation.fromJSON(item);
case ObjectType.OrganizationMembership:
return OrganizationMembership.fromJSON(item);
case ObjectType.OrganizationSettings:
return OrganizationSettings.fromJSON(item);
case ObjectType.PhoneNumber:
return PhoneNumber.fromJSON(item);
case ObjectType.ProxyCheck:
Expand Down
7 changes: 7 additions & 0 deletions packages/backend/src/api/resources/Enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ export type SignUpStatus = 'missing_requirements' | 'complete' | 'abandoned';

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

export const DomainsEnrollmentModes = {
ManualInvitation: 'manual_invitation',
AutomaticInvitation: 'automatic_invitation',
AutomaticSuggestion: 'automatic_suggestion',
} as const;
export type DomainsEnrollmentModes = (typeof DomainsEnrollmentModes)[keyof typeof DomainsEnrollmentModes];

export const ActorTokenStatus = {
Pending: 'pending',
Accepted: 'accepted',
Expand Down
13 changes: 13 additions & 0 deletions packages/backend/src/api/resources/Instance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { InstanceJSON } from './JSON';

export class Instance {
constructor(
readonly id: string,
readonly environmentType: string,
readonly allowedOrigins: Array<string> | null,
) {}

static fromJSON(data: InstanceJSON): Instance {
return new Instance(data.id, data.environment_type, data.allowed_origins);
}
}
21 changes: 21 additions & 0 deletions packages/backend/src/api/resources/InstanceRestrictions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { InstanceRestrictionsJSON } from './JSON';

export class InstanceRestrictions {
constructor(
readonly allowlist: boolean,
readonly blocklist: boolean,
readonly blockEmailSubaddresses: boolean,
readonly blockDisposableEmailDomains: boolean,
readonly ignoreDotsForGmailAddresses: boolean,
) {}

static fromJSON(data: InstanceRestrictionsJSON): InstanceRestrictions {
return new InstanceRestrictions(
data.allowlist,
data.blocklist,
data.block_email_subaddresses,
data.block_disposable_email_domains,
data.ignore_dots_for_gmail_addresses,
);
}
}
21 changes: 21 additions & 0 deletions packages/backend/src/api/resources/InstanceSettings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { InstanceSettingsJSON } from './JSON';

export class InstanceSettings {
constructor(
readonly id?: string | undefined,
readonly restrictedToAllowlist?: boolean | undefined,
readonly fromEmailAddress?: string | undefined,
readonly progressiveSignUp?: boolean | undefined,
readonly enhancedEmailDeliverability?: boolean | undefined,
) {}

static fromJSON(data: InstanceSettingsJSON): InstanceSettings {
return new InstanceSettings(
data.id,
data.restricted_to_allowlist,
data.from_email_address,
data.progressive_sign_up,
data.enhanced_email_deliverability,
);
}
}
43 changes: 43 additions & 0 deletions packages/backend/src/api/resources/JSON.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type {
ActorTokenStatus,
AllowlistIdentifierType,
BlocklistIdentifierType,
DomainsEnrollmentModes,
InvitationStatus,
OrganizationDomainVerificationStatus,
OrganizationDomainVerificationStrategy,
Expand All @@ -25,6 +26,9 @@ export const ObjectType = {
ExternalAccount: 'external_account',
FacebookAccount: 'facebook_account',
GoogleAccount: 'google_account',
Instance: 'instance',
InstanceRestrictions: 'instance_restrictions',
InstanceSettings: 'instance_settings',
Invitation: 'invitation',
JwtTemplate: 'jwt_template',
OauthAccessToken: 'oauth_access_token',
Expand All @@ -33,6 +37,7 @@ export const ObjectType = {
OrganizationDomain: 'organization_domain',
OrganizationInvitation: 'organization_invitation',
OrganizationMembership: 'organization_membership',
OrganizationSettings: 'organization_settings',
PhoneNumber: 'phone_number',
ProxyCheck: 'proxy_check',
RedirectUrl: 'redirect_url',
Expand Down Expand Up @@ -224,6 +229,44 @@ export interface IdentificationLinkJSON extends ClerkResourceJSON {
type: string;
}

export interface OrganizationSettingsJSON extends ClerkResourceJSON {
object: typeof ObjectType.OrganizationSettings;
enabled: boolean;
max_allowed_memberships: number;
max_allowed_roles: number;
max_allowed_permissions: number;
creator_role: string;
admin_delete_enabled: boolean;
domains_enabled: boolean;
domains_enrollment_modes: Array<DomainsEnrollmentModes>;
domains_default_role: string;
}

export interface InstanceJSON extends ClerkResourceJSON {
object: typeof ObjectType.Instance;
id: string;
environment_type: string;
allowed_origins: Array<string> | null;
}

export interface InstanceRestrictionsJSON extends ClerkResourceJSON {
object: typeof ObjectType.InstanceRestrictions;
allowlist: boolean;
blocklist: boolean;
block_email_subaddresses: boolean;
block_disposable_email_domains: boolean;
ignore_dots_for_gmail_addresses: boolean;
}

export interface InstanceSettingsJSON extends ClerkResourceJSON {
object: typeof ObjectType.InstanceSettings;
id: string;
restricted_to_allowlist: boolean;
from_email_address: string;
progressive_sign_up: boolean;
enhanced_email_deliverability: boolean;
}

export interface InvitationJSON extends ClerkResourceJSON {
object: typeof ObjectType.Invitation;
email_address: string;
Expand Down
30 changes: 30 additions & 0 deletions packages/backend/src/api/resources/OrganizationSettings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import type { DomainsEnrollmentModes } from './Enums';
import type { OrganizationSettingsJSON } from './JSON';

export class OrganizationSettings {
constructor(
readonly enabled: boolean,
readonly maxAllowedMemberships: number,
readonly maxAllowedRoles: number,
readonly maxAllowedPermissions: number,
readonly creatorRole: string,
readonly adminDeleteEnabled: boolean,
readonly domainsEnabled: boolean,
readonly domainsEnrollmentModes: Array<DomainsEnrollmentModes>,
readonly domainsDefaultRole: string,
) {}

static fromJSON(data: OrganizationSettingsJSON): OrganizationSettings {
return new OrganizationSettings(
data.enabled,
data.max_allowed_memberships,
data.max_allowed_roles,
data.max_allowed_permissions,
data.creator_role,
data.admin_delete_enabled,
data.domains_enabled,
data.domains_enrollment_modes,
data.domains_default_role,
);
}
}
4 changes: 4 additions & 0 deletions packages/backend/src/api/resources/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ export type {

export * from './ExternalAccount';
export * from './IdentificationLink';
export * from './Instance';
export * from './InstanceRestrictions';
export * from './InstanceSettings';
export * from './Invitation';
export * from './JSON';
export * from './JwtTemplate';
Expand All @@ -30,6 +33,7 @@ export * from './OAuthApplication';
export * from './Organization';
export * from './OrganizationInvitation';
export * from './OrganizationMembership';
export * from './OrganizationSettings';
export * from './PhoneNumber';
export * from './ProxyCheck';
export * from './RedirectUrl';
Expand Down
Loading