@seams/wallet-server 0.27.0 → 0.29.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 (182) hide show
  1. package/README.md +19 -4
  2. package/artifact-manifest.json +28 -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/application-auth.js +2 -0
  9. package/dist/esm/auth/applicationAuth.js +85 -0
  10. package/dist/esm/auth/externalAuthService.js +149 -0
  11. package/dist/esm/auth/externalAuthStore.js +353 -0
  12. package/dist/esm/auth/externalOwnerOperations.js +82 -0
  13. package/dist/esm/authorization/domain.js +2 -2
  14. package/dist/esm/authorization/factorEvidence.js +1 -1
  15. package/dist/esm/authorization/service.js +2 -2
  16. package/dist/esm/authorization/vaultProxyUse.js +1 -1
  17. package/dist/esm/cloud-host.js +12 -11
  18. package/dist/esm/core/ThresholdService/validation.js +1 -1
  19. package/dist/esm/core/WebAuthnCredentialBindingStore.js +1 -1
  20. package/dist/esm/core/authService/webauthn.js +1 -1
  21. package/dist/esm/core/d1EcdsaSignerRead.js +1 -1
  22. package/dist/esm/core/d1WalletAuthMethodStore.js +2 -2
  23. package/dist/esm/core/d1WalletStore.js +1 -1
  24. package/dist/esm/core/deviceLinking/linkedDeviceEmailOtpGrant.js +1 -1
  25. package/dist/esm/core/deviceLinking/linkedDeviceManagement.js +1 -1
  26. package/dist/esm/core/deviceLinking/linkedDeviceSessionRecord.js +2 -2
  27. package/dist/esm/core/nearRelayerAccountProvisioning.js +2 -2
  28. package/dist/esm/hosted-wallet-gateway.js +3 -3
  29. package/dist/esm/index.js +1 -1
  30. package/dist/esm/router/auth/webAuthnCredentialCodecs.js +1 -1
  31. package/dist/esm/router/cloudflare/d1/auth/d1AuthorizedOperationRouteService.js +2 -0
  32. package/dist/esm/router/cloudflare/d1/auth/d1RecordedOwnerOperation.js +43 -0
  33. package/dist/esm/router/cloudflare/d1/auth/d1RouterApiAuthService.js +27 -3
  34. package/dist/esm/router/cloudflare/d1/authorization/d1AuthorizationStore.js +2 -2
  35. package/dist/esm/router/cloudflare/d1/authorization/d1VaultProxyStore.js +2 -2
  36. package/dist/esm/router/cloudflare/d1/deviceLinking/d1LinkedDeviceAuthorityInstallService.js +3 -3
  37. package/dist/esm/router/cloudflare/d1/deviceLinking/d1LinkedDeviceEd25519ExportRootStore.js +1 -1
  38. package/dist/esm/router/cloudflare/d1/deviceLinking/d1LinkedDeviceOwnerAuthorizationProvider.js +2 -2
  39. package/dist/esm/router/cloudflare/d1/deviceLinking/d1LinkedDeviceSessionStore.js +1 -1
  40. package/dist/esm/router/cloudflare/d1/deviceLinking/d1LinkedDeviceSourceContributionPreparationPlanner.js +1 -1
  41. package/dist/esm/router/cloudflare/d1/deviceLinking/d1LinkedDeviceTargetAuthenticatorStore.js +1 -1
  42. package/dist/esm/router/cloudflare/d1/deviceLinking/d1LinkedDeviceTargetCredentialProvider.js +1 -1
  43. package/dist/esm/router/cloudflare/d1/deviceLinking/d1LinkedDeviceTargetPlanner.js +1 -1
  44. package/dist/esm/router/cloudflare/d1/deviceLinking/d1LinkedDeviceVerifiedLinkBuilder.js +2 -2
  45. package/dist/esm/router/cloudflare/d1/deviceLinking/d1LinkedDeviceVerifiedLinkSourceReader.js +1 -1
  46. package/dist/esm/router/cloudflare/d1/ed25519Yao/d1Ed25519YaoCapabilityPersistence.js +1 -1
  47. package/dist/esm/router/cloudflare/d1/emailOtp/d1EmailOtpChallengeIssuer.js +1 -1
  48. package/dist/esm/router/cloudflare/d1/emailOtp/d1EmailOtpChallengeService.js +1 -1
  49. package/dist/esm/router/cloudflare/d1/emailOtp/d1EmailOtpChallengeStore.js +1 -1
  50. package/dist/esm/router/cloudflare/d1/emailOtp/d1EmailOtpRecords.js +2 -2
  51. package/dist/esm/router/cloudflare/d1/emailOtp/d1EmailOtpRecoveryService.js +3 -3
  52. package/dist/esm/router/cloudflare/d1/emailOtp/d1EmailOtpRegistrationEnrollmentFinalizer.js +1 -1
  53. package/dist/esm/router/cloudflare/d1/emailOtp/d1GoogleEmailOtpRegistrationAttemptStore.js +1 -1
  54. package/dist/esm/router/cloudflare/d1/emailOtp/d1GoogleEmailOtpSessionResolver.js +3 -3
  55. package/dist/esm/router/cloudflare/d1/oidc/d1OidcBoundary.js +7 -1
  56. package/dist/esm/router/cloudflare/d1/oidc/d1OidcVerificationService.js +1 -1
  57. package/dist/esm/router/cloudflare/d1/passkeyCustody/d1PasskeyCustodyRouteService.js +4 -4
  58. package/dist/esm/router/cloudflare/d1/passkeyCustody/d1WalletCustodyCommitStore.js +4 -4
  59. package/dist/esm/router/cloudflare/d1/passkeyCustody/d1WalletRecoveryGoogleEmailOtpRecords.js +1 -1
  60. package/dist/esm/router/cloudflare/d1/passkeyCustody/d1WalletRecoveryGoogleEmailOtpService.js +3 -3
  61. package/dist/esm/router/cloudflare/d1/registration/d1EvmFamilyEcdsaRegistrationBranch.js +1 -1
  62. package/dist/esm/router/cloudflare/d1/registration/d1RegistrationCeremonyRecords.js +11 -2
  63. package/dist/esm/router/cloudflare/d1/registration/d1RegistrationIntentService.js +1 -1
  64. package/dist/esm/router/cloudflare/d1/registration/d1WalletExecutionAuthority.js +9 -16
  65. package/dist/esm/router/cloudflare/d1/registration/d1WalletRegistrationCommitStore.js +1 -1
  66. package/dist/esm/router/cloudflare/d1/registration/d1WalletRegistrationService.js +46 -4
  67. package/dist/esm/router/cloudflare/d1/registration/d1WalletRegistrationSetup.js +12 -0
  68. package/dist/esm/router/cloudflare/d1/registration/d1WalletRegistrationSetupExecution.js +1 -0
  69. package/dist/esm/router/cloudflare/d1/registration/registrationCompletionWrite.js +1 -0
  70. package/dist/esm/router/cloudflare/d1/registration/walletRegistrationEstablishedSessionIssuer.js +1 -1
  71. package/dist/esm/router/cloudflare/d1/registration/walletRegistrationSessionCommitReceipt.js +36 -1
  72. package/dist/esm/router/cloudflare/d1/signingAdmission/d1RouterAbNormalSigningAdmissionStore.js +1 -1
  73. package/dist/esm/router/cloudflare/d1/wallet/authorizationSnapshotLayout.js +1 -0
  74. package/dist/esm/router/cloudflare/d1/wallet/d1VoiceIdWalletSessionChallenges.js +2 -2
  75. package/dist/esm/router/cloudflare/d1/wallet/d1WalletAuthMethodBoundary.js +1 -1
  76. package/dist/esm/router/cloudflare/d1/wallet/d1WalletAuthMethodRevocationReplayStore.js +1 -1
  77. package/dist/esm/router/cloudflare/d1/wallet/d1WalletAuthMethodService.js +6 -5
  78. package/dist/esm/router/cloudflare/d1/wallet/d1WalletAuthorityStore.js +2 -2
  79. package/dist/esm/router/cloudflare/d1/wallet/d1WalletAuthorizationCleanup.js +2 -1
  80. package/dist/esm/router/cloudflare/d1/wallet/d1WalletAuthorizationIntake.js +1 -1
  81. package/dist/esm/router/cloudflare/d1/wallet/d1WalletAuthorizationSnapshot.js +1 -1
  82. package/dist/esm/router/cloudflare/d1/wallet/d1WalletRelocationChallenges.js +1 -1
  83. package/dist/esm/router/cloudflare/d1/wallet/d1WalletRelocationOwnerRequest.js +2 -2
  84. package/dist/esm/router/cloudflare/d1/webauthn/d1WebAuthnAuthService.js +2 -2
  85. package/dist/esm/router/cloudflare/d1/webauthn/d1WebAuthnRecords.js +2 -2
  86. package/dist/esm/router/cloudflare/runtime/staticWalletConsoleBinding.js +9 -0
  87. package/dist/esm/router/cloudflare/runtime/walletConsoleOpsClient.js +6 -0
  88. package/dist/esm/router/cloudflare-adaptor.js +1 -1
  89. package/dist/esm/router/cloudflare.js +6139 -5567
  90. package/dist/esm/router/domains/ecdsa/routerAbEcdsaStrictRegistration.js +1 -1
  91. package/dist/esm/router/domains/ed25519Yao/export/routerAbEd25519YaoExport.js +5 -5
  92. package/dist/esm/router/domains/ed25519Yao/recovery/routerAbEd25519YaoRecovery.js +2 -2
  93. package/dist/esm/router/domains/ed25519Yao/recovery/routerAbEd25519YaoRecoveryWalletSessionAuthorization.js +1 -1
  94. package/dist/esm/router/domains/ed25519Yao/registration/routerAbEd25519YaoHttpRegistrationBackend.js +1 -1
  95. package/dist/esm/router/domains/ed25519Yao/registration/routerAbEd25519YaoRegistration.js +1 -1
  96. package/dist/esm/router/domains/ed25519Yao/registration/routerAbEd25519YaoRegistrationIntentAuthorization.js +1 -1
  97. package/dist/esm/router/domains/emailOtp/emailOtpRouteHandlers.js +2 -2
  98. package/dist/esm/router/domains/passkeyCustody/walletCustodyRegistrationCommit.js +1 -1
  99. package/dist/esm/router/domains/signingOperations/routerAbEcdsaDerivationNormalSigningRoute.js +2 -2
  100. package/dist/esm/router/domains/signingOperations/routerAbEd25519NormalSigningRoute.js +37 -3
  101. package/dist/esm/router/domains/signingOperations/routerAbOperationStepUp.js +1 -1
  102. package/dist/esm/router/domains/tenantRoot/tenantRootIdentityResolution.js +1 -1
  103. package/dist/esm/router/domains/walletRegistration/walletRegistrationReservation.js +18 -3
  104. package/dist/esm/router/domains/walletRegistration/walletRegistrationRoutes.js +12 -4
  105. package/dist/esm/router/domains/walletUnlock/walletUnlockRouteHandlers.js +3 -3
  106. package/dist/esm/router/framework/http.js +1 -1
  107. package/dist/esm/router/framework/routeDefinitions.js +2 -1
  108. package/dist/esm/router/transport/fetch/createFetchRouter.js +5 -3
  109. package/dist/esm/router/transport/fetch/routes/auth.js +1 -1
  110. package/dist/esm/router/transport/fetch/routes/deviceLinking.js +3 -3
  111. package/dist/esm/router/transport/fetch/routes/deviceLinkingOwnerAuthorization.js +1 -1
  112. package/dist/esm/router/transport/fetch/routes/ecdsaOperationStepUpFactor.js +3 -3
  113. package/dist/esm/router/transport/fetch/routes/externalAuth.js +351 -0
  114. package/dist/esm/router/transport/fetch/routes/normalSigningRouterProxy.js +1 -1
  115. package/dist/esm/router/transport/fetch/routes/passkeyCustody.js +46 -306
  116. package/dist/esm/router/transport/fetch/routes/sessions.js +4 -4
  117. package/dist/esm/router/transport/fetch/routes/syncAccount.js +2 -2
  118. package/dist/esm/router/transport/fetch/routes/syncAccountBootstrap.js +1 -1
  119. package/dist/esm/router/transport/fetch/routes/thresholdEcdsa.js +4 -4
  120. package/dist/esm/router/transport/fetch/routes/thresholdEd25519.js +6 -6
  121. package/dist/esm/router/transport/fetch/routes/walletExecutionLanePreflight.js +2 -2
  122. package/dist/esm/router/transport/fetch/routes/walletRegistration.js +4 -4
  123. package/dist/esm/router/transport/fetch/walletOwnerOperationAuthorization.js +306 -0
  124. package/dist/esm/shared-ts/src/auth/externalAuth.js +118 -0
  125. package/dist/esm/shared-ts/src/authorization/index.js +1 -1
  126. package/dist/esm/shared-ts/src/authorization/voiceIdDeviceAssertion.js +1 -1
  127. package/dist/esm/shared-ts/src/authorization/walletAuthority.js +2 -2
  128. package/dist/esm/shared-ts/src/authorization/{walletCustodyOperation.js → walletOwnerOperation.js} +4 -4
  129. package/dist/esm/shared-ts/src/device-linking/delegatedActivationPlan.js +1 -1
  130. package/dist/esm/shared-ts/src/device-linking/digests.js +1 -1
  131. package/dist/esm/shared-ts/src/device-linking/ed25519ExportRoot.js +1 -1
  132. package/dist/esm/shared-ts/src/device-linking/parsers.js +2 -2
  133. package/dist/esm/shared-ts/src/device-linking/sourceContribution.js +1 -1
  134. package/dist/esm/shared-ts/src/device-linking/walletSessionCredentialDelivery.js +1 -1
  135. package/dist/esm/shared-ts/src/device-linking/wireFields.js +2 -2
  136. package/dist/esm/shared-ts/src/passkey-custody/custodyEnvelope.js +1 -1
  137. package/dist/esm/shared-ts/src/passkey-custody/custodySecretBinding.js +1 -1
  138. package/dist/esm/shared-ts/src/signing-lanes/ownerContinuity.js +1 -1
  139. package/dist/esm/shared-ts/src/utils/routerAbEcdsaDerivation.js +1 -1
  140. package/dist/esm/shared-ts/src/utils/walletAuthAuthority.js +1 -1
  141. package/dist/esm/shared-ts/src/wallet-recovery/index.js +3 -3
  142. package/dist/esm/shared-ts/src/wallet-recovery/walletRecoveryCommittedProjection.js +1 -1
  143. package/dist/esm/shared-ts/src/wallet-recovery/walletRecoveryEnvelopeSet.js +1 -1
  144. package/dist/esm/threshold/session/signingSessionSeal/crypto/cipher.js +1 -1
  145. package/dist/esm/threshold/session/signingSessionSeal/service.js +1 -1
  146. package/dist/esm/threshold/session/signingSessionSeal/transport/fetch.js +1 -1
  147. package/dist/esm/threshold/session/signingSessionSeal/transport/shared.js +1 -1
  148. package/dist/types/shared-ts/src/auth/externalAuth.d.ts +127 -0
  149. package/dist/types/shared-ts/src/authorization/index.d.ts +1 -1
  150. package/dist/types/shared-ts/src/authorization/walletOwnerOperation.d.ts +10 -0
  151. package/dist/types/shared-ts/src/utils/registrationEstablishedSession.d.ts +4 -0
  152. package/dist/types/shared-ts/src/utils/registrationIntent.d.ts +2 -0
  153. package/dist/types/shared-ts/src/utils/walletManagementProtocol.d.ts +1 -1
  154. package/dist/types/wallet-server/src/application-auth.d.ts +3 -0
  155. package/dist/types/wallet-server/src/auth/applicationAuth.d.ts +29 -0
  156. package/dist/types/wallet-server/src/auth/externalAuthService.d.ts +55 -0
  157. package/dist/types/wallet-server/src/auth/externalAuthStore.d.ts +115 -0
  158. package/dist/types/wallet-server/src/auth/externalOwnerOperations.d.ts +18 -0
  159. package/dist/types/wallet-server/src/cloud-host.d.ts +2 -1
  160. package/dist/types/wallet-server/src/core/threeRouteRegistrationContracts.d.ts +12 -0
  161. package/dist/types/wallet-server/src/router/cloudflare/d1/auth/d1AuthorizedOperationRouteService.d.ts +5 -1
  162. package/dist/types/wallet-server/src/router/cloudflare/d1/auth/d1RecordedOwnerOperation.d.ts +16 -0
  163. package/dist/types/wallet-server/src/router/cloudflare/d1/oidc/d1OidcBoundary.d.ts +5 -0
  164. package/dist/types/wallet-server/src/router/cloudflare/d1/registration/d1RegistrationCeremonyRecords.d.ts +1 -0
  165. package/dist/types/wallet-server/src/router/cloudflare/d1/registration/d1WalletRegistrationSetup.d.ts +2 -0
  166. package/dist/types/wallet-server/src/router/cloudflare/d1/registration/walletRegistrationSessionCommitReceipt.d.ts +7 -1
  167. package/dist/types/wallet-server/src/router/cloudflare/d1/wallet/authorizationSnapshotLayout.d.ts +1 -1
  168. package/dist/types/wallet-server/src/router/cloudflare/d1/wallet/d1WalletAuthMethodService.d.ts +1 -0
  169. package/dist/types/wallet-server/src/router/cloudflare/runtime/staticWalletConsoleBinding.d.ts +2 -0
  170. package/dist/types/wallet-server/src/router/domains/signingOperations/routerAbEd25519NormalSigningRoute.d.ts +2 -2
  171. package/dist/types/wallet-server/src/router/domains/signingOperations/routerAbOperationStepUp.d.ts +1 -1
  172. package/dist/types/wallet-server/src/router/domains/walletRegistration/walletRegistrationInputs.d.ts +1 -0
  173. package/dist/types/wallet-server/src/router/domains/walletRegistration/walletRegistrationReservation.d.ts +11 -6
  174. package/dist/types/wallet-server/src/router/framework/apiCredentialPorts.d.ts +1 -0
  175. package/dist/types/wallet-server/src/router/framework/authServicePort.d.ts +12 -0
  176. package/dist/types/wallet-server/src/router/transport/fetch/routes/externalAuth.d.ts +5 -0
  177. package/dist/types/wallet-server/src/router/transport/fetch/walletOwnerOperationAuthorization.d.ts +23 -0
  178. package/migrations/d1-signer/0085_external_wallet_auth.sql +158 -0
  179. package/migrations/d1-signer/0086_application_auth.sql +67 -0
  180. package/migrations/d1-signer/0087_registration_without_session.sql +43 -0
  181. package/package.json +7 -2
  182. package/dist/types/shared-ts/src/authorization/walletCustodyOperation.d.ts +0 -10
