diff --git a/.changeset/smooth-ways-arrive.md b/.changeset/smooth-ways-arrive.md new file mode 100644 index 0000000000..ebcbdeb8b0 --- /dev/null +++ b/.changeset/smooth-ways-arrive.md @@ -0,0 +1,15 @@ +--- +'@clerk/backend': minor +--- + +Adds the ability to retrieve and update Sign Up Attempts 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 0000000000..63473a243c --- /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: joinPaths(basePath, signUpAttemptId), + }); + } + + 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 134e1333d6..ca66f27471 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 3a2dcd0555..0dcab1a07d 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 13a80e76c4..45c15e4f45 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 b29c980189..fe67dd4028 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 19387178e5..d6f9fd2303 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'; @@ -466,18 +468,45 @@ export interface SignInTokenJSON extends ClerkResourceJSON { export interface SignUpJSON 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 0000000000..62707b24bb --- /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 { SignUpJSON, 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 | null, + 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: SignUpJSON): 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) : null, + 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 f170e76fa3..e21aa4ade2 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'; @@ -39,6 +40,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 0b9e1d9f6a..eadfe571e5 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -89,6 +89,8 @@ export type { SignInJSON, SignInTokenJSON, SignUpJSON, + SignUpVerificationJSON, + SignUpVerificationsJSON, SMSMessageJSON, UserJSON, VerificationJSON, @@ -130,6 +132,7 @@ export type { PhoneNumber, Session, SignInToken, + SignUpAttempt, SMSMessage, Token, User,