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.cjs
CHANGED
|
@@ -3869,13 +3869,21 @@ async function createDecaneConnect(config) {
|
|
|
3869
3869
|
const wrappingKey = await importAesKey(prfOutput);
|
|
3870
3870
|
const iv = generateRandomBytes(12);
|
|
3871
3871
|
const wrappedDeviceShare = await aesGcmEncrypt(wrappingKey, deviceShare, iv);
|
|
3872
|
+
const existing = await storage.getShare(userId);
|
|
3872
3873
|
await storage.putShare({
|
|
3874
|
+
...existing,
|
|
3873
3875
|
userId,
|
|
3874
3876
|
wrappedDeviceShare,
|
|
3875
3877
|
iv,
|
|
3876
3878
|
unlockMethod: "passkey",
|
|
3877
3879
|
credentialId,
|
|
3878
3880
|
prfSalt,
|
|
3881
|
+
pinSalt: void 0,
|
|
3882
|
+
pinIterations: void 0,
|
|
3883
|
+
passwordSalt: void 0,
|
|
3884
|
+
passwordIterations: void 0,
|
|
3885
|
+
wrappedUnlockToken: void 0,
|
|
3886
|
+
unlockTokenIv: void 0,
|
|
3879
3887
|
version: 1
|
|
3880
3888
|
});
|
|
3881
3889
|
try {
|
|
@@ -3901,6 +3909,38 @@ async function createDecaneConnect(config) {
|
|
|
3901
3909
|
);
|
|
3902
3910
|
}
|
|
3903
3911
|
}
|
|
3912
|
+
async function persistProvisionedShare(userId, jwt, deviceShare) {
|
|
3913
|
+
if (isPasskeySupported()) {
|
|
3914
|
+
try {
|
|
3915
|
+
await registerPasskeyForDevice(userId, jwt, deviceShare);
|
|
3916
|
+
return;
|
|
3917
|
+
} catch (e) {
|
|
3918
|
+
console.warn(
|
|
3919
|
+
"decane-connect-kit: passkey provisioning failed; falling back to an unlock password so the share is still stored.",
|
|
3920
|
+
e
|
|
3921
|
+
);
|
|
3922
|
+
}
|
|
3923
|
+
}
|
|
3924
|
+
if (!config.promptUnlockPassword) {
|
|
3925
|
+
console.error(
|
|
3926
|
+
"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."
|
|
3927
|
+
);
|
|
3928
|
+
return;
|
|
3929
|
+
}
|
|
3930
|
+
const { wrappingKey, tokenKey, meta } = await passwordSetup();
|
|
3931
|
+
const iv = generateRandomBytes(12);
|
|
3932
|
+
const wrappedDeviceShare = await aesGcmEncrypt(wrappingKey, deviceShare, iv);
|
|
3933
|
+
await storage.putShare({
|
|
3934
|
+
userId,
|
|
3935
|
+
wrappedDeviceShare,
|
|
3936
|
+
iv,
|
|
3937
|
+
unlockMethod: "password",
|
|
3938
|
+
passwordSalt: meta.passwordSalt ? b64uToBuffer(meta.passwordSalt) : void 0,
|
|
3939
|
+
passwordIterations: meta.passwordIterations,
|
|
3940
|
+
version: 1
|
|
3941
|
+
});
|
|
3942
|
+
await attachUnlockToken(userId, jwt, tokenKey);
|
|
3943
|
+
}
|
|
3904
3944
|
async function firstTimeSetup(userId, jwt) {
|
|
3905
3945
|
prewarmAttestation(teeUrl);
|
|
3906
3946
|
let unlockMethod;
|
|
@@ -4065,8 +4105,7 @@ async function createDecaneConnect(config) {
|
|
|
4065
4105
|
const tee = new TeeClient(teeUrl);
|
|
4066
4106
|
const provisioned = await tee.provisionDeviceShare(jwt);
|
|
4067
4107
|
if (provisioned) {
|
|
4068
|
-
await
|
|
4069
|
-
});
|
|
4108
|
+
await persistProvisionedShare(userId, jwt, provisioned);
|
|
4070
4109
|
let addrs;
|
|
4071
4110
|
try {
|
|
4072
4111
|
addrs = await session.start(provisioned, jwt, recordedAddresses);
|
|
@@ -4196,6 +4235,12 @@ async function createDecaneConnect(config) {
|
|
|
4196
4235
|
const candidates = (await storage.listShares()).map(usablePasskeyShare).filter(Boolean);
|
|
4197
4236
|
return candidates.length === 1 ? candidates[0] : null;
|
|
4198
4237
|
}
|
|
4238
|
+
async function canOfferPasskey() {
|
|
4239
|
+
if (!isPasskeySupported()) return false;
|
|
4240
|
+
if (await passkeyDeviceCandidate()) return true;
|
|
4241
|
+
if (!persisted?.userId) return false;
|
|
4242
|
+
return await storage.getShare(persisted.userId) == null;
|
|
4243
|
+
}
|
|
4199
4244
|
async function rememberProfile(userId, profile) {
|
|
4200
4245
|
const incoming = Object.fromEntries(Object.entries(profile).filter(([, v]) => v));
|
|
4201
4246
|
if (!Object.keys(incoming).length) return;
|
|
@@ -4211,6 +4256,28 @@ async function createDecaneConnect(config) {
|
|
|
4211
4256
|
if (!stored.credentialId || !stored.prfSalt) return null;
|
|
4212
4257
|
return stored;
|
|
4213
4258
|
}
|
|
4259
|
+
async function passkeyLoginWithoutShare(userId) {
|
|
4260
|
+
events.emit("wallet-unlocking");
|
|
4261
|
+
const options = await api.getWebAuthnLoginOptions(userId, rpId);
|
|
4262
|
+
if (!options) throw new NewDeviceError();
|
|
4263
|
+
const { response } = await authenticateWithPasskey({
|
|
4264
|
+
optionsJSON: options,
|
|
4265
|
+
prfSalt: generateRandomBytes(32)
|
|
4266
|
+
});
|
|
4267
|
+
const result = await api.postWebAuthnLogin(userId, response, window.location.origin);
|
|
4268
|
+
const addresses = await returningUserUnlock(userId, result.jwt);
|
|
4269
|
+
auth = { jwt: result.jwt, userId, addresses };
|
|
4270
|
+
persisted = { userId, addresses };
|
|
4271
|
+
savePersisted(PERSIST_KEY, persisted);
|
|
4272
|
+
events.emit("connected");
|
|
4273
|
+
persistLiveSession();
|
|
4274
|
+
const stored = await storage.getShare(userId);
|
|
4275
|
+
return {
|
|
4276
|
+
addresses,
|
|
4277
|
+
isNewUser: false,
|
|
4278
|
+
...stored?.profile ? { profile: stored.profile } : {}
|
|
4279
|
+
};
|
|
4280
|
+
}
|
|
4214
4281
|
async function passkeyLogin(stored) {
|
|
4215
4282
|
const userId = stored.userId;
|
|
4216
4283
|
events.emit("wallet-unlocking");
|
|
@@ -4370,12 +4437,15 @@ async function createDecaneConnect(config) {
|
|
|
4370
4437
|
return handleAuthResult(result);
|
|
4371
4438
|
},
|
|
4372
4439
|
async canSignInWithPasskey() {
|
|
4373
|
-
return
|
|
4440
|
+
return canOfferPasskey();
|
|
4374
4441
|
},
|
|
4375
4442
|
async signInWithPasskey() {
|
|
4376
4443
|
const candidate = await passkeyDeviceCandidate();
|
|
4377
|
-
if (
|
|
4378
|
-
|
|
4444
|
+
if (candidate) return passkeyLogin(candidate);
|
|
4445
|
+
if (isPasskeySupported() && persisted?.userId && await storage.getShare(persisted.userId) == null) {
|
|
4446
|
+
return passkeyLoginWithoutShare(persisted.userId);
|
|
4447
|
+
}
|
|
4448
|
+
throw new NewDeviceError();
|
|
4379
4449
|
},
|
|
4380
4450
|
/**
|
|
4381
4451
|
* Turn on "unlock with your password" for this device.
|