@seams/wallet-server 0.54.0 → 0.56.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 +4 -4
- package/artifact-manifest.json +11 -11
- 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/cloud-host.js +2 -2
- package/dist/esm/router/cloudflare/d1/auth/d1RouterApiAuthService.js +1 -1
- package/dist/esm/router/cloudflare/d1/deviceLinking/d1LinkedDeviceAuthorityInstallService.js +4 -2
- package/dist/esm/router/cloudflare/d1/deviceLinking/d1LinkedDeviceRouteService.js +1 -1
- package/dist/esm/router/cloudflare/d1/deviceLinking/d1LinkedDeviceTargetCredentialProvider.js +1 -1
- package/dist/esm/router/cloudflare/d1/deviceLinking/index.js +2 -2
- package/dist/esm/router/cloudflare/d1/registration/d1WalletExecutionAuthority.js +6 -13
- package/dist/esm/router/domains/speakToSign/speakToSignVerify.js +48 -22
- package/dist/types/wallet-server/src/router/cloudflare/d1/deviceLinking/d1LinkedDeviceAuthorityInstallService.d.ts +1 -1
- package/dist/types/wallet-server/src/router/cloudflare/d1/registration/d1WalletExecutionAuthority.d.ts +2 -2
- package/dist/types/wallet-server/src/router/domains/speakToSign/speakToSignVerify.d.ts +6 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,17 +4,17 @@ This package owns the reusable signer server surface: route adapters,
|
|
|
4
4
|
WebAuthn verification policy, threshold signing services, signer storage
|
|
5
5
|
adapters, and server wasm bindings.
|
|
6
6
|
|
|
7
|
-
The package version is `@seams/wallet-server@0.
|
|
8
|
-
`@seams/wallet@0.
|
|
7
|
+
The package version is `@seams/wallet-server@0.56.0`. Pair it with
|
|
8
|
+
`@seams/wallet@0.56.0` and matching hosted assets. Use `@seams/wallet-cli@0.29.0`.
|
|
9
9
|
The wallet iframe protocol is **23.0.0**.
|
|
10
10
|
|
|
11
11
|
```sh
|
|
12
|
-
pnpm add @seams/wallet-server@0.
|
|
12
|
+
pnpm add @seams/wallet-server@0.56.0
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
## Google BYO authentication
|
|
16
16
|
|
|
17
|
-
Version 0.
|
|
17
|
+
Version 0.56.0 verifies a configured application's signed session assertion or the app's Google ID token or exchanges it for a browser-bound handoff.
|
|
18
18
|
An environment-specific Google client configuration and an owner-approved wallet association are required.
|
|
19
19
|
Apply the packaged signer migrations before enabling the feature.
|
|
20
20
|
External sessions grant settings reads and remain separate from owner and signing credentials.
|
package/artifact-manifest.json
CHANGED
|
@@ -3,17 +3,17 @@
|
|
|
3
3
|
"walletSecurityPolicy": "scoped_seals_and_owner_proofs_v1",
|
|
4
4
|
"package": {
|
|
5
5
|
"name": "@seams/wallet-server",
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.56.0"
|
|
7
7
|
},
|
|
8
8
|
"workers": {
|
|
9
9
|
"router": {
|
|
10
10
|
"entry": "cloudflare-router-ab/build/router/worker/shim.mjs",
|
|
11
|
-
"fingerprint": "
|
|
11
|
+
"fingerprint": "2bce0668c0ea0ebe987780665679d355755c54dba199037d2888b2f45618bd9f",
|
|
12
12
|
"files": [
|
|
13
13
|
{
|
|
14
14
|
"path": "cloudflare-router-ab/build/router/build-stamp.json",
|
|
15
15
|
"bytes": 119,
|
|
16
|
-
"sha256": "
|
|
16
|
+
"sha256": "e52ab10f7f5ccbc3489faa7dedbe99411a921f3a337c02a9dbc6157c270b5837"
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
"path": "cloudflare-router-ab/build/router/index.js",
|
|
@@ -39,12 +39,12 @@
|
|
|
39
39
|
},
|
|
40
40
|
"deriver-a": {
|
|
41
41
|
"entry": "cloudflare-router-ab/build/deriver-a/worker/shim.mjs",
|
|
42
|
-
"fingerprint": "
|
|
42
|
+
"fingerprint": "64b21a5543d049faf93a8c811df01b3189f237a78525d794ab8fed4d363d5c77",
|
|
43
43
|
"files": [
|
|
44
44
|
{
|
|
45
45
|
"path": "cloudflare-router-ab/build/deriver-a/build-stamp.json",
|
|
46
46
|
"bytes": 122,
|
|
47
|
-
"sha256": "
|
|
47
|
+
"sha256": "12a004595c4e75fc0c60eb4a86c1d097b60219a12a87e72629fec88de3c5c9aa"
|
|
48
48
|
},
|
|
49
49
|
{
|
|
50
50
|
"path": "cloudflare-router-ab/build/deriver-a/index.js",
|
|
@@ -70,12 +70,12 @@
|
|
|
70
70
|
},
|
|
71
71
|
"deriver-b": {
|
|
72
72
|
"entry": "cloudflare-router-ab/build/deriver-b/worker/shim.mjs",
|
|
73
|
-
"fingerprint": "
|
|
73
|
+
"fingerprint": "c5176d3f8dccc72bb1dc577d975470b4213f1a72ded5e66bab8ff7f34c190724",
|
|
74
74
|
"files": [
|
|
75
75
|
{
|
|
76
76
|
"path": "cloudflare-router-ab/build/deriver-b/build-stamp.json",
|
|
77
77
|
"bytes": 122,
|
|
78
|
-
"sha256": "
|
|
78
|
+
"sha256": "740681c46eb3400e69130059ef4a9423e9e06b3da129e8b834e0b82227003861"
|
|
79
79
|
},
|
|
80
80
|
{
|
|
81
81
|
"path": "cloudflare-router-ab/build/deriver-b/index.js",
|
|
@@ -101,12 +101,12 @@
|
|
|
101
101
|
},
|
|
102
102
|
"signing-worker": {
|
|
103
103
|
"entry": "cloudflare-router-ab/build/signing-worker/worker/shim.mjs",
|
|
104
|
-
"fingerprint": "
|
|
104
|
+
"fingerprint": "4ea33a63250bfbf073102ceafb22193d3f300a562bba4537d067ef2a8de07b44",
|
|
105
105
|
"files": [
|
|
106
106
|
{
|
|
107
107
|
"path": "cloudflare-router-ab/build/signing-worker/build-stamp.json",
|
|
108
108
|
"bytes": 127,
|
|
109
|
-
"sha256": "
|
|
109
|
+
"sha256": "b0dacdc6a53ebd644b08f9f186ba0eed59a3a4a5f3a14936fc2197138c13e20a"
|
|
110
110
|
},
|
|
111
111
|
{
|
|
112
112
|
"path": "cloudflare-router-ab/build/signing-worker/index.js",
|
|
@@ -132,12 +132,12 @@
|
|
|
132
132
|
},
|
|
133
133
|
"tenant-root-control-plane": {
|
|
134
134
|
"entry": "cloudflare-router-ab/build/tenant-root-control-plane/worker/shim.mjs",
|
|
135
|
-
"fingerprint": "
|
|
135
|
+
"fingerprint": "e79512250e1c788de74dcb29fdb56c004ab511ad58b41be8bc7af14bc777ad2a",
|
|
136
136
|
"files": [
|
|
137
137
|
{
|
|
138
138
|
"path": "cloudflare-router-ab/build/tenant-root-control-plane/build-stamp.json",
|
|
139
139
|
"bytes": 139,
|
|
140
|
-
"sha256": "
|
|
140
|
+
"sha256": "c375a2555ade8c3bff20b13436430e608bfdc2388e709f4e02f959722723c679"
|
|
141
141
|
},
|
|
142
142
|
{
|
|
143
143
|
"path": "cloudflare-router-ab/build/tenant-root-control-plane/index.js",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"profile":"release","wallet_objects":true,"features":"strict-worker-deriver-a-entrypoint","started_at_ms":
|
|
1
|
+
{"profile":"release","wallet_objects":true,"features":"strict-worker-deriver-a-entrypoint","started_at_ms":1794146490393}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"profile":"release","wallet_objects":true,"features":"strict-worker-deriver-b-entrypoint","started_at_ms":
|
|
1
|
+
{"profile":"release","wallet_objects":true,"features":"strict-worker-deriver-b-entrypoint","started_at_ms":1794146682144}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"profile":"release","wallet_objects":true,"features":"strict-worker-router-entrypoint","started_at_ms":
|
|
1
|
+
{"profile":"release","wallet_objects":true,"features":"strict-worker-router-entrypoint","started_at_ms":1794146860888}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"profile":"release","wallet_objects":true,"features":"strict-worker-signing-worker-entrypoint","started_at_ms":
|
|
1
|
+
{"profile":"release","wallet_objects":true,"features":"strict-worker-signing-worker-entrypoint","started_at_ms":1794146191911}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"profile":"release","wallet_objects":false,"features":"strict-worker-tenant-root-control-plane-entrypoint","started_at_ms":
|
|
1
|
+
{"profile":"release","wallet_objects":false,"features":"strict-worker-tenant-root-control-plane-entrypoint","started_at_ms":1794147071629}
|
package/dist/esm/cloud-host.js
CHANGED
|
@@ -77,6 +77,8 @@ import { CloudflareD1WebAuthnStore, prepareD1WebAuthnAuthenticatorInsertStatemen
|
|
|
77
77
|
import { CloudflareD1WebAuthnAuthService } from "./router/cloudflare/d1/webauthn/d1WebAuthnAuthService.js";
|
|
78
78
|
import { admitD1EstablishedWalletHome, resumeRegistrationDirectoryPublicationsV1 } from "./router/cloudflare/d1/registration/d1WalletExecutionAuthority.js";
|
|
79
79
|
import { parseLinkedDeviceSessionRecordV1 } from "./core/deviceLinking/linkedDeviceSessionRecord.js";
|
|
80
|
+
import { parseD1LinkedDeviceSessionRowV1, parseD1LinkedDeviceSessionTranscriptRowV1 } from "./router/cloudflare/d1/deviceLinking/d1LinkedDeviceSessionRecords.js";
|
|
81
|
+
import { D1LinkedDeviceSessionStoreV1, SESSION_CAS_GUARD_SQL } from "./router/cloudflare/d1/deviceLinking/d1LinkedDeviceSessionStore.js";
|
|
80
82
|
import { D1LinkedDeviceSourceContributionPreparationPlannerV1, createD1LinkedDeviceSourceContributionPreparationPlannerV1, linkedDeviceX25519RecipientPublicKeyB64uV1 } from "./router/cloudflare/d1/deviceLinking/d1LinkedDeviceSourceContributionPreparationPlanner.js";
|
|
81
83
|
import { D1LinkedDeviceAuthorityInstallServiceV1, parseInstalledEcdsaCustodySnapshotV1, readD1LinkedDeviceEcdsaSourceV1, readD1LinkedDeviceEd25519SourceV1 } from "./router/cloudflare/d1/deviceLinking/d1LinkedDeviceAuthorityInstallService.js";
|
|
82
84
|
import { CloudflareD1AuthorizationStore } from "./router/cloudflare/d1/authorization/d1AuthorizationStore.js";
|
|
@@ -113,8 +115,6 @@ import { D1IdentityStore } from "./core/d1IdentityStore.js";
|
|
|
113
115
|
import { D1LinkedDeviceEd25519ExportRootStoreV1 } from "./router/cloudflare/d1/deviceLinking/d1LinkedDeviceEd25519ExportRootStore.js";
|
|
114
116
|
import { D1LinkedDeviceRequestProofNonceStoreV1 } from "./router/cloudflare/d1/deviceLinking/d1LinkedDeviceRequestProofNonceStore.js";
|
|
115
117
|
import { createD1LinkedDeviceSessionServiceV1 } from "./router/cloudflare/d1/deviceLinking/d1LinkedDeviceSessionService.js";
|
|
116
|
-
import { parseD1LinkedDeviceSessionRowV1, parseD1LinkedDeviceSessionTranscriptRowV1 } from "./router/cloudflare/d1/deviceLinking/d1LinkedDeviceSessionRecords.js";
|
|
117
|
-
import { D1LinkedDeviceSessionStoreV1, SESSION_CAS_GUARD_SQL } from "./router/cloudflare/d1/deviceLinking/d1LinkedDeviceSessionStore.js";
|
|
118
118
|
import { createD1LinkedDeviceRouteServiceV1 } from "./router/cloudflare/d1/deviceLinking/d1LinkedDeviceRouteService.js";
|
|
119
119
|
import { LinkedDeviceSourceFamilyUnavailableErrorV1, createD1LinkedDeviceVerifiedLinkSourceReaderV1 } from "./router/cloudflare/d1/deviceLinking/d1LinkedDeviceVerifiedLinkSourceReader.js";
|
|
120
120
|
import { buildVerifiedLinkInputV1, buildVerifiedTargetFactorV1, computeVerifiedTargetFactorVerificationDigestV1, linkedDeviceSignerManifestsEqualV1 } from "./router/cloudflare/d1/deviceLinking/d1LinkedDeviceVerifiedLinkBuilder.js";
|
|
@@ -16,6 +16,7 @@ import { resolveActiveOwnerWalletExecutionLane, resolveWalletAuthMethodIdForAuth
|
|
|
16
16
|
import { DEFAULT_WALLET_SESSION_TTL_MS, LINKED_DEVICE_WALLET_SESSION_TTL_MS } from "../../../../shared-ts/src/threshold/sessionPolicy.js";
|
|
17
17
|
import { createDeviceLinkingOwnerRequestAuthenticatorV1 } from "../../../transport/fetch/routes/deviceLinkingOwnerAuthorization.js";
|
|
18
18
|
import { verifyD1LinkedDeviceFreshRevokeProofV1 } from "../wallet/d1WalletAuthMethodBoundary.js";
|
|
19
|
+
import { D1LinkedDeviceSessionStoreV1 } from "../deviceLinking/d1LinkedDeviceSessionStore.js";
|
|
19
20
|
import { D1LinkedDeviceAuthorityInstallServiceV1 } from "../deviceLinking/d1LinkedDeviceAuthorityInstallService.js";
|
|
20
21
|
import { D1WalletAuthorityStore } from "../wallet/d1WalletAuthorityStore.js";
|
|
21
22
|
import { createD1Ed25519YaoLifecycleRecords } from "../ed25519Yao/d1Ed25519YaoProductRegistrationPartitionedStateStore.js";
|
|
@@ -68,7 +69,6 @@ import { CloudflareD1WalletRecoveryGoogleEmailOtpService } from "../passkeyCusto
|
|
|
68
69
|
import { CloudflareD1WalletAddSignerService, parseD1WalletAddSignerStartSideEffectRecord } from "../wallet/d1WalletAddSignerService.js";
|
|
69
70
|
import { CloudflareD1RegistrationIntentService } from "../registration/d1RegistrationIntentService.js";
|
|
70
71
|
import { createD1LinkedDeviceSessionServiceV1 } from "../deviceLinking/d1LinkedDeviceSessionService.js";
|
|
71
|
-
import { D1LinkedDeviceSessionStoreV1 } from "../deviceLinking/d1LinkedDeviceSessionStore.js";
|
|
72
72
|
import { createD1LinkedDeviceRouteServiceV1 } from "../deviceLinking/d1LinkedDeviceRouteService.js";
|
|
73
73
|
import { D1LinkedDeviceEmailOtpGrantStoreV1 } from "../deviceLinking/d1LinkedDeviceEmailOtpGrantStore.js";
|
|
74
74
|
import { createD1LinkedDeviceManagementServiceV1 } from "../deviceLinking/d1LinkedDeviceManagementService.js";
|
package/dist/esm/router/cloudflare/d1/deviceLinking/d1LinkedDeviceAuthorityInstallService.js
CHANGED
|
@@ -28,6 +28,7 @@ import { prepareD1WebAuthnAuthenticatorInsertStatement } from "../webauthn/d1Web
|
|
|
28
28
|
import { admitD1EstablishedWalletHome } from "../registration/d1WalletExecutionAuthority.js";
|
|
29
29
|
import { findInstalledEcdsaAuthority, installedEcdsaAuthorityChain, projectInstalledEcdsaAuthority } from "./installedEcdsaAuthority.js";
|
|
30
30
|
import { buildAuthorityActiveSessionRecordV1, buildAuthorityPendingLocalInstallSessionRecordV1 } from "../../../../core/deviceLinking/linkedDeviceSessionRecord.js";
|
|
31
|
+
import { SESSION_CAS_GUARD_SQL } from "./d1LinkedDeviceSessionStore.js";
|
|
31
32
|
import { linkedDeviceX25519RecipientPublicKeyB64uV1 } from "./d1LinkedDeviceSourceContributionPreparationPlanner.js";
|
|
32
33
|
//#region src/router/cloudflare/d1/deviceLinking/d1LinkedDeviceAuthorityInstallService.ts
|
|
33
34
|
var LinkedDeviceActivationIntegrityErrorV1 = class extends Error {
|
|
@@ -580,9 +581,10 @@ var D1LinkedDeviceAuthorityInstallServiceV1 = class {
|
|
|
580
581
|
});
|
|
581
582
|
}
|
|
582
583
|
async commitAcknowledgementCleanupV1(input) {
|
|
584
|
+
const allocationDelete = this.buildAuthorityAllocationDeleteStatement(input.prepared.acknowledgement.linkSessionId);
|
|
583
585
|
const beforeDeleteStatements = [
|
|
584
586
|
this.buildCredentialDeliveryAcknowledgementStatement(input.prepared),
|
|
585
|
-
|
|
587
|
+
allocationDelete,
|
|
586
588
|
this.buildCredentialDeliveryCleanupStateStatement({
|
|
587
589
|
linkSessionId: input.prepared.acknowledgement.linkSessionId,
|
|
588
590
|
from: "pending",
|
|
@@ -609,7 +611,7 @@ var D1LinkedDeviceAuthorityInstallServiceV1 = class {
|
|
|
609
611
|
const detail = "message" in deleted && deleted.message ? `: ${deleted.message}` : "";
|
|
610
612
|
throw new Error(`active authority cleanup failed: ${deleted.outcome}${detail}`);
|
|
611
613
|
}
|
|
612
|
-
} else await this.options.database.batch([...beforeDeleteStatements, ...afterDeleteStatements]);
|
|
614
|
+
} else await this.options.database.batch([...beforeDeleteStatements, ...afterDeleteStatements].flatMap((statement) => statement === allocationDelete ? [statement] : [statement, this.options.database.prepare(SESSION_CAS_GUARD_SQL)]));
|
|
613
615
|
} catch (error) {
|
|
614
616
|
const replay = await this.readActivationCleanupReceiptV1({
|
|
615
617
|
linkSessionId: input.prepared.acknowledgement.linkSessionId,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { parseLinkDeviceSessionId } from "../../../../shared-ts/src/utils/domainIds.js";
|
|
2
2
|
import { readJson } from "../../../framework/http.js";
|
|
3
3
|
import { LinkedDeviceRequestProofVerifierV1 } from "../../../../core/deviceLinking/requestProof.js";
|
|
4
|
+
import { D1LinkedDeviceSessionStoreV1 } from "./d1LinkedDeviceSessionStore.js";
|
|
4
5
|
import { D1LinkedDeviceEd25519ExportRootStoreV1 } from "./d1LinkedDeviceEd25519ExportRootStore.js";
|
|
5
6
|
import { D1LinkedDeviceRequestProofNonceStoreV1 } from "./d1LinkedDeviceRequestProofNonceStore.js";
|
|
6
7
|
import { createD1LinkedDeviceSessionServiceV1 } from "./d1LinkedDeviceSessionService.js";
|
|
7
|
-
import { D1LinkedDeviceSessionStoreV1 } from "./d1LinkedDeviceSessionStore.js";
|
|
8
8
|
//#region src/router/cloudflare/d1/deviceLinking/d1LinkedDeviceRouteService.ts
|
|
9
9
|
function createD1LinkedDeviceRouteServiceV1(options) {
|
|
10
10
|
const nowV1 = options.nowV1 ?? Date.now;
|
package/dist/esm/router/cloudflare/d1/deviceLinking/d1LinkedDeviceTargetCredentialProvider.js
CHANGED
|
@@ -10,9 +10,9 @@ import { assertLinkedDeviceTargetCredentialRegistrationMatchesPreparationV1, com
|
|
|
10
10
|
import { d1ChangedRows } from "../../../../storage/d1Sql.js";
|
|
11
11
|
import { errorMessage } from "../../../../shared-ts/src/utils/errors.js";
|
|
12
12
|
import { parseClientDataJsonBase64url } from "../../../../core/authService/webauthnOidcHelpers.js";
|
|
13
|
+
import { SESSION_CAS_GUARD_SQL } from "./d1LinkedDeviceSessionStore.js";
|
|
13
14
|
import { linkedDeviceX25519RecipientPublicKeyB64uV1 } from "./d1LinkedDeviceSourceContributionPreparationPlanner.js";
|
|
14
15
|
import { verifyWebAuthnRegistrationCredentialForIntent } from "../../../../core/authService/webauthn.js";
|
|
15
|
-
import { SESSION_CAS_GUARD_SQL } from "./d1LinkedDeviceSessionStore.js";
|
|
16
16
|
import { linkedDeviceEmailOtpDescriptorCredentialIdV1 } from "../../../../core/deviceLinking/linkedDeviceEmailOtpGrant.js";
|
|
17
17
|
import { buildVerifiedLinkInputV1, buildVerifiedTargetFactorV1 } from "./d1LinkedDeviceVerifiedLinkBuilder.js";
|
|
18
18
|
//#region src/router/cloudflare/d1/deviceLinking/d1LinkedDeviceTargetCredentialProvider.ts
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { parseD1LinkedDeviceSessionRowV1, parseD1LinkedDeviceSessionTranscriptRowV1 } from "./d1LinkedDeviceSessionRecords.js";
|
|
2
|
+
import { D1LinkedDeviceSessionStoreV1, SESSION_CAS_GUARD_SQL } from "./d1LinkedDeviceSessionStore.js";
|
|
1
3
|
import { D1LinkedDeviceSourceContributionPreparationPlannerV1, createD1LinkedDeviceSourceContributionPreparationPlannerV1, linkedDeviceX25519RecipientPublicKeyB64uV1 } from "./d1LinkedDeviceSourceContributionPreparationPlanner.js";
|
|
2
4
|
import { D1LinkedDeviceAuthorityInstallServiceV1, parseInstalledEcdsaCustodySnapshotV1, readD1LinkedDeviceEcdsaSourceV1, readD1LinkedDeviceEd25519SourceV1 } from "./d1LinkedDeviceAuthorityInstallService.js";
|
|
3
5
|
import { D1LinkedDeviceEd25519ExportRootStoreV1 } from "./d1LinkedDeviceEd25519ExportRootStore.js";
|
|
4
6
|
import { D1LinkedDeviceRequestProofNonceStoreV1 } from "./d1LinkedDeviceRequestProofNonceStore.js";
|
|
5
7
|
import { createD1LinkedDeviceSessionServiceV1 } from "./d1LinkedDeviceSessionService.js";
|
|
6
|
-
import { parseD1LinkedDeviceSessionRowV1, parseD1LinkedDeviceSessionTranscriptRowV1 } from "./d1LinkedDeviceSessionRecords.js";
|
|
7
|
-
import { D1LinkedDeviceSessionStoreV1, SESSION_CAS_GUARD_SQL } from "./d1LinkedDeviceSessionStore.js";
|
|
8
8
|
import { createD1LinkedDeviceRouteServiceV1 } from "./d1LinkedDeviceRouteService.js";
|
|
9
9
|
import { LinkedDeviceSourceFamilyUnavailableErrorV1, createD1LinkedDeviceVerifiedLinkSourceReaderV1 } from "./d1LinkedDeviceVerifiedLinkSourceReader.js";
|
|
10
10
|
import { buildVerifiedLinkInputV1, buildVerifiedTargetFactorV1, computeVerifiedTargetFactorVerificationDigestV1, linkedDeviceSignerManifestsEqualV1 } from "./d1LinkedDeviceVerifiedLinkBuilder.js";
|
|
@@ -82,10 +82,7 @@ var D1WalletExecutionAuthority = class {
|
|
|
82
82
|
outcome: "cancelled"
|
|
83
83
|
};
|
|
84
84
|
await this.fixCancellation(input);
|
|
85
|
-
|
|
86
|
-
if (!completed.ok) return completed;
|
|
87
|
-
await this.database.batch(this.prepareState(input, "retired"));
|
|
88
|
-
return completed;
|
|
85
|
+
return await this.registration.complete(completion);
|
|
89
86
|
}
|
|
90
87
|
prepareEstablished(input) {
|
|
91
88
|
if (input.registration.state === "active") return null;
|
|
@@ -96,7 +93,7 @@ var D1WalletExecutionAuthority = class {
|
|
|
96
93
|
attempts: 0
|
|
97
94
|
};
|
|
98
95
|
return {
|
|
99
|
-
statements: [...this.
|
|
96
|
+
statements: [...this.prepareEstablishedState(input.registration), prepareD1TenantStatement(this.database, this.scope, `INSERT INTO wallet_registration_directory_publications
|
|
100
97
|
(namespace, org_id, project_id, env_id, wallet_id, ceremony_id,
|
|
101
98
|
session_json, next_attempt_at_ms)
|
|
102
99
|
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)
|
|
@@ -185,16 +182,12 @@ var D1WalletExecutionAuthority = class {
|
|
|
185
182
|
AND registration_completion IN ('pending', 'cancelled')
|
|
186
183
|
AND state IN ('registering', 'retired')`, [input.walletId, input.ceremonyId]), this.database.prepare(D1_BATCH_CAS_GUARD_SQL)]);
|
|
187
184
|
}
|
|
188
|
-
/** The state update with its guard. The guard reads the row count of the update before it. */
|
|
189
|
-
|
|
190
|
-
return [prepareD1TenantStatement(this.database, this.scope, `UPDATE wallet_execution_generations SET state =
|
|
185
|
+
/** The established-state update with its guard. The guard reads the row count of the update before it. */
|
|
186
|
+
prepareEstablishedState(input) {
|
|
187
|
+
return [prepareD1TenantStatement(this.database, this.scope, `UPDATE wallet_execution_generations SET state = 'active', registration_completion = 'established'
|
|
191
188
|
WHERE namespace = ?1 AND org_id = ?2 AND project_id = ?3 AND env_id = ?4
|
|
192
189
|
AND wallet_id = ?5 AND origin = 'registration' AND origin_id = ?6
|
|
193
|
-
AND state IN ('registering',
|
|
194
|
-
input.walletId,
|
|
195
|
-
input.ceremonyId,
|
|
196
|
-
state
|
|
197
|
-
]), this.database.prepare(D1_BATCH_CAS_GUARD_SQL)];
|
|
190
|
+
AND state IN ('registering', 'active')`, [input.walletId, input.ceremonyId]), this.database.prepare(D1_BATCH_CAS_GUARD_SQL)];
|
|
198
191
|
}
|
|
199
192
|
async admitEstablishedHome(input) {
|
|
200
193
|
return admitD1EstablishedWalletHome(this.database, this.scope, input);
|
|
@@ -4,11 +4,14 @@ import { routerAbMpcMaterialActivationRefToWire } from "../../../shared-ts/src/u
|
|
|
4
4
|
import { hasExactKeys } from "../../../shared-ts/src/utils/exactKeys.js";
|
|
5
5
|
import { extractBearerCredential } from "../../auth/routerApiKeyAuth.js";
|
|
6
6
|
import { defineRoute } from "../../framework/routeDefinitions.js";
|
|
7
|
-
import {
|
|
7
|
+
import { resolveWalletSessionAuthorizationV2Admission } from "../signingOperations/walletExecutionAdmission.js";
|
|
8
8
|
//#region src/router/domains/speakToSign/speakToSignVerify.ts
|
|
9
9
|
const SPEAK_TO_SIGN_VERIFY_PATH = "/speak-to-sign/v1/verify";
|
|
10
|
-
/**
|
|
11
|
-
|
|
10
|
+
/**
|
|
11
|
+
* An enrollment carries three clips. 8 s of 16 kHz PCM16 is 256,000 bytes, about 341 KB in
|
|
12
|
+
* base64, so three clips are about 1,000 KB, with room for the fields.
|
|
13
|
+
*/
|
|
14
|
+
const SPEAK_TO_SIGN_VERIFY_MAX_BODY_BYTES = 1100 * 1024;
|
|
12
15
|
/** The verifier calls two remote models; the whole flow has a 10 s budget. */
|
|
13
16
|
const OUTLAYER_TIMEOUT_MS = 2e4;
|
|
14
17
|
const OUTLAYER_RESOURCE_LIMITS = Object.freeze({
|
|
@@ -81,10 +84,10 @@ var SpeakToSignVerifyRoutes = class {
|
|
|
81
84
|
path: SPEAK_TO_SIGN_VERIFY_PATH,
|
|
82
85
|
auth: {
|
|
83
86
|
plane: "public",
|
|
84
|
-
rationale: "The handler requires
|
|
87
|
+
rationale: "The handler requires a live Wallet Session operation credential of the owner."
|
|
85
88
|
},
|
|
86
89
|
metering: { kind: "none" },
|
|
87
|
-
summary: "Run the pinned OutLayer voice verifier for a spoken payment"
|
|
90
|
+
summary: "Run the pinned OutLayer voice verifier for a spoken payment or an enrollment"
|
|
88
91
|
})];
|
|
89
92
|
now;
|
|
90
93
|
constructor(config, dependencies) {
|
|
@@ -94,8 +97,10 @@ var SpeakToSignVerifyRoutes = class {
|
|
|
94
97
|
}
|
|
95
98
|
async handleFetchRoute(input) {
|
|
96
99
|
const startedAtMs = this.now();
|
|
97
|
-
const
|
|
98
|
-
if (
|
|
100
|
+
const authentication = await this.authenticate(input.request, startedAtMs, input.logger);
|
|
101
|
+
if (authentication.kind === "unauthorized") return failure(401, "unauthorized");
|
|
102
|
+
if (authentication.kind === "unavailable") return failure(503, "session_unavailable");
|
|
103
|
+
const { subject } = authentication;
|
|
99
104
|
const { walletId } = subject;
|
|
100
105
|
if (this.dependencies.rateLimit) {
|
|
101
106
|
if (!(await this.dependencies.rateLimit({ walletId })).allowed) return failure(429, "rate_limited");
|
|
@@ -109,32 +114,53 @@ var SpeakToSignVerifyRoutes = class {
|
|
|
109
114
|
if (verifierInput.owner !== owner) return failure(403, "owner_mismatch");
|
|
110
115
|
return await this.callVerifier(verifierInput, input.logger, startedAtMs);
|
|
111
116
|
}
|
|
112
|
-
|
|
117
|
+
/**
|
|
118
|
+
* The route needs the owner's identity, not a signing allowance: the vault transfer is signed
|
|
119
|
+
* later through the Wallet's own signing path, which spends the quota. An exhausted session
|
|
120
|
+
* still names its owner, so the identity read admits it.
|
|
121
|
+
*/
|
|
122
|
+
async authenticate(request, nowMs, logger) {
|
|
113
123
|
const token = extractBearerCredential(request.headers);
|
|
114
|
-
if (!token) return
|
|
124
|
+
if (!token) return { kind: "unauthorized" };
|
|
115
125
|
const sessions = this.dependencies.authorizationSessions;
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
126
|
+
let context;
|
|
127
|
+
try {
|
|
128
|
+
context = await sessions.readWalletSessionExactOperationContextByCredential({
|
|
129
|
+
tenantId: sessions.tenantId,
|
|
130
|
+
token,
|
|
131
|
+
nowMs
|
|
132
|
+
});
|
|
133
|
+
} catch (error) {
|
|
134
|
+
logger.warn("[speak-to-sign] Wallet Session read failed", { error: error instanceof Error ? error.message : "unknown" });
|
|
135
|
+
return { kind: "unavailable" };
|
|
136
|
+
}
|
|
137
|
+
if (!context) return { kind: "unauthorized" };
|
|
138
|
+
const { session } = context;
|
|
139
|
+
const admission = resolveWalletSessionAuthorizationV2Admission({
|
|
140
|
+
authorization: session,
|
|
141
|
+
authority: context.authority,
|
|
142
|
+
authMethod: context.authMethod,
|
|
143
|
+
retiredAtMs: context.retiredAtMs,
|
|
124
144
|
nowMs,
|
|
125
145
|
operation: {
|
|
146
|
+
tenantId: session.tenantId,
|
|
147
|
+
principalId: session.principalId,
|
|
148
|
+
walletId: session.walletId,
|
|
126
149
|
keyFamily: "ed25519",
|
|
127
150
|
operationKind: NEAR_ED25519_MPC_OPERATION_KINDS.signTransaction
|
|
128
151
|
}
|
|
129
|
-
})
|
|
152
|
+
});
|
|
153
|
+
if (!admission.ok || admission.keyFamily !== "ed25519") return { kind: "unauthorized" };
|
|
130
154
|
return {
|
|
131
|
-
|
|
132
|
-
|
|
155
|
+
kind: "admitted",
|
|
156
|
+
subject: {
|
|
157
|
+
walletId: session.walletId,
|
|
158
|
+
ed25519: admission.materialActivation
|
|
159
|
+
}
|
|
133
160
|
};
|
|
134
161
|
}
|
|
135
|
-
/** The NEAR account of the session's Ed25519 signer, or null when
|
|
162
|
+
/** The NEAR account of the session's Ed25519 signer, or null when it has none. */
|
|
136
163
|
async sessionNearAccount(subject) {
|
|
137
|
-
if (!subject.ed25519) return null;
|
|
138
164
|
const resolved = await this.dependencies.walletRegistration.resolveEd25519MaterialActivation({
|
|
139
165
|
walletId: String(subject.walletId),
|
|
140
166
|
materialActivation: routerAbMpcMaterialActivationRefToWire(subject.ed25519)
|
|
@@ -17,7 +17,7 @@ import type { LinkedDeviceEcdsaSourcePreservingActivationReceiptV1 } from '../..
|
|
|
17
17
|
import type { D1DatabaseLike, D1PreparedStatementLike } from '../../../../storage/tenantRoute';
|
|
18
18
|
import { type RouterAbEd25519YaoActivationPublicReceiptV1, type RouterAbEd25519YaoApplicationBindingFactsV1, type RouterAbEd25519YaoCeremonyBindingV1 } from '../../../../../../shared-ts/src/utils/routerAbEd25519Yao';
|
|
19
19
|
import { D1WalletAuthorityStore, type D1WalletAuthorityStoreScope } from '../wallet/d1WalletAuthorityStore';
|
|
20
|
-
import type
|
|
20
|
+
import { type D1LinkedDeviceSessionStoreV1 } from './d1LinkedDeviceSessionStore';
|
|
21
21
|
import type { D1WalletAuthMethodStore } from '../../../../core/d1WalletAuthMethodStore';
|
|
22
22
|
import type { WalletEd25519SignerRecord } from '../../../../core/WalletStore';
|
|
23
23
|
import type { AuthorizationService } from '../../../../authorization/service';
|
|
@@ -36,8 +36,8 @@ export declare class D1WalletExecutionAuthority implements WalletRegistrationHom
|
|
|
36
36
|
private deliver;
|
|
37
37
|
private preparePublicationUpdate;
|
|
38
38
|
private fixCancellation;
|
|
39
|
-
/** The state update with its guard. The guard reads the row count of the update before it. */
|
|
40
|
-
private
|
|
39
|
+
/** The established-state update with its guard. The guard reads the row count of the update before it. */
|
|
40
|
+
private prepareEstablishedState;
|
|
41
41
|
admitEstablishedHome(input: Parameters<WalletExecutionAuthority['admitEstablishedHome']>[0]): Promise<OrdinaryAdmission>;
|
|
42
42
|
}
|
|
43
43
|
/** Records the authenticated reservation beside setup without reopening a retired home. */
|
|
@@ -2,7 +2,10 @@ import type { WalletId } from '../../../../../shared-ts/src/utils/domainIds';
|
|
|
2
2
|
import type { RouterApiAuthorizationSessionService, RouterApiWalletRegistrationService } from '../../framework/authServicePort';
|
|
3
3
|
import type { RouterApiRouteExtension } from '../../framework/routeExtensions';
|
|
4
4
|
export declare const SPEAK_TO_SIGN_VERIFY_PATH = "/speak-to-sign/v1/verify";
|
|
5
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* An enrollment carries three clips. 8 s of 16 kHz PCM16 is 256,000 bytes, about 341 KB in
|
|
7
|
+
* base64, so three clips are about 1,000 KB, with room for the fields.
|
|
8
|
+
*/
|
|
6
9
|
export declare const SPEAK_TO_SIGN_VERIFY_MAX_BODY_BYTES: number;
|
|
7
10
|
/** The route exists only when the server configuration holds every field. */
|
|
8
11
|
export type SpeakToSignVerifyConfig = Readonly<{
|
|
@@ -25,7 +28,8 @@ export type SpeakToSignVerifyRateLimit = (input: {
|
|
|
25
28
|
readonly allowed: boolean;
|
|
26
29
|
}>;
|
|
27
30
|
export type SpeakToSignVerifyDependencies = Readonly<{
|
|
28
|
-
|
|
31
|
+
/** The identity read: it admits an exhausted session, because the route spends no quota. */
|
|
32
|
+
authorizationSessions: Pick<RouterApiAuthorizationSessionService, 'tenantId' | 'readWalletSessionExactOperationContextByCredential'>;
|
|
29
33
|
/** Resolves the NEAR account of the session's Ed25519 signer for the owner check. */
|
|
30
34
|
walletRegistration: Pick<RouterApiWalletRegistrationService, 'resolveEd25519MaterialActivation'>;
|
|
31
35
|
fetch: typeof fetch;
|