@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
@@ -0,0 +1,351 @@
1
+ import { parseWalletId } from "../../../../shared-ts/src/utils/domainIds.js";
2
+ import { requireCanonicalString, requireRecord } from "../../../../shared-ts/src/utils/validation.js";
3
+ import { sha256HexUtf8 } from "../../../../shared-ts/src/utils/digests.js";
4
+ import { EXTERNAL_AUTH_PATH, externalAuthFailure, parseExternalAuthConfiguration } from "../../../../shared-ts/src/auth/externalAuth.js";
5
+ import { externalOwnerChallenge, readExternalOwnerOperation } from "../../../../auth/externalOwnerOperations.js";
6
+ import { authorizeWalletOwnerOperation, completeWalletOwnerOperation, walletOwnerOperationAuthorizationId } from "../walletOwnerOperationAuthorization.js";
7
+ import { extractBearerCredential, resolveSourceIpFromFetchHeaders } from "../../../auth/routerApiKeyAuth.js";
8
+ import { json, readJson } from "../../../framework/http.js";
9
+ //#region src/router/transport/fetch/routes/externalAuth.ts
10
+ const ACTIONS = [
11
+ "associate",
12
+ "unlink",
13
+ "prepare",
14
+ "prepare-association",
15
+ "prepare-unlink",
16
+ "owner-challenge",
17
+ "exchange",
18
+ "backend-exchange",
19
+ "redeem",
20
+ "settings",
21
+ "logout"
22
+ ];
23
+ function parseIdentityProof(value) {
24
+ const proof = requireRecord(value, "external identity proof");
25
+ switch (proof.kind) {
26
+ case "google": return {
27
+ kind: "google",
28
+ idToken: requireCanonicalString(proof.idToken, "idToken")
29
+ };
30
+ case "application": return {
31
+ kind: "application",
32
+ assertion: requireCanonicalString(proof.assertion, "assertion")
33
+ };
34
+ default: throw new Error("Unsupported external identity proof");
35
+ }
36
+ }
37
+ function parseCommand(action, body) {
38
+ const wallet = parseWalletId(body.walletId);
39
+ if (!wallet.ok) throw new Error("Wallet ID is invalid");
40
+ const walletId = wallet.value;
41
+ if (action === "backend-exchange") return {
42
+ action,
43
+ walletId,
44
+ attemptId: requireCanonicalString(body.attemptId, "attemptId"),
45
+ proof: parseIdentityProof(body.proof)
46
+ };
47
+ const appOrigin = requireCanonicalString(body.appOrigin, "appOrigin");
48
+ if (new URL(appOrigin).origin !== appOrigin) throw new Error("Application origin is invalid");
49
+ switch (action) {
50
+ case "associate":
51
+ case "unlink": return {
52
+ action,
53
+ walletId,
54
+ appOrigin,
55
+ attemptId: requireCanonicalString(body.attemptId, "attemptId"),
56
+ binding: secret(body.binding, "binding"),
57
+ sessionToken: secret(body.sessionToken, "sessionToken"),
58
+ code: secret(body.code, "code"),
59
+ factorProof: body.factorProof
60
+ };
61
+ case "owner-challenge": return {
62
+ action,
63
+ walletId,
64
+ appOrigin,
65
+ attemptId: requireCanonicalString(body.attemptId, "attemptId"),
66
+ binding: secret(body.binding, "binding"),
67
+ sessionToken: secret(body.sessionToken, "sessionToken"),
68
+ code: secret(body.code, "code")
69
+ };
70
+ case "prepare-unlink": return {
71
+ action,
72
+ walletId,
73
+ appOrigin,
74
+ associationId: requireCanonicalString(body.associationId, "associationId"),
75
+ binding: secret(body.binding, "binding"),
76
+ sessionToken: secret(body.sessionToken, "sessionToken")
77
+ };
78
+ case "prepare":
79
+ case "prepare-association": return {
80
+ action,
81
+ walletId,
82
+ appOrigin,
83
+ binding: secret(body.binding, "binding"),
84
+ sessionToken: secret(body.sessionToken, "sessionToken")
85
+ };
86
+ case "exchange": return {
87
+ action,
88
+ walletId,
89
+ appOrigin,
90
+ attemptId: requireCanonicalString(body.attemptId, "attemptId"),
91
+ idToken: requireCanonicalString(body.idToken, "idToken")
92
+ };
93
+ case "redeem": return {
94
+ action,
95
+ walletId,
96
+ appOrigin,
97
+ attemptId: requireCanonicalString(body.attemptId, "attemptId"),
98
+ binding: secret(body.binding, "binding"),
99
+ sessionToken: secret(body.sessionToken, "sessionToken"),
100
+ code: secret(body.code, "code")
101
+ };
102
+ case "settings":
103
+ case "logout": return {
104
+ action,
105
+ walletId,
106
+ appOrigin,
107
+ sessionToken: secret(body.sessionToken, "sessionToken")
108
+ };
109
+ default: return assertNever(action);
110
+ }
111
+ }
112
+ function assertNever(value) {
113
+ throw new Error(`Unsupported external authentication action: ${String(value)}`);
114
+ }
115
+ function externalAuthRoutes() {
116
+ return ACTIONS.map(externalAuthRoute);
117
+ }
118
+ function externalAuthRoute(action) {
119
+ return {
120
+ id: `external_auth_${action}`,
121
+ surface: "relay",
122
+ method: "POST",
123
+ path: `${EXTERNAL_AUTH_PATH}/${action}`,
124
+ auth: {
125
+ plane: "public",
126
+ proof: "challenge_exchange",
127
+ rationale: "Application credentials and scoped external identity evidence are verified by the handler."
128
+ },
129
+ metering: { kind: "none" },
130
+ summary: "External authentication for wallet settings"
131
+ };
132
+ }
133
+ function parseAction(pathname) {
134
+ for (const action of ACTIONS) if (pathname === `/wallet/external-auth/${action}`) return action;
135
+ return null;
136
+ }
137
+ function secret(value, label) {
138
+ const text = requireCanonicalString(value, label);
139
+ if (!/^[A-Za-z0-9_-]{43}$/.test(text)) throw new Error(`${label} must contain 32 random bytes`);
140
+ return text;
141
+ }
142
+ function failure(status, code) {
143
+ return json(externalAuthFailure(code), {
144
+ status,
145
+ headers: { "Cache-Control": "no-store" }
146
+ });
147
+ }
148
+ async function resolveApplicationTrust(ctx, service, command) {
149
+ const credential = extractBearerCredential(ctx.request.headers);
150
+ if (!credential) return null;
151
+ const scope = service.store.scope;
152
+ if (ctx.request.headers.get("x-seams-environment-id") !== scope.envId) return null;
153
+ const result = command.action === "backend-exchange" ? await ctx.opts.apiKeyAuth?.authenticate({
154
+ secret: credential,
155
+ endpoint: ctx.pathname,
156
+ requiredScopes: ["wallets.read"],
157
+ environmentId: scope.envId
158
+ }) : await ctx.opts.publishableKeyAuth?.authenticate({
159
+ secret: credential,
160
+ origin: ctx.request.headers.get("origin") || "",
161
+ environmentId: scope.envId
162
+ });
163
+ if (!result?.ok || result.principal.orgId !== scope.orgId || result.principal.projectId !== scope.projectId || result.principal.environmentId !== scope.envId) return null;
164
+ if (command.action !== "backend-exchange") {
165
+ const parent = await ctx.opts.publishableKeyAuth?.authenticate({
166
+ secret: credential,
167
+ origin: command.appOrigin,
168
+ environmentId: scope.envId
169
+ });
170
+ if (!parent?.ok || parent.principal.apiKeyId !== result.principal.apiKeyId) return null;
171
+ }
172
+ const environment = (await ctx.opts.orgProjectEnv?.listEnvironments({
173
+ orgId: scope.orgId,
174
+ projectId: scope.projectId,
175
+ environmentId: scope.envId,
176
+ actorUserId: result.principal.apiKeyId,
177
+ roles: []
178
+ }, { status: "ACTIVE" }))?.find(matchesEnvironment.bind(null, scope.envId));
179
+ return parseExternalAuthConfiguration(environment?.externalAuth ?? {
180
+ kind: "disabled",
181
+ revision: 0
182
+ });
183
+ }
184
+ function matchesEnvironment(environmentId, environment) {
185
+ return environment.id === environmentId;
186
+ }
187
+ async function handleExternalAuth(ctx) {
188
+ const action = parseAction(ctx.pathname);
189
+ if (!action) return null;
190
+ if (ctx.method !== "POST") return failure(405, "invalid_request");
191
+ const service = ctx.service.externalAuth;
192
+ if (!service) return failure(503, "unavailable");
193
+ let command;
194
+ try {
195
+ command = parseCommand(action, requireRecord(await readJson(ctx.request), "external authentication request"));
196
+ } catch {
197
+ return failure(400, "invalid_request");
198
+ }
199
+ try {
200
+ const trust = await resolveApplicationTrust(ctx, service, command);
201
+ if (!trust) return failure(403, "access_denied");
202
+ return await dispatch(ctx, service, trust, command);
203
+ } catch {
204
+ return failure(503, "unavailable");
205
+ }
206
+ }
207
+ async function dispatch(ctx, service, trust, command) {
208
+ const walletId = command.walletId;
209
+ if (command.action === "prepare" || command.action === "prepare-association" || command.action === "prepare-unlink" || command.action === "exchange" || command.action === "backend-exchange" || command.action === "associate") {
210
+ const rateKey = await sha256HexUtf8(resolveSourceIpFromFetchHeaders(ctx.request.headers) || "unknown");
211
+ if (!await service.store.consumeAuthenticationAllowance(rateKey, Date.now())) return failure(429, "rate_limited");
212
+ }
213
+ if (command.action === "backend-exchange") {
214
+ if (trust.kind === "disabled") return failure(403, "access_denied");
215
+ return json(await service.exchange({
216
+ walletId,
217
+ attemptId: command.attemptId,
218
+ proof: command.proof,
219
+ trust
220
+ }));
221
+ }
222
+ const originBinding = JSON.stringify([ctx.request.headers.get("origin") || "", command.appOrigin]);
223
+ if (command.action === "prepare-unlink") return json(await service.prepareUnlink({
224
+ walletId,
225
+ associationId: command.associationId,
226
+ originBinding,
227
+ binding: command.binding,
228
+ sessionToken: command.sessionToken,
229
+ trustRevision: trust.revision
230
+ }));
231
+ if (command.action === "owner-challenge" || command.action === "associate" || command.action === "unlink") {
232
+ const binding = {
233
+ walletId,
234
+ attemptId: command.attemptId,
235
+ originBinding,
236
+ bindingHash: await sha256HexUtf8(command.binding),
237
+ sessionTokenHash: await sha256HexUtf8(command.sessionToken),
238
+ codeHash: await sha256HexUtf8(command.code),
239
+ trustRevision: trust.revision,
240
+ nowMs: Date.now()
241
+ };
242
+ const result = await readExternalOwnerOperation({
243
+ store: service.store,
244
+ binding,
245
+ requestOrigin: ctx.request.headers.get("origin") || "",
246
+ phase: command.action === "owner-challenge" ? "prepare" : "complete"
247
+ });
248
+ if (!result.ok) return json(result, { status: 403 });
249
+ const ownerOperation = result.value;
250
+ if (command.action === "owner-challenge") return json({
251
+ ok: true,
252
+ value: externalOwnerChallenge(ownerOperation)
253
+ });
254
+ if (command.action !== ownerOperation.attempt.purpose) return failure(403, "access_denied");
255
+ if (ownerOperation.attempt.status === "consumed") {
256
+ const stored = await ctx.service.authorizedOperations.readAuthorizedOperationById({
257
+ tenantId: ctx.service.authorizedOperations.tenantId,
258
+ authorizedOperationId: walletOwnerOperationAuthorizationId(ownerOperation.operation, ownerOperation.challengeB64u)
259
+ });
260
+ if (!stored || stored.authorization.kind !== "verified_step_up" || stored.quota.kind !== "quota_neutral" || stored.lifecycle !== "completed") return failure(503, "unavailable");
261
+ return new Response(stored.response.bodyText, {
262
+ status: stored.response.status,
263
+ headers: {
264
+ "Content-Type": stored.response.contentType,
265
+ "Cache-Control": "no-store"
266
+ }
267
+ });
268
+ }
269
+ const authorized = await authorizeWalletOwnerOperation({
270
+ ctx,
271
+ walletId,
272
+ operation: ownerOperation.operation,
273
+ payload: ownerOperation.payload,
274
+ factorProof: command.factorProof
275
+ });
276
+ if (!authorized.ok) return authorized.response;
277
+ if (!await service.store.consumeOwnerAttempt({
278
+ walletId,
279
+ attemptId: binding.attemptId,
280
+ originBinding,
281
+ bindingHash: binding.bindingHash,
282
+ sessionTokenHash: binding.sessionTokenHash,
283
+ codeHash: binding.codeHash,
284
+ trustRevision: binding.trustRevision,
285
+ nowMs: Date.now()
286
+ }, authorized.ownerAuthority)) return completeWalletOwnerOperation(ctx, authorized.operation, 403, externalAuthFailure("access_denied"), "failed_before_side_effect");
287
+ const attempt = ownerOperation.attempt;
288
+ if (attempt.purpose === "associate") {
289
+ if (!await service.store.associate({
290
+ walletId,
291
+ identity: attempt.identity,
292
+ authorityId: authorized.ownerAuthority.authorityId,
293
+ authorityEpoch: authorized.ownerAuthority.revocationEpoch
294
+ })) return completeWalletOwnerOperation(ctx, authorized.operation, 403, externalAuthFailure("access_denied"), "failed_before_side_effect");
295
+ } else await service.store.revokeAssociation({
296
+ walletId,
297
+ associationId: attempt.associationId,
298
+ owner: authorized.ownerAuthority
299
+ });
300
+ return completeWalletOwnerOperation(ctx, authorized.operation, 200, {
301
+ ok: true,
302
+ value: null
303
+ });
304
+ }
305
+ if (trust.kind === "disabled") return failure(403, "access_denied");
306
+ if (command.action === "prepare" || command.action === "prepare-association") return json(await service.prepare({
307
+ purpose: command.action === "prepare" ? "authenticate" : "associate",
308
+ walletId,
309
+ originBinding,
310
+ binding: command.binding,
311
+ sessionToken: command.sessionToken
312
+ }));
313
+ if (command.action === "exchange") return json(await service.exchange({
314
+ trust,
315
+ walletId,
316
+ attemptId: command.attemptId,
317
+ proof: {
318
+ kind: "google",
319
+ idToken: command.idToken
320
+ }
321
+ }));
322
+ const tokenHash = await sha256HexUtf8(command.sessionToken);
323
+ if (command.action === "logout") {
324
+ await service.store.revokeSession(tokenHash, originBinding);
325
+ return json({ ok: true });
326
+ }
327
+ const authentication = command.action === "redeem" ? await service.store.redeem({
328
+ walletId,
329
+ originBinding,
330
+ attemptId: command.attemptId,
331
+ bindingHash: await sha256HexUtf8(command.binding),
332
+ codeHash: await sha256HexUtf8(command.code),
333
+ sessionTokenHash: tokenHash,
334
+ nowMs: Date.now(),
335
+ trustRevision: trust.revision
336
+ }) : await service.store.readSession({
337
+ tokenHash,
338
+ originBinding,
339
+ nowMs: Date.now(),
340
+ trustRevision: trust.revision
341
+ });
342
+ if (!authentication || authentication.walletId !== walletId) return failure(403, "access_denied");
343
+ return json({
344
+ ok: true,
345
+ value: await service.settings(authentication)
346
+ }, { headers: { "Cache-Control": "no-store" } });
347
+ }
348
+ //#endregion
349
+ export { externalAuthRoutes, handleExternalAuth };
350
+
351
+ //# sourceMappingURL=externalAuth.js.map
@@ -1,9 +1,9 @@
1
1
  import { isPlainObject } from "../../../../shared-ts/src/utils/validation.js";
2
2
  import { routerAbMpcMaterialActivationRefFromWire } from "../../../../shared-ts/src/utils/routerAbNormalSigningIdentity.js";
3
+ import { json } from "../../../framework/http.js";
3
4
  import { walletObjectIdentity, walletObjectIdentityHeaders } from "../../../cloudflare/runtime/walletObjectAddresses.js";
4
5
  import { ROUTER_AB_INTERNAL_SERVICE_AUTH_HEADER_V1, normalizeRouterAbInternalServiceAuthSecret } from "../../../../core/ThresholdService/routerAb/internalServiceHttp.js";
5
6
  import { prepareOwnerWalletExecution } from "../../../domains/signingOperations/walletExecutionAdmission.js";
6
- import { json } from "../../../framework/http.js";
7
7
  //#region src/router/transport/fetch/routes/normalSigningRouterProxy.ts
8
8
  async function proxyNormalSigningRequestToMpcRouter(input) {
9
9
  const proxy = input.proxy;