@seams/wallet-server 0.9.2 → 0.9.3
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/artifact-manifest.json +28 -23
- package/cloudflare-router-ab/build/deriver-a/build-stamp.json +1 -1
- package/cloudflare-router-ab/build/deriver-a/index_bg.wasm +0 -0
- package/cloudflare-router-ab/build/deriver-b/build-stamp.json +1 -1
- package/cloudflare-router-ab/build/deriver-b/index_bg.wasm +0 -0
- package/cloudflare-router-ab/build/local-tools/darwin-arm64/router_ab_local_init +0 -0
- package/cloudflare-router-ab/build/local-tools/darwin-x64/router_ab_local_init +0 -0
- package/cloudflare-router-ab/build/local-tools/linux-x64/router_ab_local_init +0 -0
- package/cloudflare-router-ab/build/router/build-stamp.json +1 -1
- package/cloudflare-router-ab/build/router/index_bg.wasm +0 -0
- package/cloudflare-router-ab/build/signing-worker/build-stamp.json +1 -1
- package/cloudflare-router-ab/build/signing-worker/index_bg.wasm +0 -0
- package/cloudflare-router-ab/build/tenant-root-control-plane/build-stamp.json +1 -1
- package/cloudflare-router-ab/build/tenant-root-control-plane/index_bg.wasm +0 -0
- package/dist/esm/router/cloudflare/d1/auth/d1RouterApiAuthService.js +1 -1
- package/dist/esm/router/cloudflare/d1/registration/d1WalletRegistrationCommitStore.js +46 -2
- package/dist/esm/router/cloudflare/d1/registration/d1WalletRegistrationRead.js +7 -4
- package/dist/esm/router/cloudflare/d1/registration/d1WalletRegistrationService.js +25 -9
- package/dist/esm/router/cloudflare.js +1 -1
- package/dist/esm/router/domains/walletRegistration/walletRegistrationRoutes.js +1 -1
- package/dist/esm/router/express.js +1 -1
- package/dist/esm/wasm/shamir3pass_runtime/pkg/shamir3pass_runtime_bg.wasm +0 -0
- package/dist/types/wallet-server/src/router/cloudflare/d1/registration/d1WalletRegistrationCommitStore.d.ts +34 -2
- package/dist/types/wallet-server/src/router/cloudflare/d1/registration/d1WalletRegistrationRead.d.ts +4 -1
- package/dist/types/wallet-server/src/router/cloudflare/d1/registration/d1WalletRegistrationService.d.ts +1 -1
- package/dist/types/wallet-server/src/router/framework/authServicePort.d.ts +1 -1
- package/migrations/d1-signer/0078_registration_passkey_claims.sql +20 -0
- package/package.json +1 -1
package/artifact-manifest.json
CHANGED
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
"schemaVersion": "seams_wallet_server_artifact_manifest_v1",
|
|
3
3
|
"package": {
|
|
4
4
|
"name": "@seams/wallet-server",
|
|
5
|
-
"version": "0.9.
|
|
5
|
+
"version": "0.9.3"
|
|
6
6
|
},
|
|
7
7
|
"workers": {
|
|
8
8
|
"router": {
|
|
9
9
|
"entry": "cloudflare-router-ab/build/router/worker/shim.mjs",
|
|
10
|
-
"fingerprint": "
|
|
10
|
+
"fingerprint": "9aa6adbab32e5358ec10422887961436e7fef549c9c740331e05f51aa6f095e7",
|
|
11
11
|
"files": [
|
|
12
12
|
{
|
|
13
13
|
"path": "cloudflare-router-ab/build/router/build-stamp.json",
|
|
14
14
|
"bytes": 119,
|
|
15
|
-
"sha256": "
|
|
15
|
+
"sha256": "e0dc70f76677f14be6bfe19283b8d1ac143e19f87f747f1b7b547301d6d1d732"
|
|
16
16
|
},
|
|
17
17
|
{
|
|
18
18
|
"path": "cloudflare-router-ab/build/router/index.js",
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
23
|
"path": "cloudflare-router-ab/build/router/index_bg.wasm",
|
|
24
|
-
"bytes":
|
|
25
|
-
"sha256": "
|
|
24
|
+
"bytes": 8093903,
|
|
25
|
+
"sha256": "07aec63f57a09dbcb3c6fa25dee6184ef45fa6e9168816b0d80a3b9259a4cd4d"
|
|
26
26
|
},
|
|
27
27
|
{
|
|
28
28
|
"path": "cloudflare-router-ab/build/router/package.json",
|
|
@@ -38,12 +38,12 @@
|
|
|
38
38
|
},
|
|
39
39
|
"deriver-a": {
|
|
40
40
|
"entry": "cloudflare-router-ab/build/deriver-a/worker/shim.mjs",
|
|
41
|
-
"fingerprint": "
|
|
41
|
+
"fingerprint": "9d5e7b103536e7ebb8171d92441c5e9e3d0ffc56e7fa2c14ae366b4c830a6021",
|
|
42
42
|
"files": [
|
|
43
43
|
{
|
|
44
44
|
"path": "cloudflare-router-ab/build/deriver-a/build-stamp.json",
|
|
45
45
|
"bytes": 122,
|
|
46
|
-
"sha256": "
|
|
46
|
+
"sha256": "f4584b8a1735dceb2c83fe247d053a368d36f195baea47e0e84a0db749c0489a"
|
|
47
47
|
},
|
|
48
48
|
{
|
|
49
49
|
"path": "cloudflare-router-ab/build/deriver-a/index.js",
|
|
@@ -52,8 +52,8 @@
|
|
|
52
52
|
},
|
|
53
53
|
{
|
|
54
54
|
"path": "cloudflare-router-ab/build/deriver-a/index_bg.wasm",
|
|
55
|
-
"bytes":
|
|
56
|
-
"sha256": "
|
|
55
|
+
"bytes": 13967465,
|
|
56
|
+
"sha256": "ab240fd5d74b80184b32d46427b490b72df08c2d90a42a47124a7f1d132f1bbd"
|
|
57
57
|
},
|
|
58
58
|
{
|
|
59
59
|
"path": "cloudflare-router-ab/build/deriver-a/package.json",
|
|
@@ -69,12 +69,12 @@
|
|
|
69
69
|
},
|
|
70
70
|
"deriver-b": {
|
|
71
71
|
"entry": "cloudflare-router-ab/build/deriver-b/worker/shim.mjs",
|
|
72
|
-
"fingerprint": "
|
|
72
|
+
"fingerprint": "7d00d2bd13540dbf54852a229719bb49f63e13f63156eae27ed6d02bf155bfd5",
|
|
73
73
|
"files": [
|
|
74
74
|
{
|
|
75
75
|
"path": "cloudflare-router-ab/build/deriver-b/build-stamp.json",
|
|
76
76
|
"bytes": 122,
|
|
77
|
-
"sha256": "
|
|
77
|
+
"sha256": "42b32d715606dc48c314c621a98bf9427aa497d4a2b5ea42d90fd5773ee10f7d"
|
|
78
78
|
},
|
|
79
79
|
{
|
|
80
80
|
"path": "cloudflare-router-ab/build/deriver-b/index.js",
|
|
@@ -83,8 +83,8 @@
|
|
|
83
83
|
},
|
|
84
84
|
{
|
|
85
85
|
"path": "cloudflare-router-ab/build/deriver-b/index_bg.wasm",
|
|
86
|
-
"bytes":
|
|
87
|
-
"sha256": "
|
|
86
|
+
"bytes": 14020253,
|
|
87
|
+
"sha256": "5ed975153c24dd4504d9bab7eb418fb4478edc25a26e424001b166cde68522a6"
|
|
88
88
|
},
|
|
89
89
|
{
|
|
90
90
|
"path": "cloudflare-router-ab/build/deriver-b/package.json",
|
|
@@ -100,12 +100,12 @@
|
|
|
100
100
|
},
|
|
101
101
|
"signing-worker": {
|
|
102
102
|
"entry": "cloudflare-router-ab/build/signing-worker/worker/shim.mjs",
|
|
103
|
-
"fingerprint": "
|
|
103
|
+
"fingerprint": "70f940a849724cee4ccbd8aab11645858179e95578badd8dab090a657a816e90",
|
|
104
104
|
"files": [
|
|
105
105
|
{
|
|
106
106
|
"path": "cloudflare-router-ab/build/signing-worker/build-stamp.json",
|
|
107
107
|
"bytes": 127,
|
|
108
|
-
"sha256": "
|
|
108
|
+
"sha256": "01ea18855d322077772ed0ecd6d4b0117627648fdbbfb06c0d946f704dcc3d07"
|
|
109
109
|
},
|
|
110
110
|
{
|
|
111
111
|
"path": "cloudflare-router-ab/build/signing-worker/index.js",
|
|
@@ -114,8 +114,8 @@
|
|
|
114
114
|
},
|
|
115
115
|
{
|
|
116
116
|
"path": "cloudflare-router-ab/build/signing-worker/index_bg.wasm",
|
|
117
|
-
"bytes":
|
|
118
|
-
"sha256": "
|
|
117
|
+
"bytes": 8988257,
|
|
118
|
+
"sha256": "2410f507efeb9b7501018dcfee4de2ffc36a664fe8ab59b3c3e487368ba05f56"
|
|
119
119
|
},
|
|
120
120
|
{
|
|
121
121
|
"path": "cloudflare-router-ab/build/signing-worker/package.json",
|
|
@@ -131,12 +131,12 @@
|
|
|
131
131
|
},
|
|
132
132
|
"tenant-root-control-plane": {
|
|
133
133
|
"entry": "cloudflare-router-ab/build/tenant-root-control-plane/worker/shim.mjs",
|
|
134
|
-
"fingerprint": "
|
|
134
|
+
"fingerprint": "6ea385bf5cdad01cd1363343344884ee9c96dc3497896a398c49f3757b6c501a",
|
|
135
135
|
"files": [
|
|
136
136
|
{
|
|
137
137
|
"path": "cloudflare-router-ab/build/tenant-root-control-plane/build-stamp.json",
|
|
138
138
|
"bytes": 139,
|
|
139
|
-
"sha256": "
|
|
139
|
+
"sha256": "f55647bb6cda0be99e58c921780c808e6b40ef608e08fef94163ab3b66c57938"
|
|
140
140
|
},
|
|
141
141
|
{
|
|
142
142
|
"path": "cloudflare-router-ab/build/tenant-root-control-plane/index.js",
|
|
@@ -145,8 +145,8 @@
|
|
|
145
145
|
},
|
|
146
146
|
{
|
|
147
147
|
"path": "cloudflare-router-ab/build/tenant-root-control-plane/index_bg.wasm",
|
|
148
|
-
"bytes":
|
|
149
|
-
"sha256": "
|
|
148
|
+
"bytes": 5220037,
|
|
149
|
+
"sha256": "58da690b6240f7f081a47a300d05c109e23428793c429e47b368f168ca93f917"
|
|
150
150
|
},
|
|
151
151
|
{
|
|
152
152
|
"path": "cloudflare-router-ab/build/tenant-root-control-plane/package.json",
|
|
@@ -164,8 +164,8 @@
|
|
|
164
164
|
"migrations": {
|
|
165
165
|
"signer": {
|
|
166
166
|
"directory": "migrations/d1-signer",
|
|
167
|
-
"head": "
|
|
168
|
-
"fingerprint": "
|
|
167
|
+
"head": "0078_registration_passkey_claims.sql",
|
|
168
|
+
"fingerprint": "d1305343eafa23d821d280e0ca7955ea4cbb685850ead2a2e4b611e7cab5fb33",
|
|
169
169
|
"files": [
|
|
170
170
|
{
|
|
171
171
|
"path": "migrations/d1-signer/0001_signer_d1_initial.sql",
|
|
@@ -551,6 +551,11 @@
|
|
|
551
551
|
"path": "migrations/d1-signer/0077_wallet_object_addresses.sql",
|
|
552
552
|
"bytes": 1243,
|
|
553
553
|
"sha256": "693c3606116f2ff49c1eb77a56631c97448cc296855753c7cf54c1e9a103e9f6"
|
|
554
|
+
},
|
|
555
|
+
{
|
|
556
|
+
"path": "migrations/d1-signer/0078_registration_passkey_claims.sql",
|
|
557
|
+
"bytes": 880,
|
|
558
|
+
"sha256": "c0bab803761cc25b502217a1f9b3fe7d5082711d4632ab1ffc117ba47581f530"
|
|
554
559
|
}
|
|
555
560
|
]
|
|
556
561
|
},
|
|
@@ -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":1791827115529}
|
|
Binary file
|
|
@@ -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":1791827364267}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -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":1791827596969}
|
|
Binary file
|
|
@@ -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":1791826784348}
|
|
Binary file
|
|
@@ -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":1791827823651}
|
|
Binary file
|
|
@@ -846,7 +846,7 @@ function createCloudflareD1RouterApiAuthAssembly(input) {
|
|
|
846
846
|
tenantRootCustodyLineage: options.tenantRootCustodyLineage,
|
|
847
847
|
getWalletStore,
|
|
848
848
|
activateSideEffects: walletRegistrationActivateSideEffectStore(options),
|
|
849
|
-
registrationRead: new D1WalletRegistrationRead(options.database, options.registrationAuthority ?? null, walletRegistrationNearProvisioningSideEffectStore(options), getRegistrationCeremonyIntentStore, yaoLifecycleDecisions),
|
|
849
|
+
registrationRead: new D1WalletRegistrationRead(options.database, options.registrationAuthority ?? null, walletRegistrationNearProvisioningSideEffectStore(options), getRegistrationCeremonyIntentStore, yaoLifecycleDecisions, walletRegistrationCommitStore),
|
|
850
850
|
walletRegistrationCommitStore,
|
|
851
851
|
walletCustodyCommitStore,
|
|
852
852
|
walletAuthMethods,
|
|
@@ -201,20 +201,64 @@ function emailOtpCommitStatements(input) {
|
|
|
201
201
|
case "email_otp_wallet_registration_commit_v1": return input.emailOtp.statements;
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
|
+
function heldClaimCovers(held, authority) {
|
|
205
|
+
return held !== null && held.walletId === authority.walletId && held.rpId === authority.rpId && held.credentialIdB64u === authority.credentialIdB64u;
|
|
206
|
+
}
|
|
204
207
|
var CloudflareD1WalletRegistrationCommitStore = class {
|
|
205
208
|
constructor(input) {
|
|
206
209
|
this.credentialClaims = input.credentialClaims;
|
|
207
210
|
this.database = input.database;
|
|
208
211
|
this.scope = normalizeScope(input);
|
|
209
212
|
}
|
|
210
|
-
async reserveAuthority(authority) {
|
|
211
|
-
if (authority.kind === "passkey") await reservePasskeyCredential(this.credentialClaims, {
|
|
213
|
+
async reserveAuthority(authority, held = null) {
|
|
214
|
+
if (authority.kind === "passkey" && !heldClaimCovers(held, authority)) await reservePasskeyCredential(this.credentialClaims, {
|
|
212
215
|
walletId: authority.walletId,
|
|
213
216
|
rpId: authority.rpId,
|
|
214
217
|
credentialIdB64u: authority.credentialIdB64u
|
|
215
218
|
});
|
|
216
219
|
return this.commit.bind(this, authority);
|
|
217
220
|
}
|
|
221
|
+
async claimPasskeyAhead(input) {
|
|
222
|
+
const { authority } = input;
|
|
223
|
+
if (authority.kind !== "passkey" || !this.credentialClaims) return;
|
|
224
|
+
if (!await this.credentialClaims.claim({
|
|
225
|
+
walletId: authority.walletId,
|
|
226
|
+
rpId: authority.rpId,
|
|
227
|
+
credentialIdB64u: authority.credentialIdB64u
|
|
228
|
+
})) return;
|
|
229
|
+
await this.database.prepare(`INSERT OR IGNORE INTO registration_passkey_claims (
|
|
230
|
+
namespace, org_id, project_id, env_id, registration_ceremony_id,
|
|
231
|
+
wallet_id, rp_id, credential_id_b64u, claimed_at_ms
|
|
232
|
+
) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)`).bind(this.scope.namespace, this.scope.orgId, this.scope.projectId, this.scope.envId, input.registrationCeremonyId, authority.walletId, authority.rpId, authority.credentialIdB64u, input.nowMs).run();
|
|
233
|
+
}
|
|
234
|
+
prepareHeldPasskeyClaimRead(registrationCeremonyId) {
|
|
235
|
+
return this.database.prepare(`SELECT wallet_id, rp_id, credential_id_b64u
|
|
236
|
+
FROM registration_passkey_claims
|
|
237
|
+
WHERE namespace = ?1
|
|
238
|
+
AND org_id = ?2
|
|
239
|
+
AND project_id = ?3
|
|
240
|
+
AND env_id = ?4
|
|
241
|
+
AND registration_ceremony_id = ?5`).bind(this.scope.namespace, this.scope.orgId, this.scope.projectId, this.scope.envId, registrationCeremonyId);
|
|
242
|
+
}
|
|
243
|
+
parseHeldPasskeyClaimRead(result) {
|
|
244
|
+
if (!result.success) throw new Error("Registration passkey claim read failed");
|
|
245
|
+
const row = result.results?.[0];
|
|
246
|
+
if (!row) return null;
|
|
247
|
+
if (typeof row.wallet_id !== "string" || typeof row.rp_id !== "string" || typeof row.credential_id_b64u !== "string") throw new Error("Stored registration passkey claim is invalid");
|
|
248
|
+
return {
|
|
249
|
+
walletId: row.wallet_id,
|
|
250
|
+
rpId: row.rp_id,
|
|
251
|
+
credentialIdB64u: row.credential_id_b64u
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
async deleteHeldPasskeyClaim(registrationCeremonyId) {
|
|
255
|
+
await this.database.prepare(`DELETE FROM registration_passkey_claims
|
|
256
|
+
WHERE namespace = ?1
|
|
257
|
+
AND org_id = ?2
|
|
258
|
+
AND project_id = ?3
|
|
259
|
+
AND env_id = ?4
|
|
260
|
+
AND registration_ceremony_id = ?5`).bind(this.scope.namespace, this.scope.orgId, this.scope.projectId, this.scope.envId, registrationCeremonyId).run();
|
|
261
|
+
}
|
|
218
262
|
async commit(authority, input, executeBatch) {
|
|
219
263
|
assertCommitWalletIdentity(input, authority);
|
|
220
264
|
const ed25519Signer = input.walletSigners.find(isEd25519Signer);
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
//#region src/router/cloudflare/d1/registration/d1WalletRegistrationRead.ts
|
|
2
2
|
/** Read admission and immutable finalization state from one D1 snapshot. */
|
|
3
3
|
var D1WalletRegistrationRead = class {
|
|
4
|
-
constructor(database, authority, operations, getCeremonies, decisions) {
|
|
4
|
+
constructor(database, authority, operations, getCeremonies, decisions, passkeyClaims) {
|
|
5
5
|
this.database = database;
|
|
6
6
|
this.authority = authority;
|
|
7
7
|
this.operations = operations;
|
|
8
8
|
this.getCeremonies = getCeremonies;
|
|
9
9
|
this.decisions = decisions;
|
|
10
|
+
this.passkeyClaims = passkeyClaims;
|
|
10
11
|
}
|
|
11
12
|
async readRespond(home) {
|
|
12
13
|
if (!this.authority) return {
|
|
@@ -37,10 +38,11 @@ var D1WalletRegistrationRead = class {
|
|
|
37
38
|
this.authority.prepareHomeRead(home),
|
|
38
39
|
this.operations.prepareRead(operationKey),
|
|
39
40
|
ceremonies.prepareCeremonyRead(home.ceremonyId),
|
|
40
|
-
this.decisions.prepareRead(home.ceremonyId)
|
|
41
|
+
this.decisions.prepareRead(home.ceremonyId),
|
|
42
|
+
this.passkeyClaims.prepareHeldPasskeyClaimRead(home.ceremonyId)
|
|
41
43
|
]);
|
|
42
|
-
const [homeRead, operationRead, ceremonyRead, decisionRead] = results;
|
|
43
|
-
if (results.length !==
|
|
44
|
+
const [homeRead, operationRead, ceremonyRead, decisionRead, passkeyClaimRead] = results;
|
|
45
|
+
if (results.length !== 5 || !homeRead || !operationRead || !ceremonyRead || !decisionRead || !passkeyClaimRead) throw new Error("Registration finalization D1 batch returned incomplete results");
|
|
44
46
|
const admission = await this.authority.admitHomeRead(home, homeRead);
|
|
45
47
|
if (!admission.ok) return admission;
|
|
46
48
|
if (!ceremonyRead.success) throw new Error("Registration ceremony D1 read failed");
|
|
@@ -48,6 +50,7 @@ var D1WalletRegistrationRead = class {
|
|
|
48
50
|
ok: true,
|
|
49
51
|
operation: this.operations.parseReadResult(operationRead),
|
|
50
52
|
decision: this.decisions.parseRead(home.ceremonyId, decisionRead),
|
|
53
|
+
heldPasskeyClaim: this.passkeyClaims.parseHeldPasskeyClaimRead(passkeyClaimRead),
|
|
51
54
|
readCeremony: ceremonies.parseCeremonyRead.bind(ceremonies, home.ceremonyId, ceremonyRead)
|
|
52
55
|
};
|
|
53
56
|
}
|
|
@@ -529,9 +529,7 @@ function assertNeverRegistrationReceiptCommit(value) {
|
|
|
529
529
|
throw new Error(`Unsupported registration receipt commit: ${String(value)}`);
|
|
530
530
|
}
|
|
531
531
|
async function cleanupFinalizedRegistrationCeremony(input) {
|
|
532
|
-
|
|
533
|
-
await input.store.deleteCeremony(input.registrationCeremonyId);
|
|
534
|
-
} catch {}
|
|
532
|
+
await Promise.allSettled([input.store.deleteCeremony(input.registrationCeremonyId), input.passkeyClaims?.deleteHeldPasskeyClaim(input.registrationCeremonyId)]);
|
|
535
533
|
}
|
|
536
534
|
function d1RegistrationRouteDiagnostics(recorder) {
|
|
537
535
|
return {
|
|
@@ -1690,7 +1688,7 @@ var CloudflareD1WalletRegistrationService = class {
|
|
|
1690
1688
|
};
|
|
1691
1689
|
}
|
|
1692
1690
|
}
|
|
1693
|
-
async respondWalletRegistration(input, traceContext, timingSink) {
|
|
1691
|
+
async respondWalletRegistration(input, traceContext, timingSink, waitUntil) {
|
|
1694
1692
|
const serverTiming = timingSink ?? [];
|
|
1695
1693
|
const totalStartedAtMs = Date.now();
|
|
1696
1694
|
const mark = (name, startedAtMs) => {
|
|
@@ -1769,6 +1767,7 @@ var CloudflareD1WalletRegistrationService = class {
|
|
|
1769
1767
|
ownershipGeneration: homeAdmission.ownershipGeneration,
|
|
1770
1768
|
startedDispatchRoot,
|
|
1771
1769
|
expectedOrigin,
|
|
1770
|
+
waitUntil,
|
|
1772
1771
|
...input.userAgent ? { userAgent: input.userAgent } : {}
|
|
1773
1772
|
});
|
|
1774
1773
|
if (!ecdsaBranch) return {
|
|
@@ -2167,7 +2166,7 @@ var CloudflareD1WalletRegistrationService = class {
|
|
|
2167
2166
|
ed25519: args.input.ed25519,
|
|
2168
2167
|
emailOtpEnrollment: args.input.emailOtpEnrollment,
|
|
2169
2168
|
walletCustodyCommit: args.input.walletCustodyCommit
|
|
2170
|
-
}, effectivePrepared, source, args.serverTiming, initialSessionPlan, args.lifecycleDecision);
|
|
2169
|
+
}, effectivePrepared, source, args.serverTiming, initialSessionPlan, args.lifecycleDecision, args.heldPasskeyClaim);
|
|
2171
2170
|
if (!execution.ok) return unissuedRegistrationCommit(throwIfRouterAbEd25519YaoRetryableSideEffectFailureV1(execution));
|
|
2172
2171
|
const finalized = execution.finalized;
|
|
2173
2172
|
if (finalized.kind !== "near_ed25519") {
|
|
@@ -2414,7 +2413,8 @@ var CloudflareD1WalletRegistrationService = class {
|
|
|
2414
2413
|
setupClaims: verified.claims,
|
|
2415
2414
|
authoritySource: verified.source,
|
|
2416
2415
|
preparedCeremony: () => preparedCeremony,
|
|
2417
|
-
lifecycleDecision: initial.decision
|
|
2416
|
+
lifecycleDecision: initial.decision,
|
|
2417
|
+
heldPasskeyClaim: initial.heldPasskeyClaim
|
|
2418
2418
|
}),
|
|
2419
2419
|
projectReceipt: (execution) => projectWalletRegistrationSessionCommitReceiptV2({
|
|
2420
2420
|
operation: "near_provisioning",
|
|
@@ -2475,7 +2475,8 @@ var CloudflareD1WalletRegistrationService = class {
|
|
|
2475
2475
|
startedAtMs: Date.now(),
|
|
2476
2476
|
promise: cleanupFinalizedRegistrationCeremony({
|
|
2477
2477
|
store: this.getRegistrationCeremonyIntentStore(),
|
|
2478
|
-
registrationCeremonyId: input.registrationCeremonyId
|
|
2478
|
+
registrationCeremonyId: input.registrationCeremonyId,
|
|
2479
|
+
passkeyClaims: this.walletRegistrationCommitStore
|
|
2479
2480
|
})
|
|
2480
2481
|
});
|
|
2481
2482
|
if (waitUntil) waitUntil(cleanup);
|
|
@@ -2650,6 +2651,16 @@ var CloudflareD1WalletRegistrationService = class {
|
|
|
2650
2651
|
expected: input.snapshot,
|
|
2651
2652
|
next: authorizedCeremony(admitted.admissionRequest)
|
|
2652
2653
|
});
|
|
2654
|
+
if (!stored && input.waitUntil && verifiedAuthority.kind === "passkey") input.waitUntil(this.walletRegistrationCommitStore.claimPasskeyAhead({
|
|
2655
|
+
registrationCeremonyId: ceremony.registrationCeremonyId,
|
|
2656
|
+
authority: verifiedAuthority,
|
|
2657
|
+
nowMs: Date.now()
|
|
2658
|
+
}).catch((error) => {
|
|
2659
|
+
console.warn("[wallet-registration] early passkey claim failed", {
|
|
2660
|
+
registrationCeremonyId: ceremony.registrationCeremonyId,
|
|
2661
|
+
reason: errorMessage(error)
|
|
2662
|
+
});
|
|
2663
|
+
}));
|
|
2653
2664
|
return {
|
|
2654
2665
|
ok: true,
|
|
2655
2666
|
registrationCeremonyId: ceremony.registrationCeremonyId,
|
|
@@ -3215,7 +3226,7 @@ var CloudflareD1WalletRegistrationService = class {
|
|
|
3215
3226
|
};
|
|
3216
3227
|
}
|
|
3217
3228
|
}
|
|
3218
|
-
async executeWalletRegistrationFinalize(request, prepared, source, serverTiming, initialSessionPlan, lifecycleDecision) {
|
|
3229
|
+
async executeWalletRegistrationFinalize(request, prepared, source, serverTiming, initialSessionPlan, lifecycleDecision, heldPasskeyClaim = null) {
|
|
3219
3230
|
const finalizeTiming = createD1RegistrationRouteTimingRecorder("wallets_register_finalize");
|
|
3220
3231
|
const totalTiming = startD1RegistrationRouteTiming("registerFinalizeTotalMs");
|
|
3221
3232
|
let startedCustody = null;
|
|
@@ -3450,7 +3461,12 @@ var CloudflareD1WalletRegistrationService = class {
|
|
|
3450
3461
|
},
|
|
3451
3462
|
consumerBinding: requestFingerprint
|
|
3452
3463
|
})
|
|
3453
|
-
}),
|
|
3464
|
+
}), recordRegistrationPromiseTiming({
|
|
3465
|
+
sink: serverTiming,
|
|
3466
|
+
name: "near_finalize_authority_reserve",
|
|
3467
|
+
startedAtMs: Date.now(),
|
|
3468
|
+
promise: this.walletRegistrationCommitStore.reserveAuthority(ceremonyAuthority, heldPasskeyClaim)
|
|
3469
|
+
})]);
|
|
3454
3470
|
if (activationConsumption.status === "rejected") throw activationConsumption.reason;
|
|
3455
3471
|
if (authorityReservation.status === "rejected") throw authorityReservation.reason;
|
|
3456
3472
|
const consumed = activationConsumption.value;
|
|
@@ -13961,7 +13961,7 @@ async function handleRouterApiWalletRegistrationRespond(input) {
|
|
|
13961
13961
|
...parsed.value,
|
|
13962
13962
|
verifier: session,
|
|
13963
13963
|
...registrationUserAgentFromHeaders(input.headers) ? { userAgent: registrationUserAgentFromHeaders(input.headers) } : {}
|
|
13964
|
-
}, traceContext.value ?? void 0, serverTiming);
|
|
13964
|
+
}, traceContext.value ?? void 0, serverTiming, input.waitUntil);
|
|
13965
13965
|
return routeJson(result.ok ? 200 : 400, result, { headers: ecdsaGatewayServerTimingHeaders(serverTiming) });
|
|
13966
13966
|
}
|
|
13967
13967
|
async function handleRouterApiWalletRegistrationNearAdmission(input) {
|
|
@@ -1641,7 +1641,7 @@ async function handleRouterApiWalletRegistrationRespond(input) {
|
|
|
1641
1641
|
...parsed.value,
|
|
1642
1642
|
verifier: session,
|
|
1643
1643
|
...registrationUserAgentFromHeaders(input.headers) ? { userAgent: registrationUserAgentFromHeaders(input.headers) } : {}
|
|
1644
|
-
}, traceContext.value ?? void 0, serverTiming);
|
|
1644
|
+
}, traceContext.value ?? void 0, serverTiming, input.waitUntil);
|
|
1645
1645
|
return routeJson(result.ok ? 200 : 400, result, { headers: ecdsaGatewayServerTimingHeaders(serverTiming) });
|
|
1646
1646
|
}
|
|
1647
1647
|
async function handleRouterApiWalletRegistrationNearAdmission(input) {
|
|
@@ -9314,7 +9314,7 @@ async function handleRouterApiWalletRegistrationRespond(input) {
|
|
|
9314
9314
|
...parsed.value,
|
|
9315
9315
|
verifier: session,
|
|
9316
9316
|
...registrationUserAgentFromHeaders(input.headers) ? { userAgent: registrationUserAgentFromHeaders(input.headers) } : {}
|
|
9317
|
-
}, traceContext.value ?? void 0, serverTiming);
|
|
9317
|
+
}, traceContext.value ?? void 0, serverTiming, input.waitUntil);
|
|
9318
9318
|
return routeJson(result.ok ? 200 : 400, result, { headers: ecdsaGatewayServerTimingHeaders(serverTiming) });
|
|
9319
9319
|
}
|
|
9320
9320
|
async function handleRouterApiWalletRegistrationNearAdmission(input) {
|
|
Binary file
|
|
@@ -31,8 +31,32 @@ type D1WalletRegistrationCommitInput = (D1WalletRegistrationCommitBase & D1Walle
|
|
|
31
31
|
readonly kind: 'email_otp_wallet_registration_commit_v1';
|
|
32
32
|
readonly emailOtp: D1EmailOtpRegistrationCommitPlan;
|
|
33
33
|
});
|
|
34
|
+
/** A passkey claim that shared authority granted for one ceremony before its finalization. */
|
|
35
|
+
export type HeldRegistrationPasskeyClaim = {
|
|
36
|
+
readonly walletId: string;
|
|
37
|
+
readonly rpId: string;
|
|
38
|
+
readonly credentialIdB64u: string;
|
|
39
|
+
};
|
|
34
40
|
export interface D1WalletRegistrationCommitStore {
|
|
35
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Claims the authority's passkey through shared authority, unless `held`
|
|
43
|
+
* records that the same claim was already granted for this ceremony.
|
|
44
|
+
*/
|
|
45
|
+
reserveAuthority(authority: RegistrationAuthority, held?: HeldRegistrationPasskeyClaim | null): Promise<ReservedWalletRegistrationCommit>;
|
|
46
|
+
/**
|
|
47
|
+
* Claims a verified passkey before finalization and records a granted
|
|
48
|
+
* claim. A refusal records nothing, so finalization claims again and
|
|
49
|
+
* reports it.
|
|
50
|
+
*/
|
|
51
|
+
claimPasskeyAhead(input: {
|
|
52
|
+
readonly registrationCeremonyId: string;
|
|
53
|
+
readonly authority: RegistrationAuthority;
|
|
54
|
+
readonly nowMs: number;
|
|
55
|
+
}): Promise<void>;
|
|
56
|
+
prepareHeldPasskeyClaimRead(registrationCeremonyId: string): D1PreparedStatementLike;
|
|
57
|
+
parseHeldPasskeyClaimRead(result: D1ResultLike<Record<string, unknown>>): HeldRegistrationPasskeyClaim | null;
|
|
58
|
+
/** Removes the record of a finalized ceremony. Shared authority keeps the claim. */
|
|
59
|
+
deleteHeldPasskeyClaim(registrationCeremonyId: string): Promise<void>;
|
|
36
60
|
}
|
|
37
61
|
export declare class CloudflareD1WalletRegistrationCommitStore implements D1WalletRegistrationCommitStore {
|
|
38
62
|
private readonly database;
|
|
@@ -46,7 +70,15 @@ export declare class CloudflareD1WalletRegistrationCommitStore implements D1Wall
|
|
|
46
70
|
readonly projectId: string;
|
|
47
71
|
readonly envId: string;
|
|
48
72
|
});
|
|
49
|
-
reserveAuthority(authority: RegistrationAuthority): Promise<ReservedWalletRegistrationCommit>;
|
|
73
|
+
reserveAuthority(authority: RegistrationAuthority, held?: HeldRegistrationPasskeyClaim | null): Promise<ReservedWalletRegistrationCommit>;
|
|
74
|
+
claimPasskeyAhead(input: {
|
|
75
|
+
readonly registrationCeremonyId: string;
|
|
76
|
+
readonly authority: RegistrationAuthority;
|
|
77
|
+
readonly nowMs: number;
|
|
78
|
+
}): Promise<void>;
|
|
79
|
+
prepareHeldPasskeyClaimRead(registrationCeremonyId: string): D1PreparedStatementLike;
|
|
80
|
+
parseHeldPasskeyClaimRead(result: D1ResultLike<Record<string, unknown>>): HeldRegistrationPasskeyClaim | null;
|
|
81
|
+
deleteHeldPasskeyClaim(registrationCeremonyId: string): Promise<void>;
|
|
50
82
|
private commit;
|
|
51
83
|
}
|
|
52
84
|
export {};
|
package/dist/types/wallet-server/src/router/cloudflare/d1/registration/d1WalletRegistrationRead.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { WalletRegistrationHomeAuthority } from '../../../domains/walletReg
|
|
|
3
3
|
import type { CloudflareD1RegistrationCeremonyIntentStore } from './d1RegistrationCeremonyStore';
|
|
4
4
|
import type { D1WalletExecutionAuthority } from './d1WalletExecutionAuthority';
|
|
5
5
|
import type { D1WalletOperationStore } from './d1WalletOperationStore';
|
|
6
|
+
import type { D1WalletRegistrationCommitStore } from './d1WalletRegistrationCommitStore';
|
|
6
7
|
import type { D1WalletRegistrationNearProvisioningSideEffectRecord } from './d1WalletRegistrationService';
|
|
7
8
|
import type { CloudflareD1Ed25519YaoLifecycleDecisionStoreV1 } from '../ed25519Yao/d1Ed25519YaoLifecycleDecisionStore';
|
|
8
9
|
/** Read admission and immutable finalization state from one D1 snapshot. */
|
|
@@ -12,7 +13,8 @@ export declare class D1WalletRegistrationRead {
|
|
|
12
13
|
readonly operations: D1WalletOperationStore<D1WalletRegistrationNearProvisioningSideEffectRecord>;
|
|
13
14
|
private readonly getCeremonies;
|
|
14
15
|
private readonly decisions;
|
|
15
|
-
|
|
16
|
+
private readonly passkeyClaims;
|
|
17
|
+
constructor(database: D1DatabaseLike, authority: D1WalletExecutionAuthority | null, operations: D1WalletOperationStore<D1WalletRegistrationNearProvisioningSideEffectRecord>, getCeremonies: () => CloudflareD1RegistrationCeremonyIntentStore, decisions: CloudflareD1Ed25519YaoLifecycleDecisionStoreV1, passkeyClaims: Pick<D1WalletRegistrationCommitStore, 'prepareHeldPasskeyClaimRead' | 'parseHeldPasskeyClaimRead'>);
|
|
16
18
|
readRespond(home: Parameters<WalletRegistrationHomeAuthority['admitHome']>[0]): Promise<{
|
|
17
19
|
readonly ok: false;
|
|
18
20
|
readonly code: string;
|
|
@@ -37,6 +39,7 @@ export declare class D1WalletRegistrationRead {
|
|
|
37
39
|
ok: true;
|
|
38
40
|
operation: import("../../../cloudflare-adaptor").VersionedJsonRecordReadResult<D1WalletRegistrationNearProvisioningSideEffectRecord>;
|
|
39
41
|
decision: import("../../../domains/ed25519Yao/capabilityLifecycle/routerAbEd25519YaoLifecycleDecision").RouterAbEd25519YaoLifecycleDecisionV1 | null;
|
|
42
|
+
heldPasskeyClaim: import("./d1WalletRegistrationCommitStore").HeldRegistrationPasskeyClaim | null;
|
|
40
43
|
readCeremony: () => Promise<import("../../../../core/RegistrationCeremonyStore").StoredWalletRegistrationCeremony | null>;
|
|
41
44
|
}>;
|
|
42
45
|
}
|
|
@@ -260,7 +260,7 @@ export declare class CloudflareD1WalletRegistrationService {
|
|
|
260
260
|
provisionEd25519YaoWalletSession(request: RouterAbEd25519YaoVerifiedWalletUnlockRequestV1): Promise<RouterAbEd25519YaoVerifiedWalletUnlockResponseV1>;
|
|
261
261
|
setupWalletRegistration(input: WalletRegistrationSetupInput): Promise<WalletRegistrationSetupResponseV2>;
|
|
262
262
|
authorizeWalletRegistrationNearAdmission(input: WalletRegistrationNearAdmissionInput): Promise<WalletRegistrationNearAdmissionResponseV2>;
|
|
263
|
-
respondWalletRegistration(input: WalletRegistrationRespondInput, traceContext?: RouterAbTraceContextV1, timingSink?: Array<readonly [string, number]>): Promise<WalletRegistrationRespondResponseV2>;
|
|
263
|
+
respondWalletRegistration(input: WalletRegistrationRespondInput, traceContext?: RouterAbTraceContextV1, timingSink?: Array<readonly [string, number]>, waitUntil?: (promise: Promise<unknown>) => void): Promise<WalletRegistrationRespondResponseV2>;
|
|
264
264
|
/** For callers that must read the context before finalize tombstones it. */
|
|
265
265
|
private registrationOwnerProofWithContext;
|
|
266
266
|
authorizeNearRegistrationContinuation(input: {
|
|
@@ -1031,7 +1031,7 @@ export interface RouterApiWalletRegistrationService {
|
|
|
1031
1031
|
listWalletEcdsaKeyFactsInventory(input: RouterApiMethodTypes['listWalletEcdsaKeyFactsInventory']['input']): Promise<RouterApiMethodTypes['listWalletEcdsaKeyFactsInventory']['result']>;
|
|
1032
1032
|
readActiveEmailOtpEnrollment(input: RouterApiMethodTypes['readActiveEmailOtpEnrollment']['input']): Promise<RouterApiMethodTypes['readActiveEmailOtpEnrollment']['result']>;
|
|
1033
1033
|
setupWalletRegistration(input: WalletRegistrationSetupInput): Promise<RouteResponse>;
|
|
1034
|
-
respondWalletRegistration(input: WalletRegistrationRespondInput, traceContext?: RouterAbTraceContextV1, serverTiming?: Array<readonly [string, number]>): Promise<WalletRegistrationRespondResponseV2>;
|
|
1034
|
+
respondWalletRegistration(input: WalletRegistrationRespondInput, traceContext?: RouterAbTraceContextV1, serverTiming?: Array<readonly [string, number]>, waitUntil?: (promise: Promise<unknown>) => void): Promise<WalletRegistrationRespondResponseV2>;
|
|
1035
1035
|
authorizeWalletRegistrationNearAdmission(input: WalletRegistrationNearAdmissionInput): Promise<WalletRegistrationNearAdmissionResponseV2>;
|
|
1036
1036
|
activateWalletRegistration(input: WalletRegistrationActivateInput, traceContext?: RouterAbTraceContextV1, serverTiming?: Array<readonly [string, number]>): Promise<WalletRegistrationActivateResponseV2>;
|
|
1037
1037
|
completeWalletRegistrationNearProvisioning(input: WalletRegistrationNearProvisioningInput, serverTiming?: Array<readonly [string, number]>, waitUntil?: (promise: Promise<unknown>) => void): Promise<WalletRegistrationNearProvisioningResponseV2>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
-- A passkey ownership claim that shared authority granted for one registration
|
|
2
|
+
-- ceremony before its finalization. Finalization reads the row and skips its
|
|
3
|
+
-- own claim request. A missing row means only that finalization claims again.
|
|
4
|
+
CREATE TABLE registration_passkey_claims (
|
|
5
|
+
namespace TEXT NOT NULL,
|
|
6
|
+
org_id TEXT NOT NULL,
|
|
7
|
+
project_id TEXT NOT NULL,
|
|
8
|
+
env_id TEXT NOT NULL,
|
|
9
|
+
registration_ceremony_id TEXT NOT NULL,
|
|
10
|
+
wallet_id TEXT NOT NULL,
|
|
11
|
+
rp_id TEXT NOT NULL,
|
|
12
|
+
credential_id_b64u TEXT NOT NULL,
|
|
13
|
+
claimed_at_ms INTEGER NOT NULL,
|
|
14
|
+
PRIMARY KEY (namespace, org_id, project_id, env_id, registration_ceremony_id)
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
-- Shared authority never reassigns a claim, so a recorded claim never changes.
|
|
18
|
+
CREATE TRIGGER registration_passkey_claims_immutable
|
|
19
|
+
BEFORE UPDATE ON registration_passkey_claims
|
|
20
|
+
BEGIN SELECT RAISE(ABORT, 'registration_passkey_claims_immutable'); END;
|