@learncard/sss-key-manager 0.2.6 → 0.2.7

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.
@@ -27805,6 +27805,7 @@ var EscrowRequestError = class extends Error {
27805
27805
  __name(this, "EscrowRequestError");
27806
27806
  }
27807
27807
  };
27808
+ var isConflictRefusal = /* @__PURE__ */ __name((error) => error instanceof EscrowRequestError && error.status === 409, "isConflictRefusal");
27808
27809
  var DuplicateShareCoordinateError = class extends Error {
27809
27810
  static {
27810
27811
  __name(this, "DuplicateShareCoordinateError");
@@ -28701,21 +28702,34 @@ function createSSSStrategy(config) {
28701
28702
  }
28702
28703
  };
28703
28704
  }, "captureActiveUserGuard");
28704
- const pinHasRecordedWrongAttempts = /* @__PURE__ */ __name((escrowPin) => {
28705
+ const recordedWrongPinAttempts = /* @__PURE__ */ __name((escrowPin) => {
28705
28706
  const status = escrowPin;
28706
- if (status?.state === "none") return false;
28707
- return typeof status?.wrongAttempts !== "number" || status.wrongAttempts > 0;
28708
- }, "pinHasRecordedWrongAttempts");
28709
- const refreshablePinCharges = /* @__PURE__ */ __name((before, after) => {
28707
+ if (status?.state === "none") return 0;
28708
+ const count = status?.wrongAttempts;
28709
+ return typeof count === "number" && Number.isSafeInteger(count) && count >= 0 ? count : void 0;
28710
+ }, "recordedWrongPinAttempts");
28711
+ const attemptCount = /* @__PURE__ */ __name((value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0, "attemptCount");
28712
+ const pinSignInClean = /* @__PURE__ */ __name((observed, flowAttempts) => {
28713
+ const flowSalt = flowAttempts?.pinSalt;
28714
+ const sameGeneration = typeof flowSalt !== "string" || observed.every((status) => {
28715
+ const salt = status?.salt;
28716
+ return typeof salt !== "string" || salt === flowSalt;
28717
+ });
28718
+ const attempts = sameGeneration ? flowAttempts : void 0;
28719
+ const own = attempts ? attemptCount(attempts.ownWrongAttempts) : 0;
28720
+ if (own === void 0) return { clean: false, ownWrongAttempts: 0 };
28721
+ const prior = attempts ? attemptCount(attempts.priorWrongAttempts) : recordedWrongPinAttempts(observed[0]);
28722
+ const clean = prior === 0 && observed.length > 0 && observed.every((status) => recordedWrongPinAttempts(status) === own);
28723
+ return { clean, ownWrongAttempts: own };
28724
+ }, "pinSignInClean");
28725
+ const refreshablePinCharges = /* @__PURE__ */ __name((before, after, ownWrongAttempts) => {
28710
28726
  const pre = before;
28711
28727
  const post = after;
28712
- if (pinHasRecordedWrongAttempts(before) || pinHasRecordedWrongAttempts(after))
28713
- return void 0;
28714
28728
  if (pre?.state !== "enabled" || post?.state !== "enabled") return void 0;
28715
28729
  if (typeof pre.attemptsRemaining !== "number" || typeof post.attemptsRemaining !== "number")
28716
28730
  return void 0;
28717
28731
  if (post.attemptsRemaining !== pre.attemptsRemaining - 1) return void 0;
28718
- return { attemptsRemaining: post.attemptsRemaining, wrongAttempts: 0 };
28732
+ return { attemptsRemaining: post.attemptsRemaining, wrongAttempts: ownWrongAttempts };
28719
28733
  }, "refreshablePinCharges");
28720
28734
  const freshPinMaterial = /* @__PURE__ */ __name(async (pin) => {
28721
28735
  if (!validatePin(pin).ok) return void 0;
@@ -28738,7 +28752,7 @@ function createSSSStrategy(config) {
28738
28752
  primaryDid: released.primaryDid
28739
28753
  };
28740
28754
  }, "adoptReleasedShare");
28741
- const completePinSignInKeep = /* @__PURE__ */ __name(async (params, released, pin, pinBefore) => {
28755
+ const completePinSignInKeep = /* @__PURE__ */ __name(async (params, released, pin, pinBefore, pinAttempts) => {
28742
28756
  const storage2 = guardedStorage();
28743
28757
  const generation = storageGeneration;
28744
28758
  const storageId = activeStorageId;
@@ -28747,7 +28761,11 @@ function createSSSStrategy(config) {
28747
28761
  throw new Error("This recovery request was cancelled.");
28748
28762
  }
28749
28763
  }, "assertActive");
28750
- const { mode, newPinRequired, refreshCharges } = await withShareUpdateLock(
28764
+ const {
28765
+ mode,
28766
+ newPinRequired: newPinRequiredAtRelease,
28767
+ refreshCharges
28768
+ } = await withShareUpdateLock(
28751
28769
  storage2,
28752
28770
  storageId,
28753
28771
  () => withRotationLock(async () => {
@@ -28791,18 +28809,23 @@ function createSSSStrategy(config) {
28791
28809
  current.shareVersion
28792
28810
  );
28793
28811
  pendingIdentityRecovery = void 0;
28812
+ const { clean, ownWrongAttempts } = pinSignInClean(
28813
+ [pinBefore, current.escrowPin],
28814
+ pinAttempts
28815
+ );
28794
28816
  return {
28795
28817
  mode: mode2,
28796
- newPinRequired: pinHasRecordedWrongAttempts(pinBefore) || pinHasRecordedWrongAttempts(current.escrowPin),
28797
- refreshCharges: refreshablePinCharges(pinBefore, current.escrowPin)
28818
+ newPinRequired: !clean,
28819
+ refreshCharges: clean ? refreshablePinCharges(pinBefore, current.escrowPin, ownWrongAttempts) : void 0
28798
28820
  };
28799
28821
  })
28800
28822
  );
28801
28823
  let pinRefreshed = false;
28802
28824
  let pinRefreshFailed = false;
28825
+ let newPinRequired = newPinRequiredAtRelease;
28803
28826
  if (mode === "kept" && !newPinRequired && refreshCharges && params.signDidAuthVp) {
28804
28827
  const pinMaterial = await freshPinMaterial(pin);
28805
- pinRefreshed = pinMaterial !== void 0 && await tryEnrollEscrow(
28828
+ const refresh = pinMaterial === void 0 ? void 0 : await attemptEnrollEscrow(
28806
28829
  params.token,
28807
28830
  params.providerType,
28808
28831
  released.privateKey,
@@ -28818,7 +28841,18 @@ function createSSSStrategy(config) {
28818
28841
  params.options?.recoveryLanguage,
28819
28842
  refreshCharges
28820
28843
  );
28844
+ pinRefreshed = refresh?.enrolled === true;
28821
28845
  pinRefreshFailed = pinMaterial !== void 0 && !pinRefreshed;
28846
+ if (pinRefreshFailed) {
28847
+ const latest = await fetchAuthShareRaw(
28848
+ serverUrl,
28849
+ params.token,
28850
+ params.providerType,
28851
+ void 0,
28852
+ tenantId
28853
+ ).catch(() => void 0);
28854
+ newPinRequired = latest ? !pinSignInClean([pinBefore, latest.escrowPin], pinAttempts).clean : isConflictRefusal(refresh && !refresh.enrolled ? refresh.error : void 0);
28855
+ }
28822
28856
  }
28823
28857
  assertActive();
28824
28858
  return {
@@ -28906,7 +28940,7 @@ function createSSSStrategy(config) {
28906
28940
  pinSignIn: {
28907
28941
  mode: "relinked",
28908
28942
  pinRefreshed: false,
28909
- newPinRequired: pinHasRecordedWrongAttempts(status?.escrowPin)
28943
+ newPinRequired: !pinSignInClean([status?.escrowPin], pending.pinKeep?.pinAttempts).clean
28910
28944
  }
28911
28945
  };
28912
28946
  }, "completePinSignInRelink");
@@ -29826,7 +29860,8 @@ function createSSSStrategy(config) {
29826
29860
  shareVersion: material.shareVersion
29827
29861
  },
29828
29862
  input.pin,
29829
- pinBefore
29863
+ pinBefore,
29864
+ input.pinAttempts
29830
29865
  );
29831
29866
  }
29832
29867
  pendingIdentityRecovery = {
@@ -29837,7 +29872,12 @@ function createSSSStrategy(config) {
29837
29872
  rebindSessionToken: material.rebindSessionToken,
29838
29873
  fromEscrow: true,
29839
29874
  // The PIN itself is not kept across the new-login step.
29840
- ...input.method === "escrow-pin" ? { pinKeep: { shareVersion: material.shareVersion } } : {}
29875
+ ...input.method === "escrow-pin" ? {
29876
+ pinKeep: {
29877
+ shareVersion: material.shareVersion,
29878
+ ...input.pinAttempts ? { pinAttempts: { ...input.pinAttempts } } : {}
29879
+ }
29880
+ } : {}
29841
29881
  };
29842
29882
  if (!token) return { privateKey: privateKey2, did: material.primaryDid };
29843
29883
  return completeIdentityRecoveryWith({