@@ -15,11 +15,14 @@ import type {
15
15
AuthenticateWithWeb3Params ,
16
16
CreateEmailLinkFlowReturn ,
17
17
EmailCodeConfig ,
18
+ EmailCodeFactor ,
18
19
EmailLinkConfig ,
20
+ EmailLinkFactor ,
19
21
EnterpriseSSOConfig ,
20
22
PassKeyConfig ,
21
23
PasskeyFactor ,
22
24
PhoneCodeConfig ,
25
+ PhoneCodeFactor ,
23
26
PrepareFirstFactorParams ,
24
27
PrepareSecondFactorParams ,
25
28
ResetPasswordEmailCodeFactorConfig ,
@@ -544,6 +547,11 @@ export class SignIn extends BaseResource implements SignInResource {
544
547
}
545
548
}
546
549
550
+ type SelectFirstFactorParams =
551
+ | { strategy : 'email_code' ; emailAddressId ?: string ; phoneNumberId ?: never }
552
+ | { strategy : 'email_link' ; emailAddressId ?: string ; phoneNumberId ?: never }
553
+ | { strategy : 'phone_code' ; phoneNumberId ?: string ; emailAddressId ?: never } ;
554
+
547
555
class SignInFuture implements SignInFutureResource {
548
556
emailCode = {
549
557
sendCode : this . sendEmailCode . bind ( this ) ,
@@ -692,22 +700,32 @@ class SignInFuture implements SignInFutureResource {
692
700
} ) ;
693
701
}
694
702
695
- async sendEmailCode ( params : SignInFutureEmailCodeSendParams ) : Promise < { error : unknown } > {
696
- const { email } = params ;
703
+ async sendEmailCode ( params : SignInFutureEmailCodeSendParams = { } ) : Promise < { error : unknown } > {
704
+ const { emailAddress, emailAddressId } = params ;
705
+ if ( ! this . resource . id && emailAddressId ) {
706
+ throw new Error (
707
+ 'signIn.emailCode.sendCode() cannot be called with an emailAddressId if an existing signIn does not exist.' ,
708
+ ) ;
709
+ }
710
+
711
+ if ( ! this . resource . id && ! emailAddress ) {
712
+ throw new Error (
713
+ 'signIn.emailCode.sendCode() cannot be called without an emailAddress if an existing signIn does not exist.' ,
714
+ ) ;
715
+ }
716
+
697
717
return runAsyncResourceTask ( this . resource , async ( ) => {
698
- if ( ! this . resource . id ) {
699
- await this . create ( { identifier : email } ) ;
718
+ if ( emailAddress ) {
719
+ await this . create ( { identifier : emailAddress } ) ;
700
720
}
701
721
702
- const emailCodeFactor = this . resource . supportedFirstFactors ?. find ( f => f . strategy === 'email_code' ) ;
703
-
722
+ const emailCodeFactor = this . selectFirstFactor ( { strategy : 'email_code' , emailAddressId } ) ;
704
723
if ( ! emailCodeFactor ) {
705
724
throw new Error ( 'Email code factor not found' ) ;
706
725
}
707
726
708
- const { emailAddressId } = emailCodeFactor ;
709
727
await this . resource . __internal_basePost ( {
710
- body : { emailAddressId, strategy : 'email_code' } ,
728
+ body : { emailAddressId : emailCodeFactor . emailAddressId , strategy : 'email_code' } ,
711
729
action : 'prepare_first_factor' ,
712
730
} ) ;
713
731
} ) ;
@@ -724,20 +742,29 @@ class SignInFuture implements SignInFutureResource {
724
742
}
725
743
726
744
async sendEmailLink ( params : SignInFutureEmailLinkSendParams ) : Promise < { error : unknown } > {
727
- const { email, verificationUrl } = params ;
745
+ const { emailAddress, verificationUrl, emailAddressId } = params ;
746
+ if ( ! this . resource . id && emailAddressId ) {
747
+ throw new Error (
748
+ 'signIn.emailLink.sendLink() cannot be called with an emailAddressId if an existing signIn does not exist.' ,
749
+ ) ;
750
+ }
751
+
752
+ if ( ! this . resource . id && ! emailAddress ) {
753
+ throw new Error (
754
+ 'signIn.emailLink.sendLink() cannot be called without an emailAddress if an existing signIn does not exist.' ,
755
+ ) ;
756
+ }
757
+
728
758
return runAsyncResourceTask ( this . resource , async ( ) => {
729
- if ( ! this . resource . id ) {
730
- await this . create ( { identifier : email } ) ;
759
+ if ( emailAddress ) {
760
+ await this . create ( { identifier : emailAddress } ) ;
731
761
}
732
762
733
- const emailLinkFactor = this . resource . supportedFirstFactors ?. find ( f => f . strategy === 'email_link' ) ;
734
-
763
+ const emailLinkFactor = this . selectFirstFactor ( { strategy : 'email_link' , emailAddressId } ) ;
735
764
if ( ! emailLinkFactor ) {
736
765
throw new Error ( 'Email link factor not found' ) ;
737
766
}
738
767
739
- const { emailAddressId } = emailLinkFactor ;
740
-
741
768
let absoluteVerificationUrl = verificationUrl ;
742
769
try {
743
770
new URL ( verificationUrl ) ;
@@ -746,7 +773,11 @@ class SignInFuture implements SignInFutureResource {
746
773
}
747
774
748
775
await this . resource . __internal_basePost ( {
749
- body : { emailAddressId, redirectUrl : absoluteVerificationUrl , strategy : 'email_link' } ,
776
+ body : {
777
+ emailAddressId : emailLinkFactor . emailAddressId ,
778
+ redirectUrl : absoluteVerificationUrl ,
779
+ strategy : 'email_link' ,
780
+ } ,
750
781
action : 'prepare_first_factor' ,
751
782
} ) ;
752
783
} ) ;
@@ -773,22 +804,32 @@ class SignInFuture implements SignInFutureResource {
773
804
} ) ;
774
805
}
775
806
776
- async sendPhoneCode ( params : SignInFuturePhoneCodeSendParams ) : Promise < { error : unknown } > {
777
- const { phoneNumber, channel = 'sms' } = params ;
807
+ async sendPhoneCode ( params : SignInFuturePhoneCodeSendParams = { } ) : Promise < { error : unknown } > {
808
+ const { phoneNumber, phoneNumberId, channel = 'sms' } = params ;
809
+ if ( ! this . resource . id && phoneNumberId ) {
810
+ throw new Error (
811
+ 'signIn.phoneCode.sendCode() cannot be called with an phoneNumberId if an existing signIn does not exist.' ,
812
+ ) ;
813
+ }
814
+
815
+ if ( ! this . resource . id && ! phoneNumber ) {
816
+ throw new Error (
817
+ 'signIn.phoneCode.sendCode() cannot be called without an phoneNumber if an existing signIn does not exist.' ,
818
+ ) ;
819
+ }
820
+
778
821
return runAsyncResourceTask ( this . resource , async ( ) => {
779
- if ( ! this . resource . id ) {
822
+ if ( phoneNumber ) {
780
823
await this . create ( { identifier : phoneNumber } ) ;
781
824
}
782
825
783
- const phoneCodeFactor = this . resource . supportedFirstFactors ?. find ( f => f . strategy === 'phone_code' ) ;
784
-
826
+ const phoneCodeFactor = this . selectFirstFactor ( { strategy : 'phone_code' , phoneNumberId } ) ;
785
827
if ( ! phoneCodeFactor ) {
786
828
throw new Error ( 'Phone code factor not found' ) ;
787
829
}
788
830
789
- const { phoneNumberId } = phoneCodeFactor ;
790
831
await this . resource . __internal_basePost ( {
791
- body : { phoneNumberId, strategy : 'phone_code' , channel } ,
832
+ body : { phoneNumberId : phoneCodeFactor . phoneNumberId , strategy : 'phone_code' , channel } ,
792
833
action : 'prepare_first_factor' ,
793
834
} ) ;
794
835
} ) ;
@@ -886,4 +927,60 @@ class SignInFuture implements SignInFutureResource {
886
927
await SignIn . clerk . setActive ( { session : this . resource . createdSessionId , navigate } ) ;
887
928
} ) ;
888
929
}
930
+
931
+ private selectFirstFactor (
932
+ params : Extract < SelectFirstFactorParams , { strategy : 'email_code' } > ,
933
+ ) : EmailCodeFactor | null ;
934
+ private selectFirstFactor (
935
+ params : Extract < SelectFirstFactorParams , { strategy : 'email_link' } > ,
936
+ ) : EmailLinkFactor | null ;
937
+ private selectFirstFactor (
938
+ params : Extract < SelectFirstFactorParams , { strategy : 'phone_code' } > ,
939
+ ) : PhoneCodeFactor | null ;
940
+ private selectFirstFactor ( {
941
+ strategy,
942
+ emailAddressId,
943
+ phoneNumberId,
944
+ } : SelectFirstFactorParams ) : EmailCodeFactor | EmailLinkFactor | PhoneCodeFactor | null {
945
+ if ( ! this . resource . supportedFirstFactors ) {
946
+ return null ;
947
+ }
948
+
949
+ if ( emailAddressId ) {
950
+ const factor = this . resource . supportedFirstFactors . find (
951
+ f => f . strategy === strategy && f . emailAddressId === emailAddressId ,
952
+ ) as EmailCodeFactor | EmailLinkFactor ;
953
+ if ( factor ) {
954
+ return factor ;
955
+ }
956
+ }
957
+
958
+ if ( phoneNumberId ) {
959
+ const factor = this . resource . supportedFirstFactors . find (
960
+ f => f . strategy === strategy && f . phoneNumberId === phoneNumberId ,
961
+ ) as PhoneCodeFactor ;
962
+ if ( factor ) {
963
+ return factor ;
964
+ }
965
+ }
966
+
967
+ // Try to find a factor that matches the identifier.
968
+ const factorForIdentifier = this . resource . supportedFirstFactors . find (
969
+ f => f . strategy === strategy && f . safeIdentifier === this . resource . identifier ,
970
+ ) as EmailCodeFactor | EmailLinkFactor | PhoneCodeFactor ;
971
+ if ( factorForIdentifier ) {
972
+ return factorForIdentifier ;
973
+ }
974
+
975
+ // If no factor is found matching the identifier, try to find a factor that matches the strategy.
976
+ const factorForStrategy = this . resource . supportedFirstFactors . find ( f => f . strategy === strategy ) as
977
+ | EmailCodeFactor
978
+ | EmailLinkFactor
979
+ | PhoneCodeFactor ;
980
+ if ( factorForStrategy ) {
981
+ return factorForStrategy ;
982
+ }
983
+
984
+ return null ;
985
+ }
889
986
}
0 commit comments