decane-connect-kit 2.13.0 → 2.15.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 CHANGED
@@ -2887,7 +2887,7 @@ var PBKDF2_ITERATIONS = 6e5;
2887
2887
  function buf2(u) {
2888
2888
  return u.buffer.slice(u.byteOffset, u.byteOffset + u.byteLength);
2889
2889
  }
2890
- var MIN_PASSWORD_LENGTH = 12;
2890
+ var MIN_PASSWORD_LENGTH = 8;
2891
2891
  var TOO_PREDICTABLE = [
2892
2892
  /^(.)\1+$/,
2893
2893
  // one repeated character
@@ -3869,13 +3869,21 @@ async function createDecaneConnect(config) {
3869
3869
  const wrappingKey = await importAesKey(prfOutput);
3870
3870
  const iv = generateRandomBytes(12);
3871
3871
  const wrappedDeviceShare = await aesGcmEncrypt(wrappingKey, deviceShare, iv);
3872
+ const existing = await storage.getShare(userId);
3872
3873
  await storage.putShare({
3874
+ ...existing,
3873
3875
  userId,
3874
3876
  wrappedDeviceShare,
3875
3877
  iv,
3876
3878
  unlockMethod: "passkey",
3877
3879
  credentialId,
3878
3880
  prfSalt,
3881
+ pinSalt: void 0,
3882
+ pinIterations: void 0,
3883
+ passwordSalt: void 0,
3884
+ passwordIterations: void 0,
3885
+ wrappedUnlockToken: void 0,
3886
+ unlockTokenIv: void 0,
3879
3887
  version: 1
3880
3888
  });
3881
3889
  try {
@@ -3901,6 +3909,38 @@ async function createDecaneConnect(config) {
3901
3909
  );
3902
3910
  }
3903
3911
  }
3912
+ async function persistProvisionedShare(userId, jwt, deviceShare) {
3913
+ if (isPasskeySupported()) {
3914
+ try {
3915
+ await registerPasskeyForDevice(userId, jwt, deviceShare);
3916
+ return;
3917
+ } catch (e) {
3918
+ console.warn(
3919
+ "decane-connect-kit: passkey provisioning failed; falling back to an unlock password so the share is still stored.",
3920
+ e
3921
+ );
3922
+ }
3923
+ }
3924
+ if (!config.promptUnlockPassword) {
3925
+ console.error(
3926
+ "decane-connect-kit: this device could not store its share \u2014 no passkey and no promptUnlockPassword. The session works, but every visit will re-provision and no unlock will be offered. Configure promptUnlockPassword."
3927
+ );
3928
+ return;
3929
+ }
3930
+ const { wrappingKey, tokenKey, meta } = await passwordSetup();
3931
+ const iv = generateRandomBytes(12);
3932
+ const wrappedDeviceShare = await aesGcmEncrypt(wrappingKey, deviceShare, iv);
3933
+ await storage.putShare({
3934
+ userId,
3935
+ wrappedDeviceShare,
3936
+ iv,
3937
+ unlockMethod: "password",
3938
+ passwordSalt: meta.passwordSalt ? b64uToBuffer(meta.passwordSalt) : void 0,
3939
+ passwordIterations: meta.passwordIterations,
3940
+ version: 1
3941
+ });
3942
+ await attachUnlockToken(userId, jwt, tokenKey);
3943
+ }
3904
3944
  async function firstTimeSetup(userId, jwt) {
3905
3945
  prewarmAttestation(teeUrl);
3906
3946
  let unlockMethod;
@@ -4065,8 +4105,7 @@ async function createDecaneConnect(config) {
4065
4105
  const tee = new TeeClient(teeUrl);
4066
4106
  const provisioned = await tee.provisionDeviceShare(jwt);
4067
4107
  if (provisioned) {
4068
- await registerPasskeyAndBackup(userId, jwt, provisioned).catch(() => {
4069
- });
4108
+ await persistProvisionedShare(userId, jwt, provisioned);
4070
4109
  let addrs;
4071
4110
  try {
4072
4111
  addrs = await session.start(provisioned, jwt, recordedAddresses);
@@ -4196,6 +4235,12 @@ async function createDecaneConnect(config) {
4196
4235
  const candidates = (await storage.listShares()).map(usablePasskeyShare).filter(Boolean);
4197
4236
  return candidates.length === 1 ? candidates[0] : null;
4198
4237
  }
4238
+ async function canOfferPasskey() {
4239
+ if (!isPasskeySupported()) return false;
4240
+ if (await passkeyDeviceCandidate()) return true;
4241
+ if (!persisted?.userId) return false;
4242
+ return await storage.getShare(persisted.userId) == null;
4243
+ }
4199
4244
  async function rememberProfile(userId, profile) {
4200
4245
  const incoming = Object.fromEntries(Object.entries(profile).filter(([, v]) => v));
4201
4246
  if (!Object.keys(incoming).length) return;
@@ -4211,6 +4256,28 @@ async function createDecaneConnect(config) {
4211
4256
  if (!stored.credentialId || !stored.prfSalt) return null;
4212
4257
  return stored;
4213
4258
  }
4259
+ async function passkeyLoginWithoutShare(userId) {
4260
+ events.emit("wallet-unlocking");
4261
+ const options = await api.getWebAuthnLoginOptions(userId, rpId);
4262
+ if (!options) throw new NewDeviceError();
4263
+ const { response } = await authenticateWithPasskey({
4264
+ optionsJSON: options,
4265
+ prfSalt: generateRandomBytes(32)
4266
+ });
4267
+ const result = await api.postWebAuthnLogin(userId, response, window.location.origin);
4268
+ const addresses = await returningUserUnlock(userId, result.jwt);
4269
+ auth = { jwt: result.jwt, userId, addresses };
4270
+ persisted = { userId, addresses };
4271
+ savePersisted(PERSIST_KEY, persisted);
4272
+ events.emit("connected");
4273
+ persistLiveSession();
4274
+ const stored = await storage.getShare(userId);
4275
+ return {
4276
+ addresses,
4277
+ isNewUser: false,
4278
+ ...stored?.profile ? { profile: stored.profile } : {}
4279
+ };
4280
+ }
4214
4281
  async function passkeyLogin(stored) {
4215
4282
  const userId = stored.userId;
4216
4283
  events.emit("wallet-unlocking");
@@ -4370,12 +4437,15 @@ async function createDecaneConnect(config) {
4370
4437
  return handleAuthResult(result);
4371
4438
  },
4372
4439
  async canSignInWithPasskey() {
4373
- return await passkeyDeviceCandidate() !== null;
4440
+ return canOfferPasskey();
4374
4441
  },
4375
4442
  async signInWithPasskey() {
4376
4443
  const candidate = await passkeyDeviceCandidate();
4377
- if (!candidate) throw new NewDeviceError();
4378
- return passkeyLogin(candidate);
4444
+ if (candidate) return passkeyLogin(candidate);
4445
+ if (isPasskeySupported() && persisted?.userId && await storage.getShare(persisted.userId) == null) {
4446
+ return passkeyLoginWithoutShare(persisted.userId);
4447
+ }
4448
+ throw new NewDeviceError();
4379
4449
  },
4380
4450
  /**
4381
4451
  * Turn on "unlock with your password" for this device.