@@ -61,6 +61,7 @@ import auth, {
6161 verifyBeforeUpdateEmail ,
6262 getAdditionalUserInfo ,
6363 getCustomAuthDomain ,
64+ validatePassword ,
6465 AppleAuthProvider ,
6566 EmailAuthProvider ,
6667 FacebookAuthProvider ,
@@ -72,6 +73,8 @@ import auth, {
7273 TwitterAuthProvider ,
7374} from '../lib' ;
7475
76+ const PasswordPolicyImpl = require ( '../lib/password-policy/PasswordPolicyImpl' ) . default ;
77+
7578// @ts -ignore test
7679import FirebaseModule from '../../app/lib/internal/FirebaseModule' ;
7780// @ts -ignore - We don't mind missing types here
@@ -133,13 +136,13 @@ describe('Auth', function () {
133136 const result = auth ( ) . useEmulator ( 'http://my-host:9099' ) ;
134137 expect ( result ) . toEqual ( [ 'my-host' , 9099 ] ) ;
135138 } ) ;
136- } ) ;
137139
138- describe ( 'tenantId' , function ( ) {
139- it ( 'should be able to set tenantId ' , function ( ) {
140- const auth = firebase . app ( ) . auth ( ) ;
141- auth . setTenantId ( 'test-id' ) . then ( ( ) => {
142- expect ( auth . tenantId ) . toBe ( 'test-id' ) ;
140+ describe ( 'tenantId' , function ( ) {
141+ it ( 'should be able to set tenantId ' , function ( ) {
142+ const auth = firebase . app ( ) . auth ( ) ;
143+ auth . setTenantId ( 'test-id' ) . then ( ( ) => {
144+ expect ( auth . tenantId ) . toBe ( 'test-id' ) ;
145+ } ) ;
143146 } ) ;
144147 } ) ;
145148
@@ -201,6 +204,84 @@ describe('Auth', function () {
201204 expect ( actual . _auth ) . not . toBeNull ( ) ;
202205 } ) ;
203206 } ) ;
207+
208+ describe ( 'ActionCodeSettings' , function ( ) {
209+ beforeAll ( function ( ) {
210+ // @ts -ignore test
211+ jest . spyOn ( FirebaseModule . prototype , 'native' , 'get' ) . mockImplementation ( ( ) => {
212+ return new Proxy (
213+ { } ,
214+ {
215+ get : ( ) => jest . fn ( ) . mockResolvedValue ( { } as never ) ,
216+ } ,
217+ ) ;
218+ } ) ;
219+ } ) ;
220+
221+ it ( 'should allow linkDomain as `ActionCodeSettings.linkDomain`' , function ( ) {
222+ const auth = firebase . app ( ) . auth ( ) ;
223+ const actionCodeSettings : FirebaseAuthTypes . ActionCodeSettings = {
224+ url : 'https://example.com' ,
225+ handleCodeInApp : true ,
226+ linkDomain : 'example.com' ,
227+ } ;
228+ const email = 'fake@example.com' ;
229+ auth . sendSignInLinkToEmail ( email , actionCodeSettings ) ;
230+ auth . sendPasswordResetEmail ( email , actionCodeSettings ) ;
231+ sendPasswordResetEmail ( auth , email , actionCodeSettings ) ;
232+ sendSignInLinkToEmail ( auth , email , actionCodeSettings ) ;
233+
234+ const user : FirebaseAuthTypes . User = new User ( auth , { } ) ;
235+
236+ user . sendEmailVerification ( actionCodeSettings ) ;
237+ user . verifyBeforeUpdateEmail ( email , actionCodeSettings ) ;
238+ sendEmailVerification ( user , actionCodeSettings ) ;
239+ verifyBeforeUpdateEmail ( user , email , actionCodeSettings ) ;
240+ } ) ;
241+
242+ it ( 'should warn using `ActionCodeSettings.dynamicLinkDomain`' , function ( ) {
243+ const auth = firebase . app ( ) . auth ( ) ;
244+ const actionCodeSettings : FirebaseAuthTypes . ActionCodeSettings = {
245+ url : 'https://example.com' ,
246+ handleCodeInApp : true ,
247+ linkDomain : 'example.com' ,
248+ dynamicLinkDomain : 'example.com' ,
249+ } ;
250+ const email = 'fake@example.com' ;
251+ let warnings = 0 ;
252+ const consoleWarnSpy = jest . spyOn ( console , 'warn' ) ;
253+ consoleWarnSpy . mockReset ( ) ;
254+ consoleWarnSpy . mockImplementation ( warnMessage => {
255+ if (
256+ warnMessage . includes (
257+ 'Instead, use ActionCodeSettings.linkDomain to set up a custom domain' ,
258+ )
259+ ) {
260+ warnings ++ ;
261+ }
262+ } ) ;
263+ auth . sendSignInLinkToEmail ( email , actionCodeSettings ) ;
264+ expect ( warnings ) . toBe ( 1 ) ;
265+ auth . sendPasswordResetEmail ( email , actionCodeSettings ) ;
266+ expect ( warnings ) . toBe ( 2 ) ;
267+ sendPasswordResetEmail ( auth , email , actionCodeSettings ) ;
268+ expect ( warnings ) . toBe ( 3 ) ;
269+ sendSignInLinkToEmail ( auth , email , actionCodeSettings ) ;
270+ expect ( warnings ) . toBe ( 4 ) ;
271+ const user : FirebaseAuthTypes . User = new User ( auth , { } ) ;
272+
273+ user . sendEmailVerification ( actionCodeSettings ) ;
274+ expect ( warnings ) . toBe ( 5 ) ;
275+ user . verifyBeforeUpdateEmail ( email , actionCodeSettings ) ;
276+ expect ( warnings ) . toBe ( 6 ) ;
277+ sendEmailVerification ( user , actionCodeSettings ) ;
278+ expect ( warnings ) . toBe ( 7 ) ;
279+ verifyBeforeUpdateEmail ( user , email , actionCodeSettings ) ;
280+ expect ( warnings ) . toBe ( 8 ) ;
281+ consoleWarnSpy . mockReset ( ) ;
282+ consoleWarnSpy . mockRestore ( ) ;
283+ } ) ;
284+ } ) ;
204285 } ) ;
205286
206287 describe ( 'modular' , function ( ) {
@@ -420,6 +501,10 @@ describe('Auth', function () {
420501 expect ( getCustomAuthDomain ) . toBeDefined ( ) ;
421502 } ) ;
422503
504+ it ( '`validatePassword` function is properly exposed to end user' , function ( ) {
505+ expect ( validatePassword ) . toBeDefined ( ) ;
506+ } ) ;
507+
423508 it ( '`AppleAuthProvider` class is properly exposed to end user' , function ( ) {
424509 expect ( AppleAuthProvider ) . toBeDefined ( ) ;
425510 } ) ;
@@ -456,81 +541,79 @@ describe('Auth', function () {
456541 expect ( TwitterAuthProvider ) . toBeDefined ( ) ;
457542 } ) ;
458543
459- describe ( 'ActionCodeSettings' , function ( ) {
460- beforeAll ( function ( ) {
461- // @ts -ignore test
462- jest . spyOn ( FirebaseModule . prototype , 'native' , 'get' ) . mockImplementation ( ( ) => {
463- return new Proxy (
464- { } ,
465- {
466- get : ( ) => jest . fn ( ) . mockResolvedValue ( { } as never ) ,
467- } ,
468- ) ;
469- } ) ;
544+ describe ( 'PasswordPolicyImpl' , function ( ) {
545+ const TEST_MIN_PASSWORD_LENGTH = 6 ;
546+ const TEST_SCHEMA_VERSION = 1 ;
547+
548+ const testPolicy = {
549+ customStrengthOptions : {
550+ minPasswordLength : 6 ,
551+ maxPasswordLength : 4096 ,
552+ containsLowercaseCharacter : true ,
553+ containsUppercaseCharacter : true ,
554+ containsNumericCharacter : true ,
555+ containsNonAlphanumericCharacter : true ,
556+ } ,
557+ allowedNonAlphanumericCharacters : [ '$' , '*' ] ,
558+ schemaVersion : 1 ,
559+ enforcementState : 'OFF' ,
560+ } ;
561+
562+ it ( 'should create a password policy' , async ( ) => {
563+ let passwordPolicy = new PasswordPolicyImpl ( testPolicy ) ;
564+ expect ( passwordPolicy ) . toBeDefined ( ) ;
565+ expect ( passwordPolicy . customStrengthOptions . minPasswordLength ) . toEqual (
566+ TEST_MIN_PASSWORD_LENGTH ,
567+ ) ;
568+ expect ( passwordPolicy . schemaVersion ) . toEqual ( TEST_SCHEMA_VERSION ) ;
470569 } ) ;
471570
472- it ( 'should allow linkDomain as `ActionCodeSettings.linkDomain`' , function ( ) {
473- const auth = firebase . app ( ) . auth ( ) ;
474- const actionCodeSettings : FirebaseAuthTypes . ActionCodeSettings = {
475- url : 'https://example.com' ,
476- handleCodeInApp : true ,
477- linkDomain : 'example.com' ,
478- } ;
479- const email = 'fake@example.com' ;
480- auth . sendSignInLinkToEmail ( email , actionCodeSettings ) ;
481- auth . sendPasswordResetEmail ( email , actionCodeSettings ) ;
482- sendPasswordResetEmail ( auth , email , actionCodeSettings ) ;
483- sendSignInLinkToEmail ( auth , email , actionCodeSettings ) ;
571+ it ( 'should return statusValid: true when the password satisfies the password policy' , async ( ) => {
572+ const passwordPolicy = new PasswordPolicyImpl ( testPolicy ) ;
573+ let password = 'Password$123' ;
574+ let status = passwordPolicy . validatePassword ( password ) ;
575+ expect ( status ) . toBeDefined ( ) ;
576+ expect ( status . isValid ) . toEqual ( true ) ;
577+ } ) ;
484578
485- const user : FirebaseAuthTypes . User = new User ( auth , { } ) ;
579+ it ( 'should return statusValid: false when the password is too short' , async ( ) => {
580+ const passwordPolicy = new PasswordPolicyImpl ( testPolicy ) ;
581+ let password = 'Pa1$' ;
582+ let status = passwordPolicy . validatePassword ( password ) ;
583+ expect ( status ) . toBeDefined ( ) ;
584+ expect ( status . isValid ) . toEqual ( false ) ;
585+ } ) ;
486586
487- user . sendEmailVerification ( actionCodeSettings ) ;
488- user . verifyBeforeUpdateEmail ( email , actionCodeSettings ) ;
489- sendEmailVerification ( user , actionCodeSettings ) ;
490- verifyBeforeUpdateEmail ( user , email , actionCodeSettings ) ;
587+ it ( 'should return statusValid: false when the password has no capital characters' , async ( ) => {
588+ const passwordPolicy = new PasswordPolicyImpl ( testPolicy ) ;
589+ let password = 'password123$' ;
590+ let status = passwordPolicy . validatePassword ( password ) ;
591+ expect ( status ) . toBeDefined ( ) ;
592+ expect ( status . isValid ) . toEqual ( false ) ;
491593 } ) ;
492594
493- it ( 'should warn using `ActionCodeSettings.dynamicLinkDomain`' , function ( ) {
494- const auth = firebase . app ( ) . auth ( ) ;
495- const actionCodeSettings : FirebaseAuthTypes . ActionCodeSettings = {
496- url : 'https://example.com' ,
497- handleCodeInApp : true ,
498- linkDomain : 'example.com' ,
499- dynamicLinkDomain : 'example.com' ,
500- } ;
501- const email = 'fake@example.com' ;
502- let warnings = 0 ;
503- const consoleWarnSpy = jest . spyOn ( console , 'warn' ) ;
504- consoleWarnSpy . mockReset ( ) ;
505- consoleWarnSpy . mockImplementation ( warnMessage => {
506- if (
507- warnMessage . includes (
508- 'Instead, use ActionCodeSettings.linkDomain to set up a custom domain' ,
509- )
510- ) {
511- warnings ++ ;
512- }
513- } ) ;
514- auth . sendSignInLinkToEmail ( email , actionCodeSettings ) ;
515- expect ( warnings ) . toBe ( 1 ) ;
516- auth . sendPasswordResetEmail ( email , actionCodeSettings ) ;
517- expect ( warnings ) . toBe ( 2 ) ;
518- sendPasswordResetEmail ( auth , email , actionCodeSettings ) ;
519- expect ( warnings ) . toBe ( 3 ) ;
520- sendSignInLinkToEmail ( auth , email , actionCodeSettings ) ;
521- expect ( warnings ) . toBe ( 4 ) ;
522- const user : FirebaseAuthTypes . User = new User ( auth , { } ) ;
595+ it ( 'should return statusValid: false when the password has no lowercase characters' , async ( ) => {
596+ const passwordPolicy = new PasswordPolicyImpl ( testPolicy ) ;
597+ let password = 'PASSWORD123$' ;
598+ let status = passwordPolicy . validatePassword ( password ) ;
599+ expect ( status ) . toBeDefined ( ) ;
600+ expect ( status . isValid ) . toEqual ( false ) ;
601+ } ) ;
523602
524- user . sendEmailVerification ( actionCodeSettings ) ;
525- expect ( warnings ) . toBe ( 5 ) ;
526- user . verifyBeforeUpdateEmail ( email , actionCodeSettings ) ;
527- expect ( warnings ) . toBe ( 6 ) ;
528- sendEmailVerification ( user , actionCodeSettings ) ;
529- expect ( warnings ) . toBe ( 7 ) ;
530- verifyBeforeUpdateEmail ( user , email , actionCodeSettings ) ;
531- expect ( warnings ) . toBe ( 8 ) ;
532- consoleWarnSpy . mockReset ( ) ;
533- consoleWarnSpy . mockRestore ( ) ;
603+ it ( 'should return statusValid: false when the password has no numbers' , async ( ) => {
604+ const passwordPolicy = new PasswordPolicyImpl ( testPolicy ) ;
605+ let password = 'Password$' ;
606+ let status = passwordPolicy . validatePassword ( password ) ;
607+ expect ( status ) . toBeDefined ( ) ;
608+ expect ( status . isValid ) . toEqual ( false ) ;
609+ } ) ;
610+
611+ it ( 'should return statusValid: false when the password has no special characters' , async ( ) => {
612+ const passwordPolicy = new PasswordPolicyImpl ( testPolicy ) ;
613+ let password = 'Password123' ;
614+ let status = passwordPolicy . validatePassword ( password ) ;
615+ expect ( status ) . toBeDefined ( ) ;
616+ expect ( status . isValid ) . toEqual ( false ) ;
534617 } ) ;
535618 } ) ;
536619 } ) ;
0 commit comments