decane-connect-kit 2.15.0 → 2.17.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 +60 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +60 -10
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3776,14 +3776,35 @@ async function createDecaneConnect(config) {
|
|
|
3776
3776
|
async function registerPasskeyForDevice(userId, jwt, deviceShare) {
|
|
3777
3777
|
if (!isPasskeySupported()) throw new WebAuthnNotSupportedError();
|
|
3778
3778
|
const prfSalt = generateRandomBytes(32);
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3779
|
+
let credentialId;
|
|
3780
|
+
let prfOutput;
|
|
3781
|
+
try {
|
|
3782
|
+
({ credentialId, prfOutput } = await registerWebAuthnCredential({
|
|
3783
|
+
api,
|
|
3784
|
+
jwt,
|
|
3785
|
+
rpId,
|
|
3786
|
+
rpName,
|
|
3787
|
+
prfSalt
|
|
3788
|
+
}));
|
|
3789
|
+
} catch (e) {
|
|
3790
|
+
if (e?.name !== "InvalidStateError") throw e;
|
|
3791
|
+
return adoptExistingPasskey(userId, jwt, deviceShare);
|
|
3792
|
+
}
|
|
3793
|
+
let key = prfOutput;
|
|
3794
|
+
if (!key) {
|
|
3795
|
+
try {
|
|
3796
|
+
key = await getPrfOutput({ rpId, credentialId, prfSalt });
|
|
3797
|
+
} catch (e) {
|
|
3798
|
+
console.warn(
|
|
3799
|
+
"decane-connect-kit: the new credential did not return a PRF output; it cannot wrap the share.",
|
|
3800
|
+
e
|
|
3801
|
+
);
|
|
3802
|
+
}
|
|
3803
|
+
}
|
|
3804
|
+
if (!key) throw new WebAuthnNotSupportedError();
|
|
3805
|
+
await storeShareUnderPasskey(userId, jwt, deviceShare, credentialId, key, prfSalt);
|
|
3806
|
+
}
|
|
3807
|
+
async function storeShareUnderPasskey(userId, jwt, deviceShare, credentialId, prfOutput, prfSalt) {
|
|
3787
3808
|
const wrappingKey = await importAesKey(prfOutput);
|
|
3788
3809
|
const iv = generateRandomBytes(12);
|
|
3789
3810
|
const wrappedDeviceShare = await aesGcmEncrypt(wrappingKey, deviceShare, iv);
|
|
@@ -3816,6 +3837,24 @@ async function createDecaneConnect(config) {
|
|
|
3816
3837
|
console.warn("decane-connect-kit: wrapped share backup upload failed; user can retry from settings.", e);
|
|
3817
3838
|
}
|
|
3818
3839
|
}
|
|
3840
|
+
async function adoptExistingPasskey(userId, jwt, deviceShare) {
|
|
3841
|
+
const options = await api.getWebAuthnLoginOptions(userId, rpId);
|
|
3842
|
+
if (!options) throw new WebAuthnNotSupportedError();
|
|
3843
|
+
const prfSalt = generateRandomBytes(32);
|
|
3844
|
+
const { response, prfOutput } = await authenticateWithPasskey({
|
|
3845
|
+
optionsJSON: options,
|
|
3846
|
+
prfSalt
|
|
3847
|
+
});
|
|
3848
|
+
if (!prfOutput) throw new WebAuthnNotSupportedError();
|
|
3849
|
+
await storeShareUnderPasskey(
|
|
3850
|
+
userId,
|
|
3851
|
+
jwt,
|
|
3852
|
+
deviceShare,
|
|
3853
|
+
b64uToBuffer(response.id),
|
|
3854
|
+
prfOutput,
|
|
3855
|
+
prfSalt
|
|
3856
|
+
);
|
|
3857
|
+
}
|
|
3819
3858
|
async function registerPasskeyAndBackup(userId, jwt, deviceShare) {
|
|
3820
3859
|
if (!isPasskeySupported()) return;
|
|
3821
3860
|
try {
|
|
@@ -4495,8 +4534,19 @@ async function createDecaneConnect(config) {
|
|
|
4495
4534
|
if (!auth) throw new ReconnectRequiredError();
|
|
4496
4535
|
if (!isPasskeySupported()) throw new WebAuthnNotSupportedError();
|
|
4497
4536
|
const stored = await storage.getShare(auth.userId);
|
|
4498
|
-
if (
|
|
4499
|
-
if (stored
|
|
4537
|
+
if (stored?.unlockMethod === "passkey") return;
|
|
4538
|
+
if (!stored) {
|
|
4539
|
+
if (!serverAssistedRecovery) throw new NewDeviceError();
|
|
4540
|
+
const tee = new TeeClient(teeUrl);
|
|
4541
|
+
const provisioned = await tee.provisionDeviceShare(auth.jwt);
|
|
4542
|
+
if (!provisioned) throw new NewDeviceError();
|
|
4543
|
+
try {
|
|
4544
|
+
await registerPasskeyForDevice(auth.userId, auth.jwt, provisioned);
|
|
4545
|
+
} finally {
|
|
4546
|
+
zeroize(provisioned);
|
|
4547
|
+
}
|
|
4548
|
+
return;
|
|
4549
|
+
}
|
|
4500
4550
|
let deviceShare;
|
|
4501
4551
|
try {
|
|
4502
4552
|
deviceShare = await aesGcmDecrypt(
|