idp-admin 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,9 +6,10 @@ const errors_1 = require("../errors");
6
6
  const STATUSES = {
7
7
  CONFIRMED: "active",
8
8
  FORCE_CHANGE_PASSWORD: "password_change_required",
9
- RESET_REQUIRED: "password_change_required",
10
- // Cognito suspects the password leaked, so the user must set a new one
11
- COMPROMISED: "password_change_required",
9
+ // an admin reset the password; the user must use the forgot-password code to set a new one
10
+ RESET_REQUIRED: "password_reset_required",
11
+ // Cognito suspects the password leaked, so the user must reset it the same way
12
+ COMPROMISED: "password_reset_required",
12
13
  UNCONFIRMED: "unconfirmed",
13
14
  EXTERNAL_PROVIDER: "federated",
14
15
  };
@@ -27,6 +27,12 @@ export declare class FakeAdapter<const TAttr extends string = never> implements
27
27
  name?: string;
28
28
  attributes?: UserAttributes<TAttr>;
29
29
  }): IdpUser<TAttr>;
30
+ /**
31
+ * Requires the user to reset their password, as an admin reset at a real provider does, so their
32
+ * status becomes `password_reset_required` until a password is set. Only the fake can do this
33
+ * directly. A federated user is unaffected. Synchronous. @throws {UserNotFoundError}
34
+ */
35
+ requirePasswordReset(userId: string): void;
30
36
  createUser(input: AdapterCreateUserInput<TAttr>): Promise<IdpUser<TAttr>>;
31
37
  createUserWithInvite(input: AdapterInviteUserInput<TAttr>): Promise<IdpUser<TAttr>>;
32
38
  getUser(userId: string): Promise<IdpUser<TAttr>>;
