@@ -32,7 +32,8 @@ import {
32
32
addTokenListener ,
33
33
removeTokenListener ,
34
34
formatDummyToken ,
35
- defaultTokenErrorData
35
+ defaultTokenErrorData ,
36
+ getScopedToken
36
37
} from './internal-api' ;
37
38
import * as reCAPTCHA from './recaptcha' ;
38
39
import * as client from './client' ;
@@ -637,6 +638,180 @@ describe('internal api', () => {
637
638
} ) ;
638
639
} ) ;
639
640
641
+ describe ( 'getScopedToken()' , ( ) => {
642
+ it ( 'uses customTokenProvider to get an AppCheck token' , async ( ) => {
643
+ const customTokenProvider = getFakeCustomTokenProvider ( ) ;
644
+ const customProviderSpy = spy ( customTokenProvider , 'getToken' ) ;
645
+
646
+ const appCheck = initializeAppCheck ( app , {
647
+ provider : customTokenProvider
648
+ } ) ;
649
+ const token = await getScopedToken ( appCheck as AppCheckService ) ;
650
+
651
+ expect ( customProviderSpy ) . to . be . called ;
652
+ expect ( token ) . to . deep . equal ( {
653
+ token : 'fake-custom-app-check-token'
654
+ } ) ;
655
+ } ) ;
656
+
657
+ it ( 'does not interact with state' , async ( ) => {
658
+ const customTokenProvider = getFakeCustomTokenProvider ( ) ;
659
+ spy ( customTokenProvider , 'getToken' ) ;
660
+
661
+ const appCheck = initializeAppCheck ( app , {
662
+ provider : customTokenProvider
663
+ } ) ;
664
+ await getScopedToken ( appCheck as AppCheckService ) ;
665
+
666
+ expect ( getStateReference ( app ) . token ) . to . be . undefined ;
667
+ expect ( getStateReference ( app ) . isTokenAutoRefreshEnabled ) . to . be . false ;
668
+ } ) ;
669
+
670
+ it ( 'uses reCAPTCHA (V3) token to exchange for AppCheck token' , async ( ) => {
671
+ const appCheck = initializeAppCheck ( app , {
672
+ provider : new ReCaptchaV3Provider ( FAKE_SITE_KEY )
673
+ } ) ;
674
+
675
+ const reCAPTCHASpy = stub ( reCAPTCHA , 'getToken' ) . returns (
676
+ Promise . resolve ( fakeRecaptchaToken )
677
+ ) ;
678
+ const exchangeTokenStub : SinonStub = stub (
679
+ client ,
680
+ 'exchangeToken'
681
+ ) . returns ( Promise . resolve ( fakeRecaptchaAppCheckToken ) ) ;
682
+
683
+ const token = await getScopedToken ( appCheck as AppCheckService ) ;
684
+
685
+ expect ( reCAPTCHASpy ) . to . be . called ;
686
+
687
+ expect ( exchangeTokenStub . args [ 0 ] [ 0 ] . body [ 'recaptcha_v3_token' ] ) . to . equal (
688
+ fakeRecaptchaToken
689
+ ) ;
690
+ expect ( token ) . to . deep . equal ( { token : fakeRecaptchaAppCheckToken . token } ) ;
691
+ } ) ;
692
+
693
+ it ( 'uses reCAPTCHA (Enterprise) token to exchange for AppCheck token' , async ( ) => {
694
+ const appCheck = initializeAppCheck ( app , {
695
+ provider : new ReCaptchaEnterpriseProvider ( FAKE_SITE_KEY )
696
+ } ) ;
697
+
698
+ const reCAPTCHASpy = stub ( reCAPTCHA , 'getToken' ) . returns (
699
+ Promise . resolve ( fakeRecaptchaToken )
700
+ ) ;
701
+ const exchangeTokenStub : SinonStub = stub (
702
+ client ,
703
+ 'exchangeToken'
704
+ ) . returns ( Promise . resolve ( fakeRecaptchaAppCheckToken ) ) ;
705
+
706
+ const token = await getScopedToken ( appCheck as AppCheckService ) ;
707
+
708
+ expect ( reCAPTCHASpy ) . to . be . called ;
709
+
710
+ expect (
711
+ exchangeTokenStub . args [ 0 ] [ 0 ] . body [ 'recaptcha_enterprise_token' ]
712
+ ) . to . equal ( fakeRecaptchaToken ) ;
713
+ expect ( token ) . to . deep . equal ( { token : fakeRecaptchaAppCheckToken . token } ) ;
714
+ } ) ;
715
+
716
+ it ( 'resolves with a dummy token and an error if failed to get a token' , async ( ) => {
717
+ const errorStub = stub ( console , 'error' ) ;
718
+ const appCheck = initializeAppCheck ( app , {
719
+ provider : new ReCaptchaV3Provider ( FAKE_SITE_KEY )
720
+ } ) ;
721
+
722
+ const reCAPTCHASpy = stub ( reCAPTCHA , 'getToken' ) . returns (
723
+ Promise . resolve ( fakeRecaptchaToken )
724
+ ) ;
725
+
726
+ const error = new Error ( 'oops, something went wrong' ) ;
727
+ stub ( client , 'exchangeToken' ) . returns ( Promise . reject ( error ) ) ;
728
+
729
+ const token = await getScopedToken ( appCheck as AppCheckService ) ;
730
+
731
+ expect ( reCAPTCHASpy ) . to . be . called ;
732
+ expect ( token ) . to . deep . equal ( {
733
+ token : formatDummyToken ( defaultTokenErrorData ) ,
734
+ error
735
+ } ) ;
736
+ expect ( errorStub . args [ 0 ] [ 1 ] . message ) . to . include (
737
+ 'oops, something went wrong'
738
+ ) ;
739
+ errorStub . restore ( ) ;
740
+ } ) ;
741
+
742
+ it ( 'exchanges debug token if in debug mode' , async ( ) => {
743
+ const exchangeTokenStub : SinonStub = stub (
744
+ client ,
745
+ 'exchangeToken'
746
+ ) . returns ( Promise . resolve ( fakeRecaptchaAppCheckToken ) ) ;
747
+ const debugState = getDebugState ( ) ;
748
+ debugState . enabled = true ;
749
+ debugState . token = new Deferred ( ) ;
750
+ debugState . token . resolve ( 'my-debug-token' ) ;
751
+ const appCheck = initializeAppCheck ( app , {
752
+ provider : new ReCaptchaV3Provider ( FAKE_SITE_KEY )
753
+ } ) ;
754
+
755
+ const token = await getScopedToken ( appCheck as AppCheckService ) ;
756
+ expect ( exchangeTokenStub . args [ 0 ] [ 0 ] . body [ 'debug_token' ] ) . to . equal (
757
+ 'my-debug-token'
758
+ ) ;
759
+ expect ( token ) . to . deep . equal ( { token : fakeRecaptchaAppCheckToken . token } ) ;
760
+ } ) ;
761
+
762
+ it ( 'throttles for a period less than 1d on 503' , async ( ) => {
763
+ // More detailed check of exponential backoff in providers.test.ts
764
+ const appCheck = initializeAppCheck ( app , {
765
+ provider : new ReCaptchaV3Provider ( FAKE_SITE_KEY )
766
+ } ) ;
767
+ const warnStub = stub ( logger , 'warn' ) ;
768
+ stub ( client , 'exchangeToken' ) . returns (
769
+ Promise . reject (
770
+ ERROR_FACTORY . create ( AppCheckError . FETCH_STATUS_ERROR , {
771
+ httpStatus : 503
772
+ } )
773
+ )
774
+ ) ;
775
+
776
+ const token = await getScopedToken ( appCheck as AppCheckService ) ;
777
+
778
+ // ReCaptchaV3Provider's _throttleData is private so checking
779
+ // the resulting error message to be sure it has roughly the
780
+ // correct throttle time. This also tests the time formatter.
781
+ // Check both the error itself and that it makes it through to
782
+ // console.warn
783
+ expect ( token . error ?. message ) . to . include ( '503' ) ;
784
+ expect ( token . error ?. message ) . to . include ( '00m' ) ;
785
+ expect ( token . error ?. message ) . to . not . include ( '1d' ) ;
786
+ expect ( warnStub . args [ 0 ] [ 0 ] ) . to . include ( '503' ) ;
787
+ } ) ;
788
+
789
+ it ( 'throttles 1d on 403' , async ( ) => {
790
+ const appCheck = initializeAppCheck ( app , {
791
+ provider : new ReCaptchaV3Provider ( FAKE_SITE_KEY )
792
+ } ) ;
793
+ const warnStub = stub ( logger , 'warn' ) ;
794
+ stub ( client , 'exchangeToken' ) . returns (
795
+ Promise . reject (
796
+ ERROR_FACTORY . create ( AppCheckError . FETCH_STATUS_ERROR , {
797
+ httpStatus : 403
798
+ } )
799
+ )
800
+ ) ;
801
+
802
+ const token = await getScopedToken ( appCheck as AppCheckService ) ;
803
+
804
+ // ReCaptchaV3Provider's _throttleData is private so checking
805
+ // the resulting error message to be sure it has roughly the
806
+ // correct throttle time. This also tests the time formatter.
807
+ // Check both the error itself and that it makes it through to
808
+ // console.warn
809
+ expect ( token . error ?. message ) . to . include ( '403' ) ;
810
+ expect ( token . error ?. message ) . to . include ( '1d' ) ;
811
+ expect ( warnStub . args [ 0 ] [ 0 ] ) . to . include ( '403' ) ;
812
+ } ) ;
813
+ } ) ;
814
+
640
815
describe ( 'addTokenListener' , ( ) => {
641
816
afterEach ( async ( ) => {
642
817
clearState ( ) ;
0 commit comments