Skip to content
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
7 changes: 7 additions & 0 deletions .changeset/hungry-dogs-stick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@clerk/clerk-js': minor
'@clerk/clerk-react': minor
'@clerk/types': minor
---

[Experimental] Signal phone code support
38 changes: 38 additions & 0 deletions packages/clerk-js/src/core/resources/SignIn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import type {
SignInFutureEmailCodeVerifyParams,
SignInFutureFinalizeParams,
SignInFuturePasswordParams,
SignInFuturePhoneCodeSendParams,
SignInFuturePhoneCodeVerifyParams,
SignInFutureResetPasswordSubmitParams,
SignInFutureResource,
SignInFutureSSOParams,
Expand Down Expand Up @@ -508,6 +510,11 @@ class SignInFuture implements SignInFutureResource {
submitPassword: this.submitResetPassword.bind(this),
};

phoneCode = {
sendCode: this.sendPhoneCode.bind(this),
verifyCode: this.verifyPhoneCode.bind(this),
};

constructor(readonly resource: SignIn) {}

get status() {
Expand Down Expand Up @@ -627,6 +634,37 @@ class SignInFuture implements SignInFutureResource {
});
}

async sendPhoneCode(params: SignInFuturePhoneCodeSendParams): Promise<{ error: unknown }> {
const { phoneNumber, channel = 'sms' } = params;
return runAsyncResourceTask(this.resource, async () => {
if (!this.resource.id) {
await this.create({ identifier: phoneNumber });
}

const phoneCodeFactor = this.resource.supportedFirstFactors?.find(f => f.strategy === 'phone_code');

if (!phoneCodeFactor) {
throw new Error('Phone code factor not found');
}

const { phoneNumberId } = phoneCodeFactor;
await this.resource.__internal_basePost({
body: { phoneNumberId, strategy: 'phone_code', channel },
action: 'prepare_first_factor',
});
});
}

async verifyPhoneCode(params: SignInFuturePhoneCodeVerifyParams): Promise<{ error: unknown }> {
const { code } = params;
return runAsyncResourceTask(this.resource, async () => {
await this.resource.__internal_basePost({
body: { code, strategy: 'phone_code' },
action: 'attempt_first_factor',
});
});
}

