@seams/wallet-server 0.22.0 → 0.24.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 (84) hide show
  1. package/THIRD_PARTY_NOTICES.md +186 -6
  2. package/artifact-manifest.json +23 -13
  3. package/cloudflare-router-ab/build/deriver-a/build-stamp.json +1 -1
  4. package/cloudflare-router-ab/build/deriver-b/build-stamp.json +1 -1
  5. package/cloudflare-router-ab/build/router/build-stamp.json +1 -1
  6. package/cloudflare-router-ab/build/signing-worker/build-stamp.json +1 -1
  7. package/cloudflare-router-ab/build/tenant-root-control-plane/build-stamp.json +1 -1
  8. package/dist/esm/authorization/voiceIdDeviceProof.js +32 -0
  9. package/dist/esm/core/ThresholdService/validation.js +17 -1
  10. package/dist/esm/core/d1WalletAuthMethodStore.js +42 -1
  11. package/dist/esm/core/deviceLinking/linkedDeviceManagement.js +5 -0
  12. package/dist/esm/core/signingLanes/WalletExecutionLaneProjection.js +11 -2
  13. package/dist/esm/router/cloudflare/d1/auth/d1RouterApiAuthService.js +23 -6
  14. package/dist/esm/router/cloudflare/d1/deviceLinking/d1LinkedDeviceAuthorityInstallService.js +5 -5
  15. package/dist/esm/router/cloudflare/d1/emailOtp/d1GoogleEmailOtpRegistrationAttemptStore.js +1 -1
  16. package/dist/esm/router/cloudflare/d1/passkeyCustody/d1PasskeyCustodyEnvelopeStore.js +6 -0
  17. package/dist/esm/router/cloudflare/d1/wallet/d1VoiceIdActivation.js +139 -0
  18. package/dist/esm/router/cloudflare/d1/wallet/d1VoiceIdSessions.js +138 -0
  19. package/dist/esm/router/cloudflare/d1/wallet/d1VoiceIdWalletSessionChallenges.js +88 -0
  20. package/dist/esm/router/cloudflare/d1/wallet/d1WalletAuthMethodService.js +12 -4
  21. package/dist/esm/router/cloudflare/d1/wallet/d1WalletAuthorityStore.js +14 -0
  22. package/dist/esm/router/cloudflare/d1/webauthn/d1WebAuthnRecords.js +1 -0
  23. package/dist/esm/router/cloudflare.js +576 -10
  24. package/dist/esm/router/domains/ed25519Yao/export/routerAbEd25519YaoExport.js +5 -0
  25. package/dist/esm/router/domains/passkeyCustody/walletRecoveryFinalization.js +1 -0
  26. package/dist/esm/router/domains/signingOperations/routerAbNormalSigningAdmissionCore.js +1 -0
  27. package/dist/esm/router/domains/signingOperations/walletExecutionAdmission.js +1 -0
  28. package/dist/esm/router/domains/voiceId/voiceIdActivation.js +12 -0
  29. package/dist/esm/router/domains/voiceId/voiceIdSessions.js +0 -0
  30. package/dist/esm/router/framework/routeDefinitions.js +13 -0
  31. package/dist/esm/router/transport/fetch/createFetchRouter.js +5 -0
  32. package/dist/esm/router/transport/fetch/routes/deviceLinkingOwnerAuthorization.js +12 -1
  33. package/dist/esm/router/transport/fetch/routes/walletExecutionLanePreflight.js +1 -0
  34. package/dist/esm/router/transport/fetch/routes/walletVoiceIdActivation.js +139 -0
  35. package/dist/esm/router/transport/fetch/routes/walletVoiceIdNear.js +36 -0
  36. package/dist/esm/router/transport/fetch/routes/walletVoiceIdSessions.js +127 -0
  37. package/dist/esm/shared-ts/src/authorization/voiceIdDeviceAssertion.js +110 -0
  38. package/dist/esm/shared-ts/src/device-linking/authorityActivation.js +1 -0
  39. package/dist/esm/shared-ts/src/device-linking/committedSignerPackages.js +1 -1
  40. package/dist/esm/shared-ts/src/passkey-custody/custodyEnvelope.js +30 -3
  41. package/dist/esm/shared-ts/src/passkey-custody/custodySecretBinding.js +1 -1
  42. package/dist/esm/shared-ts/src/signing-lanes/recordParsers.js +15 -1
  43. package/dist/esm/shared-ts/src/threshold/sessionPolicy.js +2 -1
  44. package/dist/esm/shared-ts/src/utils/registrationIntent.js +1 -3
  45. package/dist/esm/shared-ts/src/utils/walletAuthAuthority.js +46 -2
  46. package/dist/esm/shared-ts/src/utils/walletAuthMethodRecord.js +116 -7
  47. package/dist/esm/shared-ts/src/wallet-recovery/walletRecoveryCommittedProjection.js +1 -0
  48. package/dist/esm/wasm/near_signer/pkg/wasm_signer_worker_bg.wasm +0 -0
  49. package/dist/types/shared-ts/src/authorization/voiceIdDeviceAssertion.d.ts +32 -0
  50. package/dist/types/shared-ts/src/device-linking/committedSignerPackages.d.ts +3 -1
  51. package/dist/types/shared-ts/src/device-linking/contracts.d.ts +8 -0
  52. package/dist/types/shared-ts/src/passkey-custody/custodyEnvelope.d.ts +29 -0
  53. package/dist/types/shared-ts/src/passkey-custody/custodySecretBinding.d.ts +1 -0
  54. package/dist/types/shared-ts/src/signing-lanes/execution.d.ts +2 -2
  55. package/dist/types/shared-ts/src/signing-lanes/recordParsers.d.ts +2 -1
  56. package/dist/types/shared-ts/src/signing-lanes/records.d.ts +10 -2
  57. package/dist/types/shared-ts/src/signing-lanes/rotation.d.ts +2 -2
  58. package/dist/types/shared-ts/src/threshold/sessionPolicy.d.ts +9 -0
  59. package/dist/types/shared-ts/src/utils/addWalletAuthMethod.d.ts +3 -3
  60. package/dist/types/shared-ts/src/utils/domainIds.d.ts +4 -5
  61. package/dist/types/shared-ts/src/utils/registrationIntent.d.ts +0 -1
  62. package/dist/types/shared-ts/src/utils/signerDomain.d.ts +3 -0
  63. package/dist/types/shared-ts/src/utils/walletAuthAuthority.d.ts +32 -2
  64. package/dist/types/shared-ts/src/utils/walletAuthMethodRecord.d.ts +15 -1
  65. package/dist/types/shared-ts/src/utils/walletCapabilityBindings.d.ts +20 -0
  66. package/dist/types/wallet-server/src/authorization/voiceIdDeviceProof.d.ts +29 -0
  67. package/dist/types/wallet-server/src/core/d1WalletAuthMethodStore.d.ts +13 -2
  68. package/dist/types/wallet-server/src/core/registrationContracts.d.ts +8 -0
  69. package/dist/types/wallet-server/src/core/signingLanes/WalletExecutionLaneProjection.d.ts +4 -0
  70. package/dist/types/wallet-server/src/router/cloudflare/d1/passkeyCustody/d1PasskeyCustodyEnvelopeStore.d.ts +4 -0
  71. package/dist/types/wallet-server/src/router/cloudflare/d1/wallet/d1VoiceIdActivation.d.ts +14 -0
  72. package/dist/types/wallet-server/src/router/cloudflare/d1/wallet/d1VoiceIdSessions.d.ts +19 -0
  73. package/dist/types/wallet-server/src/router/cloudflare/d1/wallet/d1VoiceIdWalletSessionChallenges.d.ts +26 -0
  74. package/dist/types/wallet-server/src/router/cloudflare/d1/wallet/d1WalletAuthMethodService.d.ts +1 -0
  75. package/dist/types/wallet-server/src/router/cloudflare/d1/wallet/d1WalletRelocationOwner.d.ts +25 -0
  76. package/dist/types/wallet-server/src/router/domains/voiceId/voiceIdActivation.d.ts +29 -0
  77. package/dist/types/wallet-server/src/router/domains/voiceId/voiceIdSessions.d.ts +35 -0
  78. package/dist/types/wallet-server/src/router/framework/authServicePort.d.ts +3 -0
  79. package/dist/types/wallet-server/src/router/transport/fetch/routes/walletVoiceIdActivation.d.ts +12 -0
  80. package/dist/types/wallet-server/src/router/transport/fetch/routes/walletVoiceIdNear.d.ts +14 -0
  81. package/dist/types/wallet-server/src/router/transport/fetch/routes/walletVoiceIdSessions.d.ts +19 -0
  82. package/migrations/d1-signer/0083_voiceid_wallet_session_challenges.sql +29 -0
  83. package/migrations/d1-signer/0084_voiceid_wallet_auth_methods.sql +373 -0
  84. package/package.json +1 -1
