@byline/admin 6.7.2 → 6.8.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.
Files changed (86) hide show
  1. package/dist/modules/admin-account/components/change-password.js +1 -1
  2. package/dist/modules/admin-account/components/container.js +18 -3
  3. package/dist/modules/admin-account/components/container.module.js +2 -0
  4. package/dist/modules/admin-account/components/container_module.css +7 -0
  5. package/dist/modules/admin-account/components/preferences.js +1 -1
  6. package/dist/modules/admin-account/components/update.js +1 -1
  7. package/dist/modules/admin-roles/components/create.js +1 -1
  8. package/dist/modules/admin-roles/components/permissions.js +1 -1
  9. package/dist/modules/admin-roles/components/update.js +1 -1
  10. package/dist/modules/admin-users/abilities.d.ts +1 -0
  11. package/dist/modules/admin-users/abilities.js +8 -1
  12. package/dist/modules/admin-users/components/create.js +1 -1
  13. package/dist/modules/admin-users/components/roles.js +1 -1
  14. package/dist/modules/admin-users/components/set-password.js +1 -1
  15. package/dist/modules/admin-users/components/update.js +1 -1
  16. package/dist/modules/auth/auth-maintenance-repository.d.ts +31 -0
  17. package/dist/modules/auth/auth-maintenance-repository.js +1 -0
  18. package/dist/modules/auth/auth-maintenance.d.ts +11 -0
  19. package/dist/modules/auth/auth-maintenance.js +39 -0
  20. package/dist/modules/auth/auth-maintenance.test.node.d.ts +1 -0
  21. package/dist/modules/auth/components/passkey-browser.d.ts +14 -0
  22. package/dist/modules/auth/components/passkey-browser.js +118 -0
  23. package/dist/modules/auth/components/passkey-browser.test.node.d.ts +1 -0
  24. package/dist/modules/auth/components/passkey-manager.d.ts +26 -0
  25. package/dist/modules/auth/components/passkey-manager.js +406 -0
  26. package/dist/modules/auth/components/passkey-manager.module.js +22 -0
  27. package/dist/modules/auth/components/passkey-manager_module.css +104 -0
  28. package/dist/modules/auth/components/sign-in-form.js +62 -3
  29. package/dist/modules/auth/components/sign-in-form.module.js +5 -1
  30. package/dist/modules/auth/components/sign-in-form_module.css +40 -0
  31. package/dist/modules/auth/index.d.ts +5 -0
  32. package/dist/modules/auth/index.js +3 -0
  33. package/dist/modules/auth/jwt-session-provider.d.ts +12 -4
  34. package/dist/modules/auth/jwt-session-provider.js +135 -94
  35. package/dist/modules/auth/native-passkey-provider.d.ts +72 -0
  36. package/dist/modules/auth/native-passkey-provider.js +618 -0
  37. package/dist/modules/auth/passkey-rate-limiter.d.ts +14 -0
  38. package/dist/modules/auth/passkey-rate-limiter.js +60 -0
  39. package/dist/modules/auth/passkey-rate-limiter.test.node.d.ts +8 -0
  40. package/dist/modules/auth/passkey-storage-validation.d.ts +14 -0
  41. package/dist/modules/auth/passkey-storage-validation.js +59 -0
  42. package/dist/modules/auth/passkeys-repository.d.ts +96 -0
  43. package/dist/modules/auth/passkeys-repository.js +1 -0
  44. package/dist/modules/auth/refresh-tokens-repository.d.ts +1 -1
  45. package/dist/modules/auth/sign-in-rate-limiter.d.ts +2 -0
  46. package/dist/modules/auth/sign-in-rate-limiter.js +1 -1
  47. package/dist/services/admin-services-context.d.ts +1 -1
  48. package/dist/services/admin-services-types.d.ts +60 -0
  49. package/dist/store.d.ts +4 -0
  50. package/package.json +16 -9
  51. package/src/modules/admin-account/components/change-password.tsx +1 -1
  52. package/src/modules/admin-account/components/container.module.css +9 -0
  53. package/src/modules/admin-account/components/container.tsx +12 -3
  54. package/src/modules/admin-account/components/preferences.tsx +1 -1
  55. package/src/modules/admin-account/components/update.tsx +1 -1
  56. package/src/modules/admin-roles/components/create.tsx +1 -1
  57. package/src/modules/admin-roles/components/permissions.tsx +1 -1
  58. package/src/modules/admin-roles/components/update.tsx +1 -1
  59. package/src/modules/admin-users/abilities.ts +7 -0
  60. package/src/modules/admin-users/components/create.tsx +1 -1
  61. package/src/modules/admin-users/components/roles.tsx +1 -1
  62. package/src/modules/admin-users/components/set-password.tsx +1 -1
  63. package/src/modules/admin-users/components/update.tsx +1 -1
  64. package/src/modules/auth/auth-maintenance-repository.ts +26 -0
  65. package/src/modules/auth/auth-maintenance.test.node.ts +110 -0
  66. package/src/modules/auth/auth-maintenance.ts +62 -0
  67. package/src/modules/auth/components/passkey-browser.test.node.ts +188 -0
  68. package/src/modules/auth/components/passkey-browser.ts +139 -0
  69. package/src/modules/auth/components/passkey-manager.module.css +100 -0
  70. package/src/modules/auth/components/passkey-manager.test.tsx +189 -0
  71. package/src/modules/auth/components/passkey-manager.tsx +386 -0
  72. package/src/modules/auth/components/sign-in-form.module.css +37 -0
  73. package/src/modules/auth/components/sign-in-form.test.tsx +92 -0
  74. package/src/modules/auth/components/sign-in-form.tsx +65 -4
  75. package/src/modules/auth/index.ts +15 -0
  76. package/src/modules/auth/jwt-session-provider.ts +116 -44
  77. package/src/modules/auth/native-passkey-provider.ts +710 -0
  78. package/src/modules/auth/passkey-rate-limiter.test.node.ts +83 -0
  79. package/src/modules/auth/passkey-rate-limiter.ts +76 -0
  80. package/src/modules/auth/passkey-storage-validation.ts +108 -0
  81. package/src/modules/auth/passkeys-repository.ts +97 -0
  82. package/src/modules/auth/refresh-tokens-repository.ts +1 -1
  83. package/src/modules/auth/sign-in-rate-limiter.ts +1 -1
  84. package/src/services/admin-services-context.tsx +2 -0
  85. package/src/services/admin-services-types.ts +60 -0
  86. package/src/store.ts +4 -0
