decane-connect-kit 2.1.0 → 2.2.1

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
@@ -2267,6 +2267,12 @@ var TeeClient = class {
2267
2267
  this.teeUrl = teeUrl;
2268
2268
  this.handle = null;
2269
2269
  this.attested = false;
2270
+ /** Mirrors what the signer confirmed for this session, not what we asked for. */
2271
+ this.assertionPerSignature = false;
2272
+ }
2273
+ /** Whether this session actually got per-signature assertions. */
2274
+ requiresAssertionPerSignature() {
2275
+ return this.assertionPerSignature;
2270
2276
  }
2271
2277
  async ensureAttested() {
2272
2278
  if (this.attested) return;
@@ -2282,16 +2288,20 @@ var TeeClient = class {
2282
2288
  * couldn't derive them (older signer build, or a transient share-fetch
2283
2289
  * failure); callers fall back to the backend's addresses in that case.
2284
2290
  */
2285
- async openSession(deviceShare, jwt) {
2291
+ async openSession(deviceShare, jwt, requireAssertionPerSignature = false) {
2286
2292
  await this.ensureAttested();
2287
2293
  const res = await fetch(`${this.teeUrl}/signer/session`, {
2288
2294
  method: "POST",
2289
2295
  headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" },
2290
- body: JSON.stringify({ deviceShare: bytesToBase64(deviceShare) })
2296
+ body: JSON.stringify({
2297
+ deviceShare: bytesToBase64(deviceShare),
2298
+ requireAssertionPerSignature
2299
+ })
2291
2300
  });
2292
2301
  if (!res.ok) throw new Error("session open failed");
2293
2302
  const body = await res.json();
2294
2303
  this.handle = body.handle;
2304
+ this.assertionPerSignature = body.requireAssertionPerSignature === true;
2295
2305
  return body.addresses ?? null;
2296
2306
  }
2297
2307
  /**
@@ -2382,14 +2392,33 @@ var TeeClient = class {
2382
2392
  hasSession() {
2383
2393
  return this.handle !== null;
2384
2394
  }
2385
- async sign(req) {
2395
+ /**
2396
+ * `jwt` is only needed when this session requires per-signature assertions —
2397
+ * 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.
2400
+ */
2401
+ async sign(req, jwt) {
2386
2402
  if (!this.handle) throw new Error("no session");
2403
+ let gate = null;
2404
+ if (this.assertionPerSignature) {
2405
+ if (!jwt) throw new Error("sign requires a jwt when per-signature assertion is enabled");
2406
+ gate = await this.completeAssertion(jwt);
2407
+ }
2387
2408
  const res = await fetch(`${this.teeUrl}/signer/sign`, {
2388
2409
  method: "POST",
2389
2410
  headers: { "Content-Type": "application/json" },
2390
- body: JSON.stringify({ handle: this.handle, ...req })
2411
+ body: JSON.stringify({
2412
+ handle: this.handle,
2413
+ ...req,
2414
+ ...gate ? { ...gate, origin: window.location.origin } : {}
2415
+ })
2391
2416
  });
2392
2417
  if (res.status === 401) {
2418
+ const body = await res.clone().json().catch(() => ({}));
2419
+ if (body?.error === "assertion_required") {
2420
+ throw new ExportNotAuthorizedError();
2421
+ }
2393
2422
  this.handle = null;
2394
2423
  throw new SessionExpiredError();
2395
2424
  }
@@ -2412,10 +2441,11 @@ var TeeClient = class {
2412
2441
  // src/social/session.ts
2413
2442
  init_errors();
2414
2443
  var SessionManager = class {
2415
- constructor(events, durationMs, teeUrl) {
2444
+ constructor(events, durationMs, teeUrl, requireAssertionPerSignature = false) {
2416
2445
  this.events = events;
2417
2446
  this.durationMs = durationMs;
2418
2447
  this.teeUrl = teeUrl;
2448
+ this.requireAssertionPerSignature = requireAssertionPerSignature;
2419
2449
  this.tee = null;
2420
2450
  this.evmAddress = null;
2421
2451
  this.solanaAddress = null;
@@ -2435,7 +2465,18 @@ var SessionManager = class {
2435
2465
  */
2436
2466
  async start(deviceShare, jwt, fallback) {
2437
2467
  this.tee = new TeeClient(this.teeUrl);
2438
- const verified = await this.tee.openSession(deviceShare, jwt);
2468
+ const verified = await this.tee.openSession(
2469
+ deviceShare,
2470
+ jwt,
2471
+ this.requireAssertionPerSignature
2472
+ );
2473
+ if (this.requireAssertionPerSignature && !this.tee.requiresAssertionPerSignature()) {
2474
+ void this.tee.closeSession();
2475
+ this.tee = null;
2476
+ throw new Error(
2477
+ "requireAssertionPerSignature was requested but this signer does not support it \u2014 refusing to open a session with weaker authority than configured."
2478
+ );
2479
+ }
2439
2480
  const addresses = verified ?? fallback;
2440
2481
  if (!addresses) {
2441
2482
  this.tee = null;
@@ -2664,15 +2705,15 @@ async function verifyEmailCode(api, email, code) {
2664
2705
 
2665
2706
  // src/social/signers/evm.ts
2666
2707
  var import_ethers = require("ethers");
2667
- async function signEvmTransaction(tee, chain, tx) {
2708
+ async function signEvmTransaction(tee, chain, tx, jwt) {
2668
2709
  const resolved = await (0, import_ethers.resolveProperties)(tx);
2669
2710
  const unsigned = import_ethers.Transaction.from(resolved).unsignedSerialized;
2670
2711
  const payload = unsigned.startsWith("0x") ? unsigned.slice(2) : unsigned;
2671
2712
  const txHash = (0, import_ethers.keccak256)("0x" + payload);
2672
- return tee.sign({ chain, kind: "transaction", payload, txHash });
2713
+ return tee.sign({ chain, kind: "transaction", payload, txHash }, jwt);
2673
2714
  }
2674
- async function signEvmMessage(tee, chain, message) {
2675
- return tee.sign({ chain, kind: "message", payload: message });
2715
+ async function signEvmMessage(tee, chain, message, jwt) {
2716
+ return tee.sign({ chain, kind: "message", payload: message }, jwt);
2676
2717
  }
2677
2718
 
2678
2719
  // src/social/signers/solana.ts
@@ -2685,13 +2726,13 @@ function b64(bytes) {
2685
2726
  function unb64(s) {
2686
2727
  return Uint8Array.from(atob(s), (c) => c.charCodeAt(0));
2687
2728
  }
2688
- async function signSolanaTransaction(tee, chain, tx) {
2729
+ async function signSolanaTransaction(tee, chain, tx, jwt) {
2689
2730
  const bytes = tx instanceof import_web3.VersionedTransaction ? tx.serialize() : tx.serialize({ requireAllSignatures: false });
2690
- const signed = await tee.sign({ chain, kind: "transaction", payload: b64(bytes) });
2731
+ const signed = await tee.sign({ chain, kind: "transaction", payload: b64(bytes) }, jwt);
2691
2732
  return unb64(signed);
2692
2733
  }
2693
- async function signSolanaMessage(tee, chain, message) {
2694
- const sig = await tee.sign({ chain, kind: "message", payload: b64(message) });
2734
+ async function signSolanaMessage(tee, chain, message, jwt) {
2735
+ const sig = await tee.sign({ chain, kind: "message", payload: b64(message) }, jwt);
2695
2736
  return unb64(sig);
2696
2737
  }
2697
2738
 
@@ -2964,7 +3005,12 @@ async function createDecaneConnect(config) {
2964
3005
  const events = new EventEmitter();
2965
3006
  const api = new ApiClient(baseUrl, config.apiKey, config.appId);
2966
3007
  const storage = new Storage();
2967
- const session = new SessionManager(events, durationMs, teeUrl);
3008
+ const session = new SessionManager(
3009
+ events,
3010
+ durationMs,
3011
+ teeUrl,
3012
+ config.requireAssertionPerSignature === true
3013
+ );
2968
3014
  let auth = null;
2969
3015
  let persisted = loadPersisted(PERSIST_KEY);
2970
3016
  async function pinSetup() {
@@ -3363,11 +3409,11 @@ async function createDecaneConnect(config) {
3363
3409
  async signMessage(req) {
3364
3410
  const tee = session.requireActive();
3365
3411
  if (req.chain.startsWith("evm:")) {
3366
- return signEvmMessage(tee, req.chain, req.message);
3412
+ return signEvmMessage(tee, req.chain, req.message, auth?.jwt);
3367
3413
  }
3368
3414
  if (req.chain.startsWith("solana:")) {
3369
3415
  const msg = typeof req.message === "string" ? new TextEncoder().encode(req.message) : req.message;
3370
- const sig = await signSolanaMessage(tee, req.chain, msg);
3416
+ const sig = await signSolanaMessage(tee, req.chain, msg, auth?.jwt);
3371
3417
  return bufferToB64u(sig);
3372
3418
  }
3373
3419
  throw new CallSequenceError(`Unsupported chain: ${req.chain}`);
@@ -3415,7 +3461,7 @@ async function createDecaneConnect(config) {
3415
3461
  maxPriorityFeePerGas: feeData.maxPriorityFeePerGas ?? 0n
3416
3462
  } : { gasPrice: req.gasPrice ?? feeData.gasPrice ?? void 0 }
3417
3463
  };
3418
- const signedTx = await signEvmTransaction(tee, req.chain, tx);
3464
+ const signedTx = await signEvmTransaction(tee, req.chain, tx, auth?.jwt);
3419
3465
  const txResponse = await provider.broadcastTransaction(signedTx);
3420
3466
  return txResponse.hash;
3421
3467
  },
@@ -3428,7 +3474,7 @@ async function createDecaneConnect(config) {
3428
3474
  tx = import_web32.Transaction.from(serializedTx);
3429
3475
  }
3430
3476
  const solChain = config.chains.find((c) => c.startsWith("solana:")) ?? "solana:mainnet";
3431
- return signSolanaTransaction(tee, solChain, tx);
3477
+ return signSolanaTransaction(tee, solChain, tx, auth?.jwt);
3432
3478
  },
3433
3479
  async disconnect() {
3434
3480
  session.end();
@@ -5265,7 +5311,15 @@ function DecaneKit({ config, children }) {
5265
5311
  (0, import_react4.useEffect)(() => {
5266
5312
  if (!hasSocial(mode) || !config?.social || socialInitialized.current) return;
5267
5313
  socialInitialized.current = true;
5268
- const { apiKey, authMethods, promptPin, apiBaseUrl, chains, rpcUrls } = config.social;
5314
+ const {
5315
+ apiKey,
5316
+ authMethods,
5317
+ promptPin,
5318
+ apiBaseUrl,
5319
+ chains,
5320
+ rpcUrls,
5321
+ requireAssertionPerSignature
5322
+ } = config.social;
5269
5323
  createDecaneConnect({
5270
5324
  appId: config.appId,
5271
5325
  apiKey,
@@ -5273,7 +5327,8 @@ function DecaneKit({ config, children }) {
5273
5327
  authMethods: authMethods ?? ["google", "email"],
5274
5328
  chains: chains ?? ["evm:1", "solana:mainnet"],
5275
5329
  rpcUrls,
5276
- promptPin
5330
+ promptPin,
5331
+ requireAssertionPerSignature
5277
5332
  }).then((sdk) => {
5278
5333
  socialRef.current = sdk;
5279
5334
  sdk.on(