@passlock/client 2.0.6 → 2.2.1

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.
Files changed (40) hide show
  1. package/dist/index.d.ts +101 -18
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +106 -17
  4. package/dist/index.js.map +1 -1
  5. package/dist/internal/network.d.ts +14 -1
  6. package/dist/internal/network.d.ts.map +1 -1
  7. package/dist/internal/network.js +11 -1
  8. package/dist/internal/network.js.map +1 -1
  9. package/dist/internal/promise.d.ts +7 -3
  10. package/dist/internal/promise.d.ts.map +1 -1
  11. package/dist/internal/promise.js +9 -5
  12. package/dist/internal/promise.js.map +1 -1
  13. package/dist/internal/result.d.ts +33 -0
  14. package/dist/internal/result.d.ts.map +1 -0
  15. package/dist/internal/result.js +35 -0
  16. package/dist/internal/result.js.map +1 -0
  17. package/dist/options.d.ts +5 -5
  18. package/dist/options.d.ts.map +1 -1
  19. package/dist/passkey/authentication/authentication.d.ts +13 -6
  20. package/dist/passkey/authentication/authentication.d.ts.map +1 -1
  21. package/dist/passkey/authentication/authentication.js +4 -1
  22. package/dist/passkey/authentication/authentication.js.map +1 -1
  23. package/dist/passkey/errors.d.ts +19 -13
  24. package/dist/passkey/errors.d.ts.map +1 -1
  25. package/dist/passkey/errors.js +16 -16
  26. package/dist/passkey/errors.js.map +1 -1
  27. package/dist/passkey/registration/registration.d.ts +9 -6
  28. package/dist/passkey/registration/registration.d.ts.map +1 -1
  29. package/dist/passkey/registration/registration.js +3 -3
  30. package/dist/passkey/registration/registration.js.map +1 -1
  31. package/dist/passkey/signals/signals.d.ts +188 -27
  32. package/dist/passkey/signals/signals.d.ts.map +1 -1
  33. package/dist/passkey/signals/signals.js +122 -24
  34. package/dist/passkey/signals/signals.js.map +1 -1
  35. package/dist/principal.d.ts +1 -1
  36. package/dist/safe.d.ts +165 -59
  37. package/dist/safe.d.ts.map +1 -1
  38. package/dist/safe.js +163 -54
  39. package/dist/safe.js.map +1 -1
  40. package/package.json +7 -7
@@ -3,42 +3,105 @@ import { Logger } from "../../logger.js";
3
3
  import type { PasslockOptions } from "../../options.js";
4
4
  import { DeleteError, type OrphanedPasskeyError, PruningError, UpdateError } from "../errors.js";
5
5
  /**
6
- * Does the current device support local passkey removal
6
+ * Detect support for browser-driven local passkey removal via
7
+ * `PublicKeyCredential.signalUnknownCredential`.
7
8
  */
8
9
  export declare const isPasskeyDeleteSupport: Micro.Micro<boolean, never, never>;
9
10
  /**
10
- * Does the current device support local passkey pruning
11
+ * Detect support for browser-driven passkey pruning via
12
+ * `PublicKeyCredential.signalAllAcceptedCredentials`.
11
13
  */
12
14
  export declare const isPasskeyPruningSupport: Micro.Micro<boolean, never, never>;
13
15
  /**
14
- * Does the current device support local passkey updates
16
+ * Detect support for browser-driven passkey user-detail updates via
17
+ * `PublicKeyCredential.signalCurrentUserDetails`.
15
18
  */
16
19
  export declare const isPasskeyUpdateSupport: Micro.Micro<boolean, never, never>;
20
+ /**
21
+ * Delete a local passkey by Passlock passkey ID.
22
+ *
23
+ * The library uses the tenancy information to look up the credential metadata
24
+ * before signalling the browser.
25
+ *
26
+ * @see {@link deletePasskey}
27
+ * @category Passkeys (core)
28
+ */
17
29
  export interface DeletePasskeyOptions extends PasslockOptions {
30
+ /**
31
+ * Passlock passkey ID (authenticator ID).
32
+ */
18
33
  passkeyId: string;
19
34
  }
