Skip to content

Commit 263722e

Browse files
authored
feat(clerk-js,clerk-react,types): Signal MFA support (#6659)
1 parent b298a0c commit 263722e

File tree

4 files changed

+87
-0
lines changed

4 files changed

+87
-0
lines changed

.changeset/twelve-fans-smell.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@clerk/clerk-js': minor
3+
'@clerk/clerk-react': minor
4+
'@clerk/types': minor
5+
---
6+
7+
[Experimental] Signal MFA support

packages/clerk-js/src/core/resources/SignIn.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,19 @@ import type {
2727
SamlConfig,
2828
SignInCreateParams,
2929
SignInFirstFactor,
30+
SignInFutureBackupCodeVerifyParams,
3031
SignInFutureCreateParams,
3132
SignInFutureEmailCodeSendParams,
3233
SignInFutureEmailCodeVerifyParams,
3334
SignInFutureFinalizeParams,
35+
SignInFutureMFAPhoneCodeVerifyParams,
3436
SignInFuturePasswordParams,
3537
SignInFuturePhoneCodeSendParams,
3638
SignInFuturePhoneCodeVerifyParams,
3739
SignInFutureResetPasswordSubmitParams,
3840
SignInFutureResource,
3941
SignInFutureSSOParams,
42+
SignInFutureTOTPVerifyParams,
4043
SignInIdentifier,
4144
SignInJSON,
4245
SignInJSONSnapshot,
@@ -515,6 +518,13 @@ class SignInFuture implements SignInFutureResource {
515518
verifyCode: this.verifyPhoneCode.bind(this),
516519
};
517520

521+
mfa = {
522+
sendPhoneCode: this.sendMFAPhoneCode.bind(this),
523+
verifyPhoneCode: this.verifyMFAPhoneCode.bind(this),
524+
verifyTOTP: this.verifyTOTP.bind(this),
525+
verifyBackupCode: this.verifyBackupCode.bind(this),
526+
};
527+
518528
constructor(readonly resource: SignIn) {}
519529

520530
get status() {
@@ -688,6 +698,52 @@ class SignInFuture implements SignInFutureResource {
688698
});
689699
}
690700

701+
async sendMFAPhoneCode(): Promise<{ error: unknown }> {
702+
return runAsyncResourceTask(this.resource, async () => {
703+
const phoneCodeFactor = this.resource.supportedSecondFactors?.find(f => f.strategy === 'phone_code');
704+
705+
if (!phoneCodeFactor) {
706+
throw new Error('Phone code factor not found');
707+
}
708+
709+
const { phoneNumberId } = phoneCodeFactor;
710+
await this.resource.__internal_basePost({
711+
body: { phoneNumberId, strategy: 'phone_code' },
712+
action: 'prepare_second_factor',
713+
});
714+
});
715+
}
716+
717+
async verifyMFAPhoneCode(params: SignInFutureMFAPhoneCodeVerifyParams): Promise<{ error: unknown }> {
718+
const { code } = params;
719+
return runAsyncResourceTask(this.resource, async () => {
720+
await this.resource.__internal_basePost({
721+
body: { code, strategy: 'phone_code' },
722+
action: 'attempt_second_factor',
723+
});
724+
});
725+
}
726+
727+
async verifyTOTP(params: SignInFutureTOTPVerifyParams): Promise<{ error: unknown }> {
728+
const { code } = params;
729+
return runAsyncResourceTask(this.resource, async () => {
730+
await this.resource.__internal_basePost({
731+
body: { code, strategy: 'totp' },
732+
action: 'attempt_second_factor',
733+
});
734+
});
735+
}
736+
737+
async verifyBackupCode(params: SignInFutureBackupCodeVerifyParams): Promise<{ error: unknown }> {
738+
const { code } = params;
739+
return runAsyncResourceTask(this.resource, async () => {
740+
await this.resource.__internal_basePost({
741+
body: { code, strategy: 'backup_code' },
742+
action: 'attempt_second_factor',
743+
});
744+
});
745+
}
746+
691747
async finalize(params?: SignInFutureFinalizeParams): Promise<{ error: unknown }> {
692748
const { navigate } = params || {};
693749
return runAsyncResourceTask(this.resource, async () => {

packages/react/src/stateProxy.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ export class StateProxy implements State {
5757
'submitPassword',
5858
] as const),
5959
phoneCode: this.wrapMethods(() => target().phoneCode, ['sendCode', 'verifyCode'] as const),
60+
mfa: this.wrapMethods(() => target().mfa, [
61+
'sendPhoneCode',
62+
'verifyPhoneCode',
63+
'verifyTOTP',
64+
'verifyBackupCode',
65+
] as const),
6066
},
6167
};
6268
}

packages/types/src/signInFuture.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,18 @@ export interface SignInFutureSSOParams {
5151
redirectCallbackUrl: string;
5252
}
5353

54+
export interface SignInFutureMFAPhoneCodeVerifyParams {
55+
code: string;
56+
}
57+
58+
export interface SignInFutureTOTPVerifyParams {
59+
code: string;
60+
}
61+
62+
export interface SignInFutureBackupCodeVerifyParams {
63+
code: string;
64+
}
65+
5466
export interface SignInFutureFinalizeParams {
5567
navigate?: SetActiveNavigate;
5668
}
@@ -76,5 +88,11 @@ export interface SignInFutureResource {
7688
submitPassword: (params: SignInFutureResetPasswordSubmitParams) => Promise<{ error: unknown }>;
7789
};
7890
sso: (params: SignInFutureSSOParams) => Promise<{ error: unknown }>;
91+
mfa: {
92+
sendPhoneCode: () => Promise<{ error: unknown }>;
93+
verifyPhoneCode: (params: SignInFutureMFAPhoneCodeVerifyParams) => Promise<{ error: unknown }>;
94+
verifyTOTP: (params: SignInFutureTOTPVerifyParams) => Promise<{ error: unknown }>;
95+
verifyBackupCode: (params: SignInFutureBackupCodeVerifyParams) => Promise<{ error: unknown }>;
96+
};
7997
finalize: (params?: SignInFutureFinalizeParams) => Promise<{ error: unknown }>;
8098
}

0 commit comments

Comments
 (0)