@seams/wallet-server 0.23.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/THIRD_PARTY_NOTICES.md +182 -5
- 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/authorization/voiceIdDeviceProof.js +32 -0
- package/dist/esm/core/ThresholdService/validation.js +17 -1
- package/dist/esm/core/d1WalletAuthMethodStore.js +42 -1
- package/dist/esm/core/deviceLinking/linkedDeviceManagement.js +5 -0
- package/dist/esm/core/signingLanes/WalletExecutionLaneProjection.js +11 -2
- package/dist/esm/router/cloudflare/d1/auth/d1RouterApiAuthService.js +23 -6
- package/dist/esm/router/cloudflare/d1/deviceLinking/d1LinkedDeviceAuthorityInstallService.js +5 -5
- package/dist/esm/router/cloudflare/d1/emailOtp/d1GoogleEmailOtpRegistrationAttemptStore.js +1 -1
- package/dist/esm/router/cloudflare/d1/passkeyCustody/d1PasskeyCustodyEnvelopeStore.js +6 -0
- package/dist/esm/router/cloudflare/d1/wallet/d1VoiceIdActivation.js +139 -0
- package/dist/esm/router/cloudflare/d1/wallet/d1VoiceIdSessions.js +138 -0
- package/dist/esm/router/cloudflare/d1/wallet/d1VoiceIdWalletSessionChallenges.js +88 -0
- package/dist/esm/router/cloudflare/d1/wallet/d1WalletAuthMethodService.js +12 -4
- package/dist/esm/router/cloudflare/d1/wallet/d1WalletAuthorityStore.js +14 -0
- package/dist/esm/router/cloudflare/d1/webauthn/d1WebAuthnRecords.js +1 -0
- package/dist/esm/router/cloudflare.js +576 -10
- package/dist/esm/router/domains/ed25519Yao/export/routerAbEd25519YaoExport.js +5 -0
- package/dist/esm/router/domains/passkeyCustody/walletRecoveryFinalization.js +1 -0
- package/dist/esm/router/domains/signingOperations/routerAbNormalSigningAdmissionCore.js +1 -0
- package/dist/esm/router/domains/signingOperations/walletExecutionAdmission.js +1 -0
- package/dist/esm/router/domains/voiceId/voiceIdActivation.js +12 -0
- package/dist/esm/router/domains/voiceId/voiceIdSessions.js +0 -0
- package/dist/esm/router/framework/routeDefinitions.js +13 -0
- package/dist/esm/router/transport/fetch/createFetchRouter.js +5 -0
- package/dist/esm/router/transport/fetch/routes/deviceLinkingOwnerAuthorization.js +12 -1
- package/dist/esm/router/transport/fetch/routes/walletExecutionLanePreflight.js +1 -0
- package/dist/esm/router/transport/fetch/routes/walletVoiceIdActivation.js +139 -0
- package/dist/esm/router/transport/fetch/routes/walletVoiceIdNear.js +36 -0
- package/dist/esm/router/transport/fetch/routes/walletVoiceIdSessions.js +127 -0
- package/dist/esm/shared-ts/src/authorization/voiceIdDeviceAssertion.js +110 -0
- package/dist/esm/shared-ts/src/device-linking/authorityActivation.js +1 -0
- package/dist/esm/shared-ts/src/device-linking/committedSignerPackages.js +1 -1
- package/dist/esm/shared-ts/src/passkey-custody/custodyEnvelope.js +30 -3
- package/dist/esm/shared-ts/src/passkey-custody/custodySecretBinding.js +1 -1
- package/dist/esm/shared-ts/src/signing-lanes/recordParsers.js +15 -1
- package/dist/esm/shared-ts/src/threshold/sessionPolicy.js +2 -1
- package/dist/esm/shared-ts/src/utils/registrationIntent.js +1 -3
- package/dist/esm/shared-ts/src/utils/walletAuthAuthority.js +46 -2
- package/dist/esm/shared-ts/src/utils/walletAuthMethodRecord.js +116 -7
- package/dist/esm/shared-ts/src/wallet-recovery/walletRecoveryCommittedProjection.js +1 -0
- package/dist/esm/wasm/near_signer/pkg/wasm_signer_worker_bg.wasm +0 -0
- package/dist/types/shared-ts/src/authorization/voiceIdDeviceAssertion.d.ts +32 -0
- package/dist/types/shared-ts/src/device-linking/committedSignerPackages.d.ts +3 -1
- package/dist/types/shared-ts/src/device-linking/contracts.d.ts +8 -0
- package/dist/types/shared-ts/src/passkey-custody/custodyEnvelope.d.ts +29 -0
- package/dist/types/shared-ts/src/passkey-custody/custodySecretBinding.d.ts +1 -0
- package/dist/types/shared-ts/src/signing-lanes/execution.d.ts +2 -2
- package/dist/types/shared-ts/src/signing-lanes/recordParsers.d.ts +2 -1
- package/dist/types/shared-ts/src/signing-lanes/records.d.ts +10 -2
- package/dist/types/shared-ts/src/signing-lanes/rotation.d.ts +2 -2
- package/dist/types/shared-ts/src/threshold/sessionPolicy.d.ts +9 -0
- package/dist/types/shared-ts/src/utils/addWalletAuthMethod.d.ts +3 -3
- package/dist/types/shared-ts/src/utils/domainIds.d.ts +4 -5
- package/dist/types/shared-ts/src/utils/registrationIntent.d.ts +0 -1
- package/dist/types/shared-ts/src/utils/signerDomain.d.ts +3 -0
- package/dist/types/shared-ts/src/utils/walletAuthAuthority.d.ts +32 -2
- package/dist/types/shared-ts/src/utils/walletAuthMethodRecord.d.ts +15 -1
- package/dist/types/shared-ts/src/utils/walletCapabilityBindings.d.ts +20 -0
- package/dist/types/wallet-server/src/authorization/voiceIdDeviceProof.d.ts +29 -0
- package/dist/types/wallet-server/src/core/d1WalletAuthMethodStore.d.ts +13 -2
- package/dist/types/wallet-server/src/core/registrationContracts.d.ts +8 -0
- package/dist/types/wallet-server/src/core/signingLanes/WalletExecutionLaneProjection.d.ts +4 -0
- package/dist/types/wallet-server/src/router/cloudflare/d1/passkeyCustody/d1PasskeyCustodyEnvelopeStore.d.ts +4 -0
- package/dist/types/wallet-server/src/router/cloudflare/d1/wallet/d1VoiceIdActivation.d.ts +14 -0
- package/dist/types/wallet-server/src/router/cloudflare/d1/wallet/d1VoiceIdSessions.d.ts +19 -0
- package/dist/types/wallet-server/src/router/cloudflare/d1/wallet/d1VoiceIdWalletSessionChallenges.d.ts +26 -0
- package/dist/types/wallet-server/src/router/cloudflare/d1/wallet/d1WalletAuthMethodService.d.ts +1 -0
- package/dist/types/wallet-server/src/router/cloudflare/d1/wallet/d1WalletRelocationOwner.d.ts +25 -0
- package/dist/types/wallet-server/src/router/domains/voiceId/voiceIdActivation.d.ts +29 -0
- package/dist/types/wallet-server/src/router/domains/voiceId/voiceIdSessions.d.ts +35 -0
- package/dist/types/wallet-server/src/router/framework/authServicePort.d.ts +3 -0
- package/dist/types/wallet-server/src/router/transport/fetch/routes/walletVoiceIdActivation.d.ts +12 -0
- package/dist/types/wallet-server/src/router/transport/fetch/routes/walletVoiceIdNear.d.ts +14 -0
- package/dist/types/wallet-server/src/router/transport/fetch/routes/walletVoiceIdSessions.d.ts +19 -0
- package/migrations/d1-signer/0083_voiceid_wallet_session_challenges.sql +29 -0
- package/migrations/d1-signer/0084_voiceid_wallet_auth_methods.sql +373 -0
- package/package.json +1 -1
|
@@ -9,7 +9,7 @@ import { executeSignedDelegateWithRelayer } from "../../../../delegateAction/ind
|
|
|
9
9
|
import { parseWalletUnlockIssuanceRejectionCode } from "../../../framework/authServicePort.js";
|
|
10
10
|
import { parseWebAuthnAuthenticationCredential } from "../../../auth/webAuthnCredentialCodecs.js";
|
|
11
11
|
import { resolveActiveOwnerWalletExecutionLane, resolveWalletAuthMethodIdForAuthority } from "../../../../core/signingLanes/WalletExecutionLaneProjection.js";
|
|
12
|
-
import { DEFAULT_WALLET_SESSION_TTL_MS } from "../../../../shared-ts/src/threshold/sessionPolicy.js";
|
|
12
|
+
import { DEFAULT_WALLET_SESSION_TTL_MS, LINKED_DEVICE_WALLET_SESSION_TTL_MS } from "../../../../shared-ts/src/threshold/sessionPolicy.js";
|
|
13
13
|
import { createDeviceLinkingOwnerRequestAuthenticatorV1 } from "../../../transport/fetch/routes/deviceLinkingOwnerAuthorization.js";
|
|
14
14
|
import { sha256BytesPortable, toRecordValue } from "./d1RouterApiAuthBoundary.js";
|
|
15
15
|
import { verifyD1LinkedDeviceFreshRevokeProofV1 } from "../wallet/d1WalletAuthMethodBoundary.js";
|
|
@@ -31,6 +31,11 @@ import { dispatchWalletRegistrationSetup } from "../../../framework/registration
|
|
|
31
31
|
import { readActiveWalletSessionCredential, readEd25519WalletSessionSigningRead, readExhaustedWalletSessionCredential, readWalletSessionSigningCandidate } from "./walletSessionCredentialReaders.js";
|
|
32
32
|
import { createD1AuthorizedOperationRouteService } from "./d1AuthorizedOperationRouteService.js";
|
|
33
33
|
import { parseRegistrationEstablishedSessionProjectionV2 } from "../../../../shared-ts/src/utils/registrationEstablishedSession.js";
|
|
34
|
+
import { D1WalletAuthMethodRevocationReplayStoreV1 } from "../wallet/d1WalletAuthMethodRevocationReplayStore.js";
|
|
35
|
+
import { CloudflareD1WalletAuthMethodService } from "../wallet/d1WalletAuthMethodService.js";
|
|
36
|
+
import { D1VoiceIdActivation } from "../wallet/d1VoiceIdActivation.js";
|
|
37
|
+
import { D1VoiceIdSessions } from "../wallet/d1VoiceIdSessions.js";
|
|
38
|
+
import { D1VoiceIdWalletSessionChallenges } from "../wallet/d1VoiceIdWalletSessionChallenges.js";
|
|
34
39
|
import { signGasRelayerNearTransactionWithDeps } from "../../../../core/authService/nearTransactions.js";
|
|
35
40
|
import { ensureSignerWasmRuntime } from "../../../../core/authService/wasm.js";
|
|
36
41
|
import { CloudflareD1PasskeyCustodyEnvelopeStore } from "../passkeyCustody/d1PasskeyCustodyEnvelopeStore.js";
|
|
@@ -49,8 +54,6 @@ import { CloudflareD1EmailOtpRecoveryService } from "../emailOtp/d1EmailOtpRecov
|
|
|
49
54
|
import { CloudflareD1GoogleEmailOtpSessionResolver } from "../emailOtp/d1GoogleEmailOtpSessionResolver.js";
|
|
50
55
|
import { CloudflareD1IdentityService } from "../identity/d1IdentityService.js";
|
|
51
56
|
import { CloudflareD1OidcVerificationService } from "../oidc/d1OidcVerificationService.js";
|
|
52
|
-
import { D1WalletAuthMethodRevocationReplayStoreV1 } from "../wallet/d1WalletAuthMethodRevocationReplayStore.js";
|
|
53
|
-
import { CloudflareD1WalletAuthMethodService } from "../wallet/d1WalletAuthMethodService.js";
|
|
54
57
|
import { parseWalletRegistrationSessionCommitReceiptV2 } from "../registration/walletRegistrationSessionCommitReceipt.js";
|
|
55
58
|
import { CloudflareD1WalletRegistrationService } from "../registration/d1WalletRegistrationService.js";
|
|
56
59
|
import { CloudflareD1WalletRegistrationCommitStore } from "../registration/d1WalletRegistrationCommitStore.js";
|
|
@@ -1054,7 +1057,14 @@ function createD1WalletRegistrationRouteService(assembly) {
|
|
|
1054
1057
|
};
|
|
1055
1058
|
}
|
|
1056
1059
|
function createD1WalletAuthMethodRouteService(assembly) {
|
|
1060
|
+
const voiceIdActivation = new D1VoiceIdActivation(assembly.options.database, {
|
|
1061
|
+
namespace: assembly.options.namespace,
|
|
1062
|
+
orgId: assembly.options.orgId,
|
|
1063
|
+
projectId: assembly.options.projectId,
|
|
1064
|
+
envId: assembly.options.envId
|
|
1065
|
+
}, assembly.walletAuthMethodStore, assembly.passkeyCustodyEnvelopes);
|
|
1057
1066
|
return {
|
|
1067
|
+
activateVoiceId: voiceIdActivation.activate.bind(voiceIdActivation),
|
|
1058
1068
|
resolveActiveWalletSessionAuthority: assembly.walletAuthMethods.resolveActiveWalletSessionAuthority.bind(assembly.walletAuthMethods),
|
|
1059
1069
|
revokeWalletAuthMethodWithFreshProof: assembly.walletAuthMethods.revokeWalletAuthMethodWithFreshProof.bind(assembly.walletAuthMethods),
|
|
1060
1070
|
verifyActivePasskeyAuthority: assembly.walletAuthMethods.verifyActivePasskeyAuthority.bind(assembly.walletAuthMethods),
|
|
@@ -1075,8 +1085,6 @@ function createD1WalletAuthMethodRouteService(assembly) {
|
|
|
1075
1085
|
startWalletAddSigner: assembly.walletAddSigners.startWalletAddSigner.bind(assembly.walletAddSigners)
|
|
1076
1086
|
};
|
|
1077
1087
|
}
|
|
1078
|
-
const LINKED_DEVICE_WALLET_SESSION_TTL_MS = 900 * 1e3;
|
|
1079
|
-
const LINKED_DEVICE_WALLET_SESSION_REMAINING_USES = 100;
|
|
1080
1088
|
async function resolveEmailOtpAuthorityForUnlock(input) {
|
|
1081
1089
|
try {
|
|
1082
1090
|
const enrollment = await input.emailOtpRecoveryService.readEmailOtpEnrollment({
|
|
@@ -1127,7 +1135,7 @@ async function issueWalletSessionForActiveAuthority(input) {
|
|
|
1127
1135
|
authority: input.resolved.authority,
|
|
1128
1136
|
walletAuthMethodId: input.resolved.authMethod.walletAuthMethodId,
|
|
1129
1137
|
mintId: mintId.value,
|
|
1130
|
-
remainingUses: deviceLinked ?
|
|
1138
|
+
remainingUses: deviceLinked ? 100 : 3,
|
|
1131
1139
|
issuedAtMs,
|
|
1132
1140
|
expiresAtMs: issuedAtMs + (deviceLinked ? LINKED_DEVICE_WALLET_SESSION_TTL_MS : DEFAULT_WALLET_SESSION_TTL_MS)
|
|
1133
1141
|
});
|
|
@@ -1195,7 +1203,16 @@ async function issueWalletSessionForEmailOtpUnlock(input) {
|
|
|
1195
1203
|
});
|
|
1196
1204
|
}
|
|
1197
1205
|
function createD1WalletUnlockRouteService(assembly) {
|
|
1206
|
+
const scope = {
|
|
1207
|
+
namespace: assembly.options.namespace,
|
|
1208
|
+
orgId: assembly.options.orgId,
|
|
1209
|
+
projectId: assembly.options.projectId,
|
|
1210
|
+
envId: assembly.options.envId
|
|
1211
|
+
};
|
|
1212
|
+
const voiceIdSessions = new D1VoiceIdSessions(assembly.walletAuthMethodStore, assembly.walletAuthorityStore, assembly.passkeyCustodyEnvelopes, new D1VoiceIdWalletSessionChallenges(assembly.options.database, scope), assembly.authorizationService, assembly.options.orgId);
|
|
1198
1213
|
return {
|
|
1214
|
+
issueVoiceIdChallenge: voiceIdSessions.issueChallenge.bind(voiceIdSessions),
|
|
1215
|
+
verifyAndIssueVoiceId: voiceIdSessions.verifyAndIssue.bind(voiceIdSessions),
|
|
1199
1216
|
createEmailOtpUnlockChallenge: assembly.emailOtpRecoveryService.createEmailOtpUnlockChallenge.bind(assembly.emailOtpRecoveryService),
|
|
1200
1217
|
createWebAuthnLoginOptions: assembly.webAuthnAuthService.createWebAuthnLoginOptions.bind(assembly.webAuthnAuthService),
|
|
1201
1218
|
markEmailOtpStrongAuthSatisfied: assembly.emailOtpRecoveryService.markEmailOtpStrongAuthSatisfied.bind(assembly.emailOtpRecoveryService),
|
package/dist/esm/router/cloudflare/d1/deviceLinking/d1LinkedDeviceAuthorityInstallService.js
CHANGED
|
@@ -217,7 +217,7 @@ var D1LinkedDeviceAuthorityInstallServiceV1 = class {
|
|
|
217
217
|
};
|
|
218
218
|
const authority = await this.options.authorityStore.readById(stored.authorityId);
|
|
219
219
|
const authMethod = await this.options.authMethodStore.readByIdV2({ walletAuthMethodId: stored.authMethodId });
|
|
220
|
-
if (!authority || !authMethod) return {
|
|
220
|
+
if (!authority || !authMethod || authMethod.kind === "voiceid") return {
|
|
221
221
|
kind: "integrity_error",
|
|
222
222
|
message: "pending authority records are missing"
|
|
223
223
|
};
|
|
@@ -1295,7 +1295,7 @@ function buildPendingAuthMethod(input, authorityId, nowMs) {
|
|
|
1295
1295
|
createdAtMs: draft.createdAtMs,
|
|
1296
1296
|
updatedAtMs: nowMs
|
|
1297
1297
|
});
|
|
1298
|
-
if (record.status !== "pending_local_install") throw new Error("auth method builder returned a non-pending record");
|
|
1298
|
+
if (record.status !== "pending_local_install" || record.kind !== "passkey") throw new Error("auth method builder returned a non-pending record");
|
|
1299
1299
|
return record;
|
|
1300
1300
|
}
|
|
1301
1301
|
const record = buildWalletAuthMethodRecordV2({
|
|
@@ -1310,7 +1310,7 @@ function buildPendingAuthMethod(input, authorityId, nowMs) {
|
|
|
1310
1310
|
createdAtMs: draft.createdAtMs,
|
|
1311
1311
|
updatedAtMs: nowMs
|
|
1312
1312
|
});
|
|
1313
|
-
if (record.status !== "pending_local_install") throw new Error("auth method builder returned a non-pending record");
|
|
1313
|
+
if (record.status !== "pending_local_install" || record.kind !== "email_otp") throw new Error("auth method builder returned a non-pending record");
|
|
1314
1314
|
return record;
|
|
1315
1315
|
}
|
|
1316
1316
|
function buildActivationSet(manifest, reservations) {
|
|
@@ -1399,7 +1399,7 @@ function buildActiveAuthMethod(pending, activatedAtMs) {
|
|
|
1399
1399
|
updatedAtMs: activatedAtMs,
|
|
1400
1400
|
activatedAtMs
|
|
1401
1401
|
});
|
|
1402
|
-
if (record.status !== "active") throw new Error("auth method builder returned a non-active record");
|
|
1402
|
+
if (record.status !== "active" || record.kind !== "passkey") throw new Error("auth method builder returned a non-active record");
|
|
1403
1403
|
return record;
|
|
1404
1404
|
}
|
|
1405
1405
|
const record = buildWalletAuthMethodRecordV2({
|
|
@@ -1415,7 +1415,7 @@ function buildActiveAuthMethod(pending, activatedAtMs) {
|
|
|
1415
1415
|
updatedAtMs: activatedAtMs,
|
|
1416
1416
|
activatedAtMs
|
|
1417
1417
|
});
|
|
1418
|
-
if (record.status !== "active") throw new Error("auth method builder returned a non-active record");
|
|
1418
|
+
if (record.status !== "active" || record.kind !== "email_otp") throw new Error("auth method builder returned a non-active record");
|
|
1419
1419
|
return record;
|
|
1420
1420
|
}
|
|
1421
1421
|
async function buildPasskeyCredentialPromotionStatements(input) {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { normalizeOptionalTrimmedString } from "../../../../shared-ts/src/utils/normalize.js";
|
|
2
2
|
import { secureRandomBase64Url } from "../../../../shared-ts/src/utils/secureRandomId.js";
|
|
3
3
|
import { d1MutationChanges } from "../auth/d1RouterApiAuthBoundary.js";
|
|
4
|
+
import { abandonedGoogleEmailOtpRegistrationAttemptRecord, parseGoogleEmailOtpRegistrationAttemptRow } from "./d1GoogleEmailOtpRegistrationRecords.js";
|
|
4
5
|
import { registrationAttemptMatchesReplacementScope, registrationAttemptMatchesStartedScope } from "../../../../core/EmailOtpRecords.js";
|
|
5
6
|
import { completeGoogleRegistration } from "./d1GoogleRegistrationCompletion.js";
|
|
6
7
|
import { emailOtpRegistrationAttemptRows } from "../../../../core/emailOtpD1Statements.js";
|
|
7
|
-
import { abandonedGoogleEmailOtpRegistrationAttemptRecord, parseGoogleEmailOtpRegistrationAttemptRow } from "./d1GoogleEmailOtpRegistrationRecords.js";
|
|
8
8
|
//#region src/router/cloudflare/d1/emailOtp/d1GoogleEmailOtpRegistrationAttemptStore.ts
|
|
9
9
|
var CloudflareD1GoogleEmailOtpRegistrationAttemptStore = class {
|
|
10
10
|
prepare;
|
|
@@ -43,6 +43,10 @@ function envelopeFactorRef(envelope) {
|
|
|
43
43
|
enrollmentId: envelope.factor.enrollmentId,
|
|
44
44
|
enrollmentSealKeyVersion: envelope.factor.enrollmentSealKeyVersion
|
|
45
45
|
};
|
|
46
|
+
case "voiceid": return {
|
|
47
|
+
kind: "voiceid",
|
|
48
|
+
deviceKeyId: envelope.factor.deviceKeyId
|
|
49
|
+
};
|
|
46
50
|
}
|
|
47
51
|
}
|
|
48
52
|
/**
|
|
@@ -62,6 +66,7 @@ function factorKeyPart(factor) {
|
|
|
62
66
|
factor.enrollmentId,
|
|
63
67
|
factor.enrollmentSealKeyVersion
|
|
64
68
|
];
|
|
69
|
+
case "voiceid": return ["voiceid", String(factor.deviceKeyId)];
|
|
65
70
|
}
|
|
66
71
|
}
|
|
67
72
|
function factorRefsMatch(left, right) {
|
|
@@ -73,6 +78,7 @@ function factorRefsMatch(left, right) {
|
|
|
73
78
|
case "email_otp":
|
|
74
79
|
if (right.kind !== "email_otp") return false;
|
|
75
80
|
return left.enrollmentId === right.enrollmentId && left.enrollmentSealKeyVersion === right.enrollmentSealKeyVersion;
|
|
81
|
+
case "voiceid": return right.kind === "voiceid" && left.deviceKeyId === right.deviceKeyId;
|
|
76
82
|
}
|
|
77
83
|
return false;
|
|
78
84
|
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { base64UrlDecode } from "../../../../shared-ts/src/utils/base64.js";
|
|
2
|
+
import { alphabetizeStringify } from "../../../../shared-ts/src/utils/digests.js";
|
|
3
|
+
import { sha256DigestB64u } from "../../../../shared-ts/src/utils/canonicalPrimitives.js";
|
|
4
|
+
import { isFullOwnerAuthorityV1 } from "./d1WalletAuthMethodService.js";
|
|
5
|
+
//#region src/router/cloudflare/d1/wallet/d1VoiceIdActivation.ts
|
|
6
|
+
function ownerSessionIsCurrent(source, nowMs) {
|
|
7
|
+
const session = source.authorization.session;
|
|
8
|
+
const quota = source.authorization.quota;
|
|
9
|
+
return source.authority.state === "active" && source.authMethod.status === "active" && source.authority.walletId === session.walletId && source.authority.authorityId === session.authorityId && source.authority.authorityDigestB64u === session.authorityDigestB64u && source.authority.revocationEpoch === session.authorityRevocationEpoch && source.authMethod.walletId === session.walletId && source.authMethod.walletAuthorityId === session.authorityId && source.authMethod.walletAuthMethodId === session.walletAuthMethodId && quota.walletSessionId === session.walletSessionId && quota.quotaId === session.quotaId && quota.tenantId === session.tenantId && quota.principalId === session.principalId && quota.remainingUses > 0 && session.createdAtMs <= nowMs && session.expiresAtMs > nowMs && quota.expiresAtMs > nowMs && isFullOwnerAuthorityV1(source.authority);
|
|
10
|
+
}
|
|
11
|
+
/** Aborts the D1 batch if the source Wallet Session lost its live quota or expired. */
|
|
12
|
+
function prepareOwnerSessionGuard(input) {
|
|
13
|
+
const session = input.source.authorization.session;
|
|
14
|
+
return input.database.prepare(`
|
|
15
|
+
INSERT INTO wallet_authority_cas_guard (guard_id)
|
|
16
|
+
SELECT 1 WHERE NOT EXISTS (
|
|
17
|
+
SELECT 1 FROM wallet_session_authorizations_v2 AS source_session
|
|
18
|
+
JOIN authorization_wallet_session_quotas AS quota
|
|
19
|
+
ON quota.namespace = source_session.namespace
|
|
20
|
+
AND quota.tenant_id = source_session.tenant_id
|
|
21
|
+
AND quota.quota_id = source_session.quota_id
|
|
22
|
+
AND quota.wallet_session_id = source_session.wallet_session_id
|
|
23
|
+
WHERE source_session.namespace = ? AND source_session.org_id = ?
|
|
24
|
+
AND source_session.project_id = ? AND source_session.env_id = ?
|
|
25
|
+
AND source_session.tenant_id = ? AND source_session.authorization_id = ?
|
|
26
|
+
AND source_session.wallet_id = ? AND source_session.authority_id = ?
|
|
27
|
+
AND source_session.wallet_auth_method_id = ?
|
|
28
|
+
AND source_session.authority_digest_b64u = ?
|
|
29
|
+
AND source_session.authority_revocation_epoch = ?
|
|
30
|
+
AND source_session.wallet_session_id = ? AND source_session.quota_id = ?
|
|
31
|
+
AND source_session.retired_at_ms IS NULL
|
|
32
|
+
AND source_session.issued_at_ms <= ? AND source_session.expires_at_ms > ?
|
|
33
|
+
AND quota.lifecycle_kind = 'active' AND quota.remaining_uses > 0
|
|
34
|
+
AND quota.expires_at_ms > ?
|
|
35
|
+
)
|
|
36
|
+
`).bind(input.scope.namespace, input.scope.orgId, input.scope.projectId, input.scope.envId, String(session.tenantId), String(session.authorizationId), String(session.walletId), String(session.authorityId), String(session.walletAuthMethodId), String(session.authorityDigestB64u), session.authorityRevocationEpoch, String(session.walletSessionId), String(session.quotaId), input.nowMs, input.nowMs, input.nowMs);
|
|
37
|
+
}
|
|
38
|
+
function buildActiveVoiceIdMethod(input) {
|
|
39
|
+
return {
|
|
40
|
+
version: "wallet_auth_method_v2",
|
|
41
|
+
walletAuthMethodId: input.walletAuthMethodId,
|
|
42
|
+
walletId: input.walletId,
|
|
43
|
+
walletAuthorityId: input.walletAuthorityId,
|
|
44
|
+
kind: "voiceid",
|
|
45
|
+
status: "active",
|
|
46
|
+
deviceKeyId: input.deviceKeyId,
|
|
47
|
+
devicePublicKeyB64u: input.devicePublicKeyB64u,
|
|
48
|
+
createdAtMs: input.nowMs,
|
|
49
|
+
updatedAtMs: input.nowMs,
|
|
50
|
+
activatedAtMs: input.nowMs
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
async function readCommittedActivation(input) {
|
|
54
|
+
const storedMethod = await input.authMethods.readByIdV2({ walletAuthMethodId: input.command.targetWalletAuthMethodId });
|
|
55
|
+
const session = input.command.source.authorization.session;
|
|
56
|
+
if (!storedMethod || storedMethod.kind !== "voiceid" || storedMethod.status !== "active" || storedMethod.walletId !== session.walletId || storedMethod.walletAuthorityId !== session.authorityId || storedMethod.deviceKeyId !== input.deviceKeyId || storedMethod.devicePublicKeyB64u !== input.command.devicePublicKeyB64u) return null;
|
|
57
|
+
const storedEnvelope = await input.envelopes.lookupEnvelopeForWalletAuthMethod({
|
|
58
|
+
walletId: session.walletId,
|
|
59
|
+
walletAuthMethodId: input.command.targetWalletAuthMethodId,
|
|
60
|
+
factor: {
|
|
61
|
+
kind: "voiceid",
|
|
62
|
+
deviceKeyId: input.deviceKeyId
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
if (storedEnvelope.kind !== "active" || alphabetizeStringify(storedEnvelope.envelope) !== alphabetizeStringify(input.expectedEnvelope)) return null;
|
|
66
|
+
return storedMethod;
|
|
67
|
+
}
|
|
68
|
+
var D1VoiceIdActivation = class {
|
|
69
|
+
constructor(database, scope, authMethods, envelopes) {
|
|
70
|
+
this.database = database;
|
|
71
|
+
this.scope = scope;
|
|
72
|
+
this.authMethods = authMethods;
|
|
73
|
+
this.envelopes = envelopes;
|
|
74
|
+
}
|
|
75
|
+
async activate(input) {
|
|
76
|
+
if (!Number.isSafeInteger(input.nowMs) || !ownerSessionIsCurrent(input.source, input.nowMs)) return {
|
|
77
|
+
kind: "rejected",
|
|
78
|
+
code: "invalid_source"
|
|
79
|
+
};
|
|
80
|
+
const session = input.source.authorization.session;
|
|
81
|
+
const deviceKeyId = await sha256DigestB64u(base64UrlDecode(input.devicePublicKeyB64u));
|
|
82
|
+
const ciphertextDigest = await sha256DigestB64u(base64UrlDecode(input.envelope.sealedCustodySecretB64u));
|
|
83
|
+
if (!String(input.targetWalletAuthMethodId).startsWith("wallet-auth-method:") || input.targetWalletAuthMethodId === input.source.authMethod.walletAuthMethodId || input.envelope.walletId !== session.walletId || input.envelope.ownership.walletAuthMethodId !== input.targetWalletAuthMethodId || input.envelope.factor.deviceKeyId !== deviceKeyId || input.envelope.createdAtMs > input.nowMs || input.envelope.lifecycle.activatedAtMs > input.nowMs || ciphertextDigest !== input.envelope.ciphertextDigestB64u) return {
|
|
84
|
+
kind: "rejected",
|
|
85
|
+
code: "invalid_target"
|
|
86
|
+
};
|
|
87
|
+
const authMethod = buildActiveVoiceIdMethod({
|
|
88
|
+
walletAuthMethodId: input.targetWalletAuthMethodId,
|
|
89
|
+
walletId: session.walletId,
|
|
90
|
+
walletAuthorityId: session.authorityId,
|
|
91
|
+
deviceKeyId,
|
|
92
|
+
devicePublicKeyB64u: input.devicePublicKeyB64u,
|
|
93
|
+
nowMs: input.nowMs
|
|
94
|
+
});
|
|
95
|
+
if ((await this.envelopes.linkWalletCustodyFactorAtomically({
|
|
96
|
+
envelope: input.envelope,
|
|
97
|
+
additionalStatements: [
|
|
98
|
+
prepareOwnerSessionGuard({
|
|
99
|
+
database: this.database,
|
|
100
|
+
scope: this.scope,
|
|
101
|
+
source: input.source,
|
|
102
|
+
nowMs: input.nowMs
|
|
103
|
+
}),
|
|
104
|
+
...this.authMethods.prepareActiveV2SourceGuardStatements({
|
|
105
|
+
walletId: session.walletId,
|
|
106
|
+
walletAuthMethodId: input.source.authMethod.walletAuthMethodId,
|
|
107
|
+
walletAuthorityId: session.authorityId,
|
|
108
|
+
authorityDigestB64u: session.authorityDigestB64u,
|
|
109
|
+
authorityRevocationEpoch: session.authorityRevocationEpoch
|
|
110
|
+
}),
|
|
111
|
+
...this.authMethods.prepareV2InsertStatements(authMethod)
|
|
112
|
+
]
|
|
113
|
+
})).kind !== "stored") {
|
|
114
|
+
const committed = await readCommittedActivation({
|
|
115
|
+
authMethods: this.authMethods,
|
|
116
|
+
envelopes: this.envelopes,
|
|
117
|
+
command: input,
|
|
118
|
+
deviceKeyId,
|
|
119
|
+
expectedEnvelope: input.envelope
|
|
120
|
+
});
|
|
121
|
+
if (committed) return {
|
|
122
|
+
kind: "activated",
|
|
123
|
+
authMethod: committed
|
|
124
|
+
};
|
|
125
|
+
return {
|
|
126
|
+
kind: "rejected",
|
|
127
|
+
code: "conflict"
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
kind: "activated",
|
|
132
|
+
authMethod
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
//#endregion
|
|
137
|
+
export { D1VoiceIdActivation };
|
|
138
|
+
|
|
139
|
+
//# sourceMappingURL=d1VoiceIdActivation.js.map
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { parsePrincipalId, parseTenantId } from "../../../../shared-ts/src/authorization/capabilityKinds.js";
|
|
2
|
+
import { verifyVoiceIdWalletSessionAssertion } from "../../../../authorization/voiceIdDeviceProof.js";
|
|
3
|
+
import { isVoiceIdSeedEnvelope } from "../../../../shared-ts/src/passkey-custody/custodyEnvelope.js";
|
|
4
|
+
import { DEFAULT_WALLET_SESSION_TTL_MS, LINKED_DEVICE_WALLET_SESSION_TTL_MS } from "../../../../shared-ts/src/threshold/sessionPolicy.js";
|
|
5
|
+
import { isFullOwnerAuthorityV1 } from "./d1WalletAuthMethodService.js";
|
|
6
|
+
//#region src/router/cloudflare/d1/wallet/d1VoiceIdSessions.ts
|
|
7
|
+
var D1VoiceIdSessions = class {
|
|
8
|
+
tenantId;
|
|
9
|
+
constructor(authMethods, authorities, envelopes, challenges, authorization, orgId) {
|
|
10
|
+
this.authMethods = authMethods;
|
|
11
|
+
this.authorities = authorities;
|
|
12
|
+
this.envelopes = envelopes;
|
|
13
|
+
this.challenges = challenges;
|
|
14
|
+
this.authorization = authorization;
|
|
15
|
+
const tenantId = parseTenantId(orgId);
|
|
16
|
+
if (!tenantId.ok) throw new Error(tenantId.error.message);
|
|
17
|
+
this.tenantId = tenantId.value;
|
|
18
|
+
}
|
|
19
|
+
async resolveActiveMethod(walletId, walletAuthMethodId) {
|
|
20
|
+
const method = await this.authMethods.readByIdV2({ walletAuthMethodId });
|
|
21
|
+
if (!method || method.kind !== "voiceid" || method.status !== "active" || method.walletId !== walletId) return null;
|
|
22
|
+
const authority = await this.authorities.readById(method.walletAuthorityId);
|
|
23
|
+
if (!authority || authority.state !== "active" || authority.walletId !== walletId || authority.authorityId !== method.walletAuthorityId || !isFullOwnerAuthorityV1(authority)) return null;
|
|
24
|
+
const custody = await this.envelopes.lookupEnvelopeForWalletAuthMethod({
|
|
25
|
+
walletId,
|
|
26
|
+
walletAuthMethodId,
|
|
27
|
+
factor: {
|
|
28
|
+
kind: "voiceid",
|
|
29
|
+
deviceKeyId: method.deviceKeyId
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
if (custody.kind !== "active" || !isVoiceIdSeedEnvelope(custody.envelope)) return null;
|
|
33
|
+
return {
|
|
34
|
+
authMethod: method,
|
|
35
|
+
authority,
|
|
36
|
+
registered: {
|
|
37
|
+
walletId,
|
|
38
|
+
walletAuthMethodId,
|
|
39
|
+
walletAuthorityId: method.walletAuthorityId,
|
|
40
|
+
deviceKeyId: method.deviceKeyId,
|
|
41
|
+
publicKeyB64u: method.devicePublicKeyB64u,
|
|
42
|
+
status: "active"
|
|
43
|
+
},
|
|
44
|
+
custodyEnvelope: custody.envelope
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
async issueChallenge(input) {
|
|
48
|
+
const active = await this.resolveActiveMethod(input.walletId, input.walletAuthMethodId);
|
|
49
|
+
if (!active) return {
|
|
50
|
+
kind: "rejected",
|
|
51
|
+
code: "method_unavailable"
|
|
52
|
+
};
|
|
53
|
+
return {
|
|
54
|
+
kind: "issued",
|
|
55
|
+
challenge: await this.challenges.issue({
|
|
56
|
+
registered: active.registered,
|
|
57
|
+
requestOrigin: input.requestOrigin,
|
|
58
|
+
audience: input.requestOrigin,
|
|
59
|
+
nowMs: input.nowMs
|
|
60
|
+
})
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
async verifyAndIssue(input) {
|
|
64
|
+
const supplied = input.assertion.challenge;
|
|
65
|
+
const expected = await this.challenges.read({
|
|
66
|
+
challengeId: supplied.challengeId,
|
|
67
|
+
requestOrigin: input.requestOrigin,
|
|
68
|
+
audience: input.requestOrigin,
|
|
69
|
+
nowMs: input.nowMs
|
|
70
|
+
});
|
|
71
|
+
if (!expected) return {
|
|
72
|
+
kind: "rejected",
|
|
73
|
+
code: "challenge_unavailable"
|
|
74
|
+
};
|
|
75
|
+
const active = await this.resolveActiveMethod(expected.walletId, expected.walletAuthMethodId);
|
|
76
|
+
if (!active) return {
|
|
77
|
+
kind: "rejected",
|
|
78
|
+
code: "method_unavailable"
|
|
79
|
+
};
|
|
80
|
+
let verified;
|
|
81
|
+
try {
|
|
82
|
+
verified = await verifyVoiceIdWalletSessionAssertion({
|
|
83
|
+
registered: active.registered,
|
|
84
|
+
expectedChallenge: expected,
|
|
85
|
+
assertion: input.assertion,
|
|
86
|
+
nowMs: input.nowMs,
|
|
87
|
+
consumeChallenge: this.challenges.consume.bind(this.challenges)
|
|
88
|
+
});
|
|
89
|
+
} catch {
|
|
90
|
+
return {
|
|
91
|
+
kind: "rejected",
|
|
92
|
+
code: "invalid_assertion"
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
const principalId = parsePrincipalId(String(active.authority.walletId));
|
|
96
|
+
if (!principalId.ok) return {
|
|
97
|
+
kind: "rejected",
|
|
98
|
+
code: "issuance_unavailable"
|
|
99
|
+
};
|
|
100
|
+
const deviceLinked = active.authority.provenance.kind === "device_link";
|
|
101
|
+
let issued;
|
|
102
|
+
try {
|
|
103
|
+
issued = await this.authorization.issueDirectWalletSessionAuthorizationV2({
|
|
104
|
+
tenantId: this.tenantId,
|
|
105
|
+
principalId: principalId.value,
|
|
106
|
+
walletId: active.authority.walletId,
|
|
107
|
+
authority: active.authority,
|
|
108
|
+
walletAuthMethodId: active.authMethod.walletAuthMethodId,
|
|
109
|
+
mintId: verified.challenge.challengeId,
|
|
110
|
+
remainingUses: deviceLinked ? 100 : 3,
|
|
111
|
+
issuedAtMs: input.nowMs,
|
|
112
|
+
expiresAtMs: input.nowMs + (deviceLinked ? LINKED_DEVICE_WALLET_SESSION_TTL_MS : DEFAULT_WALLET_SESSION_TTL_MS)
|
|
113
|
+
});
|
|
114
|
+
} catch {
|
|
115
|
+
return {
|
|
116
|
+
kind: "rejected",
|
|
117
|
+
code: "issuance_unavailable"
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
if (issued.kind !== "issued") return {
|
|
121
|
+
kind: "rejected",
|
|
122
|
+
code: "issuance_unavailable"
|
|
123
|
+
};
|
|
124
|
+
return {
|
|
125
|
+
kind: "issued",
|
|
126
|
+
authorization: {
|
|
127
|
+
session: issued.session,
|
|
128
|
+
quota: issued.quota
|
|
129
|
+
},
|
|
130
|
+
operationCredential: issued.operationCredential,
|
|
131
|
+
custodyEnvelope: active.custodyEnvelope
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
//#endregion
|
|
136
|
+
export { D1VoiceIdSessions };
|
|
137
|
+
|
|
138
|
+
//# sourceMappingURL=d1VoiceIdSessions.js.map
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { parseWalletSessionMintId } from "../../../../shared-ts/src/authorization/capabilityKinds.js";
|
|
2
|
+
import { alphabetizeStringify } from "../../../../shared-ts/src/utils/digests.js";
|
|
3
|
+
import { parseDigestB64u } from "../../../../shared-ts/src/utils/canonicalPrimitives.js";
|
|
4
|
+
import { buildVoiceIdWalletSessionChallenge, parseVoiceIdWalletSessionChallenge } from "../../../../shared-ts/src/authorization/voiceIdDeviceAssertion.js";
|
|
5
|
+
import { secureRandomBase64Url } from "../../../../shared-ts/src/utils/secureRandomId.js";
|
|
6
|
+
import { d1ChangedRows, parseD1JsonColumn } from "../../../../storage/d1Sql.js";
|
|
7
|
+
//#region src/router/cloudflare/d1/wallet/d1VoiceIdWalletSessionChallenges.ts
|
|
8
|
+
const CHALLENGE_LIFETIME_MS = 6e4;
|
|
9
|
+
const EXPIRY_CLEANUP_LIMIT = 64;
|
|
10
|
+
var D1VoiceIdWalletSessionChallenges = class {
|
|
11
|
+
constructor(database, scope) {
|
|
12
|
+
this.database = database;
|
|
13
|
+
this.scope = scope;
|
|
14
|
+
}
|
|
15
|
+
async issue(input) {
|
|
16
|
+
requireTime(input.nowMs);
|
|
17
|
+
const expiresAtMs = input.nowMs + CHALLENGE_LIFETIME_MS;
|
|
18
|
+
requireTime(expiresAtMs);
|
|
19
|
+
const challengeId = parseWalletSessionMintId(`voiceid:${secureRandomBase64Url(32, "VoiceID Wallet Session challenge")}`);
|
|
20
|
+
if (!challengeId.ok) throw new Error(challengeId.error.message);
|
|
21
|
+
const challenge = buildVoiceIdWalletSessionChallenge({
|
|
22
|
+
walletId: input.registered.walletId,
|
|
23
|
+
walletAuthMethodId: input.registered.walletAuthMethodId,
|
|
24
|
+
walletAuthorityId: input.registered.walletAuthorityId,
|
|
25
|
+
deviceKeyId: input.registered.deviceKeyId,
|
|
26
|
+
challengeId: challengeId.value,
|
|
27
|
+
nonce: parseDigestB64u(secureRandomBase64Url(32, "VoiceID Wallet Session nonce")),
|
|
28
|
+
requestOrigin: input.requestOrigin,
|
|
29
|
+
audience: input.audience,
|
|
30
|
+
issuedAtMs: input.nowMs,
|
|
31
|
+
expiresAtMs
|
|
32
|
+
});
|
|
33
|
+
await this.pruneExpired(input.nowMs);
|
|
34
|
+
if (d1ChangedRows(await this.database.prepare(`INSERT INTO voiceid_wallet_session_challenges (
|
|
35
|
+
namespace, org_id, project_id, env_id, challenge_id, wallet_id,
|
|
36
|
+
wallet_auth_method_id, challenge_json, issued_at_ms, expires_at_ms
|
|
37
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).bind(...this.scopeValues(), String(challenge.challengeId), String(challenge.walletId), String(challenge.walletAuthMethodId), alphabetizeStringify(challenge), challenge.issuedAtMs, challenge.expiresAtMs).run()) !== 1) throw new Error("VoiceID challenge was not stored");
|
|
38
|
+
return challenge;
|
|
39
|
+
}
|
|
40
|
+
async read(input) {
|
|
41
|
+
requireTime(input.nowMs);
|
|
42
|
+
const row = await this.database.prepare(`SELECT challenge_json FROM voiceid_wallet_session_challenges
|
|
43
|
+
WHERE namespace = ? AND org_id = ? AND project_id = ? AND env_id = ?
|
|
44
|
+
AND challenge_id = ? AND consumed_at_ms IS NULL
|
|
45
|
+
AND issued_at_ms <= ? AND expires_at_ms > ?
|
|
46
|
+
LIMIT 1`).bind(...this.scopeValues(), String(input.challengeId), input.nowMs, input.nowMs).first();
|
|
47
|
+
if (!row) return null;
|
|
48
|
+
const challenge = parseVoiceIdWalletSessionChallenge(parseD1JsonColumn(row.challenge_json));
|
|
49
|
+
if (challenge.challengeId !== input.challengeId) throw new Error("Stored VoiceID challenge identity changed");
|
|
50
|
+
if (challenge.requestOrigin !== input.requestOrigin || challenge.audience !== input.audience) return null;
|
|
51
|
+
return challenge;
|
|
52
|
+
}
|
|
53
|
+
async consume(challenge, nowMs) {
|
|
54
|
+
requireTime(nowMs);
|
|
55
|
+
const parsed = parseVoiceIdWalletSessionChallenge(challenge);
|
|
56
|
+
if (nowMs < parsed.issuedAtMs || nowMs >= parsed.expiresAtMs) return false;
|
|
57
|
+
return d1ChangedRows(await this.database.prepare(`UPDATE voiceid_wallet_session_challenges SET consumed_at_ms = ?
|
|
58
|
+
WHERE namespace = ? AND org_id = ? AND project_id = ? AND env_id = ?
|
|
59
|
+
AND challenge_id = ? AND wallet_id = ? AND wallet_auth_method_id = ?
|
|
60
|
+
AND challenge_json = ? AND consumed_at_ms IS NULL
|
|
61
|
+
AND issued_at_ms <= ? AND expires_at_ms > ?`).bind(nowMs, ...this.scopeValues(), String(parsed.challengeId), String(parsed.walletId), String(parsed.walletAuthMethodId), alphabetizeStringify(parsed), nowMs, nowMs).run()) === 1;
|
|
62
|
+
}
|
|
63
|
+
async pruneExpired(nowMs) {
|
|
64
|
+
await this.database.prepare(`WITH expired AS (
|
|
65
|
+
SELECT rowid FROM voiceid_wallet_session_challenges
|
|
66
|
+
WHERE namespace = ? AND org_id = ? AND project_id = ? AND env_id = ?
|
|
67
|
+
AND expires_at_ms <= ?
|
|
68
|
+
ORDER BY expires_at_ms ASC, challenge_id ASC
|
|
69
|
+
LIMIT ?
|
|
70
|
+
) DELETE FROM voiceid_wallet_session_challenges
|
|
71
|
+
WHERE rowid IN (SELECT rowid FROM expired)`).bind(...this.scopeValues(), nowMs, EXPIRY_CLEANUP_LIMIT).run();
|
|
72
|
+
}
|
|
73
|
+
scopeValues() {
|
|
74
|
+
return [
|
|
75
|
+
this.scope.namespace,
|
|
76
|
+
this.scope.orgId,
|
|
77
|
+
this.scope.projectId,
|
|
78
|
+
this.scope.envId
|
|
79
|
+
];
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
function requireTime(value) {
|
|
83
|
+
if (!Number.isSafeInteger(value) || value <= 0) throw new Error("VoiceID challenge time is invalid");
|
|
84
|
+
}
|
|
85
|
+
//#endregion
|
|
86
|
+
export { D1VoiceIdWalletSessionChallenges };
|
|
87
|
+
|
|
88
|
+
//# sourceMappingURL=d1VoiceIdWalletSessionChallenges.js.map
|
|
@@ -22,9 +22,9 @@ import { resolveWalletAuthMethodIdForAuthority } from "../../../../core/signingL
|
|
|
22
22
|
import { toRecordValue } from "../auth/d1RouterApiAuthBoundary.js";
|
|
23
23
|
import { d1HostIsWithinWebAuthnRpId, resolveD1AddAuthMethodExistingAuth, resolveD1AddSignerExistingAuth, verifyD1LinkedDeviceFreshRevokeProofV1, walletAuthAuthorityFromRegistrationAuthority } from "./d1WalletAuthMethodBoundary.js";
|
|
24
24
|
import { parseWalletIdForIntent } from "../registration/d1RegistrationCeremonyRecords.js";
|
|
25
|
-
import { runtimePolicyScopeKey } from "../../../../core/EmailOtpRecords.js";
|
|
26
|
-
import { expiredGoogleEmailOtpRegistrationAttemptRecord } from "../emailOtp/d1GoogleEmailOtpRegistrationRecords.js";
|
|
27
25
|
import { admitAddWalletAuthMethod } from "../../../../shared-ts/src/utils/addWalletAuthMethod.js";
|
|
26
|
+
import { expiredGoogleEmailOtpRegistrationAttemptRecord } from "../emailOtp/d1GoogleEmailOtpRegistrationRecords.js";
|
|
27
|
+
import { runtimePolicyScopeKey } from "../../../../core/EmailOtpRecords.js";
|
|
28
28
|
import { computeWalletAuthMethodRevocationProofDigestV1 } from "./d1WalletAuthMethodRevocationReplayStore.js";
|
|
29
29
|
//#region src/router/cloudflare/d1/wallet/d1WalletAuthMethodService.ts
|
|
30
30
|
/** Long enough for a device to retry across a reload, short of ceremony scale. */
|
|
@@ -141,6 +141,14 @@ function walletAuthMethodRevokedResponse(walletId, record) {
|
|
|
141
141
|
status: "revoked"
|
|
142
142
|
}
|
|
143
143
|
};
|
|
144
|
+
case "voiceid": return {
|
|
145
|
+
ok: true,
|
|
146
|
+
walletId,
|
|
147
|
+
authMethod: {
|
|
148
|
+
kind: "voiceid",
|
|
149
|
+
status: "revoked"
|
|
150
|
+
}
|
|
151
|
+
};
|
|
144
152
|
default: return unreachableRevokedMethodKind(record);
|
|
145
153
|
}
|
|
146
154
|
}
|
|
@@ -506,7 +514,7 @@ var CloudflareD1WalletAuthMethodService = class {
|
|
|
506
514
|
walletId,
|
|
507
515
|
auth: storedAuth.auth
|
|
508
516
|
});
|
|
509
|
-
if (!sourceMethod) return {
|
|
517
|
+
if (!sourceMethod || sourceMethod.kind === "voiceid") return {
|
|
510
518
|
ok: false,
|
|
511
519
|
code: "unauthorized",
|
|
512
520
|
message: "Add-auth-method source is not an active V2 wallet auth method"
|
|
@@ -2114,6 +2122,6 @@ var CloudflareD1WalletAuthMethodService = class {
|
|
|
2114
2122
|
}
|
|
2115
2123
|
};
|
|
2116
2124
|
//#endregion
|
|
2117
|
-
export { CloudflareD1WalletAuthMethodService };
|
|
2125
|
+
export { CloudflareD1WalletAuthMethodService, isFullOwnerAuthorityV1 };
|
|
2118
2126
|
|
|
2119
2127
|
//# sourceMappingURL=d1WalletAuthMethodService.js.map
|
|
@@ -724,6 +724,20 @@ function buildRevokedAuthMethod(method, revokedAtMs) {
|
|
|
724
724
|
activatedAtMs: method.activatedAtMs,
|
|
725
725
|
revokedAtMs
|
|
726
726
|
};
|
|
727
|
+
if (method.kind === "voiceid") return {
|
|
728
|
+
version: "wallet_auth_method_v2",
|
|
729
|
+
walletAuthMethodId: method.walletAuthMethodId,
|
|
730
|
+
walletId: method.walletId,
|
|
731
|
+
walletAuthorityId: method.walletAuthorityId,
|
|
732
|
+
kind: "voiceid",
|
|
733
|
+
status: "revoked",
|
|
734
|
+
deviceKeyId: method.deviceKeyId,
|
|
735
|
+
devicePublicKeyB64u: method.devicePublicKeyB64u,
|
|
736
|
+
createdAtMs: method.createdAtMs,
|
|
737
|
+
updatedAtMs: revokedAtMs,
|
|
738
|
+
activatedAtMs: method.activatedAtMs,
|
|
739
|
+
revokedAtMs
|
|
740
|
+
};
|
|
727
741
|
return {
|
|
728
742
|
version: "wallet_auth_method_v2",
|
|
729
743
|
walletAuthMethodId: method.walletAuthMethodId,
|
|
@@ -243,6 +243,7 @@ function buildWebAuthnRecoveryContinuityEnvelopeAnchor(envelope) {
|
|
|
243
243
|
updatedAtMs: envelope.updatedAtMs,
|
|
244
244
|
bindingKind: "wallet_custody_seed_v1"
|
|
245
245
|
};
|
|
246
|
+
case "voiceid": throw new Error("WebAuthn recovery continuity does not accept a VoiceID envelope");
|
|
246
247
|
}
|
|
247
248
|
}
|
|
248
249
|
function parseWebAuthnRecoveryContinuityEnvelopeAnchor(input) {
|