@byline/admin 6.7.2 → 6.9.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 (102) hide show
  1. package/dist/fields/blocks/blocks-field.js +33 -3
  2. package/dist/fields/blocks/blocks-field.module.js +1 -0
  3. package/dist/fields/blocks/blocks-field_module.css +19 -0
  4. package/dist/forms/form-page-chrome.d.ts +3 -2
  5. package/dist/forms/form-page-chrome.js +13 -1
  6. package/dist/forms/form-renderer.d.ts +2 -1
  7. package/dist/forms/form-renderer.js +23 -17
  8. package/dist/forms/unavailable-blocks.d.ts +15 -0
  9. package/dist/forms/unavailable-blocks.js +38 -0
  10. package/dist/modules/admin-account/components/change-password.js +1 -1
  11. package/dist/modules/admin-account/components/container.js +18 -3
  12. package/dist/modules/admin-account/components/container.module.js +2 -0
  13. package/dist/modules/admin-account/components/container_module.css +7 -0
  14. package/dist/modules/admin-account/components/preferences.js +1 -1
  15. package/dist/modules/admin-account/components/update.js +1 -1
  16. package/dist/modules/admin-roles/components/create.js +1 -1
  17. package/dist/modules/admin-roles/components/permissions.js +1 -1
  18. package/dist/modules/admin-roles/components/update.js +1 -1
  19. package/dist/modules/admin-users/abilities.d.ts +1 -0
  20. package/dist/modules/admin-users/abilities.js +8 -1
  21. package/dist/modules/admin-users/components/create.js +1 -1
  22. package/dist/modules/admin-users/components/roles.js +1 -1
  23. package/dist/modules/admin-users/components/set-password.js +1 -1
  24. package/dist/modules/admin-users/components/update.js +1 -1
  25. package/dist/modules/auth/auth-maintenance-repository.d.ts +31 -0
  26. package/dist/modules/auth/auth-maintenance-repository.js +1 -0
  27. package/dist/modules/auth/auth-maintenance.d.ts +11 -0
  28. package/dist/modules/auth/auth-maintenance.js +39 -0
  29. package/dist/modules/auth/auth-maintenance.test.node.d.ts +1 -0
  30. package/dist/modules/auth/components/passkey-browser.d.ts +14 -0
  31. package/dist/modules/auth/components/passkey-browser.js +118 -0
  32. package/dist/modules/auth/components/passkey-browser.test.node.d.ts +1 -0
  33. package/dist/modules/auth/components/passkey-manager.d.ts +26 -0
  34. package/dist/modules/auth/components/passkey-manager.js +406 -0
  35. package/dist/modules/auth/components/passkey-manager.module.js +22 -0
  36. package/dist/modules/auth/components/passkey-manager_module.css +104 -0
  37. package/dist/modules/auth/components/sign-in-form.js +62 -3
  38. package/dist/modules/auth/components/sign-in-form.module.js +5 -1
  39. package/dist/modules/auth/components/sign-in-form_module.css +40 -0
  40. package/dist/modules/auth/index.d.ts +5 -0
  41. package/dist/modules/auth/index.js +3 -0
  42. package/dist/modules/auth/jwt-session-provider.d.ts +12 -4
  43. package/dist/modules/auth/jwt-session-provider.js +135 -94
  44. package/dist/modules/auth/native-passkey-provider.d.ts +72 -0
  45. package/dist/modules/auth/native-passkey-provider.js +618 -0
  46. package/dist/modules/auth/passkey-rate-limiter.d.ts +14 -0
  47. package/dist/modules/auth/passkey-rate-limiter.js +60 -0
  48. package/dist/modules/auth/passkey-rate-limiter.test.node.d.ts +8 -0
  49. package/dist/modules/auth/passkey-storage-validation.d.ts +14 -0
  50. package/dist/modules/auth/passkey-storage-validation.js +59 -0
  51. package/dist/modules/auth/passkeys-repository.d.ts +96 -0
  52. package/dist/modules/auth/passkeys-repository.js +1 -0
  53. package/dist/modules/auth/refresh-tokens-repository.d.ts +1 -1
  54. package/dist/modules/auth/sign-in-rate-limiter.d.ts +2 -0
  55. package/dist/modules/auth/sign-in-rate-limiter.js +1 -1
  56. package/dist/services/admin-services-context.d.ts +1 -1
  57. package/dist/services/admin-services-types.d.ts +60 -0
  58. package/dist/store.d.ts +4 -0
  59. package/package.json +16 -9
  60. package/src/fields/blocks/blocks-field.module.css +23 -0
  61. package/src/fields/blocks/blocks-field.tsx +26 -2
  62. package/src/forms/form-page-chrome.tsx +19 -1
  63. package/src/forms/form-renderer-capabilities.test.tsx +6 -6
  64. package/src/forms/form-renderer.tsx +24 -15
  65. package/src/forms/unavailable-blocks.test.tsx +152 -0
  66. package/src/forms/unavailable-blocks.tsx +58 -0
  67. package/src/modules/admin-account/components/change-password.tsx +1 -1
  68. package/src/modules/admin-account/components/container.module.css +9 -0
  69. package/src/modules/admin-account/components/container.tsx +12 -3
  70. package/src/modules/admin-account/components/preferences.tsx +1 -1
  71. package/src/modules/admin-account/components/update.tsx +1 -1
  72. package/src/modules/admin-roles/components/create.tsx +1 -1
  73. package/src/modules/admin-roles/components/permissions.tsx +1 -1
  74. package/src/modules/admin-roles/components/update.tsx +1 -1
  75. package/src/modules/admin-users/abilities.ts +7 -0
  76. package/src/modules/admin-users/components/create.tsx +1 -1
  77. package/src/modules/admin-users/components/roles.tsx +1 -1
  78. package/src/modules/admin-users/components/set-password.tsx +1 -1
  79. package/src/modules/admin-users/components/update.tsx +1 -1
  80. package/src/modules/auth/auth-maintenance-repository.ts +26 -0
  81. package/src/modules/auth/auth-maintenance.test.node.ts +110 -0
  82. package/src/modules/auth/auth-maintenance.ts +62 -0
  83. package/src/modules/auth/components/passkey-browser.test.node.ts +188 -0
  84. package/src/modules/auth/components/passkey-browser.ts +139 -0
  85. package/src/modules/auth/components/passkey-manager.module.css +100 -0
  86. package/src/modules/auth/components/passkey-manager.test.tsx +189 -0
  87. package/src/modules/auth/components/passkey-manager.tsx +386 -0
  88. package/src/modules/auth/components/sign-in-form.module.css +37 -0
  89. package/src/modules/auth/components/sign-in-form.test.tsx +92 -0
  90. package/src/modules/auth/components/sign-in-form.tsx +65 -4
  91. package/src/modules/auth/index.ts +15 -0
  92. package/src/modules/auth/jwt-session-provider.ts +116 -44
  93. package/src/modules/auth/native-passkey-provider.ts +710 -0
  94. package/src/modules/auth/passkey-rate-limiter.test.node.ts +83 -0
  95. package/src/modules/auth/passkey-rate-limiter.ts +76 -0
  96. package/src/modules/auth/passkey-storage-validation.ts +108 -0
  97. package/src/modules/auth/passkeys-repository.ts +97 -0
  98. package/src/modules/auth/refresh-tokens-repository.ts +1 -1
  99. package/src/modules/auth/sign-in-rate-limiter.ts +1 -1
  100. package/src/services/admin-services-context.tsx +2 -0
  101. package/src/services/admin-services-types.ts +60 -0
  102. package/src/store.ts +4 -0
