decane-connect-kit 2.20.0 → 2.22.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 +71 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +40 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +71 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -2038,10 +2038,13 @@ function readRedirectOutcome(search) {
|
|
|
2038
2038
|
const name = params.get("decane_name");
|
|
2039
2039
|
const email = params.get("decane_email");
|
|
2040
2040
|
const picture = params.get("decane_picture");
|
|
2041
|
+
const username = params.get("decane_username");
|
|
2042
|
+
const providerSubject = params.get("decane_provider_subject");
|
|
2041
2043
|
const profile = {
|
|
2042
2044
|
...name ? { name } : {},
|
|
2043
2045
|
...email ? { email } : {},
|
|
2044
|
-
...picture ? { picture } : {}
|
|
2046
|
+
...picture ? { picture } : {},
|
|
2047
|
+
...username ? { username } : {}
|
|
2045
2048
|
};
|
|
2046
2049
|
return {
|
|
2047
2050
|
kind: "result",
|
|
@@ -2054,7 +2057,8 @@ function readRedirectOutcome(search) {
|
|
|
2054
2057
|
// test `result.profile` rather than an object that is always present but
|
|
2055
2058
|
// sometimes empty. X never sends an email, so this is the normal case
|
|
2056
2059
|
// there rather than an edge one.
|
|
2057
|
-
...Object.keys(profile).length ? { profile } : {}
|
|
2060
|
+
...Object.keys(profile).length ? { profile } : {},
|
|
2061
|
+
...providerSubject ? { providerSubject } : {}
|
|
2058
2062
|
}
|
|
2059
2063
|
};
|
|
2060
2064
|
}
|
|
@@ -3685,6 +3689,29 @@ function clearPersisted(key) {
|
|
|
3685
3689
|
} catch {
|
|
3686
3690
|
}
|
|
3687
3691
|
}
|
|
3692
|
+
function makeLastUserKey(apiKey) {
|
|
3693
|
+
return `${makePersistKey(apiKey)}:last-user`;
|
|
3694
|
+
}
|
|
3695
|
+
function loadLastUser(key) {
|
|
3696
|
+
try {
|
|
3697
|
+
const raw = typeof localStorage !== "undefined" ? localStorage.getItem(key) : null;
|
|
3698
|
+
return raw ? JSON.parse(raw).userId ?? null : null;
|
|
3699
|
+
} catch {
|
|
3700
|
+
return null;
|
|
3701
|
+
}
|
|
3702
|
+
}
|
|
3703
|
+
function saveLastUser(key, userId) {
|
|
3704
|
+
try {
|
|
3705
|
+
localStorage.setItem(key, JSON.stringify({ userId }));
|
|
3706
|
+
} catch {
|
|
3707
|
+
}
|
|
3708
|
+
}
|
|
3709
|
+
function clearLastUser(key) {
|
|
3710
|
+
try {
|
|
3711
|
+
localStorage.removeItem(key);
|
|
3712
|
+
} catch {
|
|
3713
|
+
}
|
|
3714
|
+
}
|
|
3688
3715
|
function sessionPersistKey(apiKey) {
|
|
3689
3716
|
return `decane:sess:${apiKey.slice(0, 20)}`;
|
|
3690
3717
|
}
|
|
@@ -3723,6 +3750,7 @@ async function createDecaneConnect(config) {
|
|
|
3723
3750
|
const rpId = config.rpId ?? (typeof window !== "undefined" ? window.location.hostname : "localhost");
|
|
3724
3751
|
const rpName = config.rpName ?? "Decane Connect";
|
|
3725
3752
|
const PERSIST_KEY = makePersistKey(config.apiKey);
|
|
3753
|
+
const LAST_USER_KEY = makeLastUserKey(config.apiKey);
|
|
3726
3754
|
const teeUrl = config.teeUrl ?? DEFAULT_TEE_URL;
|
|
3727
3755
|
const events = new EventEmitter();
|
|
3728
3756
|
const api = new ApiClient(baseUrl, config.apiKey, config.appId);
|
|
@@ -3735,6 +3763,7 @@ async function createDecaneConnect(config) {
|
|
|
3735
3763
|
);
|
|
3736
3764
|
let auth = null;
|
|
3737
3765
|
let persisted = loadPersisted(PERSIST_KEY);
|
|
3766
|
+
let lastUserId = loadLastUser(LAST_USER_KEY);
|
|
3738
3767
|
let ethProvider = null;
|
|
3739
3768
|
const persistSession = config.persistSession !== false;
|
|
3740
3769
|
const serverAssistedRecovery = true;
|
|
@@ -3880,6 +3909,10 @@ async function createDecaneConnect(config) {
|
|
|
3880
3909
|
const stored = await storage.getShare(persisted.userId);
|
|
3881
3910
|
return hasPasswordUnlock(stored) ? stored : null;
|
|
3882
3911
|
}
|
|
3912
|
+
if (lastUserId) {
|
|
3913
|
+
const stored = await storage.getShare(lastUserId);
|
|
3914
|
+
return hasPasswordUnlock(stored) ? stored : null;
|
|
3915
|
+
}
|
|
3883
3916
|
const candidates = (await storage.listShares()).filter(hasPasswordUnlock);
|
|
3884
3917
|
return candidates.length === 1 ? candidates[0] : null;
|
|
3885
3918
|
}
|
|
@@ -4333,14 +4366,16 @@ async function createDecaneConnect(config) {
|
|
|
4333
4366
|
async function passkeyDeviceCandidate() {
|
|
4334
4367
|
if (!isPasskeySupported()) return null;
|
|
4335
4368
|
if (persisted?.userId) return passkeyLoginCandidate();
|
|
4369
|
+
if (lastUserId) return usablePasskeyShare(await storage.getShare(lastUserId));
|
|
4336
4370
|
const candidates = (await storage.listShares()).map(usablePasskeyShare).filter(Boolean);
|
|
4337
4371
|
return candidates.length === 1 ? candidates[0] : null;
|
|
4338
4372
|
}
|
|
4339
4373
|
async function canOfferPasskey() {
|
|
4340
4374
|
if (!isPasskeySupported()) return false;
|
|
4341
4375
|
if (await passkeyDeviceCandidate()) return true;
|
|
4342
|
-
|
|
4343
|
-
|
|
4376
|
+
const known = persisted?.userId ?? lastUserId;
|
|
4377
|
+
if (!known) return false;
|
|
4378
|
+
return await storage.getShare(known) == null;
|
|
4344
4379
|
}
|
|
4345
4380
|
async function rememberProfile(userId, profile) {
|
|
4346
4381
|
const incoming = Object.fromEntries(Object.entries(profile).filter(([, v]) => v));
|
|
@@ -4450,7 +4485,8 @@ async function createDecaneConnect(config) {
|
|
|
4450
4485
|
return {
|
|
4451
4486
|
addresses,
|
|
4452
4487
|
isNewUser: result.isNewUser,
|
|
4453
|
-
...result.profile ? { profile: result.profile } : {}
|
|
4488
|
+
...result.profile ? { profile: result.profile } : {},
|
|
4489
|
+
...result.providerSubject ? { providerSubject: result.providerSubject } : {}
|
|
4454
4490
|
};
|
|
4455
4491
|
}
|
|
4456
4492
|
const client = {
|
|
@@ -4552,8 +4588,9 @@ async function createDecaneConnect(config) {
|
|
|
4552
4588
|
async signInWithPasskey() {
|
|
4553
4589
|
const candidate = await passkeyDeviceCandidate();
|
|
4554
4590
|
if (candidate) return passkeyLogin(candidate);
|
|
4555
|
-
|
|
4556
|
-
|
|
4591
|
+
const known = persisted?.userId ?? lastUserId;
|
|
4592
|
+
if (isPasskeySupported() && known && await storage.getShare(known) == null) {
|
|
4593
|
+
return passkeyLoginWithoutShare(known);
|
|
4557
4594
|
}
|
|
4558
4595
|
throw new NewDeviceError();
|
|
4559
4596
|
},
|
|
@@ -4893,12 +4930,21 @@ async function createDecaneConnect(config) {
|
|
|
4893
4930
|
await api.revoke(auth.jwt).catch(() => {
|
|
4894
4931
|
});
|
|
4895
4932
|
}
|
|
4933
|
+
const leaving = auth?.userId ?? persisted?.userId ?? null;
|
|
4934
|
+
if (leaving) {
|
|
4935
|
+
lastUserId = leaving;
|
|
4936
|
+
saveLastUser(LAST_USER_KEY, leaving);
|
|
4937
|
+
}
|
|
4896
4938
|
auth = null;
|
|
4897
4939
|
persisted = null;
|
|
4898
4940
|
clearPersisted(PERSIST_KEY);
|
|
4899
4941
|
dropLiveSession();
|
|
4900
4942
|
events.emit("disconnected");
|
|
4901
4943
|
},
|
|
4944
|
+
forgetLastUser() {
|
|
4945
|
+
lastUserId = null;
|
|
4946
|
+
clearLastUser(LAST_USER_KEY);
|
|
4947
|
+
},
|
|
4902
4948
|
getAddresses() {
|
|
4903
4949
|
return auth?.addresses ?? persisted?.addresses ?? null;
|
|
4904
4950
|
},
|
|
@@ -7263,6 +7309,7 @@ var DEFAULT_SOCIAL = {
|
|
|
7263
7309
|
addresses: null,
|
|
7264
7310
|
isNewUser: false,
|
|
7265
7311
|
profile: null,
|
|
7312
|
+
providerSubject: null,
|
|
7266
7313
|
isUnlocked: false,
|
|
7267
7314
|
sessionExpiresAt: null,
|
|
7268
7315
|
needsReconnect: false,
|
|
@@ -7398,6 +7445,7 @@ function DecaneKit({ config, children }) {
|
|
|
7398
7445
|
addresses: result.addresses,
|
|
7399
7446
|
isNewUser: result.isNewUser,
|
|
7400
7447
|
profile: result.profile ?? null,
|
|
7448
|
+
providerSubject: result.providerSubject ?? null,
|
|
7401
7449
|
isUnlocked: sdk?.isUnlocked() ?? false,
|
|
7402
7450
|
sessionExpiresAt: sdk?.sessionExpiresAt() ?? null,
|
|
7403
7451
|
needsReconnect: false,
|
|
@@ -7604,6 +7652,14 @@ function DecaneKit({ config, children }) {
|
|
|
7604
7652
|
if (!sdk) return false;
|
|
7605
7653
|
return sdk.canUnlockWithPassword();
|
|
7606
7654
|
}, []);
|
|
7655
|
+
const socialForgetLastUser = (0, import_react5.useCallback)(() => {
|
|
7656
|
+
socialRef.current?.forgetLastUser();
|
|
7657
|
+
const sdk = socialRef.current;
|
|
7658
|
+
if (sdk) {
|
|
7659
|
+
sdk.canSignInWithPasskey().then((can) => setSocialAuth((p) => ({ ...p, canUsePasskey: can }))).catch(() => {
|
|
7660
|
+
});
|
|
7661
|
+
}
|
|
7662
|
+
}, []);
|
|
7607
7663
|
const socialUnlockWithPassword = (0, import_react5.useCallback)(async (password) => {
|
|
7608
7664
|
const sdk = socialRef.current;
|
|
7609
7665
|
if (!sdk) throw new Error("Social wallet not connected");
|
|
@@ -7680,6 +7736,7 @@ function DecaneKit({ config, children }) {
|
|
|
7680
7736
|
socialAddPasskey,
|
|
7681
7737
|
socialSetUnlockPassword,
|
|
7682
7738
|
socialCanUnlockWithPassword,
|
|
7739
|
+
socialForgetLastUser,
|
|
7683
7740
|
socialUnlockWithPassword,
|
|
7684
7741
|
socialRemoveUnlockPassword,
|
|
7685
7742
|
socialShouldPromptRecovery,
|
|
@@ -7930,6 +7987,9 @@ function useSocialAuth() {
|
|
|
7930
7987
|
// user first signed in, so it carries the same name and email a Google
|
|
7931
7988
|
// sign-in does rather than coming back anonymous.
|
|
7932
7989
|
profile: socialAuth.profile,
|
|
7990
|
+
// The provider's stable id, where it gives one. Null otherwise. Safe to
|
|
7991
|
+
// match an account on, unlike the handle in `profile.username`.
|
|
7992
|
+
providerSubject: socialAuth.providerSubject,
|
|
7933
7993
|
// Long-running work in progress, for driving your own status UI.
|
|
7934
7994
|
phase: socialAuth.phase,
|
|
7935
7995
|
isCreatingWallet: socialAuth.phase === "creating",
|
|
@@ -7986,6 +8046,7 @@ function useSocialWallet() {
|
|
|
7986
8046
|
socialAddPasskey,
|
|
7987
8047
|
socialSetUnlockPassword,
|
|
7988
8048
|
socialCanUnlockWithPassword,
|
|
8049
|
+
socialForgetLastUser,
|
|
7989
8050
|
socialUnlockWithPassword,
|
|
7990
8051
|
socialRemoveUnlockPassword,
|
|
7991
8052
|
socialShouldPromptRecovery,
|
|
@@ -8094,6 +8155,7 @@ function useSocialWallet() {
|
|
|
8094
8155
|
throw e;
|
|
8095
8156
|
}
|
|
8096
8157
|
}, [socialSetUnlockPassword]);
|
|
8158
|
+
const forgetLastUser = (0, import_react6.useCallback)(() => socialForgetLastUser(), [socialForgetLastUser]);
|
|
8097
8159
|
const canUnlockWithPassword = (0, import_react6.useCallback)(
|
|
8098
8160
|
() => socialCanUnlockWithPassword(),
|
|
8099
8161
|
[socialCanUnlockWithPassword]
|
|
@@ -8179,6 +8241,8 @@ function useSocialWallet() {
|
|
|
8179
8241
|
canUnlockWithPassword,
|
|
8180
8242
|
unlockWithPassword,
|
|
8181
8243
|
removeUnlockPassword,
|
|
8244
|
+
// "Use a different account": stop offering the account that signed out.
|
|
8245
|
+
forgetLastUser,
|
|
8182
8246
|
// Recovery (2-of-3 Shamir share set)
|
|
8183
8247
|
rotateShares,
|
|
8184
8248
|
exportPortableBackup,
|