decane-connect-kit 2.26.1 → 2.27.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
@@ -36,6 +36,7 @@ __export(errors_exports, {
36
36
  AttestationError: () => AttestationError,
37
37
  CallSequenceError: () => CallSequenceError,
38
38
  DecaneConnectError: () => DecaneConnectError,
39
+ EnclaveHttpError: () => EnclaveHttpError,
39
40
  ExportNotAuthorizedError: () => ExportNotAuthorizedError,
40
41
  InvalidAccessTokenError: () => InvalidAccessTokenError,
41
42
  InvalidApiKeyError: () => InvalidApiKeyError,
@@ -46,6 +47,7 @@ __export(errors_exports, {
46
47
  OriginNotAllowedError: () => OriginNotAllowedError,
47
48
  PromptPasswordNotConfiguredError: () => PromptPasswordNotConfiguredError,
48
49
  PromptPinNotConfiguredError: () => PromptPinNotConfiguredError,
50
+ ProvisioningRateLimitedError: () => ProvisioningRateLimitedError,
49
51
  ProvisioningTimeoutError: () => ProvisioningTimeoutError,
50
52
  ReconnectRequiredError: () => ReconnectRequiredError,
51
53
  RecoveryFailedError: () => RecoveryFailedError,
@@ -56,11 +58,12 @@ __export(errors_exports, {
56
58
  UnlockRequiredError: () => UnlockRequiredError,
57
59
  UnlockTokenRejectedError: () => UnlockTokenRejectedError,
58
60
  UserCancelledError: () => UserCancelledError,
61
+ WalletEnrolmentError: () => WalletEnrolmentError,
59
62
  WebAuthnNotSupportedError: () => WebAuthnNotSupportedError,
60
63
  WrongPasswordError: () => WrongPasswordError,
61
64
  WrongUnlockPasswordError: () => WrongUnlockPasswordError
62
65
  });
63
- var DecaneConnectError, InvalidApiKeyError, OriginNotAllowedError, SessionExpiredError, UnlockRequiredError, ReconnectRequiredError, UserCancelledError, WebAuthnNotSupportedError, InvalidPinError, CallSequenceError, NetworkError, ShareCorruptedError, ProvisioningTimeoutError, NewDeviceError, StaleDeviceShareError, PromptPinNotConfiguredError, PromptPasswordNotConfiguredError, WrongUnlockPasswordError, InvalidAccessTokenError, UnlockTokenRejectedError, AttestationError, RecoveryFailedError, InvalidRecoveryFileError, WrongPasswordError, RecoveryWalletMismatchError, ExportNotAuthorizedError;
66
+ var DecaneConnectError, InvalidApiKeyError, OriginNotAllowedError, SessionExpiredError, UnlockRequiredError, ReconnectRequiredError, UserCancelledError, WebAuthnNotSupportedError, InvalidPinError, CallSequenceError, NetworkError, ShareCorruptedError, ProvisioningTimeoutError, NewDeviceError, StaleDeviceShareError, PromptPinNotConfiguredError, PromptPasswordNotConfiguredError, WrongUnlockPasswordError, InvalidAccessTokenError, UnlockTokenRejectedError, AttestationError, RecoveryFailedError, InvalidRecoveryFileError, WrongPasswordError, RecoveryWalletMismatchError, ExportNotAuthorizedError, WalletEnrolmentError, ProvisioningRateLimitedError, EnclaveHttpError;
64
67
  var init_errors = __esm({
65
68
  "src/social/errors.ts"() {
66
69
  "use strict";
@@ -209,6 +212,31 @@ var init_errors = __esm({
209
212
  super("EXPORT_NOT_AUTHORIZED", "Export requires a fresh security check", true);
210
213
  }
211
214
  };
215
+ WalletEnrolmentError = class extends DecaneConnectError {
216
+ constructor(cause) {
217
+ super(
218
+ "WALLET_ENROLMENT_FAILED",
219
+ "Your wallet could not be set up: the signing enclave did not accept it. Nothing was created \u2014 sign in again to try again.",
220
+ true
221
+ );
222
+ if (cause !== void 0) this.cause = cause;
223
+ }
224
+ };
225
+ ProvisioningRateLimitedError = class extends DecaneConnectError {
226
+ constructor() {
227
+ super(
228
+ "PROVISIONING_RATE_LIMITED",
229
+ "This wallet has been opened on too many devices or tabs in the last hour. Wait a little and sign in again.",
230
+ true
231
+ );
232
+ }
233
+ };
234
+ EnclaveHttpError = class extends DecaneConnectError {
235
+ constructor(status, message) {
236
+ super("ENCLAVE_HTTP", message, status >= 500);
237
+ this.status = status;
238
+ }
239
+ };
212
240
  }
213
241
  });
214
242
 
@@ -226,6 +254,7 @@ __export(index_exports, {
226
254
  DecaneLogo: () => DecaneLogo,
227
255
  DecaneMark: () => DecaneMark,
228
256
  DecaneWordmark: () => DecaneWordmark,
257
+ EnclaveHttpError: () => EnclaveHttpError,
229
258
  ExportNotAuthorizedError: () => ExportNotAuthorizedError,
230
259
  InvalidAccessTokenError: () => InvalidAccessTokenError,
231
260
  InvalidApiKeyError: () => InvalidApiKeyError,
@@ -237,6 +266,7 @@ __export(index_exports, {
237
266
  OriginNotAllowedError: () => OriginNotAllowedError,
238
267
  PromptPasswordNotConfiguredError: () => PromptPasswordNotConfiguredError,
239
268
  PromptPinNotConfiguredError: () => PromptPinNotConfiguredError,
269
+ ProvisioningRateLimitedError: () => ProvisioningRateLimitedError,
240
270
  ProvisioningTimeoutError: () => ProvisioningTimeoutError,
241
271
  ReconnectRequiredError: () => ReconnectRequiredError,
242
272
  RecoveryFailedError: () => RecoveryFailedError,
@@ -248,6 +278,7 @@ __export(index_exports, {
248
278
  UnlockRequiredError: () => UnlockRequiredError,
249
279
  UnlockTokenRejectedError: () => UnlockTokenRejectedError,
250
280
  UserCancelledError: () => UserCancelledError,
281
+ WalletEnrolmentError: () => WalletEnrolmentError,
251
282
  WalletSelector: () => WalletSelector,
252
283
  WeakPasswordError: () => WeakPasswordError,
253
284
  WebAuthnNotSupportedError: () => WebAuthnNotSupportedError,
@@ -2197,7 +2228,8 @@ var ApiClient = class {
2197
2228
  wrappedShareMeta: btoa(JSON.stringify(share.wrappedShareMeta)),
2198
2229
  evmAddress: share.evmAddress,
2199
2230
  solanaAddress: share.solanaAddress,
2200
- tronAddress: share.tronAddress
2231
+ tronAddress: share.tronAddress,
2232
+ ...share.pendingEnrolment === void 0 ? {} : { pendingEnrolment: share.pendingEnrolment }
2201
2233
  })
2202
2234
  });
2203
2235
  }
@@ -2776,7 +2808,8 @@ var TeeClient = class {
2776
2808
  });
2777
2809
  if (!res.ok) {
2778
2810
  const detail = await res.text().catch(() => "");
2779
- throw new Error(
2811
+ throw new EnclaveHttpError(
2812
+ res.status,
2780
2813
  `auth share enrolment failed (HTTP ${res.status})${detail ? `: ${detail.slice(0, 200)}` : ""}`
2781
2814
  );
2782
2815
  }
@@ -2797,9 +2830,11 @@ var TeeClient = class {
2797
2830
  headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" }
2798
2831
  });
2799
2832
  if (res.status === 409) return null;
2833
+ if (res.status === 429) throw new ProvisioningRateLimitedError();
2800
2834
  if (!res.ok) {
2801
2835
  const detail = await res.text().catch(() => "");
2802
- throw new Error(
2836
+ throw new EnclaveHttpError(
2837
+ res.status,
2803
2838
  `device share provisioning failed (HTTP ${res.status})${detail ? `: ${detail.slice(0, 200)}` : ""}`
2804
2839
  );
2805
2840
  }
@@ -3014,6 +3049,35 @@ var SessionManager = class {
3014
3049
  }
3015
3050
  };
3016
3051
 
3052
+ // src/social/identityTier.ts
3053
+ function resolveProtectionTier(config) {
3054
+ if (config.protection) return config.protection;
3055
+ return config.deferDeviceProtection === true ? "identity" : "device";
3056
+ }
3057
+ function resumeAcrossTabsDefault(config, identityTier) {
3058
+ return config.resumeSessionAcrossTabs ?? identityTier;
3059
+ }
3060
+ var ENROL_BACKOFF_MS = [0, 1e3, 3e3];
3061
+ function enrolmentRetryable(status) {
3062
+ return status !== 429 && status !== 403;
3063
+ }
3064
+ function shouldTryProvisionFirst(identityTier, hasStoredShare) {
3065
+ return identityTier || !hasStoredShare;
3066
+ }
3067
+ function routeAfterProvision(input) {
3068
+ switch (input.outcome) {
3069
+ case "share":
3070
+ return "session";
3071
+ case "rate-limited":
3072
+ return "fail";
3073
+ case "not-enrolled":
3074
+ if (!input.identityTier) return "recover";
3075
+ return input.hasStoredShare ? "migrate" : "new-device";
3076
+ case "error":
3077
+ return input.identityTier ? "fail" : "recover";
3078
+ }
3079
+ }
3080
+
3017
3081
  // src/social/crypto/shares.ts
3018
3082
  var import_secp256k1 = require("@noble/curves/secp256k1");
3019
3083
  var import_shamir_secret_sharing = require("shamir-secret-sharing");
@@ -3924,7 +3988,8 @@ function clearSessionPersist(key) {
3924
3988
  async function createDecaneConnect(config) {
3925
3989
  if (!config.appId) throw new Error("decane-connect-kit: appId is required");
3926
3990
  if (!config.apiKey) throw new Error("decane-connect-kit: apiKey is required");
3927
- useCrossTabSessionStore(config.resumeSessionAcrossTabs === true);
3991
+ const identityTier = resolveProtectionTier(config) === "identity";
3992
+ useCrossTabSessionStore(resumeAcrossTabsDefault(config, identityTier));
3928
3993
  if (!config.chains?.length)
3929
3994
  throw new Error("decane-connect-kit: at least one chain is required");
3930
3995
  if (config.rotateOnNewDeviceRecovery && !config.onRecoveryRotated) {
@@ -3962,7 +4027,6 @@ async function createDecaneConnect(config) {
3962
4027
  let lastUserId = loadLastUser(LAST_USER_KEY) ?? (LEGACY_LAST_USER_KEY !== LAST_USER_KEY ? loadLastUser(LEGACY_LAST_USER_KEY) : null);
3963
4028
  let ethProvider = null;
3964
4029
  const persistSession = config.persistSession !== false;
3965
- const deferDeviceProtection = config.deferDeviceProtection === true;
3966
4030
  const serverAssistedRecovery = true;
3967
4031
  const RECOVERY_OFFER_TIMEOUT_MS = 5 * 6e4;
3968
4032
  const selfHealAttempted = /* @__PURE__ */ new Set();
@@ -3983,22 +4047,27 @@ async function createDecaneConnect(config) {
3983
4047
  zeroize(share);
3984
4048
  }
3985
4049
  }
3986
- async function enrolInBackground(jwt, share) {
4050
+ async function enrolBlocking(jwt, share, budgetMs) {
4051
+ const startedAt = Date.now();
3987
4052
  try {
3988
- for (let attempt = 1; attempt <= 2; attempt++) {
4053
+ let lastError;
4054
+ for (const wait of ENROL_BACKOFF_MS) {
4055
+ if (wait) await new Promise((r) => setTimeout(r, wait));
3989
4056
  try {
3990
- await new TeeClient(teeUrl).enrolAuthShare(jwt, share);
4057
+ await withDeadline(
4058
+ new TeeClient(teeUrl).enrolAuthShare(jwt, share),
4059
+ budgetMs - (Date.now() - startedAt),
4060
+ "enrolling your wallet"
4061
+ );
3991
4062
  return;
3992
4063
  } catch (e) {
3993
- if (attempt === 2) {
3994
- events.emit("enrolment-failed");
3995
- console.warn(
3996
- "decane-connect-kit: could not enrol this wallet for server-assisted recovery. The wallet is fine and fully usable; it will be re-enrolled automatically on the next unlock. Until then a brand-new device would need a passkey backup or a recovery file.",
3997
- e
3998
- );
3999
- }
4064
+ if (e instanceof AttestationError || e instanceof ProvisioningTimeoutError) throw e;
4065
+ lastError = e;
4066
+ if (!enrolmentRetryable(e.status)) break;
4000
4067
  }
4001
4068
  }
4069
+ events.emit("enrolment-failed");
4070
+ throw new WalletEnrolmentError(lastError);
4002
4071
  } finally {
4003
4072
  zeroize(share);
4004
4073
  }
@@ -4094,6 +4163,7 @@ async function createDecaneConnect(config) {
4094
4163
  else scheduleAccessTokenExpiry(saved.jwt, saved.expiresAt);
4095
4164
  auth = { jwt: saved.jwt, userId: saved.userId, addresses: saved.addresses };
4096
4165
  persisted = { userId: saved.userId, addresses: saved.addresses };
4166
+ if (saved.handle) armTokenRefresh(saved.jwt);
4097
4167
  } catch {
4098
4168
  dropLiveSession();
4099
4169
  }
@@ -4103,6 +4173,13 @@ async function createDecaneConnect(config) {
4103
4173
  if (LEGACY_SESSION_KEY !== SESSION_KEY) clearSessionPersist(LEGACY_SESSION_KEY);
4104
4174
  }
4105
4175
  events.on("session-expired", () => dropLiveSession());
4176
+ function armTokenRefresh(jwt) {
4177
+ const expiresAt = decodeAccessToken(jwt)?.expiresAt;
4178
+ if (expiresAt) scheduleTokenRefresh(jwt, expiresAt);
4179
+ }
4180
+ events.on("connected", () => {
4181
+ if (auth) armTokenRefresh(auth.jwt);
4182
+ });
4106
4183
  async function passwordSetup() {
4107
4184
  if (!config.promptUnlockPassword) throw new PromptPasswordNotConfiguredError();
4108
4185
  const password = await config.promptUnlockPassword({ kind: "set" });
@@ -4327,7 +4404,7 @@ async function createDecaneConnect(config) {
4327
4404
  let wrappingKey = null;
4328
4405
  let meta = {};
4329
4406
  let unlockTokenKey = null;
4330
- if (deferDeviceProtection) {
4407
+ if (identityTier) {
4331
4408
  } else if (isPasskeySupported()) {
4332
4409
  const prfSalt = generateRandomBytes(32);
4333
4410
  try {
@@ -4399,14 +4476,29 @@ async function createDecaneConnect(config) {
4399
4476
  iv: serverIv,
4400
4477
  wrappedShareMeta: {
4401
4478
  // The server records this on the wallet_created event and nothing else.
4402
- unlockMethod: deferDeviceProtection ? "deferred" : unlockMethod
4479
+ unlockMethod: identityTier ? "identity" : unlockMethod
4403
4480
  // credential IDs, prfSalt, pinSalt stay on-device — never sent to server
4404
4481
  },
4405
4482
  evmAddress,
4406
4483
  solanaAddress,
4407
- tronAddress
4484
+ tronAddress,
4485
+ // Provisional until the enclave has sealed the auth share, just below. A
4486
+ // backend that knows the flag keeps this row invisible to hasShare until
4487
+ // then, so a creation that fails past this point leaves no wallet behind
4488
+ // and the next sign-in simply creates again — with a new key, which is
4489
+ // why the backend also replaces the recorded addresses outright.
4490
+ pendingEnrolment: true
4408
4491
  }), remainingBudget(), "storing your wallet");
4409
4492
  zeroize(shareSet.server);
4493
+ try {
4494
+ await enrolBlocking(jwt, new Uint8Array(shareSet.recovery), remainingBudget());
4495
+ } catch (e) {
4496
+ zeroize(shareSet.device);
4497
+ zeroize(shareSet.recovery);
4498
+ if (wrappingKey) await storage2.deleteShare(userId).catch(() => {
4499
+ });
4500
+ throw e;
4501
+ }
4410
4502
  const addresses = { evm: evmAddress, solana: solanaAddress, tron: tronAddress };
4411
4503
  if (unlockMethod === "passkey" && meta.credentialId && meta.prfSalt && wrappedDeviceShare && iv) {
4412
4504
  try {
@@ -4435,10 +4527,7 @@ async function createDecaneConnect(config) {
4435
4527
  addresses.evm = evmAddress = confirmed.evm;
4436
4528
  addresses.solana = solanaAddress = confirmed.solana;
4437
4529
  addresses.tron = tronAddress = confirmed.tron ?? evmToTronAddress2(confirmed.evm);
4438
- if (serverAssistedRecovery) {
4439
- void enrolInBackground(jwt, new Uint8Array(shareSet.recovery));
4440
- }
4441
- if (config.offerRecoveryAtSignup === true && config.onRecoveryShareOffer) {
4530
+ if (!identityTier && config.offerRecoveryAtSignup === true && config.onRecoveryShareOffer) {
4442
4531
  void offerRecoveryShare(
4443
4532
  jwt,
4444
4533
  new Uint8Array(shareSet.recovery),
@@ -4485,31 +4574,44 @@ async function createDecaneConnect(config) {
4485
4574
  const stored = await storage2.getShare(userId);
4486
4575
  let deviceShare;
4487
4576
  let joinedViaPasskeySync = false;
4488
- if (!stored) {
4489
- if (serverAssistedRecovery) {
4577
+ let migrating = false;
4578
+ if (serverAssistedRecovery && shouldTryProvisionFirst(identityTier, !!stored)) {
4579
+ let provisioned = null;
4580
+ let failure;
4581
+ let outcome;
4582
+ try {
4583
+ provisioned = await new TeeClient(teeUrl).provisionDeviceShare(jwt);
4584
+ outcome = provisioned ? "share" : "not-enrolled";
4585
+ } catch (e) {
4586
+ failure = e;
4587
+ outcome = e instanceof ProvisioningRateLimitedError ? "rate-limited" : "error";
4588
+ }
4589
+ const route = routeAfterProvision({ identityTier, hasStoredShare: !!stored, outcome });
4590
+ if (route === "session" && provisioned) {
4591
+ if (!identityTier) await persistProvisionedShare(userId, jwt, provisioned);
4592
+ let addrs;
4490
4593
  try {
4491
- const tee = new TeeClient(teeUrl);
4492
- const provisioned = await tee.provisionDeviceShare(jwt);
4493
- if (provisioned) {
4494
- if (!deferDeviceProtection) await persistProvisionedShare(userId, jwt, provisioned);
4495
- let addrs;
4496
- try {
4497
- addrs = await session.start(provisioned, jwt, recordedAddresses);
4498
- } finally {
4499
- zeroize(provisioned);
4500
- }
4501
- return addrs;
4502
- }
4503
- console.warn(
4504
- "decane-connect-kit: this wallet is not enrolled for server-assisted provisioning (/signer/provision returned 409 not_enrolled), so a device share cannot be issued from the JWT alone. Falling back to passkey sync, then the recovery file."
4505
- );
4506
- } catch (e) {
4507
- console.warn(
4508
- "decane-connect-kit: server-assisted provisioning unavailable; falling back to passkey/recovery-file recovery.",
4509
- e
4510
- );
4594
+ addrs = await session.start(provisioned, jwt, recordedAddresses);
4595
+ } finally {
4596
+ zeroize(provisioned);
4511
4597
  }
4598
+ return addrs;
4599
+ }
4600
+ if (route === "fail") throw failure;
4601
+ if (route === "new-device") throw new NewDeviceError();
4602
+ if (route === "migrate") migrating = true;
4603
+ if (route === "recover" && outcome === "not-enrolled") {
4604
+ console.warn(
4605
+ "decane-connect-kit: this wallet is not enrolled for server-assisted provisioning (/signer/provision returned 409 not_enrolled), so a device share cannot be issued from the JWT alone. Falling back to passkey sync, then the recovery file."
4606
+ );
4607
+ } else if (route === "recover") {
4608
+ console.warn(
4609
+ "decane-connect-kit: server-assisted provisioning unavailable; falling back to passkey/recovery-file recovery.",
4610
+ failure
4611
+ );
4512
4612
  }
4613
+ }
4614
+ if (!stored) {
4513
4615
  if (!config.onRecoveryRotated) {
4514
4616
  throw new CallSequenceError(
4515
4617
  "Recovery rotates the share set and issues a new recovery file, but no onRecoveryRotated callback is configured to save it. Configure onRecoveryRotated before allowing recovery."
@@ -4535,7 +4637,7 @@ async function createDecaneConnect(config) {
4535
4637
  });
4536
4638
  deviceShare = recovered.deviceShare;
4537
4639
  joinedViaPasskeySync = recovered.flow === "passkey-sync";
4538
- if (!deferDeviceProtection && !await storage2.getShare(userId) && isPasskeySupported()) {
4640
+ if (!identityTier && !await storage2.getShare(userId) && isPasskeySupported()) {
4539
4641
  await registerPasskeyAndBackup(userId, jwt, deviceShare);
4540
4642
  }
4541
4643
  } else {
@@ -4549,6 +4651,14 @@ async function createDecaneConnect(config) {
4549
4651
  } catch {
4550
4652
  throw stored.unlockMethod === "pin" ? new InvalidPinError() : new ShareCorruptedError();
4551
4653
  }
4654
+ if (migrating) {
4655
+ try {
4656
+ await enrolBlocking(jwt, new Uint8Array(deviceShare), PROVISIONING_BUDGET_MS);
4657
+ } catch (e) {
4658
+ zeroize(deviceShare);
4659
+ throw e;
4660
+ }
4661
+ }
4552
4662
  }
4553
4663
  let addresses;
4554
4664
  try {
@@ -4563,7 +4673,11 @@ async function createDecaneConnect(config) {
4563
4673
  }
4564
4674
  throw e;
4565
4675
  }
4566
- void selfHealEnrolment(userId, jwt, new Uint8Array(deviceShare));
4676
+ if (migrating) {
4677
+ events.emit("migrated-to-identity");
4678
+ } else if (!identityTier) {
4679
+ void selfHealEnrolment(userId, jwt, new Uint8Array(deviceShare));
4680
+ }
4567
4681
  zeroize(deviceShare);
4568
4682
  if (recordedAddresses && recordedAddresses.evm.toLowerCase() !== addresses.evm.toLowerCase()) {
4569
4683
  console.warn(
@@ -4746,7 +4860,7 @@ async function createDecaneConnect(config) {
4746
4860
  if (auth && session.isActive()) {
4747
4861
  return { addresses: auth.addresses, isNewUser: false };
4748
4862
  }
4749
- const candidate = await passkeyLoginCandidate();
4863
+ const candidate = identityTier ? null : await passkeyLoginCandidate();
4750
4864
  if (candidate) {
4751
4865
  try {
4752
4866
  return await passkeyLogin(candidate);
@@ -4856,9 +4970,15 @@ async function createDecaneConnect(config) {
4856
4970
  return handleAuthResult(result);
4857
4971
  },
4858
4972
  async canSignInWithPasskey() {
4973
+ if (identityTier) return false;
4859
4974
  return canOfferPasskey();
4860
4975
  },
4861
4976
  async signInWithPasskey() {
4977
+ if (identityTier) {
4978
+ throw new CallSequenceError(
4979
+ "signInWithPasskey() is not available in the identity tier (protection: 'identity'); use connect()."
4980
+ );
4981
+ }
4862
4982
  const candidate = await passkeyDeviceCandidate();
4863
4983
  if (candidate) return passkeyLogin(candidate);
4864
4984
  const known = persisted?.userId ?? lastUserId;
@@ -4994,6 +5114,11 @@ async function createDecaneConnect(config) {
4994
5114
  return stored?.unlockMethod === "passkey";
4995
5115
  },
4996
5116
  async addPasskey() {
5117
+ if (identityTier) {
5118
+ throw new CallSequenceError(
5119
+ "addPasskey() is not available in the identity tier (protection: 'identity'): nothing is stored on the device for a passkey to protect."
5120
+ );
5121
+ }
4997
5122
  if (!auth) throw new ReconnectRequiredError();
4998
5123
  if (!isPasskeySupported()) throw new WebAuthnNotSupportedError();
4999
5124
  const stored = await storage2.getShare(auth.userId);
@@ -5032,6 +5157,7 @@ async function createDecaneConnect(config) {
5032
5157
  return await storage2.getShare(auth.userId) !== null;
5033
5158
  },
5034
5159
  async protectDevice() {
5160
+ if (identityTier) return false;
5035
5161
  if (!auth) throw new ReconnectRequiredError();
5036
5162
  if (await storage2.getShare(auth.userId)) return true;
5037
5163
  if (!serverAssistedRecovery) throw new NewDeviceError();
@@ -5047,7 +5173,7 @@ async function createDecaneConnect(config) {
5047
5173
  },
5048
5174
  async unlock() {
5049
5175
  if (!auth) {
5050
- const candidate = await passkeyLoginCandidate();
5176
+ const candidate = identityTier ? null : await passkeyLoginCandidate();
5051
5177
  if (candidate) {
5052
5178
  await passkeyLogin(candidate);
5053
5179
  return;
@@ -5216,6 +5342,8 @@ async function createDecaneConnect(config) {
5216
5342
  },
5217
5343
  async disconnect() {
5218
5344
  session.end();
5345
+ if (tokenRefresh) clearTimeout(tokenRefresh);
5346
+ tokenRefresh = null;
5219
5347
  if (auth?.jwt) {
5220
5348
  await api.revoke(auth.jwt).catch(() => {
5221
5349
  });
@@ -7666,6 +7794,7 @@ function DecaneKit({ config, children }) {
7666
7794
  chains,
7667
7795
  rpcUrls,
7668
7796
  requireAssertionPerSignature,
7797
+ protection,
7669
7798
  deferDeviceProtection,
7670
7799
  resumeSessionAcrossTabs,
7671
7800
  // Recovery callbacks. Forwarding these is what makes recovery
@@ -7688,6 +7817,7 @@ function DecaneKit({ config, children }) {
7688
7817
  promptPin,
7689
7818
  promptUnlockPassword,
7690
7819
  requireAssertionPerSignature,
7820
+ protection,
7691
7821
  deferDeviceProtection,
7692
7822
  resumeSessionAcrossTabs,
7693
7823
  offerRecoveryAtSignup,
@@ -8897,6 +9027,7 @@ function useSignTypedData() {
8897
9027
  DecaneLogo,
8898
9028
  DecaneMark,
8899
9029
  DecaneWordmark,
9030
+ EnclaveHttpError,
8900
9031
  ExportNotAuthorizedError,
8901
9032
  InvalidAccessTokenError,
8902
9033
  InvalidApiKeyError,
@@ -8908,6 +9039,7 @@ function useSignTypedData() {
8908
9039
  OriginNotAllowedError,
8909
9040
  PromptPasswordNotConfiguredError,
8910
9041
  PromptPinNotConfiguredError,
9042
+ ProvisioningRateLimitedError,
8911
9043
  ProvisioningTimeoutError,
8912
9044
  ReconnectRequiredError,
8913
9045
  RecoveryFailedError,
@@ -8919,6 +9051,7 @@ function useSignTypedData() {
8919
9051
  UnlockRequiredError,
8920
9052
  UnlockTokenRejectedError,
8921
9053
  UserCancelledError,
9054
+ WalletEnrolmentError,
8922
9055
  WalletSelector,
8923
9056
  WeakPasswordError,
8924
9057
  WebAuthnNotSupportedError,