decane-connect-kit 2.13.0 → 2.14.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 +75 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +75 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3787,13 +3787,21 @@ async function createDecaneConnect(config) {
|
|
|
3787
3787
|
const wrappingKey = await importAesKey(prfOutput);
|
|
3788
3788
|
const iv = generateRandomBytes(12);
|
|
3789
3789
|
const wrappedDeviceShare = await aesGcmEncrypt(wrappingKey, deviceShare, iv);
|
|
3790
|
+
const existing = await storage.getShare(userId);
|
|
3790
3791
|
await storage.putShare({
|
|
3792
|
+
...existing,
|
|
3791
3793
|
userId,
|
|
3792
3794
|
wrappedDeviceShare,
|
|
3793
3795
|
iv,
|
|
3794
3796
|
unlockMethod: "passkey",
|
|
3795
3797
|
credentialId,
|
|
3796
3798
|
prfSalt,
|
|
3799
|
+
pinSalt: void 0,
|
|
3800
|
+
pinIterations: void 0,
|
|
3801
|
+
passwordSalt: void 0,
|
|
3802
|
+
passwordIterations: void 0,
|
|
3803
|
+
wrappedUnlockToken: void 0,
|
|
3804
|
+
unlockTokenIv: void 0,
|
|
3797
3805
|
version: 1
|
|
3798
3806
|
});
|
|
3799
3807
|
try {
|
|
@@ -3819,6 +3827,38 @@ async function createDecaneConnect(config) {
|
|
|
3819
3827
|
);
|
|
3820
3828
|
}
|
|
3821
3829
|
}
|
|
3830
|
+
async function persistProvisionedShare(userId, jwt, deviceShare) {
|
|
3831
|
+
if (isPasskeySupported()) {
|
|
3832
|
+
try {
|
|
3833
|
+
await registerPasskeyForDevice(userId, jwt, deviceShare);
|
|
3834
|
+
return;
|
|
3835
|
+
} catch (e) {
|
|
3836
|
+
console.warn(
|
|
3837
|
+
"decane-connect-kit: passkey provisioning failed; falling back to an unlock password so the share is still stored.",
|
|
3838
|
+
e
|
|
3839
|
+
);
|
|
3840
|
+
}
|
|
3841
|
+
}
|
|
3842
|
+
if (!config.promptUnlockPassword) {
|
|
3843
|
+
console.error(
|
|
3844
|
+
"decane-connect-kit: this device could not store its share \u2014 no passkey and no promptUnlockPassword. The session works, but every visit will re-provision and no unlock will be offered. Configure promptUnlockPassword."
|
|
3845
|
+
);
|
|
3846
|
+
return;
|
|
3847
|
+
}
|
|
3848
|
+
const { wrappingKey, tokenKey, meta } = await passwordSetup();
|
|
3849
|
+
const iv = generateRandomBytes(12);
|
|
3850
|
+
const wrappedDeviceShare = await aesGcmEncrypt(wrappingKey, deviceShare, iv);
|
|
3851
|
+
await storage.putShare({
|
|
3852
|
+
userId,
|
|
3853
|
+
wrappedDeviceShare,
|
|
3854
|
+
iv,
|
|
3855
|
+
unlockMethod: "password",
|
|
3856
|
+
passwordSalt: meta.passwordSalt ? b64uToBuffer(meta.passwordSalt) : void 0,
|
|
3857
|
+
passwordIterations: meta.passwordIterations,
|
|
3858
|
+
version: 1
|
|
3859
|
+
});
|
|
3860
|
+
await attachUnlockToken(userId, jwt, tokenKey);
|
|
3861
|
+
}
|
|
3822
3862
|
async function firstTimeSetup(userId, jwt) {
|
|
3823
3863
|
prewarmAttestation(teeUrl);
|
|
3824
3864
|
let unlockMethod;
|
|
@@ -3983,8 +4023,7 @@ async function createDecaneConnect(config) {
|
|
|
3983
4023
|
const tee = new TeeClient(teeUrl);
|
|
3984
4024
|
const provisioned = await tee.provisionDeviceShare(jwt);
|
|
3985
4025
|
if (provisioned) {
|
|
3986
|
-
await
|
|
3987
|
-
});
|
|
4026
|
+
await persistProvisionedShare(userId, jwt, provisioned);
|
|
3988
4027
|
let addrs;
|
|
3989
4028
|
try {
|
|
3990
4029
|
addrs = await session.start(provisioned, jwt, recordedAddresses);
|
|
@@ -4114,6 +4153,12 @@ async function createDecaneConnect(config) {
|
|
|
4114
4153
|
const candidates = (await storage.listShares()).map(usablePasskeyShare).filter(Boolean);
|
|
4115
4154
|
return candidates.length === 1 ? candidates[0] : null;
|
|
4116
4155
|
}
|
|
4156
|
+
async function canOfferPasskey() {
|
|
4157
|
+
if (!isPasskeySupported()) return false;
|
|
4158
|
+
if (await passkeyDeviceCandidate()) return true;
|
|
4159
|
+
if (!persisted?.userId) return false;
|
|
4160
|
+
return await storage.getShare(persisted.userId) == null;
|
|
4161
|
+
}
|
|
4117
4162
|
async function rememberProfile(userId, profile) {
|
|
4118
4163
|
const incoming = Object.fromEntries(Object.entries(profile).filter(([, v]) => v));
|
|
4119
4164
|
if (!Object.keys(incoming).length) return;
|
|
@@ -4129,6 +4174,28 @@ async function createDecaneConnect(config) {
|
|
|
4129
4174
|
if (!stored.credentialId || !stored.prfSalt) return null;
|
|
4130
4175
|
return stored;
|
|
4131
4176
|
}
|
|
4177
|
+
async function passkeyLoginWithoutShare(userId) {
|
|
4178
|
+
events.emit("wallet-unlocking");
|
|
4179
|
+
const options = await api.getWebAuthnLoginOptions(userId, rpId);
|
|
4180
|
+
if (!options) throw new NewDeviceError();
|
|
4181
|
+
const { response } = await authenticateWithPasskey({
|
|
4182
|
+
optionsJSON: options,
|
|
4183
|
+
prfSalt: generateRandomBytes(32)
|
|
4184
|
+
});
|
|
4185
|
+
const result = await api.postWebAuthnLogin(userId, response, window.location.origin);
|
|
4186
|
+
const addresses = await returningUserUnlock(userId, result.jwt);
|
|
4187
|
+
auth = { jwt: result.jwt, userId, addresses };
|
|
4188
|
+
persisted = { userId, addresses };
|
|
4189
|
+
savePersisted(PERSIST_KEY, persisted);
|
|
4190
|
+
events.emit("connected");
|
|
4191
|
+
persistLiveSession();
|
|
4192
|
+
const stored = await storage.getShare(userId);
|
|
4193
|
+
return {
|
|
4194
|
+
addresses,
|
|
4195
|
+
isNewUser: false,
|
|
4196
|
+
...stored?.profile ? { profile: stored.profile } : {}
|
|
4197
|
+
};
|
|
4198
|
+
}
|
|
4132
4199
|
async function passkeyLogin(stored) {
|
|
4133
4200
|
const userId = stored.userId;
|
|
4134
4201
|
events.emit("wallet-unlocking");
|
|
@@ -4288,12 +4355,15 @@ async function createDecaneConnect(config) {
|
|
|
4288
4355
|
return handleAuthResult(result);
|
|
4289
4356
|
},
|
|
4290
4357
|
async canSignInWithPasskey() {
|
|
4291
|
-
return
|
|
4358
|
+
return canOfferPasskey();
|
|
4292
4359
|
},
|
|
4293
4360
|
async signInWithPasskey() {
|
|
4294
4361
|
const candidate = await passkeyDeviceCandidate();
|
|
4295
|
-
if (
|
|
4296
|
-
|
|
4362
|
+
if (candidate) return passkeyLogin(candidate);
|
|
4363
|
+
if (isPasskeySupported() && persisted?.userId && await storage.getShare(persisted.userId) == null) {
|
|
4364
|
+
return passkeyLoginWithoutShare(persisted.userId);
|
|
4365
|
+
}
|
|
4366
|
+
throw new NewDeviceError();
|
|
4297
4367
|
},
|
|
4298
4368
|
/**
|
|
4299
4369
|
* Turn on "unlock with your password" for this device.
|