@dynamic-labs/waas 4.89.0 → 4.91.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/CHANGELOG.md +36 -0
- package/package.cjs +1 -1
- package/package.js +1 -1
- package/package.json +13 -13
- package/src/DynamicWaasMixin.cjs +73 -17
- package/src/DynamicWaasMixin.d.ts +15 -1
- package/src/DynamicWaasMixin.js +74 -18
- package/utils/tokenHasPendingAuthScope.cjs +55 -0
- package/utils/tokenHasPendingAuthScope.d.ts +17 -0
- package/utils/tokenHasPendingAuthScope.js +51 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,40 @@
|
|
|
1
1
|
|
|
2
|
+
## [4.91.0](https://github.com/dynamic-labs/dynamic-auth/compare/v4.90.0...v4.91.0) (2026-06-23)
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
* **midnight:** add WaaS layer to @dynamic-labs/midnight ([#11006](https://github.com/dynamic-labs/dynamic-auth/issues/11006)) ([01b9d16](https://github.com/dynamic-labs/dynamic-auth/commit/01b9d16d0ba92a141a9be546ca00cca4029894d5))
|
|
8
|
+
* **sdk-react-core:** widget support for Midnight multi-address display + export hiding ([#11008](https://github.com/dynamic-labs/dynamic-auth/issues/11008)) ([87320b4](https://github.com/dynamic-labs/dynamic-auth/commit/87320b4d6e286b88e17f144641346e16a27e9e71))
|
|
9
|
+
* **waas:** wire session public key through host backup ops for keyshare diagnostics [DYNT-1280] ([#11675](https://github.com/dynamic-labs/dynamic-auth/issues/11675)) ([4f7981c](https://github.com/dynamic-labs/dynamic-auth/commit/4f7981c009930469dec7b3dec93f20f99bbe5498))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
* **ethereum-aa:** make ZeroDevConnector inert instead of throwing when ZeroDev config is absent ([#11610](https://github.com/dynamic-labs/dynamic-auth/issues/11610)) ([a3aab6a](https://github.com/dynamic-labs/dynamic-auth/commit/a3aab6a97e2e996f96b725f9336704b844f79603))
|
|
15
|
+
* **react-native-extension:** fall back to react-native-webview when embedded native module is unavailable ([#11683](https://github.com/dynamic-labs/dynamic-auth/issues/11683)) ([3f8d92c](https://github.com/dynamic-labs/dynamic-auth/commit/3f8d92ca0c327128b16e3355ab2f4e63d2c8e731))
|
|
16
|
+
* **sdk-react-core:** warn instead of throwing when ZeroDev connector is absent ([#11611](https://github.com/dynamic-labs/dynamic-auth/issues/11611)) ([5836252](https://github.com/dynamic-labs/dynamic-auth/commit/58362524e45205399c0fdc5b92414190d1929e0b))
|
|
17
|
+
* **waas-evm:** sign EOA transactions with the resolved chainId, not the connector default ([#11653](https://github.com/dynamic-labs/dynamic-auth/issues/11653)) ([e634098](https://github.com/dynamic-labs/dynamic-auth/commit/e634098f0f23f4c53f92a580db6f4aacbd6b1f92))
|
|
18
|
+
|
|
19
|
+
## [4.90.0](https://github.com/dynamic-labs/dynamic-auth/compare/v4.89.0...v4.90.0) (2026-06-19)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### Features
|
|
23
|
+
|
|
24
|
+
* add microsoft to social providers list ([#11631](https://github.com/dynamic-labs/dynamic-auth/issues/11631)) ([761b82d](https://github.com/dynamic-labs/dynamic-auth/commit/761b82dea2e2e86ec1c8b3fc8bab32ed72492132))
|
|
25
|
+
* **sdk-react-core:** preconnect to waas iframe origin at auth-flow start ([#11549](https://github.com/dynamic-labs/dynamic-auth/issues/11549)) ([1bb810c](https://github.com/dynamic-labs/dynamic-auth/commit/1bb810c4eb3d592b43f62c1eaf5450a0e42981c9)), closes [dynamic-labs/dynamic-wallet-sdk#1314](https://github.com/dynamic-labs/dynamic-wallet-sdk/issues/1314)
|
|
26
|
+
* **waas:** opt into SDK eager key-share recovery, drop host RECOVER loop (DYNT-1125) ([#11620](https://github.com/dynamic-labs/dynamic-auth/issues/11620)) ([4bef955](https://github.com/dynamic-labs/dynamic-auth/commit/4bef955b902a7e484ba8bd6323582843bb692a17))
|
|
27
|
+
* **wallet-connector-core:** add isMidnightConnector util + Midnight interface hooks ([#11005](https://github.com/dynamic-labs/dynamic-auth/issues/11005)) ([55117a8](https://github.com/dynamic-labs/dynamic-auth/commit/55117a8cf59640d00b358f68be8df6b29231fab5))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
### Bug Fixes
|
|
31
|
+
|
|
32
|
+
* **sdk-react-core:** gate smart-wallet init on user:basic scope ([#11624](https://github.com/dynamic-labs/dynamic-auth/issues/11624)) ([9f891a4](https://github.com/dynamic-labs/dynamic-auth/commit/9f891a46a191532acdd4a91b74c4586089ef0e1a)), closes [#11623](https://github.com/dynamic-labs/dynamic-auth/issues/11623) [#11623](https://github.com/dynamic-labs/dynamic-auth/issues/11623) [#11626](https://github.com/dynamic-labs/dynamic-auth/issues/11626)
|
|
33
|
+
* **sdk-react-core:** search wallet list by metadata name in addition to connector name ([#11638](https://github.com/dynamic-labs/dynamic-auth/issues/11638)) ([a647614](https://github.com/dynamic-labs/dynamic-auth/commit/a647614bcba769e09ef59bf1ddb925ce9835842e))
|
|
34
|
+
* **waas:** rebuild WaaS client on session change after step-up reauth ([#11581](https://github.com/dynamic-labs/dynamic-auth/issues/11581)) ([2b7093e](https://github.com/dynamic-labs/dynamic-auth/commit/2b7093e27f57f1dfcdd303361d436d9bf33e28a9))
|
|
35
|
+
* **waas:** refuse to build WaaS client while auth scope is pending (createRooms 401 chokepoint) ([#11623](https://github.com/dynamic-labs/dynamic-auth/issues/11623)) ([08755f0](https://github.com/dynamic-labs/dynamic-auth/commit/08755f0bda2b74c7bcdff3d0ac9c228a5205ad6e)), closes [#11624](https://github.com/dynamic-labs/dynamic-auth/issues/11624)
|
|
36
|
+
* **wallet-connect:** skip address comparison on reconnect when expectedAddress is empty ([#11639](https://github.com/dynamic-labs/dynamic-auth/issues/11639)) ([8d665cf](https://github.com/dynamic-labs/dynamic-auth/commit/8d665cf3bf1fb0e498d71f14cc8a53d91593c23d))
|
|
37
|
+
|
|
2
38
|
## [4.89.0](https://github.com/dynamic-labs/dynamic-auth/compare/v4.88.6...v4.89.0) (2026-06-16)
|
|
3
39
|
|
|
4
40
|
|
package/package.cjs
CHANGED
package/package.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dynamic-labs/waas",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.91.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"author": "Dynamic Labs, Inc.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -16,18 +16,18 @@
|
|
|
16
16
|
"./package.json": "./package.json"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@dynamic-labs-sdk/client": "1.
|
|
20
|
-
"@dynamic-labs/assert-package-version": "4.
|
|
21
|
-
"@dynamic-labs/sdk-api-core": "0.0.
|
|
22
|
-
"@dynamic-labs-wallet/browser-wallet-client": "1.0.
|
|
23
|
-
"@dynamic-labs-wallet/forward-mpc-client": "0.
|
|
24
|
-
"@dynamic-labs/ethereum-core": "4.
|
|
25
|
-
"@dynamic-labs/logger": "4.
|
|
26
|
-
"@dynamic-labs/solana-core": "4.
|
|
27
|
-
"@dynamic-labs/sui-core": "4.
|
|
28
|
-
"@dynamic-labs/utils": "4.
|
|
29
|
-
"@dynamic-labs/wallet-book": "4.
|
|
30
|
-
"@dynamic-labs/wallet-connector-core": "4.
|
|
19
|
+
"@dynamic-labs-sdk/client": "1.12.1",
|
|
20
|
+
"@dynamic-labs/assert-package-version": "4.91.0",
|
|
21
|
+
"@dynamic-labs/sdk-api-core": "0.0.1046",
|
|
22
|
+
"@dynamic-labs-wallet/browser-wallet-client": "1.0.44",
|
|
23
|
+
"@dynamic-labs-wallet/forward-mpc-client": "0.12.0",
|
|
24
|
+
"@dynamic-labs/ethereum-core": "4.91.0",
|
|
25
|
+
"@dynamic-labs/logger": "4.91.0",
|
|
26
|
+
"@dynamic-labs/solana-core": "4.91.0",
|
|
27
|
+
"@dynamic-labs/sui-core": "4.91.0",
|
|
28
|
+
"@dynamic-labs/utils": "4.91.0",
|
|
29
|
+
"@dynamic-labs/wallet-book": "4.91.0",
|
|
30
|
+
"@dynamic-labs/wallet-connector-core": "4.91.0"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {}
|
|
33
33
|
}
|
package/src/DynamicWaasMixin.cjs
CHANGED
|
@@ -11,6 +11,7 @@ var _package = require('../package.cjs');
|
|
|
11
11
|
var constants = require('../utils/constants.cjs');
|
|
12
12
|
var createWaasClientSecureStorage = require('../utils/createWaasClientSecureStorage.cjs');
|
|
13
13
|
var instrumentation = require('../utils/instrumentation.cjs');
|
|
14
|
+
var tokenHasPendingAuthScope = require('../utils/tokenHasPendingAuthScope.cjs');
|
|
14
15
|
|
|
15
16
|
// Fallback error code used when a thrown value carries no code, or when
|
|
16
17
|
// formatting the error itself fails.
|
|
@@ -126,9 +127,12 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
126
127
|
setGetSignedSessionIdFunction(getSignedSessionId) {
|
|
127
128
|
this.getSignedSessionId = getSignedSessionId;
|
|
128
129
|
}
|
|
130
|
+
setGetSessionPublicKeyFunction(getSessionPublicKey) {
|
|
131
|
+
this.getSessionPublicKey = getSessionPublicKey;
|
|
132
|
+
}
|
|
129
133
|
delegateKeyShares(_a) {
|
|
130
134
|
return _tslib.__awaiter(this, arguments, void 0, function* ({ accountAddress, password, }) {
|
|
131
|
-
var _b, _c;
|
|
135
|
+
var _b, _c, _d;
|
|
132
136
|
if (!accountAddress) {
|
|
133
137
|
throw new Error('Account address is required');
|
|
134
138
|
}
|
|
@@ -146,6 +150,7 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
146
150
|
accountAddress,
|
|
147
151
|
authToken,
|
|
148
152
|
password: resolvedPassword,
|
|
153
|
+
sessionPublicKey: yield ((_d = this.getSessionPublicKey) === null || _d === void 0 ? void 0 : _d.call(this)),
|
|
149
154
|
signedSessionId,
|
|
150
155
|
});
|
|
151
156
|
});
|
|
@@ -165,6 +170,7 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
165
170
|
ALEO: 'ALEO',
|
|
166
171
|
BTC: 'BTC',
|
|
167
172
|
EVM: 'EVM',
|
|
173
|
+
MIDNIGHT: 'MIDNIGHT',
|
|
168
174
|
SOL: 'SVM',
|
|
169
175
|
STELLAR: 'STELLAR',
|
|
170
176
|
SUI: 'SUI',
|
|
@@ -194,12 +200,17 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
194
200
|
baseClientKeysharesRelayApiUrl: this.baseClientKeysharesRelayApiUrl,
|
|
195
201
|
baseMPCRelayApiUrl: this.relayUrl || constants.DEFAULT_BASE_MPC_RELAY_API_URL,
|
|
196
202
|
chainName: this.chainName,
|
|
203
|
+
// Opt into the SDK self-driving eager key-share recovery on auth-token
|
|
204
|
+
// arrival, so the host no longer runs its own per-wallet RECOVER loop.
|
|
205
|
+
eagerlyRecoverKeyShares: true,
|
|
197
206
|
environmentId: this.environmentId,
|
|
198
207
|
sdkVersion: _package.version,
|
|
199
|
-
}, Object.assign(Object.assign(Object.assign({}, (utils.PlatformService.isWaasSecureStorageSupported
|
|
208
|
+
}, Object.assign(Object.assign(Object.assign(Object.assign({}, (utils.PlatformService.isWaasSecureStorageSupported
|
|
200
209
|
? { secureStorage: createWaasClientSecureStorage.createWaasClientSecureStorage() }
|
|
201
210
|
: {})), (this.getSignedSessionId
|
|
202
211
|
? { getSignedSessionId: this.getSignedSessionId }
|
|
212
|
+
: {})), (this.getSessionPublicKey
|
|
213
|
+
? { getSessionPublicKey: this.getSessionPublicKey }
|
|
203
214
|
: {})), (this.onUnauthorized
|
|
204
215
|
? { onUnauthorized: this.onUnauthorized }
|
|
205
216
|
: {})));
|
|
@@ -212,9 +223,29 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
212
223
|
return client;
|
|
213
224
|
});
|
|
214
225
|
}
|
|
215
|
-
getWaasWalletClient(
|
|
216
|
-
return _tslib.__awaiter(this,
|
|
226
|
+
getWaasWalletClient(traceContext_1) {
|
|
227
|
+
return _tslib.__awaiter(this, arguments, void 0, function* (traceContext, { forceRebuild = false } = {}) {
|
|
228
|
+
var _a;
|
|
229
|
+
// forceRebuild drops the cached client so the iframe re-auths with the
|
|
230
|
+
// fresh session (e.g. after a step-up reauth) instead of 401-ing on the
|
|
231
|
+
// stale cached token. Callers must only force a rebuild when a token is
|
|
232
|
+
// available (see getWaasWalletConnector's live-token guard); otherwise
|
|
233
|
+
// createDynamicWaasClient would throw "Auth token is required".
|
|
234
|
+
if (forceRebuild) {
|
|
235
|
+
this.dynamicWaasClient = undefined;
|
|
236
|
+
}
|
|
217
237
|
if (!this.dynamicWaasClient) {
|
|
238
|
+
// Chokepoint: never build + initialize the iframe client while the auth
|
|
239
|
+
// token is still pending MFA/KYC/device registration. The iframe's
|
|
240
|
+
// init-time room-cache pre-warm fires `createRooms` using the token
|
|
241
|
+
// captured at construction — before any per-operation token resync
|
|
242
|
+
// exists to correct it. Built during the pending window, that pre-warm
|
|
243
|
+
// 401s and trips the iframe's unauthorized → forced-logout path. Every
|
|
244
|
+
// WaaS caller funnels through here, so this single guard covers auto
|
|
245
|
+
// wallet creation, ZeroDev smart-wallet init, getWallet, etc.
|
|
246
|
+
if (tokenHasPendingAuthScope.tokenHasPendingAuthScope((_a = this.getAuthToken) === null || _a === void 0 ? void 0 : _a.call(this))) {
|
|
247
|
+
throw new utils.WaasAuthScopePendingError();
|
|
248
|
+
}
|
|
218
249
|
this.dynamicWaasClient = yield this.createDynamicWaasClient(traceContext);
|
|
219
250
|
}
|
|
220
251
|
return this.dynamicWaasClient;
|
|
@@ -320,7 +351,7 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
320
351
|
}
|
|
321
352
|
backupKeySharesToGoogleDrive(_a) {
|
|
322
353
|
return _tslib.__awaiter(this, arguments, void 0, function* ({ accountAddress, password, googleDriveAccessToken, }) {
|
|
323
|
-
var _b, _c;
|
|
354
|
+
var _b, _c, _d;
|
|
324
355
|
if (!accountAddress) {
|
|
325
356
|
throw new utils.DynamicError('Account address is required');
|
|
326
357
|
}
|
|
@@ -335,6 +366,7 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
335
366
|
authToken: (_c = this.getAuthToken) === null || _c === void 0 ? void 0 : _c.call(this),
|
|
336
367
|
googleDriveAccessToken,
|
|
337
368
|
password: resolvedPassword,
|
|
369
|
+
sessionPublicKey: yield ((_d = this.getSessionPublicKey) === null || _d === void 0 ? void 0 : _d.call(this)),
|
|
338
370
|
signedSessionId,
|
|
339
371
|
});
|
|
340
372
|
});
|
|
@@ -361,7 +393,7 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
361
393
|
}
|
|
362
394
|
backupKeySharesToICloud(_a) {
|
|
363
395
|
return _tslib.__awaiter(this, arguments, void 0, function* ({ accountAddress, password, }) {
|
|
364
|
-
var _b, _c;
|
|
396
|
+
var _b, _c, _d;
|
|
365
397
|
if (!accountAddress) {
|
|
366
398
|
throw new utils.DynamicError('Account address is required');
|
|
367
399
|
}
|
|
@@ -375,6 +407,7 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
375
407
|
accountAddress,
|
|
376
408
|
authToken: (_c = this.getAuthToken) === null || _c === void 0 ? void 0 : _c.call(this),
|
|
377
409
|
password: resolvedPassword,
|
|
410
|
+
sessionPublicKey: yield ((_d = this.getSessionPublicKey) === null || _d === void 0 ? void 0 : _d.call(this)),
|
|
378
411
|
signedSessionId,
|
|
379
412
|
});
|
|
380
413
|
});
|
|
@@ -401,7 +434,7 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
401
434
|
}
|
|
402
435
|
refreshWalletAccountShares(_a) {
|
|
403
436
|
return _tslib.__awaiter(this, arguments, void 0, function* ({ accountAddress, password, }) {
|
|
404
|
-
var _b, _c, _d, _e, _f, _g;
|
|
437
|
+
var _b, _c, _d, _e, _f, _g, _h;
|
|
405
438
|
if (!accountAddress) {
|
|
406
439
|
throw new utils.DynamicError('Account address is required');
|
|
407
440
|
}
|
|
@@ -429,13 +462,14 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
429
462
|
elevatedAccessToken,
|
|
430
463
|
mfaToken,
|
|
431
464
|
password: resolvedPassword,
|
|
465
|
+
sessionPublicKey: yield ((_h = this.getSessionPublicKey) === null || _h === void 0 ? void 0 : _h.call(this)),
|
|
432
466
|
signedSessionId: backupSignedSessionId,
|
|
433
467
|
});
|
|
434
468
|
});
|
|
435
469
|
}
|
|
436
470
|
reshareWalletAccountShares(_a) {
|
|
437
471
|
return _tslib.__awaiter(this, arguments, void 0, function* ({ accountAddress, thresholdSignatureScheme, password, }) {
|
|
438
|
-
var _b, _c, _d, _e;
|
|
472
|
+
var _b, _c, _d, _e, _f;
|
|
439
473
|
if (!accountAddress) {
|
|
440
474
|
throw new utils.DynamicError('Account address is required');
|
|
441
475
|
}
|
|
@@ -465,13 +499,14 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
465
499
|
newThresholdSignatureScheme: thresholdSignatureScheme,
|
|
466
500
|
oldThresholdSignatureScheme,
|
|
467
501
|
password: resolvedPassword,
|
|
502
|
+
sessionPublicKey: yield ((_f = this.getSessionPublicKey) === null || _f === void 0 ? void 0 : _f.call(this)),
|
|
468
503
|
signedSessionId,
|
|
469
504
|
});
|
|
470
505
|
});
|
|
471
506
|
}
|
|
472
507
|
revokeDelegation(_a) {
|
|
473
508
|
return _tslib.__awaiter(this, arguments, void 0, function* ({ accountAddress, password, }) {
|
|
474
|
-
var _b, _c;
|
|
509
|
+
var _b, _c, _d;
|
|
475
510
|
if (!accountAddress) {
|
|
476
511
|
throw new utils.DynamicError('Account address is required');
|
|
477
512
|
}
|
|
@@ -485,6 +520,7 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
485
520
|
accountAddress,
|
|
486
521
|
authToken: (_c = this.getAuthToken) === null || _c === void 0 ? void 0 : _c.call(this),
|
|
487
522
|
password: resolvedPassword,
|
|
523
|
+
sessionPublicKey: yield ((_d = this.getSessionPublicKey) === null || _d === void 0 ? void 0 : _d.call(this)),
|
|
488
524
|
signedSessionId,
|
|
489
525
|
});
|
|
490
526
|
});
|
|
@@ -506,7 +542,7 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
506
542
|
const encryptedWallets = wallets.filter((w) => { var _a; return (_a = w.clientKeySharesBackupInfo) === null || _a === void 0 ? void 0 : _a.passwordEncrypted; });
|
|
507
543
|
const passwordUpdateBatchId = crypto.randomUUID();
|
|
508
544
|
yield Promise.all(encryptedWallets.map((wallet) => _tslib.__awaiter(this, void 0, void 0, function* () {
|
|
509
|
-
var _b;
|
|
545
|
+
var _b, _c;
|
|
510
546
|
const signedSessionId = yield this.getSignedSessionId();
|
|
511
547
|
yield walletClient.updatePassword({
|
|
512
548
|
accountAddress: wallet.accountAddress,
|
|
@@ -514,6 +550,7 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
514
550
|
existingPassword: resolvedExistingPassword,
|
|
515
551
|
newPassword,
|
|
516
552
|
passwordUpdateBatchId,
|
|
553
|
+
sessionPublicKey: yield ((_c = this.getSessionPublicKey) === null || _c === void 0 ? void 0 : _c.call(this)),
|
|
517
554
|
signedSessionId,
|
|
518
555
|
});
|
|
519
556
|
})));
|
|
@@ -531,13 +568,14 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
531
568
|
const wallets = yield walletClient.getAllWallets();
|
|
532
569
|
const passwordUpdateBatchId = crypto.randomUUID();
|
|
533
570
|
yield Promise.all(wallets.map((wallet) => _tslib.__awaiter(this, void 0, void 0, function* () {
|
|
534
|
-
var _b;
|
|
571
|
+
var _b, _c;
|
|
535
572
|
const signedSessionId = yield this.getSignedSessionId();
|
|
536
573
|
yield walletClient.setPassword({
|
|
537
574
|
accountAddress: wallet.accountAddress,
|
|
538
575
|
authToken: (_b = this.getAuthToken) === null || _b === void 0 ? void 0 : _b.call(this),
|
|
539
576
|
newPassword,
|
|
540
577
|
passwordUpdateBatchId,
|
|
578
|
+
sessionPublicKey: yield ((_c = this.getSessionPublicKey) === null || _c === void 0 ? void 0 : _c.call(this)),
|
|
541
579
|
signedSessionId,
|
|
542
580
|
});
|
|
543
581
|
})));
|
|
@@ -612,9 +650,18 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
612
650
|
}
|
|
613
651
|
endSession(reason) {
|
|
614
652
|
return _tslib.__awaiter(this, void 0, void 0, function* () {
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
653
|
+
// Building the wallet client requires an auth token (createDynamicWaasClient
|
|
654
|
+
// throws in header auth mode when none is present); the new scope guard in
|
|
655
|
+
// getWaasWalletClient also throws during pending-auth (MFA/KYC/device). On
|
|
656
|
+
// logout we must not let either throw abort logout — the security-critical
|
|
657
|
+
// key share removal below does not need the client. Only the iframe cleanup
|
|
658
|
+
// does, so we proceed without it if the client can't be obtained.
|
|
659
|
+
let waasClient;
|
|
660
|
+
try {
|
|
661
|
+
waasClient = yield this.getWaasWalletClient();
|
|
662
|
+
}
|
|
663
|
+
catch (error) {
|
|
664
|
+
this.instrument('[endSession] getWaasWalletClient failed; proceeding', Object.assign({ key: 'endSession-getClient-failed', time: 0 }, this.buildErrorInstrumentContext(error)));
|
|
618
665
|
}
|
|
619
666
|
// When a session token expires, we preserve key shares in storage instead
|
|
620
667
|
// of clearing them. Customers with short-lived sessions (minutes) were
|
|
@@ -646,9 +693,13 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
646
693
|
// iframe cleanup's request-channel timeout. The iframe handler already
|
|
647
694
|
// swallows its own errors, and the DOM teardown obsoletes anything it
|
|
648
695
|
// misses. Promise.resolve guards against a non-promise return in tests.
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
696
|
+
// Skipped when the client could not be built/obtained (no token, or
|
|
697
|
+
// pending-auth scope guard) — there is no iframe to tear down then.
|
|
698
|
+
if (waasClient) {
|
|
699
|
+
void Promise.resolve(waasClient.cleanup()).catch((error) => {
|
|
700
|
+
this.instrument('[endSession] background iframe cleanup failed', Object.assign({ key: 'endSession-cleanup-failed', time: 0 }, this.buildErrorInstrumentContext(error)));
|
|
701
|
+
});
|
|
702
|
+
}
|
|
652
703
|
}
|
|
653
704
|
this.dynamicWaasClient = undefined;
|
|
654
705
|
});
|
|
@@ -755,6 +806,11 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
755
806
|
}
|
|
756
807
|
});
|
|
757
808
|
}
|
|
809
|
+
getPrivateBalance() {
|
|
810
|
+
return _tslib.__awaiter(this, void 0, void 0, function* () {
|
|
811
|
+
throw new utils.DynamicError(`getPrivateBalance is not supported for ${this.chainName} wallets`);
|
|
812
|
+
});
|
|
813
|
+
}
|
|
758
814
|
}
|
|
759
815
|
return DynamicWaasMixin;
|
|
760
816
|
};
|
|
@@ -15,6 +15,7 @@ export declare const withDynamicWaas: <T extends abstract new (...args: any[]) =
|
|
|
15
15
|
overrideKey: string;
|
|
16
16
|
isEmbeddedWallet: boolean;
|
|
17
17
|
getSignedSessionId?: (() => Promise<string>) | undefined;
|
|
18
|
+
getSessionPublicKey?: (() => Promise<string | undefined>) | undefined;
|
|
18
19
|
getMfaToken?: ((props?: {
|
|
19
20
|
mfaAction?: MFAAction;
|
|
20
21
|
}) => Promise<string | undefined>) | undefined;
|
|
@@ -71,12 +72,15 @@ export declare const withDynamicWaas: <T extends abstract new (...args: any[]) =
|
|
|
71
72
|
setBaseClientKeysharesRelayApiUrl(baseClientKeysharesRelayApiUrl?: string): void;
|
|
72
73
|
setRelayUrl(relayUrl: string): void;
|
|
73
74
|
setGetSignedSessionIdFunction(getSignedSessionId: () => Promise<string>): void;
|
|
75
|
+
setGetSessionPublicKeyFunction(getSessionPublicKey: () => Promise<string | undefined>): void;
|
|
74
76
|
delegateKeyShares({ accountAddress, password, }: {
|
|
75
77
|
accountAddress: string;
|
|
76
78
|
password?: string;
|
|
77
79
|
}): Promise<void>;
|
|
78
80
|
createDynamicWaasClient(traceContext?: TraceContext): Promise<DynamicWalletClient>;
|
|
79
|
-
getWaasWalletClient(traceContext?: TraceContext
|
|
81
|
+
getWaasWalletClient(traceContext?: TraceContext, { forceRebuild }?: {
|
|
82
|
+
forceRebuild?: boolean;
|
|
83
|
+
}): Promise<DynamicWalletClient>;
|
|
80
84
|
createWalletAccount({ thresholdSignatureScheme, password, bitcoinConfig, }?: {
|
|
81
85
|
thresholdSignatureScheme?: string;
|
|
82
86
|
password?: string;
|
|
@@ -199,4 +203,14 @@ export declare const withDynamicWaas: <T extends abstract new (...args: any[]) =
|
|
|
199
203
|
fn: (timing: InstrumentationTimer) => Promise<T_1>;
|
|
200
204
|
context?: Record<string, any> | undefined;
|
|
201
205
|
}): Promise<T_1>;
|
|
206
|
+
getPrivateBalance(): Promise<{
|
|
207
|
+
unshielded: Record<string, string>;
|
|
208
|
+
shielded: Record<string, string>;
|
|
209
|
+
dust: {
|
|
210
|
+
balance: string;
|
|
211
|
+
cap: string;
|
|
212
|
+
};
|
|
213
|
+
address: string;
|
|
214
|
+
dustSynced: boolean;
|
|
215
|
+
}>;
|
|
202
216
|
}) & T;
|
package/src/DynamicWaasMixin.js
CHANGED
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
import { __awaiter } from '../_virtual/_tslib.js';
|
|
3
3
|
import { DynamicWalletClient } from '@dynamic-labs-wallet/browser-wallet-client';
|
|
4
4
|
import { MFAAction, TokenScope } from '@dynamic-labs/sdk-api-core';
|
|
5
|
-
import { DynamicError, UserRejectedRequestError, PlatformService } from '@dynamic-labs/utils';
|
|
5
|
+
import { DynamicError, UserRejectedRequestError, PlatformService, WaasAuthScopePendingError } from '@dynamic-labs/utils';
|
|
6
6
|
import { version } from '../package.js';
|
|
7
7
|
import { DEFAULT_BASE_API_URL, DEFAULT_BASE_MPC_RELAY_API_URL } from '../utils/constants.js';
|
|
8
8
|
import { createWaasClientSecureStorage } from '../utils/createWaasClientSecureStorage.js';
|
|
9
9
|
import { InstrumentationTimer } from '../utils/instrumentation.js';
|
|
10
|
+
import { tokenHasPendingAuthScope } from '../utils/tokenHasPendingAuthScope.js';
|
|
10
11
|
|
|
11
12
|
// Fallback error code used when a thrown value carries no code, or when
|
|
12
13
|
// formatting the error itself fails.
|
|
@@ -122,9 +123,12 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
122
123
|
setGetSignedSessionIdFunction(getSignedSessionId) {
|
|
123
124
|
this.getSignedSessionId = getSignedSessionId;
|
|
124
125
|
}
|
|
126
|
+
setGetSessionPublicKeyFunction(getSessionPublicKey) {
|
|
127
|
+
this.getSessionPublicKey = getSessionPublicKey;
|
|
128
|
+
}
|
|
125
129
|
delegateKeyShares(_a) {
|
|
126
130
|
return __awaiter(this, arguments, void 0, function* ({ accountAddress, password, }) {
|
|
127
|
-
var _b, _c;
|
|
131
|
+
var _b, _c, _d;
|
|
128
132
|
if (!accountAddress) {
|
|
129
133
|
throw new Error('Account address is required');
|
|
130
134
|
}
|
|
@@ -142,6 +146,7 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
142
146
|
accountAddress,
|
|
143
147
|
authToken,
|
|
144
148
|
password: resolvedPassword,
|
|
149
|
+
sessionPublicKey: yield ((_d = this.getSessionPublicKey) === null || _d === void 0 ? void 0 : _d.call(this)),
|
|
145
150
|
signedSessionId,
|
|
146
151
|
});
|
|
147
152
|
});
|
|
@@ -161,6 +166,7 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
161
166
|
ALEO: 'ALEO',
|
|
162
167
|
BTC: 'BTC',
|
|
163
168
|
EVM: 'EVM',
|
|
169
|
+
MIDNIGHT: 'MIDNIGHT',
|
|
164
170
|
SOL: 'SVM',
|
|
165
171
|
STELLAR: 'STELLAR',
|
|
166
172
|
SUI: 'SUI',
|
|
@@ -190,12 +196,17 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
190
196
|
baseClientKeysharesRelayApiUrl: this.baseClientKeysharesRelayApiUrl,
|
|
191
197
|
baseMPCRelayApiUrl: this.relayUrl || DEFAULT_BASE_MPC_RELAY_API_URL,
|
|
192
198
|
chainName: this.chainName,
|
|
199
|
+
// Opt into the SDK self-driving eager key-share recovery on auth-token
|
|
200
|
+
// arrival, so the host no longer runs its own per-wallet RECOVER loop.
|
|
201
|
+
eagerlyRecoverKeyShares: true,
|
|
193
202
|
environmentId: this.environmentId,
|
|
194
203
|
sdkVersion: version,
|
|
195
|
-
}, Object.assign(Object.assign(Object.assign({}, (PlatformService.isWaasSecureStorageSupported
|
|
204
|
+
}, Object.assign(Object.assign(Object.assign(Object.assign({}, (PlatformService.isWaasSecureStorageSupported
|
|
196
205
|
? { secureStorage: createWaasClientSecureStorage() }
|
|
197
206
|
: {})), (this.getSignedSessionId
|
|
198
207
|
? { getSignedSessionId: this.getSignedSessionId }
|
|
208
|
+
: {})), (this.getSessionPublicKey
|
|
209
|
+
? { getSessionPublicKey: this.getSessionPublicKey }
|
|
199
210
|
: {})), (this.onUnauthorized
|
|
200
211
|
? { onUnauthorized: this.onUnauthorized }
|
|
201
212
|
: {})));
|
|
@@ -208,9 +219,29 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
208
219
|
return client;
|
|
209
220
|
});
|
|
210
221
|
}
|
|
211
|
-
getWaasWalletClient(
|
|
212
|
-
return __awaiter(this,
|
|
222
|
+
getWaasWalletClient(traceContext_1) {
|
|
223
|
+
return __awaiter(this, arguments, void 0, function* (traceContext, { forceRebuild = false } = {}) {
|
|
224
|
+
var _a;
|
|
225
|
+
// forceRebuild drops the cached client so the iframe re-auths with the
|
|
226
|
+
// fresh session (e.g. after a step-up reauth) instead of 401-ing on the
|
|
227
|
+
// stale cached token. Callers must only force a rebuild when a token is
|
|
228
|
+
// available (see getWaasWalletConnector's live-token guard); otherwise
|
|
229
|
+
// createDynamicWaasClient would throw "Auth token is required".
|
|
230
|
+
if (forceRebuild) {
|
|
231
|
+
this.dynamicWaasClient = undefined;
|
|
232
|
+
}
|
|
213
233
|
if (!this.dynamicWaasClient) {
|
|
234
|
+
// Chokepoint: never build + initialize the iframe client while the auth
|
|
235
|
+
// token is still pending MFA/KYC/device registration. The iframe's
|
|
236
|
+
// init-time room-cache pre-warm fires `createRooms` using the token
|
|
237
|
+
// captured at construction — before any per-operation token resync
|
|
238
|
+
// exists to correct it. Built during the pending window, that pre-warm
|
|
239
|
+
// 401s and trips the iframe's unauthorized → forced-logout path. Every
|
|
240
|
+
// WaaS caller funnels through here, so this single guard covers auto
|
|
241
|
+
// wallet creation, ZeroDev smart-wallet init, getWallet, etc.
|
|
242
|
+
if (tokenHasPendingAuthScope((_a = this.getAuthToken) === null || _a === void 0 ? void 0 : _a.call(this))) {
|
|
243
|
+
throw new WaasAuthScopePendingError();
|
|
244
|
+
}
|
|
214
245
|
this.dynamicWaasClient = yield this.createDynamicWaasClient(traceContext);
|
|
215
246
|
}
|
|
216
247
|
return this.dynamicWaasClient;
|
|
@@ -316,7 +347,7 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
316
347
|
}
|
|
317
348
|
backupKeySharesToGoogleDrive(_a) {
|
|
318
349
|
return __awaiter(this, arguments, void 0, function* ({ accountAddress, password, googleDriveAccessToken, }) {
|
|
319
|
-
var _b, _c;
|
|
350
|
+
var _b, _c, _d;
|
|
320
351
|
if (!accountAddress) {
|
|
321
352
|
throw new DynamicError('Account address is required');
|
|
322
353
|
}
|
|
@@ -331,6 +362,7 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
331
362
|
authToken: (_c = this.getAuthToken) === null || _c === void 0 ? void 0 : _c.call(this),
|
|
332
363
|
googleDriveAccessToken,
|
|
333
364
|
password: resolvedPassword,
|
|
365
|
+
sessionPublicKey: yield ((_d = this.getSessionPublicKey) === null || _d === void 0 ? void 0 : _d.call(this)),
|
|
334
366
|
signedSessionId,
|
|
335
367
|
});
|
|
336
368
|
});
|
|
@@ -357,7 +389,7 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
357
389
|
}
|
|
358
390
|
backupKeySharesToICloud(_a) {
|
|
359
391
|
return __awaiter(this, arguments, void 0, function* ({ accountAddress, password, }) {
|
|
360
|
-
var _b, _c;
|
|
392
|
+
var _b, _c, _d;
|
|
361
393
|
if (!accountAddress) {
|
|
362
394
|
throw new DynamicError('Account address is required');
|
|
363
395
|
}
|
|
@@ -371,6 +403,7 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
371
403
|
accountAddress,
|
|
372
404
|
authToken: (_c = this.getAuthToken) === null || _c === void 0 ? void 0 : _c.call(this),
|
|
373
405
|
password: resolvedPassword,
|
|
406
|
+
sessionPublicKey: yield ((_d = this.getSessionPublicKey) === null || _d === void 0 ? void 0 : _d.call(this)),
|
|
374
407
|
signedSessionId,
|
|
375
408
|
});
|
|
376
409
|
});
|
|
@@ -397,7 +430,7 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
397
430
|
}
|
|
398
431
|
refreshWalletAccountShares(_a) {
|
|
399
432
|
return __awaiter(this, arguments, void 0, function* ({ accountAddress, password, }) {
|
|
400
|
-
var _b, _c, _d, _e, _f, _g;
|
|
433
|
+
var _b, _c, _d, _e, _f, _g, _h;
|
|
401
434
|
if (!accountAddress) {
|
|
402
435
|
throw new DynamicError('Account address is required');
|
|
403
436
|
}
|
|
@@ -425,13 +458,14 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
425
458
|
elevatedAccessToken,
|
|
426
459
|
mfaToken,
|
|
427
460
|
password: resolvedPassword,
|
|
461
|
+
sessionPublicKey: yield ((_h = this.getSessionPublicKey) === null || _h === void 0 ? void 0 : _h.call(this)),
|
|
428
462
|
signedSessionId: backupSignedSessionId,
|
|
429
463
|
});
|
|
430
464
|
});
|
|
431
465
|
}
|
|
432
466
|
reshareWalletAccountShares(_a) {
|
|
433
467
|
return __awaiter(this, arguments, void 0, function* ({ accountAddress, thresholdSignatureScheme, password, }) {
|
|
434
|
-
var _b, _c, _d, _e;
|
|
468
|
+
var _b, _c, _d, _e, _f;
|
|
435
469
|
if (!accountAddress) {
|
|
436
470
|
throw new DynamicError('Account address is required');
|
|
437
471
|
}
|
|
@@ -461,13 +495,14 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
461
495
|
newThresholdSignatureScheme: thresholdSignatureScheme,
|
|
462
496
|
oldThresholdSignatureScheme,
|
|
463
497
|
password: resolvedPassword,
|
|
498
|
+
sessionPublicKey: yield ((_f = this.getSessionPublicKey) === null || _f === void 0 ? void 0 : _f.call(this)),
|
|
464
499
|
signedSessionId,
|
|
465
500
|
});
|
|
466
501
|
});
|
|
467
502
|
}
|
|
468
503
|
revokeDelegation(_a) {
|
|
469
504
|
return __awaiter(this, arguments, void 0, function* ({ accountAddress, password, }) {
|
|
470
|
-
var _b, _c;
|
|
505
|
+
var _b, _c, _d;
|
|
471
506
|
if (!accountAddress) {
|
|
472
507
|
throw new DynamicError('Account address is required');
|
|
473
508
|
}
|
|
@@ -481,6 +516,7 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
481
516
|
accountAddress,
|
|
482
517
|
authToken: (_c = this.getAuthToken) === null || _c === void 0 ? void 0 : _c.call(this),
|
|
483
518
|
password: resolvedPassword,
|
|
519
|
+
sessionPublicKey: yield ((_d = this.getSessionPublicKey) === null || _d === void 0 ? void 0 : _d.call(this)),
|
|
484
520
|
signedSessionId,
|
|
485
521
|
});
|
|
486
522
|
});
|
|
@@ -502,7 +538,7 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
502
538
|
const encryptedWallets = wallets.filter((w) => { var _a; return (_a = w.clientKeySharesBackupInfo) === null || _a === void 0 ? void 0 : _a.passwordEncrypted; });
|
|
503
539
|
const passwordUpdateBatchId = crypto.randomUUID();
|
|
504
540
|
yield Promise.all(encryptedWallets.map((wallet) => __awaiter(this, void 0, void 0, function* () {
|
|
505
|
-
var _b;
|
|
541
|
+
var _b, _c;
|
|
506
542
|
const signedSessionId = yield this.getSignedSessionId();
|
|
507
543
|
yield walletClient.updatePassword({
|
|
508
544
|
accountAddress: wallet.accountAddress,
|
|
@@ -510,6 +546,7 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
510
546
|
existingPassword: resolvedExistingPassword,
|
|
511
547
|
newPassword,
|
|
512
548
|
passwordUpdateBatchId,
|
|
549
|
+
sessionPublicKey: yield ((_c = this.getSessionPublicKey) === null || _c === void 0 ? void 0 : _c.call(this)),
|
|
513
550
|
signedSessionId,
|
|
514
551
|
});
|
|
515
552
|
})));
|
|
@@ -527,13 +564,14 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
527
564
|
const wallets = yield walletClient.getAllWallets();
|
|
528
565
|
const passwordUpdateBatchId = crypto.randomUUID();
|
|
529
566
|
yield Promise.all(wallets.map((wallet) => __awaiter(this, void 0, void 0, function* () {
|
|
530
|
-
var _b;
|
|
567
|
+
var _b, _c;
|
|
531
568
|
const signedSessionId = yield this.getSignedSessionId();
|
|
532
569
|
yield walletClient.setPassword({
|
|
533
570
|
accountAddress: wallet.accountAddress,
|
|
534
571
|
authToken: (_b = this.getAuthToken) === null || _b === void 0 ? void 0 : _b.call(this),
|
|
535
572
|
newPassword,
|
|
536
573
|
passwordUpdateBatchId,
|
|
574
|
+
sessionPublicKey: yield ((_c = this.getSessionPublicKey) === null || _c === void 0 ? void 0 : _c.call(this)),
|
|
537
575
|
signedSessionId,
|
|
538
576
|
});
|
|
539
577
|
})));
|
|
@@ -608,9 +646,18 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
608
646
|
}
|
|
609
647
|
endSession(reason) {
|
|
610
648
|
return __awaiter(this, void 0, void 0, function* () {
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
649
|
+
// Building the wallet client requires an auth token (createDynamicWaasClient
|
|
650
|
+
// throws in header auth mode when none is present); the new scope guard in
|
|
651
|
+
// getWaasWalletClient also throws during pending-auth (MFA/KYC/device). On
|
|
652
|
+
// logout we must not let either throw abort logout — the security-critical
|
|
653
|
+
// key share removal below does not need the client. Only the iframe cleanup
|
|
654
|
+
// does, so we proceed without it if the client can't be obtained.
|
|
655
|
+
let waasClient;
|
|
656
|
+
try {
|
|
657
|
+
waasClient = yield this.getWaasWalletClient();
|
|
658
|
+
}
|
|
659
|
+
catch (error) {
|
|
660
|
+
this.instrument('[endSession] getWaasWalletClient failed; proceeding', Object.assign({ key: 'endSession-getClient-failed', time: 0 }, this.buildErrorInstrumentContext(error)));
|
|
614
661
|
}
|
|
615
662
|
// When a session token expires, we preserve key shares in storage instead
|
|
616
663
|
// of clearing them. Customers with short-lived sessions (minutes) were
|
|
@@ -642,9 +689,13 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
642
689
|
// iframe cleanup's request-channel timeout. The iframe handler already
|
|
643
690
|
// swallows its own errors, and the DOM teardown obsoletes anything it
|
|
644
691
|
// misses. Promise.resolve guards against a non-promise return in tests.
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
692
|
+
// Skipped when the client could not be built/obtained (no token, or
|
|
693
|
+
// pending-auth scope guard) — there is no iframe to tear down then.
|
|
694
|
+
if (waasClient) {
|
|
695
|
+
void Promise.resolve(waasClient.cleanup()).catch((error) => {
|
|
696
|
+
this.instrument('[endSession] background iframe cleanup failed', Object.assign({ key: 'endSession-cleanup-failed', time: 0 }, this.buildErrorInstrumentContext(error)));
|
|
697
|
+
});
|
|
698
|
+
}
|
|
648
699
|
}
|
|
649
700
|
this.dynamicWaasClient = undefined;
|
|
650
701
|
});
|
|
@@ -751,6 +802,11 @@ const withDynamicWaas = (BaseClass) => {
|
|
|
751
802
|
}
|
|
752
803
|
});
|
|
753
804
|
}
|
|
805
|
+
getPrivateBalance() {
|
|
806
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
807
|
+
throw new DynamicError(`getPrivateBalance is not supported for ${this.chainName} wallets`);
|
|
808
|
+
});
|
|
809
|
+
}
|
|
754
810
|
}
|
|
755
811
|
return DynamicWaasMixin;
|
|
756
812
|
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
5
|
+
|
|
6
|
+
var sdkApiCore = require('@dynamic-labs/sdk-api-core');
|
|
7
|
+
|
|
8
|
+
// Scopes that mean the user has not finished authenticating (MFA / KYC /
|
|
9
|
+
// device registration still pending). A JWT carrying any of these has not
|
|
10
|
+
// upgraded to `user:basic`, and the WaaS backend rejects calls made with it
|
|
11
|
+
// (e.g. createRooms) with a 401.
|
|
12
|
+
//
|
|
13
|
+
// Note `user:basic` and `waasBackupToken` are intentionally NOT in this list:
|
|
14
|
+
// backup / recovery flows legitimately run with a `waasBackupToken` scope, so
|
|
15
|
+
// only the pending-auth scopes below should block WaaS client creation.
|
|
16
|
+
const PENDING_AUTH_SCOPES = [
|
|
17
|
+
sdkApiCore.JwtScope.RequiresAdditionalAuth,
|
|
18
|
+
sdkApiCore.JwtScope.UserDataForm,
|
|
19
|
+
sdkApiCore.JwtScope.Deviceregister,
|
|
20
|
+
];
|
|
21
|
+
const decodeScope = (authToken) => {
|
|
22
|
+
const payload = authToken === null || authToken === void 0 ? void 0 : authToken.split('.')[1];
|
|
23
|
+
if (!payload) {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
return JSON.parse(atob(payload)).scope;
|
|
28
|
+
}
|
|
29
|
+
catch (_a) {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Returns true when the auth token still carries a pending-authentication
|
|
35
|
+
* scope (`requiresAdditionalAuth` / `userDataForm` / `device:register`).
|
|
36
|
+
*
|
|
37
|
+
* The WaaS iframe's API client is a shared, mutable axios instance. The token
|
|
38
|
+
* captured at client construction is what the init-time room-cache pre-warm
|
|
39
|
+
* `createRooms` uses — it runs before any per-operation token resync. So if the
|
|
40
|
+
* client is built during this pending window, that pre-warm fires with the
|
|
41
|
+
* stale pre-`user:basic` token and 401s; the iframe reports the 401 back to the
|
|
42
|
+
* host, which force-logs-out the user. Callers must therefore refuse to build
|
|
43
|
+
* the client until the token clears (the vulnerable window is construction +
|
|
44
|
+
* init, not the whole client lifetime).
|
|
45
|
+
*
|
|
46
|
+
* A token with no decodable scope is treated as not-pending (returns false):
|
|
47
|
+
* the backend remains the source of truth and will reject anything invalid.
|
|
48
|
+
*/
|
|
49
|
+
const tokenHasPendingAuthScope = (authToken) => {
|
|
50
|
+
var _a, _b;
|
|
51
|
+
const scopes = (_b = (_a = decodeScope(authToken)) === null || _a === void 0 ? void 0 : _a.split(' ')) !== null && _b !== void 0 ? _b : [];
|
|
52
|
+
return scopes.some((scope) => PENDING_AUTH_SCOPES.includes(scope));
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
exports.tokenHasPendingAuthScope = tokenHasPendingAuthScope;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns true when the auth token still carries a pending-authentication
|
|
3
|
+
* scope (`requiresAdditionalAuth` / `userDataForm` / `device:register`).
|
|
4
|
+
*
|
|
5
|
+
* The WaaS iframe's API client is a shared, mutable axios instance. The token
|
|
6
|
+
* captured at client construction is what the init-time room-cache pre-warm
|
|
7
|
+
* `createRooms` uses — it runs before any per-operation token resync. So if the
|
|
8
|
+
* client is built during this pending window, that pre-warm fires with the
|
|
9
|
+
* stale pre-`user:basic` token and 401s; the iframe reports the 401 back to the
|
|
10
|
+
* host, which force-logs-out the user. Callers must therefore refuse to build
|
|
11
|
+
* the client until the token clears (the vulnerable window is construction +
|
|
12
|
+
* init, not the whole client lifetime).
|
|
13
|
+
*
|
|
14
|
+
* A token with no decodable scope is treated as not-pending (returns false):
|
|
15
|
+
* the backend remains the source of truth and will reject anything invalid.
|
|
16
|
+
*/
|
|
17
|
+
export declare const tokenHasPendingAuthScope: (authToken?: string) => boolean;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import { JwtScope } from '@dynamic-labs/sdk-api-core';
|
|
3
|
+
|
|
4
|
+
// Scopes that mean the user has not finished authenticating (MFA / KYC /
|
|
5
|
+
// device registration still pending). A JWT carrying any of these has not
|
|
6
|
+
// upgraded to `user:basic`, and the WaaS backend rejects calls made with it
|
|
7
|
+
// (e.g. createRooms) with a 401.
|
|
8
|
+
//
|
|
9
|
+
// Note `user:basic` and `waasBackupToken` are intentionally NOT in this list:
|
|
10
|
+
// backup / recovery flows legitimately run with a `waasBackupToken` scope, so
|
|
11
|
+
// only the pending-auth scopes below should block WaaS client creation.
|
|
12
|
+
const PENDING_AUTH_SCOPES = [
|
|
13
|
+
JwtScope.RequiresAdditionalAuth,
|
|
14
|
+
JwtScope.UserDataForm,
|
|
15
|
+
JwtScope.Deviceregister,
|
|
16
|
+
];
|
|
17
|
+
const decodeScope = (authToken) => {
|
|
18
|
+
const payload = authToken === null || authToken === void 0 ? void 0 : authToken.split('.')[1];
|
|
19
|
+
if (!payload) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
return JSON.parse(atob(payload)).scope;
|
|
24
|
+
}
|
|
25
|
+
catch (_a) {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Returns true when the auth token still carries a pending-authentication
|
|
31
|
+
* scope (`requiresAdditionalAuth` / `userDataForm` / `device:register`).
|
|
32
|
+
*
|
|
33
|
+
* The WaaS iframe's API client is a shared, mutable axios instance. The token
|
|
34
|
+
* captured at client construction is what the init-time room-cache pre-warm
|
|
35
|
+
* `createRooms` uses — it runs before any per-operation token resync. So if the
|
|
36
|
+
* client is built during this pending window, that pre-warm fires with the
|
|
37
|
+
* stale pre-`user:basic` token and 401s; the iframe reports the 401 back to the
|
|
38
|
+
* host, which force-logs-out the user. Callers must therefore refuse to build
|
|
39
|
+
* the client until the token clears (the vulnerable window is construction +
|
|
40
|
+
* init, not the whole client lifetime).
|
|
41
|
+
*
|
|
42
|
+
* A token with no decodable scope is treated as not-pending (returns false):
|
|
43
|
+
* the backend remains the source of truth and will reject anything invalid.
|
|
44
|
+
*/
|
|
45
|
+
const tokenHasPendingAuthScope = (authToken) => {
|
|
46
|
+
var _a, _b;
|
|
47
|
+
const scopes = (_b = (_a = decodeScope(authToken)) === null || _a === void 0 ? void 0 : _a.split(' ')) !== null && _b !== void 0 ? _b : [];
|
|
48
|
+
return scopes.some((scope) => PENDING_AUTH_SCOPES.includes(scope));
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export { tokenHasPendingAuthScope };
|