Skip to content

Commit df18ec1

Browse files
authored
Merge 928a7b5 into ca662bd
2 parents ca662bd + 928a7b5 commit df18ec1

File tree

6 files changed

+68
-77
lines changed

6 files changed

+68
-77
lines changed

common/api-review/auth.api.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,7 @@ export interface ParsedToken {
564564

565565
// @public
566566
export interface PasswordPolicy {
567-
readonly allowedNonAlphanumericCharacters?: string[];
567+
readonly allowedNonAlphanumericCharacters: string[];
568568
readonly customStrengthOptions: {
569569
readonly minPasswordLength?: number;
570570
readonly maxPasswordLength?: number;

packages/auth/src/core/auth/auth_impl.test.ts

Lines changed: 29 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -790,64 +790,58 @@ describe('core/auth/auth_impl', () => {
790790
context('passwordPolicy', () => {
791791
const TEST_ALLOWED_NON_ALPHANUMERIC_CHARS = ['!', '(', ')'];
792792
const TEST_MIN_PASSWORD_LENGTH = 6;
793+
const TEST_SCHEMA_VERSION = 1;
794+
const TEST_TENANT_ID = 'tenant-id';
795+
const TEST_TENANT_ID_UNSUPPORTED_POLICY_VERSION =
796+
'tenant-id-unsupported-policy-version';
793797

794-
const passwordPolicyResponse = {
798+
const PASSWORD_POLICY_RESPONSE = {
795799
customStrengthOptions: {
796800
minPasswordLength: TEST_MIN_PASSWORD_LENGTH
797801
},
798802
allowedNonAlphanumericCharacters: TEST_ALLOWED_NON_ALPHANUMERIC_CHARS,
799-
schemaVersion: 1
803+
schemaVersion: TEST_SCHEMA_VERSION
800804
};
801-
const passwordPolicyResponseRequireNumeric = {
805+
const PASSWORD_POLICY_RESPONSE_REQUIRE_NUMERIC = {
802806
customStrengthOptions: {
803807
minPasswordLength: TEST_MIN_PASSWORD_LENGTH,
804808
containsNumericCharacter: true
805809
},
806810
allowedNonAlphanumericCharacters: TEST_ALLOWED_NON_ALPHANUMERIC_CHARS,
807-
schemaVersion: 1
811+
schemaVersion: TEST_SCHEMA_VERSION
808812
};
809-
const passwordPolicyResponseUnsupportedVersion = {
813+
const PASSWORD_POLICY_RESPONSE_UNSUPPORTED_VERSION = {
810814
customStrengthOptions: {
811815
maxPasswordLength: TEST_MIN_PASSWORD_LENGTH,
812816
unsupportedPasswordPolicyProperty: 10
813817
},
814818
allowedNonAlphanumericCharacters: TEST_ALLOWED_NON_ALPHANUMERIC_CHARS,
815819
schemaVersion: 0
816820
};
817-
const cachedPasswordPolicy = {
818-
customStrengthOptions: {
819-
minPasswordLength: TEST_MIN_PASSWORD_LENGTH
820-
},
821-
allowedNonAlphanumericCharacters: TEST_ALLOWED_NON_ALPHANUMERIC_CHARS
822-
};
823-
const cachedPasswordPolicyRequireNumeric = {
824-
customStrengthOptions: {
825-
minPasswordLength: TEST_MIN_PASSWORD_LENGTH,
826-
containsNumericCharacter: true
827-
},
828-
allowedNonAlphanumericCharacters: TEST_ALLOWED_NON_ALPHANUMERIC_CHARS
829-
};
821+
const CACHED_PASSWORD_POLICY = PASSWORD_POLICY_RESPONSE;
822+
const CACHED_PASSWORD_POLICY_REQUIRE_NUMERIC =
823+
PASSWORD_POLICY_RESPONSE_REQUIRE_NUMERIC;
830824

831825
beforeEach(async () => {
832826
mockFetch.setUp();
833827
mockEndpointWithParams(
834828
Endpoint.GET_PASSWORD_POLICY,
835829
{},
836-
passwordPolicyResponse
830+
PASSWORD_POLICY_RESPONSE
837831
);
838832
mockEndpointWithParams(
839833
Endpoint.GET_PASSWORD_POLICY,
840834
{
841-
tenantId: 'tenant-id'
835+
tenantId: TEST_TENANT_ID
842836
},
843-
passwordPolicyResponseRequireNumeric
837+
PASSWORD_POLICY_RESPONSE_REQUIRE_NUMERIC
844838
);
845839
mockEndpointWithParams(
846840
Endpoint.GET_PASSWORD_POLICY,
847841
{
848-
tenantId: 'tenant-id-with-unsupported-policy-version'
842+
tenantId: TEST_TENANT_ID_UNSUPPORTED_POLICY_VERSION
849843
},
850-
passwordPolicyResponseUnsupportedVersion
844+
PASSWORD_POLICY_RESPONSE_UNSUPPORTED_VERSION
851845
);
852846
});
853847

@@ -860,16 +854,16 @@ describe('core/auth/auth_impl', () => {
860854
auth.tenantId = null;
861855
await auth._updatePasswordPolicy();
862856

863-
expect(auth._getPasswordPolicy()).to.eql(cachedPasswordPolicy);
857+
expect(auth._getPasswordPolicyInternal()).to.eql(CACHED_PASSWORD_POLICY);
864858
});
865859

866860
it('password policy should be set for tenant if tenant ID is not null', async () => {
867861
auth = await testAuth();
868-
auth.tenantId = 'tenant-id';
862+
auth.tenantId = TEST_TENANT_ID;
869863
await auth._updatePasswordPolicy();
870864

871-
expect(auth._getPasswordPolicy()).to.eql(
872-
cachedPasswordPolicyRequireNumeric
865+
expect(auth._getPasswordPolicyInternal()).to.eql(
866+
CACHED_PASSWORD_POLICY_REQUIRE_NUMERIC
873867
);
874868
});
875869

@@ -878,27 +872,27 @@ describe('core/auth/auth_impl', () => {
878872
auth.tenantId = null;
879873
await auth._updatePasswordPolicy();
880874

881-
auth.tenantId = 'tenant-id';
875+
auth.tenantId = TEST_TENANT_ID;
882876
await auth._updatePasswordPolicy();
883877

884878
auth.tenantId = null;
885-
expect(auth._getPasswordPolicy()).to.eql(cachedPasswordPolicy);
886-
auth.tenantId = 'tenant-id';
887-
expect(auth._getPasswordPolicy()).to.eql(
888-
cachedPasswordPolicyRequireNumeric
879+
expect(auth._getPasswordPolicyInternal()).to.eql(CACHED_PASSWORD_POLICY);
880+
auth.tenantId = TEST_TENANT_ID;
881+
expect(auth._getPasswordPolicyInternal()).to.eql(
882+
CACHED_PASSWORD_POLICY_REQUIRE_NUMERIC
889883
);
890884
auth.tenantId = 'other-tenant-id';
891-
expect(auth._getPasswordPolicy()).to.be.undefined;
885+
expect(auth._getPasswordPolicyInternal()).to.be.undefined;
892886
});
893887

894888
it('password policy should not be set when the schema version is not supported', async () => {
895889
auth = await testAuth();
896-
auth.tenantId = 'tenant-id-with-unsupported-policy-version';
890+
auth.tenantId = TEST_TENANT_ID_UNSUPPORTED_POLICY_VERSION;
897891
await expect(auth._updatePasswordPolicy()).to.be.rejectedWith(
898892
AuthErrorCode.UNSUPPORTED_PASSWORD_POLICY_SCHEMA_VERSION
899893
);
900894

901-
expect(auth._getPasswordPolicy()).to.be.undefined;
895+
expect(auth._getPasswordPolicyInternal()).to.be.undefined;
902896
});
903897
});
904898
});

packages/auth/src/core/auth/auth_impl.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ import {
3232
ErrorFn,
3333
NextFn,
3434
Unsubscribe,
35-
PasswordPolicy,
3635
PasswordValidationStatus
3736
} from '../../model/public_types';
3837
import {
@@ -70,6 +69,8 @@ import { AuthMiddlewareQueue } from './middleware';
7069
import { RecaptchaConfig } from '../../platform_browser/recaptcha/recaptcha';
7170
import { _logWarn } from '../util/log';
7271
import { _getPasswordPolicy } from '../../api/password_policy/get_password_policy';
72+
import { PasswordPolicyInternal } from '../../model/password_policy';
73+
import { PasswordPolicyImpl } from './password_policy_impl';
7374

7475
interface AsyncAction {
7576
(): Promise<void>;
@@ -105,8 +106,8 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
105106
_DEFAULT_AUTH_ERROR_FACTORY;
106107
_agentRecaptchaConfig: RecaptchaConfig | null = null;
107108
_tenantRecaptchaConfigs: Record<string, RecaptchaConfig> = {};
108-
_projectPasswordPolicy: PasswordPolicy | null = null;
109-
_tenantPasswordPolicies: Record<string, PasswordPolicy> = {};
109+
_projectPasswordPolicy: PasswordPolicyInternal | null = null;
110+
_tenantPasswordPolicies: Record<string, PasswordPolicyInternal> = {};
110111
readonly name: string;
111112

112113
// Tracks the last notified UID for state change listeners to prevent
@@ -429,11 +430,14 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
429430
}
430431

431432
async validatePassword(password: string): Promise<PasswordValidationStatus> {
432-
// TODO(chazzy): Implement.
433-
return Promise.reject(password);
433+
if (!this._getPasswordPolicyInternal()) {
434+
await this._updatePasswordPolicy();
435+
}
436+
437+
return this._getPasswordPolicyInternal()!.validatePassword(password);
434438
}
435439

436-
_getPasswordPolicy(): PasswordPolicy | null {
440+
_getPasswordPolicyInternal(): PasswordPolicyInternal | null {
437441
if (this.tenantId === null) {
438442
return this._projectPasswordPolicy;
439443
} else {
@@ -457,11 +461,9 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
457461
);
458462
}
459463

460-
const passwordPolicy: PasswordPolicy = {
461-
customStrengthOptions: response.customStrengthOptions,
462-
allowedNonAlphanumericCharacters:
463-
response.allowedNonAlphanumericCharacters
464-
};
464+
const passwordPolicy: PasswordPolicyInternal = new PasswordPolicyImpl(
465+
response
466+
);
465467

466468
if (this.tenantId === null) {
467469
this._projectPasswordPolicy = passwordPolicy;

packages/auth/src/core/strategies/email_and_password.test.ts

Lines changed: 22 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -757,34 +757,24 @@ describe('core/strategies/email_and_password/createUserWithEmailAndPassword', ()
757757
const PASSWORD_ERROR_MSG =
758758
'Firebase: The password does not meet the requirements. (auth/password-does-not-meet-requirements).';
759759

760-
const passwordPolicyResponse = {
760+
const PASSWORD_POLICY_RESPONSE = {
761761
customStrengthOptions: {
762762
minPasswordLength: TEST_MIN_PASSWORD_LENGTH
763763
},
764764
allowedNonAlphanumericCharacters: TEST_ALLOWED_NON_ALPHANUMERIC_CHARS,
765765
schemaVersion: TEST_SCHEMA_VERSION
766766
};
767-
const passwordPolicyResponseRequireNumeric = {
767+
const PASSWORD_POLICY_RESPONSE_REQUIRE_NUMERIC = {
768768
customStrengthOptions: {
769769
minPasswordLength: TEST_MIN_PASSWORD_LENGTH,
770770
containsNumericCharacter: true
771771
},
772772
allowedNonAlphanumericCharacters: TEST_ALLOWED_NON_ALPHANUMERIC_CHARS,
773773
schemaVersion: TEST_SCHEMA_VERSION
774774
};
775-
const cachedPasswordPolicy = {
776-
customStrengthOptions: {
777-
minPasswordLength: TEST_MIN_PASSWORD_LENGTH
778-
},
779-
allowedNonAlphanumericCharacters: TEST_ALLOWED_NON_ALPHANUMERIC_CHARS
780-
};
781-
const cachedPasswordPolicyRequireNumeric = {
782-
customStrengthOptions: {
783-
minPasswordLength: TEST_MIN_PASSWORD_LENGTH,
784-
containsNumericCharacter: true
785-
},
786-
allowedNonAlphanumericCharacters: TEST_ALLOWED_NON_ALPHANUMERIC_CHARS
787-
};
775+
const CACHED_PASSWORD_POLICY = PASSWORD_POLICY_RESPONSE;
776+
const CACHED_PASSWORD_POLICY_REQUIRE_NUMERIC =
777+
PASSWORD_POLICY_RESPONSE_REQUIRE_NUMERIC;
788778
let policyEndpointMock: mockFetch.Route;
789779
let policyEndpointMockWithTenant: mockFetch.Route;
790780
let policyEndpointMockWithOtherTenant: mockFetch.Route;
@@ -793,21 +783,21 @@ describe('core/strategies/email_and_password/createUserWithEmailAndPassword', ()
793783
policyEndpointMock = mockEndpointWithParams(
794784
Endpoint.GET_PASSWORD_POLICY,
795785
{},
796-
passwordPolicyResponse
786+
PASSWORD_POLICY_RESPONSE
797787
);
798788
policyEndpointMockWithTenant = mockEndpointWithParams(
799789
Endpoint.GET_PASSWORD_POLICY,
800790
{
801791
tenantId: TEST_TENANT_ID
802792
},
803-
passwordPolicyResponse
793+
PASSWORD_POLICY_RESPONSE
804794
);
805795
policyEndpointMockWithOtherTenant = mockEndpointWithParams(
806796
Endpoint.GET_PASSWORD_POLICY,
807797
{
808798
tenantId: TEST_REQUIRE_NUMERIC_TENANT_ID
809799
},
810-
passwordPolicyResponseRequireNumeric
800+
PASSWORD_POLICY_RESPONSE_REQUIRE_NUMERIC
811801
);
812802
});
813803

@@ -817,7 +807,7 @@ describe('core/strategies/email_and_password/createUserWithEmailAndPassword', ()
817807
).to.be.fulfilled;
818808

819809
expect(policyEndpointMock.calls.length).to.eq(0);
820-
expect(auth._getPasswordPolicy()).to.be.null;
810+
expect(auth._getPasswordPolicyInternal()).to.be.null;
821811
});
822812

823813
it('does not update the cached password policy upon successful sign up when there is an existing policy cache', async () => {
@@ -828,7 +818,7 @@ describe('core/strategies/email_and_password/createUserWithEmailAndPassword', ()
828818
).to.be.fulfilled;
829819

830820
expect(policyEndpointMock.calls.length).to.eq(1);
831-
expect(auth._getPasswordPolicy()).to.eql(cachedPasswordPolicy);
821+
expect(auth._getPasswordPolicyInternal()).to.eql(CACHED_PASSWORD_POLICY);
832822
});
833823

834824
context('handles password validation errors', () => {
@@ -848,43 +838,47 @@ describe('core/strategies/email_and_password/createUserWithEmailAndPassword', ()
848838
it('updates the cached password policy when password does not meet backend requirements', async () => {
849839
await auth._updatePasswordPolicy();
850840
expect(policyEndpointMock.calls.length).to.eq(1);
851-
expect(auth._getPasswordPolicy()).to.eql(cachedPasswordPolicy);
841+
expect(auth._getPasswordPolicyInternal()).to.eql(
842+
CACHED_PASSWORD_POLICY
843+
);
852844

853845
// Password policy changed after previous fetch.
854-
policyEndpointMock.response = passwordPolicyResponseRequireNumeric;
846+
policyEndpointMock.response = PASSWORD_POLICY_RESPONSE_REQUIRE_NUMERIC;
855847
await expect(
856848
createUserWithEmailAndPassword(auth, 'some-email', 'some-password')
857849
).to.be.rejectedWith(FirebaseError, PASSWORD_ERROR_MSG);
858850

859851
expect(policyEndpointMock.calls.length).to.eq(2);
860-
expect(auth._getPasswordPolicy()).to.eql(
861-
cachedPasswordPolicyRequireNumeric
852+
expect(auth._getPasswordPolicyInternal()).to.eql(
853+
CACHED_PASSWORD_POLICY_REQUIRE_NUMERIC
862854
);
863855
});
864856

865857
it('does not update the cached password policy upon error if policy has not previously been fetched', async () => {
866-
expect(auth._getPasswordPolicy()).to.be.null;
858+
expect(auth._getPasswordPolicyInternal()).to.be.null;
867859

868860
await expect(
869861
createUserWithEmailAndPassword(auth, 'some-email', 'some-password')
870862
).to.be.rejectedWith(FirebaseError, PASSWORD_ERROR_MSG);
871863

872864
expect(policyEndpointMock.calls.length).to.eq(0);
873-
expect(auth._getPasswordPolicy()).to.be.null;
865+
expect(auth._getPasswordPolicyInternal()).to.be.null;
874866
});
875867

876868
it('does not update the cached password policy upon error if tenant changes and policy has not previously been fetched', async () => {
877869
auth.tenantId = TEST_TENANT_ID;
878870
await auth._updatePasswordPolicy();
879871
expect(policyEndpointMockWithTenant.calls.length).to.eq(1);
880-
expect(auth._getPasswordPolicy()).to.eql(cachedPasswordPolicy);
872+
expect(auth._getPasswordPolicyInternal()).to.eql(
873+
CACHED_PASSWORD_POLICY
874+
);
881875

882876
auth.tenantId = TEST_REQUIRE_NUMERIC_TENANT_ID;
883877
await expect(
884878
createUserWithEmailAndPassword(auth, 'some-email', 'some-password')
885879
).to.be.rejectedWith(FirebaseError, PASSWORD_ERROR_MSG);
886880
expect(policyEndpointMockWithOtherTenant.calls.length).to.eq(0);
887-
expect(auth._getPasswordPolicy()).to.be.undefined;
881+
expect(auth._getPasswordPolicyInternal()).to.be.undefined;
888882
});
889883
});
890884
});

packages/auth/src/core/strategies/email_and_password.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ export async function createUserWithEmailAndPassword(
313313
if (
314314
error.code ===
315315
`auth/${AuthErrorCode.PASSWORD_DOES_NOT_MEET_REQUIREMENTS}` &&
316-
authInternal._getPasswordPolicy()
316+
authInternal._getPasswordPolicyInternal()
317317
) {
318318
await authInternal._updatePasswordPolicy();
319319
}

packages/auth/src/model/auth.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import { PopupRedirectResolverInternal } from './popup_redirect';
3232
import { UserInternal } from './user';
3333
import { ClientPlatform } from '../core/util/version';
3434
import { RecaptchaConfig } from '../platform_browser/recaptcha/recaptcha';
35+
import { PasswordPolicyInternal } from './password_policy';
3536

3637
export type AppName = string;
3738
export type ApiKey = string;
@@ -87,7 +88,7 @@ export interface AuthInternal extends Auth {
8788
_stopProactiveRefresh(): void;
8889
_getPersistence(): string;
8990
_getRecaptchaConfig(): RecaptchaConfig | null;
90-
_getPasswordPolicy(): PasswordPolicy | null;
91+
_getPasswordPolicyInternal(): PasswordPolicyInternal | null;
9192
_updatePasswordPolicy(): Promise<void>;
9293
_logFramework(framework: string): void;
9394
_getFrameworks(): readonly string[];

0 commit comments

Comments
 (0)