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.d.cts CHANGED
@@ -94,11 +94,8 @@ interface DecaneConnectConfig {
94
94
  chains: Chain[];
95
95
  authMethods: Array<'google' | 'email' | 'phone' | 'kingschat' | 'x'>;
96
96
  /**
97
- * Scopes requested from KingsChat at sign-in. Defaults to
98
- * `['send_chat_message']`, which is what KingsChat's own SDK asks for and is
99
- * enough to establish identity. Widen only if the app actually calls
100
- * KingsChat APIs on the user's behalf — the consent screen lists every scope,
101
- * so asking for more than you use costs conversions.
97
+ * @deprecated Ignored since 2.31.0. KingsChat scopes are now set on the
98
+ * KingsChat project in its developer portal, not per login request.
102
99
  */
103
100
  kingsChatScopes?: string[];
104
101
  apiBaseUrl?: string;
@@ -765,9 +762,14 @@ declare class ApiClient {
765
762
  authorizationUrl: string;
766
763
  }>;
767
764
  /**
768
- * Exchange a KingsChat access token for a Decane session. The backend spends
769
- * the token against KingsChat's own API to establish the identity — it is
770
- * never trusted on its face, and never stored.
765
+ * Exchange a KingsChat authorization code for a Decane session. The backend
766
+ * trades it with KingsChat for an access token, verifies that token (it must
767
+ * have been issued to this project's KingsChat app), and never stores it.
768
+ */
769
+ authKingsChatCode(code: string): Promise<AuthResult>;
770
+ /**
771
+ * Exchange a KingsChat access token the app already holds for a Decane
772
+ * session. Verified the same way as a code's token, and never stored.
771
773
  */
772
774
  authKingsChat(accessToken: string): Promise<AuthResult>;
773
775
  authEmailStart(email: string): Promise<void>;
@@ -989,8 +991,8 @@ interface SocialConfig {
989
991
  apiKey: string;
990
992
  authMethods?: ("google" | "email" | "phone" | "kingschat" | "x")[];
991
993
  /**
992
- * Scopes requested from KingsChat. Defaults to `['send_chat_message']`, which
993
- * is what KingsChat's own SDK asks for and is enough to establish identity.
994
+ * @deprecated Ignored since 2.31.0. KingsChat scopes are now set on the
995
+ * KingsChat project in its developer portal, not per login request.
994
996
  */
995
997
  kingsChatScopes?: string[];
996
998
  promptPin?: () => Promise<string>;
@@ -1194,19 +1196,18 @@ declare function startEmailAuth(api: ApiClient, email: string): Promise<void>;
1194
1196
  */
1195
1197
  declare function verifyEmailCode(api: ApiClient, email: string, code: string): Promise<AuthResult>;
1196
1198
 
1199
+ /** @deprecated KingsChat no longer issues tokens to a popup. */
1197
1200
  interface KingsChatTokens {
1198
1201
  accessToken: string;
1199
1202
  refreshToken?: string;
1200
1203
  expiresInMillis?: number;
1201
1204
  }
1202
1205
  /**
1203
- * Open the KingsChat consent popup and resolve with the tokens it returns.
1204
- *
1205
- * Must be called synchronously from a user gesture (a click handler) — popup
1206
- * blockers reject a window.open that happens after an await, which is why the
1207
- * clientId is fetched by the caller *before* this runs.
1206
+ * @deprecated Since 2.31.0. KingsChat retired the popup login; use
1207
+ * `connectWithKingsChat()`, which redirects to KingsChat and finishes on the
1208
+ * page registered as the KingsChat application's Redirect URL.
1208
1209
  */
1209
- declare function loginWithKingsChatPopup(opts: {
1210
+ declare function loginWithKingsChatPopup(_opts?: {
1210
1211
  clientId: string;
1211
1212
  scopes?: string[];
1212
1213
  timeoutMs?: number;
package/dist/index.d.ts CHANGED
@@ -94,11 +94,8 @@ interface DecaneConnectConfig {
94
94
  chains: Chain[];
95
95
  authMethods: Array<'google' | 'email' | 'phone' | 'kingschat' | 'x'>;
96
96
  /**
97
- * Scopes requested from KingsChat at sign-in. Defaults to
98
- * `['send_chat_message']`, which is what KingsChat's own SDK asks for and is
99
- * enough to establish identity. Widen only if the app actually calls
100
- * KingsChat APIs on the user's behalf — the consent screen lists every scope,
101
- * so asking for more than you use costs conversions.
97
+ * @deprecated Ignored since 2.31.0. KingsChat scopes are now set on the
98
+ * KingsChat project in its developer portal, not per login request.
102
99
  */
103
100
  kingsChatScopes?: string[];
104
101
  apiBaseUrl?: string;
@@ -765,9 +762,14 @@ declare class ApiClient {
765
762
  authorizationUrl: string;
766
763
  }>;
767
764
  /**
768
- * Exchange a KingsChat access token for a Decane session. The backend spends
769
- * the token against KingsChat's own API to establish the identity — it is
770
- * never trusted on its face, and never stored.
765
+ * Exchange a KingsChat authorization code for a Decane session. The backend
766
+ * trades it with KingsChat for an access token, verifies that token (it must
767
+ * have been issued to this project's KingsChat app), and never stores it.
768
+ */
769
+ authKingsChatCode(code: string): Promise<AuthResult>;
770
+ /**
771
+ * Exchange a KingsChat access token the app already holds for a Decane
772
+ * session. Verified the same way as a code's token, and never stored.
771
773
  */
772
774
  authKingsChat(accessToken: string): Promise<AuthResult>;
773
775
  authEmailStart(email: string): Promise<void>;
@@ -989,8 +991,8 @@ interface SocialConfig {
989
991
  apiKey: string;
990
992
  authMethods?: ("google" | "email" | "phone" | "kingschat" | "x")[];
991
993
  /**
992
- * Scopes requested from KingsChat. Defaults to `['send_chat_message']`, which
993
- * is what KingsChat's own SDK asks for and is enough to establish identity.
994
+ * @deprecated Ignored since 2.31.0. KingsChat scopes are now set on the
995
+ * KingsChat project in its developer portal, not per login request.
994
996
  */
995
997
  kingsChatScopes?: string[];
996
998
  promptPin?: () => Promise<string>;
@@ -1194,19 +1196,18 @@ declare function startEmailAuth(api: ApiClient, email: string): Promise<void>;
1194
1196
  */
1195
1197
  declare function verifyEmailCode(api: ApiClient, email: string, code: string): Promise<AuthResult>;
1196
1198
 
1199
+ /** @deprecated KingsChat no longer issues tokens to a popup. */
1197
1200
  interface KingsChatTokens {
1198
1201
  accessToken: string;
1199
1202
  refreshToken?: string;
1200
1203
  expiresInMillis?: number;
1201
1204
  }
1202
1205
  /**
1203
- * Open the KingsChat consent popup and resolve with the tokens it returns.
1204
- *
1205
- * Must be called synchronously from a user gesture (a click handler) — popup
1206
- * blockers reject a window.open that happens after an await, which is why the
1207
- * clientId is fetched by the caller *before* this runs.
1206
+ * @deprecated Since 2.31.0. KingsChat retired the popup login; use
1207
+ * `connectWithKingsChat()`, which redirects to KingsChat and finishes on the
1208
+ * page registered as the KingsChat application's Redirect URL.
1208
1209
  */
1209
- declare function loginWithKingsChatPopup(opts: {
1210
+ declare function loginWithKingsChatPopup(_opts?: {
1210
1211
  clientId: string;
1211
1212
  scopes?: string[];
1212
1213
  timeoutMs?: number;
package/dist/index.js CHANGED
@@ -2106,9 +2106,19 @@ var ApiClient = class {
2106
2106
  return this._request("/auth/kingschat/config", { method: "GET" });
2107
2107
  }
2108
2108
  /**
2109
- * Exchange a KingsChat access token for a Decane session. The backend spends
2110
- * the token against KingsChat's own API to establish the identity — it is
2111
- * never trusted on its face, and never stored.
2109
+ * Exchange a KingsChat authorization code for a Decane session. The backend
2110
+ * trades it with KingsChat for an access token, verifies that token (it must
2111
+ * have been issued to this project's KingsChat app), and never stores it.
2112
+ */
2113
+ async authKingsChatCode(code) {
2114
+ return this._request("/auth/kingschat/code", {
2115
+ method: "POST",
2116
+ body: JSON.stringify({ code })
2117
+ });
2118
+ }
2119
+ /**
2120
+ * Exchange a KingsChat access token the app already holds for a Decane
2121
+ * session. Verified the same way as a code's token, and never stored.
2112
2122
  */
2113
2123
  async authKingsChat(accessToken) {
2114
2124
  return this._request("/auth/kingschat", {
@@ -2463,15 +2473,16 @@ init_errors();
2463
2473
 
2464
2474
  // ../infra/tee-measurement.json
2465
2475
  var tee_measurement_default = {
2466
- signerVersion: "0.2.10",
2476
+ signerVersion: "0.2.13",
2467
2477
  acceptedMrtd: [
2478
+ '["fb14dc139f33d6fcf474bc8332cac001259fb31cfbcb6b34d4ceeb552a2c4466884a0cbde45ad98a05c5c060c23ad65a","07e6f51aa763abfe75c3ddfbf4f425fe3f0ceff66d807a75e049303dce9addf68e7218729bd419638af63a370f65878c","a2a58c9a959a4fa44bd6da0c97a2270c051faf12084cfe91ae900e4fdff6cdd4f69a82005e04ee920f231497894d677f","5538de3ba0930ae65c7c2b99346742a67dff7891545d91e8d74006d215fff256db91998eaff1c3dcca679a89df566700"]',
2468
2479
  '["fb14dc139f33d6fcf474bc8332cac001259fb31cfbcb6b34d4ceeb552a2c4466884a0cbde45ad98a05c5c060c23ad65a","07e6f51aa763abfe75c3ddfbf4f425fe3f0ceff66d807a75e049303dce9addf68e7218729bd419638af63a370f65878c","a2a58c9a959a4fa44bd6da0c97a2270c051faf12084cfe91ae900e4fdff6cdd4f69a82005e04ee920f231497894d677f","a76a81147b7f733cb400914c8791ec149bb3a31f3727b1c1e12ec22e9c1a183b6e617e78636dca37075f5a695afd46c2"]',
2469
2480
  '["68102e7b524af310f7b7d426ce75481e36c40f5d513a9009c046e9d37e31551f0134d954b496a3357fd61d03f07ffe96","07e6f51aa763abfe75c3ddfbf4f425fe3f0ceff66d807a75e049303dce9addf68e7218729bd419638af63a370f65878c","a2a58c9a959a4fa44bd6da0c97a2270c051faf12084cfe91ae900e4fdff6cdd4f69a82005e04ee920f231497894d677f","a76a81147b7f733cb400914c8791ec149bb3a31f3727b1c1e12ec22e9c1a183b6e617e78636dca37075f5a695afd46c2"]'
2470
2481
  ],
2471
- primaryMrtd: '["fb14dc139f33d6fcf474bc8332cac001259fb31cfbcb6b34d4ceeb552a2c4466884a0cbde45ad98a05c5c060c23ad65a","07e6f51aa763abfe75c3ddfbf4f425fe3f0ceff66d807a75e049303dce9addf68e7218729bd419638af63a370f65878c","a2a58c9a959a4fa44bd6da0c97a2270c051faf12084cfe91ae900e4fdff6cdd4f69a82005e04ee920f231497894d677f","a76a81147b7f733cb400914c8791ec149bb3a31f3727b1c1e12ec22e9c1a183b6e617e78636dca37075f5a695afd46c2"]',
2472
- capturedAt: "2026-09-07T07:19:30.544601Z",
2482
+ primaryMrtd: '["fb14dc139f33d6fcf474bc8332cac001259fb31cfbcb6b34d4ceeb552a2c4466884a0cbde45ad98a05c5c060c23ad65a","07e6f51aa763abfe75c3ddfbf4f425fe3f0ceff66d807a75e049303dce9addf68e7218729bd419638af63a370f65878c","a2a58c9a959a4fa44bd6da0c97a2270c051faf12084cfe91ae900e4fdff6cdd4f69a82005e04ee920f231497894d677f","5538de3ba0930ae65c7c2b99346742a67dff7891545d91e8d74006d215fff256db91998eaff1c3dcca679a89df566700"]',
2483
+ capturedAt: "2026-09-30T18:36:41Z",
2473
2484
  tcbStatus: "UpToDate",
2474
- _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."
2485
+ _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."
2475
2486
  };
2476
2487
 
2477
2488
  // src/social/attestation.ts
@@ -3287,75 +3298,47 @@ async function verifyPhoneCode(api, phone, code) {
3287
3298
 
3288
3299
  // src/social/auth/kingschat.ts
3289
3300
  init_errors();
3290
- var KINGSCHAT_AUTH_ORIGIN = "https://accounts.kingsch.at";
3291
- function readTokens(data) {
3292
- if (!data || typeof data !== "object") return null;
3293
- const d = data;
3294
- const accessToken = d.accessToken ?? d.access_token;
3295
- if (typeof accessToken !== "string" || !accessToken) return null;
3296
- const refreshToken = d.refreshToken ?? d.refresh_token;
3297
- const expires = d.expiresInMillis ?? d.expires_in_millis;
3298
- return {
3299
- accessToken,
3300
- refreshToken: typeof refreshToken === "string" ? refreshToken : void 0,
3301
- expiresInMillis: typeof expires === "number" ? expires : void 0
3302
- };
3301
+ var KINGSCHAT_LOGIN_URL = "https://accounts.kingschat.online/log-in";
3302
+ var STATE_KEY = "decane:kingschat_state";
3303
+ function randomState() {
3304
+ const bytes = new Uint8Array(16);
3305
+ crypto.getRandomValues(bytes);
3306
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
3307
+ }
3308
+ function storage2() {
3309
+ try {
3310
+ return window.sessionStorage ?? null;
3311
+ } catch {
3312
+ return null;
3313
+ }
3303
3314
  }
3304
- function buildAuthUrl(clientId, scopes) {
3305
- const url = new URL(KINGSCHAT_AUTH_ORIGIN);
3306
- url.searchParams.set("client_id", clientId);
3307
- url.searchParams.set("scopes", JSON.stringify(scopes));
3308
- url.searchParams.set("redirect_uri", window.location.origin);
3309
- url.searchParams.set("post_message", "1");
3315
+ function beginKingsChatLogin(clientId, opts = {}) {
3316
+ const store = storage2();
3317
+ if (!store) throw new Error("KingsChat sign-in needs sessionStorage, which this browser is blocking");
3318
+ const state = randomState();
3319
+ store.setItem(STATE_KEY, state);
3320
+ const url = new URL(KINGSCHAT_LOGIN_URL);
3321
+ url.searchParams.set("clientId", clientId);
3322
+ url.searchParams.set("origin", state);
3323
+ if (opts.forceLogin) url.searchParams.set("forceLogin", "true");
3310
3324
  return url.toString();
3311
3325
  }
3312
- function loginWithKingsChatPopup(opts) {
3313
- const scopes = opts.scopes ?? ["profile"];
3314
- const popup = window.open(
3315
- buildAuthUrl(opts.clientId, scopes),
3316
- "decane-kingschat",
3317
- "width=480,height=720,menubar=no,toolbar=no"
3326
+ function readKingsChatReturn(search) {
3327
+ const params = new URLSearchParams(search);
3328
+ const code = params.get("code");
3329
+ const origin = params.get("origin");
3330
+ if (!code || !origin) return null;
3331
+ const expected = storage2()?.getItem(STATE_KEY);
3332
+ if (!expected || expected !== origin) return null;
3333
+ return { code };
3334
+ }
3335
+ function clearKingsChatState() {
3336
+ storage2()?.removeItem(STATE_KEY);
3337
+ }
3338
+ async function loginWithKingsChatPopup(_opts) {
3339
+ throw new CallSequenceError(
3340
+ "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."
3318
3341
  );
3319
- if (!popup) {
3320
- throw new CallSequenceError(
3321
- "The KingsChat sign-in popup was blocked. Call connectWithKingsChat() directly from a click handler."
3322
- );
3323
- }
3324
- return new Promise((resolve, reject) => {
3325
- let settled = false;
3326
- const finish = (fn) => {
3327
- if (settled) return;
3328
- settled = true;
3329
- window.removeEventListener("message", onMessage);
3330
- clearInterval(closedPoll);
3331
- clearTimeout(timeout);
3332
- try {
3333
- popup.close();
3334
- } catch {
3335
- }
3336
- fn();
3337
- };
3338
- const onMessage = (event) => {
3339
- if (event.origin !== KINGSCHAT_AUTH_ORIGIN) return;
3340
- const data = event.data;
3341
- if (data && typeof data === "object" && data.error) {
3342
- finish(() => reject(new CallSequenceError(`KingsChat sign-in failed: ${String(data.error)}`)));
3343
- return;
3344
- }
3345
- const tokens2 = readTokens(event.data);
3346
- if (tokens2) finish(() => resolve(tokens2));
3347
- };
3348
- window.addEventListener("message", onMessage);
3349
- const closedPoll = setInterval(() => {
3350
- if (popup.closed) finish(() => reject(new UserCancelledError()));
3351
- }, 400);
3352
- const timeout = setTimeout(
3353
- () => finish(
3354
- () => reject(new CallSequenceError("KingsChat sign-in timed out before returning a token."))
3355
- ),
3356
- opts.timeoutMs ?? 5 * 6e4
3357
- );
3358
- });
3359
3342
  }
3360
3343
 
3361
3344
  // src/social/signers/evm.ts
@@ -3960,7 +3943,7 @@ async function createDecaneConnect(config) {
3960
3943
  const teeUrl = config.teeUrl ?? DEFAULT_TEE_URL;
3961
3944
  const events = new EventEmitter();
3962
3945
  const api = new ApiClient(baseUrl, config.apiKey, config.appId);
3963
- const storage2 = new Storage();
3946
+ const storage3 = new Storage();
3964
3947
  const session = new SessionManager(
3965
3948
  events,
3966
3949
  durationMs,
@@ -4150,7 +4133,7 @@ async function createDecaneConnect(config) {
4150
4133
  }
4151
4134
  async function attachUnlockToken(userId, jwt, tokenKey) {
4152
4135
  try {
4153
- const stored = await storage2.getShare(userId);
4136
+ const stored = await storage3.getShare(userId);
4154
4137
  if (!stored) return;
4155
4138
  const { token } = await api.issueUnlockToken(jwt);
4156
4139
  const unlockTokenIv = generateRandomBytes(12);
@@ -4159,13 +4142,13 @@ async function createDecaneConnect(config) {
4159
4142
  new TextEncoder().encode(token),
4160
4143
  unlockTokenIv
4161
4144
  );
4162
- await storage2.putShare({ ...stored, wrappedUnlockToken, unlockTokenIv });
4145
+ await storage3.putShare({ ...stored, wrappedUnlockToken, unlockTokenIv });
4163
4146
  } catch (e) {
4164
4147
  console.warn("decane-connect-kit: could not store an unlock token for this device.", e);
4165
4148
  }
4166
4149
  }
4167
4150
  async function persistRotatedDeviceShare(userId, deviceShare) {
4168
- const stored = await storage2.getShare(userId);
4151
+ const stored = await storage3.getShare(userId);
4169
4152
  if (!stored) {
4170
4153
  throw new CallSequenceError(
4171
4154
  "Share set was rotated but this device has no stored share to update. Recover the wallet on this device before rotating."
@@ -4174,21 +4157,21 @@ async function createDecaneConnect(config) {
4174
4157
  const wrappingKey = await getWrappingKey(stored);
4175
4158
  const iv = generateRandomBytes(12);
4176
4159
  const wrappedDeviceShare = await aesGcmEncrypt(wrappingKey, deviceShare, iv);
4177
- await storage2.putShare({ ...stored, wrappedDeviceShare, iv });
4160
+ await storage3.putShare({ ...stored, wrappedDeviceShare, iv });
4178
4161
  }
4179
4162
  function hasPasswordUnlock(stored) {
4180
4163
  return !!stored && stored.unlockMethod === "password" && !!stored.passwordSalt && !!stored.wrappedUnlockToken && !!stored.unlockTokenIv;
4181
4164
  }
4182
4165
  async function passwordDeviceCandidate() {
4183
4166
  if (persisted?.userId) {
4184
- const stored = await storage2.getShare(persisted.userId);
4167
+ const stored = await storage3.getShare(persisted.userId);
4185
4168
  return hasPasswordUnlock(stored) ? stored : null;
4186
4169
  }
4187
4170
  if (lastUserId) {
4188
- const stored = await storage2.getShare(lastUserId);
4171
+ const stored = await storage3.getShare(lastUserId);
4189
4172
  return hasPasswordUnlock(stored) ? stored : null;
4190
4173
  }
4191
- const candidates = (await storage2.listShares()).filter(hasPasswordUnlock);
4174
+ const candidates = (await storage3.listShares()).filter(hasPasswordUnlock);
4192
4175
  return candidates.length === 1 ? candidates[0] : null;
4193
4176
  }
4194
4177
  async function getWrappingKey(stored) {
@@ -4216,7 +4199,7 @@ async function createDecaneConnect(config) {
4216
4199
  let credentialId;
4217
4200
  let prfOutput;
4218
4201
  try {
4219
- const label = (await storage2.getShare(userId))?.profile?.email;
4202
+ const label = (await storage3.getShare(userId))?.profile?.email;
4220
4203
  ({ credentialId, prfOutput } = await registerWebAuthnCredential({
4221
4204
  api,
4222
4205
  jwt,
@@ -4248,8 +4231,8 @@ async function createDecaneConnect(config) {
4248
4231
  const wrappingKey = await importAesKey(prfOutput);
4249
4232
  const iv = generateRandomBytes(12);
4250
4233
  const wrappedDeviceShare = await aesGcmEncrypt(wrappingKey, deviceShare, iv);
4251
- const existing = await storage2.getShare(userId);
4252
- await storage2.putShare({
4234
+ const existing = await storage3.getShare(userId);
4235
+ await storage3.putShare({
4253
4236
  ...existing,
4254
4237
  userId,
4255
4238
  wrappedDeviceShare,
@@ -4333,7 +4316,7 @@ async function createDecaneConnect(config) {
4333
4316
  const { wrappingKey, tokenKey, meta } = await passwordSetup();
4334
4317
  const iv = generateRandomBytes(12);
4335
4318
  const wrappedDeviceShare = await aesGcmEncrypt(wrappingKey, deviceShare, iv);
4336
- await storage2.putShare({
4319
+ await storage3.putShare({
4337
4320
  userId,
4338
4321
  wrappedDeviceShare,
4339
4322
  iv,
@@ -4407,7 +4390,7 @@ async function createDecaneConnect(config) {
4407
4390
  shareSet.device,
4408
4391
  iv
4409
4392
  );
4410
- await storage2.putShare({
4393
+ await storage3.putShare({
4411
4394
  userId,
4412
4395
  wrappedDeviceShare,
4413
4396
  iv,
@@ -4447,7 +4430,7 @@ async function createDecaneConnect(config) {
4447
4430
  zeroize(shareSet.server);
4448
4431
  zeroize(shareSet.device);
4449
4432
  zeroize(shareSet.recovery);
4450
- if (wrappingKey) await storage2.deleteShare(userId).catch(() => {
4433
+ if (wrappingKey) await storage3.deleteShare(userId).catch(() => {
4451
4434
  });
4452
4435
  events.emit("wallet-unlocking");
4453
4436
  return returningUserUnlock(userId, jwt);
@@ -4460,7 +4443,7 @@ async function createDecaneConnect(config) {
4460
4443
  } catch (e) {
4461
4444
  zeroize(shareSet.device);
4462
4445
  zeroize(shareSet.recovery);
4463
- if (wrappingKey) await storage2.deleteShare(userId).catch(() => {
4446
+ if (wrappingKey) await storage3.deleteShare(userId).catch(() => {
4464
4447
  });
4465
4448
  throw e;
4466
4449
  }
@@ -4536,7 +4519,7 @@ async function createDecaneConnect(config) {
4536
4519
  async function returningUserUnlock(userId, jwt) {
4537
4520
  const rawAddresses = await api.getAddresses(jwt);
4538
4521
  const recordedAddresses = rawAddresses.evm && rawAddresses.solana ? { evm: rawAddresses.evm, solana: rawAddresses.solana, tron: rawAddresses.tron ?? evmToTronAddress2(rawAddresses.evm) } : null;
4539
- const stored = await storage2.getShare(userId);
4522
+ const stored = await storage3.getShare(userId);
4540
4523
  let deviceShare;
4541
4524
  let joinedViaPasskeySync = false;
4542
4525
  let migrating = false;
@@ -4588,7 +4571,7 @@ async function createDecaneConnect(config) {
4588
4571
  rpId,
4589
4572
  teeUrl,
4590
4573
  api,
4591
- storage: storage2,
4574
+ storage: storage3,
4592
4575
  onProgress: config.onRecoveryProgress,
4593
4576
  promptForRecoveryFile: config.promptForRecoveryFile,
4594
4577
  onNewRecoveryShare: async (recoveryShare, shareSetVersion) => {
@@ -4602,7 +4585,7 @@ async function createDecaneConnect(config) {
4602
4585
  });
4603
4586
  deviceShare = recovered.deviceShare;
4604
4587
  joinedViaPasskeySync = recovered.flow === "passkey-sync";
4605
- if (!identityTier && !await storage2.getShare(userId) && isPasskeySupported()) {
4588
+ if (!identityTier && !await storage3.getShare(userId) && isPasskeySupported()) {
4606
4589
  await registerPasskeyAndBackup(userId, jwt, deviceShare);
4607
4590
  }
4608
4591
  } else {
@@ -4631,7 +4614,7 @@ async function createDecaneConnect(config) {
4631
4614
  } catch (e) {
4632
4615
  if (e instanceof StaleDeviceShareError) {
4633
4616
  zeroize(deviceShare);
4634
- await storage2.deleteShare(userId);
4617
+ await storage3.deleteShare(userId);
4635
4618
  if (stored) return returningUserUnlock(userId, jwt);
4636
4619
  } else {
4637
4620
  zeroize(deviceShare);
@@ -4659,7 +4642,7 @@ async function createDecaneConnect(config) {
4659
4642
  const result = await tee.rotateShares(jwt);
4660
4643
  try {
4661
4644
  await persistRotatedDeviceShare(userId, result.deviceShare);
4662
- const refreshed = await storage2.getShare(userId);
4645
+ const refreshed = await storage3.getShare(userId);
4663
4646
  if (refreshed?.unlockMethod === "passkey" && refreshed.credentialId && refreshed.prfSalt) {
4664
4647
  await api.putRecoveryBackup(jwt, {
4665
4648
  wrappedDeviceShare: refreshed.wrappedDeviceShare,
@@ -4692,13 +4675,13 @@ async function createDecaneConnect(config) {
4692
4675
  }
4693
4676
  async function passkeyLoginCandidate() {
4694
4677
  if (!persisted?.userId || !isPasskeySupported()) return null;
4695
- return usablePasskeyShare(await storage2.getShare(persisted.userId));
4678
+ return usablePasskeyShare(await storage3.getShare(persisted.userId));
4696
4679
  }
4697
4680
  async function passkeyDeviceCandidate() {
4698
4681
  if (!isPasskeySupported()) return null;
4699
4682
  if (persisted?.userId) return passkeyLoginCandidate();
4700
- if (lastUserId) return usablePasskeyShare(await storage2.getShare(lastUserId));
4701
- const candidates = (await storage2.listShares()).map(usablePasskeyShare).filter(Boolean);
4683
+ if (lastUserId) return usablePasskeyShare(await storage3.getShare(lastUserId));
4684
+ const candidates = (await storage3.listShares()).map(usablePasskeyShare).filter(Boolean);
4702
4685
  return candidates.length === 1 ? candidates[0] : null;
4703
4686
  }
4704
4687
  async function canOfferPasskey() {
@@ -4706,15 +4689,15 @@ async function createDecaneConnect(config) {
4706
4689
  if (await passkeyDeviceCandidate()) return true;
4707
4690
  const known = persisted?.userId ?? lastUserId;
4708
4691
  if (!known) return false;
4709
- return await storage2.getShare(known) == null;
4692
+ return await storage3.getShare(known) == null;
4710
4693
  }
4711
4694
  async function rememberProfile(userId, profile) {
4712
4695
  const incoming = Object.fromEntries(Object.entries(profile).filter(([, v]) => v));
4713
4696
  if (!Object.keys(incoming).length) return;
4714
4697
  try {
4715
- const stored = await storage2.getShare(userId);
4698
+ const stored = await storage3.getShare(userId);
4716
4699
  if (!stored) return;
4717
- await storage2.putShare({ ...stored, profile: { ...stored.profile, ...incoming } });
4700
+ await storage3.putShare({ ...stored, profile: { ...stored.profile, ...incoming } });
4718
4701
  } catch {
4719
4702
  }
4720
4703
  }
@@ -4738,7 +4721,7 @@ async function createDecaneConnect(config) {
4738
4721
  savePersisted(PERSIST_KEY, persisted);
4739
4722
  events.emit("connected");
4740
4723
  persistLiveSession();
4741
- const stored = await storage2.getShare(userId);
4724
+ const stored = await storage3.getShare(userId);
4742
4725
  return {
4743
4726
  addresses,
4744
4727
  isNewUser: false,
@@ -4798,6 +4781,11 @@ async function createDecaneConnect(config) {
4798
4781
  return new Promise(() => {
4799
4782
  });
4800
4783
  }
4784
+ async function finishKingsChatReturn(code) {
4785
+ clearKingsChatState();
4786
+ window.history.replaceState({}, "", window.location.pathname);
4787
+ return handleAuthResult(await api.authKingsChatCode(code));
4788
+ }
4801
4789
  async function handleAuthResult(result) {
4802
4790
  let addresses;
4803
4791
  if (!result.hasShare) {
@@ -4860,6 +4848,8 @@ async function createDecaneConnect(config) {
4860
4848
  * this never starts a flow.
4861
4849
  */
4862
4850
  async completeRedirectSignIn() {
4851
+ const pending = readKingsChatReturn(window.location.search);
4852
+ if (pending) return finishKingsChatReturn(pending.code);
4863
4853
  const outcome = readRedirectOutcome(window.location.search);
4864
4854
  if (outcome.kind === "none") {
4865
4855
  throw new CallSequenceError("No sign-in result in the URL to complete");
@@ -4878,12 +4868,12 @@ async function createDecaneConnect(config) {
4878
4868
  return handleAuthResult(await api.authGoogleToken(idToken));
4879
4869
  },
4880
4870
  async connectWithKingsChat() {
4871
+ const pending = readKingsChatReturn(window.location.search);
4872
+ if (pending) return finishKingsChatReturn(pending.code);
4881
4873
  const { clientId } = await api.getKingsChatConfig();
4882
- const tokens2 = await loginWithKingsChatPopup({
4883
- clientId,
4884
- scopes: config.kingsChatScopes
4874
+ window.location.href = beginKingsChatLogin(clientId);
4875
+ return new Promise(() => {
4885
4876
  });
4886
- return client.connectWithKingsChatToken(tokens2.accessToken);
4887
4877
  },
4888
4878
  async connectWithKingsChatToken(accessToken) {
4889
4879
  if (!accessToken) {
@@ -4925,7 +4915,7 @@ async function createDecaneConnect(config) {
4925
4915
  events.emit("connected");
4926
4916
  persistLiveSession();
4927
4917
  scheduleAccessTokenExpiry(token, claims.expiresAt);
4928
- const stored = await storage2.getShare(claims.userId);
4918
+ const stored = await storage3.getShare(claims.userId);
4929
4919
  return {
4930
4920
  addresses,
4931
4921
  isNewUser: false,
@@ -4961,7 +4951,7 @@ async function createDecaneConnect(config) {
4961
4951
  const candidate = await passkeyDeviceCandidate();
4962
4952
  if (candidate) return passkeyLogin(candidate);
4963
4953
  const known = persisted?.userId ?? lastUserId;
4964
- if (isPasskeySupported() && known && await storage2.getShare(known) == null) {
4954
+ if (isPasskeySupported() && known && await storage3.getShare(known) == null) {
4965
4955
  return passkeyLoginWithoutShare(known);
4966
4956
  }
4967
4957
  throw new NewDeviceError();
@@ -4980,7 +4970,7 @@ async function createDecaneConnect(config) {
4980
4970
  async setUnlockPassword(password) {
4981
4971
  if (!auth) throw new ReconnectRequiredError();
4982
4972
  validateUnlockPassword(password);
4983
- const stored = await storage2.getShare(auth.userId);
4973
+ const stored = await storage3.getShare(auth.userId);
4984
4974
  if (!stored) throw new NewDeviceError();
4985
4975
  const deviceShare = await aesGcmDecrypt(
4986
4976
  await getWrappingKey(stored),
@@ -4999,7 +4989,7 @@ async function createDecaneConnect(config) {
4999
4989
  );
5000
4990
  const iv = generateRandomBytes(12);
5001
4991
  const wrappedDeviceShare = await aesGcmEncrypt(shareKey, deviceShare, iv);
5002
- await storage2.putShare({
4992
+ await storage3.putShare({
5003
4993
  ...stored,
5004
4994
  unlockMethod: "password",
5005
4995
  wrappedDeviceShare,
@@ -5079,9 +5069,9 @@ async function createDecaneConnect(config) {
5079
5069
  async removeUnlockPassword() {
5080
5070
  if (!auth) throw new ReconnectRequiredError();
5081
5071
  await api.revokeUnlockTokens(auth.jwt);
5082
- const stored = await storage2.getShare(auth.userId);
5072
+ const stored = await storage3.getShare(auth.userId);
5083
5073
  if (!stored || stored.unlockMethod !== "password") return;
5084
- await storage2.putShare({
5074
+ await storage3.putShare({
5085
5075
  ...stored,
5086
5076
  wrappedUnlockToken: void 0,
5087
5077
  unlockTokenIv: void 0
@@ -5089,7 +5079,7 @@ async function createDecaneConnect(config) {
5089
5079
  },
5090
5080
  async hasPasskey() {
5091
5081
  if (!auth) return false;
5092
- const stored = await storage2.getShare(auth.userId);
5082
+ const stored = await storage3.getShare(auth.userId);
5093
5083
  return stored?.unlockMethod === "passkey";
5094
5084
  },
5095
5085
  async addPasskey() {
@@ -5100,7 +5090,7 @@ async function createDecaneConnect(config) {
5100
5090
  }
5101
5091
  if (!auth) throw new ReconnectRequiredError();
5102
5092
  if (!isPasskeySupported()) throw new WebAuthnNotSupportedError();
5103
- const stored = await storage2.getShare(auth.userId);
5093
+ const stored = await storage3.getShare(auth.userId);
5104
5094
  if (stored?.unlockMethod === "passkey") return;
5105
5095
  if (!stored) {
5106
5096
  if (!serverAssistedRecovery) throw new NewDeviceError();
@@ -5133,12 +5123,12 @@ async function createDecaneConnect(config) {
5133
5123
  },
5134
5124
  async deviceProtected() {
5135
5125
  if (!auth) return false;
5136
- return await storage2.getShare(auth.userId) !== null;
5126
+ return await storage3.getShare(auth.userId) !== null;
5137
5127
  },
5138
5128
  async protectDevice() {
5139
5129
  if (identityTier) return false;
5140
5130
  if (!auth) throw new ReconnectRequiredError();
5141
- if (await storage2.getShare(auth.userId)) return true;
5131
+ if (await storage3.getShare(auth.userId)) return true;
5142
5132
  if (!serverAssistedRecovery) throw new NewDeviceError();
5143
5133
  const tee = new TeeClient(teeUrl);
5144
5134
  const provisioned = await tee.provisionDeviceShare(auth.jwt);
@@ -5148,7 +5138,7 @@ async function createDecaneConnect(config) {
5148
5138
  } finally {
5149
5139
  zeroize(provisioned);
5150
5140
  }
5151
- return await storage2.getShare(auth.userId) !== null;
5141
+ return await storage3.getShare(auth.userId) !== null;
5152
5142
  },
5153
5143
  async unlock() {
5154
5144
  if (!auth) {
@@ -6111,7 +6101,6 @@ function SignInView({
6111
6101
  await onKingsChatSignIn();
6112
6102
  } catch (e) {
6113
6103
  setError(e.message);
6114
- } finally {
6115
6104
  setKingsChatLoading(false);
6116
6105
  }
6117
6106
  }
@@ -6247,7 +6236,7 @@ function SignInView({
6247
6236
  value: phone,
6248
6237
  onChange: (e) => setPhone(e.target.value)
6249
6238
  }
6250
- ), /* @__PURE__ */ React2.createElement("button", { type: "submit", className: "dcm-submit", disabled: busy || !phone.trim() }, phoneLoading ? "Sending\u2026" : "Submit")), authMethods.includes("x") && onXSignIn && /* @__PURE__ */ React2.createElement("button", { type: "button", className: "dcm-method", onClick: handleX, disabled: busy }, /* @__PURE__ */ React2.createElement(XLogo, { size: 16 }), /* @__PURE__ */ React2.createElement("span", null, xLoading ? "Redirecting to X\u2026" : "X")), authMethods.includes("kingschat") && onKingsChatSignIn && /* @__PURE__ */ React2.createElement("button", { type: "button", className: "dcm-method", onClick: handleKingsChat, disabled: busy }, /* @__PURE__ */ React2.createElement(ChatIcon, { size: 18 }), /* @__PURE__ */ React2.createElement("span", null, kingsChatLoading ? "Waiting for KingsChat\u2026" : "KingsChat")), customAuthLabel && onCustomAuthSignIn && /* @__PURE__ */ React2.createElement("button", { type: "button", className: "dcm-method", onClick: handleCustomAuth, disabled: busy }, /* @__PURE__ */ React2.createElement(UserIcon, { size: 18 }), /* @__PURE__ */ React2.createElement("span", null, customLoading ? "Connecting\u2026" : customAuthLabel))), extra, errorLine, canUsePasskey && onPasskeySignIn && /* @__PURE__ */ React2.createElement("button", { type: "button", className: "dcm-link", onClick: () => {
6239
+ ), /* @__PURE__ */ React2.createElement("button", { type: "submit", className: "dcm-submit", disabled: busy || !phone.trim() }, phoneLoading ? "Sending\u2026" : "Submit")), authMethods.includes("x") && onXSignIn && /* @__PURE__ */ React2.createElement("button", { type: "button", className: "dcm-method", onClick: handleX, disabled: busy }, /* @__PURE__ */ React2.createElement(XLogo, { size: 16 }), /* @__PURE__ */ React2.createElement("span", null, xLoading ? "Redirecting to X\u2026" : "X")), authMethods.includes("kingschat") && onKingsChatSignIn && /* @__PURE__ */ React2.createElement("button", { type: "button", className: "dcm-method", onClick: handleKingsChat, disabled: busy }, /* @__PURE__ */ React2.createElement(ChatIcon, { size: 18 }), /* @__PURE__ */ React2.createElement("span", null, kingsChatLoading ? "Redirecting to KingsChat\u2026" : "KingsChat")), customAuthLabel && onCustomAuthSignIn && /* @__PURE__ */ React2.createElement("button", { type: "button", className: "dcm-method", onClick: handleCustomAuth, disabled: busy }, /* @__PURE__ */ React2.createElement(UserIcon, { size: 18 }), /* @__PURE__ */ React2.createElement("span", null, customLoading ? "Connecting\u2026" : customAuthLabel))), extra, errorLine, canUsePasskey && onPasskeySignIn && /* @__PURE__ */ React2.createElement("button", { type: "button", className: "dcm-link", onClick: () => {
6251
6240
  setError("");
6252
6241
  setShowOtherMethods(false);
6253
6242
  } }, "Use my passkey instead"), /* @__PURE__ */ React2.createElement(ProtectedBy, null));
@@ -6911,7 +6900,7 @@ function DecaneKit({ config, children }) {
6911
6900
  sdk.canSignInWithPasskey().then((can) => setSocialAuth((p) => ({ ...p, canUsePasskey: can }))).catch(() => {
6912
6901
  });
6913
6902
  const params = new URLSearchParams(window.location.search);
6914
- if (params.has("decane_jwt") || params.has("decane_error")) {
6903
+ if (params.has("decane_jwt") || params.has("decane_error") || readKingsChatReturn(window.location.search)) {
6915
6904
  sdk.completeRedirectSignIn().then(applyResult).catch((e) => {
6916
6905
  setSocialAuth((p) => p.phase ? { ...p, phase: null } : p);
6917
6906
  console.error(e);