@@ -182,7 +182,7 @@ function ChangeAccountPassword({ account, onClose, onSuccess }) {
182
182
  children: [
183
183
  /*#__PURE__*/ jsx(Button, {
184
184
  type: "button",
185
- intent: "secondary",
185
+ intent: "noeffect",
186
186
  size: "sm",
187
187
  onClick: onClose,
188
188
  className: clsx('byline-account-change-password-action', change_password_module.action),
@@ -5,6 +5,7 @@ import { LocalDateTime } from "@byline/admin/react";
5
5
  import { useTranslation } from "@byline/i18n/react";
6
6
  import { CloseIcon, Drawer, EditIcon, IconButton } from "@byline/ui/react";
7
7
  import clsx from "clsx";
8
+ import { PasskeyManager } from "../../auth/components/passkey-manager.js";
8
9
  import { ChangeAccountPassword } from "./change-password.js";
9
10
  import container_module from "./container.module.js";
10
11
  import { Preferences } from "./preferences.js";
@@ -208,6 +209,19 @@ function AccountSelfContainer({ account }) {
208
209
  /*#__PURE__*/ jsx("p", {
209
210
  className: clsx('muted', 'byline-account-status-help', container_module["status-help"]),
210
211
  children: t('account.preferences.help')
212
+ }),
213
+ /*#__PURE__*/ jsxs("div", {
214
+ className: clsx('byline-account-preferences-theme', container_module["preferences-theme"]),
215
+ children: [
216
+ /*#__PURE__*/ jsx("p", {
217
+ className: clsx('byline-account-line', container_module.line),
218
+ children: /*#__PURE__*/ jsx("span", {
219
+ className: "muted",
220
+ children: t('account.appearance.colorTheme')
221
+ })
222
+ }),
223
+ /*#__PURE__*/ jsx(ThemeSwitch, {})
224
+ ]
211
225
  })
212
226
  ]
213
227
  })
@@ -225,9 +239,10 @@ function AccountSelfContainer({ account }) {
225
239
  children: t('account.password.intro')
226
240
  })
227
241
  }),