35
+ /**
36
+ * Delete a local passkey using credential metadata you already have.
37
+ *
38
+ * This shape is typically produced by `@passlock/server`'s
39
+ * `deleteUserPasskeys` helper, so the browser can be signalled without an
40
+ * extra Passlock lookup.
41
+ *
42
+ * @see {@link deletePasskey}
43
+ * @see {@link deleteUserPasskeys}
44
+ * @category Passkeys (core)
45
+ */
20
46
  export interface DeleteCredentialOptions extends PasslockOptions {
47
+ /**
48
+ * WebAuthn credential ID.
49
+ */
21
50
  credentialId: string;
51
+ /**
52
+ * Credential user ID.
53
+ */
22
54
  userId: string;
55
+ /**
56
+ * Relying party ID.
57
+ */
23
58
  rpId: string;
24
59
  }
25
60
  /**
26
- * Instruct the device to remove a passkey. E.g. attempt to remove it from
27
- * Apple Password Manager / iCloud.
61
+ * Instruct the browser to remove a local passkey, for example from a password
62
+ * manager.
63
+ *
64
+ * If you pass a Passlock `passkeyId`, the library first fetches the associated
65
+ * credential metadata from Passlock. If you pass a credential payload or
66
+ * {@link OrphanedPasskeyError}, it can signal the browser directly.
28
67
  *
29
68
  * @param options Passkey identifier/credential details and Passlock tenancy options.
30
- * @returns A Micro effect that resolves with a {@link DeleteSuccess} or fails with {@link DeleteError}.
69
+ * @returns A Micro effect that resolves with a {@link DeleteSuccess} once the
70
+ * removal signal has been queued, or fails with {@link DeleteError}.
31
71
  */
32
72
  export declare const deletePasskey: (options: DeletePasskeyOptions | DeleteCredentialOptions | OrphanedPasskeyError) => Micro.Micro<DeleteSuccess, DeleteError, Logger>;
73
+ /**
74
+ * Keep only the listed Passlock passkeys for a user on the current device.
75
+ *
76
+ * The library resolves those passkey IDs to the accepted WebAuthn credential
77
+ * list before signalling the browser.
78
+ *
79
+ * @see {@link prunePasskeys}
80
+ * @category Passkeys (core)
81
+ */
33
82
  export interface PrunePasskeyOptions extends PasslockOptions {
83
+ /**
84
+ * Passlock passkey IDs that should remain available on this device.
85
+ */
34
86
  allowablePasskeyIds: Array<string>;
35
87
  }
88
+ /**
89
+ * Indicates the library finished the accepted-credentials signalling flow.
90
+ *
91
+ * @category Passkeys (core)
92
+ */
36
93
  export type PruningSuccess = {
37
94
  _tag: "PruningSuccess";
38
95
  };
96
+ /**
97
+ * Type guard for {@link PruningSuccess}.
98
+ *
99
+ * @category Passkeys (other)
100
+ */
39
101
  export declare const isPruningSuccess: (payload: unknown) => payload is PruningSuccess;
40
102
  /**
41
- * Given a list of passkey IDs, instruct the device to remove any redundant passkeys.
103
+ * Given a list of passkey IDs to keep, instruct the device to remove any
104
+ * redundant passkeys for the same account on the same relying party.
42
105
  *
43
106
  * Note: this will only remove redundant passkeys (based on the userId).
44
107
  *
@@ -53,7 +116,9 @@ export declare const isPruningSuccess: (payload: unknown) => payload is PruningS
53
116
  * different account, the device will retain it.
54
117
  *
55
118
  * @param options Passlock tenancy/endpoint options and the passkey IDs to keep.
56
- * @returns A Micro effect that resolves with a {@link PruningSuccess} or fails with {@link PruningError}.
119
+ * @returns A Micro effect that resolves with a {@link PruningSuccess} once the
120
+ * accepted-credentials signal has been sent, or fails with
121
+ * {@link PruningError}.
57
122
  */
58
123
  export declare const prunePasskeys: (options: PrunePasskeyOptions) => Micro.Micro<PruningSuccess, PruningError, Logger>;
