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 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 access token for a Decane session. The backend spends
2198
- * the token against KingsChat's own API to establish the identity — it is
2199
- * never trusted on its face, and never stored.
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.10",
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","a76a81147b7f733cb400914c8791ec149bb3a31f3727b1c1e12ec22e9c1a183b6e617e78636dca37075f5a695afd46c2"]',
2555
- capturedAt: "2026-09-07T07:19:30.544601Z",
2565
+ primaryMrtd: '["fb14dc139f33d6fcf474bc8332cac001259fb31cfbcb6b34d4ceeb552a2c4466884a0cbde45ad98a05c5c060c23ad65a","07e6f51aa763abfe75c3ddfbf4f425fe3f0ceff66d807a75e049303dce9addf68e7218729bd419638af63a370f65878c","a2a58c9a959a4fa44bd6da0c97a2270c051faf12084cfe91ae900e4fdff6cdd4f69a82005e04ee920f231497894d677f","5538de3ba0930ae65c7c2b99346742a67dff7891545d91e8d74006d215fff256db91998eaff1c3dcca679a89df566700"]',
2566
+ capturedAt: "2026-09-30T18:36:41Z",
2556
2567
  tcbStatus: "UpToDate",
2557
- _note: "Captured from the live CVM 786e714d-5042-4bf1-8060-0bc358f6a72f (app_4405742df08d\u2026) after the 2026-09-07 credential recovery. RTMR0 changed; RTMR1-3 did not \u2014 RTMR3 is the app-compose measurement, so the app's identity is unchanged and this is a platform/firmware move underneath the CVM (Phala node v0.6.0), not a change to what we deploy. Sealing was verified intact across it (/signer/sealing-status: sealingAvailable, roundTripOk, ready), so no auth_shares were lost. The previous measurement is retained above so a rollback still verifies. measurement is the JSON.stringify(replayRtmrs()) output, compared as an opaque string (not parsed per-RTMR), matching social/attestation.ts."
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 KINGSCHAT_AUTH_ORIGIN = "https://accounts.kingsch.at";
3374
- function readTokens(data) {
3375
- if (!data || typeof data !== "object") return null;
3376
- const d = data;
3377
- const accessToken = d.accessToken ?? d.access_token;
3378
- if (typeof accessToken !== "string" || !accessToken) return null;
3379
- const refreshToken = d.refreshToken ?? d.refresh_token;
3380
- const expires = d.expiresInMillis ?? d.expires_in_millis;
3381
- return {
3382
- accessToken,
3383
- refreshToken: typeof refreshToken === "string" ? refreshToken : void 0,
3384
- expiresInMillis: typeof expires === "number" ? expires : void 0
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 buildAuthUrl(clientId, scopes) {
3388
- const url = new URL(KINGSCHAT_AUTH_ORIGIN);
3389
- url.searchParams.set("client_id", clientId);
3390
- url.searchParams.set("scopes", JSON.stringify(scopes));
3391
- url.searchParams.set("redirect_uri", window.location.origin);
3392
- url.searchParams.set("post_message", "1");
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 loginWithKingsChatPopup(opts) {
3396
- const scopes = opts.scopes ?? ["profile"];
3397
- const popup = window.open(
3398
- buildAuthUrl(opts.clientId, scopes),
3399
- "decane-kingschat",
3400
- "width=480,height=720,menubar=no,toolbar=no"
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 storage2 = new Storage();
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 storage2.getShare(userId);
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 storage2.putShare({ ...stored, wrappedUnlockToken, unlockTokenIv });
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 storage2.getShare(userId);
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 storage2.putShare({ ...stored, wrappedDeviceShare, iv });
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 storage2.getShare(persisted.userId);
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 storage2.getShare(lastUserId);
4254
+ const stored = await storage3.getShare(lastUserId);
4272
4255
  return hasPasswordUnlock(stored) ? stored : null;
4273
4256
  }
4274
- const candidates = (await storage2.listShares()).filter(hasPasswordUnlock);
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 storage2.getShare(userId))?.profile?.email;
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 storage2.getShare(userId);
4335
- await storage2.putShare({
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 storage2.putShare({
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 storage2.putShare({
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 storage2.deleteShare(userId).catch(() => {
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 storage2.deleteShare(userId).catch(() => {
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 storage2.getShare(userId);
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: storage2,
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 storage2.getShare(userId) && isPasskeySupported()) {
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 storage2.deleteShare(userId);
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 storage2.getShare(userId);
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 storage2.getShare(persisted.userId));
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 storage2.getShare(lastUserId));
4784
- const candidates = (await storage2.listShares()).map(usablePasskeyShare).filter(Boolean);
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 storage2.getShare(known) == null;
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 storage2.getShare(userId);
4781
+ const stored = await storage3.getShare(userId);
4799
4782
  if (!stored) return;
4800
- await storage2.putShare({ ...stored, profile: { ...stored.profile, ...incoming } });
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 storage2.getShare(userId);
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
- const tokens2 = await loginWithKingsChatPopup({
4966
- clientId,
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 storage2.getShare(claims.userId);
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 storage2.getShare(known) == null) {
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 storage2.getShare(auth.userId);
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 storage2.putShare({
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 storage2.getShare(auth.userId);
5155
+ const stored = await storage3.getShare(auth.userId);
5166
5156
  if (!stored || stored.unlockMethod !== "password") return;
5167
- await storage2.putShare({
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 storage2.getShare(auth.userId);
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 storage2.getShare(auth.userId);
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 storage2.getShare(auth.userId) !== null;
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 storage2.getShare(auth.userId)) return true;
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 storage2.getShare(auth.userId) !== null;
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 ? "Waiting for 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: () => {
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);