1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-09 05:00:10 +00:00

update storybook stories and docs

This commit is contained in:
rr-bw
2025-04-25 16:00:29 -07:00
parent 7ee1122ae1
commit eb84a9b06c
2 changed files with 129 additions and 29 deletions

View File

@@ -6,9 +6,10 @@ import * as stories from "./input-password.stories.ts";
# InputPassword Component
The `InputPasswordComponent` allows a user to enter master password related credentials. On
submission it creates a master key, master key hash, and emits those values to the parent (along
with the other values found in `PasswordInputResult`).
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 found in `PasswordInputResult`).
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
@@ -16,12 +17,27 @@ the parent component to act on those values as needed.
<br />
## Table of Contents
- [@Inputs](#inputs)
- [@Outputs](#outputs)
- [The InputPasswordFlow](#the-inputpasswordflow)
- [HTML - Form Fields](#html---form-fields)
- [TypeScript - Credential Generation](#typescript---credential-generation)
- [Difference between AccountRegistration and SetInitialPasswordAuthedUser](#difference-between-accountregistration-and-setinitialpasswordautheduser)
- [Validation](#validation)
- [Submit Logic](#submit-logic)
- [Example](#example)
<br />
## `@Input()`'s
**Required**
- `inputPasswordFlow` - the parent component must provide the correct flow, which is used to
determine which form input elements will be displayed in the UI.
- `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.
@@ -29,13 +45,15 @@ the parent component to act on those values as needed.
- `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` true.
The primary button will also show a spinner if `loading` is true.
- `masterPasswordPolicyOptions` - used to display and enforce master password policy requirements.
- `inlineButtons` - takes a boolean that determines if the button(s) should be displayed inline (as
opposed to full-width)
- `primaryButtonText` - takes a `Translation` object that can be used as button text
- `secondaryButtonText` - takes a `Translation` object that can be used as button text
<br />
## `@Output()`'s
- `onPasswordFormSubmit` - on form submit, emits a `PasswordInputResult` object
@@ -45,25 +63,31 @@ the parent component to act on those values as needed.
<br />
## Form Input Fields
## The `InputPasswordFlow`
The `InputPasswordComponent` can handle up to 6 different form input fields, depending on the
`InputPasswordFlow` provided by the parent component.
The `InputPasswordFlow` is a crucial and required `@Input` that influences both the HTML and the
credential generation logic of the component.
**InputPasswordFlow.SetInitialPassword**
<br />
### HTML - Form Fields
The `InputPasswordFlow` determines which form fields get displayed in the UI.
**`InputPasswordFlow.AccountRegistration`** and **`InputPasswordFlow.SetInitialPasswordAuthedUser`**
- Input: New password
- Input: Confirm new password
- Input: Hint
- Checkbox: Check for breaches
**InputPasswordFlow.ChangePassword**
**`InputPasswordFlow.ChangePassword`**
Includes everything above, plus:
- Input: Current password (as the first element in the UI)
**InputPasswordFlow.ChangePasswordWithOptionalUserKeyRotation**
**`InputPasswordFlow.ChangePasswordWithOptionalUserKeyRotation`**
Includes everything above, plus:
@@ -71,17 +95,88 @@ Includes everything above, plus:
<br />
### TypeScript - Credential Generation
- The `AccountRegistration` 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`).
<br />
### Difference between `AccountRegistration` and `SetInitialPasswordAuthedUser`
These two flows are similar in that they display the same form fields and only generate new
credentials, but we need to keep them separate for the following reasons:
- `AccountRegistration` 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:
- If there is no `userId` passed down from the parent, we default to `DEFAULT_KDF_CONFIG`
- If there is a `userId` passed down from the parent, we get the `kdfConfig` from state using the
`userId`
That said, we cannot mark the `userId` as a required via `@Input({ required: true })` because
`AccountRegistration` flows will not have a `userId`. But we still want to require a `userId` in a
`SetInitialPasswordAuthedUser` flow. Therefore the `InputPasswordComponent` has init logic that
ensures the following:
- If the passed down flow is `AccountRegistration`, require that the parent **MUST NOT** have passed
down a `userId`
- If the passed down flow is `SetInitialPasswordAuthedUser` require that the parent must also have
passed down a `userId`
If either of these checks is not met, the component throws to alert the dev of a mistake.
<br />
## Validation
Validation ensures that:
Form validators ensure that:
- The current password and new password are NOT the same
- 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 />
## On Submit
## Submit Logic
When the form is submitted, the `InputPasswordComponent` does the following in order:
@@ -113,12 +208,8 @@ export interface PasswordInputResult {
}
```
# Example - InputPasswordFlow.SetInitialPassword
# Example
<Story of={stories.SetInitialPassword} />
**`InputPasswordFlow.ChangePasswordWithOptionalUserKeyRotation`**
<br />
# Example - With Policy Requrements
<Story of={stories.WithPolicies} />
<Story of={stories.ChangePasswordWithOptionalUserKeyRotation} />

View File

@@ -154,7 +154,16 @@ export const AccountRegistration: Story = {
render: (args) => ({
props: args,
template: `
<auth-input-password [inputPasswordFlow]="InputPasswordFlow.AccountRegistration"></auth-input-password>
<auth-input-password [flow]="InputPasswordFlow.AccountRegistration"></auth-input-password>
`,
}),
};
export const SetInitialPasswordAuthedUser: Story = {
render: (args) => ({
props: args,
template: `
<auth-input-password [flow]="InputPasswordFlow.SetInitialPasswordAuthedUser"></auth-input-password>
`,
}),
};
@@ -163,7 +172,7 @@ export const ChangePassword: Story = {
render: (args) => ({
props: args,
template: `
<auth-input-password [inputPasswordFlow]="InputPasswordFlow.ChangePassword"></auth-input-password>
<auth-input-password [flow]="InputPasswordFlow.ChangePassword"></auth-input-password>
`,
}),
};
@@ -173,7 +182,7 @@ export const ChangePasswordWithOptionalUserKeyRotation: Story = {
props: args,
template: `
<auth-input-password
[inputPasswordFlow]="InputPasswordFlow.ChangePasswordWithOptionalUserKeyRotation"
[flow]="InputPasswordFlow.ChangePasswordWithOptionalUserKeyRotation"
></auth-input-password>
`,
}),
@@ -184,7 +193,7 @@ export const WithPolicies: Story = {
props: args,
template: `
<auth-input-password
[inputPasswordFlow]="InputPasswordFlow.SetInitialPasswordAuthedUser"
[flow]="InputPasswordFlow.SetInitialPasswordAuthedUser"
[masterPasswordPolicyOptions]="masterPasswordPolicyOptions"
></auth-input-password>
`,
@@ -196,7 +205,7 @@ export const SecondaryButton: Story = {
props: args,
template: `
<auth-input-password
[inputPasswordFlow]="InputPasswordFlow.AccountRegistration"
[flow]="InputPasswordFlow.AccountRegistration"
[secondaryButtonText]="{ key: 'cancel' }"
(onSecondaryButtonClick)="onSecondaryButtonClick()"
></auth-input-password>
@@ -209,7 +218,7 @@ export const SecondaryButtonWithPlaceHolderText: Story = {
props: args,
template: `
<auth-input-password
[inputPasswordFlow]="InputPasswordFlow.AccountRegistration"
[flow]="InputPasswordFlow.AccountRegistration"
[secondaryButtonText]="{ key: 'backTo', placeholders: ['homepage'] }"
(onSecondaryButtonClick)="onSecondaryButtonClick()"
></auth-input-password>
@@ -222,7 +231,7 @@ export const InlineButton: Story = {
props: args,
template: `
<auth-input-password
[inputPasswordFlow]="InputPasswordFlow.AccountRegistration"
[flow]="InputPasswordFlow.AccountRegistration"
[inlineButtons]="true"
></auth-input-password>
`,
@@ -234,7 +243,7 @@ export const InlineButtons: Story = {
props: args,
template: `
<auth-input-password
[inputPasswordFlow]="InputPasswordFlow.AccountRegistration"
[flow]="InputPasswordFlow.AccountRegistration"
[secondaryButtonText]="{ key: 'cancel' }"
[inlineButtons]="true"
(onSecondaryButtonClick)="onSecondaryButtonClick()"