@@ -33,6 +33,16 @@ class FakeAdapter {
33
33
  addFederatedUser(input) {
34
34
  return copy(this.#add(input, "federated"));
35
35
  }
36
+ /**
37
+ * Requires the user to reset their password, as an admin reset at a real provider does, so their
38
+ * status becomes `password_reset_required` until a password is set. Only the fake can do this
39
+ * directly. A federated user is unaffected. Synchronous. @throws {UserNotFoundError}
40
+ */
41
+ requirePasswordReset(userId) {
42
+ const user = this.#user(userId);
43
+ if (user.status !== "federated")
44
+ user.status = "password_reset_required";
45
+ }
36
46
  async createUser(input) {
37
47
  return copy(this.#add(input, "password_change_required"));
38
48
  }
package/dist/types.d.ts CHANGED
@@ -3,10 +3,12 @@
3
3
  *
4
4
  * - `active`: has a permanent password, or otherwise needs nothing before signing in
5
5
  * - `password_change_required`: has a temporary password and must set their own at next sign-in
6
+ * - `password_reset_required`: must reset their password through the provider's forgot-password flow;
7
+ * a temporary password won't sign them in
6
8
  * - `federated`: signs in through another identity provider (SSO), so has no password here
7
9
  * - `unconfirmed`: signed themselves up and hasn't confirmed their account yet
8
10
  */
9
- export type UserStatus = "active" | "password_change_required" | "federated" | "unconfirmed";
11
+ export type UserStatus = "active" | "password_change_required" | "password_reset_required" | "federated" | "unconfirmed";
10
12
  /**
11
13
  * Custom attributes, keyed by the names the app declared in its adapter's config. Values are strings,
12
14
  * because that is what providers store; convert other types yourself. With none declared, no key is
package/docs/cognito.md CHANGED
@@ -47,7 +47,8 @@ Every one of them can be limited to the pool's ARN, `arn:aws:cognito-idp:<region
47
47
  | Cognito `UserStatus` | `status` |
48
48
  |---|---|
49
49
  | `CONFIRMED` | `active` |
50
- | `FORCE_CHANGE_PASSWORD`, `RESET_REQUIRED`, `COMPROMISED` | `password_change_required` |
50
+ | `FORCE_CHANGE_PASSWORD` | `password_change_required` |
51
+ | `RESET_REQUIRED`, `COMPROMISED` | `password_reset_required` |
51
52
  | `UNCONFIRMED` | `unconfirmed` |
52
53
  | `EXTERNAL_PROVIDER` | `federated` |
53
54
  | anything else | `active` |
package/docs/fake.md CHANGED
@@ -26,7 +26,8 @@ const fake = new FakeAdapter({ attributes: ["company"] })
26
26
  - **Status:**
27
27
  - a user from `createUser` or `createUserWithInvite` starts as `password_change_required`
28
28
  - `setPassword` with `temporary: true` sets `password_change_required`, and with `temporary: false` sets `active`
29
- - a federated user stays `federated` whatever `setPassword` does, because they sign in elsewhere
29
+ - `requirePasswordReset` sets `password_reset_required`, until a password is set
30
+ - a federated user stays `federated` whatever `setPassword` or `requirePasswordReset` does, because they sign in elsewhere
30
31
  - **Disabling** changes `enabled` only; the status is kept.
31
32
  - **Lists** return items in creation order. A cursor is the offset of the next page's first item.
32
33
  - **Copies:** every user returned is a copy, so changing it doesn't change the stored user.
@@ -42,6 +43,14 @@ const admin = fake.addFederatedUser({ email: "admin@mc3.co", name: "An Admin", a
42
43
 
43
44
  Adds a user who signs in through another identity provider (SSO), with status `federated`. A real provider creates these when someone first signs in through SSO, so only the fake can add one directly. Synchronous. Throws `UserExistsError` if the email is taken.
44
45
 
46
+ ### `requirePasswordReset(userId)`
47
+
48
+ ```ts
49
+ fake.requirePasswordReset(user.id)
50
+ ```
51
+
52
+ Puts the user into `password_reset_required`, as an admin reset at a real provider does, so tests can cover that state. It lasts until a password is set with `setPassword`. A federated user is unaffected. Synchronous. Throws `UserNotFoundError` for an unknown id.
53
+
45
54
  ### `invitations`
46
55
 
47
56
  ```ts
package/docs/index.md CHANGED
@@ -106,13 +106,14 @@ type IdpUser<TAttr extends string = never> = {
106
106
  ### `UserStatus`
107
107
 
108
108
  ```ts
109
- type UserStatus = "active" | "password_change_required" | "federated" | "unconfirmed"
109
+ type UserStatus = "active" | "password_change_required" | "password_reset_required" | "federated" | "unconfirmed"
110
110
  ```
111
111
 
112
112
  | Value | Meaning |
113
113
  |---|---|
114
114
  | `active` | Has a permanent password, or otherwise needs nothing before signing in. |
115
115
  | `password_change_required` | Has a temporary password and must set their own at next sign-in. |
116
+ | `password_reset_required` | Must reset their password through the provider's forgot-password flow. A temporary password won't sign them in. Only some providers report it. |
116
117
  | `federated` | Signs in through another identity provider (SSO), so has no password here. |
117
118
  | `unconfirmed` | Signed themselves up and hasn't confirmed their account yet. |
118
119
 
package/docs/keycloak.md CHANGED
@@ -43,6 +43,7 @@ Also worth knowing: by default Keycloak requires a first and last name before an
43
43
  - `federated`: the user is linked to an identity provider (they signed in through SSO). This takes precedence over everything else.
44
44
  - `password_change_required`: the user has Keycloak's `UPDATE_PASSWORD` required action, which a temporary password sets.
45
45
  - `unconfirmed`: the user has the `VERIFY_EMAIL` required action.
46
+ - never `password_reset_required`: an admin reset in Keycloak sets the same `UPDATE_PASSWORD` action as a temporary password, so it reports `password_change_required`.
46
47
  - `active`: anything else.
47
48
  - **Attributes** are stored as single-value Keycloak attributes under their Keycloak names. `updateAttributes` keeps every attribute the app didn't declare, including ones set by other apps.
48
49
  - **Groups** are top-level realm groups, addressed by exact name. Subgroups aren't supported.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "idp-admin",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "One user-management API for any identity provider.",
5
5
  "keywords": [
6
6
  "identity-provider",