decane-connect-kit 2.23.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,7 +3786,14 @@ 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;
3769
3799
  const deferDeviceProtection = config.deferDeviceProtection === true;
@@ -3826,12 +3856,13 @@ async function createDecaneConnect(config) {
3826
3856
  })
3827
3857
  ]).finally(() => clearTimeout(timer));
3828
3858
  }
3829
- const SESSION_KEY = sessionPersistKey(config.apiKey);
3859
+ const SESSION_KEY = sessionPersistKey(config.appId);
3860
+ const LEGACY_SESSION_KEY = sessionPersistKey(config.apiKey);
3830
3861
  function persistLiveSession() {
3831
- if (!persistSession) return;
3862
+ if (!persistSession || !auth) return;
3832
3863
  const handle = session.getHandle();
3833
- const expiresAt = session.expiresAtTimestamp();
3834
- if (!handle || !auth || !expiresAt) return;
3864
+ const expiresAt = handle ? session.expiresAtTimestamp() : decodeAccessToken(auth.jwt)?.expiresAt ?? null;
3865
+ if (!expiresAt) return;
3835
3866
  saveSession(SESSION_KEY, {
3836
3867
  handle,
3837
3868
  jwt: auth.jwt,
@@ -3843,11 +3874,23 @@ async function createDecaneConnect(config) {
3843
3874
  function dropLiveSession() {
3844
3875
  clearSessionPersist(SESSION_KEY);
3845
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
+ }
3846
3888
  if (persistSession) {
3847
- const saved = loadSession(SESSION_KEY);
3848
- 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) {
3849
3891
  try {
3850
- 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);
3851
3894
  auth = { jwt: saved.jwt, userId: saved.userId, addresses: saved.addresses };
3852
3895
  persisted = { userId: saved.userId, addresses: saved.addresses };
3853
3896
  } catch {
@@ -3856,6 +3899,7 @@ async function createDecaneConnect(config) {
3856
3899
  } else if (saved) {
3857
3900
  dropLiveSession();
3858
3901
  }
3902
+ if (LEGACY_SESSION_KEY !== SESSION_KEY) clearSessionPersist(LEGACY_SESSION_KEY);
3859
3903
  }
3860
3904
  events.on("session-expired", () => dropLiveSession());
3861
3905
  async function passwordSetup() {
@@ -4573,6 +4617,27 @@ async function createDecaneConnect(config) {
4573
4617
  const result = await api.authExternal(token, opts?.providerId);
4574
4618
  return handleAuthResult(result);
4575
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
+ },
4576
4641
  async connectWithEmail(email) {
4577
4642
  await startEmailAuth(api, email);
4578
4643
  return { codeRequired: true };
@@ -7558,6 +7623,18 @@ function DecaneKit({ config, children }) {
7558
7623
  throw e;
7559
7624
  }
7560
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]);
7561
7638
  const customAuthConfig = config?.social?.customAuth;
7562
7639
  const connectWithCustomAuth = (0, import_react5.useCallback)(async () => {
7563
7640
  if (!customAuthConfig) throw new Error("Custom auth not configured");
@@ -7750,6 +7827,7 @@ function DecaneKit({ config, children }) {
7750
7827
  connectWithPhone,
7751
7828
  verifyPhoneCode: verifyPhoneCode2,
7752
7829
  connectWithToken,
7830
+ connectWithAccessToken,
7753
7831
  customAuthLabel: customAuthConfig?.label,
7754
7832
  connectWithCustomAuth: customAuthConfig ? connectWithCustomAuth : void 0,
7755
7833
  socialUnlock,
@@ -7865,6 +7943,7 @@ function useSocialAuth() {
7865
7943
  connectWithPhone,
7866
7944
  verifyPhoneCode: verifyPhoneCode2,
7867
7945
  connectWithToken,
7946
+ connectWithAccessToken,
7868
7947
  customAuthLabel,
7869
7948
  connectWithCustomAuth,
7870
7949
  signInWithPasskey: ctxSignInWithPasskey,
@@ -7973,6 +8052,18 @@ function useSocialAuth() {
7973
8052
  setTokenLoading(false);
7974
8053
  }
7975
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]);
7976
8067
  const signInWithCustomAuth = (0, import_react6.useCallback)(async () => {
7977
8068
  if (!connectWithCustomAuth) throw new Error("Custom auth not configured");
7978
8069
  setCustomLoading(true);
@@ -8055,6 +8146,7 @@ function useSocialAuth() {
8055
8146
  sendPhoneCode,
8056
8147
  confirmPhoneCode,
8057
8148
  signInWithToken,
8149
+ signInWithAccessToken,
8058
8150
  signInWithCustomAuth: connectWithCustomAuth ? signInWithCustomAuth : void 0,
8059
8151
  signInWithPasskey,
8060
8152
  unlock,
@@ -8604,6 +8696,7 @@ function useSignTypedData() {
8604
8696
  DecaneMark,
8605
8697
  DecaneWordmark,
8606
8698
  ExportNotAuthorizedError,
8699
+ InvalidAccessTokenError,
8607
8700
  InvalidApiKeyError,
8608
8701
  InvalidPinError,
8609
8702
  InvalidRecoveryFileError,