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 +104 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -1
- package/dist/index.d.ts +18 -1
- package/dist/index.js +103 -11
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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.
|
|
3753
|
-
const LAST_USER_KEY = makeLastUserKey(config.
|
|
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
|
-
|
|
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.
|
|
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 (!
|
|
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
|
|
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,
|