decane-connect-kit 2.9.1 → 2.11.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 +46 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -3
- package/dist/index.d.ts +9 -3
- package/dist/index.js +46 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1126,9 +1126,14 @@ interface SocialAuthState {
|
|
|
1126
1126
|
* Who the provider said the user is — `{ name?, email?, picture? }`.
|
|
1127
1127
|
*
|
|
1128
1128
|
* `null` until a sign-in returns one, and stays null for email sign-in and
|
|
1129
|
-
* for providers the user withheld a profile from.
|
|
1130
|
-
*
|
|
1131
|
-
*
|
|
1129
|
+
* for providers the user withheld a profile from.
|
|
1130
|
+
*
|
|
1131
|
+
* The backend stores no profile, so this cannot be re-fetched — but whatever
|
|
1132
|
+
* a sign-in does reveal is kept beside this device's share, and a passkey
|
|
1133
|
+
* login hands it back. A passkey sign-in therefore carries the same name and
|
|
1134
|
+
* email the original Google sign-in did. A session merely restored from a
|
|
1135
|
+
* reload still has an address and no name, so copy it somewhere durable if
|
|
1136
|
+
* you need it in that state.
|
|
1132
1137
|
*/
|
|
1133
1138
|
profile: AuthProfile | null;
|
|
1134
1139
|
isUnlocked: boolean;
|
|
@@ -1222,6 +1227,7 @@ declare function useSocialAuth(): {
|
|
|
1222
1227
|
} | null;
|
|
1223
1228
|
sessionExpiresAt: number | null;
|
|
1224
1229
|
canUsePasskey: boolean;
|
|
1230
|
+
profile: AuthProfile | null;
|
|
1225
1231
|
phase: "creating" | "unlocking" | null;
|
|
1226
1232
|
isCreatingWallet: boolean;
|
|
1227
1233
|
needsReconnect: boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -1126,9 +1126,14 @@ interface SocialAuthState {
|
|
|
1126
1126
|
* Who the provider said the user is — `{ name?, email?, picture? }`.
|
|
1127
1127
|
*
|
|
1128
1128
|
* `null` until a sign-in returns one, and stays null for email sign-in and
|
|
1129
|
-
* for providers the user withheld a profile from.
|
|
1130
|
-
*
|
|
1131
|
-
*
|
|
1129
|
+
* for providers the user withheld a profile from.
|
|
1130
|
+
*
|
|
1131
|
+
* The backend stores no profile, so this cannot be re-fetched — but whatever
|
|
1132
|
+
* a sign-in does reveal is kept beside this device's share, and a passkey
|
|
1133
|
+
* login hands it back. A passkey sign-in therefore carries the same name and
|
|
1134
|
+
* email the original Google sign-in did. A session merely restored from a
|
|
1135
|
+
* reload still has an address and no name, so copy it somewhere durable if
|
|
1136
|
+
* you need it in that state.
|
|
1132
1137
|
*/
|
|
1133
1138
|
profile: AuthProfile | null;
|
|
1134
1139
|
isUnlocked: boolean;
|
|
@@ -1222,6 +1227,7 @@ declare function useSocialAuth(): {
|
|
|
1222
1227
|
} | null;
|
|
1223
1228
|
sessionExpiresAt: number | null;
|
|
1224
1229
|
canUsePasskey: boolean;
|
|
1230
|
+
profile: AuthProfile | null;
|
|
1225
1231
|
phase: "creating" | "unlocking" | null;
|
|
1226
1232
|
isCreatingWallet: boolean;
|
|
1227
1233
|
needsReconnect: boolean;
|
package/dist/index.js
CHANGED
|
@@ -2195,6 +2195,15 @@ var Storage = class {
|
|
|
2195
2195
|
async getShare(userId) {
|
|
2196
2196
|
return (await this._db).get(STORE, userId) ?? null;
|
|
2197
2197
|
}
|
|
2198
|
+
/**
|
|
2199
|
+
* Every share this browser holds. Needed because signing out clears the
|
|
2200
|
+
* remembered identity while deliberately leaving the share behind, so the
|
|
2201
|
+
* userId that keys it is no longer known and the share can only be found by
|
|
2202
|
+
* enumeration. See passkeyDeviceCandidate().
|
|
2203
|
+
*/
|
|
2204
|
+
async listShares() {
|
|
2205
|
+
return (await this._db).getAll(STORE);
|
|
2206
|
+
}
|
|
2198
2207
|
async putShare(share) {
|
|
2199
2208
|
await (await this._db).put(STORE, share);
|
|
2200
2209
|
}
|
|
@@ -3948,7 +3957,25 @@ async function createDecaneConnect(config) {
|
|
|
3948
3957
|
}
|
|
3949
3958
|
async function passkeyLoginCandidate() {
|
|
3950
3959
|
if (!persisted?.userId || !isPasskeySupported()) return null;
|
|
3951
|
-
|
|
3960
|
+
return usablePasskeyShare(await storage.getShare(persisted.userId));
|
|
3961
|
+
}
|
|
3962
|
+
async function passkeyDeviceCandidate() {
|
|
3963
|
+
if (!isPasskeySupported()) return null;
|
|
3964
|
+
if (persisted?.userId) return passkeyLoginCandidate();
|
|
3965
|
+
const candidates = (await storage.listShares()).map(usablePasskeyShare).filter(Boolean);
|
|
3966
|
+
return candidates.length === 1 ? candidates[0] : null;
|
|
3967
|
+
}
|
|
3968
|
+
async function rememberProfile(userId, profile) {
|
|
3969
|
+
const incoming = Object.fromEntries(Object.entries(profile).filter(([, v]) => v));
|
|
3970
|
+
if (!Object.keys(incoming).length) return;
|
|
3971
|
+
try {
|
|
3972
|
+
const stored = await storage.getShare(userId);
|
|
3973
|
+
if (!stored) return;
|
|
3974
|
+
await storage.putShare({ ...stored, profile: { ...stored.profile, ...incoming } });
|
|
3975
|
+
} catch {
|
|
3976
|
+
}
|
|
3977
|
+
}
|
|
3978
|
+
function usablePasskeyShare(stored) {
|
|
3952
3979
|
if (!stored || stored.unlockMethod !== "passkey") return null;
|
|
3953
3980
|
if (!stored.credentialId || !stored.prfSalt) return null;
|
|
3954
3981
|
return stored;
|
|
@@ -3985,7 +4012,11 @@ async function createDecaneConnect(config) {
|
|
|
3985
4012
|
savePersisted(PERSIST_KEY, persisted);
|
|
3986
4013
|
events.emit("connected");
|
|
3987
4014
|
persistLiveSession();
|
|
3988
|
-
return {
|
|
4015
|
+
return {
|
|
4016
|
+
addresses,
|
|
4017
|
+
isNewUser: false,
|
|
4018
|
+
...stored.profile ? { profile: stored.profile } : {}
|
|
4019
|
+
};
|
|
3989
4020
|
}
|
|
3990
4021
|
async function handleAuthResult(result) {
|
|
3991
4022
|
let addresses;
|
|
@@ -4001,6 +4032,7 @@ async function createDecaneConnect(config) {
|
|
|
4001
4032
|
savePersisted(PERSIST_KEY, persisted);
|
|
4002
4033
|
events.emit("connected");
|
|
4003
4034
|
persistLiveSession();
|
|
4035
|
+
if (result.profile) await rememberProfile(result.userId, result.profile);
|
|
4004
4036
|
return {
|
|
4005
4037
|
addresses,
|
|
4006
4038
|
isNewUser: result.isNewUser,
|
|
@@ -4107,10 +4139,10 @@ async function createDecaneConnect(config) {
|
|
|
4107
4139
|
return handleAuthResult(result);
|
|
4108
4140
|
},
|
|
4109
4141
|
async canSignInWithPasskey() {
|
|
4110
|
-
return await
|
|
4142
|
+
return await passkeyDeviceCandidate() !== null;
|
|
4111
4143
|
},
|
|
4112
4144
|
async signInWithPasskey() {
|
|
4113
|
-
const candidate = await
|
|
4145
|
+
const candidate = await passkeyDeviceCandidate();
|
|
4114
4146
|
if (!candidate) throw new NewDeviceError();
|
|
4115
4147
|
return passkeyLogin(candidate);
|
|
4116
4148
|
},
|
|
@@ -6694,7 +6726,11 @@ function DecaneKit({ config, children }) {
|
|
|
6694
6726
|
"session-expired",
|
|
6695
6727
|
() => setSocialAuth((p) => ({ ...p, isUnlocked: false, sessionExpiresAt: null }))
|
|
6696
6728
|
);
|
|
6697
|
-
sdk.on("disconnected", () =>
|
|
6729
|
+
sdk.on("disconnected", () => {
|
|
6730
|
+
setSocialAuth(DEFAULT_SOCIAL);
|
|
6731
|
+
sdk.canSignInWithPasskey().then((can) => setSocialAuth((p) => ({ ...p, canUsePasskey: can }))).catch(() => {
|
|
6732
|
+
});
|
|
6733
|
+
});
|
|
6698
6734
|
sdk.on("wallet-creating", () => setSocialAuth((p) => ({ ...p, phase: "creating" })));
|
|
6699
6735
|
sdk.on("wallet-unlocking", () => setSocialAuth((p) => ({ ...p, phase: "unlocking" })));
|
|
6700
6736
|
sdk.on("connected", () => setSocialAuth((p) => ({ ...p, phase: null })));
|
|
@@ -7210,6 +7246,11 @@ function useSocialAuth() {
|
|
|
7210
7246
|
sessionExpiresAt: socialAuth.sessionExpiresAt,
|
|
7211
7247
|
// True when this device can skip the full sign-in and resume with a passkey.
|
|
7212
7248
|
canUsePasskey: socialAuth.canUsePasskey,
|
|
7249
|
+
// Who the provider said the user is — { name?, email?, picture? }, or null.
|
|
7250
|
+
// A passkey sign-in returns the profile recorded on this device when the
|
|
7251
|
+
// user first signed in, so it carries the same name and email a Google
|
|
7252
|
+
// sign-in does rather than coming back anonymous.
|
|
7253
|
+
profile: socialAuth.profile,
|
|
7213
7254
|
// Long-running work in progress, for driving your own status UI.
|
|
7214
7255
|
phase: socialAuth.phase,
|
|
7215
7256
|
isCreatingWallet: socialAuth.phase === "creating",
|