228
- /*#__PURE__*/ jsx(ContainerSection, {
229
- title: t('account.sections.appearance'),
230
- children: /*#__PURE__*/ jsx(ThemeSwitch, {})
242
+ /*#__PURE__*/ jsx(PasskeyManager, {
243
+ targetUserId: currentAccount.id,
244
+ targetIsSuperAdmin: currentAccount.is_super_admin,
245
+ section: ContainerSection
231
246
  }),
232
247
  /*#__PURE__*/ jsxs(ContainerSection, {
233
248
  title: t('account.sections.status'),
@@ -15,6 +15,8 @@ const container_module = {
15
15
  statusOff: "status-off-WJAcBy",
16
16
  "status-help": "status-help-GVOlMV",
17
17
  statusHelp: "status-help-GVOlMV",
18
+ "preferences-theme": "preferences-theme-SHTZmr",
19
+ preferencesTheme: "preferences-theme-SHTZmr",
18
20
  "not-set": "not-set-Ek2yXe",
19
21
  notSet: "not-set-Ek2yXe",
20
22
  drawer: "drawer-dDlpic",
@@ -60,6 +60,13 @@
60
60
  margin-bottom: 0;
61
61
  }
62
62
 
63
+ :is(.preferences-theme-SHTZmr, .byline-account-preferences-theme) {
64
+ gap: var(--spacing-8);
65
+ margin-top: var(--spacing-20);
66
+ flex-direction: column;
67
+ display: flex;
68
+ }
69
+
63
70
  :is(.not-set-Ek2yXe, .byline-account-not-set) {
64
71
  font-style: italic;
65
72
  }
@@ -124,7 +124,7 @@ function Preferences({ account, onClose, onSuccess }) {
124
124
  children: [
125
125
  /*#__PURE__*/ jsx(Button, {
126
126
  type: "button",
127
- intent: "secondary",
127
+ intent: "noeffect",
128
128
  size: "sm",
129
129
  onClick: onClose,
130
130
  className: clsx('byline-account-preferences-action', preferences_module.action),
@@ -183,7 +183,7 @@ function UpdateAccount({ account, onClose, onSuccess }) {
183
183
  children: [
184
184
  /*#__PURE__*/ jsx(Button, {
185
185
  type: "button",
186
- intent: "secondary",
186
+ intent: "noeffect",
187
187
  size: "sm",
188
188
  onClick: onClose,
189
189
  className: clsx('byline-account-update-action', update_module.action),
@@ -144,7 +144,7 @@ function CreateAdminRole({ onClose, onSuccess }) {
144
144
  children: [
145
145
  /*#__PURE__*/ jsx(Button, {
146
146
  type: "button",
147
- intent: "secondary",
147
+ intent: "noeffect",
148
148
  size: "sm",
149
149
  onClick: onClose,
150
150
  className: clsx('byline-role-create-action', create_module.action),
@@ -207,7 +207,7 @@ function RolePermissions({ role, registered, initialAbilities, onSaved }) {
207
207
  children: [
208
208
  /*#__PURE__*/ jsx(Button, {
209
209
  type: "button",
210
- intent: "secondary",
210
+ intent: "noeffect",
211
211
  size: "xs",
212
212
  onClick: handleCancel,
213
213
  disabled: saving,
@@ -134,7 +134,7 @@ function UpdateRole({ role, onClose, onSuccess }) {
134
134
  children: [
135
135
  /*#__PURE__*/ jsx(Button, {
136
136
  type: "button",
137
- intent: "secondary",
137
+ intent: "noeffect",
138
138
  size: "sm",
139
139
  onClick: onClose,
140
140
  className: clsx('byline-role-update-action', update_module.action),
@@ -30,6 +30,7 @@ export declare const ADMIN_USERS_ABILITIES: {
30
30
  readonly update: 'admin.users.update';
31
31
  readonly delete: 'admin.users.delete';
32
32
  readonly changePassword: 'admin.users.changePassword';
33
+ readonly managePasskeys: 'admin.users.managePasskeys';
33
34
  };
34
35
  export type AdminUsersAbilityKey = (typeof ADMIN_USERS_ABILITIES)[keyof typeof ADMIN_USERS_ABILITIES];
35
36
  /**
@@ -3,9 +3,16 @@ const ADMIN_USERS_ABILITIES = {
3
3
  create: 'admin.users.create',
4
4
  update: 'admin.users.update',
5
5
  delete: 'admin.users.delete',
6
- changePassword: 'admin.users.changePassword'
6
+ changePassword: 'admin.users.changePassword',
7
+ managePasskeys: 'admin.users.managePasskeys'
7
8
  };
8
9
  function registerAdminUsersAbilities(registry) {
10
+ registry.register({
11
+ key: ADMIN_USERS_ABILITIES.managePasskeys,
12
+ label: 'Manage admin user passkeys',
13
+ group: 'admin.users',
14
+ source: 'admin'
15
+ });
9
16
  registry.register({
10
17
  key: ADMIN_USERS_ABILITIES.read,
11
18
  label: 'Read admin users',
@@ -239,7 +239,7 @@ function CreateAdminUser({ onClose, onSuccess }) {
239
239
  children: [
240
240
  /*#__PURE__*/ jsx(Button, {
241
241
  type: "button",
242
- intent: "secondary",
242
+ intent: "noeffect",
243
243
  size: "sm",
244
244
  onClick: onClose,
245
245
  className: clsx('byline-user-create-action', create_module.action),
@@ -113,7 +113,7 @@ function UserRoles({ user, allRoles, initialRoleIds, onClose, onSaved }) {
113
113
  children: [
114
114
  /*#__PURE__*/ jsx(Button, {
115
115
  type: "button",
116
- intent: "secondary",
116
+ intent: "noeffect",
117
117
  size: "xs",
118
118
  onClick: onClose,
119
119
  disabled: saving,
@@ -130,7 +130,7 @@ function SetPassword({ user, onClose, onSuccess }) {
130
130
  children: [
131
131
  /*#__PURE__*/ jsx(Button, {
132
132
  type: "button",
133
- intent: "secondary",
133
+ intent: "noeffect",
134
134
  size: "sm",
135
135
  onClick: onClose,
136
136
  className: clsx('byline-user-set-password-action', set_password_module.action),
@@ -230,7 +230,7 @@ function UpdateUser({ user, onClose, onSuccess }) {
230
230
  children: [
231
231
  /*#__PURE__*/ jsx(Button, {
232
232
  type: "button",
233
- intent: "secondary",
233
+ intent: "noeffect",
234
234
  size: "sm",
235
235
  onClick: onClose,
236
236
  className: clsx('byline-user-update-action', update_module.action),
@@ -0,0 +1,31 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ /** Bounded housekeeping; expiry and revocation remain independent authorization checks. */
9
+ export interface AuthMaintenanceRepository {
10
+ /** Remove at most 100 attempts expired or consumed on/before the supplied grace cutoff. */
11
+ purgeAttempts(before: Date): Promise<number>;
12
+ /** Candidate only: eligibility must be checked again under its owning account lock. */
13
+ findTerminalLogin(before: Date): Promise<{
14
+ id: string;
15
+ adminUserId: string;
16
+ } | null>;
17
+ /**
18
+ * Requires the owning account lock. Re-read eligibility, delete at most 100 refresh
19
+ * children, then delete the terminal parent only if no children remain. Never cascade.
20
+ */
21
+ pruneTerminalLogin(input: {
22
+ id: string;
23
+ adminUserId: string;
24
+ before: Date;
25
+ }): Promise<{
26
+ refreshDeleted: number;
27
+ loginDeleted: boolean;
28
+ }>;
29
+ /** Remove at most 100 legacy null-sid rows expired on/before the retention cutoff. */
30
+ purgeLegacyRefreshTokens(before: Date): Promise<number>;
31
+ }
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ import type { RecurringTaskDefinition } from '@byline/core';
9
+ import type { AdminStore } from '../../store.js';
10
+ /** Register for native sessions even on password-only sites; configuration never runs cleanup. */
11
+ export declare function createAuthMaintenanceTasks(store: AdminStore, now?: () => Date): RecurringTaskDefinition[];
@@ -0,0 +1,39 @@
1
+ const DAY = 86400000;
2
+ function createAuthMaintenanceTasks(store, now = ()=>new Date()) {
3
+ const task = (name, retention, batch)=>({
4
+ name: `auth.maintenance.${name}`,
5
+ intervalMs: 60000,
6
+ leaseMs: 60000,
7
+ async run (context) {
8
+ const started = performance.now();
9
+ const cutoff = new Date(now().getTime() - retention);
10
+ for(let n = 0; n < 32 && performance.now() - started < 30000; n++){
11
+ context.signal.throwIfAborted();
12
+ await context.heartbeat();
13
+ context.signal.throwIfAborted();
14
+ if (!await batch(cutoff)) return {
15
+ workRemaining: false
16
+ };
17
+ }
18
+ return {
19
+ workRemaining: true
20
+ };
21
+ }
22
+ });
23
+ return [
24
+ task('attempts', 300000, async (cutoff)=>await store.authMaintenance.purgeAttempts(cutoff) === 100),
25
+ task('logins', 30 * DAY, async (cutoff)=>{
26
+ const candidate = await store.authMaintenance.findTerminalLogin(cutoff);
27
+ if (!candidate) return false;
28
+ await store.withSessionLock(candidate.adminUserId, async (scoped)=>{
29
+ await scoped.authMaintenance.pruneTerminalLogin({
30
+ ...candidate,
31
+ before: cutoff
32
+ });
33
+ });
34
+ return true;
35
+ }),
36
+ task('legacy-refresh', 30 * DAY, async (cutoff)=>await store.authMaintenance.purgeLegacyRefreshTokens(cutoff) === 100)
37
+ ];
38
+ }
39
+ export { createAuthMaintenanceTasks };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,14 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ import type { PasskeyManagementAction } from '@byline/auth';
9
+ import type { PasskeyAdminServices, PasskeyPageBinding } from '../../../services/admin-services-types.js';
10
+ export declare function signInWithPasskey(services: PasskeyAdminServices, isActive?: () => boolean): Promise<unknown>;
11
+ export declare function provePasskeyAction(services: PasskeyAdminServices, binding: PasskeyPageBinding, action: PasskeyManagementAction, password?: string): Promise<import("@byline/auth").PasskeyManagementGrant>;
12
+ export declare function enrollPasskey(services: PasskeyAdminServices, binding: PasskeyPageBinding, name: string, password?: string): Promise<import("@byline/auth").PasskeyCredentialSummary>;
13
+ /** Render fixed translated messages, never provider payloads or authenticator identifiers. */
14
+ export declare function passkeyErrorKey(error: unknown): string;
@@ -0,0 +1,118 @@
1
+ import { browserSupportsWebAuthn, startAuthentication, startRegistration } from "@simplewebauthn/browser";
2
+ function ready(binding, expiresAt) {
3
+ binding.assertCurrent();
4
+ if (expiresAt && Date.parse(expiresAt) <= Date.now()) throw Object.assign(new Error('Expired ceremony'), {
5
+ code: 'ERR_INVALID_TOKEN'
6
+ });
7
+ }
8
+ function supported() {
9
+ if (!browserSupportsWebAuthn()) throw Object.assign(new Error('WebAuthn unavailable'), {
10
+ code: 'PASSKEY_UNSUPPORTED'
11
+ });
12
+ }
13
+ async function signInWithPasskey(services, isActive = ()=>true) {
14
+ supported();
15
+ const captured = services.captureLogin(true);
16
+ const binding = {
17
+ ...captured,
18
+ assertCurrent: ()=>{
19
+ if (!isActive()) throw new Error('Sign-in page closed');
20
+ captured.assertCurrent();
21
+ }
22
+ };
23
+ const ceremony = await services.signInOptions({
24
+ data: {}
25
+ });
26
+ ready(binding, ceremony.expiresAt);
27
+ const response = await startAuthentication({
28
+ optionsJSON: ceremony.options
29
+ });
30
+ ready(binding, ceremony.expiresAt);
31
+ return services.signIn({
32
+ data: {
33
+ ceremonyId: ceremony.ceremonyId,
34
+ response
35
+ }
36
+ }, binding);
37
+ }
38
+ async function provePasskeyAction(services, binding, action, password) {
39
+ ready(binding);
40
+ const expectedSessionId = binding.expectedSessionId;
41
+ if (!expectedSessionId) throw Object.assign(new Error('Login changed'), {
42
+ code: 'ERR_SESSION_CHANGED'
43
+ });
44
+ if (void 0 !== password) {
45
+ const grant = await services.passwordProof({
46
+ data: {
47
+ expectedSessionId,
48
+ action,
49
+ password
50
+ }
51
+ });
52
+ ready(binding, grant.expiresAt);
53
+ return grant;
54
+ }
55
+ supported();
56
+ const ceremony = await services.proofOptions({
57
+ data: {
58
+ expectedSessionId,
59
+ action
60
+ }
61
+ });
62
+ ready(binding, ceremony.expiresAt);
63
+ const response = await startAuthentication({
64
+ optionsJSON: ceremony.options
65
+ });
66
+ ready(binding, ceremony.expiresAt);
67
+ const grant = await services.proof({
68
+ data: {
69
+ expectedSessionId,
70
+ ceremonyId: ceremony.ceremonyId,
71
+ response
72
+ }
73
+ });
74
+ ready(binding, grant.expiresAt);
75
+ return grant;
76
+ }
77
+ async function enrollPasskey(services, binding, name, password) {
78
+ supported();
79
+ const grant = await provePasskeyAction(services, binding, {
80
+ purpose: 'enroll'
81
+ }, password);
82
+ const expectedSessionId = binding.expectedSessionId;
83
+ const ceremony = await services.enrollmentOptions({
84
+ data: {
85
+ expectedSessionId,
86
+ grantId: grant.grantId
87
+ }
88
+ });
89
+ ready(binding, ceremony.expiresAt);
90
+ const response = await startRegistration({
91
+ optionsJSON: ceremony.options
92
+ });
93
+ ready(binding, ceremony.expiresAt);
94
+ return services.enrollment({
95
+ data: {
96
+ expectedSessionId,
97
+ ceremonyId: ceremony.ceremonyId,
98
+ response,
99
+ name
100
+ }
101
+ });
102
+ }
103
+ function passkeyErrorKey(error) {
104
+ const value = error;
105
+ const name = value?.cause?.name ?? value?.name;
106
+ if ('AbortError' === name || 'NotAllowedError' === name || value?.code === 'ERROR_CEREMONY_ABORTED') return 'passkeys.errors.cancelled';
107
+ if ('ConstraintError' === name || 'NotSupportedError' === name || value?.code === 'PASSKEY_UNSUPPORTED' || value?.code?.startsWith('ERROR_AUTHENTICATOR_MISSING_') || value?.code === 'ERROR_AUTHENTICATOR_NO_SUPPORTED_PUBKEYCREDPARAMS_ALG') return 'passkeys.errors.unsupported';
108
+ if ('UnknownError' === name || value?.code === 'ERROR_AUTHENTICATOR_GENERAL_ERROR') return 'passkeys.errors.device';
109
+ if (value?.code === 'ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED') return 'passkeys.errors.duplicate';
110
+ if (value?.code === 'ERR_INVALID_TOKEN') return 'passkeys.errors.expired';
111
+ if (value?.code === 'ERR_SESSION_CHANGED') return 'passkeys.errors.sessionChanged';
112
+ if (value?.code === 'ERR_PASSKEY_LIMIT') return 'passkeys.errors.limit';
113
+ if (value?.code === 'ERR_FORBIDDEN') return 'passkeys.errors.forbidden';
114
+ if (value?.code === 'ERR_INVALID_CREDENTIALS') return 'passkeys.errors.proof';
115
+ if (value?.status === 429) return 'passkeys.errors.rateLimited';
116
+ return 'passkeys.errors.unavailable';
117
+ }
118
+ export { enrollPasskey, passkeyErrorKey, provePasskeyAction, signInWithPasskey };
@@ -0,0 +1,26 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ import type React from 'react';
9
+ /** A host page's card component, so the passkey panel matches the cards around it. */
10
+ type PasskeySectionComponent = React.ComponentType<{
11
+ title: string;
12
+ children: React.ReactNode;
13
+ }>;
14
+ export declare function PasskeyManager({ targetUserId, targetIsSuperAdmin, section: Section, ownAccountNotice, }: {
15
+ targetUserId: string;
16
+ targetIsSuperAdmin?: boolean;
17
+ /** Renders the panel inside the page's own card; defaults to a standalone card. */
18
+ section?: PasskeySectionComponent;
19
+ /**
20
+ * Admin screens pass this to keep the admin (recovery) view even when the
21
+ * target is the signed-in user; it replaces the description in that case,
22
+ * typically pointing the user to their own account page.
23
+ */
24
+ ownAccountNotice?: React.ReactNode;
25
+ }): React.JSX.Element | null;
26
+ export {};