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.cjs
CHANGED
|
@@ -3053,6 +3053,7 @@ function bufFromBytes(u) {
|
|
|
3053
3053
|
}
|
|
3054
3054
|
async function registerWebAuthnCredential(opts) {
|
|
3055
3055
|
const baseOptions = await opts.api.getWebAuthnRegisterOptions(opts.jwt, opts.rpId, opts.rpName);
|
|
3056
|
+
if (opts.allowDuplicate) delete baseOptions.excludeCredentials;
|
|
3056
3057
|
const optionsJSON = {
|
|
3057
3058
|
...baseOptions,
|
|
3058
3059
|
extensions: {
|
|
@@ -3061,11 +3062,7 @@ async function registerWebAuthnCredential(opts) {
|
|
|
3061
3062
|
}
|
|
3062
3063
|
};
|
|
3063
3064
|
const { credentialId, prfOutput, response } = await registerPasskey({ optionsJSON });
|
|
3064
|
-
|
|
3065
|
-
await opts.api.postWebAuthnRegister(opts.jwt, response, window.location.origin);
|
|
3066
|
-
} catch (e) {
|
|
3067
|
-
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);
|
|
3068
|
-
}
|
|
3065
|
+
await opts.api.postWebAuthnRegister(opts.jwt, response, window.location.origin);
|
|
3069
3066
|
return { credentialId, prfOutput };
|
|
3070
3067
|
}
|
|
3071
3068
|
|
|
@@ -3855,16 +3852,24 @@ async function createDecaneConnect(config) {
|
|
|
3855
3852
|
const pin = await config.promptPin();
|
|
3856
3853
|
return deriveKeyFromPin(pin, stored.pinSalt);
|
|
3857
3854
|
}
|
|
3858
|
-
async function registerPasskeyForDevice(userId, jwt, deviceShare) {
|
|
3855
|
+
async function registerPasskeyForDevice(userId, jwt, deviceShare, opts = {}) {
|
|
3859
3856
|
if (!isPasskeySupported()) throw new WebAuthnNotSupportedError();
|
|
3860
3857
|
const prfSalt = generateRandomBytes(32);
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3858
|
+
let credentialId;
|
|
3859
|
+
let prfOutput;
|
|
3860
|
+
try {
|
|
3861
|
+
({ credentialId, prfOutput } = await registerWebAuthnCredential({
|
|
3862
|
+
api,
|
|
3863
|
+
jwt,
|
|
3864
|
+
rpId,
|
|
3865
|
+
rpName,
|
|
3866
|
+
prfSalt,
|
|
3867
|
+
...opts.allowDuplicate ? { allowDuplicate: true } : {}
|
|
3868
|
+
}));
|
|
3869
|
+
} catch (e) {
|
|
3870
|
+
if (e?.name !== "InvalidStateError" || opts.allowDuplicate) throw e;
|
|
3871
|
+
return adoptExistingPasskey(userId, jwt, deviceShare);
|
|
3872
|
+
}
|
|
3868
3873
|
let key = prfOutput;
|
|
3869
3874
|
if (!key) {
|
|
3870
3875
|
try {
|
|
@@ -3877,7 +3882,10 @@ async function createDecaneConnect(config) {
|
|
|
3877
3882
|
}
|
|
3878
3883
|
}
|
|
3879
3884
|
if (!key) throw new WebAuthnNotSupportedError();
|
|
3880
|
-
|
|
3885
|
+
await storeShareUnderPasskey(userId, jwt, deviceShare, credentialId, key, prfSalt);
|
|
3886
|
+
}
|
|
3887
|
+
async function storeShareUnderPasskey(userId, jwt, deviceShare, credentialId, prfOutput, prfSalt) {
|
|
3888
|
+
const wrappingKey = await importAesKey(prfOutput);
|
|
3881
3889
|
const iv = generateRandomBytes(12);
|
|
3882
3890
|
const wrappedDeviceShare = await aesGcmEncrypt(wrappingKey, deviceShare, iv);
|
|
3883
3891
|
const existing = await storage.getShare(userId);
|
|
@@ -3909,6 +3917,29 @@ async function createDecaneConnect(config) {
|
|
|
3909
3917
|
console.warn("decane-connect-kit: wrapped share backup upload failed; user can retry from settings.", e);
|
|
3910
3918
|
}
|
|
3911
3919
|
}
|
|
3920
|
+
async function adoptExistingPasskey(userId, jwt, deviceShare) {
|
|
3921
|
+
const options = await api.getWebAuthnLoginOptions(userId, rpId);
|
|
3922
|
+
if (!options) {
|
|
3923
|
+
console.warn(
|
|
3924
|
+
"decane-connect-kit: this authenticator holds a credential the server does not know; registering a fresh one."
|
|
3925
|
+
);
|
|
3926
|
+
return registerPasskeyForDevice(userId, jwt, deviceShare, { allowDuplicate: true });
|
|
3927
|
+
}
|
|
3928
|
+
const prfSalt = generateRandomBytes(32);
|
|
3929
|
+
const { response, prfOutput } = await authenticateWithPasskey({
|
|
3930
|
+
optionsJSON: options,
|
|
3931
|
+
prfSalt
|
|
3932
|
+
});
|
|
3933
|
+
if (!prfOutput) throw new WebAuthnNotSupportedError();
|
|
3934
|
+
await storeShareUnderPasskey(
|
|
3935
|
+
userId,
|
|
3936
|
+
jwt,
|
|
3937
|
+
deviceShare,
|
|
3938
|
+
b64uToBuffer(response.id),
|
|
3939
|
+
prfOutput,
|
|
3940
|
+
prfSalt
|
|
3941
|
+
);
|
|
3942
|
+
}
|
|
3912
3943
|
async function registerPasskeyAndBackup(userId, jwt, deviceShare) {
|
|
3913
3944
|
if (!isPasskeySupported()) return;
|
|
3914
3945
|
try {
|
|
@@ -3938,19 +3969,26 @@ async function createDecaneConnect(config) {
|
|
|
3938
3969
|
);
|
|
3939
3970
|
return;
|
|
3940
3971
|
}
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3972
|
+
try {
|
|
3973
|
+
const { wrappingKey, tokenKey, meta } = await passwordSetup();
|
|
3974
|
+
const iv = generateRandomBytes(12);
|
|
3975
|
+
const wrappedDeviceShare = await aesGcmEncrypt(wrappingKey, deviceShare, iv);
|
|
3976
|
+
await storage.putShare({
|
|
3977
|
+
userId,
|
|
3978
|
+
wrappedDeviceShare,
|
|
3979
|
+
iv,
|
|
3980
|
+
unlockMethod: "password",
|
|
3981
|
+
passwordSalt: meta.passwordSalt ? b64uToBuffer(meta.passwordSalt) : void 0,
|
|
3982
|
+
passwordIterations: meta.passwordIterations,
|
|
3983
|
+
version: 1
|
|
3984
|
+
});
|
|
3985
|
+
await attachUnlockToken(userId, jwt, tokenKey);
|
|
3986
|
+
} catch (e) {
|
|
3987
|
+
console.warn(
|
|
3988
|
+
"decane-connect-kit: no unlock password was set, so this device stored no share. The session works; the next visit will provision again.",
|
|
3989
|
+
e
|
|
3990
|
+
);
|
|
3991
|
+
}
|
|
3954
3992
|
}
|
|
3955
3993
|
async function firstTimeSetup(userId, jwt) {
|
|
3956
3994
|
prewarmAttestation(teeUrl);
|