decane-connect-kit 2.2.0 → 2.2.2

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
@@ -1983,7 +1983,11 @@ var EventEmitter = class {
1983
1983
  init_errors();
1984
1984
  var ERROR_MAP = {
1985
1985
  INVALID_API_KEY: InvalidApiKeyError,
1986
- ORIGIN_NOT_ALLOWED: OriginNotAllowedError
1986
+ ORIGIN_NOT_ALLOWED: OriginNotAllowedError,
1987
+ // The backend's authMiddleware code for an expired/revoked JWT. Surfacing it
1988
+ // as ReconnectRequiredError routes callers to the sign-in UI instead of a
1989
+ // generic failure message.
1990
+ UNAUTHORIZED: ReconnectRequiredError
1987
1991
  };
1988
1992
  var ApiClient = class {
1989
1993
  constructor(baseUrl, apiKey, appId) {
@@ -2269,11 +2273,22 @@ var TeeClient = class {
2269
2273
  this.attested = false;
2270
2274
  /** Mirrors what the signer confirmed for this session, not what we asked for. */
2271
2275
  this.assertionPerSignature = false;
2276
+ /** The signer's authoritative remaining lifetime (seconds), null until a session opens. */
2277
+ this.sessionExpiresInSec = null;
2272
2278
  }
2273
2279
  /** Whether this session actually got per-signature assertions. */
2274
2280
  requiresAssertionPerSignature() {
2275
2281
  return this.assertionPerSignature;
2276
2282
  }
2283
+ /**
2284
+ * The lifetime the signer granted this session, in seconds — which can be
2285
+ * shorter than the client's configured duration when the JWT expires first.
2286
+ * Null if the signer didn't report one (older build). Callers clamp their own
2287
+ * countdown to this so it never promises time the handle doesn't have.
2288
+ */
2289
+ sessionExpiresIn() {
2290
+ return this.sessionExpiresInSec;
2291
+ }
2277
2292
  async ensureAttested() {
2278
2293
  if (this.attested) return;
2279
2294
  const nonce = crypto.getRandomValues(new Uint8Array(16));
@@ -2302,6 +2317,7 @@ var TeeClient = class {
2302
2317
  const body = await res.json();
2303
2318
  this.handle = body.handle;
2304
2319
  this.assertionPerSignature = body.requireAssertionPerSignature === true;
2320
+ this.sessionExpiresInSec = typeof body.expiresIn === "number" ? body.expiresIn : null;
2305
2321
  return body.addresses ?? null;
2306
2322
  }
2307
2323
  /**
@@ -2344,6 +2360,7 @@ var TeeClient = class {
2344
2360
  headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" },
2345
2361
  body: JSON.stringify({ rpId: window.location.hostname })
2346
2362
  });
2363
+ if (res.status === 401) throw new ReconnectRequiredError();
2347
2364
  if (!res.ok) throw new ExportNotAuthorizedError();
2348
2365
  const { challengeId, options } = await res.json();
2349
2366
  let assertion;
@@ -2395,8 +2412,9 @@ var TeeClient = class {
2395
2412
  /**
2396
2413
  * `jwt` is only needed when this session requires per-signature assertions —
2397
2414
  * it authenticates the challenge request. Passed per call rather than cached
2398
- * on the instance deliberately: the backend's JWTs are shorter-lived than the
2399
- * TEE session TTL, so a stored copy would go stale mid-session.
2415
+ * on the instance deliberately: JWT lifetime is configured independently of
2416
+ * the TEE session TTL (they are aligned at 30min by default, but an operator
2417
+ * can shorten JWT_EXPIRY_SECONDS), so a stored copy could go stale mid-session.
2400
2418
  */
2401
2419
  async sign(req, jwt) {
2402
2420
  if (!this.handle) throw new Error("no session");
@@ -2486,12 +2504,14 @@ var SessionManager = class {
2486
2504
  }
2487
2505
  this.evmAddress = addresses.evm;
2488
2506
  this.solanaAddress = addresses.solana;
2489
- this.expiresAt = Date.now() + this.durationMs;
2507
+ const serverTtlMs = this.tee.sessionExpiresIn();
2508
+ const effectiveMs = serverTtlMs != null ? Math.min(this.durationMs, serverTtlMs * 1e3) : this.durationMs;
2509
+ this.expiresAt = Date.now() + effectiveMs;
2490
2510
  if (this.timeoutId) clearTimeout(this.timeoutId);
2491
2511
  this.timeoutId = setTimeout(() => {
2492
2512
  this.end();
2493
2513
  this.events.emit("session-expired");
2494
- }, this.durationMs);
2514
+ }, effectiveMs);
2495
2515
  this.events.emit("session-started");
2496
2516
  return addresses;
2497
2517
  }
@@ -5311,7 +5331,15 @@ function DecaneKit({ config, children }) {
5311
5331
  (0, import_react4.useEffect)(() => {
5312
5332
  if (!hasSocial(mode) || !config?.social || socialInitialized.current) return;
5313
5333
  socialInitialized.current = true;
5314
- const { apiKey, authMethods, promptPin, apiBaseUrl, chains, rpcUrls } = config.social;
5334
+ const {
5335
+ apiKey,
5336
+ authMethods,
5337
+ promptPin,
5338
+ apiBaseUrl,
5339
+ chains,
5340
+ rpcUrls,
5341
+ requireAssertionPerSignature
5342
+ } = config.social;
5315
5343
  createDecaneConnect({
5316
5344
  appId: config.appId,
5317
5345
  apiKey,
@@ -5319,7 +5347,8 @@ function DecaneKit({ config, children }) {
5319
5347
  authMethods: authMethods ?? ["google", "email"],
5320
5348
  chains: chains ?? ["evm:1", "solana:mainnet"],
5321
5349
  rpcUrls,
5322
- promptPin
5350
+ promptPin,
5351
+ requireAssertionPerSignature
5323
5352
  }).then((sdk) => {
5324
5353
  socialRef.current = sdk;
5325
5354
  sdk.on(