@@ -1,33 +1,30 @@
1
1
  import { parseEmailOtpChallengeId, parseOrgId, parseProviderSubject, parseWalletAuthMethodId, parseWalletAuthorityId, parseWalletId, parseWalletRecoveryOperationId, parseWebAuthnCredentialIdB64u, parseWebAuthnRpId } from "../../../../shared-ts/src/utils/domainIds.js";
2
- import { buildVaultOperationRef, parseAuthFactorId, parseAuthorizationAuditEventId, parseAuthorizationEvidenceId, parseAuthorizationEvidenceSetId, parseAuthorizedOperationId, parseCapabilityId, parseCapabilityOperationId, parseDeviceId, parsePrincipalId, parseTenantId } from "../../../../shared-ts/src/authorization/capabilityKinds.js";
3
2
  import { normalizeOptionalTrimmedString } from "../../../../shared-ts/src/utils/normalize.js";
4
3
  import { isPlainObject, requireTrimmedString } from "../../../../shared-ts/src/utils/validation.js";
5
- import { buildDelegatedWalletAuthorityV1, buildFullOwnerPermissionsV1, sameDelegatedWalletAuthorityV1 } from "../../../../shared-ts/src/authorization/delegatedAuthority.js";
6
4
  import { base64UrlDecode } from "../../../../shared-ts/src/utils/base64.js";