@@ -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 {};
@@ -0,0 +1,406 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useId, useRef, useState } from "react";
4
+ import { getAdminConfig } from "@byline/core";
5
+ import { useTranslation } from "@byline/i18n/react";
6
+ import { Alert, Button, CloseIcon, Drawer, IconButton, Input, InputPassword, Modal } from "@byline/ui/react";
7
+ import { useBylineAdminServices } from "../../../services/admin-services-context.js";
8
+ import { enrollPasskey, passkeyErrorKey, provePasskeyAction } from "./passkey-browser.js";
9
+ import passkey_manager_module from "./passkey-manager.module.js";
10
+ function PasskeyManager({ targetUserId, targetIsSuperAdmin = false, section: Section, ownAccountNotice }) {
11
+ const { passkeys } = useBylineAdminServices();
12
+ const { t } = useTranslation('byline-admin');
13
+ const id = useId();
14
+ const binding = useRef(null);
15
+ const [enabled, setEnabled] = useState(false);
16
+ const [loading, setLoading] = useState(true);
17
+ const [allowed, setAllowed] = useState(false);
18
+ const [self, setSelf] = useState(false);
19
+ const [credentials, setCredentials] = useState([]);
20
+ const [edit, setEdit] = useState(null);
21
+ const [name, setName] = useState('');
22
+ const [password, setPassword] = useState('');
23
+ const [pending, setPending] = useState(false);
24
+ const [error, setError] = useState(null);
25
+ const [notice, setNotice] = useState(null);
26
+ const mounted = useRef(false);
27
+ useEffect(()=>{
28
+ let active = true;
29
+ mounted.current = true;
30
+ setAllowed(false);
31
+ setEnabled(false);
32
+ setLoading(true);
33
+ setError(null);
34
+ if (passkeys) (async ()=>{
35
+ if (!(await passkeys.capability()).enabled || !active) return;
36
+ setEnabled(true);
37
+ const captured = passkeys.captureLogin();
38
+ const page = {
39
+ ...captured,
40
+ assertCurrent: ()=>{
41
+ if (!active) throw new Error('Passkey page closed');
42
+ captured.assertCurrent();
43
+ }
44
+ };
45
+ binding.current = page;
46
+ const actor = await passkeys.managementContext(page);
47
+ const isSelf = actor.id === targetUserId;
48
+ if (!isSelf && (!actor.canManagePasskeys || targetIsSuperAdmin && !actor.isSuperAdmin)) return;
49
+ page.assertCurrent();
50
+ setSelf(isSelf);
51
+ setAllowed(true);
52
+ const rows = await passkeys.list({
53
+ data: {
54
+ expectedSessionId: page.expectedSessionId,
55
+ targetUserId
56
+ }
57
+ });
58
+ page.assertCurrent();
59
+ setCredentials(rows);
60
+ setLoading(false);
61
+ })().catch((error)=>{
62
+ if (active) setError(passkeyErrorKey(error));
63
+ });
64
+ return ()=>{
65
+ active = false;
66
+ mounted.current = false;
67
+ binding.current = null;
68
+ };
69
+ }, [
70
+ passkeys,
71
+ targetUserId,
72
+ targetIsSuperAdmin
73
+ ]);
74
+ function open(value) {
75
+ setEdit(value);
76
+ setName('rename' === value.kind ? value.credential.name : '');
77
+ setPassword('');
78
+ setError(null);
79
+ setNotice(null);
80
+ }
81
+ function close() {
82
+ if (pending) return;
83
+ setEdit(null);
84
+ setPassword('');
85
+ setError(null);
86
+ }
87
+ async function perform(usePassword) {
88
+ const page = binding.current;
89
+ if (!passkeys || !edit || !page?.expectedSessionId || pending) return;
90
+ setPending(true);
91
+ setError(null);
92
+ const suppliedPassword = usePassword ? password : void 0;
93
+ setPassword('');
94
+ try {
95
+ page.assertCurrent();
96
+ const expectedSessionId = page.expectedSessionId;
97
+ if ('rename' === edit.kind) {
98
+ const updated = await passkeys.rename({
99
+ data: {
100
+ expectedSessionId,
101
+ credentialRecordId: edit.credential.id,
102
+ name: name.trim()
103
+ }
104
+ });
105
+ page.assertCurrent();
106
+ setCredentials((rows)=>rows.map((row)=>row.id === updated.id ? updated : row));
107
+ setNotice('passkeys.renamed');
108
+ } else if ('enroll' === edit.kind) {
109
+ const created = await enrollPasskey(passkeys, page, name.trim(), suppliedPassword);
110
+ page.assertCurrent();
111
+ setCredentials((rows)=>[
112
+ ...rows,
113
+ created
114
+ ]);
115
+ setNotice('passkeys.added');
116
+ } else {
117
+ const grant = await provePasskeyAction(passkeys, page, {
118
+ purpose: 'remove',
119
+ targetUserId,
120
+ credentialRecordId: edit.credential.id
121
+ }, suppliedPassword);
122
+ page.assertCurrent();
123
+ const result = await passkeys.remove({
124
+ data: {
125
+ expectedSessionId,
126
+ grantId: grant.grantId
127
+ }
128
+ });
129
+ if (result.signedOut) return void window.location.assign(getAdminConfig().routes.signIn);
130
+ page.assertCurrent();
131
+ setCredentials((rows)=>rows.filter((row)=>row.id !== edit.credential.id));
132
+ setNotice('passkeys.removed');
133
+ }
134
+ setEdit(null);
135
+ } catch (error) {
136
+ if (mounted.current) setError(passkeyErrorKey(error));
137
+ } finally{
138
+ if (mounted.current) setPending(false);
139
+ }
140
+ }
141
+ if (!passkeys || !enabled) return null;
142
+ if (!allowed) return error ? /*#__PURE__*/ jsx(Alert, {
143
+ intent: "danger",
144
+ children: t(error)
145
+ }) : null;
146
+ const selfService = self && void 0 === ownAccountNotice;
147
+ const fields = /*#__PURE__*/ jsxs(Fragment, {
148
+ children: [
149
+ error && /*#__PURE__*/ jsx(Alert, {
150
+ intent: "danger",
151
+ children: t(error)
152
+ }),
153
+ edit?.kind === 'remove' ? /*#__PURE__*/ jsx("p", {
154
+ children: t('passkeys.removeWarning', {
155
+ name: edit.credential.name
156
+ })
157
+ }) : /*#__PURE__*/ jsx(Input, {
158
+ id: `${id}-name`,
159
+ name: "passkey-name",
160
+ label: t('passkeys.name'),
161
+ value: name,
162
+ maxLength: 80,
163
+ disabled: pending,
164
+ onChange: (event)=>setName(event.currentTarget.value)
165
+ }),
166
+ edit?.kind !== 'rename' && /*#__PURE__*/ jsxs(Fragment, {
167
+ children: [
168
+ /*#__PURE__*/ jsx("p", {
169
+ children: t("passkeys.proofDescription")
170
+ }),
171
+ /*#__PURE__*/ jsx(InputPassword, {
172
+ id: `${id}-password`,
173
+ name: "passkey-password",
174
+ label: t('common.fields.password'),
175
+ autoComplete: "current-password",
176
+ value: password,
177
+ disabled: pending,
178
+ onChange: (event)=>setPassword(event.currentTarget.value),
179
+ showPasswordLabel: t('common.actions.showPassword'),
180
+ hidePasswordLabel: t('common.actions.hidePassword')
181
+ })
182
+ ]
183
+ }),
184
+ pending && /*#__PURE__*/ jsx("p", {
185
+ role: "status",
186
+ children: t('passkeys.waiting')
187
+ })
188
+ ]
189
+ });
190
+ const actions = /*#__PURE__*/ jsxs(Fragment, {
191
+ children: [
192
+ /*#__PURE__*/ jsx(Button, {
193
+ type: "button",
194
+ size: "sm",
195
+ intent: "noeffect",
196
+ disabled: pending,
197
+ onClick: close,
198
+ children: t('common.actions.cancel')
199
+ }),
200
+ edit?.kind === 'rename' ? /*#__PURE__*/ jsx(Button, {
201
+ type: "button",
202
+ size: "sm",
203
+ disabled: pending || !name.trim(),
204
+ onClick: ()=>void perform(false),
205
+ children: t('common.actions.save')
206
+ }) : /*#__PURE__*/ jsxs(Fragment, {
207
+ children: [
208
+ /*#__PURE__*/ jsx(Button, {
209
+ type: "button",
210
+ size: "sm",
211
+ disabled: pending || !password || edit?.kind === 'enroll' && !name.trim(),
212
+ onClick: ()=>void perform(true),
213
+ children: t('passkeys.usePassword')
214
+ }),
215
+ /*#__PURE__*/ jsx(Button, {
216
+ type: "button",
217
+ size: "sm",
218
+ disabled: pending || edit?.kind === 'enroll' && !name.trim(),
219
+ onClick: ()=>void perform(false),
220
+ children: t('passkeys.usePasskey')
221
+ })
222
+ ]
223
+ })
224
+ ]
225
+ });
226
+ const body = /*#__PURE__*/ jsxs(Fragment, {
227
+ children: [
228
+ self && void 0 !== ownAccountNotice ? ownAccountNotice : /*#__PURE__*/ jsx("p", {
229
+ children: t(selfService ? "passkeys.description" : "passkeys.recoveryDescription")
230
+ }),
231
+ !edit && error && /*#__PURE__*/ jsx(Alert, {
232
+ intent: "danger",
233
+ children: t(error)
234
+ }),
235
+ notice && /*#__PURE__*/ jsx("p", {
236
+ role: "status",
237
+ children: t(notice)
238
+ }),
239
+ loading ? /*#__PURE__*/ jsx("p", {
240
+ role: "status",
241
+ children: t('common.loading')
242
+ }) : 0 === credentials.length ? /*#__PURE__*/ jsx("p", {
243
+ className: passkey_manager_module.empty,
244
+ children: t('passkeys.empty')
245
+ }) : /*#__PURE__*/ jsx("ul", {
246
+ className: passkey_manager_module.list,
247
+ children: credentials.map((credential)=>/*#__PURE__*/ jsxs("li", {
248
+ className: passkey_manager_module.row,
249
+ children: [
250
+ /*#__PURE__*/ jsxs("div", {
251
+ children: [
252
+ /*#__PURE__*/ jsx("strong", {
253
+ children: credential.name
254
+ }),
255
+ /*#__PURE__*/ jsxs("p", {
256
+ children: [
257
+ t('passkeys.lastUsed'),
258
+ ":",
259
+ ' ',
260
+ credential.lastUsedAt ? new Date(credential.lastUsedAt).toLocaleString() : t('common.never')
261
+ ]
262
+ })
263
+ ]
264
+ }),
265
+ /*#__PURE__*/ jsxs("div", {
266
+ className: passkey_manager_module.actions,
267
+ children: [
268
+ selfService && /*#__PURE__*/ jsx(Button, {
269
+ type: "button",
270
+ size: "sm",
271
+ variant: "text",
272
+ onClick: ()=>open({
273
+ kind: 'rename',
274
+ credential
275
+ }),
276
+ "aria-label": t('passkeys.renameNamed', {
277
+ name: credential.name
278
+ }),
279
+ children: t('passkeys.rename')
280
+ }),
281
+ /*#__PURE__*/ jsx(Button, {
282
+ type: "button",
283
+ size: "sm",
284
+ intent: "danger",
285
+ variant: "text",
286
+ onClick: ()=>open({
287
+ kind: 'remove',
288
+ credential
289
+ }),
290
+ "aria-label": t('passkeys.removeNamed', {
291
+ name: credential.name
292
+ }),
293
+ children: t('passkeys.remove')
294
+ })
295
+ ]
296
+ })
297
+ ]
298
+ }, credential.id))
299
+ }),
300
+ selfService && /*#__PURE__*/ jsx(Button, {
301
+ type: "button",
302
+ size: "sm",
303
+ disabled: loading,
304
+ onClick: ()=>open({
305
+ kind: 'enroll'
306
+ }),
307
+ className: passkey_manager_module.add,
308
+ children: t('passkeys.add')
309
+ }),
310
+ /*#__PURE__*/ jsx(Modal, {
311
+ isOpen: edit?.kind === 'remove',
312
+ onDismiss: close,
313
+ closeOnOverlayClick: false,
314
+ ariaLabel: t('passkeys.remove'),
315
+ children: /*#__PURE__*/ jsxs(Modal.Container, {
316
+ className: `byline-passkey-modal ${passkey_manager_module.modal}`,
317
+ children: [
318
+ /*#__PURE__*/ jsx(Modal.Header, {
319
+ children: /*#__PURE__*/ jsx("h2", {
320
+ children: t('passkeys.remove')
321
+ })
322
+ }),
323
+ /*#__PURE__*/ jsx(Modal.Content, {
324
+ children: fields
325
+ }),
326
+ /*#__PURE__*/ jsx(Modal.Actions, {
327
+ className: `byline-passkey-modal-actions ${passkey_manager_module["modal-actions"]}`,
328
+ children: actions
329
+ })
330
+ ]
331
+ })
332
+ }),
333
+ /*#__PURE__*/ jsx(Drawer, {
334
+ id: `${id}-passkey-drawer`,
335
+ closeOnOverlayClick: false,
336
+ width: "medium",
337
+ topOffset: "46px",
338
+ isOpen: null !== edit && 'remove' !== edit.kind,
339
+ onDismiss: close,
340
+ className: `byline-passkey-drawer ${passkey_manager_module.drawer}`,
341
+ children: /*#__PURE__*/ jsxs(Drawer.Container, {
342
+ "aria-hidden": null === edit || 'remove' === edit.kind,
343
+ className: `byline-passkey-drawer-body ${passkey_manager_module["drawer-body"]}`,
344
+ children: [
345
+ /*#__PURE__*/ jsxs(Drawer.TopActions, {
346
+ children: [
347
+ /*#__PURE__*/ jsx("button", {
348
+ type: "button",
349
+ tabIndex: 0,
350
+ className: passkey_manager_module["drawer-skip"],
351
+ children: "no action"
352
+ }),
353
+ /*#__PURE__*/ jsx(IconButton, {
354
+ "aria-label": t('common.actions.close'),
355
+ size: "sm",
356
+ onClick: close,
357
+ children: /*#__PURE__*/ jsx(CloseIcon, {
358
+ width: "14px",
359
+ height: "14px",
360
+ svgClassName: "white-icon stroke-white"
361
+ })
362
+ })
363
+ ]
364
+ }),
365
+ /*#__PURE__*/ jsx(Drawer.Header, {
366
+ children: /*#__PURE__*/ jsx("h2", {
367
+ children: t(edit?.kind === 'rename' ? 'passkeys.rename' : 'passkeys.add')
368
+ })
369
+ }),
370
+ /*#__PURE__*/ jsx(Drawer.Content, {
371
+ children: /*#__PURE__*/ jsxs("div", {
372
+ className: `byline-passkey-drawer-form ${passkey_manager_module["drawer-form"]}`,
373
+ children: [
374
+ fields,
375
+ /*#__PURE__*/ jsx("div", {
376
+ className: `byline-passkey-drawer-actions ${passkey_manager_module["drawer-actions"]} ${passkey_manager_module["modal-actions"]}`,
377
+ children: actions
378
+ })
379
+ ]
380
+ })
381
+ })
382
+ ]
383
+ })
384
+ })
385
+ ]
386
+ });
387
+ if (Section) return /*#__PURE__*/ jsx(Section, {
388
+ title: t('passkeys.title'),
389
+ children: /*#__PURE__*/ jsx("div", {
390
+ className: "byline-passkey-manager",
391
+ children: body
392
+ })
393
+ });
394
+ return /*#__PURE__*/ jsxs("section", {
395
+ className: `byline-passkey-manager ${passkey_manager_module.manager}`,
396
+ "aria-labelledby": `${id}-title`,
397
+ children: [
398
+ /*#__PURE__*/ jsx("h2", {
399
+ id: `${id}-title`,
400
+ children: t('passkeys.title')
401
+ }),
402
+ body
403
+ ]
404
+ });
405
+ }
406
+ export { PasskeyManager };
@@ -0,0 +1,22 @@
1
+ import "./passkey-manager_module.css";
2
+ const passkey_manager_module = {
3
+ manager: "manager-mPrpUs",
4
+ list: "list-Mb1OlA",
5
+ empty: "empty-pDRaVX",
6
+ add: "add-Mmfyxh",
7
+ row: "row-We5Ooj",
8
+ actions: "actions-Cu2Zmf",
9
+ modal: "modal-T_EV6A",
10
+ "drawer-body": "drawer-body-GlBpJK",
11
+ drawerBody: "drawer-body-GlBpJK",
12
+ "drawer-form": "drawer-form-Qi9gs0",
13
+ drawerForm: "drawer-form-Qi9gs0",
14
+ "drawer-actions": "drawer-actions-wJGh5j",
15
+ drawerActions: "drawer-actions-wJGh5j",
16
+ "drawer-skip": "drawer-skip-Vzeo1m",
17
+ drawerSkip: "drawer-skip-Vzeo1m",
18
+ "modal-actions": "modal-actions-j4wk2d",
19
+ modalActions: "modal-actions-j4wk2d",
20
+ drawer: "drawer-pc4pSE"
21
+ };
22
+ export default passkey_manager_module;
@@ -0,0 +1,104 @@
1
+ .manager-mPrpUs {
2
+ border: 1px solid var(--border-color, #8885);
3
+ border-radius: .5rem;
4
+ padding: 1.25rem;
5
+ }
6
+
7
+ .list-Mb1OlA {
8
+ margin: 1rem 0;
9
+ padding: 0;
10
+ list-style: none;
11
+ }
12
+
13
+ .empty-pDRaVX + .add-Mmfyxh {
14
+ margin-top: .25rem;
15
+ }
16
+
17
+ .row-We5Ooj {
18
+ border-bottom: 1px solid var(--border-color, #8885);
19
+ overflow-wrap: anywhere;
20
+ flex-wrap: wrap;
21
+ justify-content: space-between;
22
+ align-items: center;
23
+ gap: .75rem;
24
+ padding: .75rem 0;
25
+ display: flex;
26
+ }
27
+
28
+ .row-We5Ooj p {
29
+ margin: .25rem 0;
30
+ font-size: .875rem;
31
+ }
32
+
33
+ .actions-Cu2Zmf {
34
+ flex-wrap: wrap;
35
+ gap: .5rem;
36
+ display: flex;
37
+ }
38
+
39
+ .modal-T_EV6A {
40
+ width: calc(100% - 2 * var(--spacing-8));
41
+ max-width: 540px;
42
+ margin-bottom: var(--spacing-8);
43
+ }
44
+
45
+ .drawer-body-GlBpJK {
46
+ padding: var(--spacing-8);
47
+ }
48
+
49
+ .drawer-form-Qi9gs0 {
50
+ gap: var(--spacing-16);
51
+ padding-top: var(--spacing-8);
52
+ flex-direction: column;
53
+ display: flex;
54
+ }
55
+
56
+ .drawer-actions-wJGh5j {
57
+ justify-content: flex-end;
58
+ gap: var(--spacing-12);
59
+ margin-top: var(--spacing-8);
60
+ display: flex;
61
+ }
62
+
63
+ .drawer-skip-Vzeo1m {
64
+ clip: rect(0, 0, 0, 0);
65
+ white-space: nowrap;
66
+ border: 0;
67
+ width: 1px;
68
+ height: 1px;
69
+ margin: -1px;
70
+ padding: 0;
71
+ position: absolute;
72
+ overflow: hidden;
73
+ }
74
+
75
+ .modal-actions-j4wk2d {
76
+ flex-direction: column-reverse;
77
+ align-items: stretch;
78
+ }
79
+
80
+ @media (max-width: 39.99rem) {
81
+ .modal-actions-j4wk2d > * {
82
+ min-height: 2.5rem;
83
+ }
84
+ }
85
+
86
+ @media (min-width: 40rem) {
87
+ .modal-T_EV6A {
88
+ width: 100%;
89
+ margin-bottom: 0;
90
+ }
91
+
92
+ .modal-actions-j4wk2d {
93
+ align-items: center;
94
+ gap: var(--spacing-8);
95
+ flex-flow: wrap;
96
+ }
97
+ }
98
+
99
+ @media (min-width: 48rem) {
100
+ .drawer-pc4pSE {
101
+ width: 540px;
102
+ }
103
+ }
104
+