From b18dd859d056d0eec1d3604fd92b7ad3deebc0e3 Mon Sep 17 00:00:00 2001 From: Tom Milewski Date: Mon, 14 Apr 2025 15:43:58 -0400 Subject: [PATCH 1/4] feat(backend): Add sign ups to Backend API client --- .changeset/smooth-ways-arrive.md | 15 ++++ .../backend/src/api/endpoints/SignUpApi.ts | 32 +++++++ packages/backend/src/api/endpoints/index.ts | 1 + packages/backend/src/api/factory.ts | 2 + .../backend/src/api/resources/Deserializer.ts | 3 + packages/backend/src/api/resources/Enums.ts | 2 +- packages/backend/src/api/resources/JSON.ts | 41 +++++++-- .../src/api/resources/SignUpAttempt.ts | 87 +++++++++++++++++++ packages/backend/src/api/resources/index.ts | 1 + packages/backend/src/index.ts | 5 +- 10 files changed, 181 insertions(+), 8 deletions(-) create mode 100644 .changeset/smooth-ways-arrive.md create mode 100644 packages/backend/src/api/endpoints/SignUpApi.ts create mode 100644 packages/backend/src/api/resources/SignUpAttempt.ts diff --git a/.changeset/smooth-ways-arrive.md b/.changeset/smooth-ways-arrive.md new file mode 100644 index 00000000000..dc4520555c6 --- /dev/null +++ b/.changeset/smooth-ways-arrive.md @@ -0,0 +1,15 @@ +--- +'@clerk/backend': minor +--- + +Adds the ability to retrieve and update Sign Up Attemps to the Backend API client. + + +```ts + import { createClerkClient } from '@clerk/backend'; + + const clerkClient = createClerkClient(...); + + await clerkClient.signUps.get('signUpAttemptId'); + await clerkClient.signUps.update({...}); +``` \ No newline at end of file diff --git a/packages/backend/src/api/endpoints/SignUpApi.ts b/packages/backend/src/api/endpoints/SignUpApi.ts new file mode 100644 index 00000000000..5a4695f66df --- /dev/null +++ b/packages/backend/src/api/endpoints/SignUpApi.ts @@ -0,0 +1,32 @@ +import { joinPaths } from '../../util/path'; +import type { SignUpAttempt } from '../resources/SignUpAttempt'; +import { AbstractAPI } from './AbstractApi'; + +type UpdateSignUpParams = { + signUpAttemptId: string; + externalId?: string | null; + customAction?: boolean | null; +}; + +const basePath = '/sign_ups'; + +export class SignUpAPI extends AbstractAPI { + public async get(signUpAttemptId: string) { + this.requireId(signUpAttemptId); + + return this.request({ + method: 'GET', + path: basePath, + }); + } + + public async update(params: UpdateSignUpParams) { + const { signUpAttemptId, ...bodyParams } = params; + + return this.request({ + method: 'PATCH', + path: joinPaths(basePath, signUpAttemptId), + bodyParams, + }); + } +} diff --git a/packages/backend/src/api/endpoints/index.ts b/packages/backend/src/api/endpoints/index.ts index 134e1333d64..ca66f27471a 100644 --- a/packages/backend/src/api/endpoints/index.ts +++ b/packages/backend/src/api/endpoints/index.ts @@ -18,6 +18,7 @@ export * from './RedirectUrlApi'; export * from './SamlConnectionApi'; export * from './SessionApi'; export * from './SignInTokenApi'; +export * from './SignUpApi'; export * from './TestingTokenApi'; export * from './UserApi'; export * from './WaitlistEntryApi'; diff --git a/packages/backend/src/api/factory.ts b/packages/backend/src/api/factory.ts index 3a2dcd05554..0dcab1a07d2 100644 --- a/packages/backend/src/api/factory.ts +++ b/packages/backend/src/api/factory.ts @@ -18,6 +18,7 @@ import { SamlConnectionAPI, SessionAPI, SignInTokenAPI, + SignUpAPI, TestingTokenAPI, UserAPI, WaitlistEntryAPI, @@ -54,6 +55,7 @@ export function createBackendApiClient(options: CreateBackendApiOptions) { samlConnections: new SamlConnectionAPI(request), sessions: new SessionAPI(request), signInTokens: new SignInTokenAPI(request), + signUps: new SignUpAPI(request), testingTokens: new TestingTokenAPI(request), users: new UserAPI(request), waitlistEntries: new WaitlistEntryAPI(request), diff --git a/packages/backend/src/api/resources/Deserializer.ts b/packages/backend/src/api/resources/Deserializer.ts index 13a80e76c4c..45c15e4f45d 100644 --- a/packages/backend/src/api/resources/Deserializer.ts +++ b/packages/backend/src/api/resources/Deserializer.ts @@ -24,6 +24,7 @@ import { RedirectUrl, Session, SignInToken, + SignUpAttempt, SMSMessage, Token, User, @@ -126,6 +127,8 @@ function jsonToObject(item: any): any { return RedirectUrl.fromJSON(item); case ObjectType.SignInToken: return SignInToken.fromJSON(item); + case ObjectType.SignUpAttempt: + return SignUpAttempt.fromJSON(item); case ObjectType.Session: return Session.fromJSON(item); case ObjectType.SmsMessage: diff --git a/packages/backend/src/api/resources/Enums.ts b/packages/backend/src/api/resources/Enums.ts index b29c9801891..fe67dd4028d 100644 --- a/packages/backend/src/api/resources/Enums.ts +++ b/packages/backend/src/api/resources/Enums.ts @@ -33,7 +33,7 @@ export type OrganizationMembershipRole = OrganizationCustomRoleKey; export type SignInStatus = 'needs_identifier' | 'needs_factor_one' | 'needs_factor_two' | 'complete'; -export type SignUpStatus = 'missing_requirements' | 'complete' | 'abandoned'; +export type SignUpVerificationNextAction = 'needs_prepare' | 'needs_attempt' | ''; export type InvitationStatus = 'pending' | 'accepted' | 'revoked' | 'expired'; diff --git a/packages/backend/src/api/resources/JSON.ts b/packages/backend/src/api/resources/JSON.ts index 19387178e54..01dda2c1a4a 100644 --- a/packages/backend/src/api/resources/JSON.ts +++ b/packages/backend/src/api/resources/JSON.ts @@ -1,3 +1,5 @@ +import type { SignUpStatus } from '@clerk/types'; + import type { ActorTokenStatus, AllowlistIdentifierType, @@ -10,7 +12,7 @@ import type { OrganizationInvitationStatus, OrganizationMembershipRole, SignInStatus, - SignUpStatus, + SignUpVerificationNextAction, WaitlistEntryStatus, } from './Enums'; @@ -464,20 +466,47 @@ export interface SignInTokenJSON extends ClerkResourceJSON { updated_at: number; } -export interface SignUpJSON extends ClerkResourceJSON { +export interface SignUpAttemptJSON extends ClerkResourceJSON { object: typeof ObjectType.SignUpAttempt; + id: string; status: SignUpStatus; + required_fields: string[]; + optional_fields: string[]; + missing_fields: string[]; + unverified_fields: string[]; + verifications: SignUpVerificationsJSON; username: string | null; email_address: string | null; phone_number: string | null; web3_wallet: string | null; - web3_wallet_verification: VerificationJSON | null; - external_account: any; - has_password: boolean; - name_full: string | null; + password_enabled: boolean; + first_name: string | null; + last_name: string | null; + public_metadata?: Record | null; + unsafe_metadata?: Record | null; + custom_action: boolean; + external_id: string | null; created_session_id: string | null; created_user_id: string | null; abandon_at: number | null; + legal_accepted_at: number | null; + + /** + * @deprecated Please use `verifications.external_account` instead + */ + external_account: object | null; +} + +export interface SignUpVerificationsJSON { + email_address: SignUpVerificationJSON; + phone_number: SignUpVerificationJSON; + web3_wallet: SignUpVerificationJSON; + external_account: VerificationJSON; +} + +export interface SignUpVerificationJSON { + next_action: SignUpVerificationNextAction; + supported_strategies: string[]; } export interface SMSMessageJSON extends ClerkResourceJSON { diff --git a/packages/backend/src/api/resources/SignUpAttempt.ts b/packages/backend/src/api/resources/SignUpAttempt.ts new file mode 100644 index 00000000000..8140d98b318 --- /dev/null +++ b/packages/backend/src/api/resources/SignUpAttempt.ts @@ -0,0 +1,87 @@ +import type { SignUpStatus } from '@clerk/types'; + +import type { SignUpVerificationNextAction } from './Enums'; +import type { SignUpAttemptJSON, SignUpVerificationJSON, SignUpVerificationsJSON } from './JSON'; + +export class SignUpAttemptVerification { + constructor( + readonly nextAction: SignUpVerificationNextAction, + readonly supportedStrategies: string[], + ) {} + + static fromJSON(data: SignUpVerificationJSON): SignUpAttemptVerification { + return new SignUpAttemptVerification(data.next_action, data.supported_strategies); + } +} + +export class SignUpAttemptVerifications { + constructor( + readonly emailAddress: SignUpAttemptVerification | null, + readonly phoneNumber: SignUpAttemptVerification | null, + readonly web3Wallet: SignUpAttemptVerification | null, + readonly externalAccount: object | null, + ) {} + + static fromJSON(data: SignUpVerificationsJSON): SignUpAttemptVerifications { + return new SignUpAttemptVerifications( + data.email_address && SignUpAttemptVerification.fromJSON(data.email_address), + data.phone_number && SignUpAttemptVerification.fromJSON(data.phone_number), + data.web3_wallet && SignUpAttemptVerification.fromJSON(data.web3_wallet), + data.external_account, + ); + } +} + +export class SignUpAttempt { + constructor( + readonly id: string, + readonly status: SignUpStatus, + readonly requiredFields: string[], + readonly optionalFields: string[], + readonly missingFields: string[], + readonly unverifiedFields: string[], + readonly verifications: SignUpAttemptVerifications | object, + readonly username: string | null, + readonly emailAddress: string | null, + readonly phoneNumber: string | null, + readonly web3Wallet: string | null, + readonly passwordEnabled: boolean, + readonly firstName: string | null, + readonly lastName: string | null, + readonly customAction: boolean, + readonly externalId: string | null, + readonly createdSessionId: string | null, + readonly createdUserId: string | null, + readonly abandonAt: number | null, + readonly legalAcceptedAt: number | null, + readonly publicMetadata?: Record | null, + readonly unsafeMetadata?: Record | null, + ) {} + + static fromJSON(data: SignUpAttemptJSON): SignUpAttempt { + return new SignUpAttempt( + data.id, + data.status, + data.required_fields, + data.optional_fields, + data.missing_fields, + data.unverified_fields, + data.verifications ? SignUpAttemptVerifications.fromJSON(data.verifications) : {}, + data.username, + data.email_address, + data.phone_number, + data.web3_wallet, + data.password_enabled, + data.first_name, + data.last_name, + data.custom_action, + data.external_id, + data.created_session_id, + data.created_user_id, + data.abandon_at, + data.legal_accepted_at, + data.public_metadata, + data.unsafe_metadata, + ); + } +} diff --git a/packages/backend/src/api/resources/index.ts b/packages/backend/src/api/resources/index.ts index f170e76fa36..328fdc32eaa 100644 --- a/packages/backend/src/api/resources/index.ts +++ b/packages/backend/src/api/resources/index.ts @@ -39,6 +39,7 @@ export * from './ProxyCheck'; export * from './RedirectUrl'; export * from './Session'; export * from './SignInTokens'; +export * from './SignUpAttempt'; export * from './SMSMessage'; export * from './Token'; export * from './User'; diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index 0b9e1d9f6a9..678834e609a 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -88,7 +88,9 @@ export type { SessionJSON, SignInJSON, SignInTokenJSON, - SignUpJSON, + SignUpAttemptJSON, + SignUpVerificationJSON, + SignUpVerificationsJSON, SMSMessageJSON, UserJSON, VerificationJSON, @@ -130,6 +132,7 @@ export type { PhoneNumber, Session, SignInToken, + SignUpAttempt, SMSMessage, Token, User, From 80d3320bf91846a06a9178aec27aabeb14921d2c Mon Sep 17 00:00:00 2001 From: Tom Milewski Date: Mon, 14 Apr 2025 18:15:10 -0400 Subject: [PATCH 2/4] fix: Update export --- packages/backend/src/api/resources/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/api/resources/index.ts b/packages/backend/src/api/resources/index.ts index 328fdc32eaa..e21aa4ade25 100644 --- a/packages/backend/src/api/resources/index.ts +++ b/packages/backend/src/api/resources/index.ts @@ -17,9 +17,10 @@ export type { OrganizationInvitationStatus, OrganizationMembershipRole, SignInStatus, - SignUpStatus, } from './Enums'; +export type { SignUpStatus } from '@clerk/types'; + export * from './ExternalAccount'; export * from './IdentificationLink'; export * from './Instance'; From 12679ff3b0f56f3ae8cf3b720d566378220a12da Mon Sep 17 00:00:00 2001 From: Tom Milewski Date: Tue, 15 Apr 2025 10:19:13 -0400 Subject: [PATCH 3/4] fix: Review updates --- .changeset/smooth-ways-arrive.md | 2 +- packages/backend/src/api/endpoints/SignUpApi.ts | 2 +- packages/backend/src/api/resources/SignUpAttempt.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.changeset/smooth-ways-arrive.md b/.changeset/smooth-ways-arrive.md index dc4520555c6..ebcbdeb8b09 100644 --- a/.changeset/smooth-ways-arrive.md +++ b/.changeset/smooth-ways-arrive.md @@ -2,7 +2,7 @@ '@clerk/backend': minor --- -Adds the ability to retrieve and update Sign Up Attemps to the Backend API client. +Adds the ability to retrieve and update Sign Up Attempts to the Backend API client. ```ts diff --git a/packages/backend/src/api/endpoints/SignUpApi.ts b/packages/backend/src/api/endpoints/SignUpApi.ts index 5a4695f66df..63473a243cb 100644 --- a/packages/backend/src/api/endpoints/SignUpApi.ts +++ b/packages/backend/src/api/endpoints/SignUpApi.ts @@ -16,7 +16,7 @@ export class SignUpAPI extends AbstractAPI { return this.request({ method: 'GET', - path: basePath, + path: joinPaths(basePath, signUpAttemptId), }); } diff --git a/packages/backend/src/api/resources/SignUpAttempt.ts b/packages/backend/src/api/resources/SignUpAttempt.ts index 8140d98b318..784e0f9bddc 100644 --- a/packages/backend/src/api/resources/SignUpAttempt.ts +++ b/packages/backend/src/api/resources/SignUpAttempt.ts @@ -40,7 +40,7 @@ export class SignUpAttempt { readonly optionalFields: string[], readonly missingFields: string[], readonly unverifiedFields: string[], - readonly verifications: SignUpAttemptVerifications | object, + readonly verifications: SignUpAttemptVerifications | null, readonly username: string | null, readonly emailAddress: string | null, readonly phoneNumber: string | null, @@ -66,7 +66,7 @@ export class SignUpAttempt { data.optional_fields, data.missing_fields, data.unverified_fields, - data.verifications ? SignUpAttemptVerifications.fromJSON(data.verifications) : {}, + data.verifications ? SignUpAttemptVerifications.fromJSON(data.verifications) : null, data.username, data.email_address, data.phone_number, From f408a7a05a9f55cb3678cbcebd2646eaf6e093c9 Mon Sep 17 00:00:00 2001 From: Tom Milewski Date: Tue, 15 Apr 2025 13:56:54 -0400 Subject: [PATCH 4/4] fix: Revert potentially breaking interface name change --- packages/backend/src/api/resources/JSON.ts | 2 +- packages/backend/src/api/resources/SignUpAttempt.ts | 4 ++-- packages/backend/src/index.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/backend/src/api/resources/JSON.ts b/packages/backend/src/api/resources/JSON.ts index 01dda2c1a4a..d6f9fd23032 100644 --- a/packages/backend/src/api/resources/JSON.ts +++ b/packages/backend/src/api/resources/JSON.ts @@ -466,7 +466,7 @@ export interface SignInTokenJSON extends ClerkResourceJSON { updated_at: number; } -export interface SignUpAttemptJSON extends ClerkResourceJSON { +export interface SignUpJSON extends ClerkResourceJSON { object: typeof ObjectType.SignUpAttempt; id: string; status: SignUpStatus; diff --git a/packages/backend/src/api/resources/SignUpAttempt.ts b/packages/backend/src/api/resources/SignUpAttempt.ts index 784e0f9bddc..62707b24bb0 100644 --- a/packages/backend/src/api/resources/SignUpAttempt.ts +++ b/packages/backend/src/api/resources/SignUpAttempt.ts @@ -1,7 +1,7 @@ import type { SignUpStatus } from '@clerk/types'; import type { SignUpVerificationNextAction } from './Enums'; -import type { SignUpAttemptJSON, SignUpVerificationJSON, SignUpVerificationsJSON } from './JSON'; +import type { SignUpJSON, SignUpVerificationJSON, SignUpVerificationsJSON } from './JSON'; export class SignUpAttemptVerification { constructor( @@ -58,7 +58,7 @@ export class SignUpAttempt { readonly unsafeMetadata?: Record | null, ) {} - static fromJSON(data: SignUpAttemptJSON): SignUpAttempt { + static fromJSON(data: SignUpJSON): SignUpAttempt { return new SignUpAttempt( data.id, data.status, diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index 678834e609a..eadfe571e51 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -88,7 +88,7 @@ export type { SessionJSON, SignInJSON, SignInTokenJSON, - SignUpAttemptJSON, + SignUpJSON, SignUpVerificationJSON, SignUpVerificationsJSON, SMSMessageJSON,