@@ -16,6 +16,7 @@ import type {
16
16
EmailCodeConfig ,
17
17
EmailLinkConfig ,
18
18
EnterpriseSSOConfig ,
19
+ OAuthStrategy ,
19
20
PassKeyConfig ,
20
21
PasskeyFactor ,
21
22
PhoneCodeConfig ,
@@ -27,6 +28,7 @@ import type {
27
28
SamlConfig ,
28
29
SignInCreateParams ,
29
30
SignInFirstFactor ,
31
+ SignInFutureResource ,
30
32
SignInIdentifier ,
31
33
SignInJSON ,
32
34
SignInJSONSnapshot ,
@@ -66,6 +68,7 @@ import {
66
68
clerkVerifyPasskeyCalledBeforeCreate ,
67
69
clerkVerifyWeb3WalletCalledBeforeCreate ,
68
70
} from '../errors' ;
71
+ import { eventBus } from '../events' ;
69
72
import { BaseResource , UserData , Verification } from './internal' ;
70
73
71
74
export class SignIn extends BaseResource implements SignInResource {
@@ -82,6 +85,21 @@ export class SignIn extends BaseResource implements SignInResource {
82
85
createdSessionId : string | null = null ;
83
86
userData : UserData = new UserData ( null ) ;
84
87
88
+ /**
89
+ * @experimental This experimental API is subject to change.
90
+ *
91
+ * An instance of `SignInFuture`, which has a different API than `SignIn`, intended to be used in custom flows.
92
+ */
93
+ __internal_future : SignInFuture | null = new SignInFuture ( this ) ;
94
+
95
+ /**
96
+ * @internal Only used for internal purposes, and is not intended to be used directly.
97
+ *
98
+ * This property is used to provide access to underlying Client methods to `SignInFuture`, which wraps an instance
99
+ * of `SignIn`.
100
+ */
101
+ __internal_basePost = this . _basePost . bind ( this ) ;
102
+
85
103
constructor ( data : SignInJSON | SignInJSONSnapshot | null = null ) {
86
104
super ( ) ;
87
105
this . fromJSON ( data ) ;
@@ -451,6 +469,8 @@ export class SignIn extends BaseResource implements SignInResource {
451
469
this . createdSessionId = data . created_session_id ;
452
470
this . userData = new UserData ( data . user_data ) ;
453
471
}
472
+
473
+ eventBus . emit ( 'resource:update' , { resource : this } ) ;
454
474
return this ;
455
475
}
456
476
@@ -470,3 +490,132 @@ export class SignIn extends BaseResource implements SignInResource {
470
490
} ;
471
491
}
472
492
}
493
+
494
+ class SignInFuture implements SignInFutureResource {
495
+ emailCode = {
496
+ sendCode : this . sendEmailCode . bind ( this ) ,
497
+ verifyCode : this . verifyEmailCode . bind ( this ) ,
498
+ } ;
499
+
500
+ constructor ( readonly resource : SignIn ) { }
501
+
502
+ get status ( ) {
503
+ return this . resource . status ;
504
+ }
505
+
506
+ async create ( params : {
507
+ identifier ?: string ;
508
+ strategy ?: OAuthStrategy | 'saml' | 'enterprise_sso' ;
509
+ redirectUrl ?: string ;
510
+ actionCompleteRedirectUrl ?: string ;
511
+ } ) : Promise < { error : unknown } > {
512
+ eventBus . emit ( 'resource:error' , { resource : this . resource , error : null } ) ;
513
+ try {
514
+ await this . resource . __internal_basePost ( {
515
+ path : this . resource . pathRoot ,
516
+ body : params ,
517
+ } ) ;
518
+
519
+ return { error : null } ;
520
+ } catch ( err ) {
521
+ eventBus . emit ( 'resource:error' , { resource : this . resource , error : err } ) ;
522
+ return { error : err } ;
523
+ }
524
+ }
525
+
526
+ async password ( { identifier, password } : { identifier : string ; password : string } ) : Promise < { error : unknown } > {
527
+ eventBus . emit ( 'resource:error' , { resource : this . resource , error : null } ) ;
528
+ try {
529
+ await this . resource . __internal_basePost ( {
530
+ path : this . resource . pathRoot ,
531
+ body : { identifier, password } ,
532
+ } ) ;
533
+ } catch ( err ) {
534
+ eventBus . emit ( 'resource:error' , { resource : this . resource , error : err } ) ;
535
+ return { error : err } ;
536
+ }
537
+
538
+ return { error : null } ;
539
+ }
540
+
541
+ async sendEmailCode ( { email } : { email : string } ) : Promise < { error : unknown } > {
542
+ eventBus . emit ( 'resource:error' , { resource : this . resource , error : null } ) ;
543
+ try {
544
+ if ( ! this . resource . id ) {
545
+ await this . create ( { identifier : email } ) ;
546
+ }
547
+
548
+ const emailCodeFactor = this . resource . supportedFirstFactors ?. find ( f => f . strategy === 'email_code' ) ;
549
+
550
+ if ( ! emailCodeFactor ) {
551
+ throw new Error ( 'Email code factor not found' ) ;
552
+ }
553
+
554
+ const { emailAddressId } = emailCodeFactor ;
555
+ await this . resource . __internal_basePost ( {
556
+ body : { emailAddressId, strategy : 'email_code' } ,
557
+ action : 'prepare_first_factor' ,
558
+ } ) ;
559
+ } catch ( err : unknown ) {
560
+ eventBus . emit ( 'resource:error' , { resource : this . resource , error : err } ) ;
561
+ return { error : err } ;
562
+ }
563
+
564
+ return { error : null } ;
565
+ }
566
+
567
+ async verifyEmailCode ( { code } : { code : string } ) : Promise < { error : unknown } > {
568
+ eventBus . emit ( 'resource:error' , { resource : this . resource , error : null } ) ;
569
+ try {
570
+ await this . resource . __internal_basePost ( {
571
+ body : { code, strategy : 'email_code' } ,
572
+ action : 'attempt_first_factor' ,
573
+ } ) ;
574
+ } catch ( err : unknown ) {
575
+ eventBus . emit ( 'resource:error' , { resource : this . resource , error : err } ) ;
576
+ return { error : err } ;
577
+ }
578
+
579
+ return { error : null } ;
580
+ }
581
+
582
+ async sso ( {
583
+ flow = 'auto' ,
584
+ strategy,
585
+ redirectUrl,
586
+ redirectUrlComplete,
587
+ } : {
588
+ flow ?: 'auto' | 'modal' ;
589
+ strategy : OAuthStrategy | 'saml' | 'enterprise_sso' ;
590
+ redirectUrl : string ;
591
+ redirectUrlComplete : string ;
592
+ } ) : Promise < { error : unknown } > {
593
+ eventBus . emit ( 'resource:error' , { resource : this . resource , error : null } ) ;
594
+ try {
595
+ if ( flow !== 'auto' ) {
596
+ throw new Error ( 'modal flow is not supported yet' ) ;
597
+ }
598
+
599
+ const redirectUrlWithAuthToken = SignIn . clerk . buildUrlWithAuth ( redirectUrl ) ;
600
+
601
+ if ( ! this . resource . id ) {
602
+ await this . create ( {
603
+ strategy,
604
+ redirectUrl : redirectUrlWithAuthToken ,
605
+ actionCompleteRedirectUrl : redirectUrlComplete ,
606
+ } ) ;
607
+ }
608
+
609
+ const { status, externalVerificationRedirectURL } = this . resource . firstFactorVerification ;
610
+
611
+ if ( status === 'unverified' && externalVerificationRedirectURL ) {
612
+ windowNavigate ( externalVerificationRedirectURL ) ;
613
+ }
614
+ } catch ( err : unknown ) {
615
+ eventBus . emit ( 'resource:error' , { resource : this . resource , error : err } ) ;
616
+ return { error : err } ;
617
+ }
618
+
619
+ return { error : null } ;
620
+ }
621
+ }
0 commit comments