decane-connect-kit 2.17.0 → 2.19.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 +48 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +48 -25
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -2167,8 +2167,14 @@ var ApiClient = class {
|
|
|
2167
2167
|
return body.transactions;
|
|
2168
2168
|
}
|
|
2169
2169
|
// ── WebAuthn registration (additive to the PRF-only passkey flow) ─────────
|
|
2170
|
-
|
|
2171
|
-
|
|
2170
|
+
/**
|
|
2171
|
+
* `label` is what the user's password manager will show for this passkey.
|
|
2172
|
+
* Without one they see the account's opaque id. Sent, used, and forgotten —
|
|
2173
|
+
* the backend echoes it into the options and stores nothing.
|
|
2174
|
+
*/
|
|
2175
|
+
async getWebAuthnRegisterOptions(jwt, rpId, rpName, label) {
|
|
2176
|
+
const query = `rpId=${encodeURIComponent(rpId)}&rpName=${encodeURIComponent(rpName)}` + (label ? `&label=${encodeURIComponent(label)}` : "");
|
|
2177
|
+
return this._request(`/webauthn/register-options?${query}`, {
|
|
2172
2178
|
headers: { Authorization: `Bearer ${jwt}` }
|
|
2173
2179
|
});
|
|
2174
2180
|
}
|
|
@@ -3052,7 +3058,13 @@ function bufFromBytes(u) {
|
|
|
3052
3058
|
return u.buffer.slice(u.byteOffset, u.byteOffset + u.byteLength);
|
|
3053
3059
|
}
|
|
3054
3060
|
async function registerWebAuthnCredential(opts) {
|
|
3055
|
-
const baseOptions = await opts.api.getWebAuthnRegisterOptions(
|
|
3061
|
+
const baseOptions = await opts.api.getWebAuthnRegisterOptions(
|
|
3062
|
+
opts.jwt,
|
|
3063
|
+
opts.rpId,
|
|
3064
|
+
opts.rpName,
|
|
3065
|
+
opts.label
|
|
3066
|
+
);
|
|
3067
|
+
if (opts.allowDuplicate) delete baseOptions.excludeCredentials;
|
|
3056
3068
|
const optionsJSON = {
|
|
3057
3069
|
...baseOptions,
|
|
3058
3070
|
extensions: {
|
|
@@ -3061,11 +3073,7 @@ async function registerWebAuthnCredential(opts) {
|
|
|
3061
3073
|
}
|
|
3062
3074
|
};
|
|
3063
3075
|
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
|
-
}
|
|
3076
|
+
await opts.api.postWebAuthnRegister(opts.jwt, response, window.location.origin);
|
|
3069
3077
|
return { credentialId, prfOutput };
|
|
3070
3078
|
}
|
|
3071
3079
|
|
|
@@ -3855,21 +3863,24 @@ async function createDecaneConnect(config) {
|
|
|
3855
3863
|
const pin = await config.promptPin();
|
|
3856
3864
|
return deriveKeyFromPin(pin, stored.pinSalt);
|
|
3857
3865
|
}
|
|
3858
|
-
async function registerPasskeyForDevice(userId, jwt, deviceShare) {
|
|
3866
|
+
async function registerPasskeyForDevice(userId, jwt, deviceShare, opts = {}) {
|
|
3859
3867
|
if (!isPasskeySupported()) throw new WebAuthnNotSupportedError();
|
|
3860
3868
|
const prfSalt = generateRandomBytes(32);
|
|
3861
3869
|
let credentialId;
|
|
3862
3870
|
let prfOutput;
|
|
3863
3871
|
try {
|
|
3872
|
+
const label = (await storage.getShare(userId))?.profile?.email;
|
|
3864
3873
|
({ credentialId, prfOutput } = await registerWebAuthnCredential({
|
|
3865
3874
|
api,
|
|
3866
3875
|
jwt,
|
|
3867
3876
|
rpId,
|
|
3868
3877
|
rpName,
|
|
3869
|
-
prfSalt
|
|
3878
|
+
prfSalt,
|
|
3879
|
+
...label ? { label } : {},
|
|
3880
|
+
...opts.allowDuplicate ? { allowDuplicate: true } : {}
|
|
3870
3881
|
}));
|
|
3871
3882
|
} catch (e) {
|
|
3872
|
-
if (e?.name !== "InvalidStateError") throw e;
|
|
3883
|
+
if (e?.name !== "InvalidStateError" || opts.allowDuplicate) throw e;
|
|
3873
3884
|
return adoptExistingPasskey(userId, jwt, deviceShare);
|
|
3874
3885
|
}
|
|
3875
3886
|
let key = prfOutput;
|
|
@@ -3921,7 +3932,12 @@ async function createDecaneConnect(config) {
|
|
|
3921
3932
|
}
|
|
3922
3933
|
async function adoptExistingPasskey(userId, jwt, deviceShare) {
|
|
3923
3934
|
const options = await api.getWebAuthnLoginOptions(userId, rpId);
|
|
3924
|
-
if (!options)
|
|
3935
|
+
if (!options) {
|
|
3936
|
+
console.warn(
|
|
3937
|
+
"decane-connect-kit: this authenticator holds a credential the server does not know; registering a fresh one."
|
|
3938
|
+
);
|
|
3939
|
+
return registerPasskeyForDevice(userId, jwt, deviceShare, { allowDuplicate: true });
|
|
3940
|
+
}
|
|
3925
3941
|
const prfSalt = generateRandomBytes(32);
|
|
3926
3942
|
const { response, prfOutput } = await authenticateWithPasskey({
|
|
3927
3943
|
optionsJSON: options,
|
|
@@ -3966,19 +3982,26 @@ async function createDecaneConnect(config) {
|
|
|
3966
3982
|
);
|
|
3967
3983
|
return;
|
|
3968
3984
|
}
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3985
|
+
try {
|
|
3986
|
+
const { wrappingKey, tokenKey, meta } = await passwordSetup();
|
|
3987
|
+
const iv = generateRandomBytes(12);
|
|
3988
|
+
const wrappedDeviceShare = await aesGcmEncrypt(wrappingKey, deviceShare, iv);
|
|
3989
|
+
await storage.putShare({
|
|
3990
|
+
userId,
|
|
3991
|
+
wrappedDeviceShare,
|
|
3992
|
+
iv,
|
|
3993
|
+
unlockMethod: "password",
|
|
3994
|
+
passwordSalt: meta.passwordSalt ? b64uToBuffer(meta.passwordSalt) : void 0,
|
|
3995
|
+
passwordIterations: meta.passwordIterations,
|
|
3996
|
+
version: 1
|
|
3997
|
+
});
|
|
3998
|
+
await attachUnlockToken(userId, jwt, tokenKey);
|
|
3999
|
+
} catch (e) {
|
|
4000
|
+
console.warn(
|
|
4001
|
+
"decane-connect-kit: no unlock password was set, so this device stored no share. The session works; the next visit will provision again.",
|
|
4002
|
+
e
|
|
4003
|
+
);
|
|
4004
|
+
}
|
|
3982
4005
|
}
|
|
3983
4006
|
async function firstTimeSetup(userId, jwt) {
|
|
3984
4007
|
prewarmAttestation(teeUrl);
|