decane-connect-kit 2.22.0 → 2.23.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 +82 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +38 -1
- package/dist/index.d.ts +38 -1
- package/dist/index.js +82 -27
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3766,6 +3766,7 @@ async function createDecaneConnect(config) {
|
|
|
3766
3766
|
let lastUserId = loadLastUser(LAST_USER_KEY);
|
|
3767
3767
|
let ethProvider = null;
|
|
3768
3768
|
const persistSession = config.persistSession !== false;
|
|
3769
|
+
const deferDeviceProtection = config.deferDeviceProtection === true;
|
|
3769
3770
|
const serverAssistedRecovery = true;
|
|
3770
3771
|
const RECOVERY_OFFER_TIMEOUT_MS = 5 * 6e4;
|
|
3771
3772
|
const selfHealAttempted = /* @__PURE__ */ new Set();
|
|
@@ -4077,11 +4078,12 @@ async function createDecaneConnect(config) {
|
|
|
4077
4078
|
}
|
|
4078
4079
|
async function firstTimeSetup(userId, jwt) {
|
|
4079
4080
|
prewarmAttestation(teeUrl);
|
|
4080
|
-
let unlockMethod;
|
|
4081
|
-
let wrappingKey;
|
|
4081
|
+
let unlockMethod = "password";
|
|
4082
|
+
let wrappingKey = null;
|
|
4082
4083
|
let meta = {};
|
|
4083
4084
|
let unlockTokenKey = null;
|
|
4084
|
-
if (
|
|
4085
|
+
if (deferDeviceProtection) {
|
|
4086
|
+
} else if (isPasskeySupported()) {
|
|
4085
4087
|
const prfSalt = generateRandomBytes(32);
|
|
4086
4088
|
try {
|
|
4087
4089
|
const { credentialId, prfOutput } = await registerWebAuthnCredential({ api, jwt, rpId, rpName, prfSalt });
|
|
@@ -4122,32 +4124,37 @@ async function createDecaneConnect(config) {
|
|
|
4122
4124
|
} finally {
|
|
4123
4125
|
zeroize(master);
|
|
4124
4126
|
}
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4127
|
+
let wrappedDeviceShare = null;
|
|
4128
|
+
let iv = null;
|
|
4129
|
+
if (wrappingKey) {
|
|
4130
|
+
iv = generateRandomBytes(12);
|
|
4131
|
+
wrappedDeviceShare = await aesGcmEncrypt(
|
|
4132
|
+
wrappingKey,
|
|
4133
|
+
shareSet.device,
|
|
4134
|
+
iv
|
|
4135
|
+
);
|
|
4136
|
+
await storage.putShare({
|
|
4137
|
+
userId,
|
|
4138
|
+
wrappedDeviceShare,
|
|
4139
|
+
iv,
|
|
4140
|
+
unlockMethod,
|
|
4141
|
+
credentialId: meta.credentialId ? b64uToBuffer(meta.credentialId) : void 0,
|
|
4142
|
+
prfSalt: meta.prfSalt ? b64uToBuffer(meta.prfSalt) : void 0,
|
|
4143
|
+
pinSalt: meta.pinSalt ? b64uToBuffer(meta.pinSalt) : void 0,
|
|
4144
|
+
pinIterations: meta.pinIterations,
|
|
4145
|
+
passwordSalt: meta.passwordSalt ? b64uToBuffer(meta.passwordSalt) : void 0,
|
|
4146
|
+
passwordIterations: meta.passwordIterations,
|
|
4147
|
+
version: 1
|
|
4148
|
+
});
|
|
4149
|
+
if (unlockTokenKey) await attachUnlockToken(userId, jwt, unlockTokenKey);
|
|
4150
|
+
}
|
|
4145
4151
|
const serverIv = generateRandomBytes(12);
|
|
4146
4152
|
await withDeadline(api.putShare(jwt, {
|
|
4147
4153
|
ciphertext: shareSet.server,
|
|
4148
4154
|
iv: serverIv,
|
|
4149
4155
|
wrappedShareMeta: {
|
|
4150
|
-
|
|
4156
|
+
// The server records this on the wallet_created event and nothing else.
|
|
4157
|
+
unlockMethod: deferDeviceProtection ? "deferred" : unlockMethod
|
|
4151
4158
|
// credential IDs, prfSalt, pinSalt stay on-device — never sent to server
|
|
4152
4159
|
},
|
|
4153
4160
|
evmAddress,
|
|
@@ -4156,7 +4163,7 @@ async function createDecaneConnect(config) {
|
|
|
4156
4163
|
}), remainingBudget(), "storing your wallet");
|
|
4157
4164
|
zeroize(shareSet.server);
|
|
4158
4165
|
const addresses = { evm: evmAddress, solana: solanaAddress, tron: tronAddress };
|
|
4159
|
-
if (unlockMethod === "passkey" && meta.credentialId && meta.prfSalt) {
|
|
4166
|
+
if (unlockMethod === "passkey" && meta.credentialId && meta.prfSalt && wrappedDeviceShare && iv) {
|
|
4160
4167
|
try {
|
|
4161
4168
|
await api.putRecoveryBackup(jwt, {
|
|
4162
4169
|
wrappedDeviceShare,
|
|
@@ -4239,7 +4246,7 @@ async function createDecaneConnect(config) {
|
|
|
4239
4246
|
const tee = new TeeClient(teeUrl);
|
|
4240
4247
|
const provisioned = await tee.provisionDeviceShare(jwt);
|
|
4241
4248
|
if (provisioned) {
|
|
4242
|
-
await persistProvisionedShare(userId, jwt, provisioned);
|
|
4249
|
+
if (!deferDeviceProtection) await persistProvisionedShare(userId, jwt, provisioned);
|
|
4243
4250
|
let addrs;
|
|
4244
4251
|
try {
|
|
4245
4252
|
addrs = await session.start(provisioned, jwt, recordedAddresses);
|
|
@@ -4283,7 +4290,7 @@ async function createDecaneConnect(config) {
|
|
|
4283
4290
|
});
|
|
4284
4291
|
deviceShare = recovered.deviceShare;
|
|
4285
4292
|
joinedViaPasskeySync = recovered.flow === "passkey-sync";
|
|
4286
|
-
if (!await storage.getShare(userId) && isPasskeySupported()) {
|
|
4293
|
+
if (!deferDeviceProtection && !await storage.getShare(userId) && isPasskeySupported()) {
|
|
4287
4294
|
await registerPasskeyAndBackup(userId, jwt, deviceShare);
|
|
4288
4295
|
}
|
|
4289
4296
|
} else {
|
|
@@ -4754,6 +4761,24 @@ async function createDecaneConnect(config) {
|
|
|
4754
4761
|
zeroize(deviceShare);
|
|
4755
4762
|
}
|
|
4756
4763
|
},
|
|
4764
|
+
async deviceProtected() {
|
|
4765
|
+
if (!auth) return false;
|
|
4766
|
+
return await storage.getShare(auth.userId) !== null;
|
|
4767
|
+
},
|
|
4768
|
+
async protectDevice() {
|
|
4769
|
+
if (!auth) throw new ReconnectRequiredError();
|
|
4770
|
+
if (await storage.getShare(auth.userId)) return true;
|
|
4771
|
+
if (!serverAssistedRecovery) throw new NewDeviceError();
|
|
4772
|
+
const tee = new TeeClient(teeUrl);
|
|
4773
|
+
const provisioned = await tee.provisionDeviceShare(auth.jwt);
|
|
4774
|
+
if (!provisioned) throw new NewDeviceError();
|
|
4775
|
+
try {
|
|
4776
|
+
await persistProvisionedShare(auth.userId, auth.jwt, provisioned);
|
|
4777
|
+
} finally {
|
|
4778
|
+
zeroize(provisioned);
|
|
4779
|
+
}
|
|
4780
|
+
return await storage.getShare(auth.userId) !== null;
|
|
4781
|
+
},
|
|
4757
4782
|
async unlock() {
|
|
4758
4783
|
if (!auth) {
|
|
4759
4784
|
const candidate = await passkeyLoginCandidate();
|
|
@@ -7376,6 +7401,7 @@ function DecaneKit({ config, children }) {
|
|
|
7376
7401
|
chains,
|
|
7377
7402
|
rpcUrls,
|
|
7378
7403
|
requireAssertionPerSignature,
|
|
7404
|
+
deferDeviceProtection,
|
|
7379
7405
|
// Recovery callbacks. Forwarding these is what makes recovery
|
|
7380
7406
|
// configurable at all from <DecaneKit>; before, they were silently
|
|
7381
7407
|
// dropped and the headless client was constructed without them.
|
|
@@ -7396,6 +7422,7 @@ function DecaneKit({ config, children }) {
|
|
|
7396
7422
|
promptPin,
|
|
7397
7423
|
promptUnlockPassword,
|
|
7398
7424
|
requireAssertionPerSignature,
|
|
7425
|
+
deferDeviceProtection,
|
|
7399
7426
|
offerRecoveryAtSignup,
|
|
7400
7427
|
onRecoveryShareOffer,
|
|
7401
7428
|
onRecoveryFileReady,
|
|
@@ -7642,6 +7669,16 @@ function DecaneKit({ config, children }) {
|
|
|
7642
7669
|
if (!sdk) throw new Error("Social wallet not connected");
|
|
7643
7670
|
return sdk.addPasskey();
|
|
7644
7671
|
}, []);
|
|
7672
|
+
const socialDeviceProtected = (0, import_react5.useCallback)(async () => {
|
|
7673
|
+
const sdk = socialRef.current;
|
|
7674
|
+
if (!sdk) return false;
|
|
7675
|
+
return sdk.deviceProtected();
|
|
7676
|
+
}, []);
|
|
7677
|
+
const socialProtectDevice = (0, import_react5.useCallback)(async () => {
|
|
7678
|
+
const sdk = socialRef.current;
|
|
7679
|
+
if (!sdk) throw new Error("Social wallet not connected");
|
|
7680
|
+
return sdk.protectDevice();
|
|
7681
|
+
}, []);
|
|
7645
7682
|
const socialSetUnlockPassword = (0, import_react5.useCallback)(async (password) => {
|
|
7646
7683
|
const sdk = socialRef.current;
|
|
7647
7684
|
if (!sdk) throw new Error("Social wallet not connected");
|
|
@@ -7734,6 +7771,8 @@ function DecaneKit({ config, children }) {
|
|
|
7734
7771
|
socialHasRecoveryShare,
|
|
7735
7772
|
socialHasPasskey,
|
|
7736
7773
|
socialAddPasskey,
|
|
7774
|
+
socialDeviceProtected,
|
|
7775
|
+
socialProtectDevice,
|
|
7737
7776
|
socialSetUnlockPassword,
|
|
7738
7777
|
socialCanUnlockWithPassword,
|
|
7739
7778
|
socialForgetLastUser,
|
|
@@ -8044,6 +8083,8 @@ function useSocialWallet() {
|
|
|
8044
8083
|
socialHasRecoveryShare,
|
|
8045
8084
|
socialHasPasskey,
|
|
8046
8085
|
socialAddPasskey,
|
|
8086
|
+
socialDeviceProtected,
|
|
8087
|
+
socialProtectDevice,
|
|
8047
8088
|
socialSetUnlockPassword,
|
|
8048
8089
|
socialCanUnlockWithPassword,
|
|
8049
8090
|
socialForgetLastUser,
|
|
@@ -8146,6 +8187,16 @@ function useSocialWallet() {
|
|
|
8146
8187
|
throw e;
|
|
8147
8188
|
}
|
|
8148
8189
|
}, [socialAddPasskey]);
|
|
8190
|
+
const deviceProtected = (0, import_react6.useCallback)(() => run(() => socialDeviceProtected()), [socialDeviceProtected]);
|
|
8191
|
+
const protectDevice = (0, import_react6.useCallback)(async () => {
|
|
8192
|
+
setError(null);
|
|
8193
|
+
try {
|
|
8194
|
+
return await socialProtectDevice();
|
|
8195
|
+
} catch (e) {
|
|
8196
|
+
setError(e.message);
|
|
8197
|
+
throw e;
|
|
8198
|
+
}
|
|
8199
|
+
}, [socialProtectDevice]);
|
|
8149
8200
|
const setUnlockPassword = (0, import_react6.useCallback)(async (password) => {
|
|
8150
8201
|
setError(null);
|
|
8151
8202
|
try {
|
|
@@ -8234,6 +8285,10 @@ function useSocialWallet() {
|
|
|
8234
8285
|
// Passkey management for THIS device
|
|
8235
8286
|
hasPasskey,
|
|
8236
8287
|
addPasskey,
|
|
8288
|
+
// Deferred protection (deferDeviceProtection): whether this device holds
|
|
8289
|
+
// a wrapped share, and the call that stores one — passkey or password.
|
|
8290
|
+
deviceProtected,
|
|
8291
|
+
protectDevice,
|
|
8237
8292
|
// "Unlock with your password": setUnlockPassword needs a live session;
|
|
8238
8293
|
// canUnlockWithPassword and unlockWithPassword are the signed-out pair that
|
|
8239
8294
|
// open one without an identity provider or a passkey.
|