decane-connect-kit 2.17.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.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
- try {
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,7 +3770,7 @@ 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
3776
  let credentialId;
@@ -3784,10 +3781,11 @@ async function createDecaneConnect(config) {
3784
3781
  jwt,
3785
3782
  rpId,
3786
3783
  rpName,
3787
- prfSalt
3784
+ prfSalt,
3785
+ ...opts.allowDuplicate ? { allowDuplicate: true } : {}
3788
3786
  }));
3789
3787
  } catch (e) {
3790
- if (e?.name !== "InvalidStateError") throw e;
3788
+ if (e?.name !== "InvalidStateError" || opts.allowDuplicate) throw e;
3791
3789
  return adoptExistingPasskey(userId, jwt, deviceShare);
3792
3790
  }
3793
3791
  let key = prfOutput;
@@ -3839,7 +3837,12 @@ async function createDecaneConnect(config) {
3839
3837
  }
3840
3838
  async function adoptExistingPasskey(userId, jwt, deviceShare) {
3841
3839
  const options = await api.getWebAuthnLoginOptions(userId, rpId);
3842
- if (!options) throw new WebAuthnNotSupportedError();
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
+ }
3843
3846
  const prfSalt = generateRandomBytes(32);
3844
3847
  const { response, prfOutput } = await authenticateWithPasskey({
3845
3848
  optionsJSON: options,
@@ -3884,19 +3887,26 @@ async function createDecaneConnect(config) {
3884
3887
  );
3885
3888
  return;
3886
3889
  }
3887
- const { wrappingKey, tokenKey, meta } = await passwordSetup();
3888
- const iv = generateRandomBytes(12);
3889
- const wrappedDeviceShare = await aesGcmEncrypt(wrappingKey, deviceShare, iv);
3890
- await storage.putShare({
3891
- userId,
3892
- wrappedDeviceShare,
3893
- iv,
3894
- unlockMethod: "password",
3895
- passwordSalt: meta.passwordSalt ? b64uToBuffer(meta.passwordSalt) : void 0,
3896
- passwordIterations: meta.passwordIterations,
3897
- version: 1
3898
- });
3899
- await attachUnlockToken(userId, jwt, tokenKey);
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
+ }
3900
3910
  }
3901
3911
  async function firstTimeSetup(userId, jwt) {
3902
3912
  prewarmAttestation(teeUrl);