@@ -257,6 +257,7 @@ function runtimePolicyScopeKey(scope) {
257
257
  }
258
258
  function ed25519AdmissionAuthorityScopeKey(scope) {
259
259
  switch (scope.kind) {
260
+ case "voiceid": return `voiceid:${scope.deviceKeyId}`;
260
261
  case "passkey_rp": return `passkey_rp:${scope.rpId}`;
261
262
  case "email_otp": return `email_otp:${scope.provider}:${scope.providerUserId}`;
262
263
  }
@@ -1536,6 +1537,20 @@ function parseWalletCapabilitySubjectV1(raw, label) {
1536
1537
  //#endregion
1537
1538
  //#region ../shared-ts/src/utils/walletAuthAuthority.ts
1538
1539
  const WALLET_AUTHORITY_BINDING_DIGEST_DOMAIN = "seams:wallet-authority-binding:v1|";
1540
+ function buildVoiceIdWalletAuthAuthority(input) {
1541
+ return {
1542
+ walletId: input.walletId,
1543
+ bindingId: input.walletAuthMethodId,
1544
+ factor: {
1545
+ kind: "voiceid",
1546
+ deviceKeyId: input.deviceKeyId
1547
+ },
1548
+ verifier: { kind: "device_signature" }
1549
+ };
1550
+ }
1551
+ function isVoiceIdWalletAuthAuthority(authority) {
1552
+ return authority.factor.kind === "voiceid";
1553
+ }
1539
1554
  function isPasskeyWalletAuthAuthority(authority) {
1540
1555
  return authority.factor.kind === "passkey";
1541
1556
  }
@@ -1705,7 +1720,33 @@ function parseEmailOtpWalletAuthAuthorityObject(obj) {
1705
1720
  function parseWalletAuthAuthority(raw) {
1706
1721
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
1707
1722
  const obj = raw;
1708
- return parsePasskeyWalletAuthAuthorityObject(obj) || parseEmailOtpWalletAuthAuthorityObject(obj);
1723
+ return parsePasskeyWalletAuthAuthorityObject(obj) || parseEmailOtpWalletAuthAuthorityObject(obj) || parseVoiceIdWalletAuthAuthority(obj);
1724
+ }
1725
+ function parseVoiceIdWalletAuthAuthority(raw) {
1726
+ try {
1727
+ const record = requireRecord$1(raw, "device authority");
1728
+ rejectUnknownFields(record, [
1729
+ "walletId",
1730
+ "bindingId",
1731
+ "factor",
1732
+ "verifier"
1733
+ ], "device authority");
1734
+ const factor = requireRecord$1(record.factor, "device factor");
1735
+ const verifier = requireRecord$1(record.verifier, "device verifier");
1736
+ rejectUnknownFields(factor, ["kind", "deviceKeyId"], "device factor");
1737
+ rejectUnknownFields(verifier, ["kind"], "device verifier");
1738
+ if (factor.kind !== "voiceid" || verifier.kind !== "device_signature") return null;
1739
+ const walletId = parseWalletId(record.walletId);
1740
+ const method = parseWalletAuthMethodId(record.bindingId);
1741
+ if (!walletId.ok || !method.ok) return null;
1742
+ return buildVoiceIdWalletAuthAuthority({
1743
+ walletId: walletId.value,
1744
+ walletAuthMethodId: method.value,
1745
+ deviceKeyId: parseDigestB64u(factor.deviceKeyId)
1746
+ });
1747
+ } catch {
1748
+ return null;
1749
+ }
1709
1750
  }
1710
1751
  function canonicalWalletAuthorityBindingDigestInput(args) {
1711
1752
  return `${WALLET_AUTHORITY_BINDING_DIGEST_DOMAIN}${alphabetizeStringify(args.authority)}`;
@@ -5433,6 +5474,7 @@ const ROUTER_API_PASSKEY_CUSTODY_SERVICES = [
5433
5474
  ];
5434
5475
  const ROUTER_API_LINKED_DEVICE_PASSKEY_SERVICES = ["publishableKeyAuth"];
5435
5476
  const ROUTER_API_WALLET_UNLOCK_SERVICES = ["walletUnlock"];
5477
+ const ROUTER_API_VOICEID_ACTIVATION_SERVICES = ["session", "walletAuthMethods"];
5436
5478
  const ROUTER_API_HOSTED_SESSION_EXCHANGE_SERVICES = ["session"];
5437
5479
  const ROUTER_API_EMAIL_OTP_PUBLIC_SERVICES = ["emailOtp"];
5438
5480
  const ROUTER_API_EMAIL_OTP_CHALLENGE_SERVICES = ["emailOtp", "walletAuthMethods"];
@@ -5808,6 +5850,18 @@ function createRouterApiRouteDefinitions(options = {}) {
5808
5850
  plane: "public",
5809
5851
  proof: "challenge_exchange",
5810
5852
  rationale: "Wallet unlock verification is intentionally public because the challenge proof is the gate."
5853
+ }, ROUTER_API_WALLET_UNLOCK_SERVICES), publicRoute("wallet_voiceid_activate", "POST", "/wallet/voiceid/activate", "Activate a device-bound VoiceID method under an owner Wallet Session", {
5854
+ plane: "public",
5855
+ proof: "challenge_exchange",
5856
+ rationale: "An active full-owner Wallet Session and the hosted wallet origin authorize activation."
5857
+ }, ROUTER_API_VOICEID_ACTIVATION_SERVICES), publicRoute("wallet_voiceid_challenge", "POST", "/wallet/voiceid/challenge", "Issue a device-bound VoiceID Wallet Session challenge", {
5858
+ plane: "public",
5859
+ proof: "challenge_exchange",
5860
+ rationale: "An active method receives a short-lived challenge bound to the hosted wallet origin."
5861
+ }, ROUTER_API_WALLET_UNLOCK_SERVICES), publicRoute("wallet_voiceid_verify", "POST", "/wallet/voiceid/verify", "Verify a VoiceID device assertion and issue a Wallet Session", {
5862
+ plane: "public",
5863
+ proof: "signed_payload",
5864
+ rationale: "The registered device signature and single-use server challenge authorize issuance."
5811
5865
  }, ROUTER_API_WALLET_UNLOCK_SERVICES), publicRoute("wallet_email_otp_challenge", "POST", "/wallet/email-otp/challenge", "Create an operation-bound Email OTP challenge", {
5812
5866
  plane: "public",
5813
5867
  proof: "challenge_exchange",
@@ -7530,6 +7584,102 @@ function exactRecord$2(raw, keys, label) {
7530
7584
  return record;
7531
7585
  }
7532
7586
  //#endregion
7587
+ //#region ../shared-ts/src/authorization/voiceIdDeviceAssertion.ts
7588
+ const MAXIMUM_CHALLENGE_MS = 6e4;
7589
+ function parseVoiceIdDevicePublicKeyB64u(value) {
7590
+ if (typeof value !== "string") throw new Error("VoiceID public key is invalid");
7591
+ const bytes = base64UrlDecode(value);
7592
+ if (bytes.length !== 32 || base64UrlEncode(bytes) !== value) throw new Error("VoiceID public key must be canonical Ed25519 bytes");
7593
+ return value;
7594
+ }
7595
+ function exactKeys(value, keys) {
7596
+ if (Object.keys(value).length !== keys.length) return false;
7597
+ for (const key of keys) if (!Object.hasOwn(value, key)) return false;
7598
+ return true;
7599
+ }
7600
+ function requireOrigin(value, label) {
7601
+ if (typeof value !== "string") throw new Error(`${label} must be an origin`);
7602
+ let origin;
7603
+ let protocol;
7604
+ try {
7605
+ const parsed = new URL(value);
7606
+ origin = parsed.origin;
7607
+ protocol = parsed.protocol;
7608
+ } catch {
7609
+ throw new Error(`${label} must be an origin`);
7610
+ }
7611
+ if (origin !== value || protocol !== "http:" && protocol !== "https:") throw new Error(`${label} must be a canonical HTTP origin`);
7612
+ return origin;
7613
+ }
7614
+ function requireWindow(issuedAtMs, expiresAtMs) {
7615
+ if (!Number.isSafeInteger(issuedAtMs) || !Number.isSafeInteger(expiresAtMs) || Number(issuedAtMs) <= 0 || Number(expiresAtMs) <= Number(issuedAtMs) || Number(expiresAtMs) - Number(issuedAtMs) > MAXIMUM_CHALLENGE_MS) throw new Error("VoiceID challenge has an invalid validity window");
7616
+ }
7617
+ function buildVoiceIdWalletSessionChallenge(fields) {
7618
+ requireWindow(fields.issuedAtMs, fields.expiresAtMs);
7619
+ requireOrigin(fields.requestOrigin, "VoiceID requestOrigin");
7620
+ requireOrigin(fields.audience, "VoiceID audience");
7621
+ return {
7622
+ kind: "voiceid_wallet_session_challenge_v1",
7623
+ walletId: fields.walletId,
7624
+ walletAuthMethodId: fields.walletAuthMethodId,
7625
+ walletAuthorityId: fields.walletAuthorityId,
7626
+ deviceKeyId: fields.deviceKeyId,
7627
+ challengeId: fields.challengeId,
7628
+ nonce: fields.nonce,
7629
+ requestOrigin: fields.requestOrigin,
7630
+ audience: fields.audience,
7631
+ issuedAtMs: fields.issuedAtMs,
7632
+ expiresAtMs: fields.expiresAtMs
7633
+ };
7634
+ }
7635
+ function parseVoiceIdWalletSessionChallenge(raw) {
7636
+ if (!isPlainObject(raw) || !exactKeys(raw, [
7637
+ "kind",
7638
+ "walletId",
7639
+ "walletAuthMethodId",
7640
+ "walletAuthorityId",
7641
+ "deviceKeyId",
7642
+ "challengeId",
7643
+ "nonce",
7644
+ "requestOrigin",
7645
+ "audience",
7646
+ "issuedAtMs",
7647
+ "expiresAtMs"
7648
+ ]) || raw.kind !== "voiceid_wallet_session_challenge_v1") throw new Error("Invalid VoiceID Wallet Session challenge");
7649
+ const walletId = parseWalletId(raw.walletId);
7650
+ const walletAuthMethodId = parseWalletAuthMethodId(raw.walletAuthMethodId);
7651
+ const walletAuthorityId = parseWalletAuthorityId(raw.walletAuthorityId);
7652
+ const challengeId = parseWalletSessionMintId(raw.challengeId);
7653
+ if (!walletId.ok || !walletAuthMethodId.ok || !walletAuthorityId.ok || !challengeId.ok) throw new Error("VoiceID challenge identity is invalid");
7654
+ if (typeof raw.issuedAtMs !== "number" || typeof raw.expiresAtMs !== "number") throw new Error("VoiceID challenge times must be numbers");
7655
+ return buildVoiceIdWalletSessionChallenge({
7656
+ walletId: walletId.value,
7657
+ walletAuthMethodId: walletAuthMethodId.value,
7658
+ walletAuthorityId: walletAuthorityId.value,
7659
+ deviceKeyId: parseDigestB64u(raw.deviceKeyId),
7660
+ challengeId: challengeId.value,
7661
+ nonce: parseDigestB64u(raw.nonce),
7662
+ requestOrigin: requireOrigin(raw.requestOrigin, "VoiceID requestOrigin"),
7663
+ audience: requireOrigin(raw.audience, "VoiceID audience"),
7664
+ issuedAtMs: raw.issuedAtMs,
7665
+ expiresAtMs: raw.expiresAtMs
7666
+ });
7667
+ }
7668
+ function parseVoiceIdDeviceAssertion(raw) {
7669
+ if (!isPlainObject(raw) || !exactKeys(raw, [
7670
+ "kind",
7671
+ "challenge",
7672
+ "signatureB64u"
7673
+ ]) || raw.kind !== "voiceid_device_assertion_v1" || typeof raw.signatureB64u !== "string") throw new Error("Invalid VoiceID device assertion");
7674
+ const signature = base64UrlDecode(raw.signatureB64u);
7675
+ if (signature.length !== 64 || base64UrlEncode(signature) !== raw.signatureB64u) throw new Error("VoiceID signature must be canonical Ed25519 bytes");
7676
+ return {
7677
+ kind: "voiceid_device_assertion_v1",
7678
+ challenge: parseVoiceIdWalletSessionChallenge(raw.challenge),
7679
+ signatureB64u: raw.signatureB64u
7680
+ };
7681
+ }
7682
+ //#endregion
7533
7683
  //#region ../shared-ts/src/utils/registrationAuthMethodInput.ts
7534
7684
  function inspectRawObject$2(value) {
7535
7685
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
@@ -7660,7 +7810,7 @@ function parseWalletAuthMethodRecordV2(raw) {
7660
7810
  const version = trimString(record.version);
7661
7811
  const kind = trimString(record.kind);
7662
7812
  const status = trimString(record.status);
7663
- if (version !== "wallet_auth_method_v2" || kind !== "passkey" && kind !== "email_otp") return null;
7813
+ if (version !== "wallet_auth_method_v2" || kind !== "passkey" && kind !== "email_otp" && kind !== "voiceid") return null;
7664
7814
  try {
7665
7815
  const common = parseWalletAuthMethodRecordV2Common(record);
7666
7816
  if (kind === "passkey") {
@@ -7678,6 +7828,16 @@ function parseWalletAuthMethodRecordV2(raw) {
7678
7828
  counter: parseNonNegativeInteger(record.counter)
7679
7829
  });
7680
7830
  }
7831
+ if (kind === "voiceid") {
7832
+ if (!("deviceKeyId" in record) || !("devicePublicKeyB64u" in record)) return null;
7833
+ exactWalletAuthMethodV2Fields(record, "voiceid", status);
7834
+ return buildParsedVoiceIdWalletAuthMethodRecordV2({
7835
+ common,
7836
+ lifecycle: parseWalletAuthMethodLifecycle(record),
7837
+ deviceKeyId: parseDigestB64u(record.deviceKeyId),
7838
+ devicePublicKeyB64u: parseVoiceIdDevicePublicKeyB64u(record.devicePublicKeyB64u)
7839
+ });
7840
+ }
7681
7841
  if (!("emailHashHex" in record) || !("registrationAuthorityId" in record)) return null;
7682
7842
  exactWalletAuthMethodV2Fields(record, "email_otp", status);
7683
7843
  const emailHashHex = trimString(record.emailHashHex);
@@ -7736,7 +7896,8 @@ function exactWalletAuthMethodV2Fields(raw, kind, status) {
7736
7896
  "updatedAtMs"
7737
7897
  ];
7738
7898
  if (kind === "passkey") fields.push("rpId", "credentialIdB64u", "credentialPublicKeyB64u", "counter");
7739
- else fields.push("emailHashHex", "registrationAuthorityId");
7899
+ else if (kind === "email_otp") fields.push("emailHashHex", "registrationAuthorityId");
7900
+ else fields.push("deviceKeyId", "devicePublicKeyB64u");
7740
7901
  if (status === "active") fields.push("activatedAtMs");
7741
7902
  if (status === "revoked") fields.push("activatedAtMs", "revokedAtMs");
7742
7903
  if (status !== "pending_local_install" && status !== "active" && status !== "revoked") throw new Error("wallet auth method status is unsupported");
@@ -7837,6 +7998,49 @@ function buildParsedEmailOtpWalletAuthMethodRecordV2(input) {
7837
7998
  };
7838
7999
  }
7839
8000
  }
8001
+ function buildParsedVoiceIdWalletAuthMethodRecordV2(input) {
8002
+ switch (input.lifecycle.status) {
8003
+ case "pending_local_install": return {
8004
+ version: "wallet_auth_method_v2",
8005
+ walletAuthMethodId: input.common.walletAuthMethodId,
8006
+ walletId: input.common.walletId,
8007
+ walletAuthorityId: input.common.walletAuthorityId,
8008
+ kind: "voiceid",
8009
+ status: "pending_local_install",
8010
+ createdAtMs: input.common.createdAtMs,
8011
+ updatedAtMs: input.common.updatedAtMs,
8012
+ deviceKeyId: input.deviceKeyId,
8013
+ devicePublicKeyB64u: input.devicePublicKeyB64u
8014
+ };
8015
+ case "active": return {
8016
+ version: "wallet_auth_method_v2",
8017
+ walletAuthMethodId: input.common.walletAuthMethodId,
8018
+ walletId: input.common.walletId,
8019
+ walletAuthorityId: input.common.walletAuthorityId,
8020
+ kind: "voiceid",
8021
+ status: "active",
8022
+ createdAtMs: input.common.createdAtMs,
8023
+ updatedAtMs: input.common.updatedAtMs,
8024
+ deviceKeyId: input.deviceKeyId,
8025
+ devicePublicKeyB64u: input.devicePublicKeyB64u,
8026
+ activatedAtMs: input.lifecycle.activatedAtMs
8027
+ };
8028
+ case "revoked": return {
8029
+ version: "wallet_auth_method_v2",
8030
+ walletAuthMethodId: input.common.walletAuthMethodId,
8031
+ walletId: input.common.walletId,
8032
+ walletAuthorityId: input.common.walletAuthorityId,
8033
+ kind: "voiceid",
8034
+ status: "revoked",
8035
+ createdAtMs: input.common.createdAtMs,
8036
+ updatedAtMs: input.common.updatedAtMs,
8037
+ deviceKeyId: input.deviceKeyId,
8038
+ devicePublicKeyB64u: input.devicePublicKeyB64u,
8039
+ activatedAtMs: input.lifecycle.activatedAtMs,
8040
+ revokedAtMs: input.lifecycle.revokedAtMs
8041
+ };
8042
+ }
8043
+ }
7840
8044
  function parseWalletAuthMethodIdRequired$1(raw) {
7841
8045
  const parsed = parseWalletAuthMethodId(raw);
7842
8046
  if (!parsed.ok) throw new Error(parsed.error.message);
@@ -8433,7 +8637,7 @@ function parseCommittedAuthorityPackagesV1(raw) {
8433
8637
  const authority = parseWalletAuthorityV1(record.authority);
8434
8638
  if (!authority.ok || authority.value.state !== "pending_local_install") throw new Error("CommittedAuthorityPackagesV1.authority is not pending");
8435
8639
  const authMethod = parseWalletAuthMethodRecordV2(record.authMethod);
8436
- if (!authMethod || authMethod.status !== "pending_local_install") throw new Error("CommittedAuthorityPackagesV1.authMethod is not pending");
8640
+ if (!authMethod || authMethod.status !== "pending_local_install" || authMethod.kind === "voiceid") throw new Error("CommittedAuthorityPackagesV1.authMethod is not pending");
8437
8641
  if (authMethod.walletId !== authority.value.walletId || authMethod.walletAuthorityId !== authority.value.authorityId) throw new Error("CommittedAuthorityPackagesV1 authority and auth method identities differ");
8438
8642
  const signerPackages = parseCommittedSignerPackageSetV1(record.signerPackages);
8439
8643
  assertSignerPackagesMatchAuthority(signerPackages, authority.value.signerActivations);
@@ -10717,6 +10921,7 @@ function isActiveOwnerLane(lane) {
10717
10921
  if (lane.lifecycle.state !== "active") return false;
10718
10922
  switch (lane.laneKind) {
10719
10923
  case "owner_passkey":
10924
+ case "owner_voiceid":
10720
10925
  case "owner_email_otp":
10721
10926
  case "recovery":
10722
10927
  case "break_glass": return true;
@@ -11738,6 +11943,13 @@ function parsePasskeyCustodySecretBinding(raw, label = "passkeyCustodySecretBind
11738
11943
  const WALLET_CUSTODY_ENVELOPE_VERSION_V2 = "wallet_custody_envelope_v2";
11739
11944
  const PASSKEY_PRF_KEK_VERSION_V1 = "passkey_prf_kek_hkdf_sha256_v1";
11740
11945
  const EMAIL_OTP_FACTOR_KEK_VERSION_V1 = "email_otp_factor_kek_hkdf_sha256_v1";
11946
+ const VOICEID_DEVICE_FACTOR_KEK_VERSION_V1 = "voiceid_device_factor_kek_hkdf_sha256_v1";
11947
+ function isActiveMethodBoundWalletSeedEnvelope(envelope) {
11948
+ return envelope.ownership.kind === "method_bound" && envelope.binding.kind === "wallet_custody_seed_v1" && envelope.lifecycle.state === "active";
11949
+ }
11950
+ function isVoiceIdSeedEnvelope(envelope) {
11951
+ return isActiveMethodBoundWalletSeedEnvelope(envelope) && envelope.factor.kind === "voiceid";
11952
+ }
11741
11953
  const ENVELOPE_OWNERSHIP_FIELDS = ["kind", "walletAuthMethodId"];
11742
11954
  function parseWalletCustodyEnvelopeOwnership(raw, label) {
11743
11955
  if (typeof raw !== "object" || raw === null || Array.isArray(raw)) throw new Error(`${label} must be an object`);
@@ -11774,6 +11986,13 @@ function buildEmailOtpEnvelopeFactor(args) {
11774
11986
  kekVersion: EMAIL_OTP_FACTOR_KEK_VERSION_V1
11775
11987
  };
11776
11988
  }
11989
+ function buildVoiceIdEnvelopeFactor(args) {
11990
+ return {
11991
+ kind: "voiceid",
11992
+ deviceKeyId: args.deviceKeyId,
11993
+ kekVersion: VOICEID_DEVICE_FACTOR_KEK_VERSION_V1
11994
+ };
11995
+ }
11777
11996
  function buildActiveEnvelopeLifecycle(args) {
11778
11997
  return {
11779
11998
  state: "active",
@@ -11860,7 +12079,16 @@ const EMAIL_OTP_FACTOR_FIELDS = [
11860
12079
  "enrollmentSealKeyVersion",
11861
12080
  "kekVersion"
11862
12081
  ];
11863
- const ALL_FACTOR_FIELDS = Array.from(new Set([...PASSKEY_FACTOR_FIELDS, ...EMAIL_OTP_FACTOR_FIELDS]));
12082
+ const VOICEID_FACTOR_FIELDS = [
12083
+ "kind",
12084
+ "deviceKeyId",
12085
+ "kekVersion"
12086
+ ];
12087
+ const ALL_FACTOR_FIELDS = Array.from(new Set([
12088
+ ...PASSKEY_FACTOR_FIELDS,
12089
+ ...EMAIL_OTP_FACTOR_FIELDS,
12090
+ ...VOICEID_FACTOR_FIELDS
12091
+ ]));
11864
12092
  function requireNonEmptyString(value, label) {
11865
12093
  if (typeof value !== "string" || !value.trim()) throw new Error(`${label} is required`);
11866
12094
  return value;
@@ -11887,7 +12115,11 @@ function parseWalletCustodyEnvelopeFactor(raw, label = "factor") {
11887
12115
  enrollmentId: requireNonEmptyString(record.enrollmentId, `${label}.enrollmentId`),
11888
12116
  enrollmentSealKeyVersion: requireNonEmptyString(record.enrollmentSealKeyVersion, `${label}.enrollmentSealKeyVersion`)
11889
12117
  });
11890
- default: throw new Error(`${label}.kind must be passkey or email_otp`);
12118
+ case "voiceid":
12119
+ rejectUnknownFields(record, VOICEID_FACTOR_FIELDS, label, ALL_FACTOR_FIELDS);
12120
+ if (record.kekVersion !== VOICEID_DEVICE_FACTOR_KEK_VERSION_V1) throw new Error(`${label}.kekVersion must be ${VOICEID_DEVICE_FACTOR_KEK_VERSION_V1}`);
12121
+ return buildVoiceIdEnvelopeFactor({ deviceKeyId: parseDigestField$3(record.deviceKeyId, `${label}.deviceKeyId`) });
12122
+ default: throw new Error(`${label}.kind must be passkey, email_otp, or voiceid`);
11891
12123
  }
11892
12124
  }
11893
12125
  const ENVELOPE_RECORD_FIELDS = [
@@ -12419,16 +12651,14 @@ function normalizeAddAuthMethodIntentCaller(raw) {
12419
12651
  if (!source) return null;
12420
12652
  const walletAuthorityId = parseWalletAuthorityId("walletAuthorityId" in source ? source.walletAuthorityId : void 0);
12421
12653
  const walletAuthMethodId = parseWalletAuthMethodId("walletAuthMethodId" in source ? source.walletAuthMethodId : void 0);
12422
- const walletSessionId = trimString("walletSessionId" in source ? source.walletSessionId : void 0);
12423
12654
  const authorityDigestB64u = trimString("authorityDigestB64u" in source ? source.authorityDigestB64u : void 0);
12424
12655
  const revocationEpoch = "revocationEpoch" in source ? source.revocationEpoch : void 0;
12425
- if (!walletAuthorityId.ok || !walletAuthMethodId.ok || !walletSessionId || !authorityDigestB64u || typeof revocationEpoch !== "number" || !Number.isSafeInteger(revocationEpoch) || revocationEpoch < 0) return null;
12656
+ if (!walletAuthorityId.ok || !walletAuthMethodId.ok || !authorityDigestB64u || typeof revocationEpoch !== "number" || !Number.isSafeInteger(revocationEpoch) || revocationEpoch < 0) return null;
12426
12657
  return {
12427
12658
  caller: "same_device_addition",
12428
12659
  source: {
12429
12660
  walletAuthorityId: walletAuthorityId.value,
12430
12661
  walletAuthMethodId: walletAuthMethodId.value,
12431
- walletSessionId,
12432
12662
  authorityDigestB64u,
12433
12663
  revocationEpoch
12434
12664
  }
@@ -14817,6 +15047,290 @@ async function handleWalletRegistration(ctx) {
14817
15047
  return toFetchRouteResponse(response);
14818
15048
  }
14819
15049
  //#endregion
15050
+ //#region src/router/domains/voiceId/voiceIdActivation.ts
15051
+ /** Accepts only a method-bound owner seed envelope for the new VoiceID method. */
15052
+ function parseVoiceIdActivationEnvelope(raw) {
15053
+ const envelope = parsePasskeyCustodyEnvelopeRecord(raw);
15054
+ if (!isVoiceIdSeedEnvelope(envelope) || Number(envelope.envelopeRevision) !== 1) throw new Error("VoiceID activation requires an active, method-bound wallet seed envelope");
15055
+ return envelope;
15056
+ }
15057
+ //#endregion
15058
+ //#region src/router/transport/fetch/routes/walletVoiceIdActivation.ts
15059
+ const ACTIVATION_FIELDS = [
15060
+ "targetWalletAuthMethodId",
15061
+ "devicePublicKeyB64u",
15062
+ "custodyEnvelope"
15063
+ ];
15064
+ function hostedWalletOriginAllowed(request, allowedOrigins) {
15065
+ const rawOrigin = request.headers.get("origin");
15066
+ let requestOrigin;
15067
+ try {
15068
+ requestOrigin = parseSessionOrigin(rawOrigin);
15069
+ } catch {
15070
+ return false;
15071
+ }
15072
+ for (const allowed of allowedOrigins) try {
15073
+ if (parseSessionOrigin(allowed) === requestOrigin) return true;
15074
+ } catch {
15075
+ continue;
15076
+ }
15077
+ return false;
15078
+ }
15079
+ function parseActivationBody(raw) {
15080
+ const body = requireRecord$1(raw, "VoiceID activation");
15081
+ rejectUnknownFields(body, ACTIVATION_FIELDS, "VoiceID activation");
15082
+ const target = parseWalletAuthMethodId(body.targetWalletAuthMethodId);
15083
+ if (!target.ok) throw new Error(target.error.message);
15084
+ return {
15085
+ targetWalletAuthMethodId: target.value,
15086
+ devicePublicKeyB64u: parseVoiceIdDevicePublicKeyB64u(body.devicePublicKeyB64u),
15087
+ envelope: parseVoiceIdActivationEnvelope(body.custodyEnvelope)
15088
+ };
15089
+ }
15090
+ function rejectionStatus(code) {
15091
+ switch (code) {
15092
+ case "invalid_source": return 403;
15093
+ case "invalid_target": return 400;
15094
+ case "conflict": return 409;
15095
+ default: {
15096
+ const unexpected = code;
15097
+ throw new Error(`Unsupported VoiceID activation rejection: ${unexpected}`);
15098
+ }
15099
+ }
15100
+ }
15101
+ async function handleWalletVoiceIdActivation(ctx) {
15102
+ if (ctx.method !== "POST" || ctx.pathname !== "/wallet/voiceid/activate") return null;
15103
+ return await handleVoiceIdActivationRequest(ctx.request, {
15104
+ tenantId: ctx.service.authorizationSessions.tenantId,
15105
+ readWalletSessionAuthorizationV2ByOperationCredential: ctx.service.authorizationSessions.readWalletSessionAuthorizationV2ByOperationCredential,
15106
+ activateVoiceId: ctx.service.walletAuthMethods.activateVoiceId,
15107
+ hostedWalletOrigins: ctx.opts.hostedWalletOrigins ?? []
15108
+ });
15109
+ }
15110
+ async function handleVoiceIdActivationRequest(request, deps) {
15111
+ if (!hostedWalletOriginAllowed(request, deps.hostedWalletOrigins)) return json$3({
15112
+ ok: false,
15113
+ code: "origin_mismatch"
15114
+ }, { status: 403 });
15115
+ const credential = extractBearerCredential(request.headers);
15116
+ if (!credential) return json$3({
15117
+ ok: false,
15118
+ code: "unauthorized"
15119
+ }, { status: 401 });
15120
+ let token;
15121
+ try {
15122
+ token = parsePrimaryWalletSessionOperationCredentialToken(credential);
15123
+ } catch {
15124
+ return json$3({
15125
+ ok: false,
15126
+ code: "unauthorized"
15127
+ }, { status: 401 });
15128
+ }
15129
+ const nowMs = Date.now();
15130
+ let source;
15131
+ try {
15132
+ source = await deps.readWalletSessionAuthorizationV2ByOperationCredential({
15133
+ tenantId: deps.tenantId,
15134
+ token,
15135
+ nowMs
15136
+ });
15137
+ } catch {
15138
+ return json$3({
15139
+ ok: false,
15140
+ code: "wallet_session_unavailable"
15141
+ }, { status: 503 });
15142
+ }
15143
+ if (!source || source.retiredAtMs !== null || source.authMethod.status !== "active" || source.authMethod.kind !== "passkey" && source.authMethod.kind !== "email_otp") return json$3({
15144
+ ok: false,
15145
+ code: "unauthorized"
15146
+ }, { status: 401 });
15147
+ let parsedBody;
15148
+ try {
15149
+ parsedBody = parseActivationBody(await readJson(request));
15150
+ } catch (error) {
15151
+ return json$3({
15152
+ ok: false,
15153
+ code: "invalid_body",
15154
+ message: error instanceof Error ? error.message : "VoiceID activation body is invalid"
15155
+ }, { status: 400 });
15156
+ }
15157
+ let result;
15158
+ try {
15159
+ result = await deps.activateVoiceId({
15160
+ source: {
15161
+ authorization: source.authorization,
15162
+ authority: source.authority,
15163
+ authMethod: source.authMethod
15164
+ },
15165
+ targetWalletAuthMethodId: parsedBody.targetWalletAuthMethodId,
15166
+ devicePublicKeyB64u: parsedBody.devicePublicKeyB64u,
15167
+ envelope: parsedBody.envelope,
15168
+ nowMs
15169
+ });
15170
+ } catch {
15171
+ return json$3({
15172
+ ok: false,
15173
+ code: "activation_unavailable"
15174
+ }, { status: 503 });
15175
+ }
15176
+ if (result.kind === "rejected") return json$3({
15177
+ ok: false,
15178
+ code: result.code
15179
+ }, { status: rejectionStatus(result.code) });
15180
+ return json$3({
15181
+ ok: true,
15182
+ authMethod: result.authMethod
15183
+ }, { status: 200 });
15184
+ }
15185
+ //#endregion
15186
+ //#region src/router/transport/fetch/routes/walletVoiceIdNear.ts
15187
+ async function prepareVoiceIdNearInstallation(ctx, verified) {
15188
+ const session = verified.authorization.session;
15189
+ const activation = session.capabilitySubjects.find(isNearSignSubject);
15190
+ if (!activation || activation.kind !== "sign") return { kind: "unavailable" };
15191
+ const resolved = await ctx.service.walletRegistration.resolveEd25519MaterialActivation({
15192
+ walletId: session.walletId,
15193
+ materialActivation: routerAbMpcMaterialActivationRefToWire(activation.materialActivation)
15194
+ });
15195
+ if (!resolved.ok) throw new Error(resolved.message);
15196
+ const product = ctx.opts.routerAbEd25519YaoProduct;
15197
+ if (!product) throw new Error("NEAR signing is not configured");
15198
+ const capability = await product.resolveActiveCapability({
15199
+ kind: "router_ab_ed25519_yao_active_capability_lookup_v1",
15200
+ walletId: session.walletId,
15201
+ nearEd25519SigningKeyId: resolved.exportIdentity.application_binding.near_ed25519_signing_key_id,
15202
+ signerSlot: resolved.signerSlot,
15203
+ signingWorkerId: resolved.signingWorkerId,
15204
+ participantIds: resolved.participantIds
15205
+ });
15206
+ if (!capability.ok) throw new Error(capability.message);
15207
+ if (!sameRouterAbMpcMaterialActivationRef(capability.capability.materialActivation, resolved.materialActivation)) throw new Error("NEAR capability does not belong to this Wallet Session");
15208
+ return {
15209
+ kind: "ready",
15210
+ capability: capability.capability,
15211
+ remainingUses: verified.authorization.quota.remainingUses
15212
+ };
15213
+ }
15214
+ function isNearSignSubject(subject) {
15215
+ return subject.kind === "sign" && subject.keyFamily === "ed25519";
15216
+ }
15217
+ //#endregion
15218
+ //#region src/router/transport/fetch/routes/walletVoiceIdSessions.ts
15219
+ function hostedOrigin(request, allowedOrigins) {
15220
+ if (!hostedWalletOriginAllowed(request, allowedOrigins)) return null;
15221
+ return parseSessionOrigin(request.headers.get("origin"));
15222
+ }
15223
+ function parseChallengeBody(raw) {
15224
+ const body = requireRecord$1(raw, "VoiceID challenge");
15225
+ rejectUnknownFields(body, ["walletId", "walletAuthMethodId"], "VoiceID challenge");
15226
+ const walletId = parseWalletId(body.walletId);
15227
+ if (!walletId.ok) throw new Error(walletId.error.message);
15228
+ const walletAuthMethodId = parseWalletAuthMethodId(body.walletAuthMethodId);
15229
+ if (!walletAuthMethodId.ok) throw new Error(walletAuthMethodId.error.message);
15230
+ return {
15231
+ walletId: walletId.value,
15232
+ walletAuthMethodId: walletAuthMethodId.value
15233
+ };
15234
+ }
15235
+ async function handleWalletVoiceIdChallenge(ctx) {
15236
+ if (ctx.method !== "POST" || ctx.pathname !== "/wallet/voiceid/challenge") return null;
15237
+ return await handleVoiceIdChallengeRequest(ctx.request, {
15238
+ issueVoiceIdChallenge: ctx.service.walletUnlock.issueVoiceIdChallenge,
15239
+ hostedWalletOrigins: ctx.opts.hostedWalletOrigins ?? []
15240
+ });
15241
+ }
15242
+ async function handleWalletVoiceIdVerify(ctx) {
15243
+ if (ctx.method !== "POST" || ctx.pathname !== "/wallet/voiceid/verify") return null;
15244
+ return await handleVoiceIdVerifyRequest(ctx.request, {
15245
+ verifyAndIssueVoiceId: ctx.service.walletUnlock.verifyAndIssueVoiceId,
15246
+ prepareNear: prepareVoiceIdNearInstallation.bind(null, ctx),
15247
+ hostedWalletOrigins: ctx.opts.hostedWalletOrigins ?? []
15248
+ });
15249
+ }
15250
+ async function handleVoiceIdChallengeRequest(request, deps) {
15251
+ const requestOrigin = hostedOrigin(request, deps.hostedWalletOrigins);
15252
+ if (!requestOrigin) return json$3({
15253
+ ok: false,
15254
+ code: "origin_mismatch"
15255
+ }, { status: 403 });
15256
+ let body;
15257
+ try {
15258
+ body = parseChallengeBody(await readJson(request));
15259
+ } catch (error) {
15260
+ return json$3({
15261
+ ok: false,
15262
+ code: "invalid_body",
15263
+ message: error instanceof Error ? error.message : "VoiceID challenge body is invalid"
15264
+ }, { status: 400 });
15265
+ }
15266
+ try {
15267
+ const result = await deps.issueVoiceIdChallenge({
15268
+ walletId: body.walletId,
15269
+ walletAuthMethodId: body.walletAuthMethodId,
15270
+ requestOrigin,
15271
+ nowMs: Date.now()
15272
+ });
15273
+ if (result.kind === "rejected") return json$3({
15274
+ ok: false,
15275
+ code: result.code
15276
+ }, { status: 403 });
15277
+ return json$3({
15278
+ ok: true,
15279
+ challenge: result.challenge
15280
+ }, { status: 200 });
15281
+ } catch {
15282
+ return json$3({
15283
+ ok: false,
15284
+ code: "challenge_unavailable"
15285
+ }, { status: 503 });
15286
+ }
15287
+ }
15288
+ async function handleVoiceIdVerifyRequest(request, deps) {
15289
+ const requestOrigin = hostedOrigin(request, deps.hostedWalletOrigins);
15290
+ if (!requestOrigin) return json$3({
15291
+ ok: false,
15292
+ code: "origin_mismatch"
15293
+ }, { status: 403 });
15294
+ let assertion;
15295
+ try {
15296
+ const body = requireRecord$1(await readJson(request), "VoiceID verification");
15297
+ rejectUnknownFields(body, ["assertion"], "VoiceID verification");
15298
+ assertion = parseVoiceIdDeviceAssertion(body.assertion);
15299
+ } catch (error) {
15300
+ return json$3({
15301
+ ok: false,
15302
+ code: "invalid_body",
15303
+ message: error instanceof Error ? error.message : "VoiceID assertion is invalid"
15304
+ }, { status: 400 });
15305
+ }
15306
+ try {
15307
+ const result = await deps.verifyAndIssueVoiceId({
15308
+ assertion,
15309
+ requestOrigin,
15310
+ nowMs: Date.now()
15311
+ });
15312
+ if (result.kind === "rejected") {
15313
+ const status = result.code === "issuance_unavailable" ? 503 : 403;
15314
+ return json$3({
15315
+ ok: false,
15316
+ code: result.code
15317
+ }, { status });
15318
+ }
15319
+ return json$3({
15320
+ ok: true,
15321
+ walletSession: projectActiveWalletSession(result.authorization),
15322
+ operationCredential: result.operationCredential,
15323
+ custodyEnvelope: result.custodyEnvelope,
15324
+ near: await deps.prepareNear(result)
15325
+ }, { status: 200 });
15326
+ } catch {
15327
+ return json$3({
15328
+ ok: false,
15329
+ code: "verification_unavailable"
15330
+ }, { status: 503 });
15331
+ }
15332
+ }
15333
+ //#endregion
14820
15334
  //#region ../shared-ts/src/utils/errors.ts
14821
15335
  /**
14822
15336
  * Best-effort error message extractor without relying on `any`.
@@ -19888,6 +20402,14 @@ function walletAuthorityForAuthMethod(authMethod, authSource) {
19888
20402
  bindingId: authMethod.walletAuthMethodId
19889
20403
  };
19890
20404
  }
20405
+ if (authMethod.kind === "voiceid") {
20406
+ if (authSource.kind !== "voiceid" || authSource.deviceKeyId !== authMethod.deviceKeyId) return null;
20407
+ return buildVoiceIdWalletAuthAuthority({
20408
+ walletId: authMethod.walletId,
20409
+ walletAuthMethodId: authMethod.walletAuthMethodId,
20410
+ deviceKeyId: authMethod.deviceKeyId
20411
+ });
20412
+ }
19891
20413
  if (authSource.kind !== "oidc_provider") return null;
19892
20414
  const providerUserId = parseEmailOtpProviderUserId(authSource.providerSubject);
19893
20415
  if (!providerUserId.ok) return null;
@@ -25679,6 +26201,7 @@ function serializeProjection(value) {
25679
26201
  const lane = value.lane;
25680
26202
  switch (lane.laneKind) {
25681
26203
  case "owner_passkey":
26204
+ case "owner_voiceid":
25682
26205
  case "owner_email_otp": return {
25683
26206
  kind: value.kind,
25684
26207
  walletKey: serializeWalletKey(value.walletKey),
@@ -27890,6 +28413,10 @@ function thresholdEd25519AuthorityScopeFromWalletAuthAuthority(authority) {
27890
28413
  provider: authority.factor.provider,
27891
28414
  providerUserId: authority.factor.providerUserId
27892
28415
  };
28416
+ if (isVoiceIdWalletAuthAuthority(authority)) return {
28417
+ kind: "voiceid",
28418
+ deviceKeyId: authority.factor.deviceKeyId
28419
+ };
27893
28420
  throw new Error("[threshold-ed25519] unsupported wallet auth authority");
27894
28421
  }
27895
28422
  async function authenticateDeviceLinkingOwnerWalletSessionRequestV1(input) {
@@ -28064,6 +28591,17 @@ function walletAuthAuthorityFromExactV2MethodV1(input) {
28064
28591
  } catch {
28065
28592
  return null;
28066
28593
  }
28594
+ if (input.authMethod.kind === "voiceid") return {
28595
+ authority: buildVoiceIdWalletAuthAuthority({
28596
+ walletId: input.walletId,
28597
+ walletAuthMethodId: input.authMethod.walletAuthMethodId,
28598
+ deviceKeyId: input.authMethod.deviceKeyId
28599
+ }),
28600
+ authSource: {
28601
+ kind: "voiceid",
28602
+ deviceKeyId: input.authMethod.deviceKeyId
28603
+ }
28604
+ };
28067
28605
  const providerSubject = parseProviderSubject(input.principalId);
28068
28606
  if (!providerSubject.ok) return null;
28069
28607
  try {
@@ -28482,6 +29020,9 @@ function createFetchRouter(service, opts, runtime) {
28482
29020
  handleHostedWalletSessionExchangeRedeem,
28483
29021
  handleWalletUnlockChallenge,
28484
29022
  handleWalletUnlockVerify,
29023
+ handleWalletVoiceIdActivation,
29024
+ handleWalletVoiceIdChallenge,
29025
+ handleWalletVoiceIdVerify,
28485
29026
  handleWalletEmailOtpChallenge,
28486
29027
  handleWalletEmailOtpFactorRelease,
28487
29028
  handleWalletEmailOtpDevCleanupGoogleRegistration,
@@ -32819,6 +33360,8 @@ Object.freeze([
32819
33360
  updated_at_ms INTEGER NOT NULL,
32820
33361
  activated_at_ms INTEGER,
32821
33362
  revoked_at_ms INTEGER,
33363
+ provider TEXT,
33364
+ provider_user_id TEXT,
32822
33365
  PRIMARY KEY (namespace, org_id, project_id, env_id, wallet_auth_method_id),
32823
33366
  FOREIGN KEY (
32824
33367
  namespace,
@@ -32835,7 +33378,7 @@ Object.freeze([
32835
33378
  ),
32836
33379
  CHECK (length(wallet_id) > 0),
32837
33380
  CHECK (length(wallet_authority_id) > 0),
32838
- CHECK (kind IN ('passkey', 'email_otp')),
33381
+ CHECK (kind IN ('passkey', 'email_otp', 'voiceid')),
32839
33382
  CHECK (status IN ('pending_local_install', 'active', 'revoked')),
32840
33383
  CHECK (length(wallet_auth_method_id) > 0),
32841
33384
  CHECK (length(auth_identifier_key) > 0),
@@ -32869,6 +33412,18 @@ Object.freeze([
32869
33412
  AND registration_authority_id IS NOT NULL
32870
33413
  AND length(registration_authority_id) > 0
32871
33414
  AND auth_identifier_key = email_hash_hex)
33415
+ OR
33416
+ (kind = 'voiceid'
33417
+ AND rp_id = ''
33418
+ AND credential_id_b64u IS NULL
33419
+ AND credential_public_key_b64u IS NOT NULL
33420
+ AND length(credential_public_key_b64u) = 43
33421
+ AND email_hash_hex IS NULL
33422
+ AND registration_authority_id IS NULL
33423
+ AND provider IS NULL
33424
+ AND provider_user_id IS NULL
33425
+ AND length(auth_identifier_key) = 43
33426
+ AND COALESCE(auth_identifier_key = json_extract(record_json, '$.deviceKeyId'), 0))
32872
33427
  ),
32873
33428
  CHECK (json_extract(record_json, '$.version') = 'wallet_auth_method_v2'),
32874
33429
  CHECK (json_extract(record_json, '$.walletAuthMethodId') = wallet_auth_method_id),
@@ -32888,6 +33443,10 @@ Object.freeze([
32888
33443
  (kind = 'email_otp'
32889
33444
  AND json_extract(record_json, '$.emailHashHex') = email_hash_hex
32890
33445
  AND json_extract(record_json, '$.registrationAuthorityId') = registration_authority_id)
33446
+ OR
33447
+ (kind = 'voiceid'
33448
+ AND COALESCE(json_extract(record_json, '$.deviceKeyId') = auth_identifier_key, 0)
33449
+ AND COALESCE(json_extract(record_json, '$.devicePublicKeyB64u') = credential_public_key_b64u, 0))
32891
33450
  )
32892
33451
  )
32893
33452
  `,
@@ -32902,6 +33461,13 @@ Object.freeze([
32902
33461
  wallet_authority_id,
32903
33462
  status
32904
33463
  )
33464
+ `,
33465
+ `
33466
+ CREATE UNIQUE INDEX IF NOT EXISTS wallet_auth_methods_v2_voiceid_key_uidx
33467
+ ON wallet_auth_methods (
33468
+ namespace, org_id, project_id, env_id, auth_identifier_key
33469
+ )
33470
+ WHERE kind = 'voiceid'
32905
33471
  `,
32906
33472
  `
32907
33473
  CREATE UNIQUE INDEX IF NOT EXISTS wallet_auth_methods_v2_passkey_uidx