7
- import { alphabetizeStringify } from "../../../../shared-ts/src/utils/digests.js";
8
- import { sha256Utf8DigestB64u } from "../../../../shared-ts/src/utils/canonicalPrimitives.js";
9
- import { buildCapabilityOperationEnvelope, computeCapabilityOperationFingerprintDigest } from "../../../../shared-ts/src/authorization/operationFingerprint.js";
10
- import { buildPasskeyWalletAuthAuthority, walletAuthAuthorityRef } from "../../../../shared-ts/src/utils/walletAuthAuthority.js";
11
- import { parseSessionOrigin, parseVerifiedOwnerProofId } from "../../../../authorization/domain.js";
12
- import { buildVerifiedWalletOperationEmailOtpFactorResult, buildVerifiedWalletOperationPasskeyFactorResult } from "../../../../authorization/factorEvidence.js";
13
- import { findRouteDefinitionById, matchesRouteDefinitionRequest } from "../../../framework/routeDefinitions.js";
14
- import { extractBearerCredential, resolveSourceIpFromFetchHeaders } from "../../../auth/routerApiKeyAuth.js";
5
+ import { requireParsed } from "../../../../shared-ts/src/passkey-custody/primitives.js";
6
+ import { computeWalletOwnerAdminChallengeDigest } from "../../../../shared-ts/src/authorization/walletOwnerOperation.js";
7
+ import { parseDeviceId } from "../../../../shared-ts/src/authorization/capabilityKinds.js";
8
+ import { walletAuthAuthorityRef } from "../../../../shared-ts/src/utils/walletAuthAuthority.js";
9
+ import { parseSessionOrigin } from "../../../../authorization/domain.js";
10
+ import { EMAIL_OTP_CHANNEL, WALLET_EMAIL_OTP_UNLOCK_OPERATION } from "../../../../shared-ts/src/utils/emailOtpDomain.js";
11
+ import { parseWebAuthnAuthenticationCredential, unredactedWebAuthnExtensionField } from "../../../auth/webAuthnCredentialCodecs.js";
12
+ import { emailOtpStatusCode, hashEmailOtpOperationBinding } from "../../../domains/emailOtp/emailOtpSessionRouteHelpers.js";
15
13
  import { toFetchRouteResponse } from "../../../framework/routeResponses.js";
14
+ import { parseRecoveryBackupAcknowledgementRequest, parseRecoveryStatusRequest, requireExactObjectFields } from "./walletCustodyRequestBoundary.js";
15
+ import { authorizeWalletOwnerOperation, completeWalletOwnerOperation } from "../walletOwnerOperationAuthorization.js";
16
+ import { extractBearerCredential, resolveSourceIpFromFetchHeaders } from "../../../auth/routerApiKeyAuth.js";
17
+ import { readJson } from "../../../framework/http.js";
18
+ import { findRouteDefinitionById, matchesRouteDefinitionRequest } from "../../../framework/routeDefinitions.js";
16
19
  import { parsePasskeyCustodyEnvelopeRecord } from "../../../../shared-ts/src/passkey-custody/custodyEnvelope.js";
17
20
  import "../../../../shared-ts/src/passkey-custody/index.js";
18
- import { readJson } from "../../../framework/http.js";
19
- import { parseWebAuthnAuthenticationCredential, unredactedWebAuthnExtensionField } from "../../../auth/webAuthnCredentialCodecs.js";
20
- import { parseCustodyOwnerProof, parseRecoveryBackupAcknowledgementRequest, parseRecoveryStatusRequest, requireExactObjectFields } from "./walletCustodyRequestBoundary.js";
21
- import { isHostWithinRpId, originHostnameOrEmpty } from "../../../../core/authService/webauthnOidcHelpers.js";
21
+ import { parseRecoveryCodeLocatorV1 } from "../../../../shared-ts/src/wallet-recovery/recoveryCodeLocator.js";
22
22
  import { parseRecoveryCodeReservationId } from "../../../../shared-ts/src/wallet-recovery/recoveryCodeReservation.js";
23
- import { computeWalletCustodyAdminChallengeDigest } from "../../../../shared-ts/src/authorization/walletCustodyOperation.js";
24
- import { parseWalletRecoveryTargetV1 } from "../../../../shared-ts/src/wallet-recovery/walletRecoveryTarget.js";
25
- import { emailOtpStatusCode, hashEmailOtpOperationBinding } from "../../../domains/emailOtp/emailOtpSessionRouteHelpers.js";
26
- import { EMAIL_OTP_CHANNEL, WALLET_EMAIL_OTP_UNLOCK_OPERATION } from "../../../../shared-ts/src/utils/emailOtpDomain.js";
27
23
  import { parseDerivedWalletRecoveryKeyId } from "../../../../shared-ts/src/wallet-recovery/recoveryKeyId.js";