59
124
  /**
@@ -65,43 +130,99 @@ export declare const prunePasskeys: (options: PrunePasskeyOptions) => Micro.Micr
65
130
  */
66
131
  export interface UpdatePasskeyOptions extends PasslockOptions {
67
132
  /**
68
- * The Passlock passkey id
133
+ * The Passlock passkey ID (authenticator ID).
69
134
  */
70
135
  passkeyId: string;
71
136
  /**
72
- * New username
137
+ * New username shown alongside the passkey.
73
138
  */
74
139
  username: string;
75
140
  /**
76
- * New display name
141
+ * New display name shown alongside the passkey.
77
142
  */
78
143
  displayName?: string | undefined;
79
144
  }
80
145
  /**
81
- * Used when you want to update one or more passkeys by the Credential User ID i.e. the
82
- * immutable Base64Url encoded binary ID.
146
+ * Used when you want to update one or more passkeys by the credential user ID,
147
+ * that is the immutable Base64Url-encoded binary ID.
148
+ *
149
+ * This shape is usually returned by `@passlock/server`'s
150
+ * `updatePasskeyUsernames` helper and does not include tenancy or endpoint
151
+ * configuration.
83
152
  *
84
153
  * @see {@link updatePasskey}
85
154
  * @see {@link https://passlock.dev/rest-api/credential/ The Credential property (main docs site)}
86
155
  *
87
156
  * @category Passkeys (core)
88
157
  */
