decane-connect-kit 2.16.0 → 2.18.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 +65 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +65 -27
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2971,6 +2971,7 @@ function bufFromBytes(u) {
|
|
|
2971
2971
|
}
|
|
2972
2972
|
async function registerWebAuthnCredential(opts) {
|
|
2973
2973
|
const baseOptions = await opts.api.getWebAuthnRegisterOptions(opts.jwt, opts.rpId, opts.rpName);
|
|
2974
|
+
if (opts.allowDuplicate) delete baseOptions.excludeCredentials;
|
|
2974
2975
|
const optionsJSON = {
|
|
2975
2976
|
...baseOptions,
|
|
2976
2977
|
extensions: {
|
|
@@ -2979,11 +2980,7 @@ async function registerWebAuthnCredential(opts) {
|
|
|
2979
2980
|
}
|
|
2980
2981
|
};
|
|
2981
2982
|
const { credentialId, prfOutput, response } = await registerPasskey({ optionsJSON });
|
|
2982
|
-
|
|
2983
|
-
await opts.api.postWebAuthnRegister(opts.jwt, response, window.location.origin);
|
|
2984
|
-
} catch (e) {
|
|
2985
|
-
console.warn("decane-connect-kit: WebAuthn server-side registration failed; PRF-based unlock still works, but rotateShares()/exportPortableBackup() will not until this is retried.", e);
|
|
2986
|
-
}
|
|
2983
|
+
await opts.api.postWebAuthnRegister(opts.jwt, response, window.location.origin);
|
|
2987
2984
|
return { credentialId, prfOutput };
|
|
2988
2985
|
}
|
|
2989
2986
|
|
|
@@ -3773,16 +3770,24 @@ async function createDecaneConnect(config) {
|
|
|
3773
3770
|
const pin = await config.promptPin();
|
|
3774
3771
|
return deriveKeyFromPin(pin, stored.pinSalt);
|
|
3775
3772
|
}
|
|
3776
|
-
async function registerPasskeyForDevice(userId, jwt, deviceShare) {
|
|
3773
|
+
async function registerPasskeyForDevice(userId, jwt, deviceShare, opts = {}) {
|
|
3777
3774
|
if (!isPasskeySupported()) throw new WebAuthnNotSupportedError();
|
|
3778
3775
|
const prfSalt = generateRandomBytes(32);
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3776
|
+
let credentialId;
|
|
3777
|
+
let prfOutput;
|
|
3778
|
+
try {
|
|
3779
|
+
({ credentialId, prfOutput } = await registerWebAuthnCredential({
|
|
3780
|
+
api,
|
|
3781
|
+
jwt,
|
|
3782
|
+
rpId,
|
|
3783
|
+
rpName,
|
|
3784
|
+
prfSalt,
|
|
3785
|
+
...opts.allowDuplicate ? { allowDuplicate: true } : {}
|
|
3786
|
+
}));
|
|
3787
|
+
} catch (e) {
|
|
3788
|
+
if (e?.name !== "InvalidStateError" || opts.allowDuplicate) throw e;
|
|
3789
|
+
return adoptExistingPasskey(userId, jwt, deviceShare);
|
|
3790
|
+
}
|
|
3786
3791
|
let key = prfOutput;
|
|
3787
3792
|
if (!key) {
|
|
3788
3793
|
try {
|
|
@@ -3795,7 +3800,10 @@ async function createDecaneConnect(config) {
|
|
|
3795
3800
|
}
|
|
3796
3801
|
}
|
|
3797
3802
|
if (!key) throw new WebAuthnNotSupportedError();
|
|
3798
|
-
|
|
3803
|
+
await storeShareUnderPasskey(userId, jwt, deviceShare, credentialId, key, prfSalt);
|
|
3804
|
+
}
|
|
3805
|
+
async function storeShareUnderPasskey(userId, jwt, deviceShare, credentialId, prfOutput, prfSalt) {
|
|
3806
|
+
const wrappingKey = await importAesKey(prfOutput);
|
|
3799
3807
|
const iv = generateRandomBytes(12);
|
|
3800
3808
|
const wrappedDeviceShare = await aesGcmEncrypt(wrappingKey, deviceShare, iv);
|
|
3801
3809
|
const existing = await storage.getShare(userId);
|
|
@@ -3827,6 +3835,29 @@ async function createDecaneConnect(config) {
|
|
|
3827
3835
|
console.warn("decane-connect-kit: wrapped share backup upload failed; user can retry from settings.", e);
|
|
3828
3836
|
}
|
|
3829
3837
|
}
|
|
3838
|
+
async function adoptExistingPasskey(userId, jwt, deviceShare) {
|
|
3839
|
+
const options = await api.getWebAuthnLoginOptions(userId, rpId);
|
|
3840
|
+
if (!options) {
|
|
3841
|
+
console.warn(
|
|
3842
|
+
"decane-connect-kit: this authenticator holds a credential the server does not know; registering a fresh one."
|
|
3843
|
+
);
|
|
3844
|
+
return registerPasskeyForDevice(userId, jwt, deviceShare, { allowDuplicate: true });
|
|
3845
|
+
}
|
|
3846
|
+
const prfSalt = generateRandomBytes(32);
|
|
3847
|
+
const { response, prfOutput } = await authenticateWithPasskey({
|
|
3848
|
+
optionsJSON: options,
|
|
3849
|
+
prfSalt
|
|
3850
|
+
});
|
|
3851
|
+
if (!prfOutput) throw new WebAuthnNotSupportedError();
|
|
3852
|
+
await storeShareUnderPasskey(
|
|
3853
|
+
userId,
|
|
3854
|
+
jwt,
|
|
3855
|
+
deviceShare,
|
|
3856
|
+
b64uToBuffer(response.id),
|
|
3857
|
+
prfOutput,
|
|
3858
|
+
prfSalt
|
|
3859
|
+
);
|
|
3860
|
+
}
|
|
3830
3861
|
async function registerPasskeyAndBackup(userId, jwt, deviceShare) {
|
|
3831
3862
|
if (!isPasskeySupported()) return;
|
|
3832
3863
|
try {
|
|
@@ -3856,19 +3887,26 @@ async function createDecaneConnect(config) {
|
|
|
3856
3887
|
);
|
|
3857
3888
|
return;
|
|
3858
3889
|
}
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3890
|
+
try {
|
|
3891
|
+
const { wrappingKey, tokenKey, meta } = await passwordSetup();
|
|
3892
|
+
const iv = generateRandomBytes(12);
|
|
3893
|
+
const wrappedDeviceShare = await aesGcmEncrypt(wrappingKey, deviceShare, iv);
|
|
3894
|
+
await storage.putShare({
|
|
3895
|
+
userId,
|
|
3896
|
+
wrappedDeviceShare,
|
|
3897
|
+
iv,
|
|
3898
|
+
unlockMethod: "password",
|
|
3899
|
+
passwordSalt: meta.passwordSalt ? b64uToBuffer(meta.passwordSalt) : void 0,
|
|
3900
|
+
passwordIterations: meta.passwordIterations,
|
|
3901
|
+
version: 1
|
|
3902
|
+
});
|
|
3903
|
+
await attachUnlockToken(userId, jwt, tokenKey);
|
|
3904
|
+
} catch (e) {
|
|
3905
|
+
console.warn(
|
|
3906
|
+
"decane-connect-kit: no unlock password was set, so this device stored no share. The session works; the next visit will provision again.",
|
|
3907
|
+
e
|
|
3908
|
+
);
|
|
3909
|
+
}
|
|
3872
3910
|
}
|
|
3873
3911
|
async function firstTimeSetup(userId, jwt) {
|
|
3874
3912
|
prewarmAttestation(teeUrl);
|