28
- import { parseWalletRecoverySetRotationWireV1 } from "../../../../shared-ts/src/wallet-recovery/walletRecoveryEnvelopeSet.js";
29
- import { parseRecoveryCodeLocatorV1 } from "../../../../shared-ts/src/wallet-recovery/recoveryCodeLocator.js";
30
24
  import { parseWalletRecoveryEcdsaPossessionProofV1 } from "../../../../shared-ts/src/wallet-recovery/walletRecoveryEcdsaPossession.js";
25
+ import { parseWalletRecoverySetRotationWireV1 } from "../../../../shared-ts/src/wallet-recovery/walletRecoveryEnvelopeSet.js";
26
+ import { parseWalletRecoveryTargetV1 } from "../../../../shared-ts/src/wallet-recovery/walletRecoveryTarget.js";
27
+ import { isHostWithinRpId, originHostnameOrEmpty } from "../../../../core/authService/webauthnOidcHelpers.js";
31
28
  //#region src/router/transport/fetch/routes/passkeyCustody.ts
32
29
  /**
33
30
  * The transport for custody envelope retrieval.
@@ -53,239 +50,6 @@ const RECOVERY_READ_ROUTE_ID = "wallet_recovery_codes_read";
53
50
  const RECOVERY_STATUS_ROUTE_ID = "wallet_recovery_status";
54
51
  const EMAIL_OTP_CHALLENGE_ROUTE_ID = "wallet_custody_email_otp_challenge";
55
52
  const ENVELOPE_OWNERSHIP_UPGRADE_ROUTE_ID = "wallet_custody_envelope_ownership_upgrade";
56
- const CUSTODY_OPERATION_CAPABILITY_ID = "wallet-custody-admin";
57
- const CUSTODY_PROOF_TTL_MS = 6e4;
58
- function hasFullOwnerAuthority(authority) {
59
- return sameDelegatedWalletAuthorityV1(buildDelegatedWalletAuthorityV1({ permissions: authority.permissions }), buildDelegatedWalletAuthorityV1({ permissions: buildFullOwnerPermissionsV1() }));
60
- }
61
- function parseRequiredAuthorizationValue(parsed) {
62
- if (!parsed.ok) throw new Error(parsed.error.message);
63
- return parsed.value;
64
- }
65
- async function custodyDigest(domain, value) {
66
- return sha256Utf8DigestB64u(`${domain}|${alphabetizeStringify(value)}`);
67
- }
68
- async function buildWalletCustodyOperation(input) {
69
- const tenantId = parseRequiredAuthorizationValue(parseTenantId(input.tenantId));
70
- const principalId = parseRequiredAuthorizationValue(parsePrincipalId(input.principalId));
71
- const capabilityId = parseRequiredAuthorizationValue(parseCapabilityId(CUSTODY_OPERATION_CAPABILITY_ID));
72
- const operationRef = buildVaultOperationRef("vault.reveal");
73
- const laneDigest = await custodyDigest("seams:wallet-custody:lane:v1", {
74
- capabilityId,
75
- operation: input.operation,
76
- operationRef
77
- });
78
- const challengeDigest = await computeWalletCustodyAdminChallengeDigest({
79
- walletId: input.walletId,
80
- operation: input.operation,
81
- payload: input.payload,
82
- requestOrigin: input.requestOrigin
83
- });
84
- const intentDigest = challengeDigest;
85
- const displayDigest = await custodyDigest("seams:wallet-custody:display:v1", {
86
- walletId: input.walletId,
87
- operation: input.operation
88
- });
89
- return {
90
- operation: buildCapabilityOperationEnvelope({
91
- tenantId,
92
- principalId,
93
- capabilityId,
94
- operationId: parseRequiredAuthorizationValue(parseCapabilityOperationId(`wallet-custody:${input.operation}:${intentDigest}`)),
95
- operation: operationRef,
96
- digests: {
97
- laneDigest,
98
- intentDigest,
99
- displayDigest
100
- }
101
- }),
102
- challengeDigest
103
- };
104
- }
105
- async function authorizeWalletCustodyOperation(input) {
106
- const requestOrigin = normalizeOptionalTrimmedString(input.ctx.request.headers.get("origin"));
107
- if (!requestOrigin) return toCustodyAuthorizationFailure(400, "invalid_origin", "request Origin is required");
108
- let origin;
109
- let proof;
110
- let walletId;
111
- try {
112
- origin = parseSessionOrigin(requestOrigin);
113
- walletId = parseRequiredAuthorizationValue(parseWalletId(input.walletId));
114
- proof = parseCustodyOwnerProof(input.factorProof);
115
- } catch (error) {
116
- return toCustodyAuthorizationFailure(400, "invalid_owner_proof", error instanceof Error ? error.message : "owner proof is invalid");
117
- }
118
- if (proof.kind === "passkey" && proof.walletId !== input.walletId) return toCustodyAuthorizationFailure(403, "scope_mismatch", "owner proof wallet differs");
119
- let principalId;
120
- let factor;
121
- let operation;
122
- let challengeDigest;
123
- try {
124
- principalId = proof.kind === "passkey" ? input.walletId : proof.provider_subject_id;
125
- const built = await buildWalletCustodyOperation({
126
- tenantId: input.ctx.service.authorizedOperations.tenantId,
127
- principalId,
128
- walletId: input.walletId,
129
- operation: input.operation,
130
- payload: input.payload,
131
- requestOrigin
132
- });
133
- operation = built.operation;
134
- challengeDigest = built.challengeDigest;
135
- } catch (error) {
136
- return toCustodyAuthorizationFailure(400, "invalid_owner_proof", error instanceof Error ? error.message : "owner proof binding is invalid");
137
- }
138
- const operationFingerprintDigest = await computeCapabilityOperationFingerprintDigest(operation);
139
- const nowMs = Date.now();
140
- if (proof.challenge_digest !== String(challengeDigest)) return toCustodyAuthorizationFailure(403, "scope_mismatch", "factor proof challenge does not match the requested operation");
141
- if (proof.kind === "passkey") {
142
- let authority;
143
- try {
144
- authority = buildPasskeyWalletAuthAuthority({
145
- walletId,
146
- rpId: proof.rpId,
147
- credentialIdB64u: proof.credentialIdB64u
148
- });
149
- } catch (error) {
150
- return toCustodyAuthorizationFailure(400, "invalid_owner_proof", error instanceof Error ? error.message : "passkey authority is invalid");
151
- }
152
- const active = await input.ctx.service.walletAuthMethods.resolveActivePasskeyAuthorityForVerifiedCredential({
153
- walletId,
154
- rpId: authority.verifier.rpId,
155
- credentialIdB64u: authority.factor.credentialIdB64u
156
- });
157
- if (!active.ok) return toCustodyAuthorizationFailure(403, active.code, active.message);
158
- if (!hasFullOwnerAuthority(active.walletAuthority)) return toCustodyAuthorizationFailure(403, "forbidden", "Full owner authority is required");
159
- const credential = parseWebAuthnAuthenticationCredential(proof.webauthn_authentication);
160
- if (!credential) return toCustodyAuthorizationFailure(400, "invalid_owner_proof", "passkey assertion is invalid");
161
- if (String(credential.rawId || credential.id).trim() !== authority.factor.credentialIdB64u) return toCustodyAuthorizationFailure(401, "unauthorized", "passkey credential changed");
162
- const verified = await input.ctx.service.webAuthn.verifyWebAuthnAuthenticationLite({
163
- userId: input.walletId,
164
- rpId: authority.verifier.rpId,
165
- expectedChallenge: challengeDigest,
166
- expected_origin: requestOrigin,
167
- webauthn_authentication: credential
168
- });
169
- if (!verified.success || !verified.verified) return toCustodyAuthorizationFailure(401, verified.code || "not_verified", verified.message || "WebAuthn authentication verification failed");
170
- factor = buildVerifiedWalletOperationPasskeyFactorResult({
171
- tenantId: operation.tenantId,
172
- principalId: operation.principalId,
173
- walletId,
174
- authorityRef: await walletAuthAuthorityRef({ authority: active.authority }),
175
- requestOrigin: origin,
176
- audience: origin,
177
- factorId: parseRequiredAuthorizationValue(parseAuthFactorId(`passkey:${authority.factor.credentialIdB64u}`)),
178
- credentialIdB64u: parseRequiredAuthorizationValue(parseWebAuthnCredentialIdB64u(authority.factor.credentialIdB64u)),
179
- assertionDigest: await custodyDigest("seams:wallet-custody:assertion:v1", { credential }),
180
- operation,
181
- verifiedAtMs: nowMs,
182
- expiresAtMs: nowMs + CUSTODY_PROOF_TTL_MS
183
- });
184
- } else {
185
- const providerSubject = parseRequiredAuthorizationValue(parseProviderSubject(proof.provider_subject_id));
186
- const orgId = parseRequiredAuthorizationValue(parseOrgId(input.ctx.service.authorizedOperations.tenantId));
187
- const active = await input.ctx.service.walletAuthMethods.resolveActiveEmailOtpAuthorityForVerifiedSubject({
188
- walletId: input.walletId,
189
- providerUserId: proof.provider_subject_id
190
- });
191
- if (!active.ok) return toCustodyAuthorizationFailure(403, active.code, active.message);
192
- const activeAuthorityRef = await walletAuthAuthorityRef({ authority: active.authority });
193
- const ownerProofBindingDigest = await hashEmailOtpOperationBinding({
194
- walletId: input.walletId,
195
- providerUserId: proof.provider_subject_id,
196
- orgId,
197
- operation: `wallet_custody:${input.operation}`,
198
- requestOrigin,
199
- audience: requestOrigin,
200
- authorityRef: activeAuthorityRef,
201
- operationFingerprintDigest: challengeDigest
202
- });
203
- const verified = await input.ctx.service.emailOtp.verifyEmailOtpChallenge({
204
- userId: proof.provider_subject_id,
205
- walletId: input.walletId,
206
- orgId,
207
- challengeId: proof.challenge_id,
208
- otpCode: proof.otp_code,
209
- otpChannel: EMAIL_OTP_CHANNEL,
210
- ownerProofBindingDigest,
211
- operation: WALLET_EMAIL_OTP_UNLOCK_OPERATION
212
- });
213
- if (!verified.ok) return toCustodyAuthorizationFailure(verified.code === "invalid_body" ? 400 : 401, verified.code, verified.message);
214
- const consumed = await input.ctx.service.emailOtp.consumeEmailOtpGrant({
215
- subject: {
216
- kind: "provider_identity",
217
- orgId,
218
- providerSubject,
219
- walletId
220
- },
221
- loginGrant: verified.loginGrant,
222
- otpChannel: EMAIL_OTP_CHANNEL
223
- });
224
- if (!consumed.ok) return toCustodyAuthorizationFailure(emailOtpStatusCode(consumed.code), consumed.code, consumed.message);
225
- factor = buildVerifiedWalletOperationEmailOtpFactorResult({
226
- tenantId: operation.tenantId,
227
- principalId: operation.principalId,
228
- walletId,
229
- authorityRef: activeAuthorityRef,
230
- requestOrigin: origin,
231
- audience: origin,
232
- factorId: parseRequiredAuthorizationValue(parseAuthFactorId(`email_otp:${active.authority.factor.provider}:${proof.provider_subject_id}`)),
233
- operation,
234
- challengeId: parseRequiredAuthorizationValue(parseEmailOtpChallengeId(consumed.challengeId)),
235
- verificationReceiptDigest: await custodyDigest("seams:wallet-custody:otp-receipt:v1", {
236
- challengeId: consumed.challengeId,
237
- operationFingerprintDigest
238
- }),
239
- verifiedAtMs: nowMs,
240
- expiresAtMs: Math.min(nowMs + CUSTODY_PROOF_TTL_MS, verified.grantExpiresAtMs)
241
- });
242
- }
243
- const ownerProof = await input.ctx.service.authorizedOperations.buildVerifiedOwnerProof({
244
- purpose: "operation",
245
- proofId: parseVerifiedOwnerProofId(`wallet-custody:${operation.operationId}`),
246
- factor
247
- });
248
- if (ownerProof.purpose !== "operation" || ownerProof.operation.operationFingerprintDigest !== operationFingerprintDigest) return toCustodyAuthorizationFailure(403, "owner_proof_rejected", "owner proof binding is invalid");
249
- const evidenceSet = await input.ctx.service.authorizedOperations.recordVerifiedWalletOperationFactorEvidenceSet({
250
- operation,
251
- evidenceId: parseRequiredAuthorizationValue(parseAuthorizationEvidenceId(`wallet-custody:evidence:${operation.operationId}`)),
252
- evidenceSetId: parseRequiredAuthorizationValue(parseAuthorizationEvidenceSetId(`wallet-custody:evidence-set:${operation.operationId}`)),
253
- factor
254
- });
255
- const admission = await input.ctx.service.authorizedOperations.admitAuthorizedOperation({ operation: {
256
- tenantId: operation.tenantId,
257
- authorizedOperationId: parseRequiredAuthorizationValue(parseAuthorizedOperationId(`wallet-custody:authorized:${operation.operationId}`)),
258
- auditEventId: parseRequiredAuthorizationValue(parseAuthorizationAuditEventId(`wallet-custody:audit:${operation.operationId}`)),
259
- operation,
260
- authorization: {
261
- kind: "verified_step_up",
262
- evidenceSetDigest: evidenceSet.evidenceSetDigest
263
- },
264
- quota: { kind: "quota_neutral" },
265
- claimedAtMs: nowMs
266
- } });
267
- switch (admission.kind) {
268
- case "claimed": return {
269
- ok: true,
270
- operation: admission.operation
271
- };
272
- case "replayed":
273
- case "operation_in_progress": return {
274
- ok: false,
275
- response: authorizedOperationReplay(admission.operation)
276
- };
277
- case "authorization_grant_rejected":
278
- case "verified_step_up_rejected":
279
- case "wallet_session_quota_exhausted":
280
- case "material_mismatch": return toCustodyAuthorizationFailure(403, "owner_proof_rejected", "owner proof was rejected");
281
- }
282
- }
283
- function toCustodyAuthorizationFailure(status, code, message) {
284
- return {
285
- ok: false,
286
- response: routeErrorResponse(status, code, message)
287
- };
288
- }
289
53
  function routeErrorResponse(status, code, message) {
290
54
  return toFetchRouteResponse({
291
55
  status,
@@ -301,23 +65,6 @@ function matchRoute(ctx, routeId) {
301
65
  if (!route) throw new Error(`Missing route definition for ${routeId}`);
302
66
  return matchesRouteDefinitionRequest(route, ctx.method, ctx.pathname) ? route : null;
303
67
  }
304
- async function completeWalletCustodyOperation(ctx, operation, status, body, result = "succeeded") {
305
- const bodyText = JSON.stringify(body);
306
- await ctx.service.authorizedOperations.completeAuthorizedOperation({
307
- operation,
308
- result,
309
- response: {
310
- status,
311
- contentType: "application/json",
312
- bodyText
313
- },
314
- completedAtMs: Date.now()
315
- });
316
- return toFetchRouteResponse({
317
- status,
318
- body
319
- });
320
- }
321
68
  async function handlePasskeyCustody(ctx) {
322
69
  if (!matchRoute(ctx, ROUTE_ID)) return null;
323
70
  const body = await readJsonObject(ctx.request);
@@ -345,8 +92,8 @@ function parseWalletCustodyEmailOtpChallengeRequest(value, actualOrigin) {
345
92
  ];
346
93
  const withoutOptional = keys.filter((key) => key !== "requestOrigin");
347
94
  if (withoutOptional.length !== required.length || withoutOptional.some((key, index) => key !== required.slice().sort()[index]) || keys.includes("requestOrigin") && keys.length !== expected.length) throw new Error("Email OTP custody challenge body contains unsupported fields");
348
- const walletId = parseRequiredAuthorizationValue(parseWalletId(value.walletId));
349
- const providerSubjectId = parseRequiredAuthorizationValue(parseProviderSubject(value.providerSubjectId));
95
+ const walletId = requireParsed(parseWalletId(value.walletId), "authorization");
96
+ const providerSubjectId = requireParsed(parseProviderSubject(value.providerSubjectId), "authorization");
350
97
  const operation = value.operation;
351
98
  if (operation !== "credentials_list" && operation !== "credential_label" && operation !== "recovery_rotate" && operation !== "recovery_read" && operation !== "recovery_status" && operation !== "recovery_backup_acknowledge") throw new Error("Email OTP custody challenge operation is invalid");
352
99
  if (!isPlainObject(value.payload)) throw new Error("Email OTP custody challenge payload is invalid");
@@ -375,8 +122,8 @@ async function handleWalletCustodyEmailOtpChallenge(ctx) {
375
122
  providerUserId: request.providerSubjectId
376
123
  });
377
124
  if (!active.ok) return routeErrorResponse(403, active.code, active.message);
378
- const authoritativeOrgId = parseRequiredAuthorizationValue(parseOrgId(ctx.service.authorizedOperations.tenantId));
379
- const challengeDigest = await computeWalletCustodyAdminChallengeDigest(request);
125
+ const authoritativeOrgId = requireParsed(parseOrgId(ctx.service.authorizedOperations.tenantId), "authorization");
126
+ const challengeDigest = await computeWalletOwnerAdminChallengeDigest(request);
380
127
  const ownerProofBindingDigest = await hashEmailOtpOperationBinding({
381
128
  walletId: request.walletId,
382
129
  providerUserId: request.providerSubjectId,
@@ -424,7 +171,7 @@ async function handleWalletCustodyCredentialsList(ctx) {
424
171
  const walletId = walletIdFromPath(route.path, ctx.pathname);
425
172
  if (!walletId) return routeErrorResponse(400, "invalid_request", "credential list needs a wallet");
426
173
  const body = await readJsonObject(ctx.request);
427
- const authorized = await authorizeWalletCustodyOperation({
174
+ const authorized = await authorizeWalletOwnerOperation({
428
175
  ctx,
429
176
  walletId,
430
177
  operation: "credentials_list",
@@ -435,7 +182,7 @@ async function handleWalletCustodyCredentialsList(ctx) {
435
182
  const parsedWalletId = parseWalletId(walletId);
436
183
  if (!parsedWalletId.ok) return routeErrorResponse(400, "invalid_request", "wallet id is invalid");
437
184
  const result = await ctx.service.passkeyCustody.listWalletCredentials({ walletId: parsedWalletId.value });
438
- return await completeWalletCustodyOperation(ctx, authorized.operation, 200, {
185
+ return await completeWalletOwnerOperation(ctx, authorized.operation, 200, {
439
186
  ok: true,
440
187
  credentials: result
441
188
  });
@@ -503,7 +250,7 @@ async function handleWalletCustodyCredentialLabel(ctx) {
503
250
  const envelopeId = normalizeOptionalTrimmedString(body?.envelopeId);
504
251
  const label = body?.label;
505
252
  if (!walletId || !envelopeId || label !== void 0 && typeof label !== "string") return routeErrorResponse(400, "invalid_request", "credential rename needs a wallet, envelope, and optional label");
506
- const authorized = await authorizeWalletCustodyOperation({
253
+ const authorized = await authorizeWalletOwnerOperation({
507
254
  ctx,
508
255
  walletId,
509
256
  operation: "credential_label",
@@ -523,26 +270,26 @@ async function handleWalletCustodyCredentialLabel(ctx) {
523
270
  ...label === void 0 ? {} : { label }
524
271
  });
525
272
  switch (result.kind) {
526
- case "updated": return await completeWalletCustodyOperation(ctx, authorized.operation, 200, {
273
+ case "updated": return await completeWalletOwnerOperation(ctx, authorized.operation, 200, {
527
274
  ok: true,
528
275
  credential: result.projection
529
276
  });
530
- case "missing": return await completeWalletCustodyOperation(ctx, authorized.operation, 404, {
277
+ case "missing": return await completeWalletOwnerOperation(ctx, authorized.operation, 404, {
531
278
  ok: false,
532
279
  code: "credential_not_found",
533
280
  message: "credential not found"
534
281
  }, "failed_before_side_effect");
535
- case "conflict": return await completeWalletCustodyOperation(ctx, authorized.operation, 409, {
282
+ case "conflict": return await completeWalletOwnerOperation(ctx, authorized.operation, 409, {
536
283
  ok: false,
537
284
  code: "credential_activity_conflict",
538
285
  message: "credential changed; retry"
539
286
  }, "failed_before_side_effect");
540
- case "invalid_envelope_id": return await completeWalletCustodyOperation(ctx, authorized.operation, 400, {
287
+ case "invalid_envelope_id": return await completeWalletOwnerOperation(ctx, authorized.operation, 400, {
541
288
  ok: false,
542
289
  code: "invalid_envelope_id",
543
290
  message: "credential envelope id is invalid"
544
291
  }, "failed_before_side_effect");
545
- case "invalid_label": return await completeWalletCustodyOperation(ctx, authorized.operation, 400, {
292
+ case "invalid_label": return await completeWalletOwnerOperation(ctx, authorized.operation, 400, {
546
293
  ok: false,
547
294
  code: "invalid_label",
548
295
  message: result.reason
@@ -768,7 +515,7 @@ function parseWalletRecoveryOperationRequest(body, fields, label) {
768
515
  if (!body) throw new Error(`${label} body is required`);
769
516
  requireExactObjectFields(body, fields, label);
770
517
  return {
771
- recoveryOperationId: parseRequiredAuthorizationValue(parseWalletRecoveryOperationId(body.recoveryOperationId)),
518
+ recoveryOperationId: requireParsed(parseWalletRecoveryOperationId(body.recoveryOperationId), "authorization"),
772
519
  reservationId: parseRecoveryCodeReservationId(body.reservationId)
773
520
  };
774
521
  }
@@ -790,7 +537,7 @@ function parseWalletRecoveryEmailOtpVerifyRequest(body) {
790
537
  "challengeId",
791
538
  "otpCode"
792
539
  ], "wallet recovery Email OTP verification"),
793
- challengeId: parseRequiredAuthorizationValue(parseEmailOtpChallengeId(body?.challengeId)),
540
+ challengeId: requireParsed(parseEmailOtpChallengeId(body?.challengeId), "authorization"),
794
541
  otpCode: requireTrimmedString(body?.otpCode, "otpCode")
795
542
  };
796
543
  }
@@ -1093,13 +840,6 @@ function parseEcdsaMaterialPossessionProofs(value) {
1093
840
  };
1094
841
  });
1095
842
  }
1096
- function authorizedOperationReplay(operation) {
1097
- if (operation.lifecycle !== "completed") return routeErrorResponse(409, "recovery_in_progress", "wallet recovery is still in progress");
1098
- return new Response(operation.response.bodyText, {
1099
- status: operation.response.status,
1100
- headers: { "content-type": operation.response.contentType }
1101
- });
1102
- }
1103
843
  async function handleWalletRecoveryBackupAcknowledge(ctx) {
1104
844
  if (!matchRoute(ctx, RECOVERY_ACK_ROUTE_ID)) return null;
1105
845
  let request;
@@ -1108,7 +848,7 @@ async function handleWalletRecoveryBackupAcknowledge(ctx) {
1108
848
  } catch {
1109
849
  return routeErrorResponse(400, "invalid_request", "acknowledgement body is invalid");
1110
850
  }
1111
- const authorized = await authorizeWalletCustodyOperation({
851
+ const authorized = await authorizeWalletOwnerOperation({
1112
852
  ctx,
1113
853
  walletId: request.walletId,
1114
854
  operation: "recovery_backup_acknowledge",
@@ -1124,17 +864,17 @@ async function handleWalletRecoveryBackupAcknowledge(ctx) {
1124
864
  walletId: request.walletId,
1125
865
  expectedIssuedAtMs: request.expectedIssuedAtMs
1126
866
  });
1127
- if (result.kind === "conflict") return await completeWalletCustodyOperation(ctx, authorized.operation, 409, {
867
+ if (result.kind === "conflict") return await completeWalletOwnerOperation(ctx, authorized.operation, 409, {
1128
868
  ok: false,
1129
869
  code: "recovery_set_conflict",
1130
870
  message: "the recovery set changed"
1131
871
  }, "failed_before_side_effect");
1132
- if (result.kind === "no_recovery_set") return await completeWalletCustodyOperation(ctx, authorized.operation, 404, {
872
+ if (result.kind === "no_recovery_set") return await completeWalletOwnerOperation(ctx, authorized.operation, 404, {
1133
873
  ok: false,
1134
874
  code: "no_recovery_set",
1135
875
  message: "this wallet has no issued recovery codes to acknowledge"
1136
876
  }, "failed_before_side_effect");
1137
- return await completeWalletCustodyOperation(ctx, authorized.operation, 200, {
877
+ return await completeWalletOwnerOperation(ctx, authorized.operation, 200, {
1138
878
  ok: true,
1139
879
  issuedAtMs: result.issuedAtMs
1140
880
  });
@@ -1172,7 +912,7 @@ async function handleWalletRecoveryRotate(ctx) {
1172
912
  } catch (error) {
1173
913
  return routeErrorResponse(400, "invalid_request", error instanceof Error ? error.message : "replacement recovery set is invalid");
1174
914
  }
1175
- const authorized = await authorizeWalletCustodyOperation({
915
+ const authorized = await authorizeWalletOwnerOperation({
1176
916
  ctx,
1177
917
  walletId,
1178
918
  operation: "recovery_rotate",
@@ -1193,22 +933,22 @@ async function handleWalletRecoveryRotate(ctx) {
1193
933
  expectedStoreVersion
1194
934
  });
1195
935
  switch (result.kind) {
1196
- case "rotated": return await completeWalletCustodyOperation(ctx, authorized.operation, 200, {
936
+ case "rotated": return await completeWalletOwnerOperation(ctx, authorized.operation, 200, {
1197
937
  ok: true,
1198
938
  issuedAtMs: result.issuedAtMs,
1199
939
  storeVersion: result.storeVersion
1200
940
  });
1201
- case "no_recovery_set": return await completeWalletCustodyOperation(ctx, authorized.operation, 404, {
941
+ case "no_recovery_set": return await completeWalletOwnerOperation(ctx, authorized.operation, 404, {
1202
942
  ok: false,
1203
943
  code: "no_recovery_set",
1204
944
  message: "this wallet has no codes to rotate"
1205
945
  }, "failed_before_side_effect");
1206
- case "conflict": return await completeWalletCustodyOperation(ctx, authorized.operation, 409, {
946
+ case "conflict": return await completeWalletOwnerOperation(ctx, authorized.operation, 409, {
1207
947
  ok: false,
1208
948
  code: "recovery_set_conflict",
1209
949
  message: "the recovery set changed during this rotation; try again"
1210
950
  }, "failed_before_side_effect");
1211
- case "rejected": return await completeWalletCustodyOperation(ctx, authorized.operation, 400, {
951
+ case "rejected": return await completeWalletOwnerOperation(ctx, authorized.operation, 400, {
1212
952
  ok: false,
1213
953
  code: "rotation_rejected",
1214
954
  message: result.reason
@@ -1220,7 +960,7 @@ async function handleWalletRecoveryRead(ctx) {
1220
960
  const body = await readJsonObject(ctx.request);
1221
961
  const walletId = normalizeOptionalTrimmedString(body?.walletId);
1222
962
  if (!walletId) return routeErrorResponse(400, "invalid_request", "a read needs a wallet");
1223
- const authorized = await authorizeWalletCustodyOperation({
963
+ const authorized = await authorizeWalletOwnerOperation({
1224
964
  ctx,
1225
965
  walletId,
1226
966
  operation: "recovery_read",
@@ -1229,12 +969,12 @@ async function handleWalletRecoveryRead(ctx) {
1229
969
  });
1230
970
  if (!authorized.ok) return authorized.response;
1231
971
  const result = await ctx.service.passkeyCustody.readRecoverySet({ walletId });
1232
- if (result.kind === "no_recovery_set") return await completeWalletCustodyOperation(ctx, authorized.operation, 404, {
972
+ if (result.kind === "no_recovery_set") return await completeWalletOwnerOperation(ctx, authorized.operation, 404, {
1233
973
  ok: false,
1234
974
  code: "no_recovery_set",
1235
975
  message: "this wallet has no issued recovery codes"
1236
976
  }, "failed_before_side_effect");
1237
- return await completeWalletCustodyOperation(ctx, authorized.operation, 200, {
977
+ return await completeWalletOwnerOperation(ctx, authorized.operation, 200, {
1238
978
  ok: true,
1239
979
  recoverySet: result.record,
1240
980
  storeVersion: result.storeVersion
@@ -1252,7 +992,7 @@ async function handleWalletRecoveryStatus(ctx) {
1252
992
  } catch {
1253
993
  return routeErrorResponse(400, "invalid_request", "status body is invalid");
1254
994
  }
1255
- const authorized = await authorizeWalletCustodyOperation({
995
+ const authorized = await authorizeWalletOwnerOperation({
1256
996
  ctx,
1257
997
  walletId,
1258
998
  operation: "recovery_status",
@@ -1264,12 +1004,12 @@ async function handleWalletRecoveryStatus(ctx) {
1264
1004
  });
1265
1005
  if (!authorized.ok) return authorized.response;
1266
1006
  const result = await ctx.service.passkeyCustody.readRecoveryStatus({ walletId });
1267
- if (result.kind === "no_recovery_set") return await completeWalletCustodyOperation(ctx, authorized.operation, 404, {
1007
+ if (result.kind === "no_recovery_set") return await completeWalletOwnerOperation(ctx, authorized.operation, 404, {
1268
1008
  ok: false,
1269
1009
  code: "no_recovery_set",
1270
1010
  message: "this wallet has no recovery codes"
1271
1011
  }, "failed_before_side_effect");
1272
- return await completeWalletCustodyOperation(ctx, authorized.operation, 200, {
1012
+ return await completeWalletOwnerOperation(ctx, authorized.operation, 200, {
1273
1013
  ok: true,
1274
1014
  activeCodeCount: result.activeCodeCount,
1275
1015
  totalCodeCount: result.totalCodeCount,
@@ -1,20 +1,20 @@
1
1
  import { parseOrgId, parseProviderSubject, parseWalletAuthMethodId, parseWalletId, parseWebAuthnCredentialIdB64u, parseWebAuthnRpId } from "../../../../shared-ts/src/utils/domainIds.js";
2
- import { parseMpcWalletSigningQuotaId, parseWalletSessionId } from "../../../../shared-ts/src/authorization/capabilityKinds.js";
3
2
  import { isPlainObject } from "../../../../shared-ts/src/utils/validation.js";
4
3
  import { sha256HexUtf8 } from "../../../../shared-ts/src/utils/digests.js";
5
4
  import { parseDigestB64u } from "../../../../shared-ts/src/utils/canonicalPrimitives.js";
6
5
  import { base58Encode } from "../../../../shared-ts/src/utils/base58.js";
6
+ import { parseMpcWalletSigningQuotaId, parseWalletSessionId } from "../../../../shared-ts/src/authorization/capabilityKinds.js";
7
7
  import { routerAbMpcMaterialActivationRefToWire, sameRouterAbMpcMaterialActivationRef } from "../../../../shared-ts/src/utils/routerAbNormalSigningIdentity.js";
8
8
  import { normalizeRuntimePolicyScope } from "../../../../shared-ts/src/threshold/signingRootScope.js";
9
9
  import { walletAuthAuthorityRef } from "../../../../shared-ts/src/utils/walletAuthAuthority.js";
10
10
  import { parseRouterAbEcdsaCredentialFreeSessionActivationResponseV1, parseRouterAbEcdsaPostRegistrationSessionActivationPolicyV1, parseRouterAbEcdsaPostRegistrationSessionActivationResponseV1, sameRouterAbEcdsaDerivationPublicCapabilityV1, sameRouterAbEcdsaRegistrationActivationReceiptV1 } from "../../../../shared-ts/src/utils/routerAbEcdsaDerivation.js";
11
11
  import { parseHostedWalletSeamsSessionExchangeCode, parseHostedWalletSeamsSessionExchangeNonce, parsePrimaryWalletSessionOperationCredentialToken, parseSessionOrigin, projectExactWalletSessionAuthorizationV1 } from "../../../../authorization/domain.js";
12
- import { walletIdFromString } from "../../../../shared-ts/src/utils/registrationIds.js";
12
+ import { EMAIL_OTP_CHANNEL, WALLET_EMAIL_OTP_EXPORT_OPERATION } from "../../../../shared-ts/src/utils/emailOtpDomain.js";
13
+ import { emailOtpStatusCode, hashEmailOtpOperationBinding } from "../../../domains/emailOtp/emailOtpSessionRouteHelpers.js";
13
14
  import { extractBearerCredential, resolveSourceIpFromFetchHeaders } from "../../../auth/routerApiKeyAuth.js";
14
15
  import { json, readJson } from "../../../framework/http.js";
16
+ import { walletIdFromString } from "../../../../shared-ts/src/utils/registrationIds.js";
15
17
  import { routerApiEmailOtpRouteService } from "../../../framework/authServicePort.js";
16
- import { emailOtpStatusCode, hashEmailOtpOperationBinding } from "../../../domains/emailOtp/emailOtpSessionRouteHelpers.js";
17
- import { EMAIL_OTP_CHANNEL, WALLET_EMAIL_OTP_EXPORT_OPERATION } from "../../../../shared-ts/src/utils/emailOtpDomain.js";
18
18
  import { emitRouterApiWebhookEvent } from "../../../framework/routerApiWebhooks.js";
19
19
  import { parseWalletEmailOtpLoginOperation } from "../../../domains/emailOtp/emailOtpRequestValidation.js";
20
20
  import { handleWalletUnlockChallengeRoute, handleWalletUnlockVerifyRoute } from "../../../domains/walletUnlock/walletUnlockRouteHandlers.js";
@@ -1,12 +1,12 @@
1
1
  import { parseWalletId, parseWebAuthnCredentialIdB64u, parseWebAuthnRpId } from "../../../../shared-ts/src/utils/domainIds.js";
2
- import { parseAuthFactorId, parsePrincipalId, parseWalletSessionMintId } from "../../../../shared-ts/src/authorization/capabilityKinds.js";
3
2
  import { alphabetizeStringify } from "../../../../shared-ts/src/utils/digests.js";
4
3
  import { sha256Utf8DigestB64u } from "../../../../shared-ts/src/utils/canonicalPrimitives.js";
4
+ import { parseAuthFactorId, parsePrincipalId, parseWalletSessionMintId } from "../../../../shared-ts/src/authorization/capabilityKinds.js";
5
5
  import { walletAuthAuthorityRef } from "../../../../shared-ts/src/utils/walletAuthAuthority.js";
6
6
  import { parseSessionOrigin, parseVerifiedOwnerProofId } from "../../../../authorization/domain.js";
7
7
  import { buildVerifiedWalletSessionPasskeyFactorResult } from "../../../../authorization/factorEvidence.js";
8
- import { walletIdFromString } from "../../../../shared-ts/src/utils/registrationIds.js";
9
8
  import { json, jsonFailure, readJson } from "../../../framework/http.js";
9
+ import { walletIdFromString } from "../../../../shared-ts/src/utils/registrationIds.js";
10
10
  import { parseSyncAccountOptionsRequest, parseSyncAccountVerifyRequest } from "../../../domains/syncAccount/syncAccountRequestValidation.js";
11
11
  import { DEFAULT_WALLET_SESSION_TTL_MS } from "../../../../shared-ts/src/threshold/sessionPolicy.js";
12
12
  import { issueSyncAccountBootstrapV1 } from "./syncAccountBootstrap.js";
@@ -1,6 +1,6 @@
1
1
  import { mpcMaterialActivationRefsEqual, parseWebAuthnCredentialIdB64u, parseWebAuthnRpId } from "../../../../shared-ts/src/utils/domainIds.js";
2
- import { parsePrincipalId } from "../../../../shared-ts/src/authorization/capabilityKinds.js";
3
2
  import { isPlainObject } from "../../../../shared-ts/src/utils/validation.js";
3
+ import { parsePrincipalId } from "../../../../shared-ts/src/authorization/capabilityKinds.js";
4
4
  import { routerAbMpcMaterialActivationRefFromWire } from "../../../../shared-ts/src/utils/routerAbNormalSigningIdentity.js";
5
5
  import { parseRouterAbEcdsaCredentialFreeSessionActivationResponseV1, parseRouterAbEcdsaPostRegistrationSessionActivationPolicyV1 } from "../../../../shared-ts/src/utils/routerAbEcdsaDerivation.js";
6
6
  import { projectActiveWalletSession } from "../../../../authorization/domain.js";
@@ -1,20 +1,20 @@
1
1
  import { parseWalletId } from "../../../../shared-ts/src/utils/domainIds.js";
2
- import { EVM_ECDSA_MPC_OPERATION_KINDS, buildEvmEcdsaMpcOperationRef, parseAuthorizationAuditEventId, parseAuthorizationEvidenceId, parseAuthorizationEvidenceSetId, parseAuthorizedOperationId, parseCapabilityId, parseCapabilityOperationId, parseEcdsaAuthorizationSessionId, parseWalletSessionMintId } from "../../../../shared-ts/src/authorization/capabilityKinds.js";
3
2
  import { base64UrlEncode } from "../../../../shared-ts/src/utils/base64.js";
4
3
  import { parseDigestB64u } from "../../../../shared-ts/src/utils/canonicalPrimitives.js";
5
- import { buildCapabilityOperationEnvelope } from "../../../../shared-ts/src/authorization/operationFingerprint.js";
6
4
  import "../../../../shared-ts/src/utils/encoders.js";
5
+ import { EVM_ECDSA_MPC_OPERATION_KINDS, buildEvmEcdsaMpcOperationRef, parseAuthorizationAuditEventId, parseAuthorizationEvidenceId, parseAuthorizationEvidenceSetId, parseAuthorizedOperationId, parseCapabilityId, parseCapabilityOperationId, parseEcdsaAuthorizationSessionId, parseWalletSessionMintId } from "../../../../shared-ts/src/authorization/capabilityKinds.js";
6
+ import { buildCapabilityOperationEnvelope } from "../../../../shared-ts/src/authorization/operationFingerprint.js";
7
7
  import { routerAbMpcMaterialActivationRefToWire, sameRouterAbMpcMaterialActivationRef } from "../../../../shared-ts/src/utils/routerAbNormalSigningIdentity.js";
8
8
  import { walletAuthAuthorityRef } from "../../../../shared-ts/src/utils/walletAuthAuthority.js";
9
9
  import { ROUTER_AB_ECDSA_DERIVATION_EXPORT_PATH, ROUTER_AB_ECDSA_DERIVATION_NORMAL_SIGNING_STATE_KIND_V1, ROUTER_AB_ECDSA_DERIVATION_REFRESH_PATH, computeRouterAbEcdsaOperationStepUpChallengeB64u, parseRouterAbEcdsaDerivationActivationRefreshCommitRequestV1, parseRouterAbEcdsaDerivationEvmDigestSigningFinalizeRequestV1, parseRouterAbEcdsaDerivationEvmDigestSigningRequestV1, parseRouterAbEcdsaDerivationExplicitExportRequestV1, parseRouterAbEcdsaOperationStepUpAuthorizationRequestV1, parseRouterAbEcdsaPostRegistrationSessionActivationRequestV1, parseRouterAbEcdsaPrepareSourceV1, sameRouterAbEcdsaDerivationNormalSigningScopeV1, sameRouterAbEcdsaDerivationPublicCapabilityV1 } from "../../../../shared-ts/src/utils/routerAbEcdsaDerivation.js";
10
10
  import { buildWalletSessionCapabilitySubjectsV1, projectActiveWalletSession, walletSessionAuthorizationV2RecordsEqual } from "../../../../authorization/domain.js";
11
+ import { extractBearerCredential } from "../../../auth/routerApiKeyAuth.js";
12
+ import { json, readJson } from "../../../framework/http.js";
11
13
  import { walletIdFromString } from "../../../../shared-ts/src/utils/registrationIds.js";
12
14
  import { WALLET_SESSION_FAILURE_CODES } from "../../../../shared-ts/src/utils/walletSessionFailure.js";
13
- import { extractBearerCredential } from "../../../auth/routerApiKeyAuth.js";
14
15
  import { walletSessionFailure, walletSessionFailureStatus } from "../../../auth/walletSessionFailure.js";
15
16
  import { resolveWalletSessionAuthorizationV2Admission } from "../../../domains/signingOperations/walletExecutionAdmission.js";
16
17
  import { resolveWalletSessionOperationCredentialAdmission, validateEcdsaPreprocessingSession } from "../../../auth/commonRouterUtils.js";
17
- import { json, readJson } from "../../../framework/http.js";
18
18
  import { thresholdEcdsaStatusCode } from "../../../../threshold/statusCodes.js";
19
19
  import { signingPrepareSessionResponse } from "../../../domains/signingOperations/signingPrepareSession.js";
20
20
  import { authenticateRouterAbWalletOperationStepUpIdentity } from "../../../domains/signingOperations/routerAbOperationStepUp.js";