@seams/wallet-server 0.28.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 +23 -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 +58 -7
- package/dist/esm/auth/externalAuthStore.js +157 -21
- package/dist/esm/auth/externalOwnerOperations.js +82 -0
- package/dist/esm/cloud-host.js +4 -4
- package/dist/esm/core/nearRelayerAccountProvisioning.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 +22 -1
- package/dist/esm/router/cloudflare/d1/emailOtp/d1EmailOtpRecoveryService.js +1 -1
- package/dist/esm/router/cloudflare/d1/emailOtp/d1EmailOtpRegistrationEnrollmentFinalizer.js +1 -1
- package/dist/esm/router/cloudflare/d1/emailOtp/d1GoogleEmailOtpSessionResolver.js +1 -1
- package/dist/esm/router/cloudflare/d1/passkeyCustody/d1PasskeyCustodyRouteService.js +2 -2
- package/dist/esm/router/cloudflare/d1/passkeyCustody/d1WalletCustodyCommitStore.js +2 -2
- package/dist/esm/router/cloudflare/d1/passkeyCustody/d1WalletRecoveryGoogleEmailOtpService.js +2 -2
- package/dist/esm/router/cloudflare/d1/registration/d1RegistrationCeremonyRecords.js +9 -0
- package/dist/esm/router/cloudflare/d1/registration/d1WalletExecutionAuthority.js +9 -16
- package/dist/esm/router/cloudflare/d1/registration/d1WalletRegistrationService.js +43 -1
- 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/walletRegistrationSessionCommitReceipt.js +35 -0
- package/dist/esm/router/cloudflare/d1/wallet/d1WalletAuthMethodBoundary.js +1 -1
- package/dist/esm/router/cloudflare/d1/wallet/d1WalletAuthMethodService.js +4 -3
- package/dist/esm/router/cloudflare.js +1785 -1489
- package/dist/esm/router/domains/ed25519Yao/export/routerAbEd25519YaoExport.js +2 -2
- package/dist/esm/router/domains/emailOtp/emailOtpRouteHandlers.js +1 -1
- package/dist/esm/router/domains/passkeyCustody/walletCustodyRegistrationCommit.js +1 -1
- package/dist/esm/router/domains/signingOperations/routerAbEd25519NormalSigningRoute.js +35 -1
- package/dist/esm/router/domains/walletRegistration/walletRegistrationReservation.js +17 -2
- package/dist/esm/router/domains/walletRegistration/walletRegistrationRoutes.js +9 -1
- package/dist/esm/router/domains/walletUnlock/walletUnlockRouteHandlers.js +1 -1
- package/dist/esm/router/transport/fetch/routes/ecdsaOperationStepUpFactor.js +2 -2
- package/dist/esm/router/transport/fetch/routes/externalAuth.js +139 -32
- package/dist/esm/router/transport/fetch/routes/passkeyCustody.js +44 -304
- package/dist/esm/router/transport/fetch/routes/sessions.js +2 -2
- package/dist/esm/router/transport/fetch/routes/thresholdEd25519.js +2 -2
- package/dist/esm/router/transport/fetch/routes/walletRegistration.js +2 -2
- package/dist/esm/router/transport/fetch/walletOwnerOperationAuthorization.js +306 -0
- package/dist/esm/shared-ts/src/auth/externalAuth.js +87 -14
- package/dist/esm/shared-ts/src/authorization/index.js +1 -1
- package/dist/esm/shared-ts/src/authorization/{walletCustodyOperation.js → walletOwnerOperation.js} +4 -4
- package/dist/esm/shared-ts/src/wallet-recovery/index.js +3 -3
- package/dist/types/shared-ts/src/auth/externalAuth.d.ts +51 -8
- 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 +29 -10
- package/dist/types/wallet-server/src/auth/externalAuthStore.d.ts +60 -6
- package/dist/types/wallet-server/src/auth/externalOwnerOperations.d.ts +18 -0
- package/dist/types/wallet-server/src/cloud-host.d.ts +1 -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/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/d1WalletAuthMethodService.d.ts +1 -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/authServicePort.d.ts +11 -0
- package/dist/types/wallet-server/src/router/transport/fetch/walletOwnerOperationAuthorization.d.ts +23 -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
|
@@ -2,32 +2,29 @@ import { parseEmailOtpChallengeId, parseOrgId, parseProviderSubject, parseWallet
|
|
|
2
2
|
import { normalizeOptionalTrimmedString } from "../../../../shared-ts/src/utils/normalize.js";
|
|
3
3
|
import { isPlainObject, requireTrimmedString } from "../../../../shared-ts/src/utils/validation.js";
|
|
4
4
|
import { base64UrlDecode } from "../../../../shared-ts/src/utils/base64.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
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";
|
|
13
|
+
import { toFetchRouteResponse } from "../../../framework/routeResponses.js";
|
|
14
|
+
import { parseRecoveryBackupAcknowledgementRequest, parseRecoveryStatusRequest, requireExactObjectFields } from "./walletCustodyRequestBoundary.js";
|
|
15
|
+
import { authorizeWalletOwnerOperation, completeWalletOwnerOperation } from "../walletOwnerOperationAuthorization.js";
|
|
13
16
|
import { extractBearerCredential, resolveSourceIpFromFetchHeaders } from "../../../auth/routerApiKeyAuth.js";
|
|
14
17
|
import { readJson } from "../../../framework/http.js";
|
|
15
18
|
import { findRouteDefinitionById, matchesRouteDefinitionRequest } from "../../../framework/routeDefinitions.js";
|
|
16
|
-
import { toFetchRouteResponse } from "../../../framework/routeResponses.js";
|
|
17
19
|
import { parsePasskeyCustodyEnvelopeRecord } from "../../../../shared-ts/src/passkey-custody/custodyEnvelope.js";
|
|
18
20
|
import "../../../../shared-ts/src/passkey-custody/index.js";
|
|
19
|
-
import {
|
|
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 =
|
|
349
|
-
const 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 =
|
|
379
|
-
const challengeDigest = await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1012
|
+
return await completeWalletOwnerOperation(ctx, authorized.operation, 200, {
|
|
1273
1013
|
ok: true,
|
|
1274
1014
|
activeCodeCount: result.activeCodeCount,
|
|
1275
1015
|
totalCodeCount: result.totalCodeCount,
|
|
@@ -9,12 +9,12 @@ import { normalizeRuntimePolicyScope } from "../../../../shared-ts/src/threshold
|
|
|
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 { EMAIL_OTP_CHANNEL, WALLET_EMAIL_OTP_EXPORT_OPERATION } from "../../../../shared-ts/src/utils/emailOtpDomain.js";
|
|
13
|
+
import { emailOtpStatusCode, hashEmailOtpOperationBinding } from "../../../domains/emailOtp/emailOtpSessionRouteHelpers.js";
|
|
12
14
|
import { extractBearerCredential, resolveSourceIpFromFetchHeaders } from "../../../auth/routerApiKeyAuth.js";
|
|
13
15
|
import { json, readJson } from "../../../framework/http.js";
|
|
14
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";
|
|
@@ -10,13 +10,13 @@ import { sameRouterAbMpcMaterialActivationRef } from "../../../../shared-ts/src/
|
|
|
10
10
|
import { isPasskeyWalletAuthAuthority, walletAuthAuthorityRef } from "../../../../shared-ts/src/utils/walletAuthAuthority.js";
|
|
11
11
|
import { authorizedOperationReplayBodyInit, parseSessionOrigin, parseVerifiedOwnerProofId } from "../../../../authorization/domain.js";
|
|
12
12
|
import { buildVerifiedWalletOperationEmailOtpFactorResult, buildVerifiedWalletOperationPasskeyFactorResult, buildVerifiedWalletSessionPasskeyFactorResult } from "../../../../authorization/factorEvidence.js";
|
|
13
|
+
import { EMAIL_OTP_CHANNEL, WALLET_EMAIL_OTP_TRANSACTION_SIGN_OPERATION } from "../../../../shared-ts/src/utils/emailOtpDomain.js";
|
|
14
|
+
import { emailOtpStatusCode, hashEmailOtpOperationBinding } from "../../../domains/emailOtp/emailOtpSessionRouteHelpers.js";
|
|
13
15
|
import { normalizeCorsOrigin } from "../../../../core/SessionService.js";
|
|
14
16
|
import { json, readJson } from "../../../framework/http.js";
|
|
15
17
|
import { ROUTER_AB_ED25519_NORMAL_SIGNING_PATH, ROUTER_AB_ED25519_NORMAL_SIGNING_PREPARE_PATH, ROUTER_AB_ED25519_WALLET_SESSION_PATH } from "../../../../shared-ts/src/utils/signingSessionSeal.js";
|
|
16
18
|
import { walletIdFromString } from "../../../../shared-ts/src/utils/registrationIds.js";
|
|
17
19
|
import { resolveThresholdRuntimePolicyScope } from "../../../auth/commonRouterUtils.js";
|
|
18
|
-
import { emailOtpStatusCode, hashEmailOtpOperationBinding } from "../../../domains/emailOtp/emailOtpSessionRouteHelpers.js";
|
|
19
|
-
import { EMAIL_OTP_CHANNEL, WALLET_EMAIL_OTP_TRANSACTION_SIGN_OPERATION } from "../../../../shared-ts/src/utils/emailOtpDomain.js";
|
|
20
20
|
import { thresholdEd25519StatusCode } from "../../../../threshold/statusCodes.js";
|
|
21
21
|
import { signingPrepareSessionResponse } from "../../../domains/signingOperations/signingPrepareSession.js";
|
|
22
22
|
import { authenticateRouterAbWalletOperationStepUpIdentity } from "../../../domains/signingOperations/routerAbOperationStepUp.js";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { toFetchRouteResponse } from "../../../framework/routeResponses.js";
|
|
1
2
|
import { resolveSourceIpFromFetchHeaders } from "../../../auth/routerApiKeyAuth.js";
|
|
2
3
|
import "../../../../shared-ts/src/utils/walletManagementProtocol.js";
|
|
3
4
|
import { jsonFailure, readJson } from "../../../framework/http.js";
|
|
4
5
|
import { findRouteDefinitionById, matchesRouteDefinitionRequest } from "../../../framework/routeDefinitions.js";
|
|
5
|
-
import { toFetchRouteResponse } from "../../../framework/routeResponses.js";
|
|
6
6
|
import { handleRouterApiWalletAddAuthMethodEmailOtpChallenge, handleRouterApiWalletAddAuthMethodFinalize, handleRouterApiWalletAddAuthMethodIntent, handleRouterApiWalletAddAuthMethodStart, handleRouterApiWalletAddSignerEcdsaActivation, handleRouterApiWalletAddSignerEcdsaDerivationRespond, handleRouterApiWalletAddSignerFinalize, handleRouterApiWalletAddSignerIntent, handleRouterApiWalletAddSignerStart, handleRouterApiWalletEcdsaKeyFactsInventory, handleRouterApiWalletNearImplicitAccountFund, handleRouterApiWalletRegistrationActivate, handleRouterApiWalletRegistrationNearAdmission, handleRouterApiWalletRegistrationNearProvisioning, handleRouterApiWalletRegistrationRespond, handleRouterApiWalletRegistrationSetup, handleRouterApiWalletRevokeAuthMethod } from "../../../domains/walletRegistration/walletRegistrationRoutes.js";
|
|
7
7
|
import { routerApiWalletRegistrationRouteService } from "../../../framework/authServicePort.js";
|
|
8
8
|
//#region src/router/transport/fetch/routes/walletRegistration.ts
|
|
@@ -44,7 +44,7 @@ function resolveWalletRegistrationRoute(ctx) {
|
|
|
44
44
|
async function handleWalletRegistration(ctx) {
|
|
45
45
|
const route = resolveWalletRegistrationRoute(ctx);
|
|
46
46
|
if (!route) return null;
|
|
47
|
-
if (ctx.request.headers.get("X-Seams-Wallet-Protocol") !== "
|
|
47
|
+
if (ctx.request.headers.get("X-Seams-Wallet-Protocol") !== "5") return jsonFailure(409, "wallet_protocol_mismatch", "Your wallet application needs an update. Reload this page and try again. If the problem continues, ask the application developer to upgrade the Wallet SDK.");
|
|
48
48
|
const common = {
|
|
49
49
|
body: await readJson(ctx.request),
|
|
50
50
|
headers: Object.fromEntries(ctx.request.headers.entries()),
|