diff --git a/.changeset/backend-user-replace-email-phone-and-banned-locked.md b/.changeset/backend-user-replace-email-phone-and-banned-locked.md new file mode 100644 index 00000000000..e69f5e50452 --- /dev/null +++ b/.changeset/backend-user-replace-email-phone-and-banned-locked.md @@ -0,0 +1,9 @@ +--- +'@clerk/backend': minor +--- + +Add support for new Backend API user endpoints: + +- `users.replaceUserEmailAddress(userId, { emailAddress })` replaces all of a user's email addresses with a single verified, primary email address (`PUT /users/{user_id}/email_address`). +- `users.replaceUserPhoneNumber(userId, { phoneNumber })` replaces all of a user's phone numbers with a single verified, primary phone number (`PUT /users/{user_id}/phone_number`). +- `users.createUser` now accepts `banned` and `locked` parameters to create a user that is already banned or locked. diff --git a/packages/backend/src/api/endpoints/UserApi.ts b/packages/backend/src/api/endpoints/UserApi.ts index 1124da452ea..58e0ed6c7eb 100644 --- a/packages/backend/src/api/endpoints/UserApi.ts +++ b/packages/backend/src/api/endpoints/UserApi.ts @@ -5,9 +5,11 @@ import { joinPaths } from '../../util/path'; import { deprecated } from '../../util/shared'; import type { DeletedObject, + EmailAddress, OauthAccessToken, OrganizationInvitation, OrganizationMembership, + PhoneNumber, User, } from '../resources'; import type { PaginatedResourceResponse } from '../resources/Deserializer'; @@ -99,6 +101,10 @@ type CreateUserParams = { totpSecret?: string; backupCodes?: string[]; createdAt?: Date; + /** When set to `true`, the user is created already banned and cannot sign in. Requires the same plan support as the ban user endpoint. */ + banned?: boolean; + /** When set to `true`, the user is created already locked. Requires the user lockout feature to be enabled on the instance. */ + locked?: boolean; } & UserMetadataParams & (UserPasswordHashingParams | object); @@ -212,6 +218,16 @@ type SetPasswordCompromisedParams = { revokeAllSessions?: boolean; }; +type ReplaceUserEmailAddressParams = { + /** The new email address. Must adhere to the RFC 5322 specification for email address format. */ + emailAddress: string; +}; + +type ReplaceUserPhoneNumberParams = { + /** The new phone number. Must adhere to the E.164 standard for phone number format. */ + phoneNumber: string; +}; + type UserID = { userId: string; }; @@ -259,6 +275,26 @@ export class UserAPI extends AbstractAPI { }); } + public async replaceUserEmailAddress(userId: string, params: ReplaceUserEmailAddressParams) { + this.requireId(userId); + + return this.request({ + method: 'PUT', + path: joinPaths(basePath, userId, 'email_address'), + bodyParams: params, + }); + } + + public async replaceUserPhoneNumber(userId: string, params: ReplaceUserPhoneNumberParams) { + this.requireId(userId); + + return this.request({ + method: 'PUT', + path: joinPaths(basePath, userId, 'phone_number'), + bodyParams: params, + }); + } + public async updateUserProfileImage(userId: string, params: { file: Blob | File }) { this.requireId(userId);