@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.
- package/README.md +19 -4
- package/artifact-manifest.json +28 -13
- package/cloudflare-router-ab/build/deriver-a/build-stamp.json +1 -1
- package/cloudflare-router-ab/build/deriver-b/build-stamp.json +1 -1
- package/cloudflare-router-ab/build/router/build-stamp.json +1 -1
- package/cloudflare-router-ab/build/signing-worker/build-stamp.json +1 -1
- package/cloudflare-router-ab/build/tenant-root-control-plane/build-stamp.json +1 -1
- package/dist/esm/application-auth.js +2 -0
- package/dist/esm/auth/applicationAuth.js +85 -0
- package/dist/esm/auth/externalAuthService.js +149 -0
- package/dist/esm/auth/externalAuthStore.js +353 -0
- package/dist/esm/auth/externalOwnerOperations.js +82 -0
- package/dist/esm/authorization/domain.js +2 -2
- package/dist/esm/authorization/factorEvidence.js +1 -1
- package/dist/esm/authorization/service.js +2 -2
- package/dist/esm/authorization/vaultProxyUse.js +1 -1
- package/dist/esm/cloud-host.js +12 -11
- package/dist/esm/core/ThresholdService/validation.js +1 -1
- package/dist/esm/core/WebAuthnCredentialBindingStore.js +1 -1
- package/dist/esm/core/authService/webauthn.js +1 -1
- package/dist/esm/core/d1EcdsaSignerRead.js +1 -1
- package/dist/esm/core/d1WalletAuthMethodStore.js +2 -2
- package/dist/esm/core/d1WalletStore.js +1 -1
- package/dist/esm/core/deviceLinking/linkedDeviceEmailOtpGrant.js +1 -1
- package/dist/esm/core/deviceLinking/linkedDeviceManagement.js +1 -1
- package/dist/esm/core/deviceLinking/linkedDeviceSessionRecord.js +2 -2
- package/dist/esm/core/nearRelayerAccountProvisioning.js +2 -2
- package/dist/esm/hosted-wallet-gateway.js +3 -3
- package/dist/esm/index.js +1 -1
- package/dist/esm/router/auth/webAuthnCredentialCodecs.js +1 -1
- package/dist/esm/router/cloudflare/d1/auth/d1AuthorizedOperationRouteService.js +2 -0
- package/dist/esm/router/cloudflare/d1/auth/d1RecordedOwnerOperation.js +43 -0
- package/dist/esm/router/cloudflare/d1/auth/d1RouterApiAuthService.js +27 -3
- package/dist/esm/router/cloudflare/d1/authorization/d1AuthorizationStore.js +2 -2
- package/dist/esm/router/cloudflare/d1/authorization/d1VaultProxyStore.js +2 -2
- package/dist/esm/router/cloudflare/d1/deviceLinking/d1LinkedDeviceAuthorityInstallService.js +3 -3
- package/dist/esm/router/cloudflare/d1/deviceLinking/d1LinkedDeviceEd25519ExportRootStore.js +1 -1
- package/dist/esm/router/cloudflare/d1/deviceLinking/d1LinkedDeviceOwnerAuthorizationProvider.js +2 -2
- package/dist/esm/router/cloudflare/d1/deviceLinking/d1LinkedDeviceSessionStore.js +1 -1
- package/dist/esm/router/cloudflare/d1/deviceLinking/d1LinkedDeviceSourceContributionPreparationPlanner.js +1 -1
- package/dist/esm/router/cloudflare/d1/deviceLinking/d1LinkedDeviceTargetAuthenticatorStore.js +1 -1
- package/dist/esm/router/cloudflare/d1/deviceLinking/d1LinkedDeviceTargetCredentialProvider.js +1 -1
- package/dist/esm/router/cloudflare/d1/deviceLinking/d1LinkedDeviceTargetPlanner.js +1 -1
- package/dist/esm/router/cloudflare/d1/deviceLinking/d1LinkedDeviceVerifiedLinkBuilder.js +2 -2
- package/dist/esm/router/cloudflare/d1/deviceLinking/d1LinkedDeviceVerifiedLinkSourceReader.js +1 -1
- package/dist/esm/router/cloudflare/d1/ed25519Yao/d1Ed25519YaoCapabilityPersistence.js +1 -1
- package/dist/esm/router/cloudflare/d1/emailOtp/d1EmailOtpChallengeIssuer.js +1 -1
- package/dist/esm/router/cloudflare/d1/emailOtp/d1EmailOtpChallengeService.js +1 -1
- package/dist/esm/router/cloudflare/d1/emailOtp/d1EmailOtpChallengeStore.js +1 -1
- package/dist/esm/router/cloudflare/d1/emailOtp/d1EmailOtpRecords.js +2 -2
- package/dist/esm/router/cloudflare/d1/emailOtp/d1EmailOtpRecoveryService.js +3 -3
- package/dist/esm/router/cloudflare/d1/emailOtp/d1EmailOtpRegistrationEnrollmentFinalizer.js +1 -1
- package/dist/esm/router/cloudflare/d1/emailOtp/d1GoogleEmailOtpRegistrationAttemptStore.js +1 -1
- package/dist/esm/router/cloudflare/d1/emailOtp/d1GoogleEmailOtpSessionResolver.js +3 -3
- package/dist/esm/router/cloudflare/d1/oidc/d1OidcBoundary.js +7 -1
- package/dist/esm/router/cloudflare/d1/oidc/d1OidcVerificationService.js +1 -1
- package/dist/esm/router/cloudflare/d1/passkeyCustody/d1PasskeyCustodyRouteService.js +4 -4
- package/dist/esm/router/cloudflare/d1/passkeyCustody/d1WalletCustodyCommitStore.js +4 -4
- package/dist/esm/router/cloudflare/d1/passkeyCustody/d1WalletRecoveryGoogleEmailOtpRecords.js +1 -1
- package/dist/esm/router/cloudflare/d1/passkeyCustody/d1WalletRecoveryGoogleEmailOtpService.js +3 -3
- package/dist/esm/router/cloudflare/d1/registration/d1EvmFamilyEcdsaRegistrationBranch.js +1 -1
- package/dist/esm/router/cloudflare/d1/registration/d1RegistrationCeremonyRecords.js +11 -2
- package/dist/esm/router/cloudflare/d1/registration/d1RegistrationIntentService.js +1 -1
- package/dist/esm/router/cloudflare/d1/registration/d1WalletExecutionAuthority.js +9 -16
- package/dist/esm/router/cloudflare/d1/registration/d1WalletRegistrationCommitStore.js +1 -1
- package/dist/esm/router/cloudflare/d1/registration/d1WalletRegistrationService.js +46 -4
- package/dist/esm/router/cloudflare/d1/registration/d1WalletRegistrationSetup.js +12 -0
- package/dist/esm/router/cloudflare/d1/registration/d1WalletRegistrationSetupExecution.js +1 -0
- package/dist/esm/router/cloudflare/d1/registration/registrationCompletionWrite.js +1 -0
- package/dist/esm/router/cloudflare/d1/registration/walletRegistrationEstablishedSessionIssuer.js +1 -1
- package/dist/esm/router/cloudflare/d1/registration/walletRegistrationSessionCommitReceipt.js +36 -1
- package/dist/esm/router/cloudflare/d1/signingAdmission/d1RouterAbNormalSigningAdmissionStore.js +1 -1
- package/dist/esm/router/cloudflare/d1/wallet/authorizationSnapshotLayout.js +1 -0
- package/dist/esm/router/cloudflare/d1/wallet/d1VoiceIdWalletSessionChallenges.js +2 -2
- package/dist/esm/router/cloudflare/d1/wallet/d1WalletAuthMethodBoundary.js +1 -1
- package/dist/esm/router/cloudflare/d1/wallet/d1WalletAuthMethodRevocationReplayStore.js +1 -1
- package/dist/esm/router/cloudflare/d1/wallet/d1WalletAuthMethodService.js +6 -5
- package/dist/esm/router/cloudflare/d1/wallet/d1WalletAuthorityStore.js +2 -2
- package/dist/esm/router/cloudflare/d1/wallet/d1WalletAuthorizationCleanup.js +2 -1
- package/dist/esm/router/cloudflare/d1/wallet/d1WalletAuthorizationIntake.js +1 -1
- package/dist/esm/router/cloudflare/d1/wallet/d1WalletAuthorizationSnapshot.js +1 -1
- package/dist/esm/router/cloudflare/d1/wallet/d1WalletRelocationChallenges.js +1 -1
- package/dist/esm/router/cloudflare/d1/wallet/d1WalletRelocationOwnerRequest.js +2 -2
- package/dist/esm/router/cloudflare/d1/webauthn/d1WebAuthnAuthService.js +2 -2
- package/dist/esm/router/cloudflare/d1/webauthn/d1WebAuthnRecords.js +2 -2
- package/dist/esm/router/cloudflare/runtime/staticWalletConsoleBinding.js +9 -0
- package/dist/esm/router/cloudflare/runtime/walletConsoleOpsClient.js +6 -0
- package/dist/esm/router/cloudflare-adaptor.js +1 -1
- package/dist/esm/router/cloudflare.js +6139 -5567
- package/dist/esm/router/domains/ecdsa/routerAbEcdsaStrictRegistration.js +1 -1
- package/dist/esm/router/domains/ed25519Yao/export/routerAbEd25519YaoExport.js +5 -5
- package/dist/esm/router/domains/ed25519Yao/recovery/routerAbEd25519YaoRecovery.js +2 -2
- package/dist/esm/router/domains/ed25519Yao/recovery/routerAbEd25519YaoRecoveryWalletSessionAuthorization.js +1 -1
- package/dist/esm/router/domains/ed25519Yao/registration/routerAbEd25519YaoHttpRegistrationBackend.js +1 -1
- package/dist/esm/router/domains/ed25519Yao/registration/routerAbEd25519YaoRegistration.js +1 -1
- package/dist/esm/router/domains/ed25519Yao/registration/routerAbEd25519YaoRegistrationIntentAuthorization.js +1 -1
- package/dist/esm/router/domains/emailOtp/emailOtpRouteHandlers.js +2 -2
- package/dist/esm/router/domains/passkeyCustody/walletCustodyRegistrationCommit.js +1 -1
- package/dist/esm/router/domains/signingOperations/routerAbEcdsaDerivationNormalSigningRoute.js +2 -2
- package/dist/esm/router/domains/signingOperations/routerAbEd25519NormalSigningRoute.js +37 -3
- package/dist/esm/router/domains/signingOperations/routerAbOperationStepUp.js +1 -1
- package/dist/esm/router/domains/tenantRoot/tenantRootIdentityResolution.js +1 -1
- package/dist/esm/router/domains/walletRegistration/walletRegistrationReservation.js +18 -3
- package/dist/esm/router/domains/walletRegistration/walletRegistrationRoutes.js +12 -4
- package/dist/esm/router/domains/walletUnlock/walletUnlockRouteHandlers.js +3 -3
- package/dist/esm/router/framework/http.js +1 -1
- package/dist/esm/router/framework/routeDefinitions.js +2 -1
- package/dist/esm/router/transport/fetch/createFetchRouter.js +5 -3
- package/dist/esm/router/transport/fetch/routes/auth.js +1 -1
- package/dist/esm/router/transport/fetch/routes/deviceLinking.js +3 -3
- package/dist/esm/router/transport/fetch/routes/deviceLinkingOwnerAuthorization.js +1 -1
- package/dist/esm/router/transport/fetch/routes/ecdsaOperationStepUpFactor.js +3 -3
- package/dist/esm/router/transport/fetch/routes/externalAuth.js +351 -0
- package/dist/esm/router/transport/fetch/routes/normalSigningRouterProxy.js +1 -1
- package/dist/esm/router/transport/fetch/routes/passkeyCustody.js +46 -306
- package/dist/esm/router/transport/fetch/routes/sessions.js +4 -4
- package/dist/esm/router/transport/fetch/routes/syncAccount.js +2 -2
- package/dist/esm/router/transport/fetch/routes/syncAccountBootstrap.js +1 -1
- package/dist/esm/router/transport/fetch/routes/thresholdEcdsa.js +4 -4
- package/dist/esm/router/transport/fetch/routes/thresholdEd25519.js +6 -6
- package/dist/esm/router/transport/fetch/routes/walletExecutionLanePreflight.js +2 -2
- package/dist/esm/router/transport/fetch/routes/walletRegistration.js +4 -4
- package/dist/esm/router/transport/fetch/walletOwnerOperationAuthorization.js +306 -0
- package/dist/esm/shared-ts/src/auth/externalAuth.js +118 -0
- package/dist/esm/shared-ts/src/authorization/index.js +1 -1
- package/dist/esm/shared-ts/src/authorization/voiceIdDeviceAssertion.js +1 -1
- package/dist/esm/shared-ts/src/authorization/walletAuthority.js +2 -2
- package/dist/esm/shared-ts/src/authorization/{walletCustodyOperation.js → walletOwnerOperation.js} +4 -4
- package/dist/esm/shared-ts/src/device-linking/delegatedActivationPlan.js +1 -1
- package/dist/esm/shared-ts/src/device-linking/digests.js +1 -1
- package/dist/esm/shared-ts/src/device-linking/ed25519ExportRoot.js +1 -1
- package/dist/esm/shared-ts/src/device-linking/parsers.js +2 -2
- package/dist/esm/shared-ts/src/device-linking/sourceContribution.js +1 -1
- package/dist/esm/shared-ts/src/device-linking/walletSessionCredentialDelivery.js +1 -1
- package/dist/esm/shared-ts/src/device-linking/wireFields.js +2 -2
- package/dist/esm/shared-ts/src/passkey-custody/custodyEnvelope.js +1 -1
- package/dist/esm/shared-ts/src/passkey-custody/custodySecretBinding.js +1 -1
- package/dist/esm/shared-ts/src/signing-lanes/ownerContinuity.js +1 -1
- package/dist/esm/shared-ts/src/utils/routerAbEcdsaDerivation.js +1 -1
- package/dist/esm/shared-ts/src/utils/walletAuthAuthority.js +1 -1
- package/dist/esm/shared-ts/src/wallet-recovery/index.js +3 -3
- package/dist/esm/shared-ts/src/wallet-recovery/walletRecoveryCommittedProjection.js +1 -1
- package/dist/esm/shared-ts/src/wallet-recovery/walletRecoveryEnvelopeSet.js +1 -1
- package/dist/esm/threshold/session/signingSessionSeal/crypto/cipher.js +1 -1
- package/dist/esm/threshold/session/signingSessionSeal/service.js +1 -1
- package/dist/esm/threshold/session/signingSessionSeal/transport/fetch.js +1 -1
- package/dist/esm/threshold/session/signingSessionSeal/transport/shared.js +1 -1
- package/dist/types/shared-ts/src/auth/externalAuth.d.ts +127 -0
- package/dist/types/shared-ts/src/authorization/index.d.ts +1 -1
- package/dist/types/shared-ts/src/authorization/walletOwnerOperation.d.ts +10 -0
- package/dist/types/shared-ts/src/utils/registrationEstablishedSession.d.ts +4 -0
- package/dist/types/shared-ts/src/utils/registrationIntent.d.ts +2 -0
- package/dist/types/shared-ts/src/utils/walletManagementProtocol.d.ts +1 -1
- package/dist/types/wallet-server/src/application-auth.d.ts +3 -0
- package/dist/types/wallet-server/src/auth/applicationAuth.d.ts +29 -0
- package/dist/types/wallet-server/src/auth/externalAuthService.d.ts +55 -0
- package/dist/types/wallet-server/src/auth/externalAuthStore.d.ts +115 -0
- package/dist/types/wallet-server/src/auth/externalOwnerOperations.d.ts +18 -0
- package/dist/types/wallet-server/src/cloud-host.d.ts +2 -1
- package/dist/types/wallet-server/src/core/threeRouteRegistrationContracts.d.ts +12 -0
- package/dist/types/wallet-server/src/router/cloudflare/d1/auth/d1AuthorizedOperationRouteService.d.ts +5 -1
- package/dist/types/wallet-server/src/router/cloudflare/d1/auth/d1RecordedOwnerOperation.d.ts +16 -0
- package/dist/types/wallet-server/src/router/cloudflare/d1/oidc/d1OidcBoundary.d.ts +5 -0
- package/dist/types/wallet-server/src/router/cloudflare/d1/registration/d1RegistrationCeremonyRecords.d.ts +1 -0
- package/dist/types/wallet-server/src/router/cloudflare/d1/registration/d1WalletRegistrationSetup.d.ts +2 -0
- package/dist/types/wallet-server/src/router/cloudflare/d1/registration/walletRegistrationSessionCommitReceipt.d.ts +7 -1
- package/dist/types/wallet-server/src/router/cloudflare/d1/wallet/authorizationSnapshotLayout.d.ts +1 -1
- package/dist/types/wallet-server/src/router/cloudflare/d1/wallet/d1WalletAuthMethodService.d.ts +1 -0
- package/dist/types/wallet-server/src/router/cloudflare/runtime/staticWalletConsoleBinding.d.ts +2 -0
- package/dist/types/wallet-server/src/router/domains/signingOperations/routerAbEd25519NormalSigningRoute.d.ts +2 -2
- package/dist/types/wallet-server/src/router/domains/signingOperations/routerAbOperationStepUp.d.ts +1 -1
- package/dist/types/wallet-server/src/router/domains/walletRegistration/walletRegistrationInputs.d.ts +1 -0
- package/dist/types/wallet-server/src/router/domains/walletRegistration/walletRegistrationReservation.d.ts +11 -6
- package/dist/types/wallet-server/src/router/framework/apiCredentialPorts.d.ts +1 -0
- package/dist/types/wallet-server/src/router/framework/authServicePort.d.ts +12 -0
- package/dist/types/wallet-server/src/router/transport/fetch/routes/externalAuth.d.ts +5 -0
- package/dist/types/wallet-server/src/router/transport/fetch/walletOwnerOperationAuthorization.d.ts +23 -0
- package/migrations/d1-signer/0085_external_wallet_auth.sql +158 -0
- package/migrations/d1-signer/0086_application_auth.sql +67 -0
- package/migrations/d1-signer/0087_registration_without_session.sql +43 -0
- package/package.json +7 -2
- 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;
|