From eb84a9b06c21e166c9d950665a3c8a974fdbcf63 Mon Sep 17 00:00:00 2001
From: rr-bw <102181210+rr-bw@users.noreply.github.com>
Date: Fri, 25 Apr 2025 16:00:29 -0700
Subject: [PATCH] update storybook stories and docs
---
.../angular/input-password/input-password.mdx | 133 +++++++++++++++---
.../input-password/input-password.stories.ts | 25 ++--
2 files changed, 129 insertions(+), 29 deletions(-)
diff --git a/libs/auth/src/angular/input-password/input-password.mdx b/libs/auth/src/angular/input-password/input-password.mdx
index f12dd7de23b..39be25aeeaf 100644
--- a/libs/auth/src/angular/input-password/input-password.mdx
+++ b/libs/auth/src/angular/input-password/input-password.mdx
@@ -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.
+## 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)
+
+
+
## `@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
+
+
## `@Output()`'s
- `onPasswordFormSubmit` - on form submit, emits a `PasswordInputResult` object
@@ -45,25 +63,31 @@ the parent component to act on those values as needed.
-## 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**
+
+
+### 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:
+### 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`).
+
+
+
+### 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
+
+
+
+- `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—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.
+
+
+
## 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)
+
-## 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
-
+**`InputPasswordFlow.ChangePasswordWithOptionalUserKeyRotation`**
-
-
-# Example - With Policy Requrements
-
-
+
diff --git a/libs/auth/src/angular/input-password/input-password.stories.ts b/libs/auth/src/angular/input-password/input-password.stories.ts
index 6cd387fe2ac..c8f74a73ba0 100644
--- a/libs/auth/src/angular/input-password/input-password.stories.ts
+++ b/libs/auth/src/angular/input-password/input-password.stories.ts
@@ -154,7 +154,16 @@ export const AccountRegistration: Story = {
render: (args) => ({
props: args,
template: `
-
+
+ `,
+ }),
+};
+
+export const SetInitialPasswordAuthedUser: Story = {
+ render: (args) => ({
+ props: args,
+ template: `
+
`,
}),
};
@@ -163,7 +172,7 @@ export const ChangePassword: Story = {
render: (args) => ({
props: args,
template: `
-
+
`,
}),
};
@@ -173,7 +182,7 @@ export const ChangePasswordWithOptionalUserKeyRotation: Story = {
props: args,
template: `
`,
}),
@@ -184,7 +193,7 @@ export const WithPolicies: Story = {
props: args,
template: `
`,
@@ -196,7 +205,7 @@ export const SecondaryButton: Story = {
props: args,
template: `
@@ -209,7 +218,7 @@ export const SecondaryButtonWithPlaceHolderText: Story = {
props: args,
template: `
@@ -222,7 +231,7 @@ export const InlineButton: Story = {
props: args,
template: `
`,
@@ -234,7 +243,7 @@ export const InlineButtons: Story = {
props: args,
template: `