89
- export interface UpdateCredentialOptions extends PasslockOptions {
158
+ export interface UpdateCredentialOptions {
159
+ /**
160
+ * Credential user ID.
161
+ */
90
162
  userId: string;
163
+ /**
164
+ * Relying party identifier for the passkey.
165
+ */
91
166
  rpId: string;
92
167
  /**
93
- * New username
168
+ * New username shown alongside the passkey.
94
169
  */
95
170
  username: string;
96
171
  /**
97
- * New display name
172
+ * New display name shown alongside the passkey.
98
173
  */
99
174
  displayName?: string | undefined;
100
175
  }
176
+ /**
177
+ * Indicates the library finished the local passkey update signalling flow.
178
+ *
179
+ * @category Passkeys (core)
180
+ */
101
181
  export type UpdateSuccess = {
102
182
  _tag: "UpdateSuccess";
103
183
  };
184
+ /**
185
+ * Type guard for {@link UpdateSuccess}.
186
+ *
187
+ * @category Passkeys (other)
188
+ */
104
189
  export declare const isUpdateSuccess: (payload: unknown) => payload is UpdateSuccess;
190
+ /**
191
+ * Update the username and/or display name for multiple local passkeys.
192
+ *
193
+ * Note: this is purely informational. It does not change any passkey
194
+ * identifiers.
195
+ *
196
+ * The typical use case is when a user changes their account email. You would
197
+ * update the username in your backend system, then pass the returned
198
+ * credential list into this function so the same account label is shown in the
199
+ * user's password manager.
200
+ *
201
+ * @param options Credential identifiers plus the updated username/display name,
202
+ * typically taken from `@passlock/server`'s `updatePasskeyUsernames`
203
+ * response.
204
+ * @returns A Micro effect that resolves with a {@link UpdateSuccess} once the
205
+ * browser has been asked to refresh those details, or fails with
206
+ * {@link UpdateError}.
207
+ */
208
+ export declare const updatePasskeyUsernames: (options: ReadonlyArray<UpdateCredentialOptions>) => Micro.Micro<{
209
+ readonly _tag: "UpdateSuccess";
210
+ }, UpdateError, Logger>;
211
+ /**
212
+ * Delete multiple local passkeys using credentials previously returned from
213
+ * your backend.
214
+ *
215
+ * The typical flow is to delete the server-side passkeys first, then pass the
216
+ * returned `deleted` array into this function so the user’s password manager is
217
+ * updated too.
218
+ *
219
+ * @param options Credentials derived from deleted server-side passkeys.
220
+ * @returns A Micro effect that resolves with a {@link DeleteSuccess} once the
221
+ * removal signals have been queued, or fails with {@link DeleteError}.
222
+ */
223
+ export declare const deleteUserPasskeys: (options: ReadonlyArray<Credential>) => Micro.Micro<{
224
+ readonly _tag: "DeleteSuccess";
225
+ }, DeleteError, Logger>;
105
226
  /**
106
227
  * Update a passkey e.g. change the username and/or display name.
107
228
  * Note: this is purely informational, it does not change any identifiers.
@@ -111,17 +232,36 @@ export declare const isUpdateSuccess: (payload: unknown) => payload is UpdateSuc
111
232
  * account would still show up in their password manager as old-name@gmail.com.
112
233
  *
113
234
  * @param options Passkey update options.
114
- * @returns A Micro effect that resolves with a {@link UpdateSuccess} or fails with {@link UpdateError}.
235
+ * @returns A Micro effect that resolves with a {@link UpdateSuccess} once the
236
+ * browser has been asked to refresh the local details, or fails with
237
+ * {@link UpdateError}.
115
238
  */
116
239
  export declare const updatePasskey: (options: UpdatePasskeyOptions | UpdateCredentialOptions) => Micro.Micro<{
117
240
  readonly _tag: "UpdateSuccess";
118
241
  }, UpdateError, Logger>;
119
- export type CredentialMapping = {
242
+ /**
243
+ * Credential metadata required to target a local passkey on the device.
244
+ *
245
+ * @category Passkeys (core)
246
+ */
247
+ export type Credential = {
248
+ /**
249
+ * WebAuthn credential ID.
250
+ */
120
251
  credentialId: string;
252
+ /**
253
+ * Credential user ID.
254
+ */
121
255
  userId: string;
256
+ /**
257
+ * Relying party ID.
258
+ */
122
259
  rpId: string;
123
260
  };
124
- export type CredentialMappings = {
261
+ /**
262
+ * Accepted credential list for a single user on a relying party.
263
+ */
264
+ export type UserCredentials = {
125
265
  rpId: string;
126
266
  userId: string;
127
267
  allAcceptedCredentialIds: string[];
@@ -134,25 +274,46 @@ type IPasskeyNotFound = {
134
274
  export type DeleteSuccess = {
135
275
  _tag: "DeleteSuccess";
136
276
  };
277
+ /**
278
+ * Type guard for {@link DeleteSuccess}.
279
+ *
280
+ * @category Passkeys (other)
281
+ */
137
282
  export declare const isDeleteSuccess: (payload: unknown) => payload is DeleteSuccess;
138
283
  /**
139
- * Tell the client device to remove a given credential
284
+ * Queue a browser removal signal for a credential.
140
285
  *
141
- * @param credential Credential mapping or missing-passkey payload.
142
- * @returns A Micro effect that resolves with a {@link DeleteSuccess} or fails with {@link DeleteError}.
286
+ * @param credential Credential or missing-passkey payload.
287
+ * @returns A Micro effect that resolves with a {@link DeleteSuccess} once the
288
+ * removal signal has been queued, or fails with {@link DeleteError}.
143
289
  */
144
- export declare const signalCredentialRemoval: (credential: CredentialMapping | IPasskeyNotFound) => Micro.Micro<DeleteSuccess, DeleteError, Logger>;
290
+ export declare const signalCredentialRemoval: (credential: Credential | IPasskeyNotFound) => Micro.Micro<DeleteSuccess, DeleteError, Logger>;
145
291
  /**
146
- * Tell the client device which credentials are still accepted for a user.
292
+ * Tell the browser which credentials are still accepted for a user.
147
293
  *
148
- * @param credentials Accepted credential mapping for the user.
149
- * @returns A Micro effect that resolves with a {@link PruningSuccess} or fails with {@link PruningError}.
294
+ * @param credentials Accepted credentials for the user.
295
+ * @returns A Micro effect that resolves with a {@link PruningSuccess} once the
296
+ * accepted-credentials signal has been sent, or fails with
297
+ * {@link PruningError}.
298
+ */
299
+ export declare const signalAcceptedCredentials: (credentials: UserCredentials) => Micro.Micro<PruningSuccess, PruningError, Logger>;
300
+ /**
301
+ * Credential identity needed to update the user-visible details for a local
302
+ * passkey.
150
303
  */
151
- export declare const signalAcceptedCredentials: (credentials: CredentialMappings) => Micro.Micro<PruningSuccess, PruningError, Logger>;
152
304
  export type CredentialUserId = {
153
305
  userId: string;
154
306
  rpId: string;
155
307
  };
308
+ /**
309
+ * Tell the browser to refresh the username and display name shown for a local
310
+ * passkey.
311
+ *
312
+ * @param credential Credential identity used to find the passkey.
313
+ * @param updates Updated username/display-name values.
314
+ * @returns A Micro effect that resolves with an {@link UpdateSuccess} once the
315
+ * signal has been sent, or fails with {@link UpdateError}.
316
+ */
156
317
  export declare const signalCurrentUserDetails: (credential: CredentialUserId, updates: Pick<UpdatePasskeyOptions, "username" | "displayName">) => Micro.Micro<{
157
318
  readonly _tag: "UpdateSuccess";
158
319
  }, UpdateError, Logger>;
@@ -1 +1 @@
1
- {"version":3,"file":"signals.d.ts","sourceRoot":"","sources":["../../../src/passkey/signals/signals.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"signals.d.ts","sourceRoot":"","sources":["../../../src/passkey/signals/signals.ts"],"names":[],"mappings":"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"}
@@ -4,32 +4,40 @@ import { makeEndpoint } from "../../internal/index.js";
4
4
  import { Logger } from "../../logger.js";
5
5
  import { DeleteError, PruningError, UpdateError, } from "../errors.js";
6
6
  /**
7
- * Does the current device support local passkey removal
7
+ * Detect support for browser-driven local passkey removal via
8
+ * `PublicKeyCredential.signalUnknownCredential`.
8
9
  */
9
10
  export const isPasskeyDeleteSupport = Micro.sync(() => {
10
11
  return (PublicKeyCredential?.signalUnknownCredential &&
11
12
  typeof PublicKeyCredential.signalUnknownCredential === "function");
12
13
  });
13
14
  /**
14
- * Does the current device support local passkey pruning
15
+ * Detect support for browser-driven passkey pruning via
16
+ * `PublicKeyCredential.signalAllAcceptedCredentials`.
15
17
  */
16
18
  export const isPasskeyPruningSupport = Micro.sync(() => {
17
19
  return (PublicKeyCredential?.signalAllAcceptedCredentials &&
18
20
  typeof PublicKeyCredential.signalAllAcceptedCredentials === "function");
19
21
  });
20
22
  /**
21
- * Does the current device support local passkey updates
23
+ * Detect support for browser-driven passkey user-detail updates via
24
+ * `PublicKeyCredential.signalCurrentUserDetails`.
22
25
  */
23
26
  export const isPasskeyUpdateSupport = Micro.sync(() => {
24
27
  return (PublicKeyCredential?.signalCurrentUserDetails &&
25
28
  typeof PublicKeyCredential.signalCurrentUserDetails === "function");
26
29
  });
27
30
  /**
28
- * Instruct the device to remove a passkey. E.g. attempt to remove it from
29
- * Apple Password Manager / iCloud.
31
+ * Instruct the browser to remove a local passkey, for example from a password
32
+ * manager.
33
+ *
34
+ * If you pass a Passlock `passkeyId`, the library first fetches the associated
35
+ * credential metadata from Passlock. If you pass a credential payload or
36
+ * {@link OrphanedPasskeyError}, it can signal the browser directly.
30
37
  *
31
38
  * @param options Passkey identifier/credential details and Passlock tenancy options.
32
- * @returns A Micro effect that resolves with a {@link DeleteSuccess} or fails with {@link DeleteError}.
39
+ * @returns A Micro effect that resolves with a {@link DeleteSuccess} once the
40
+ * removal signal has been queued, or fails with {@link DeleteError}.
33
41
  */
34
42
  export const deletePasskey = (options) => Micro.gen(function* () {
35
43
  const logger = yield* Micro.service(Logger);
@@ -40,10 +48,10 @@ export const deletePasskey = (options) => Micro.gen(function* () {
40
48
  code: "PASSKEY_DELETION_UNSUPPORTED",
41
49
  message: "Passkey deletion not supported on this device",
42
50
  }));
43
- const credential = "rpId" in options ? options : yield* getCredentialMapping(options);
51
+ const credential = "rpId" in options ? options : yield* getCredential(options);
44
52
  return yield* signalCredentialRemoval(credential);
45
53
  });
46
- const getCredentialMapping = (options) => Micro.gen(function* () {
54
+ const getCredential = (options) => Micro.gen(function* () {
47
55
  const { tenancyId } = options;
48
56
  const logger = yield* Micro.service(Logger);
49
57
  const { endpoint } = makeEndpoint(options);
@@ -56,13 +64,18 @@ const getCredentialMapping = (options) => Micro.gen(function* () {
56
64
  message: "Unable to find the metadata associated with this passkey",
57
65
  }));
58
66
  const credential = yield* Micro.promise(() => response.json());
59
- if (!isCredentialMapping(credential))
67
+ if (!isCredential(credential))
60
68
  return yield* Micro.fail(new DeleteError({
61
69
  code: "OTHER_ERROR",
62
70
  message: "Invalid metadata associated with this passkey",
63
71
  }));
64
72
  return credential;
65
73
  });
74
+ /**
75
+ * Type guard for {@link PruningSuccess}.
76
+ *
77
+ * @category Passkeys (other)
78
+ */
66
79
  export const isPruningSuccess = (payload) => {
67
80
  if (typeof payload !== "object")
68
81
  return false;
@@ -75,7 +88,8 @@ export const isPruningSuccess = (payload) => {
75
88
  return payload._tag === "PruningSuccess";
76
89
  };
77
90
  /**
78
- * Given a list of passkey IDs, instruct the device to remove any redundant passkeys.
91
+ * Given a list of passkey IDs to keep, instruct the device to remove any
92
+ * redundant passkeys for the same account on the same relying party.
79
93
  *
80
94
  * Note: this will only remove redundant passkeys (based on the userId).
81
95
  *
@@ -90,7 +104,9 @@ export const isPruningSuccess = (payload) => {
90
104
  * different account, the device will retain it.
91
105
  *
92
106
  * @param options Passlock tenancy/endpoint options and the passkey IDs to keep.
93
- * @returns A Micro effect that resolves with a {@link PruningSuccess} or fails with {@link PruningError}.
107
+ * @returns A Micro effect that resolves with a {@link PruningSuccess} once the
108
+ * accepted-credentials signal has been sent, or fails with
109
+ * {@link PruningError}.
94
110
  */
95
111
  export const prunePasskeys = (options) => Micro.gen(function* () {
96
112
  const { tenancyId } = options;
@@ -113,13 +129,18 @@ export const prunePasskeys = (options) => Micro.gen(function* () {
113
129
  message: "Unable to find the metadata associated with these passkeys",
114
130
  }));
115
131
  const credentials = yield* Micro.promise(() => response.json());
116
- if (!isCredentialMappings(credentials))
132
+ if (!isUserCredentials(credentials))
117
133
  return yield* Micro.fail(new PruningError({
118
134
  code: "OTHER_ERROR",
119
135
  message: "Invalid metadata associated with one or more passkeys",
120
136
  }));
121
137
  return yield* signalAcceptedCredentials(credentials);
122
138
  });
139
+ /**
140
+ * Type guard for {@link UpdateSuccess}.
141
+ *
142
+ * @category Passkeys (other)
143
+ */
123
144
  export const isUpdateSuccess = (payload) => {
124
145
  if (typeof payload !== "object")
125
146
  return false;
@@ -131,6 +152,64 @@ export const isUpdateSuccess = (payload) => {
131
152
  return false;
132
153
  return payload._tag === "UpdateSuccess";
133
154
  };
155
+ /**
156
+ * Update the username and/or display name for multiple local passkeys.
157
+ *
158
+ * Note: this is purely informational. It does not change any passkey
159
+ * identifiers.
160
+ *
161
+ * The typical use case is when a user changes their account email. You would
162
+ * update the username in your backend system, then pass the returned
163
+ * credential list into this function so the same account label is shown in the
164
+ * user's password manager.
165
+ *
166
+ * @param options Credential identifiers plus the updated username/display name,
167
+ * typically taken from `@passlock/server`'s `updatePasskeyUsernames`
168
+ * response.
169
+ * @returns A Micro effect that resolves with a {@link UpdateSuccess} once the
170
+ * browser has been asked to refresh those details, or fails with
171
+ * {@link UpdateError}.
172
+ */
173
+ export const updatePasskeyUsernames = (options) => Micro.gen(function* () {
174
+ const logger = yield* Micro.service(Logger);
175
+ yield* logger.logInfo("Testing for local passkey update support");
176
+ const canUpdate = yield* isPasskeyUpdateSupport;
177
+ if (!canUpdate)
178
+ return yield* Micro.fail(new UpdateError({
179
+ code: "PASSKEY_UPDATE_UNSUPPORTED",
180
+ message: "Passkey update not supported on this device",
181
+ }));
182
+ yield* Micro.forEach(options, (credential) => signalCurrentUserDetails(credential, credential));
183
+ return {
184
+ _tag: "UpdateSuccess",
185
+ };
186
+ });
187
+ /**
188
+ * Delete multiple local passkeys using credentials previously returned from
189
+ * your backend.
190
+ *
191
+ * The typical flow is to delete the server-side passkeys first, then pass the
192
+ * returned `deleted` array into this function so the user’s password manager is
193
+ * updated too.
194
+ *
195
+ * @param options Credentials derived from deleted server-side passkeys.
196
+ * @returns A Micro effect that resolves with a {@link DeleteSuccess} once the
197
+ * removal signals have been queued, or fails with {@link DeleteError}.
198
+ */
199
+ export const deleteUserPasskeys = (options) => Micro.gen(function* () {
200
+ const logger = yield* Micro.service(Logger);
201
+ yield* logger.logInfo("Testing for local passkey removal support");
202
+ const canDelete = yield* isPasskeyDeleteSupport;
203
+ if (!canDelete)
204
+ return yield* Micro.fail(new DeleteError({
205
+ code: "PASSKEY_DELETION_UNSUPPORTED",
206
+ message: "Passkey deletion not supported on this device",
207
+ }));
208
+ yield* Micro.forEach(options, signalCredentialRemoval);
209
+ return {
210
+ _tag: "DeleteSuccess",
211
+ };
212
+ });
134
213
  /**
135
214
  * Update a passkey e.g. change the username and/or display name.
136
215
  * Note: this is purely informational, it does not change any identifiers.
@@ -140,7 +219,9 @@ export const isUpdateSuccess = (payload) => {
140
219
  * account would still show up in their password manager as old-name@gmail.com.
141
220
  *
142
221
  * @param options Passkey update options.
143
- * @returns A Micro effect that resolves with a {@link UpdateSuccess} or fails with {@link UpdateError}.
222
+ * @returns A Micro effect that resolves with a {@link UpdateSuccess} once the
223
+ * browser has been asked to refresh the local details, or fails with
224
+ * {@link UpdateError}.
144
225
  */
145
226
  export const updatePasskey = (options) => Micro.gen(function* () {
146
227
  const logger = yield* Micro.service(Logger);
@@ -151,10 +232,10 @@ export const updatePasskey = (options) => Micro.gen(function* () {
151
232
  code: "PASSKEY_UPDATE_UNSUPPORTED",
152
233
  message: "Passkey update not supported on this device",
153
234
  }));
154
- const credential = "rpId" in options ? options : yield* getUserCredentialMapping(options);
235
+ const credential = "rpId" in options ? options : yield* getUserCredential(options);
155
236
  return yield* signalCurrentUserDetails(credential, options);
156
237
  });
157
- const getUserCredentialMapping = (options) => Micro.gen(function* () {
238
+ const getUserCredential = (options) => Micro.gen(function* () {
158
239
  const { tenancyId } = options;
159
240
  const logger = yield* Micro.service(Logger);
160
241
  const { endpoint } = makeEndpoint(options);
@@ -167,14 +248,14 @@ const getUserCredentialMapping = (options) => Micro.gen(function* () {
167
248
  message: "Unable to find the metadata associated with this passkey",
168
249
  }));
169
250
  const credential = yield* Micro.promise(() => response.json());
170
- if (!isCredentialMapping(credential))
251
+ if (!isCredential(credential))
171
252
  return yield* Micro.fail(new UpdateError({
172
253
  code: "OTHER_ERROR",
173
254
  message: "Invalid metadata associated with this passkey",
174
255
  }));
175
256
  return credential;
176
257
  });
177
- const isCredentialMapping = (payload) => {
258
+ const isCredential = (payload) => {
178
259
  if (typeof payload !== "object")
179
260
  return false;
180
261
  if (payload === null)
@@ -193,7 +274,7 @@ const isCredentialMapping = (payload) => {
193
274
  return false;
194
275
  return true;
195
276
  };
196
- const isCredentialMappings = (payload) => {
277
+ const isUserCredentials = (payload) => {
197
278
  if (typeof payload !== "object")
198
279
  return false;
199
280
  if (payload === null)
@@ -212,6 +293,11 @@ const isCredentialMappings = (payload) => {
212
293
  return false;
213
294
  return true;
214
295
  };
296
+ /**
297
+ * Type guard for {@link DeleteSuccess}.
298
+ *
299
+ * @category Passkeys (other)
300
+ */
215
301
  export const isDeleteSuccess = (payload) => {
216
302
  if (typeof payload !== "object")
217
303
  return false;
@@ -224,10 +310,11 @@ export const isDeleteSuccess = (payload) => {
224
310
  return payload._tag === "DeleteSuccess";
225
311
  };
226
312
  /**
227
- * Tell the client device to remove a given credential
313
+ * Queue a browser removal signal for a credential.
228
314
  *
229
- * @param credential Credential mapping or missing-passkey payload.
230
- * @returns A Micro effect that resolves with a {@link DeleteSuccess} or fails with {@link DeleteError}.
315
+ * @param credential Credential or missing-passkey payload.
316
+ * @returns A Micro effect that resolves with a {@link DeleteSuccess} once the
317
+ * removal signal has been queued, or fails with {@link DeleteError}.
231
318
  */
232
319
  export const signalCredentialRemoval = (credential) => Micro.gen(function* () {
233
320
  const logger = yield* Micro.service(Logger);
@@ -252,10 +339,12 @@ export const signalCredentialRemoval = (credential) => Micro.gen(function* () {
252
339
  return { _tag: "DeleteSuccess" };
253
340
  });
254
341
  /**
255
- * Tell the client device which credentials are still accepted for a user.
342
+ * Tell the browser which credentials are still accepted for a user.
256
343
  *
257
- * @param credentials Accepted credential mapping for the user.
258
- * @returns A Micro effect that resolves with a {@link PruningSuccess} or fails with {@link PruningError}.
344
+ * @param credentials Accepted credentials for the user.
345
+ * @returns A Micro effect that resolves with a {@link PruningSuccess} once the
346
+ * accepted-credentials signal has been sent, or fails with
347
+ * {@link PruningError}.
259
348
  */
260
349
  export const signalAcceptedCredentials = (credentials) => Micro.gen(function* () {
261
350
  const logger = yield* Micro.service(Logger);
@@ -278,6 +367,15 @@ export const signalAcceptedCredentials = (credentials) => Micro.gen(function* ()
278
367
  yield* logger.logInfo("Accepted credentials signalled");
279
368
  return { _tag: "PruningSuccess" };
280
369
  });
370
+ /**
371
+ * Tell the browser to refresh the username and display name shown for a local
372
+ * passkey.
373
+ *
374
+ * @param credential Credential identity used to find the passkey.
375
+ * @param updates Updated username/display-name values.
376
+ * @returns A Micro effect that resolves with an {@link UpdateSuccess} once the
377
+ * signal has been sent, or fails with {@link UpdateError}.
378
+ */
281
379
  export const signalCurrentUserDetails = (credential, updates) => Micro.gen(function* () {
282
380
  const logger = yield* Micro.service(Logger);
283
381
  yield* logger.logInfo("Testing for local passkey update support");