decane-connect-kit 2.30.0 → 2.31.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/dist/index.cjs +106 -117
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -16
- package/dist/index.d.ts +17 -16
- package/dist/index.js +106 -117
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -2194,9 +2194,19 @@ var ApiClient = class {
|
|
|
2194
2194
|
return this._request("/auth/kingschat/config", { method: "GET" });
|
|
2195
2195
|
}
|
|
2196
2196
|
/**
|
|
2197
|
-
* Exchange a KingsChat
|
|
2198
|
-
*
|
|
2199
|
-
*
|
|
2197
|
+
* Exchange a KingsChat authorization code for a Decane session. The backend
|
|
2198
|
+
* trades it with KingsChat for an access token, verifies that token (it must
|
|
2199
|
+
* have been issued to this project's KingsChat app), and never stores it.
|
|
2200
|
+
*/
|
|
2201
|
+
async authKingsChatCode(code) {
|
|
2202
|
+
return this._request("/auth/kingschat/code", {
|
|
2203
|
+
method: "POST",
|
|
2204
|
+
body: JSON.stringify({ code })
|
|
2205
|
+
});
|
|
2206
|
+
}
|
|
2207
|
+
/**
|
|
2208
|
+
* Exchange a KingsChat access token the app already holds for a Decane
|
|
2209
|
+
* session. Verified the same way as a code's token, and never stored.
|
|
2200
2210
|
*/
|
|
2201
2211
|
async authKingsChat(accessToken) {
|
|
2202
2212
|
return this._request("/auth/kingschat", {
|
|
@@ -2546,15 +2556,16 @@ init_errors();
|
|
|
2546
2556
|
|
|
2547
2557
|
// ../infra/tee-measurement.json
|
|
2548
2558
|
var tee_measurement_default = {
|
|
2549
|
-
signerVersion: "0.2.
|
|
2559
|
+
signerVersion: "0.2.13",
|
|
2550
2560
|
acceptedMrtd: [
|
|
2561
|
+
'["fb14dc139f33d6fcf474bc8332cac001259fb31cfbcb6b34d4ceeb552a2c4466884a0cbde45ad98a05c5c060c23ad65a","07e6f51aa763abfe75c3ddfbf4f425fe3f0ceff66d807a75e049303dce9addf68e7218729bd419638af63a370f65878c","a2a58c9a959a4fa44bd6da0c97a2270c051faf12084cfe91ae900e4fdff6cdd4f69a82005e04ee920f231497894d677f","5538de3ba0930ae65c7c2b99346742a67dff7891545d91e8d74006d215fff256db91998eaff1c3dcca679a89df566700"]',
|
|
2551
2562
|
'["fb14dc139f33d6fcf474bc8332cac001259fb31cfbcb6b34d4ceeb552a2c4466884a0cbde45ad98a05c5c060c23ad65a","07e6f51aa763abfe75c3ddfbf4f425fe3f0ceff66d807a75e049303dce9addf68e7218729bd419638af63a370f65878c","a2a58c9a959a4fa44bd6da0c97a2270c051faf12084cfe91ae900e4fdff6cdd4f69a82005e04ee920f231497894d677f","a76a81147b7f733cb400914c8791ec149bb3a31f3727b1c1e12ec22e9c1a183b6e617e78636dca37075f5a695afd46c2"]',
|
|
2552
2563
|
'["68102e7b524af310f7b7d426ce75481e36c40f5d513a9009c046e9d37e31551f0134d954b496a3357fd61d03f07ffe96","07e6f51aa763abfe75c3ddfbf4f425fe3f0ceff66d807a75e049303dce9addf68e7218729bd419638af63a370f65878c","a2a58c9a959a4fa44bd6da0c97a2270c051faf12084cfe91ae900e4fdff6cdd4f69a82005e04ee920f231497894d677f","a76a81147b7f733cb400914c8791ec149bb3a31f3727b1c1e12ec22e9c1a183b6e617e78636dca37075f5a695afd46c2"]'
|
|
2553
2564
|
],
|
|
2554
|
-
primaryMrtd: '["fb14dc139f33d6fcf474bc8332cac001259fb31cfbcb6b34d4ceeb552a2c4466884a0cbde45ad98a05c5c060c23ad65a","07e6f51aa763abfe75c3ddfbf4f425fe3f0ceff66d807a75e049303dce9addf68e7218729bd419638af63a370f65878c","a2a58c9a959a4fa44bd6da0c97a2270c051faf12084cfe91ae900e4fdff6cdd4f69a82005e04ee920f231497894d677f","
|
|
2555
|
-
capturedAt: "2026-09-
|
|
2565
|
+
primaryMrtd: '["fb14dc139f33d6fcf474bc8332cac001259fb31cfbcb6b34d4ceeb552a2c4466884a0cbde45ad98a05c5c060c23ad65a","07e6f51aa763abfe75c3ddfbf4f425fe3f0ceff66d807a75e049303dce9addf68e7218729bd419638af63a370f65878c","a2a58c9a959a4fa44bd6da0c97a2270c051faf12084cfe91ae900e4fdff6cdd4f69a82005e04ee920f231497894d677f","5538de3ba0930ae65c7c2b99346742a67dff7891545d91e8d74006d215fff256db91998eaff1c3dcca679a89df566700"]',
|
|
2566
|
+
capturedAt: "2026-09-30T18:36:41Z",
|
|
2556
2567
|
tcbStatus: "UpToDate",
|
|
2557
|
-
_note: "Captured from the live CVM
|
|
2568
|
+
_note: "Captured from the live CVM app_4405742df08d after the 2026-09-30 restart that shipped signer 0.2.13 (Path A, compose byte-identical). RTMR3 changed; RTMR0-2 did not. The app-id and compose-hash events in RTMR3 are unchanged, so the move is in the platform-recorded events (instance, KMS or OS image) underneath the CVM, not in what we deploy. The quote verified against Intel (TCB UpToDate) and bound a fresh nonce before this was accepted, with the operator's approval. Sealing verified intact (/signer/sealing-status ready). Both previous measurements are retained so a rollback still verifies. measurement is the JSON.stringify(replayRtmrs()) output, compared as an opaque string."
|
|
2558
2569
|
};
|
|
2559
2570
|
|
|
2560
2571
|
// src/social/attestation.ts
|
|
@@ -3370,75 +3381,47 @@ async function verifyPhoneCode(api, phone, code) {
|
|
|
3370
3381
|
|
|
3371
3382
|
// src/social/auth/kingschat.ts
|
|
3372
3383
|
init_errors();
|
|
3373
|
-
var
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
const
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
}
|
|
3384
|
+
var KINGSCHAT_LOGIN_URL = "https://accounts.kingschat.online/log-in";
|
|
3385
|
+
var STATE_KEY = "decane:kingschat_state";
|
|
3386
|
+
function randomState() {
|
|
3387
|
+
const bytes = new Uint8Array(16);
|
|
3388
|
+
crypto.getRandomValues(bytes);
|
|
3389
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
3390
|
+
}
|
|
3391
|
+
function storage2() {
|
|
3392
|
+
try {
|
|
3393
|
+
return window.sessionStorage ?? null;
|
|
3394
|
+
} catch {
|
|
3395
|
+
return null;
|
|
3396
|
+
}
|
|
3386
3397
|
}
|
|
3387
|
-
function
|
|
3388
|
-
const
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
url
|
|
3398
|
+
function beginKingsChatLogin(clientId, opts = {}) {
|
|
3399
|
+
const store = storage2();
|
|
3400
|
+
if (!store) throw new Error("KingsChat sign-in needs sessionStorage, which this browser is blocking");
|
|
3401
|
+
const state = randomState();
|
|
3402
|
+
store.setItem(STATE_KEY, state);
|
|
3403
|
+
const url = new URL(KINGSCHAT_LOGIN_URL);
|
|
3404
|
+
url.searchParams.set("clientId", clientId);
|
|
3405
|
+
url.searchParams.set("origin", state);
|
|
3406
|
+
if (opts.forceLogin) url.searchParams.set("forceLogin", "true");
|
|
3393
3407
|
return url.toString();
|
|
3394
3408
|
}
|
|
3395
|
-
function
|
|
3396
|
-
const
|
|
3397
|
-
const
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3409
|
+
function readKingsChatReturn(search) {
|
|
3410
|
+
const params = new URLSearchParams(search);
|
|
3411
|
+
const code = params.get("code");
|
|
3412
|
+
const origin = params.get("origin");
|
|
3413
|
+
if (!code || !origin) return null;
|
|
3414
|
+
const expected = storage2()?.getItem(STATE_KEY);
|
|
3415
|
+
if (!expected || expected !== origin) return null;
|
|
3416
|
+
return { code };
|
|
3417
|
+
}
|
|
3418
|
+
function clearKingsChatState() {
|
|
3419
|
+
storage2()?.removeItem(STATE_KEY);
|
|
3420
|
+
}
|
|
3421
|
+
async function loginWithKingsChatPopup(_opts) {
|
|
3422
|
+
throw new CallSequenceError(
|
|
3423
|
+
"loginWithKingsChatPopup was removed in 2.31.0: KingsChat retired its popup login. Use connectWithKingsChat(), which redirects to KingsChat and finishes on your registered Redirect URL."
|
|
3401
3424
|
);
|
|
3402
|
-
if (!popup) {
|
|
3403
|
-
throw new CallSequenceError(
|
|
3404
|
-
"The KingsChat sign-in popup was blocked. Call connectWithKingsChat() directly from a click handler."
|
|
3405
|
-
);
|
|
3406
|
-
}
|
|
3407
|
-
return new Promise((resolve, reject) => {
|
|
3408
|
-
let settled = false;
|
|
3409
|
-
const finish = (fn) => {
|
|
3410
|
-
if (settled) return;
|
|
3411
|
-
settled = true;
|
|
3412
|
-
window.removeEventListener("message", onMessage);
|
|
3413
|
-
clearInterval(closedPoll);
|
|
3414
|
-
clearTimeout(timeout);
|
|
3415
|
-
try {
|
|
3416
|
-
popup.close();
|
|
3417
|
-
} catch {
|
|
3418
|
-
}
|
|
3419
|
-
fn();
|
|
3420
|
-
};
|
|
3421
|
-
const onMessage = (event) => {
|
|
3422
|
-
if (event.origin !== KINGSCHAT_AUTH_ORIGIN) return;
|
|
3423
|
-
const data = event.data;
|
|
3424
|
-
if (data && typeof data === "object" && data.error) {
|
|
3425
|
-
finish(() => reject(new CallSequenceError(`KingsChat sign-in failed: ${String(data.error)}`)));
|
|
3426
|
-
return;
|
|
3427
|
-
}
|
|
3428
|
-
const tokens2 = readTokens(event.data);
|
|
3429
|
-
if (tokens2) finish(() => resolve(tokens2));
|
|
3430
|
-
};
|
|
3431
|
-
window.addEventListener("message", onMessage);
|
|
3432
|
-
const closedPoll = setInterval(() => {
|
|
3433
|
-
if (popup.closed) finish(() => reject(new UserCancelledError()));
|
|
3434
|
-
}, 400);
|
|
3435
|
-
const timeout = setTimeout(
|
|
3436
|
-
() => finish(
|
|
3437
|
-
() => reject(new CallSequenceError("KingsChat sign-in timed out before returning a token."))
|
|
3438
|
-
),
|
|
3439
|
-
opts.timeoutMs ?? 5 * 6e4
|
|
3440
|
-
);
|
|
3441
|
-
});
|
|
3442
3425
|
}
|
|
3443
3426
|
|
|
3444
3427
|
// src/social/signers/evm.ts
|
|
@@ -4043,7 +4026,7 @@ async function createDecaneConnect(config) {
|
|
|
4043
4026
|
const teeUrl = config.teeUrl ?? DEFAULT_TEE_URL;
|
|
4044
4027
|
const events = new EventEmitter();
|
|
4045
4028
|
const api = new ApiClient(baseUrl, config.apiKey, config.appId);
|
|
4046
|
-
const
|
|
4029
|
+
const storage3 = new Storage();
|
|
4047
4030
|
const session = new SessionManager(
|
|
4048
4031
|
events,
|
|
4049
4032
|
durationMs,
|
|
@@ -4233,7 +4216,7 @@ async function createDecaneConnect(config) {
|
|
|
4233
4216
|
}
|
|
4234
4217
|
async function attachUnlockToken(userId, jwt, tokenKey) {
|
|
4235
4218
|
try {
|
|
4236
|
-
const stored = await
|
|
4219
|
+
const stored = await storage3.getShare(userId);
|
|
4237
4220
|
if (!stored) return;
|
|
4238
4221
|
const { token } = await api.issueUnlockToken(jwt);
|
|
4239
4222
|
const unlockTokenIv = generateRandomBytes(12);
|
|
@@ -4242,13 +4225,13 @@ async function createDecaneConnect(config) {
|
|
|
4242
4225
|
new TextEncoder().encode(token),
|
|
4243
4226
|
unlockTokenIv
|
|
4244
4227
|
);
|
|
4245
|
-
await
|
|
4228
|
+
await storage3.putShare({ ...stored, wrappedUnlockToken, unlockTokenIv });
|
|
4246
4229
|
} catch (e) {
|
|
4247
4230
|
console.warn("decane-connect-kit: could not store an unlock token for this device.", e);
|
|
4248
4231
|
}
|
|
4249
4232
|
}
|
|
4250
4233
|
async function persistRotatedDeviceShare(userId, deviceShare) {
|
|
4251
|
-
const stored = await
|
|
4234
|
+
const stored = await storage3.getShare(userId);
|
|
4252
4235
|
if (!stored) {
|
|
4253
4236
|
throw new CallSequenceError(
|
|
4254
4237
|
"Share set was rotated but this device has no stored share to update. Recover the wallet on this device before rotating."
|
|
@@ -4257,21 +4240,21 @@ async function createDecaneConnect(config) {
|
|
|
4257
4240
|
const wrappingKey = await getWrappingKey(stored);
|
|
4258
4241
|
const iv = generateRandomBytes(12);
|
|
4259
4242
|
const wrappedDeviceShare = await aesGcmEncrypt(wrappingKey, deviceShare, iv);
|
|
4260
|
-
await
|
|
4243
|
+
await storage3.putShare({ ...stored, wrappedDeviceShare, iv });
|
|
4261
4244
|
}
|
|
4262
4245
|
function hasPasswordUnlock(stored) {
|
|
4263
4246
|
return !!stored && stored.unlockMethod === "password" && !!stored.passwordSalt && !!stored.wrappedUnlockToken && !!stored.unlockTokenIv;
|
|
4264
4247
|
}
|
|
4265
4248
|
async function passwordDeviceCandidate() {
|
|
4266
4249
|
if (persisted?.userId) {
|
|
4267
|
-
const stored = await
|
|
4250
|
+
const stored = await storage3.getShare(persisted.userId);
|
|
4268
4251
|
return hasPasswordUnlock(stored) ? stored : null;
|
|
4269
4252
|
}
|
|
4270
4253
|
if (lastUserId) {
|
|
4271
|
-
const stored = await
|
|
4254
|
+
const stored = await storage3.getShare(lastUserId);
|
|
4272
4255
|
return hasPasswordUnlock(stored) ? stored : null;
|
|
4273
4256
|
}
|
|
4274
|
-
const candidates = (await
|
|
4257
|
+
const candidates = (await storage3.listShares()).filter(hasPasswordUnlock);
|
|
4275
4258
|
return candidates.length === 1 ? candidates[0] : null;
|
|
4276
4259
|
}
|
|
4277
4260
|
async function getWrappingKey(stored) {
|
|
@@ -4299,7 +4282,7 @@ async function createDecaneConnect(config) {
|
|
|
4299
4282
|
let credentialId;
|
|
4300
4283
|
let prfOutput;
|
|
4301
4284
|
try {
|
|
4302
|
-
const label = (await
|
|
4285
|
+
const label = (await storage3.getShare(userId))?.profile?.email;
|
|
4303
4286
|
({ credentialId, prfOutput } = await registerWebAuthnCredential({
|
|
4304
4287
|
api,
|
|
4305
4288
|
jwt,
|
|
@@ -4331,8 +4314,8 @@ async function createDecaneConnect(config) {
|
|
|
4331
4314
|
const wrappingKey = await importAesKey(prfOutput);
|
|
4332
4315
|
const iv = generateRandomBytes(12);
|
|
4333
4316
|
const wrappedDeviceShare = await aesGcmEncrypt(wrappingKey, deviceShare, iv);
|
|
4334
|
-
const existing = await
|
|
4335
|
-
await
|
|
4317
|
+
const existing = await storage3.getShare(userId);
|
|
4318
|
+
await storage3.putShare({
|
|
4336
4319
|
...existing,
|
|
4337
4320
|
userId,
|
|
4338
4321
|
wrappedDeviceShare,
|
|
@@ -4416,7 +4399,7 @@ async function createDecaneConnect(config) {
|
|
|
4416
4399
|
const { wrappingKey, tokenKey, meta } = await passwordSetup();
|
|
4417
4400
|
const iv = generateRandomBytes(12);
|
|
4418
4401
|
const wrappedDeviceShare = await aesGcmEncrypt(wrappingKey, deviceShare, iv);
|
|
4419
|
-
await
|
|
4402
|
+
await storage3.putShare({
|
|
4420
4403
|
userId,
|
|
4421
4404
|
wrappedDeviceShare,
|
|
4422
4405
|
iv,
|
|
@@ -4490,7 +4473,7 @@ async function createDecaneConnect(config) {
|
|
|
4490
4473
|
shareSet.device,
|
|
4491
4474
|
iv
|
|
4492
4475
|
);
|
|
4493
|
-
await
|
|
4476
|
+
await storage3.putShare({
|
|
4494
4477
|
userId,
|
|
4495
4478
|
wrappedDeviceShare,
|
|
4496
4479
|
iv,
|
|
@@ -4530,7 +4513,7 @@ async function createDecaneConnect(config) {
|
|
|
4530
4513
|
zeroize(shareSet.server);
|
|
4531
4514
|
zeroize(shareSet.device);
|
|
4532
4515
|
zeroize(shareSet.recovery);
|
|
4533
|
-
if (wrappingKey) await
|
|
4516
|
+
if (wrappingKey) await storage3.deleteShare(userId).catch(() => {
|
|
4534
4517
|
});
|
|
4535
4518
|
events.emit("wallet-unlocking");
|
|
4536
4519
|
return returningUserUnlock(userId, jwt);
|
|
@@ -4543,7 +4526,7 @@ async function createDecaneConnect(config) {
|
|
|
4543
4526
|
} catch (e) {
|
|
4544
4527
|
zeroize(shareSet.device);
|
|
4545
4528
|
zeroize(shareSet.recovery);
|
|
4546
|
-
if (wrappingKey) await
|
|
4529
|
+
if (wrappingKey) await storage3.deleteShare(userId).catch(() => {
|
|
4547
4530
|
});
|
|
4548
4531
|
throw e;
|
|
4549
4532
|
}
|
|
@@ -4619,7 +4602,7 @@ async function createDecaneConnect(config) {
|
|
|
4619
4602
|
async function returningUserUnlock(userId, jwt) {
|
|
4620
4603
|
const rawAddresses = await api.getAddresses(jwt);
|
|
4621
4604
|
const recordedAddresses = rawAddresses.evm && rawAddresses.solana ? { evm: rawAddresses.evm, solana: rawAddresses.solana, tron: rawAddresses.tron ?? evmToTronAddress2(rawAddresses.evm) } : null;
|
|
4622
|
-
const stored = await
|
|
4605
|
+
const stored = await storage3.getShare(userId);
|
|
4623
4606
|
let deviceShare;
|
|
4624
4607
|
let joinedViaPasskeySync = false;
|
|
4625
4608
|
let migrating = false;
|
|
@@ -4671,7 +4654,7 @@ async function createDecaneConnect(config) {
|
|
|
4671
4654
|
rpId,
|
|
4672
4655
|
teeUrl,
|
|
4673
4656
|
api,
|
|
4674
|
-
storage:
|
|
4657
|
+
storage: storage3,
|
|
4675
4658
|
onProgress: config.onRecoveryProgress,
|
|
4676
4659
|
promptForRecoveryFile: config.promptForRecoveryFile,
|
|
4677
4660
|
onNewRecoveryShare: async (recoveryShare, shareSetVersion) => {
|
|
@@ -4685,7 +4668,7 @@ async function createDecaneConnect(config) {
|
|
|
4685
4668
|
});
|
|
4686
4669
|
deviceShare = recovered.deviceShare;
|
|
4687
4670
|
joinedViaPasskeySync = recovered.flow === "passkey-sync";
|
|
4688
|
-
if (!identityTier && !await
|
|
4671
|
+
if (!identityTier && !await storage3.getShare(userId) && isPasskeySupported()) {
|
|
4689
4672
|
await registerPasskeyAndBackup(userId, jwt, deviceShare);
|
|
4690
4673
|
}
|
|
4691
4674
|
} else {
|
|
@@ -4714,7 +4697,7 @@ async function createDecaneConnect(config) {
|
|
|
4714
4697
|
} catch (e) {
|
|
4715
4698
|
if (e instanceof StaleDeviceShareError) {
|
|
4716
4699
|
zeroize(deviceShare);
|
|
4717
|
-
await
|
|
4700
|
+
await storage3.deleteShare(userId);
|
|
4718
4701
|
if (stored) return returningUserUnlock(userId, jwt);
|
|
4719
4702
|
} else {
|
|
4720
4703
|
zeroize(deviceShare);
|
|
@@ -4742,7 +4725,7 @@ async function createDecaneConnect(config) {
|
|
|
4742
4725
|
const result = await tee.rotateShares(jwt);
|
|
4743
4726
|
try {
|
|
4744
4727
|
await persistRotatedDeviceShare(userId, result.deviceShare);
|
|
4745
|
-
const refreshed = await
|
|
4728
|
+
const refreshed = await storage3.getShare(userId);
|
|
4746
4729
|
if (refreshed?.unlockMethod === "passkey" && refreshed.credentialId && refreshed.prfSalt) {
|
|
4747
4730
|
await api.putRecoveryBackup(jwt, {
|
|
4748
4731
|
wrappedDeviceShare: refreshed.wrappedDeviceShare,
|
|
@@ -4775,13 +4758,13 @@ async function createDecaneConnect(config) {
|
|
|
4775
4758
|
}
|
|
4776
4759
|
async function passkeyLoginCandidate() {
|
|
4777
4760
|
if (!persisted?.userId || !isPasskeySupported()) return null;
|
|
4778
|
-
return usablePasskeyShare(await
|
|
4761
|
+
return usablePasskeyShare(await storage3.getShare(persisted.userId));
|
|
4779
4762
|
}
|
|
4780
4763
|
async function passkeyDeviceCandidate() {
|
|
4781
4764
|
if (!isPasskeySupported()) return null;
|
|
4782
4765
|
if (persisted?.userId) return passkeyLoginCandidate();
|
|
4783
|
-
if (lastUserId) return usablePasskeyShare(await
|
|
4784
|
-
const candidates = (await
|
|
4766
|
+
if (lastUserId) return usablePasskeyShare(await storage3.getShare(lastUserId));
|
|
4767
|
+
const candidates = (await storage3.listShares()).map(usablePasskeyShare).filter(Boolean);
|
|
4785
4768
|
return candidates.length === 1 ? candidates[0] : null;
|
|
4786
4769
|
}
|
|
4787
4770
|
async function canOfferPasskey() {
|
|
@@ -4789,15 +4772,15 @@ async function createDecaneConnect(config) {
|
|
|
4789
4772
|
if (await passkeyDeviceCandidate()) return true;
|
|
4790
4773
|
const known = persisted?.userId ?? lastUserId;
|
|
4791
4774
|
if (!known) return false;
|
|
4792
|
-
return await
|
|
4775
|
+
return await storage3.getShare(known) == null;
|
|
4793
4776
|
}
|
|
4794
4777
|
async function rememberProfile(userId, profile) {
|
|
4795
4778
|
const incoming = Object.fromEntries(Object.entries(profile).filter(([, v]) => v));
|
|
4796
4779
|
if (!Object.keys(incoming).length) return;
|
|
4797
4780
|
try {
|
|
4798
|
-
const stored = await
|
|
4781
|
+
const stored = await storage3.getShare(userId);
|
|
4799
4782
|
if (!stored) return;
|
|
4800
|
-
await
|
|
4783
|
+
await storage3.putShare({ ...stored, profile: { ...stored.profile, ...incoming } });
|
|
4801
4784
|
} catch {
|
|
4802
4785
|
}
|
|
4803
4786
|
}
|
|
@@ -4821,7 +4804,7 @@ async function createDecaneConnect(config) {
|
|
|
4821
4804
|
savePersisted(PERSIST_KEY, persisted);
|
|
4822
4805
|
events.emit("connected");
|
|
4823
4806
|
persistLiveSession();
|
|
4824
|
-
const stored = await
|
|
4807
|
+
const stored = await storage3.getShare(userId);
|
|
4825
4808
|
return {
|
|
4826
4809
|
addresses,
|
|
4827
4810
|
isNewUser: false,
|
|
@@ -4881,6 +4864,11 @@ async function createDecaneConnect(config) {
|
|
|
4881
4864
|
return new Promise(() => {
|
|
4882
4865
|
});
|
|
4883
4866
|
}
|
|
4867
|
+
async function finishKingsChatReturn(code) {
|
|
4868
|
+
clearKingsChatState();
|
|
4869
|
+
window.history.replaceState({}, "", window.location.pathname);
|
|
4870
|
+
return handleAuthResult(await api.authKingsChatCode(code));
|
|
4871
|
+
}
|
|
4884
4872
|
async function handleAuthResult(result) {
|
|
4885
4873
|
let addresses;
|
|
4886
4874
|
if (!result.hasShare) {
|
|
@@ -4943,6 +4931,8 @@ async function createDecaneConnect(config) {
|
|
|
4943
4931
|
* this never starts a flow.
|
|
4944
4932
|
*/
|
|
4945
4933
|
async completeRedirectSignIn() {
|
|
4934
|
+
const pending = readKingsChatReturn(window.location.search);
|
|
4935
|
+
if (pending) return finishKingsChatReturn(pending.code);
|
|
4946
4936
|
const outcome = readRedirectOutcome(window.location.search);
|
|
4947
4937
|
if (outcome.kind === "none") {
|
|
4948
4938
|
throw new CallSequenceError("No sign-in result in the URL to complete");
|
|
@@ -4961,12 +4951,12 @@ async function createDecaneConnect(config) {
|
|
|
4961
4951
|
return handleAuthResult(await api.authGoogleToken(idToken));
|
|
4962
4952
|
},
|
|
4963
4953
|
async connectWithKingsChat() {
|
|
4954
|
+
const pending = readKingsChatReturn(window.location.search);
|
|
4955
|
+
if (pending) return finishKingsChatReturn(pending.code);
|
|
4964
4956
|
const { clientId } = await api.getKingsChatConfig();
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
scopes: config.kingsChatScopes
|
|
4957
|
+
window.location.href = beginKingsChatLogin(clientId);
|
|
4958
|
+
return new Promise(() => {
|
|
4968
4959
|
});
|
|
4969
|
-
return client.connectWithKingsChatToken(tokens2.accessToken);
|
|
4970
4960
|
},
|
|
4971
4961
|
async connectWithKingsChatToken(accessToken) {
|
|
4972
4962
|
if (!accessToken) {
|
|
@@ -5008,7 +4998,7 @@ async function createDecaneConnect(config) {
|
|
|
5008
4998
|
events.emit("connected");
|
|
5009
4999
|
persistLiveSession();
|
|
5010
5000
|
scheduleAccessTokenExpiry(token, claims.expiresAt);
|
|
5011
|
-
const stored = await
|
|
5001
|
+
const stored = await storage3.getShare(claims.userId);
|
|
5012
5002
|
return {
|
|
5013
5003
|
addresses,
|
|
5014
5004
|
isNewUser: false,
|
|
@@ -5044,7 +5034,7 @@ async function createDecaneConnect(config) {
|
|
|
5044
5034
|
const candidate = await passkeyDeviceCandidate();
|
|
5045
5035
|
if (candidate) return passkeyLogin(candidate);
|
|
5046
5036
|
const known = persisted?.userId ?? lastUserId;
|
|
5047
|
-
if (isPasskeySupported() && known && await
|
|
5037
|
+
if (isPasskeySupported() && known && await storage3.getShare(known) == null) {
|
|
5048
5038
|
return passkeyLoginWithoutShare(known);
|
|
5049
5039
|
}
|
|
5050
5040
|
throw new NewDeviceError();
|
|
@@ -5063,7 +5053,7 @@ async function createDecaneConnect(config) {
|
|
|
5063
5053
|
async setUnlockPassword(password) {
|
|
5064
5054
|
if (!auth) throw new ReconnectRequiredError();
|
|
5065
5055
|
validateUnlockPassword(password);
|
|
5066
|
-
const stored = await
|
|
5056
|
+
const stored = await storage3.getShare(auth.userId);
|
|
5067
5057
|
if (!stored) throw new NewDeviceError();
|
|
5068
5058
|
const deviceShare = await aesGcmDecrypt(
|
|
5069
5059
|
await getWrappingKey(stored),
|
|
@@ -5082,7 +5072,7 @@ async function createDecaneConnect(config) {
|
|
|
5082
5072
|
);
|
|
5083
5073
|
const iv = generateRandomBytes(12);
|
|
5084
5074
|
const wrappedDeviceShare = await aesGcmEncrypt(shareKey, deviceShare, iv);
|
|
5085
|
-
await
|
|
5075
|
+
await storage3.putShare({
|
|
5086
5076
|
...stored,
|
|
5087
5077
|
unlockMethod: "password",
|
|
5088
5078
|
wrappedDeviceShare,
|
|
@@ -5162,9 +5152,9 @@ async function createDecaneConnect(config) {
|
|
|
5162
5152
|
async removeUnlockPassword() {
|
|
5163
5153
|
if (!auth) throw new ReconnectRequiredError();
|
|
5164
5154
|
await api.revokeUnlockTokens(auth.jwt);
|
|
5165
|
-
const stored = await
|
|
5155
|
+
const stored = await storage3.getShare(auth.userId);
|
|
5166
5156
|
if (!stored || stored.unlockMethod !== "password") return;
|
|
5167
|
-
await
|
|
5157
|
+
await storage3.putShare({
|
|
5168
5158
|
...stored,
|
|
5169
5159
|
wrappedUnlockToken: void 0,
|
|
5170
5160
|
unlockTokenIv: void 0
|
|
@@ -5172,7 +5162,7 @@ async function createDecaneConnect(config) {
|
|
|
5172
5162
|
},
|
|
5173
5163
|
async hasPasskey() {
|
|
5174
5164
|
if (!auth) return false;
|
|
5175
|
-
const stored = await
|
|
5165
|
+
const stored = await storage3.getShare(auth.userId);
|
|
5176
5166
|
return stored?.unlockMethod === "passkey";
|
|
5177
5167
|
},
|
|
5178
5168
|
async addPasskey() {
|
|
@@ -5183,7 +5173,7 @@ async function createDecaneConnect(config) {
|
|
|
5183
5173
|
}
|
|
5184
5174
|
if (!auth) throw new ReconnectRequiredError();
|
|
5185
5175
|
if (!isPasskeySupported()) throw new WebAuthnNotSupportedError();
|
|
5186
|
-
const stored = await
|
|
5176
|
+
const stored = await storage3.getShare(auth.userId);
|
|
5187
5177
|
if (stored?.unlockMethod === "passkey") return;
|
|
5188
5178
|
if (!stored) {
|
|
5189
5179
|
if (!serverAssistedRecovery) throw new NewDeviceError();
|
|
@@ -5216,12 +5206,12 @@ async function createDecaneConnect(config) {
|
|
|
5216
5206
|
},
|
|
5217
5207
|
async deviceProtected() {
|
|
5218
5208
|
if (!auth) return false;
|
|
5219
|
-
return await
|
|
5209
|
+
return await storage3.getShare(auth.userId) !== null;
|
|
5220
5210
|
},
|
|
5221
5211
|
async protectDevice() {
|
|
5222
5212
|
if (identityTier) return false;
|
|
5223
5213
|
if (!auth) throw new ReconnectRequiredError();
|
|
5224
|
-
if (await
|
|
5214
|
+
if (await storage3.getShare(auth.userId)) return true;
|
|
5225
5215
|
if (!serverAssistedRecovery) throw new NewDeviceError();
|
|
5226
5216
|
const tee = new TeeClient(teeUrl);
|
|
5227
5217
|
const provisioned = await tee.provisionDeviceShare(auth.jwt);
|
|
@@ -5231,7 +5221,7 @@ async function createDecaneConnect(config) {
|
|
|
5231
5221
|
} finally {
|
|
5232
5222
|
zeroize(provisioned);
|
|
5233
5223
|
}
|
|
5234
|
-
return await
|
|
5224
|
+
return await storage3.getShare(auth.userId) !== null;
|
|
5235
5225
|
},
|
|
5236
5226
|
async unlock() {
|
|
5237
5227
|
if (!auth) {
|
|
@@ -6194,7 +6184,6 @@ function SignInView({
|
|
|
6194
6184
|
await onKingsChatSignIn();
|
|
6195
6185
|
} catch (e) {
|
|
6196
6186
|
setError(e.message);
|
|
6197
|
-
} finally {
|
|
6198
6187
|
setKingsChatLoading(false);
|
|
6199
6188
|
}
|
|
6200
6189
|
}
|
|
@@ -6330,7 +6319,7 @@ function SignInView({
|
|
|
6330
6319
|
value: phone,
|
|
6331
6320
|
onChange: (e) => setPhone(e.target.value)
|
|
6332
6321
|
}
|
|
6333
|
-
), /* @__PURE__ */ import_react2.default.createElement("button", { type: "submit", className: "dcm-submit", disabled: busy || !phone.trim() }, phoneLoading ? "Sending\u2026" : "Submit")), authMethods.includes("x") && onXSignIn && /* @__PURE__ */ import_react2.default.createElement("button", { type: "button", className: "dcm-method", onClick: handleX, disabled: busy }, /* @__PURE__ */ import_react2.default.createElement(XLogo, { size: 16 }), /* @__PURE__ */ import_react2.default.createElement("span", null, xLoading ? "Redirecting to X\u2026" : "X")), authMethods.includes("kingschat") && onKingsChatSignIn && /* @__PURE__ */ import_react2.default.createElement("button", { type: "button", className: "dcm-method", onClick: handleKingsChat, disabled: busy }, /* @__PURE__ */ import_react2.default.createElement(ChatIcon, { size: 18 }), /* @__PURE__ */ import_react2.default.createElement("span", null, kingsChatLoading ? "
|
|
6322
|
+
), /* @__PURE__ */ import_react2.default.createElement("button", { type: "submit", className: "dcm-submit", disabled: busy || !phone.trim() }, phoneLoading ? "Sending\u2026" : "Submit")), authMethods.includes("x") && onXSignIn && /* @__PURE__ */ import_react2.default.createElement("button", { type: "button", className: "dcm-method", onClick: handleX, disabled: busy }, /* @__PURE__ */ import_react2.default.createElement(XLogo, { size: 16 }), /* @__PURE__ */ import_react2.default.createElement("span", null, xLoading ? "Redirecting to X\u2026" : "X")), authMethods.includes("kingschat") && onKingsChatSignIn && /* @__PURE__ */ import_react2.default.createElement("button", { type: "button", className: "dcm-method", onClick: handleKingsChat, disabled: busy }, /* @__PURE__ */ import_react2.default.createElement(ChatIcon, { size: 18 }), /* @__PURE__ */ import_react2.default.createElement("span", null, kingsChatLoading ? "Redirecting to KingsChat\u2026" : "KingsChat")), customAuthLabel && onCustomAuthSignIn && /* @__PURE__ */ import_react2.default.createElement("button", { type: "button", className: "dcm-method", onClick: handleCustomAuth, disabled: busy }, /* @__PURE__ */ import_react2.default.createElement(UserIcon, { size: 18 }), /* @__PURE__ */ import_react2.default.createElement("span", null, customLoading ? "Connecting\u2026" : customAuthLabel))), extra, errorLine, canUsePasskey && onPasskeySignIn && /* @__PURE__ */ import_react2.default.createElement("button", { type: "button", className: "dcm-link", onClick: () => {
|
|
6334
6323
|
setError("");
|
|
6335
6324
|
setShowOtherMethods(false);
|
|
6336
6325
|
} }, "Use my passkey instead"), /* @__PURE__ */ import_react2.default.createElement(ProtectedBy, null));
|
|
@@ -6987,7 +6976,7 @@ function DecaneKit({ config, children }) {
|
|
|
6987
6976
|
sdk.canSignInWithPasskey().then((can) => setSocialAuth((p) => ({ ...p, canUsePasskey: can }))).catch(() => {
|
|
6988
6977
|
});
|
|
6989
6978
|
const params = new URLSearchParams(window.location.search);
|
|
6990
|
-
if (params.has("decane_jwt") || params.has("decane_error")) {
|
|
6979
|
+
if (params.has("decane_jwt") || params.has("decane_error") || readKingsChatReturn(window.location.search)) {
|
|
6991
6980
|
sdk.completeRedirectSignIn().then(applyResult).catch((e) => {
|
|
6992
6981
|
setSocialAuth((p) => p.phase ? { ...p, phase: null } : p);
|
|
6993
6982
|
console.error(e);
|