async sso(params: SignInFutureSSOParams): Promise<{ error: unknown }> {
const { flow = 'auto', strategy, redirectUrl, redirectCallbackUrl } = params;
return runAsyncResourceTask(this.resource, async () => {
Expand Down
36 changes: 34 additions & 2 deletions packages/clerk-js/src/core/resources/SignUp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ import type {
SignUpFutureEmailCodeVerifyParams,
SignUpFutureFinalizeParams,
SignUpFuturePasswordParams,
SignUpFuturePhoneCodeSendParams,
SignUpFuturePhoneCodeVerifyParams,
SignUpFutureResource,
SignUpFutureSSoParams,
SignUpFutureSSOParams,
SignUpIdentificationField,
SignUpJSON,
SignUpJSONSnapshot,
Expand Down Expand Up @@ -497,6 +499,8 @@ class SignUpFuture implements SignUpFutureResource {
verifications = {
sendEmailCode: this.sendEmailCode.bind(this),
verifyEmailCode: this.verifyEmailCode.bind(this),
sendPhoneCode: this.sendPhoneCode.bind(this),
verifyPhoneCode: this.verifyPhoneCode.bind(this),
};

constructor(readonly resource: SignUp) {}
Expand Down Expand Up @@ -594,7 +598,35 @@ class SignUpFuture implements SignUpFutureResource {
});
}

async sso(params: SignUpFutureSSoParams): Promise<{ error: unknown }> {
async sendPhoneCode(params: SignUpFuturePhoneCodeSendParams): Promise<{ error: unknown }> {
const { phoneNumber, channel = 'sms' } = params;
return runAsyncResourceTask(this.resource, async () => {
if (!this.resource.id) {
const { captchaToken, captchaWidgetType, captchaError } = await this.getCaptchaToken();
await this.resource.__internal_basePost({
path: this.resource.pathRoot,
body: { phoneNumber, captchaToken, captchaWidgetType, captchaError },
});
}

await this.resource.__internal_basePost({
body: { strategy: 'phone_code', channel },
action: 'prepare_verification',
});
});
}

async verifyPhoneCode(params: SignUpFuturePhoneCodeVerifyParams): Promise<{ error: unknown }> {
const { code } = params;
return runAsyncResourceTask(this.resource, async () => {
await this.resource.__internal_basePost({
body: { strategy: 'phone_code', code },
action: 'attempt_verification',
});
});
}

async sso(params: SignUpFutureSSOParams): Promise<{ error: unknown }> {
const { strategy, redirectUrl, redirectCallbackUrl } = params;
return runAsyncResourceTask(this.resource, async () => {
const { captchaToken, captchaWidgetType, captchaError } = await this.getCaptchaToken();
Expand Down
8 changes: 7 additions & 1 deletion packages/react/src/stateProxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export class StateProxy implements State {
'verifyCode',
'submitPassword',
] as const),
phoneCode: this.wrapMethods(() => target().phoneCode, ['sendCode', 'verifyCode'] as const),
},
};
}
Expand All @@ -76,7 +77,12 @@ export class StateProxy implements State {
password: this.gateMethod(target, 'password'),
finalize: this.gateMethod(target, 'finalize'),

verifications: this.wrapMethods(() => target().verifications, ['sendEmailCode', 'verifyEmailCode'] as const),
verifications: this.wrapMethods(() => target().verifications, [
'sendEmailCode',
'verifyEmailCode',
'sendPhoneCode',
'verifyPhoneCode',
] as const),
},
};
}
Expand Down
3 changes: 1 addition & 2 deletions packages/types/src/factors.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { PhoneCodeChannel } from 'phoneCodeChannel';

import type { PublicKeyCredentialWithAuthenticatorAssertionResponse } from './passkey';
import type { PhoneCodeChannel } from './phoneCodeChannel';
import type {
BackupCodeStrategy,
EmailCodeStrategy,
Expand Down
14 changes: 14 additions & 0 deletions packages/types/src/signInFuture.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { SetActiveNavigate } from './clerk';
import type { PhoneCodeChannel } from './phoneCodeChannel';
import type { SignInFirstFactor, SignInStatus } from './signInCommon';
import type { OAuthStrategy } from './strategies';

Expand Down Expand Up @@ -28,6 +29,15 @@ export interface SignInFutureResetPasswordSubmitParams {
signOutOfOtherSessions?: boolean;
}

export interface SignInFuturePhoneCodeSendParams {
phoneNumber?: string;
channel?: PhoneCodeChannel;
}
Comment on lines +32 to +35
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add JSDoc for new public params.

Public types require JSDoc (see guidelines). Document when phoneNumber is required and the default channel.

Apply:

+/**
+ * Parameters for sending a phone verification code during sign-in.
+ * - Provide `phoneNumber` when no sign-in exists yet; otherwise the existing identifier is used.
+ * - `channel` defaults to `'sms'`.
+ */
 export interface SignInFuturePhoneCodeSendParams {
   phoneNumber?: string;
   channel?: PhoneCodeChannel;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export interface SignInFuturePhoneCodeSendParams {
phoneNumber?: string;
channel?: PhoneCodeChannel;
}
/**
* Parameters for sending a phone verification code during sign-in.
* - Provide `phoneNumber` when no sign-in exists yet; otherwise the existing identifier is used.
* - `channel` defaults to `'sms'`.
*/
export interface SignInFuturePhoneCodeSendParams {
phoneNumber?: string;
channel?: PhoneCodeChannel;
}
🤖 Prompt for AI Agents
In packages/types/src/signInFuture.ts around lines 32 to 35, the exported
SignInFuturePhoneCodeSendParams interface is missing JSDoc for this public type;
add a top-level JSDoc comment describing the purpose of the interface and
individual JSDoc comments for each property: document that phoneNumber is
required when sending a phone code (i.e., for phone/SMS delivery) and optional
otherwise, and document the channel property and its default value (e.g.,
default: PhoneCodeChannel.SMS or 'sms' depending on the enum), including any
allowed enum values; ensure comments follow the project JSDoc style and mention
optionality clearly.


export interface SignInFuturePhoneCodeVerifyParams {
code: string;
}
Comment on lines +37 to +39
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add JSDoc for verify params.

Document expected format and length constraints if any.

+/** Parameters for verifying a phone verification code during sign-in. */
 export interface SignInFuturePhoneCodeVerifyParams {
   code: string;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export interface SignInFuturePhoneCodeVerifyParams {
code: string;
}
/** Parameters for verifying a phone verification code during sign-in. */
export interface SignInFuturePhoneCodeVerifyParams {
code: string;
}
🤖 Prompt for AI Agents
In packages/types/src/signInFuture.ts around lines 37 to 39, the
SignInFuturePhoneCodeVerifyParams interface lacks JSDoc; add a concise JSDoc
block above the interface that documents the purpose of the interface, the
expected format of `code` (e.g., numeric or alphanumeric), any length
constraints (min/max length or exact length), whether leading zeros are allowed,
and whether the field is required; keep the comment short and precise so
consumers know validation expectations.


export interface SignInFutureSSOParams {
flow?: 'auto' | 'modal';
strategy: OAuthStrategy | 'saml' | 'enterprise_sso';
Expand Down Expand Up @@ -56,6 +66,10 @@ export interface SignInFutureResource {
sendCode: (params: SignInFutureEmailCodeSendParams) => Promise<{ error: unknown }>;
verifyCode: (params: SignInFutureEmailCodeVerifyParams) => Promise<{ error: unknown }>;
};
phoneCode: {
sendCode: (params: SignInFuturePhoneCodeSendParams) => Promise<{ error: unknown }>;
verifyCode: (params: SignInFuturePhoneCodeVerifyParams) => Promise<{ error: unknown }>;
};
resetPasswordEmailCode: {
sendCode: () => Promise<{ error: unknown }>;
verifyCode: (params: SignInFutureEmailCodeVerifyParams) => Promise<{ error: unknown }>;
Expand Down
16 changes: 14 additions & 2 deletions packages/types/src/signUpFuture.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { SetActiveNavigate } from './clerk';
import type { PhoneCodeChannel } from './phoneCodeChannel';
import type { SignUpIdentificationField, SignUpStatus } from './signUpCommon';

export interface SignUpFutureCreateParams {
Expand All @@ -14,7 +15,16 @@ export interface SignUpFuturePasswordParams {
password: string;
}

export interface SignUpFutureSSoParams {
export interface SignUpFuturePhoneCodeSendParams {
phoneNumber?: string;
channel?: PhoneCodeChannel;
}
Comment on lines +18 to +21
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add JSDoc for new public params.

Clarify when phoneNumber is required and default channel.

+/**
+ * Parameters for sending a phone verification code during sign-up.
+ * - If a sign-up has not been created yet, `phoneNumber` is required.
+ * - `channel` defaults to `'sms'` if omitted.
+ */
 export interface SignUpFuturePhoneCodeSendParams {
   phoneNumber?: string;
   channel?: PhoneCodeChannel;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export interface SignUpFuturePhoneCodeSendParams {
phoneNumber?: string;
channel?: PhoneCodeChannel;
}
/**
* Parameters for sending a phone verification code during sign-up.
* - If a sign-up has not been created yet, `phoneNumber` is required.
* - `channel` defaults to `'sms'` if omitted.
*/
export interface SignUpFuturePhoneCodeSendParams {
phoneNumber?: string;
channel?: PhoneCodeChannel;
}
🤖 Prompt for AI Agents
In packages/types/src/signUpFuture.ts around lines 18 to 21, the new public
interface SignUpFuturePhoneCodeSendParams lacks JSDoc; add a JSDoc block for the
interface and for each field documenting that phoneNumber is required when
sending via SMS (or when the operation targets a phone) and optional otherwise,
describe the channel field and state its default (e.g., "sms") and allowed
values (PhoneCodeChannel), and include examples or notes about expected formats
(E.164) if applicable.


export interface SignUpFuturePhoneCodeVerifyParams {
code: string;
}
Comment on lines +23 to +25
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add JSDoc for verify params.

Minimal doc for public type.

+/** Parameters for verifying a phone verification code during sign-up. */
 export interface SignUpFuturePhoneCodeVerifyParams {
   code: string;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export interface SignUpFuturePhoneCodeVerifyParams {
code: string;
}
/** Parameters for verifying a phone verification code during sign-up. */
export interface SignUpFuturePhoneCodeVerifyParams {
code: string;
}
🤖 Prompt for AI Agents
In packages/types/src/signUpFuture.ts around lines 23 to 25, the exported
SignUpFuturePhoneCodeVerifyParams interface lacks JSDoc; add a concise JSDoc
block above the interface describing its purpose and the code property (e.g.,
that it represents parameters for verifying a phone verification code and that
code is the one-time numeric/alpha code to verify), include type and any
constraints if applicable.


export interface SignUpFutureSSOParams {
strategy: string;
/**
* The URL to redirect to after the user has completed the SSO flow.
Expand All @@ -39,8 +49,10 @@ export interface SignUpFutureResource {
verifications: {
sendEmailCode: () => Promise<{ error: unknown }>;
verifyEmailCode: (params: SignUpFutureEmailCodeVerifyParams) => Promise<{ error: unknown }>;
sendPhoneCode: (params: SignUpFuturePhoneCodeSendParams) => Promise<{ error: unknown }>;
verifyPhoneCode: (params: SignUpFuturePhoneCodeVerifyParams) => Promise<{ error: unknown }>;
};
password: (params: SignUpFuturePasswordParams) => Promise<{ error: unknown }>;
sso: (params: SignUpFutureSSoParams) => Promise<{ error: unknown }>;
sso: (params: SignUpFutureSSOParams) => Promise<{ error: unknown }>;
finalize: (params?: SignUpFutureFinalizeParams) => Promise<{ error: unknown }>;
}
Loading