1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-08 12:40:26 +00:00

[PM-18721] update storybook docs

This commit is contained in:
rr-bw
2025-05-08 10:04:24 -07:00
parent 1a209f2f8c
commit 7d1edb9999

View File

@@ -6,14 +6,21 @@ import * as stories from "./input-password.stories.ts";
# InputPassword Component
The `InputPasswordComponent` allows a user to enter master password related credentials. On form
submission, the component creates cryptographic properties (`newMasterKey`,
`newServerMasterKeyHash`, etc.) and emits those properties to the parent (along with the other
values defined in `PasswordInputResult`).
The `InputPasswordComponent` allows a user to enter master password related credentials.
Specifically, it does the following:
The component is intended for re-use in different scenarios throughout the application. Therefore it
is mostly presentational and simply emits values rather than acting on them itself. It is the job of
the parent component to act on those values as needed.
1. Displays form fields in the UI
2. Validates form fields
3. Generates cryptographic properties based on the form inputs (e.g. `newMasterKey`,
`newServerMasterKeyHash`, etc.)
4. Emits the generated properties to the parent component
The `InputPasswordComponent` is central to our set/change password flows. It allows us to keep our
form UI and validation logic consistent across our many set/change password flows.
The component is intended for re-use in different set/change password scenarios throughout the
application. Therefore it is mostly presentational and simply emits values rather than acting on
them itself. It is the job of the parent component to act on those values as needed.
<br />
@@ -22,11 +29,13 @@ the parent component to act on those values as needed.
- [@Inputs](#inputs)
- [@Outputs](#outputs)
- [The InputPasswordFlow](#the-inputpasswordflow)
- [Use Cases](#use-cases)
- [HTML - Form Fields](#html---form-fields)
- [TypeScript - Credential Generation](#typescript---credential-generation)
- [Difference between SetInitialPasswordAccountRegistration and SetInitialPasswordAuthedUser](#difference-between-setinitialpasswordaccountregistration-and-setinitialpasswordautheduser)
- [Validation](#validation)
- [Submit Logic](#submit-logic)
- [Submitting From a Parent Dialog Component](#submitting-from-a-parent-dialog-component)
- [Example](#example)
<br />
@@ -37,12 +46,23 @@ the parent component to act on those values as needed.
- `flow` - the parent component must provide an `InputPasswordFlow`, which is used to determine
which form input elements will be displayed in the UI and which cryptographic keys will be created
and emitted.
- `email` - the parent component must provide an email so that the `InputPasswordComponent` can
create a master key.
and emitted. [Click here](#the-inputpasswordflow) to learn more about the different
`InputPasswordFlow` options.
**Optional (sometimes)**
These two `@Inputs` are optional on some flows, but required on others. Therefore these `@Inputs`
are not marked as `{ required: true }`, but there _is_ component logic that ensures (requires) that
the `email` and/or `userId` is present in certain flows, while not present in other flows.
- `email` - allows the `InputPasswordComponent` to generate a master key
- `userId` - allows the `InputPasswordComponent` to do things like get the user's `kdfConfig`,
decrypt the user key, and perform validation prior to user a key rotation on the parent
**Optional**
These `@Inputs` are truly optional.
- `loading` - a boolean used to indicate that the parent component is performing some
long-running/async operation and that the form should be disabled until the operation is complete.
The primary button will also show a spinner if `loading` is true.
@@ -57,6 +77,7 @@ the parent component to act on those values as needed.
## `@Output()`'s
- `onPasswordFormSubmit` - on form submit, emits a `PasswordInputResult` object
([see more below](#submit-logic)).
- `onSecondaryButtonClick` - on click, emits a notice that the secondary button has been clicked.
The parent component can listen for this event and take some custom action as needed (go back,
cancel, logout, etc.)
@@ -66,44 +87,88 @@ the parent component to act on those values as needed.
## The `InputPasswordFlow`
The `InputPasswordFlow` is a crucial and required `@Input` that influences both the HTML and the
credential generation logic of the component.
credential generation logic of the component. It is important for the dev to understand when to use
each flow.
<br />
### Use Cases
**`SetInitialPasswordAccountRegistration`**
Used in scenarios where we have no existing user, and thus NO active account `userId`:
- Standard Account Registration
- Email Invite Account Registration
- Trial Initiation Account registration<br /><br />
**`SetInitialPasswordAuthedUser`**
Used in scenarios where we do have an existing and authed user, and thus an active account `userId`:
- A "just-in-time" (JIT) provisioned user joins a master password (MP) encryption org and must set
their initial password
- A "just-in-time" (JIT) provisioned user joins a trusted device encryption (TDE) org with a
starting role that requires them to have/set their initial password
- A note on JIT provisioned user flows:
- Even though a JIT provisioned user is a brand-new user who was “just” created, we consider
them to be an “existing authed user” _from the perspective of the set-password flow_. This is
because at the time they set their initial password, their account already exists in the
database (before setting their password) and they have already authenticated via SSO.
- The same is not true in the account registration flows above&mdash;that is, during account
registration when a user reaches the `/finish-signup` or `/trial-initiation` page to set their
initial password, their account does not yet exist in the database, and will only be created
once they set an initial password.
- An existing user in a TDE org logs in after the org admin upgraded the user to a role that now
requires them to have/set their initial password
- An existing user logs in after their org admin offboarded the org from TDE, and the user must now
have/set their initial password<br /><br />
**`ChangePassword`**
Used in scenarios where a we simply want simply offer the user the ability to change their password:
- User clicks an org email invite link an logs in with their password which does not meet the org's
policy requirements
- User logs in with password that does not meet the org's policy requirements
- User logs in after their password was reset via Account Recovery (and now they must change their
password)<br /><br />
**`ChangePasswordWithOptionalUserKeyRotation`**
Used in scenarios where we want to offer users the additional option of rotating their user key:
- Account Settings (Web) - change password screen
Note that the user key rotation itself does not happen on the `InputPasswordComponent`, but rather
on the parent component. The `InputPasswordComponent` simply emits a boolean value that indicates
whether or not the user key should be rotated.<br /><br />
**`ChangePasswordDelegation`**
Used in scenarios where one user changes the password for another user's account:
- Emergency Access Takeover
- Account Recovery<br /><br />
### HTML - Form Fields
The `InputPasswordFlow` determines which form fields get displayed in the UI.
**`InputPasswordFlow.SetInitialPasswordAccountRegistration`** and
**`InputPasswordFlow.SetInitialPasswordAuthedUser`**
- Input: New password
- Input: Confirm new password
- Input: Hint
- Checkbox: Check for breaches
**`InputPasswordFlow.ChangePassword`**
Includes everything above, plus:
- Input: Current password (as the first element in the UI)
**`InputPasswordFlow.ChangePasswordWithOptionalUserKeyRotation`**
Includes everything above, plus:
- Checkbox: Rotate account encryption key (as the last element in the UI)
Click through the individual Stories in Storybook to see how the `InputPassswordFlow` determines
which form field UI elements get displayed.
<br />
### TypeScript - Credential Generation
- The `SetInitialPasswordAccountRegistration` and `SetInitialPasswordAuthedUser` flows involve a
user setting their password for the first time. Therefore on submit the component will only
generate new credentials (`newMasterKey`) and not current credentials (`currentMasterKey`).
- The `ChangePassword` and `ChangePasswordWithOptionalUserKeyRotation` flows both require the user
to enter a current password along with a new password. Therefore on submit the component will
generate current credentials (`currentMasterKey`) along with new credentials (`newMasterKey`).
- **`SetInitialPasswordAccountRegistration`** and **`SetInitialPasswordAuthedUser`**
- These flows involve a user setting their password for the first time. Therefore on submit the
component will only generate new credentials (`newMasterKey`) and not current credentials
(`currentMasterKey`).<br /><br />
- **`ChangePassword`** and **`ChangePasswordWithOptionalUserKeyRotation`**
- These flows both require the user to enter a current password along with a new password.
Therefore on submit the component will generate current credentials (`currentMasterKey`) along
with new credentials (`newMasterKey`).<br /><br />
- **`ChangePasswordDelegation`**
- This flow does not generate any credentials, but simply validates the new password and emits it
up to the parent.
<br />
@@ -114,32 +179,8 @@ credentials, but we need to keep them separate for the following reasons:
- `SetInitialPasswordAccountRegistration` involves scenarios where we have no existing user, and
**thus NO active account `userId`**:
- Standard Account Registration
- Email Invite Account Registration
- Trial Initiation Account Registration
<br />
- `SetInitialPasswordAuthedUser` involves scenarios where we do have an existing and authed user,
and **thus an active account `userId`**:
- A "just-in-time" (JIT) provisioned user joins a master password (MP) encryption org and must set
their initial password
- A "just-in-time" (JIT) provisioned user joins a trusted device encryption (TDE) org with a
starting role that requires them to have/set their initial password
- A note on JIT provisioned user flows:
- Even though a JIT provisioned user is a brand-new user who was “just” created, we consider
them to be an “existing authed user” _from the perspective of the set-password flow_. This
is because at the time they set their initial password, their account already exists in the
database (before setting their password) and they have already authenticated via SSO.
- The same is not true in the account registration flows above&mdash;that is, during account
registration when a user reaches the `/finish-signup` or `/trial-initiation` page to set
their initial password, their account does not yet exist in the database, and will only be
created once they set an initial password.
- An existing user in a TDE org logs in after the org admin upgraded the user to a role that now
requires them to have/set their initial password
- An existing user logs in after their org admin offboarded the org from TDE, and the user must
now have/set their initial password
The presence or absence of an active account `userId` is important because it determines how we get
the correct `kdfConfig` prior to key generation:
@@ -170,11 +211,6 @@ Form validators ensure that:
- The new password and confirmed new password are the same
- The new password and password hint are NOT the same
Additional submit logic validation ensures that:
- The new password adheres to any enforced master password policy options (that were passed down
from the parent)
<br />
## Submit Logic
@@ -183,9 +219,10 @@ When the form is submitted, the `InputPasswordComponent` does the following in o
1. Verifies inputs:
- Checks that the current password is correct (if it was required in the flow)
- Checks if the new password is found in a breach and warns the user if so (if the user selected
the checkbox)
- Checks that the new password meets any master password policy requirements enforced by an org
- Checks that the new password is not weak or found in any breaches (if the user selected the
checkbox)
- Checks that the new password adheres to any enforced master password policies that were
optionally passed down by the parent
2. Uses the form inputs to create cryptographic properties (`newMasterKey`,
`newServerMasterKeyHash`, etc.)
3. Emits those cryptographic properties up to the parent (along with other values defined in
@@ -209,6 +246,67 @@ export interface PasswordInputResult {
}
```
## Submitting From a Parent Dialog Component
Some of our set/change password flows use dialogs, such as Emergency Access Takeover and Account
Recovery. These are covered by the `ChangePasswordDelegation` flow. Because dialogs have their own
buttons, we don't want to display an additional Submit button in the `InputPasswordComponent` when
embedded a dialog.
Therefore we do the following:
- The `InputPasswordComponent` hides the button in the UI and exposes its `submit()` method as a
public method.
- The parent dialog component can then access this method via `@ViewChild()`.
- When the user clicks the primary button on the parent dialog, we call the `submit()` method on the
`InputPasswordComponent`.
```html
<!-- emergency-access-takeover-dialog.component.html -->
<bit-dialog dialogSize="large">
<span bitDialogTitle><!-- ... --></span>
<div bitDialogContent>
<auth-input-password
[flow]="inputPasswordFlow"
[masterPasswordPolicyOptions]="masterPasswordPolicyOptions"
(onPasswordFormSubmit)="handlePasswordFormSubmit($event)"
></auth-input-password>
</div>
<ng-container bitDialogFooter>
<button type="button" bitButton buttonType="primary" (click)="handlePrimaryButtonClick()">
{{ "save" | i18n }}
</button>
<button type="button" bitButton buttonType="secondary" bitDialogClose>
{{ "cancel" | i18n }}
</button>
</ng-container>
</bit-dialog>
```
```typescript
// emergency-access-takeover-dialog.component.ts
export class EmergencyAccessTakeoverDialogComponent implements OnInit {
@ViewChild(InputPasswordComponent)
inputPasswordComponent: InputPasswordComponent;
// ...
handlePrimaryButtonClick = async () => {
await this.inputPasswordComponent.submit();
};
async handlePasswordFormSubmit(passwordInputResult: PasswordInputResult) {
// ... run logic that handles the `PasswordInputResult` object emission
}
}
```
<br />
# Example
**`InputPasswordFlow.ChangePasswordWithOptionalUserKeyRotation`**