decane-connect-kit 2.22.0 → 2.24.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
@@ -37,6 +37,7 @@ __export(errors_exports, {
37
37
  CallSequenceError: () => CallSequenceError,
38
38
  DecaneConnectError: () => DecaneConnectError,
39
39
  ExportNotAuthorizedError: () => ExportNotAuthorizedError,
40
+ InvalidAccessTokenError: () => InvalidAccessTokenError,
40
41
  InvalidApiKeyError: () => InvalidApiKeyError,
41
42
  InvalidPinError: () => InvalidPinError,
42
43
  InvalidRecoveryFileError: () => InvalidRecoveryFileError,
@@ -59,7 +60,7 @@ __export(errors_exports, {
59
60
  WrongPasswordError: () => WrongPasswordError,
60
61
  WrongUnlockPasswordError: () => WrongUnlockPasswordError
61
62
  });
62
- var DecaneConnectError, InvalidApiKeyError, OriginNotAllowedError, SessionExpiredError, UnlockRequiredError, ReconnectRequiredError, UserCancelledError, WebAuthnNotSupportedError, InvalidPinError, CallSequenceError, NetworkError, ShareCorruptedError, ProvisioningTimeoutError, NewDeviceError, StaleDeviceShareError, PromptPinNotConfiguredError, PromptPasswordNotConfiguredError, WrongUnlockPasswordError, UnlockTokenRejectedError, AttestationError, RecoveryFailedError, InvalidRecoveryFileError, WrongPasswordError, RecoveryWalletMismatchError, ExportNotAuthorizedError;
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;
63
64
  var init_errors = __esm({
64
65
  "src/social/errors.ts"() {
65
66
  "use strict";
@@ -164,6 +165,11 @@ var init_errors = __esm({
164
165
  super("WRONG_UNLOCK_PASSWORD", "That password did not unlock this device");
165
166
  }
166
167
  };
168
+ InvalidAccessTokenError = class extends DecaneConnectError {
169
+ constructor(reason) {
170
+ super("INVALID_ACCESS_TOKEN", `That access token cannot open a session: ${reason}`);
171
+ }
172
+ };
167
173
  UnlockTokenRejectedError = class extends DecaneConnectError {
168
174
  constructor() {
169
175
  super("UNLOCK_TOKEN_REJECTED", "This device's unlock token is no longer valid \u2014 sign in normally to re-enable password unlock");
@@ -221,6 +227,7 @@ __export(index_exports, {
221
227
  DecaneMark: () => DecaneMark,
222
228
  DecaneWordmark: () => DecaneWordmark,
223
229
  ExportNotAuthorizedError: () => ExportNotAuthorizedError,
230
+ InvalidAccessTokenError: () => InvalidAccessTokenError,
224
231
  InvalidApiKeyError: () => InvalidApiKeyError,
225
232
  InvalidPinError: () => InvalidPinError,
226
233
  InvalidRecoveryFileError: () => InvalidRecoveryFileError,
@@ -2355,6 +2362,20 @@ function b64ToBytes(b644) {
2355
2362
  return Uint8Array.from(atob(b644), (c) => c.charCodeAt(0));
2356
2363
  }
2357
2364
 
2365
+ // src/social/accessToken.ts
2366
+ function decodeAccessToken(token) {
2367
+ try {
2368
+ const payload = token.split(".")[1];
2369
+ if (!payload) return null;
2370
+ const json = JSON.parse(atob(payload.replace(/-/g, "+").replace(/_/g, "/")));
2371
+ if (typeof json.uid !== "string" || json.uid === "") return null;
2372
+ if (typeof json.exp !== "number" || !Number.isFinite(json.exp)) return null;
2373
+ return { userId: json.uid, expiresAt: json.exp * 1e3 };
2374
+ } catch {
2375
+ return null;
2376
+ }
2377
+ }
2378
+
2358
2379
  // src/social/storage.ts
2359
2380
  var import_idb = require("idb");
2360
2381
  var DB_NAME = "decane-connect-kit";
@@ -3749,8 +3770,10 @@ async function createDecaneConnect(config) {
3749
3770
  const durationMs = (config.sessionDurationMinutes ?? DEFAULT_SESSION_MINUTES) * 6e4;
3750
3771
  const rpId = config.rpId ?? (typeof window !== "undefined" ? window.location.hostname : "localhost");
3751
3772
  const rpName = config.rpName ?? "Decane Connect";
3752
- const PERSIST_KEY = makePersistKey(config.apiKey);
3753
- const LAST_USER_KEY = makeLastUserKey(config.apiKey);
3773
+ const PERSIST_KEY = makePersistKey(config.appId);
3774
+ const LAST_USER_KEY = makeLastUserKey(config.appId);
3775
+ const LEGACY_PERSIST_KEY = makePersistKey(config.apiKey);
3776
+ const LEGACY_LAST_USER_KEY = makeLastUserKey(config.apiKey);
3754
3777
  const teeUrl = config.teeUrl ?? DEFAULT_TEE_URL;
3755
3778
  const events = new EventEmitter();
3756
3779
  const api = new ApiClient(baseUrl, config.apiKey, config.appId);
@@ -3763,9 +3786,17 @@ async function createDecaneConnect(config) {
3763
3786
  );
3764
3787
  let auth = null;
3765
3788
  let persisted = loadPersisted(PERSIST_KEY);
3766
- let lastUserId = loadLastUser(LAST_USER_KEY);
3789
+ if (!persisted && LEGACY_PERSIST_KEY !== PERSIST_KEY) {
3790
+ persisted = loadPersisted(LEGACY_PERSIST_KEY);
3791
+ if (persisted) {
3792
+ savePersisted(PERSIST_KEY, persisted);
3793
+ clearPersisted(LEGACY_PERSIST_KEY);
3794
+ }
3795
+ }
3796
+ let lastUserId = loadLastUser(LAST_USER_KEY) ?? (LEGACY_LAST_USER_KEY !== LAST_USER_KEY ? loadLastUser(LEGACY_LAST_USER_KEY) : null);
3767
3797
  let ethProvider = null;
3768
3798
  const persistSession = config.persistSession !== false;
3799
+ const deferDeviceProtection = config.deferDeviceProtection === true;
3769
3800
  const serverAssistedRecovery = true;
3770
3801
  const RECOVERY_OFFER_TIMEOUT_MS = 5 * 6e4;
3771
3802
  const selfHealAttempted = /* @__PURE__ */ new Set();
@@ -3825,12 +3856,13 @@ async function createDecaneConnect(config) {
3825
3856
  })
3826
3857
  ]).finally(() => clearTimeout(timer));
3827
3858
  }
3828
- const SESSION_KEY = sessionPersistKey(config.apiKey);
3859
+ const SESSION_KEY = sessionPersistKey(config.appId);
3860
+ const LEGACY_SESSION_KEY = sessionPersistKey(config.apiKey);
3829
3861
  function persistLiveSession() {
3830
- if (!persistSession) return;
3862
+ if (!persistSession || !auth) return;
3831
3863
  const handle = session.getHandle();
3832
- const expiresAt = session.expiresAtTimestamp();
3833
- if (!handle || !auth || !expiresAt) return;
3864
+ const expiresAt = handle ? session.expiresAtTimestamp() : decodeAccessToken(auth.jwt)?.expiresAt ?? null;
3865
+ if (!expiresAt) return;
3834
3866
  saveSession(SESSION_KEY, {
3835
3867
  handle,
3836
3868
  jwt: auth.jwt,
@@ -3842,11 +3874,23 @@ async function createDecaneConnect(config) {
3842
3874
  function dropLiveSession() {
3843
3875
  clearSessionPersist(SESSION_KEY);
3844
3876
  }
3877
+ let accessTokenExpiry = null;
3878
+ function scheduleAccessTokenExpiry(jwt, expiresAt) {
3879
+ if (accessTokenExpiry) clearTimeout(accessTokenExpiry);
3880
+ accessTokenExpiry = setTimeout(() => {
3881
+ accessTokenExpiry = null;
3882
+ if (auth?.jwt !== jwt || session.isActive()) return;
3883
+ auth = null;
3884
+ dropLiveSession();
3885
+ events.emit("session-expired");
3886
+ }, Math.max(0, expiresAt - Date.now()));
3887
+ }
3845
3888
  if (persistSession) {
3846
- const saved = loadSession(SESSION_KEY);
3847
- if (saved?.handle && saved.expiresAt > Date.now() + 5e3) {
3889
+ const saved = loadSession(SESSION_KEY) ?? (LEGACY_SESSION_KEY !== SESSION_KEY ? loadSession(LEGACY_SESSION_KEY) : null);
3890
+ if (saved && saved.expiresAt > Date.now() + 5e3) {
3848
3891
  try {
3849
- session.resume(saved.handle, saved.addresses, saved.expiresAt);
3892
+ if (saved.handle) session.resume(saved.handle, saved.addresses, saved.expiresAt);
3893
+ else scheduleAccessTokenExpiry(saved.jwt, saved.expiresAt);
3850
3894
  auth = { jwt: saved.jwt, userId: saved.userId, addresses: saved.addresses };
3851
3895
  persisted = { userId: saved.userId, addresses: saved.addresses };
3852
3896
  } catch {
@@ -3855,6 +3899,7 @@ async function createDecaneConnect(config) {
3855
3899
  } else if (saved) {
3856
3900
  dropLiveSession();
3857
3901
  }
3902
+ if (LEGACY_SESSION_KEY !== SESSION_KEY) clearSessionPersist(LEGACY_SESSION_KEY);
3858
3903
  }
3859
3904
  events.on("session-expired", () => dropLiveSession());
3860
3905
  async function passwordSetup() {
@@ -4077,11 +4122,12 @@ async function createDecaneConnect(config) {
4077
4122
  }
4078
4123
  async function firstTimeSetup(userId, jwt) {
4079
4124
  prewarmAttestation(teeUrl);
4080
- let unlockMethod;
4081
- let wrappingKey;
4125
+ let unlockMethod = "password";
4126
+ let wrappingKey = null;
4082
4127
  let meta = {};
4083
4128
  let unlockTokenKey = null;
4084
- if (isPasskeySupported()) {
4129
+ if (deferDeviceProtection) {
4130
+ } else if (isPasskeySupported()) {
4085
4131
  const prfSalt = generateRandomBytes(32);
4086
4132
  try {
4087
4133
  const { credentialId, prfOutput } = await registerWebAuthnCredential({ api, jwt, rpId, rpName, prfSalt });
@@ -4122,32 +4168,37 @@ async function createDecaneConnect(config) {
4122
4168
  } finally {
4123
4169
  zeroize(master);
4124
4170
  }
4125
- const iv = generateRandomBytes(12);
4126
- const wrappedDeviceShare = await aesGcmEncrypt(
4127
- wrappingKey,
4128
- shareSet.device,
4129
- iv
4130
- );
4131
- await storage.putShare({
4132
- userId,
4133
- wrappedDeviceShare,
4134
- iv,
4135
- unlockMethod,
4136
- credentialId: meta.credentialId ? b64uToBuffer(meta.credentialId) : void 0,
4137
- prfSalt: meta.prfSalt ? b64uToBuffer(meta.prfSalt) : void 0,
4138
- pinSalt: meta.pinSalt ? b64uToBuffer(meta.pinSalt) : void 0,
4139
- pinIterations: meta.pinIterations,
4140
- passwordSalt: meta.passwordSalt ? b64uToBuffer(meta.passwordSalt) : void 0,
4141
- passwordIterations: meta.passwordIterations,
4142
- version: 1
4143
- });
4144
- if (unlockTokenKey) await attachUnlockToken(userId, jwt, unlockTokenKey);
4171
+ let wrappedDeviceShare = null;
4172
+ let iv = null;
4173
+ if (wrappingKey) {
4174
+ iv = generateRandomBytes(12);
4175
+ wrappedDeviceShare = await aesGcmEncrypt(
4176
+ wrappingKey,
4177
+ shareSet.device,
4178
+ iv
4179
+ );
4180
+ await storage.putShare({
4181
+ userId,
4182
+ wrappedDeviceShare,
4183
+ iv,
4184
+ unlockMethod,
4185
+ credentialId: meta.credentialId ? b64uToBuffer(meta.credentialId) : void 0,
4186
+ prfSalt: meta.prfSalt ? b64uToBuffer(meta.prfSalt) : void 0,
4187
+ pinSalt: meta.pinSalt ? b64uToBuffer(meta.pinSalt) : void 0,
4188
+ pinIterations: meta.pinIterations,
4189
+ passwordSalt: meta.passwordSalt ? b64uToBuffer(meta.passwordSalt) : void 0,
4190
+ passwordIterations: meta.passwordIterations,
4191
+ version: 1
4192
+ });
4193
+ if (unlockTokenKey) await attachUnlockToken(userId, jwt, unlockTokenKey);
4194
+ }
4145
4195
  const serverIv = generateRandomBytes(12);
4146
4196
  await withDeadline(api.putShare(jwt, {
4147
4197
  ciphertext: shareSet.server,
4148
4198
  iv: serverIv,
4149
4199
  wrappedShareMeta: {
4150
- unlockMethod
4200
+ // The server records this on the wallet_created event and nothing else.
4201
+ unlockMethod: deferDeviceProtection ? "deferred" : unlockMethod
4151
4202
  // credential IDs, prfSalt, pinSalt stay on-device — never sent to server
4152
4203
  },
4153
4204
  evmAddress,
@@ -4156,7 +4207,7 @@ async function createDecaneConnect(config) {
4156
4207
  }), remainingBudget(), "storing your wallet");
4157
4208
  zeroize(shareSet.server);
4158
4209
  const addresses = { evm: evmAddress, solana: solanaAddress, tron: tronAddress };
4159
- if (unlockMethod === "passkey" && meta.credentialId && meta.prfSalt) {
4210
+ if (unlockMethod === "passkey" && meta.credentialId && meta.prfSalt && wrappedDeviceShare && iv) {
4160
4211
  try {
4161
4212
  await api.putRecoveryBackup(jwt, {
4162
4213
  wrappedDeviceShare,
@@ -4239,7 +4290,7 @@ async function createDecaneConnect(config) {
4239
4290
  const tee = new TeeClient(teeUrl);
4240
4291
  const provisioned = await tee.provisionDeviceShare(jwt);
4241
4292
  if (provisioned) {
4242
- await persistProvisionedShare(userId, jwt, provisioned);
4293
+ if (!deferDeviceProtection) await persistProvisionedShare(userId, jwt, provisioned);
4243
4294
  let addrs;
4244
4295
  try {
4245
4296
  addrs = await session.start(provisioned, jwt, recordedAddresses);
@@ -4283,7 +4334,7 @@ async function createDecaneConnect(config) {
4283
4334
  });
4284
4335
  deviceShare = recovered.deviceShare;
4285
4336
  joinedViaPasskeySync = recovered.flow === "passkey-sync";
4286
- if (!await storage.getShare(userId) && isPasskeySupported()) {
4337
+ if (!deferDeviceProtection && !await storage.getShare(userId) && isPasskeySupported()) {
4287
4338
  await registerPasskeyAndBackup(userId, jwt, deviceShare);
4288
4339
  }
4289
4340
  } else {
@@ -4566,6 +4617,27 @@ async function createDecaneConnect(config) {
4566
4617
  const result = await api.authExternal(token, opts?.providerId);
4567
4618
  return handleAuthResult(result);
4568
4619
  },
4620
+ async connectWithAccessToken(token) {
4621
+ const claims = decodeAccessToken(token);
4622
+ if (!claims) throw new InvalidAccessTokenError("not a Decane access token");
4623
+ if (claims.expiresAt <= Date.now() + 5e3) throw new InvalidAccessTokenError("it has expired");
4624
+ const raw = await api.getAddresses(token);
4625
+ if (!raw.evm || !raw.solana) throw new InvalidAccessTokenError("this account holds no wallet yet");
4626
+ const addresses = { evm: raw.evm, solana: raw.solana, tron: raw.tron ?? evmToTronAddress2(raw.evm) };
4627
+ if (session.isActive()) session.end();
4628
+ auth = { jwt: token, userId: claims.userId, addresses };
4629
+ persisted = { userId: claims.userId, addresses };
4630
+ savePersisted(PERSIST_KEY, persisted);
4631
+ events.emit("connected");
4632
+ persistLiveSession();
4633
+ scheduleAccessTokenExpiry(token, claims.expiresAt);
4634
+ const stored = await storage.getShare(claims.userId);
4635
+ return {
4636
+ addresses,
4637
+ isNewUser: false,
4638
+ ...stored?.profile ? { profile: stored.profile } : {}
4639
+ };
4640
+ },
4569
4641
  async connectWithEmail(email) {
4570
4642
  await startEmailAuth(api, email);
4571
4643
  return { codeRequired: true };
@@ -4754,6 +4826,24 @@ async function createDecaneConnect(config) {
4754
4826
  zeroize(deviceShare);
4755
4827
  }
4756
4828
  },
4829
+ async deviceProtected() {
4830
+ if (!auth) return false;
4831
+ return await storage.getShare(auth.userId) !== null;
4832
+ },
4833
+ async protectDevice() {
4834
+ if (!auth) throw new ReconnectRequiredError();
4835
+ if (await storage.getShare(auth.userId)) return true;
4836
+ if (!serverAssistedRecovery) throw new NewDeviceError();
4837
+ const tee = new TeeClient(teeUrl);
4838
+ const provisioned = await tee.provisionDeviceShare(auth.jwt);
4839
+ if (!provisioned) throw new NewDeviceError();
4840
+ try {
4841
+ await persistProvisionedShare(auth.userId, auth.jwt, provisioned);
4842
+ } finally {
4843
+ zeroize(provisioned);
4844
+ }
4845
+ return await storage.getShare(auth.userId) !== null;
4846
+ },
4757
4847
  async unlock() {
4758
4848
  if (!auth) {
4759
4849
  const candidate = await passkeyLoginCandidate();
@@ -7376,6 +7466,7 @@ function DecaneKit({ config, children }) {
7376
7466
  chains,
7377
7467
  rpcUrls,
7378
7468
  requireAssertionPerSignature,
7469
+ deferDeviceProtection,
7379
7470
  // Recovery callbacks. Forwarding these is what makes recovery
7380
7471
  // configurable at all from <DecaneKit>; before, they were silently
7381
7472
  // dropped and the headless client was constructed without them.
@@ -7396,6 +7487,7 @@ function DecaneKit({ config, children }) {
7396
7487
  promptPin,
7397
7488
  promptUnlockPassword,
7398
7489
  requireAssertionPerSignature,
7490
+ deferDeviceProtection,
7399
7491
  offerRecoveryAtSignup,
7400
7492
  onRecoveryShareOffer,
7401
7493
  onRecoveryFileReady,
@@ -7531,6 +7623,18 @@ function DecaneKit({ config, children }) {
7531
7623
  throw e;
7532
7624
  }
7533
7625
  }, [clearPhase]);
7626
+ const connectWithAccessToken = (0, import_react5.useCallback)(async (token) => {
7627
+ const sdk = socialRef.current;
7628
+ if (!sdk) throw new Error("Social sign-in not configured");
7629
+ try {
7630
+ const result = await sdk.connectWithAccessToken(token);
7631
+ applyResult(result);
7632
+ setSelectorOpen(false);
7633
+ } catch (e) {
7634
+ clearPhase();
7635
+ throw e;
7636
+ }
7637
+ }, [clearPhase]);
7534
7638
  const customAuthConfig = config?.social?.customAuth;
7535
7639
  const connectWithCustomAuth = (0, import_react5.useCallback)(async () => {
7536
7640
  if (!customAuthConfig) throw new Error("Custom auth not configured");
@@ -7642,6 +7746,16 @@ function DecaneKit({ config, children }) {
7642
7746
  if (!sdk) throw new Error("Social wallet not connected");
7643
7747
  return sdk.addPasskey();
7644
7748
  }, []);
7749
+ const socialDeviceProtected = (0, import_react5.useCallback)(async () => {
7750
+ const sdk = socialRef.current;
7751
+ if (!sdk) return false;
7752
+ return sdk.deviceProtected();
7753
+ }, []);
7754
+ const socialProtectDevice = (0, import_react5.useCallback)(async () => {
7755
+ const sdk = socialRef.current;
7756
+ if (!sdk) throw new Error("Social wallet not connected");
7757
+ return sdk.protectDevice();
7758
+ }, []);
7645
7759
  const socialSetUnlockPassword = (0, import_react5.useCallback)(async (password) => {
7646
7760
  const sdk = socialRef.current;
7647
7761
  if (!sdk) throw new Error("Social wallet not connected");
@@ -7713,6 +7827,7 @@ function DecaneKit({ config, children }) {
7713
7827
  connectWithPhone,
7714
7828
  verifyPhoneCode: verifyPhoneCode2,
7715
7829
  connectWithToken,
7830
+ connectWithAccessToken,
7716
7831
  customAuthLabel: customAuthConfig?.label,
7717
7832
  connectWithCustomAuth: customAuthConfig ? connectWithCustomAuth : void 0,
7718
7833
  socialUnlock,
@@ -7734,6 +7849,8 @@ function DecaneKit({ config, children }) {
7734
7849
  socialHasRecoveryShare,
7735
7850
  socialHasPasskey,
7736
7851
  socialAddPasskey,
7852
+ socialDeviceProtected,
7853
+ socialProtectDevice,
7737
7854
  socialSetUnlockPassword,
7738
7855
  socialCanUnlockWithPassword,
7739
7856
  socialForgetLastUser,
@@ -7826,6 +7943,7 @@ function useSocialAuth() {
7826
7943
  connectWithPhone,
7827
7944
  verifyPhoneCode: verifyPhoneCode2,
7828
7945
  connectWithToken,
7946
+ connectWithAccessToken,
7829
7947
  customAuthLabel,
7830
7948
  connectWithCustomAuth,
7831
7949
  signInWithPasskey: ctxSignInWithPasskey,
@@ -7934,6 +8052,18 @@ function useSocialAuth() {
7934
8052
  setTokenLoading(false);
7935
8053
  }
7936
8054
  }, [connectWithToken]);
8055
+ const signInWithAccessToken = (0, import_react6.useCallback)(async (token) => {
8056
+ setTokenLoading(true);
8057
+ setError(null);
8058
+ try {
8059
+ await connectWithAccessToken(token);
8060
+ } catch (e) {
8061
+ setError(e.message);
8062
+ throw e;
8063
+ } finally {
8064
+ setTokenLoading(false);
8065
+ }
8066
+ }, [connectWithAccessToken]);
7937
8067
  const signInWithCustomAuth = (0, import_react6.useCallback)(async () => {
7938
8068
  if (!connectWithCustomAuth) throw new Error("Custom auth not configured");
7939
8069
  setCustomLoading(true);
@@ -8016,6 +8146,7 @@ function useSocialAuth() {
8016
8146
  sendPhoneCode,
8017
8147
  confirmPhoneCode,
8018
8148
  signInWithToken,
8149
+ signInWithAccessToken,
8019
8150
  signInWithCustomAuth: connectWithCustomAuth ? signInWithCustomAuth : void 0,
8020
8151
  signInWithPasskey,
8021
8152
  unlock,
@@ -8044,6 +8175,8 @@ function useSocialWallet() {
8044
8175
  socialHasRecoveryShare,
8045
8176
  socialHasPasskey,
8046
8177
  socialAddPasskey,
8178
+ socialDeviceProtected,
8179
+ socialProtectDevice,
8047
8180
  socialSetUnlockPassword,
8048
8181
  socialCanUnlockWithPassword,
8049
8182
  socialForgetLastUser,
@@ -8146,6 +8279,16 @@ function useSocialWallet() {
8146
8279
  throw e;
8147
8280
  }
8148
8281
  }, [socialAddPasskey]);
8282
+ const deviceProtected = (0, import_react6.useCallback)(() => run(() => socialDeviceProtected()), [socialDeviceProtected]);
8283
+ const protectDevice = (0, import_react6.useCallback)(async () => {
8284
+ setError(null);
8285
+ try {
8286
+ return await socialProtectDevice();
8287
+ } catch (e) {
8288
+ setError(e.message);
8289
+ throw e;
8290
+ }
8291
+ }, [socialProtectDevice]);
8149
8292
  const setUnlockPassword = (0, import_react6.useCallback)(async (password) => {
8150
8293
  setError(null);
8151
8294
  try {
@@ -8234,6 +8377,10 @@ function useSocialWallet() {
8234
8377
  // Passkey management for THIS device
8235
8378
  hasPasskey,
8236
8379
  addPasskey,
8380
+ // Deferred protection (deferDeviceProtection): whether this device holds
8381
+ // a wrapped share, and the call that stores one — passkey or password.
8382
+ deviceProtected,
8383
+ protectDevice,
8237
8384
  // "Unlock with your password": setUnlockPassword needs a live session;
8238
8385
  // canUnlockWithPassword and unlockWithPassword are the signed-out pair that
8239
8386
  // open one without an identity provider or a passkey.
@@ -8549,6 +8696,7 @@ function useSignTypedData() {
8549
8696
  DecaneMark,
8550
8697
  DecaneWordmark,
8551
8698
  ExportNotAuthorizedError,
8699
+ InvalidAccessTokenError,
8552
8700
  InvalidApiKeyError,
8553
8701
  InvalidPinError,
8554
8702
  InvalidRecoveryFileError,