Skip to content

feat(backend): Add sign ups to Backend API client #5625

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 4 commits into from
Apr 16, 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
15 changes: 15 additions & 0 deletions .changeset/smooth-ways-arrive.md
Original file line number Diff line number Diff line change
@@ -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({...});
```
32 changes: 32 additions & 0 deletions packages/backend/src/api/endpoints/SignUpApi.ts
Original file line number Diff line number Diff line change
@@ -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<SignUpAttempt>({
method: 'GET',
path: joinPaths(basePath, signUpAttemptId),
});
}

public async update(params: UpdateSignUpParams) {
const { signUpAttemptId, ...bodyParams } = params;

return this.request<SignUpAttempt>({
method: 'PATCH',
path: joinPaths(basePath, signUpAttemptId),
bodyParams,
});
}
}
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 @@ -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';
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 @@ -18,6 +18,7 @@ import {
SamlConnectionAPI,
SessionAPI,
SignInTokenAPI,
SignUpAPI,
TestingTokenAPI,
UserAPI,
WaitlistEntryAPI,
Expand Down Expand Up @@ -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),
Expand Down
3 changes: 3 additions & 0 deletions packages/backend/src/api/resources/Deserializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
RedirectUrl,
Session,
SignInToken,
SignUpAttempt,
SMSMessage,
Token,
User,
Expand Down Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/api/resources/Enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down
39 changes: 34 additions & 5 deletions packages/backend/src/api/resources/JSON.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { SignUpStatus } from '@clerk/types';

import type {
ActorTokenStatus,
AllowlistIdentifierType,
Expand All @@ -10,7 +12,7 @@ import type {
OrganizationInvitationStatus,
OrganizationMembershipRole,
SignInStatus,
SignUpStatus,
SignUpVerificationNextAction,
WaitlistEntryStatus,
} from './Enums';

Expand Down Expand Up @@ -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<string, unknown> | null;
unsafe_metadata?: Record<string, unknown> | 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 {
Expand Down
87 changes: 87 additions & 0 deletions packages/backend/src/api/resources/SignUpAttempt.ts
Original file line number Diff line number Diff line change
@@ -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<string, unknown> | null,
readonly unsafeMetadata?: Record<string, unknown> | 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,
);
}
}
4 changes: 3 additions & 1 deletion packages/backend/src/api/resources/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ export type {
OrganizationInvitationStatus,
OrganizationMembershipRole,
SignInStatus,
SignUpStatus,
} from './Enums';

export type { SignUpStatus } from '@clerk/types';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this re-export needed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jacekradko In theory, no, but I added it to keep consistency with the code above:

  SignInStatus,
} from './Enums';


export * from './ExternalAccount';
export * from './IdentificationLink';
export * from './Instance';
Expand All @@ -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';
Expand Down
3 changes: 3 additions & 0 deletions packages/backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ export type {
SignInJSON,
SignInTokenJSON,
SignUpJSON,
SignUpVerificationJSON,
SignUpVerificationsJSON,
SMSMessageJSON,
UserJSON,
VerificationJSON,
Expand Down Expand Up @@ -130,6 +132,7 @@ export type {
PhoneNumber,
Session,
SignInToken,
SignUpAttempt,
SMSMessage,
Token,
User,
Expand Down