@strands.gg/accui 1.3.2 → 1.4.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.
@@ -370,7 +370,7 @@ const _sfc_main$o = /* @__PURE__ */ vue.defineComponent({
370
370
  placeholder: { type: String, required: false },
371
371
  disabled: { type: Boolean, required: false },
372
372
  required: { type: Boolean, required: false },
373
- error: { type: String, required: false },
373
+ error: { type: Object, required: false },
374
374
  helpText: { type: String, required: false },
375
375
  autocomplete: { type: String, required: false },
376
376
  size: { type: String, required: false, default: "md" }
@@ -539,7 +539,7 @@ function _sfc_render$o(_ctx, _cache, $props, $setup, $data, $options) {
539
539
  $props.error ? (vue.openBlock(), vue.createElementBlock(
540
540
  "p",
541
541
  _hoisted_9$b,
542
- vue.toDisplayString($props.error),
542
+ vue.toDisplayString($props.error.message),
543
543
  1
544
544
  /* TEXT */
545
545
  )) : $props.helpText ? (vue.openBlock(), vue.createElementBlock(
@@ -1031,7 +1031,11 @@ function useStrandsMfa() {
1031
1031
  const { getUrl } = useStrandsConfig.useStrandsConfig();
1032
1032
  const { currentSession } = useStrandsAuth.useStrandsAuth();
1033
1033
  const hasMfaDevices = vue.computed(() => mfaDevices.value.length > 0);
1034
- const activeMfaDevices = vue.computed(() => mfaDevices.value.filter((d) => d.is_active));
1034
+ const activeMfaDevices = vue.computed(
1035
+ () => mfaDevices.value.filter(
1036
+ (d) => d.is_active && d.device_type !== "hardware" && d.device_type !== "passkey"
1037
+ )
1038
+ );
1035
1039
  const makeAuthenticatedRequest = async (url, options = {}) => {
1036
1040
  if (!currentSession.value?.accessToken) {
1037
1041
  throw new Error("No access token available");
@@ -1266,7 +1270,12 @@ const _sfc_main$i = /* @__PURE__ */ vue.defineComponent({
1266
1270
  const cooldownActive = vue.ref(false);
1267
1271
  const cooldownSeconds = vue.ref(0);
1268
1272
  let cooldownInterval = null;
1269
- const availableMethods = vue.computed(() => props.availableMfaMethods || []);
1273
+ const availableMethods = vue.computed(() => {
1274
+ const methods = props.availableMfaMethods || [];
1275
+ return methods.filter(
1276
+ (method) => method.device_type !== "hardware" && method.device_type !== "passkey"
1277
+ );
1278
+ });
1270
1279
  vue.watch(() => availableMethods.value, (methods) => {
1271
1280
  if (methods.length === 1) {
1272
1281
  selectedMethod.value = methods[0];
@@ -1392,14 +1401,33 @@ const _sfc_main$i = /* @__PURE__ */ vue.defineComponent({
1392
1401
  throw new Error("Hardware keys are not supported in this browser");
1393
1402
  }
1394
1403
  const challengeResponse = await getMfaWebAuthnChallenge(selectedMethod.value.id);
1395
- const challenge = challengeResponse.challenge;
1396
- console.log("Received WebAuthn challenge:", challenge);
1404
+ const challengeData = challengeResponse.challenge.publicKey || challengeResponse.challenge;
1405
+ const base64ToUint8Array = (base64) => {
1406
+ if (!base64 || typeof base64 !== "string") {
1407
+ return new Uint8Array(0);
1408
+ }
1409
+ const padding = "=".repeat((4 - base64.length % 4) % 4);
1410
+ const b64 = (base64 + padding).replace(/-/g, "+").replace(/_/g, "/");
1411
+ const rawData = window.atob(b64);
1412
+ const outputArray = new Uint8Array(rawData.length);
1413
+ for (let i = 0; i < rawData.length; ++i) {
1414
+ outputArray[i] = rawData.charCodeAt(i);
1415
+ }
1416
+ return outputArray;
1417
+ };
1397
1418
  const isPasskey = selectedMethod.value.device_type === "passkey";
1398
1419
  const publicKeyCredentialRequestOptions = {
1399
- ...challenge,
1400
- timeout: isPasskey ? 3e5 : challenge.timeout || 6e4,
1420
+ ...challengeData,
1421
+ challenge: challengeData.challenge ? base64ToUint8Array(challengeData.challenge) : new Uint8Array(32),
1422
+ timeout: isPasskey ? 3e5 : challengeData.timeout || 6e4,
1401
1423
  userVerification: isPasskey ? "required" : "discouraged"
1402
1424
  };
1425
+ if (challengeData.allowCredentials && Array.isArray(challengeData.allowCredentials)) {
1426
+ publicKeyCredentialRequestOptions.allowCredentials = challengeData.allowCredentials.map((cred) => ({
1427
+ ...cred,
1428
+ id: base64ToUint8Array(cred.id)
1429
+ }));
1430
+ }
1403
1431
  const credential = await navigator.credentials.get({
1404
1432
  publicKey: publicKeyCredentialRequestOptions
1405
1433
  });
@@ -1440,6 +1468,7 @@ const _sfc_main$i = /* @__PURE__ */ vue.defineComponent({
1440
1468
  await verifyMfa(selectedMethod.value.id, JSON.stringify(credentialData));
1441
1469
  emit("success");
1442
1470
  } catch (error) {
1471
+ console.error("Hardware key authentication error:", error);
1443
1472
  emit("error", error instanceof Error ? error.message : "Hardware key authentication failed");
1444
1473
  }
1445
1474
  };
@@ -1492,7 +1521,7 @@ const _hoisted_19$6 = {
1492
1521
  key: 3,
1493
1522
  class: "text-center space-y-4"
1494
1523
  };
1495
- const _hoisted_20$4 = { class: "bg-blue-50 border border-blue-200 rounded-lg p-4" };
1524
+ const _hoisted_20$5 = { class: "bg-blue-50 border border-blue-200 rounded-lg p-4" };
1496
1525
  const _hoisted_21$4 = { class: "flex items-center space-x-3" };
1497
1526
  const _hoisted_22$3 = { class: "text-left" };
1498
1527
  const _hoisted_23$2 = { class: "font-medium text-blue-900" };
@@ -1506,21 +1535,21 @@ const _hoisted_27$2 = {
1506
1535
  key: 4,
1507
1536
  class: "space-y-4"
1508
1537
  };
1509
- const _hoisted_28$2 = {
1538
+ const _hoisted_28$1 = {
1510
1539
  key: 0,
1511
1540
  class: "bg-green-50 border border-green-200 rounded-lg p-4"
1512
1541
  };
1513
- const _hoisted_29$2 = {
1542
+ const _hoisted_29$1 = {
1514
1543
  key: 1,
1515
1544
  class: "text-sm text-gray-600"
1516
1545
  };
1517
- const _hoisted_30$2 = {
1546
+ const _hoisted_30$1 = {
1518
1547
  key: 2,
1519
1548
  class: "flex justify-between text-sm"
1520
1549
  };
1521
- const _hoisted_31$2 = ["disabled"];
1522
- const _hoisted_32$2 = { class: "pt-4 border-t border-gray-200" };
1523
- const _hoisted_33$2 = {
1550
+ const _hoisted_31$1 = ["disabled"];
1551
+ const _hoisted_32$1 = { class: "pt-4 border-t border-gray-200" };
1552
+ const _hoisted_33$1 = {
1524
1553
  key: 3,
1525
1554
  class: "space-y-4 bg-amber-50 border border-amber-200 rounded-lg p-4"
1526
1555
  };
@@ -1729,7 +1758,7 @@ function _sfc_render$i(_ctx, _cache, $props, $setup, $data, $options) {
1729
1758
  ])) : vue.createCommentVNode("v-if", true),
1730
1759
  vue.createCommentVNode(" Hardware Key & Passkey Authentication "),
1731
1760
  $setup.selectedMethod?.device_type === "hardware" || $setup.selectedMethod?.device_type === "passkey" ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_19$6, [
1732
- vue.createElementVNode("div", _hoisted_20$4, [
1761
+ vue.createElementVNode("div", _hoisted_20$5, [
1733
1762
  vue.createElementVNode("div", _hoisted_21$4, [
1734
1763
  _cache[11] || (_cache[11] = vue.createElementVNode(
1735
1764
  "div",
@@ -1859,7 +1888,7 @@ function _sfc_render$i(_ctx, _cache, $props, $setup, $data, $options) {
1859
1888
  vue.createCommentVNode(" Code Input "),
1860
1889
  $setup.selectedMethod && $setup.selectedMethod.device_type !== "hardware" && $setup.selectedMethod.device_type !== "passkey" && ($setup.selectedMethod.device_type !== "email" || $setup.emailCodeSent) ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_27$2, [
1861
1890
  vue.createCommentVNode(" Email confirmation "),
1862
- $setup.selectedMethod.device_type === "email" && $setup.emailCodeSent ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_28$2, _cache[14] || (_cache[14] = [
1891
+ $setup.selectedMethod.device_type === "email" && $setup.emailCodeSent ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_28$1, _cache[14] || (_cache[14] = [
1863
1892
  vue.createElementVNode(
1864
1893
  "div",
1865
1894
  { class: "flex items-start space-x-2" },
@@ -1900,21 +1929,21 @@ function _sfc_render$i(_ctx, _cache, $props, $setup, $data, $options) {
1900
1929
  vue.createCommentVNode(" TOTP Help "),
1901
1930
  $setup.selectedMethod.device_type === "totp" ? (vue.openBlock(), vue.createElementBlock(
1902
1931
  "div",
1903
- _hoisted_29$2,
1932
+ _hoisted_29$1,
1904
1933
  ' Open your authenticator app and enter the 6-digit code for "' + vue.toDisplayString($setup.selectedMethod.device_name) + '" ',
1905
1934
  1
1906
1935
  /* TEXT */
1907
1936
  )) : vue.createCommentVNode("v-if", true),
1908
1937
  vue.createCommentVNode(" Email Resend "),
1909
- $setup.selectedMethod.device_type === "email" ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_30$2, [
1938
+ $setup.selectedMethod.device_type === "email" ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_30$1, [
1910
1939
  vue.createElementVNode("button", {
1911
1940
  onClick: $setup.sendEmailCode,
1912
1941
  disabled: $setup.loading || $setup.cooldownActive,
1913
1942
  class: "text-strands-600 hover:text-strands-800 disabled:text-gray-400 disabled:cursor-not-allowed"
1914
- }, vue.toDisplayString($setup.cooldownActive ? `Resend in ${$setup.cooldownSeconds}s` : "Resend Code"), 9, _hoisted_31$2)
1943
+ }, vue.toDisplayString($setup.cooldownActive ? `Resend in ${$setup.cooldownSeconds}s` : "Resend Code"), 9, _hoisted_31$1)
1915
1944
  ])) : vue.createCommentVNode("v-if", true),
1916
1945
  vue.createCommentVNode(" Backup Codes Option "),
1917
- vue.createElementVNode("div", _hoisted_32$2, [
1946
+ vue.createElementVNode("div", _hoisted_32$1, [
1918
1947
  vue.createElementVNode("button", {
1919
1948
  onClick: _cache[3] || (_cache[3] = ($event) => $setup.showBackupCodeInput = !$setup.showBackupCodeInput),
1920
1949
  class: "flex items-center text-sm text-gray-600 hover:text-gray-800 transition-colors"
@@ -1946,7 +1975,7 @@ function _sfc_render$i(_ctx, _cache, $props, $setup, $data, $options) {
1946
1975
  ])
1947
1976
  ]),
1948
1977
  vue.createCommentVNode(" Backup Code Input "),
1949
- $setup.showBackupCodeInput ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_33$2, [
1978
+ $setup.showBackupCodeInput ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_33$1, [
1950
1979
  _cache[16] || (_cache[16] = vue.createElementVNode(
1951
1980
  "div",
1952
1981
  { class: "flex items-start space-x-2 mb-3" },
@@ -3388,7 +3417,7 @@ const _hoisted_16$5 = { class: "alert-error" };
3388
3417
  const _hoisted_17$5 = { class: "flex items-start gap-3" };
3389
3418
  const _hoisted_18$5 = { class: "font-medium" };
3390
3419
  const _hoisted_19$5 = { class: "mt-8 text-center" };
3391
- const _hoisted_20$3 = { class: "text-sm text-neutral-600" };
3420
+ const _hoisted_20$4 = { class: "text-sm text-neutral-600" };
3392
3421
  const _hoisted_21$3 = { key: 0 };
3393
3422
  const _hoisted_22$2 = { class: "text-neutral-400 text-sm" };
3394
3423
  function _sfc_render$f(_ctx, _cache, $props, $setup, $data, $options) {
@@ -3653,7 +3682,7 @@ function _sfc_render$f(_ctx, _cache, $props, $setup, $data, $options) {
3653
3682
  ])) : vue.createCommentVNode("v-if", true),
3654
3683
  vue.createCommentVNode(" Sign in link "),
3655
3684
  vue.createElementVNode("div", _hoisted_19$5, [
3656
- vue.createElementVNode("p", _hoisted_20$3, [
3685
+ vue.createElementVNode("p", _hoisted_20$4, [
3657
3686
  _cache[10] || (_cache[10] = vue.createTextVNode(
3658
3687
  " Already have an account? ",
3659
3688
  -1
@@ -7803,7 +7832,7 @@ const _hoisted_18$3 = {
7803
7832
  class: "space-y-4"
7804
7833
  };
7805
7834
  const _hoisted_19$3 = { class: "text-center" };
7806
- const _hoisted_20$2 = { class: "text-sm text-gray-600 mb-6" };
7835
+ const _hoisted_20$3 = { class: "text-sm text-gray-600 mb-6" };
7807
7836
  const _hoisted_21$2 = { class: "flex justify-end space-x-3 pt-4" };
7808
7837
  function _sfc_render$b(_ctx, _cache, $props, $setup, $data, $options) {
7809
7838
  return vue.openBlock(), vue.createBlock($setup["UiModal"], {
@@ -8256,7 +8285,7 @@ function _sfc_render$b(_ctx, _cache, $props, $setup, $data, $options) {
8256
8285
  )),
8257
8286
  vue.createElementVNode(
8258
8287
  "p",
8259
- _hoisted_20$2,
8288
+ _hoisted_20$3,
8260
8289
  vue.toDisplayString($setup.errorMessage),
8261
8290
  1
8262
8291
  /* TEXT */
@@ -8989,21 +9018,15 @@ const _hoisted_15$2 = { class: "flex flex-col space-y-2 ml-4" };
8989
9018
  const _hoisted_16$2 = { class: "flex items-center justify-between" };
8990
9019
  const _hoisted_17$2 = { class: "flex-shrink-0 w-14 h-14 bg-[var(--strands-primary)] bg-opacity-10 rounded-xl flex items-center justify-center group-hover:bg-[var(--strands-primary)] group-hover:bg-opacity-15 transition-colors" };
8991
9020
  const _hoisted_18$2 = { class: "flex flex-col space-y-2 ml-4" };
8992
- const _hoisted_19$2 = { class: "flex items-center justify-between" };
8993
- const _hoisted_20$1 = { class: "flex-shrink-0 w-14 h-14 bg-[var(--strands-primary)] bg-opacity-10 rounded-xl flex items-center justify-center group-hover:bg-[var(--strands-primary)] group-hover:bg-opacity-15 transition-colors" };
8994
- const _hoisted_21$1 = { class: "flex flex-col space-y-2 ml-4" };
8995
- const _hoisted_22$1 = { class: "flex items-center justify-between" };
8996
- const _hoisted_23$1 = { class: "flex-shrink-0 w-14 h-14 bg-[var(--strands-primary)] bg-opacity-10 rounded-xl flex items-center justify-center group-hover:bg-[var(--strands-primary)] group-hover:bg-opacity-15 transition-colors" };
8997
- const _hoisted_24$1 = { class: "flex flex-col space-y-2 ml-4" };
8998
- const _hoisted_25$1 = { class: "space-y-6" };
8999
- const _hoisted_26$1 = { class: "max-h-96 overflow-y-auto space-y-4 pr-2" };
9000
- const _hoisted_27$1 = { class: "flex items-start justify-between" };
9001
- const _hoisted_28$1 = { class: "flex items-start space-x-4" };
9002
- const _hoisted_29$1 = { class: "min-w-0 flex-1" };
9003
- const _hoisted_30$1 = { class: "font-semibold text-gray-900 text-lg" };
9004
- const _hoisted_31$1 = { class: "text-sm text-gray-600 mt-1" };
9005
- const _hoisted_32$1 = { class: "text-xs text-gray-500 mt-2" };
9006
- const _hoisted_33$1 = { class: "flex flex-col space-y-2 ml-4" };
9021
+ const _hoisted_19$2 = { class: "space-y-6" };
9022
+ const _hoisted_20$2 = { class: "max-h-96 overflow-y-auto space-y-4 pr-2" };
9023
+ const _hoisted_21$1 = { class: "flex items-start justify-between" };
9024
+ const _hoisted_22$1 = { class: "flex items-start space-x-4" };
9025
+ const _hoisted_23$1 = { class: "min-w-0 flex-1" };
9026
+ const _hoisted_24$1 = { class: "font-semibold text-gray-900 text-lg" };
9027
+ const _hoisted_25$1 = { class: "text-sm text-gray-600 mt-1" };
9028
+ const _hoisted_26$1 = { class: "text-xs text-gray-500 mt-2" };
9029
+ const _hoisted_27$1 = { class: "flex flex-col space-y-2 ml-4" };
9007
9030
  function _sfc_render$8(_ctx, _cache, $props, $setup, $data, $options) {
9008
9031
  return vue.openBlock(), vue.createElementBlock(
9009
9032
  vue.Fragment,
@@ -9134,7 +9157,7 @@ function _sfc_render$8(_ctx, _cache, $props, $setup, $data, $options) {
9134
9157
  vue.createCommentVNode(" Tab Content: Add New Device "),
9135
9158
  $setup.activeTab === "add" ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_11$2, [
9136
9159
  vue.createElementVNode("div", null, [
9137
- _cache[21] || (_cache[21] = vue.createElementVNode(
9160
+ _cache[17] || (_cache[17] = vue.createElementVNode(
9138
9161
  "h3",
9139
9162
  { class: "text-xl font-semibold text-gray-900 mb-6" },
9140
9163
  "Choose Your Authentication Method",
@@ -9230,94 +9253,35 @@ function _sfc_render$8(_ctx, _cache, $props, $setup, $data, $options) {
9230
9253
  ])
9231
9254
  ])
9232
9255
  ]),
9233
- vue.createCommentVNode(" Hardware Key Setup "),
9234
- vue.createElementVNode("div", {
9235
- class: "group p-6 bg-white border border-gray-200 rounded-xl hover:border-gray-300 hover:shadow-md transition-all duration-200 cursor-pointer",
9236
- onClick: $setup.startHardwareKeySetup
9237
- }, [
9238
- vue.createElementVNode("div", _hoisted_19$2, [
9239
- vue.createElementVNode("div", _hoisted_20$1, [
9240
- vue.createVNode($setup["KeyRound"], {
9241
- size: 24,
9242
- class: "text-[var(--strands-primary)]"
9243
- })
9244
- ]),
9245
- _cache[18] || (_cache[18] = vue.createElementVNode(
9246
- "div",
9247
- { class: "flex items-start space-x-4" },
9248
- [
9249
- vue.createElementVNode("div", { class: "min-w-0 flex-1" }, [
9250
- vue.createElementVNode("h4", { class: "font-semibold text-gray-900 text-lg" }, "Hardware Security Key"),
9251
- vue.createElementVNode("p", { class: "text-xs text-gray-500 mt-2" }, " Use YubiKey, FIDO2, or other physical security keys for ultimate protection ")
9252
- ])
9253
- ],
9254
- -1
9255
- /* CACHED */
9256
- )),
9257
- vue.createElementVNode("div", _hoisted_21$1, [
9258
- vue.createVNode($setup["StrandsUiButton"], {
9259
- variant: "primary",
9260
- size: "md",
9261
- onClick: vue.withModifiers($setup.startHardwareKeySetup, ["stop"]),
9262
- disabled: $setup.mfaLoading
9263
- }, {
9264
- default: vue.withCtx(() => _cache[17] || (_cache[17] = [
9265
- vue.createTextVNode(
9266
- " Setup ",
9267
- -1
9268
- /* CACHED */
9269
- )
9270
- ])),
9271
- _: 1,
9272
- __: [17]
9273
- }, 8, ["disabled"])
9274
- ])
9275
- ])
9276
- ]),
9277
- vue.createCommentVNode(" Passkey Setup "),
9278
- vue.createElementVNode("div", {
9279
- class: "group p-6 bg-white border border-gray-200 rounded-xl hover:border-gray-300 hover:shadow-md transition-all duration-200 cursor-pointer",
9280
- onClick: $setup.startPasskeySetup
9281
- }, [
9282
- vue.createElementVNode("div", _hoisted_22$1, [
9283
- vue.createElementVNode("div", _hoisted_23$1, [
9284
- vue.createVNode($setup["Shield"], {
9285
- size: 24,
9286
- class: "text-[var(--strands-primary)]"
9287
- })
9288
- ]),
9289
- _cache[20] || (_cache[20] = vue.createElementVNode(
9290
- "div",
9291
- { class: "flex items-start space-x-4" },
9292
- [
9293
- vue.createElementVNode("div", { class: "min-w-0 flex-1" }, [
9294
- vue.createElementVNode("h4", { class: "font-semibold text-gray-900 text-lg" }, "Passkey"),
9295
- vue.createElementVNode("p", { class: "text-xs text-gray-500 mt-2" }, " Use your device's built-in biometrics, PIN, or cross-device passkeys ")
9296
- ])
9297
- ],
9298
- -1
9299
- /* CACHED */
9300
- )),
9301
- vue.createElementVNode("div", _hoisted_24$1, [
9302
- vue.createVNode($setup["StrandsUiButton"], {
9303
- variant: "primary",
9304
- size: "md",
9305
- onClick: vue.withModifiers($setup.startPasskeySetup, ["stop"]),
9306
- disabled: $setup.mfaLoading
9307
- }, {
9308
- default: vue.withCtx(() => _cache[19] || (_cache[19] = [
9309
- vue.createTextVNode(
9310
- " Setup ",
9311
- -1
9312
- /* CACHED */
9313
- )
9314
- ])),
9315
- _: 1,
9316
- __: [19]
9317
- }, 8, ["disabled"])
9318
- ])
9319
- ])
9320
- ])
9256
+ vue.createCommentVNode(" Hardware Key Setup - Temporarily Disabled "),
9257
+ vue.createCommentVNode(' \n <div class="group p-6 bg-white border border-gray-200 rounded-xl hover:border-gray-300 hover:shadow-md transition-all duration-200 cursor-pointer" @click="startHardwareKeySetup">\n <div class="flex items-center justify-between">\n <div class="flex-shrink-0 w-14 h-14 bg-[var(--strands-primary)] bg-opacity-10 rounded-xl flex items-center justify-center group-hover:bg-[var(--strands-primary)] group-hover:bg-opacity-15 transition-colors">\n <KeyRound :size="24" class="text-[var(--strands-primary)]" />\n </div>\n <div class="flex items-start space-x-4">\n <div class="min-w-0 flex-1">\n <h4 class="font-semibold text-gray-900 text-lg">Hardware Security Key</h4>\n <p class="text-xs text-gray-500 mt-2">\n Use YubiKey, FIDO2, or other physical security keys for ultimate protection\n </p>\n </div>\n </div>\n <div class="flex flex-col space-y-2 ml-4">\n <StrandsUiButton\n variant="primary"\n size="md"\n @click.stop="startHardwareKeySetup"\n :disabled="mfaLoading"\n >\n Setup\n </StrandsUiButton>\n </div>\n </div>\n </div>\n\n <-- Passkey Setup - Temporarily Disabled '),
9258
+ vue.createCommentVNode(`
9259
+ <div class="group p-6 bg-white border border-gray-200 rounded-xl hover:border-gray-300 hover:shadow-md transition-all duration-200 cursor-pointer" @click="startPasskeySetup">
9260
+ <div class="flex items-center justify-between">
9261
+ <div class="flex-shrink-0 w-14 h-14 bg-[var(--strands-primary)] bg-opacity-10 rounded-xl flex items-center justify-center group-hover:bg-[var(--strands-primary)] group-hover:bg-opacity-15 transition-colors">
9262
+ <Shield :size="24" class="text-[var(--strands-primary)]" />
9263
+ </div>
9264
+ <div class="flex items-start space-x-4">
9265
+ <div class="min-w-0 flex-1">
9266
+ <h4 class="font-semibold text-gray-900 text-lg">Passkey</h4>
9267
+ <p class="text-xs text-gray-500 mt-2">
9268
+ Use your device's built-in biometrics, PIN, or cross-device passkeys
9269
+ </p>
9270
+ </div>
9271
+ </div>
9272
+ <div class="flex flex-col space-y-2 ml-4">
9273
+ <StrandsUiButton
9274
+ variant="primary"
9275
+ size="md"
9276
+ @click.stop="startPasskeySetup"
9277
+ :disabled="mfaLoading"
9278
+ >
9279
+ Setup
9280
+ </StrandsUiButton>
9281
+ </div>
9282
+ </div>
9283
+ </div>
9284
+ `)
9321
9285
  ])
9322
9286
  ])
9323
9287
  ])) : $setup.activeTab === "manage" && $setup.activeMfaDevices.length > 0 ? (vue.openBlock(), vue.createElementBlock(
@@ -9325,16 +9289,16 @@ function _sfc_render$8(_ctx, _cache, $props, $setup, $data, $options) {
9325
9289
  { key: 1 },
9326
9290
  [
9327
9291
  vue.createCommentVNode(" Tab Content: Active Devices "),
9328
- vue.createElementVNode("div", _hoisted_25$1, [
9292
+ vue.createElementVNode("div", _hoisted_19$2, [
9329
9293
  vue.createElementVNode("div", null, [
9330
- _cache[25] || (_cache[25] = vue.createElementVNode(
9294
+ _cache[21] || (_cache[21] = vue.createElementVNode(
9331
9295
  "h3",
9332
9296
  { class: "text-xl font-semibold text-gray-900 mb-6" },
9333
9297
  "Manage Your Active Devices",
9334
9298
  -1
9335
9299
  /* CACHED */
9336
9300
  )),
9337
- vue.createElementVNode("div", _hoisted_26$1, [
9301
+ vue.createElementVNode("div", _hoisted_20$2, [
9338
9302
  (vue.openBlock(true), vue.createElementBlock(
9339
9303
  vue.Fragment,
9340
9304
  null,
@@ -9343,8 +9307,8 @@ function _sfc_render$8(_ctx, _cache, $props, $setup, $data, $options) {
9343
9307
  key: device.id,
9344
9308
  class: "group p-6 bg-white border border-gray-200 rounded-xl hover:border-gray-300 hover:shadow-md transition-all duration-200"
9345
9309
  }, [
9346
- vue.createElementVNode("div", _hoisted_27$1, [
9347
- vue.createElementVNode("div", _hoisted_28$1, [
9310
+ vue.createElementVNode("div", _hoisted_21$1, [
9311
+ vue.createElementVNode("div", _hoisted_22$1, [
9348
9312
  vue.createElementVNode(
9349
9313
  "div",
9350
9314
  {
@@ -9359,31 +9323,31 @@ function _sfc_render$8(_ctx, _cache, $props, $setup, $data, $options) {
9359
9323
  2
9360
9324
  /* CLASS */
9361
9325
  ),
9362
- vue.createElementVNode("div", _hoisted_29$1, [
9326
+ vue.createElementVNode("div", _hoisted_23$1, [
9363
9327
  vue.createElementVNode(
9364
9328
  "h4",
9365
- _hoisted_30$1,
9329
+ _hoisted_24$1,
9366
9330
  vue.toDisplayString(device.device_name),
9367
9331
  1
9368
9332
  /* TEXT */
9369
9333
  ),
9370
9334
  vue.createElementVNode(
9371
9335
  "p",
9372
- _hoisted_31$1,
9336
+ _hoisted_25$1,
9373
9337
  vue.toDisplayString($setup.getDeviceTypeName(device.device_type)),
9374
9338
  1
9375
9339
  /* TEXT */
9376
9340
  ),
9377
9341
  vue.createElementVNode(
9378
9342
  "p",
9379
- _hoisted_32$1,
9343
+ _hoisted_26$1,
9380
9344
  " Last used " + vue.toDisplayString($setup.formatLastUsed(device.last_used_at)),
9381
9345
  1
9382
9346
  /* TEXT */
9383
9347
  )
9384
9348
  ])
9385
9349
  ]),
9386
- vue.createElementVNode("div", _hoisted_33$1, [
9350
+ vue.createElementVNode("div", _hoisted_27$1, [
9387
9351
  vue.createCommentVNode(" Backup codes button for TOTP, Hardware Key, and Passkey devices "),
9388
9352
  device.device_type === "totp" || device.device_type === "hardware" || device.device_type === "passkey" ? (vue.openBlock(), vue.createBlock($setup["StrandsUiButton"], {
9389
9353
  key: 0,
@@ -9397,14 +9361,14 @@ function _sfc_render$8(_ctx, _cache, $props, $setup, $data, $options) {
9397
9361
  size: 14,
9398
9362
  class: "mr-2"
9399
9363
  }),
9400
- _cache[22] || (_cache[22] = vue.createTextVNode(
9364
+ _cache[18] || (_cache[18] = vue.createTextVNode(
9401
9365
  " Backup Codes ",
9402
9366
  -1
9403
9367
  /* CACHED */
9404
9368
  ))
9405
9369
  ]),
9406
9370
  _: 2,
9407
- __: [22]
9371
+ __: [18]
9408
9372
  }, 1032, ["onClick", "disabled"])) : vue.createCommentVNode("v-if", true),
9409
9373
  vue.createCommentVNode(" Test email MFA "),
9410
9374
  device.device_type === "email" ? (vue.openBlock(), vue.createBlock($setup["StrandsUiButton"], {
@@ -9419,14 +9383,14 @@ function _sfc_render$8(_ctx, _cache, $props, $setup, $data, $options) {
9419
9383
  size: 14,
9420
9384
  class: "mr-2"
9421
9385
  }),
9422
- _cache[23] || (_cache[23] = vue.createTextVNode(
9386
+ _cache[19] || (_cache[19] = vue.createTextVNode(
9423
9387
  " Send Test Code ",
9424
9388
  -1
9425
9389
  /* CACHED */
9426
9390
  ))
9427
9391
  ]),
9428
9392
  _: 2,
9429
- __: [23]
9393
+ __: [19]
9430
9394
  }, 1032, ["onClick", "disabled"])) : vue.createCommentVNode("v-if", true),
9431
9395
  vue.createCommentVNode(" Remove device "),
9432
9396
  vue.createVNode($setup["StrandsUiButton"], {
@@ -9441,14 +9405,14 @@ function _sfc_render$8(_ctx, _cache, $props, $setup, $data, $options) {
9441
9405
  size: 14,
9442
9406
  class: "mr-2"
9443
9407
  }),
9444
- _cache[24] || (_cache[24] = vue.createTextVNode(
9408
+ _cache[20] || (_cache[20] = vue.createTextVNode(
9445
9409
  " Remove ",
9446
9410
  -1
9447
9411
  /* CACHED */
9448
9412
  ))
9449
9413
  ]),
9450
9414
  _: 2,
9451
- __: [24]
9415
+ __: [20]
9452
9416
  }, 1032, ["onClick", "disabled"])
9453
9417
  ])
9454
9418
  ])
@@ -9974,7 +9938,7 @@ const _hoisted_16$1 = {
9974
9938
  const _hoisted_17$1 = { class: "space-y-3 md:space-y-4" };
9975
9939
  const _hoisted_18$1 = { class: "space-y-4 p-4 bg-gray-50 rounded-xl" };
9976
9940
  const _hoisted_19$1 = { class: "flex items-center justify-between gap-2" };
9977
- const _hoisted_20 = { class: "text-sm text-gray-600" };
9941
+ const _hoisted_20$1 = { class: "text-sm text-gray-600" };
9978
9942
  const _hoisted_21 = {
9979
9943
  key: 0,
9980
9944
  class: "space-y-3 overflow-hidden"
@@ -10236,7 +10200,7 @@ function _sfc_render$7(_ctx, _cache, $props, $setup, $data, $options) {
10236
10200
  )),
10237
10201
  vue.createElementVNode(
10238
10202
  "p",
10239
- _hoisted_20,
10203
+ _hoisted_20$1,
10240
10204
  "Last updated " + vue.toDisplayString($setup.passwordLastUpdated),
10241
10205
  1
10242
10206
  /* TEXT */
@@ -10879,15 +10843,16 @@ const _hoisted_10 = { class: "flex flex-col items-center text-center space-y-4"
10879
10843
  const _hoisted_11 = { class: "w-16 h-16 bg-[var(--strands-primary)] bg-opacity-10 rounded-xl flex items-center justify-center group-hover:bg-[var(--strands-primary)] group-hover:bg-opacity-15 transition-colors" };
10880
10844
  const _hoisted_12 = { class: "flex flex-col items-center text-center space-y-4" };
10881
10845
  const _hoisted_13 = { class: "w-16 h-16 bg-[var(--strands-primary)] bg-opacity-10 rounded-xl flex items-center justify-center group-hover:bg-[var(--strands-primary)] group-hover:bg-opacity-15 transition-colors" };
10882
- const _hoisted_14 = { class: "flex flex-col items-center text-center space-y-4" };
10883
- const _hoisted_15 = { class: "w-16 h-16 bg-[var(--strands-primary)] bg-opacity-10 rounded-xl flex items-center justify-center group-hover:bg-[var(--strands-primary)] group-hover:bg-opacity-15 transition-colors" };
10884
- const _hoisted_16 = {
10846
+ const _hoisted_14 = { class: "group p-8 border border-gray-300 rounded-xl bg-gray-50 opacity-60 cursor-not-allowed" };
10847
+ const _hoisted_15 = { class: "flex flex-col items-center text-center space-y-4" };
10848
+ const _hoisted_16 = { class: "w-16 h-16 bg-gray-200 rounded-xl flex items-center justify-center" };
10849
+ const _hoisted_17 = {
10885
10850
  key: 1,
10886
10851
  class: "border-t border-gray-200 pt-8"
10887
10852
  };
10888
- const _hoisted_17 = { class: "flex items-center justify-between" };
10889
- const _hoisted_18 = { class: "text-gray-600 text-sm mt-1" };
10890
- const _hoisted_19 = { class: "flex justify-end space-x-3" };
10853
+ const _hoisted_18 = { class: "flex items-center justify-between" };
10854
+ const _hoisted_19 = { class: "text-gray-600 text-sm mt-1" };
10855
+ const _hoisted_20 = { class: "flex justify-end space-x-3" };
10891
10856
  function _sfc_render$5(_ctx, _cache, $props, $setup, $data, $options) {
10892
10857
  return vue.openBlock(), vue.createElementBlock(
10893
10858
  vue.Fragment,
@@ -10924,7 +10889,7 @@ function _sfc_render$5(_ctx, _cache, $props, $setup, $data, $options) {
10924
10889
  ])
10925
10890
  ]),
10926
10891
  footer: vue.withCtx(() => [
10927
- vue.createElementVNode("div", _hoisted_19, [
10892
+ vue.createElementVNode("div", _hoisted_20, [
10928
10893
  vue.createVNode($setup["StrandsUiButton"], {
10929
10894
  variant: "secondary",
10930
10895
  onClick: $setup.closeModal,
@@ -11073,52 +11038,48 @@ function _sfc_render$5(_ctx, _cache, $props, $setup, $data, $options) {
11073
11038
  }, 8, ["disabled"])
11074
11039
  ])
11075
11040
  ]),
11076
- vue.createCommentVNode(" Hardware Key Setup "),
11077
- vue.createElementVNode("div", {
11078
- class: "group p-8 border border-gray-200 rounded-xl hover:shadow-lg hover:border-[var(--strands-primary)] transition-all duration-200 cursor-pointer",
11079
- onClick: $setup.startHardwareKeySetup
11080
- }, [
11081
- vue.createElementVNode("div", _hoisted_14, [
11082
- vue.createElementVNode("div", _hoisted_15, [
11041
+ vue.createCommentVNode(" Hardware Key Setup - Temporarily Disabled "),
11042
+ vue.createElementVNode("div", _hoisted_14, [
11043
+ vue.createElementVNode("div", _hoisted_15, [
11044
+ vue.createElementVNode("div", _hoisted_16, [
11083
11045
  vue.createVNode($setup["KeyRound"], {
11084
11046
  size: 28,
11085
- class: "text-[var(--strands-primary)]"
11047
+ class: "text-gray-400"
11086
11048
  })
11087
11049
  ]),
11088
11050
  _cache[12] || (_cache[12] = vue.createElementVNode(
11089
11051
  "div",
11090
11052
  { class: "space-y-2" },
11091
11053
  [
11092
- vue.createElementVNode("h4", { class: "font-semibold text-gray-900 text-lg" }, "Hardware Key & Passkeys"),
11093
- vue.createElementVNode("p", { class: "text-sm text-gray-500 leading-relaxed" }, "Use YubiKey, FIDO2 devices, WebAuthn, or built-in passkeys for maximum security")
11054
+ vue.createElementVNode("h4", { class: "font-semibold text-gray-500 text-lg" }, "Hardware Key & Passkeys"),
11055
+ vue.createElementVNode("p", { class: "text-sm text-gray-400 leading-relaxed" }, "Temporarily unavailable - cross-domain support coming soon")
11094
11056
  ],
11095
11057
  -1
11096
11058
  /* CACHED */
11097
11059
  )),
11098
11060
  vue.createVNode($setup["StrandsUiButton"], {
11099
- variant: "primary",
11061
+ variant: "secondary",
11100
11062
  size: "md",
11101
- onClick: vue.withModifiers($setup.startHardwareKeySetup, ["stop"]),
11102
- disabled: $setup.loading,
11063
+ disabled: "",
11103
11064
  class: "w-full mt-4"
11104
11065
  }, {
11105
11066
  default: vue.withCtx(() => _cache[11] || (_cache[11] = [
11106
11067
  vue.createTextVNode(
11107
- " Setup Hardware Key ",
11068
+ " Coming Soon ",
11108
11069
  -1
11109
11070
  /* CACHED */
11110
11071
  )
11111
11072
  ])),
11112
11073
  _: 1,
11113
11074
  __: [11]
11114
- }, 8, ["disabled"])
11075
+ })
11115
11076
  ])
11116
11077
  ])
11117
11078
  ])
11118
11079
  ]),
11119
11080
  vue.createCommentVNode(" Manage Existing "),
11120
- $setup.activeMfaDevices.length > 0 ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_16, [
11121
- vue.createElementVNode("div", _hoisted_17, [
11081
+ $setup.activeMfaDevices.length > 0 ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_17, [
11082
+ vue.createElementVNode("div", _hoisted_18, [
11122
11083
  vue.createElementVNode("div", null, [
11123
11084
  _cache[14] || (_cache[14] = vue.createElementVNode(
11124
11085
  "h3",
@@ -11129,7 +11090,7 @@ function _sfc_render$5(_ctx, _cache, $props, $setup, $data, $options) {
11129
11090
  )),
11130
11091
  vue.createElementVNode(
11131
11092
  "p",
11132
- _hoisted_18,
11093
+ _hoisted_19,
11133
11094
  vue.toDisplayString($setup.activeMfaDevices.length) + " device(s) currently active",
11134
11095
  1
11135
11096
  /* TEXT */