@@ -308,7 +308,210 @@ describe('core/credentials/email', () => {
308
308
idToken : 'id-token-2' ,
309
309
returnSecureToken : true ,
310
310
email : 'some-email' ,
311
- password : 'some-password'
311
+ password : 'some-password' ,
312
+ clientType : RecaptchaClientType . WEB
313
+ } ) ;
314
+ } ) ;
315
+ context ( '#recaptcha' , ( ) => {
316
+ beforeEach ( async ( ) => {
317
+ apiMock = mockEndpoint ( Endpoint . SIGN_UP , {
318
+ idToken : 'id-token' ,
319
+ refreshToken : 'refresh-token' ,
320
+ expiresIn : '1234' ,
321
+ localId : serverUser . localId !
322
+ } ) ;
323
+
324
+ } ) ;
325
+
326
+ afterEach ( ( ) => {
327
+ sinon . restore ( ) ;
328
+ } ) ;
329
+
330
+ const recaptchaConfigResponseEnforce = {
331
+ recaptchaKey : 'foo/bar/to/site-key' ,
332
+ recaptchaEnforcementState : [
333
+ { provider : 'EMAIL_PASSWORD_PROVIDER' , enforcementState : 'ENFORCE' }
334
+ ]
335
+ } ;
336
+ const recaptchaConfigResponseOff = {
337
+ recaptchaKey : 'foo/bar/to/site-key' ,
338
+ recaptchaEnforcementState : [
339
+ { provider : 'EMAIL_PASSWORD_PROVIDER' , enforcementState : 'OFF' }
340
+ ]
341
+ } ;
342
+
343
+ it ( 'calls sign up with password with recaptcha enabled' , async ( ) => {
344
+ const recaptcha = new MockGreCAPTCHATopLevel ( ) ;
345
+ if ( typeof window === 'undefined' ) {
346
+ return ;
347
+ }
348
+ window . grecaptcha = recaptcha ;
349
+ sinon
350
+ . stub ( recaptcha . enterprise , 'execute' )
351
+ . returns ( Promise . resolve ( 'recaptcha-response' ) ) ;
352
+ mockEndpointWithParams (
353
+ Endpoint . GET_RECAPTCHA_CONFIG ,
354
+ {
355
+ clientType : RecaptchaClientType . WEB ,
356
+ version : RecaptchaVersion . ENTERPRISE
357
+ } ,
358
+ recaptchaConfigResponseEnforce
359
+ ) ;
360
+ await _initializeRecaptchaConfig ( auth ) ;
361
+
362
+ const idTokenResponse = await credential . _linkToIdToken (
363
+ auth ,
364
+ 'id-token-2'
365
+ ) ;
366
+ expect ( idTokenResponse . idToken ) . to . eq ( 'id-token' ) ;
367
+ expect ( idTokenResponse . refreshToken ) . to . eq ( 'refresh-token' ) ;
368
+ expect ( idTokenResponse . expiresIn ) . to . eq ( '1234' ) ;
369
+ expect ( idTokenResponse . localId ) . to . eq ( serverUser . localId ) ;
370
+ expect ( apiMock . calls [ 0 ] . request ) . to . eql ( {
371
+ captchaResponse : 'recaptcha-response' ,
372
+ recaptchaVersion : RecaptchaVersion . ENTERPRISE ,
373
+ idToken : 'id-token-2' ,
374
+ returnSecureToken : true ,
375
+ email : 'some-email' ,
376
+ password : 'some-password' ,
377
+ clientType : 'CLIENT_TYPE_WEB'
378
+ } ) ;
379
+ } ) ;
380
+
381
+ it ( 'calls sign up with password with recaptcha disabled' , async ( ) => {
382
+ const recaptcha = new MockGreCAPTCHATopLevel ( ) ;
383
+ if ( typeof window === 'undefined' ) {
384
+ return ;
385
+ }
386
+ window . grecaptcha = recaptcha ;
387
+ sinon
388
+ . stub ( recaptcha . enterprise , 'execute' )
389
+ . returns ( Promise . resolve ( 'recaptcha-response' ) ) ;
390
+ mockEndpointWithParams (
391
+ Endpoint . GET_RECAPTCHA_CONFIG ,
392
+ {
393
+ clientType : RecaptchaClientType . WEB ,
394
+ version : RecaptchaVersion . ENTERPRISE
395
+ } ,
396
+ recaptchaConfigResponseOff
397
+ ) ;
398
+ await _initializeRecaptchaConfig ( auth ) ;
399
+ const idTokenResponse = await credential . _linkToIdToken (
400
+ auth ,
401
+ 'id-token-2'
402
+ ) ;
403
+ expect ( idTokenResponse . idToken ) . to . eq ( 'id-token' ) ;
404
+ expect ( idTokenResponse . refreshToken ) . to . eq ( 'refresh-token' ) ;
405
+ expect ( idTokenResponse . expiresIn ) . to . eq ( '1234' ) ;
406
+ expect ( idTokenResponse . localId ) . to . eq ( serverUser . localId ) ;
407
+ expect ( apiMock . calls [ 0 ] . request ) . to . eql ( {
408
+ idToken : 'id-token-2' ,
409
+ returnSecureToken : true ,
410
+ email : 'some-email' ,
411
+ password : 'some-password' ,
412
+ clientType : 'CLIENT_TYPE_WEB'
413
+ } ) ;
414
+ } ) ;
415
+
416
+ it ( 'calls sign up with password with recaptcha forced refresh' , async ( ) => {
417
+ if ( typeof window === 'undefined' ) {
418
+ return ;
419
+ }
420
+ // Mock recaptcha js loading method but not set window.recaptcha to simulate recaptcha token retrieval failure
421
+ sinon
422
+ . stub ( jsHelpers , '_loadJS' )
423
+ . returns ( Promise . resolve ( new Event ( '' ) ) ) ;
424
+ window . grecaptcha = undefined ;
425
+
426
+ const getRecaptchaConfigMock = mockEndpointWithParams (
427
+ Endpoint . GET_RECAPTCHA_CONFIG ,
428
+ {
429
+ clientType : RecaptchaClientType . WEB ,
430
+ version : RecaptchaVersion . ENTERPRISE
431
+ } ,
432
+ recaptchaConfigResponseEnforce
433
+ ) ;
434
+ await _initializeRecaptchaConfig ( auth ) ;
435
+ auth . _agentRecaptchaConfig ! . siteKey = 'cached-site-key' ;
436
+
437
+ await expect ( credential . _linkToIdToken ( auth , 'id-token-2' ) ) . to . be . rejectedWith (
438
+ 'No reCAPTCHA enterprise script loaded.'
439
+ ) ;
440
+ // Should call getRecaptchaConfig once to refresh the cached recaptcha config
441
+ expect ( getRecaptchaConfigMock . calls . length ) . to . eq ( 2 ) ;
442
+ expect ( auth . _agentRecaptchaConfig ?. siteKey ) . to . eq ( 'site-key' ) ;
443
+ } ) ;
444
+
445
+ it ( 'calls fallback to recaptcha flow when receiving MISSING_RECAPTCHA_TOKEN error' , async ( ) => {
446
+ if ( typeof window === 'undefined' ) {
447
+ return ;
448
+ }
449
+
450
+ // First call without recaptcha token should fail with MISSING_RECAPTCHA_TOKEN error
451
+ mockEndpointWithParams (
452
+ Endpoint . SIGN_UP ,
453
+ {
454
+ idToken : 'id-token-2' ,
455
+ email : 'some-email' ,
456
+ password : 'some-password' ,
457
+ returnSecureToken : true ,
458
+ clientType : RecaptchaClientType . WEB
459
+ } ,
460
+ {
461
+ error : {
462
+ code : 400 ,
463
+ message : ServerError . MISSING_RECAPTCHA_TOKEN
464
+ }
465
+ } ,
466
+ 400
467
+ ) ;
468
+
469
+ // Second call with a valid recaptcha token (captchaResp) should succeed
470
+ mockEndpointWithParams (
471
+ Endpoint . SIGN_UP ,
472
+ {
473
+ captchaResponse : 'recaptcha-response' ,
474
+ clientType : RecaptchaClientType . WEB ,
475
+ email : 'some-email' ,
476
+ password : 'some-password' ,
477
+ recaptchaVersion : RecaptchaVersion . ENTERPRISE ,
478
+ returnSecureToken : true
479
+ } ,
480
+ {
481
+ idToken : 'id-token' ,
482
+ refreshToken : 'refresh-token' ,
483
+ expiresIn : '1234' ,
484
+ localId : serverUser . localId !
485
+ }
486
+ ) ;
487
+
488
+ // Mock recaptcha js loading method and manually set window.recaptcha
489
+ sinon
490
+ . stub ( jsHelpers , '_loadJS' )
491
+ . returns ( Promise . resolve ( new Event ( '' ) ) ) ;
492
+ const recaptcha = new MockGreCAPTCHATopLevel ( ) ;
493
+ window . grecaptcha = recaptcha ;
494
+ const stub = sinon . stub ( recaptcha . enterprise , 'execute' ) ;
495
+ stub
496
+ . withArgs ( 'site-key' , {
497
+ action : RecaptchaActionName . SIGN_IN_WITH_PASSWORD
498
+ } )
499
+ . returns ( Promise . resolve ( 'recaptcha-response' ) ) ;
500
+
501
+ mockEndpointWithParams (
502
+ Endpoint . GET_RECAPTCHA_CONFIG ,
503
+ {
504
+ clientType : RecaptchaClientType . WEB ,
505
+ version : RecaptchaVersion . ENTERPRISE
506
+ } ,
507
+ recaptchaConfigResponseEnforce
508
+ ) ;
509
+
510
+ const idTokenResponse = await credential . _linkToIdToken ( auth , "id-token-2" ) ;
511
+ expect ( idTokenResponse . idToken ) . to . eq ( 'id-token' ) ;
512
+ expect ( idTokenResponse . refreshToken ) . to . eq ( 'refresh-token' ) ;
513
+ expect ( idTokenResponse . expiresIn ) . to . eq ( '1234' ) ;
514
+ expect ( idTokenResponse . localId ) . to . eq ( serverUser . localId ) ;
312
515
} ) ;
313
516
} ) ;
314
517
} ) ;
0 commit comments