Skip to content

Add support for validating passwords against the password policy in auth #7514

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Aug 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
c622c1f
Add password policy endpoint in auth
ch5zzy Jun 26, 2023
1741fef
Implement password policy caching in auth object
ch5zzy Jun 30, 2023
8be31e5
Update password policy in sign up flow (#7392)
ch5zzy Jul 6, 2023
3a7c4a3
Define validatePassword endpoint for public API (#7409)
ch5zzy Jul 6, 2023
e40b1e7
Define internal password policy typings (#7446)
ch5zzy Jul 11, 2023
ca662bd
Define implementation of internal password policy class (#7447)
ch5zzy Jul 12, 2023
97f46f5
Update Auth to use PasswordPolicyImpl for validation (#7451)
ch5zzy Jul 12, 2023
d40ca7d
Implement validatePassword endpoint and fix PasswordPolicy type to ma…
ch5zzy Jul 13, 2023
1025840
Update password policy cache in reset password flow when the password…
ch5zzy Jul 14, 2023
1532012
Update password policy cache in sign-in flow when the password does n…
ch5zzy Jul 17, 2023
5ef98ff
Include the character option statuses even when an empty string is in…
ch5zzy Jul 17, 2023
a70a9ac
Update auth demo to include password validation (#7472)
ch5zzy Jul 18, 2023
defdb66
Add enforcementState and forceUpgradeOnSignin to PasswordPolicy type …
ch5zzy Jul 20, 2023
2f51103
Fix bug when non-alphanumeric characters is undefined in password pol…
ch5zzy Jul 20, 2023
57ed176
Update password validation in auth demo to be in each auth flow with …
ch5zzy Jul 25, 2023
137b0b7
Use a default minimum password length of 6 (#7499)
ch5zzy Jul 28, 2023
642259b
Add password policy integration tests (#7489)
ch5zzy Aug 1, 2023
3aa18f7
Add changeset for password policy (#7511)
ch5zzy Aug 1, 2023
58460a1
Fix password policy recaching in sign up (#7506)
ch5zzy Aug 1, 2023
5f4b375
Merge branch 'master' into password-policy
ch5zzy Aug 1, 2023
de6e231
Fix formatting in auth tests
ch5zzy Aug 1, 2023
fcb7049
Fix spelling for updatePasswordCharacterOptionsStatuses
ch5zzy Aug 4, 2023
7ad5a8d
Merge branch 'master' into password-policy
ch5zzy Aug 4, 2023
40e3f69
Add _castAuth import back to index.ts and fix broken tests
ch5zzy Aug 4, 2023
1bb7d0a
Update docstrings for PasswordPolicy and PasswordValidationStatus
ch5zzy Aug 7, 2023
fbd258c
Add 'firebase' to changeset
ch5zzy Aug 7, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/thick-lions-divide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@firebase/auth': minor
'firebase': minor
---

Add a validatePassword method for validating passwords against the password policy configured for the project or a tenant. This method returns a status object that can be used to display the requirements of the password policy and whether each one was met.
30 changes: 30 additions & 0 deletions common/api-review/auth.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,33 @@ export interface ParsedToken {
'sub'?: string;
}

// @public
export interface PasswordPolicy {
readonly allowedNonAlphanumericCharacters: string;
readonly customStrengthOptions: {
readonly minPasswordLength?: number;
readonly maxPasswordLength?: number;
readonly containsLowercaseLetter?: boolean;
readonly containsUppercaseLetter?: boolean;
readonly containsNumericCharacter?: boolean;
readonly containsNonAlphanumericCharacter?: boolean;
};
readonly enforcementState: string;
readonly forceUpgradeOnSignin: boolean;
}

// @public
export interface PasswordValidationStatus {
readonly containsLowercaseLetter?: boolean;
readonly containsNonAlphanumericCharacter?: boolean;
readonly containsNumericCharacter?: boolean;
readonly containsUppercaseLetter?: boolean;
readonly isValid: boolean;
readonly meetsMaxPasswordLength?: boolean;
readonly meetsMinPasswordLength?: boolean;
readonly passwordPolicy: PasswordPolicy;
}

// @public
export interface Persistence {
readonly type: 'SESSION' | 'LOCAL' | 'NONE';
Expand Down Expand Up @@ -869,6 +896,9 @@ export interface UserMetadata {
// @public
export type UserProfile = Record<string, unknown>;

// @public
export function validatePassword(auth: Auth, password: string): Promise<PasswordValidationStatus>;

// @public
export function verifyBeforeUpdateEmail(user: User, newEmail: string, actionCodeSettings?: ActionCodeSettings | null): Promise<void>;

Expand Down
36 changes: 36 additions & 0 deletions docs-devsite/auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ Firebase Authentication
| [signOut(auth)](./auth.md#signout) | Signs out the current user. |
| [updateCurrentUser(auth, user)](./auth.md#updatecurrentuser) | Asynchronously sets the provided user as [Auth.currentUser](./auth.auth.md#authcurrentuser) on the [Auth](./auth.auth.md#auth_interface) instance. |
| [useDeviceLanguage(auth)](./auth.md#usedevicelanguage) | Sets the current language to the default device/browser preference. |
| [validatePassword(auth, password)](./auth.md#validatepassword) | Validates the password against the password policy configured for the project or tenant. |
| [verifyPasswordResetCode(auth, code)](./auth.md#verifypasswordresetcode) | Checks a password reset code sent to the user by email or other out-of-band mechanism. |
| <b>function(link...)</b> |
| [parseActionCodeURL(link)](./auth.md#parseactioncodeurl) | Parses the email action link string and returns an [ActionCodeURL](./auth.actioncodeurl.md#actioncodeurl_class) if the link is valid, otherwise returns null. |
Expand Down Expand Up @@ -124,6 +125,8 @@ Firebase Authentication
| [MultiFactorUser](./auth.multifactoruser.md#multifactoruser_interface) | An interface that defines the multi-factor related properties and operations pertaining to a [User](./auth.user.md#user_interface)<!-- -->. |
| [OAuthCredentialOptions](./auth.oauthcredentialoptions.md#oauthcredentialoptions_interface) | Defines the options for initializing an [OAuthCredential](./auth.oauthcredential.md#oauthcredential_class)<!-- -->. |
| [ParsedToken](./auth.parsedtoken.md#parsedtoken_interface) | Interface representing a parsed ID token. |
| [PasswordPolicy](./auth.passwordpolicy.md#passwordpolicy_interface) | A structure specifying password policy requirements. |
| [PasswordValidationStatus](./auth.passwordvalidationstatus.md#passwordvalidationstatus_interface) | A structure indicating which password policy requirements were met or violated and what the requirements are. |
| [Persistence](./auth.persistence.md#persistence_interface) | An interface covering the possible persistence mechanism types. |
| [PhoneMultiFactorAssertion](./auth.phonemultifactorassertion.md#phonemultifactorassertion_interface) | The class for asserting ownership of a phone second factor. Provided by [PhoneMultiFactorGenerator.assertion()](./auth.phonemultifactorgenerator.md#phonemultifactorgeneratorassertion)<!-- -->. |
| [PhoneMultiFactorEnrollInfoOptions](./auth.phonemultifactorenrollinfooptions.md#phonemultifactorenrollinfooptions_interface) | Options used for enrolling a second factor. |
Expand Down Expand Up @@ -1077,6 +1080,39 @@ export declare function useDeviceLanguage(auth: Auth): void;

void

## validatePassword()

Validates the password against the password policy configured for the project or tenant.

If no tenant ID is set on the `Auth` instance, then this method will use the password policy configured for the project. Otherwise, this method will use the policy configured for the tenant. If a password policy has not been configured, then the default policy configured for all projects will be used.

If an auth flow fails because a submitted password does not meet the password policy requirements and this method has previously been called, then this method will use the most recent policy available when called again.

<b>Signature:</b>

```typescript
export declare function validatePassword(auth: Auth, password: string): Promise<PasswordValidationStatus>;
```

### Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| auth | [Auth](./auth.auth.md#auth_interface) | The [Auth](./auth.auth.md#auth_interface) instance. |
| password | string | The password to validate. |

<b>Returns:</b>

Promise&lt;[PasswordValidationStatus](./auth.passwordvalidationstatus.md#passwordvalidationstatus_interface)<!-- -->&gt;

### Example


```javascript
validatePassword(auth, 'some-password');

```

## verifyPasswordResetCode()

Checks a password reset code sent to the user by email or other out-of-band mechanism.
Expand Down
75 changes: 75 additions & 0 deletions docs-devsite/auth.passwordpolicy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
Project: /docs/reference/js/_project.yaml
Book: /docs/reference/_book.yaml
page_type: reference

{% comment %}
DO NOT EDIT THIS FILE!
This is generated by the JS SDK team, and any local changes will be
overwritten. Changes should be made in the source code at
https://github.com/firebase/firebase-js-sdk
{% endcomment %}

# PasswordPolicy interface
A structure specifying password policy requirements.

<b>Signature:</b>

```typescript
export interface PasswordPolicy
```

## Properties

| Property | Type | Description |
| --- | --- | --- |
| [allowedNonAlphanumericCharacters](./auth.passwordpolicy.md#passwordpolicyallowednonalphanumericcharacters) | string | List of characters that are considered non-alphanumeric during validation. |
| [customStrengthOptions](./auth.passwordpolicy.md#passwordpolicycustomstrengthoptions) | { readonly minPasswordLength?: number; readonly maxPasswordLength?: number; readonly containsLowercaseLetter?: boolean; readonly containsUppercaseLetter?: boolean; readonly containsNumericCharacter?: boolean; readonly containsNonAlphanumericCharacter?: boolean; } | Requirements enforced by this password policy. |
| [enforcementState](./auth.passwordpolicy.md#passwordpolicyenforcementstate) | string | The enforcement state of the policy. Can be 'OFF' or 'ENFORCE'. |
| [forceUpgradeOnSignin](./auth.passwordpolicy.md#passwordpolicyforceupgradeonsignin) | boolean | Whether existing passwords must meet the policy. |

## PasswordPolicy.allowedNonAlphanumericCharacters

List of characters that are considered non-alphanumeric during validation.

<b>Signature:</b>

```typescript
readonly allowedNonAlphanumericCharacters: string;
```

## PasswordPolicy.customStrengthOptions

Requirements enforced by this password policy.

<b>Signature:</b>

```typescript
readonly customStrengthOptions: {
readonly minPasswordLength?: number;
readonly maxPasswordLength?: number;
readonly containsLowercaseLetter?: boolean;
readonly containsUppercaseLetter?: boolean;
readonly containsNumericCharacter?: boolean;
readonly containsNonAlphanumericCharacter?: boolean;
};
```

## PasswordPolicy.enforcementState

The enforcement state of the policy. Can be 'OFF' or 'ENFORCE'.

<b>Signature:</b>

```typescript
readonly enforcementState: string;
```

## PasswordPolicy.forceUpgradeOnSignin

Whether existing passwords must meet the policy.

<b>Signature:</b>

```typescript
readonly forceUpgradeOnSignin: boolean;
```
112 changes: 112 additions & 0 deletions docs-devsite/auth.passwordvalidationstatus.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
Project: /docs/reference/js/_project.yaml
Book: /docs/reference/_book.yaml
page_type: reference

{% comment %}
DO NOT EDIT THIS FILE!
This is generated by the JS SDK team, and any local changes will be
overwritten. Changes should be made in the source code at
https://github.com/firebase/firebase-js-sdk
{% endcomment %}

# PasswordValidationStatus interface
A structure indicating which password policy requirements were met or violated and what the requirements are.

<b>Signature:</b>

```typescript
export interface PasswordValidationStatus
```

## Properties

| Property | Type | Description |
| --- | --- | --- |
| [containsLowercaseLetter](./auth.passwordvalidationstatus.md#passwordvalidationstatuscontainslowercaseletter) | boolean | Whether the password contains a lowercase letter, or undefined if not required. |
| [containsNonAlphanumericCharacter](./auth.passwordvalidationstatus.md#passwordvalidationstatuscontainsnonalphanumericcharacter) | boolean | Whether the password contains a non-alphanumeric character, or undefined if not required. |
| [containsNumericCharacter](./auth.passwordvalidationstatus.md#passwordvalidationstatuscontainsnumericcharacter) | boolean | Whether the password contains a numeric character, or undefined if not required. |
| [containsUppercaseLetter](./auth.passwordvalidationstatus.md#passwordvalidationstatuscontainsuppercaseletter) | boolean | Whether the password contains an uppercase letter, or undefined if not required. |
| [isValid](./auth.passwordvalidationstatus.md#passwordvalidationstatusisvalid) | boolean | Whether the password meets all requirements. |
| [meetsMaxPasswordLength](./auth.passwordvalidationstatus.md#passwordvalidationstatusmeetsmaxpasswordlength) | boolean | Whether the password meets the maximum password length, or undefined if not required. |
| [meetsMinPasswordLength](./auth.passwordvalidationstatus.md#passwordvalidationstatusmeetsminpasswordlength) | boolean | Whether the password meets the minimum password length, or undefined if not required. |
| [passwordPolicy](./auth.passwordvalidationstatus.md#passwordvalidationstatuspasswordpolicy) | [PasswordPolicy](./auth.passwordpolicy.md#passwordpolicy_interface) | The policy used to validate the password. |

## PasswordValidationStatus.containsLowercaseLetter

Whether the password contains a lowercase letter, or undefined if not required.

<b>Signature:</b>

```typescript
readonly containsLowercaseLetter?: boolean;
```

## PasswordValidationStatus.containsNonAlphanumericCharacter

Whether the password contains a non-alphanumeric character, or undefined if not required.

<b>Signature:</b>

```typescript
readonly containsNonAlphanumericCharacter?: boolean;
```

## PasswordValidationStatus.containsNumericCharacter

Whether the password contains a numeric character, or undefined if not required.

<b>Signature:</b>

```typescript
readonly containsNumericCharacter?: boolean;
```

## PasswordValidationStatus.containsUppercaseLetter

Whether the password contains an uppercase letter, or undefined if not required.

<b>Signature:</b>

```typescript
readonly containsUppercaseLetter?: boolean;
```

## PasswordValidationStatus.isValid

Whether the password meets all requirements.

<b>Signature:</b>

```typescript
readonly isValid: boolean;
```

## PasswordValidationStatus.meetsMaxPasswordLength

Whether the password meets the maximum password length, or undefined if not required.

<b>Signature:</b>

```typescript
readonly meetsMaxPasswordLength?: boolean;
```

## PasswordValidationStatus.meetsMinPasswordLength

Whether the password meets the minimum password length, or undefined if not required.

<b>Signature:</b>

```typescript
readonly meetsMinPasswordLength?: boolean;
```

## PasswordValidationStatus.passwordPolicy

The policy used to validate the password.

<b>Signature:</b>

```typescript
readonly passwordPolicy: PasswordPolicy;
```
29 changes: 28 additions & 1 deletion packages/auth/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ firebase emulators:exec --project foo-bar --only auth "yarn test:integration:loc

### Integration testing with the production backend

Currently, MFA TOTP tests only run against the production backend (since they are not supported on the emulator yet).
Currently, MFA TOTP and password policy tests only run against the production backend (since they are not supported on the emulator yet).
Running against the backend also makes it a more reliable end-to-end test.

The TOTP tests require the following email/password combination to exist in the project, so if you are running this test against your test project, please create this user:
Expand All @@ -71,6 +71,33 @@ curl -H "Authorization: Bearer $(gcloud auth print-access-token)" -H "Conten
}'
```

The password policy tests require a tenant configured with a password policy that requires all options to exist in the project.

If you are running this test against your test project, please create the tenant and configure the policy with the following curl command:

```
curl -H "Authorization: Bearer $(gcloud auth print-access-token)" -H "Content-Type: application/json" -H "X-Goog-User-Project: ${PROJECT_ID}" -X POST https://identitytoolkit.googleapis.com/v2/projects/${PROJECT_ID}/tenants -d '{
"displayName": "passpol-tenant",
"passwordPolicyConfig": {
"passwordPolicyEnforcementState": "ENFORCE",
"passwordPolicyVersions": [
{
"customStrengthOptions": {
"minPasswordLength": 8,
"maxPasswordLength": 24,
"containsLowercaseCharacter": true,
"containsUppercaseCharacter": true,
"containsNumericCharacter": true,
"containsNonAlphanumericCharacter": true
}
}
]
}
}'
```

Replace the tenant ID `passpol-tenant-d7hha` in [test/integration/flows/password_policy.test.ts](https://github.com/firebase/firebase-js-sdk/blob/master/packages/auth/test/integration/flows/password_policy.test.ts) with the ID for the newly created tenant. The tenant ID can be found at the end of the `name` property in the response and is in the format `passpol-tenant-xxxxx`.

### Selenium Webdriver tests

These tests assume that you have both Firefox and Chrome installed on your
Expand Down
Loading