@witnium-tech/witniumchain 0.12.0 → 0.12.1

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.d.mts CHANGED
@@ -12050,6 +12050,7 @@ declare class WitniumchainClient {
12050
12050
  private refreshInFlight;
12051
12051
  private accessTokenFromSession;
12052
12052
  private sessionExchangeInFlight;
12053
+ private credentialGeneration;
12053
12054
  /** Subscriptions / billing helpers. See {@link Subscriptions}. */
12054
12055
  readonly subscriptions: Subscriptions;
12055
12056
  /** Delegated-key namespace including the one-call {@link DelegatedKeys.provision} flow. */
@@ -12161,8 +12162,9 @@ declare class WitniumchainClient {
12161
12162
  enrollTotp(): Promise<TotpEnrollResponse>;
12162
12163
  /**
12163
12164
  * Confirming bumps the user's security epoch server-side, which ends every
12164
- * session-minted access token. A cached one is dropped so the next bearer
12165
- * call mints a fresh token.
12165
+ * session-minted access token and OAuth artifact of the user. Cached and
12166
+ * in-flight tokens are dropped so the next bearer call mints a fresh one
12167
+ * from the (surviving) session.
12166
12168
  */
12167
12169
  confirmTotp(body: TotpConfirmRequest): Promise<TotpConfirmResponse>;
12168
12170
  /** Step-up: 403 `mfa_step_up_failed` on a wrong proof, 429 `mfa_rate_limited`. */
@@ -12280,6 +12282,12 @@ declare class WitniumchainClient {
12280
12282
  * valid session.
12281
12283
  */
12282
12284
  exchangeSessionForToken(): Promise<OAuthTokenSnapshot>;
12285
+ private assertCredentialGeneration;
12286
+ /**
12287
+ * Forget every cached credential and orphan in-flight mints/refreshes, so
12288
+ * none of them can store a token afterwards.
12289
+ */
12290
+ private invalidateCredentials;
12283
12291
  /**
12284
12292
  * Best-effort transparent mint for the `BearerJWT` path: if no access token
12285
12293
  * is cached, try the session exchange (single-flight). Swallows failure so
package/dist/index.d.ts CHANGED
@@ -12050,6 +12050,7 @@ declare class WitniumchainClient {
12050
12050
  private refreshInFlight;
12051
12051
  private accessTokenFromSession;
12052
12052
  private sessionExchangeInFlight;
12053
+ private credentialGeneration;
12053
12054
  /** Subscriptions / billing helpers. See {@link Subscriptions}. */
12054
12055
  readonly subscriptions: Subscriptions;
12055
12056
  /** Delegated-key namespace including the one-call {@link DelegatedKeys.provision} flow. */
@@ -12161,8 +12162,9 @@ declare class WitniumchainClient {
12161
12162
  enrollTotp(): Promise<TotpEnrollResponse>;
12162
12163
  /**
12163
12164
  * Confirming bumps the user's security epoch server-side, which ends every
12164
- * session-minted access token. A cached one is dropped so the next bearer
12165
- * call mints a fresh token.
12165
+ * session-minted access token and OAuth artifact of the user. Cached and
12166
+ * in-flight tokens are dropped so the next bearer call mints a fresh one
12167
+ * from the (surviving) session.
12166
12168
  */
12167
12169
  confirmTotp(body: TotpConfirmRequest): Promise<TotpConfirmResponse>;
12168
12170
  /** Step-up: 403 `mfa_step_up_failed` on a wrong proof, 429 `mfa_rate_limited`. */
@@ -12280,6 +12282,12 @@ declare class WitniumchainClient {
12280
12282
  * valid session.
12281
12283
  */
12282
12284
  exchangeSessionForToken(): Promise<OAuthTokenSnapshot>;
12285
+ private assertCredentialGeneration;
12286
+ /**
12287
+ * Forget every cached credential and orphan in-flight mints/refreshes, so
12288
+ * none of them can store a token afterwards.
12289
+ */
12290
+ private invalidateCredentials;
12283
12291
  /**
12284
12292
  * Best-effort transparent mint for the `BearerJWT` path: if no access token
12285
12293
  * is cached, try the session exchange (single-flight). Swallows failure so
package/dist/index.js CHANGED
@@ -121,6 +121,11 @@ var WitniumchainClient = class {
121
121
  // first BearerJWT call (and any concurrent fan-out) awaits one in-flight
122
122
  // mint rather than minting N tokens.
123
123
  sessionExchangeInFlight;
124
+ // Bumped whenever the cached credentials stop being valid (sign-out, the
125
+ // TOTP-confirm epoch bump). A mint or refresh that started under an older
126
+ // generation must not store its result: it could be a previous user's
127
+ // token, or one the server has already ended.
128
+ credentialGeneration = 0;
124
129
  /** Subscriptions / billing helpers. See {@link Subscriptions}. */
125
130
  subscriptions;
126
131
  /** Delegated-key namespace including the one-call {@link DelegatedKeys.provision} flow. */
@@ -502,18 +507,16 @@ var WitniumchainClient = class {
502
507
  }
503
508
  /**
504
509
  * Confirming bumps the user's security epoch server-side, which ends every
505
- * session-minted access token. A cached one is dropped so the next bearer
506
- * call mints a fresh token.
510
+ * session-minted access token and OAuth artifact of the user. Cached and
511
+ * in-flight tokens are dropped so the next bearer call mints a fresh one
512
+ * from the (surviving) session.
507
513
  */
508
514
  async confirmTotp(body) {
509
515
  const res = await this.req("POST", "/v1/account/mfa/totp/confirm", {
510
516
  auth: "SessionCookie",
511
517
  body
512
518
  });
513
- if (this.accessTokenFromSession) {
514
- this.accessToken = void 0;
515
- this.accessTokenFromSession = false;
516
- }
519
+ this.invalidateCredentials();
517
520
  return res;
518
521
  }
519
522
  /** Step-up: 403 `mfa_step_up_failed` on a wrong proof, 429 `mfa_rate_limited`. */
@@ -791,14 +794,16 @@ var WitniumchainClient = class {
791
794
  */
792
795
  async refreshAccessToken() {
793
796
  if (this.refreshInFlight) return this.refreshInFlight;
794
- this.refreshInFlight = (async () => {
797
+ let flight = void 0;
798
+ flight = (async () => {
795
799
  try {
796
800
  return await this.refreshAccessTokenInternal();
797
801
  } finally {
798
- this.refreshInFlight = void 0;
802
+ if (this.refreshInFlight === flight) this.refreshInFlight = void 0;
799
803
  }
800
804
  })();
801
- return this.refreshInFlight;
805
+ this.refreshInFlight = flight;
806
+ return flight;
802
807
  }
803
808
  /**
804
809
  * Exchange the logged-in session (the `wac_session` cookie) for a short-lived
@@ -823,9 +828,12 @@ var WitniumchainClient = class {
823
828
  */
824
829
  async exchangeSessionForToken() {
825
830
  if (this.sessionExchangeInFlight) return this.sessionExchangeInFlight;
826
- this.sessionExchangeInFlight = (async () => {
831
+ const generation = this.credentialGeneration;
832
+ let flight = void 0;
833
+ flight = (async () => {
827
834
  try {
828
835
  const res = await this.req("POST", "/v1/auth/access-token", { auth: "SessionCookie" });
836
+ this.assertCredentialGeneration(generation);
829
837
  this.accessToken = res.accessToken;
830
838
  this.accessTokenFromSession = true;
831
839
  this.refreshToken = void 0;
@@ -835,10 +843,32 @@ var WitniumchainClient = class {
835
843
  expiresAt: Math.floor(new Date(res.expiresAt).getTime() / 1e3)
836
844
  };
837
845
  } finally {
838
- this.sessionExchangeInFlight = void 0;
846
+ if (this.sessionExchangeInFlight === flight) this.sessionExchangeInFlight = void 0;
839
847
  }
840
848
  })();
841
- return this.sessionExchangeInFlight;
849
+ this.sessionExchangeInFlight = flight;
850
+ return flight;
851
+ }
852
+ assertCredentialGeneration(generation) {
853
+ if (generation !== this.credentialGeneration) {
854
+ throw new WitniumchainApiError({
855
+ status: 0,
856
+ message: "WitniumchainClient: the session changed (sign-out or MFA confirmation) while a token request was in flight; its token was discarded."
857
+ });
858
+ }
859
+ }
860
+ /**
861
+ * Forget every cached credential and orphan in-flight mints/refreshes, so
862
+ * none of them can store a token afterwards.
863
+ */
864
+ invalidateCredentials() {
865
+ this.credentialGeneration++;
866
+ this.accessToken = void 0;
867
+ this.accessTokenFromSession = false;
868
+ this.refreshToken = void 0;
869
+ this.hasRefreshCookie = false;
870
+ this.sessionExchangeInFlight = void 0;
871
+ this.refreshInFlight = void 0;
842
872
  }
843
873
  /**
844
874
  * Best-effort transparent mint for the `BearerJWT` path: if no access token
@@ -850,7 +880,8 @@ var WitniumchainClient = class {
850
880
  if (this.accessToken) return;
851
881
  try {
852
882
  await this.exchangeSessionForToken();
853
- } catch {
883
+ } catch (err) {
884
+ if (!(err instanceof WitniumchainApiError && err.status === 401)) throw err;
854
885
  }
855
886
  }
856
887
  /**
@@ -891,10 +922,7 @@ var WitniumchainClient = class {
891
922
  * dropped — which it is, here.
892
923
  */
893
924
  signOut() {
894
- this.accessToken = void 0;
895
- this.accessTokenFromSession = false;
896
- this.refreshToken = void 0;
897
- this.hasRefreshCookie = false;
925
+ this.invalidateCredentials();
898
926
  this.pendingLogins.clear();
899
927
  }
900
928
  // ────────────────────────────────────────────────────────────────────────
@@ -1014,6 +1042,7 @@ var WitniumchainClient = class {
1014
1042
  message: "WitniumchainClient: no refresh credential available. Call `completeOAuthLogin` first \u2014 the server delivers the refresh token either in the response body (Node) or as an HttpOnly cookie (browser)."
1015
1043
  });
1016
1044
  }
1045
+ const generation = this.credentialGeneration;
1017
1046
  const discovery = await this.fetchDiscovery();
1018
1047
  const form = new URLSearchParams();
1019
1048
  form.set("grant_type", "refresh_token");
@@ -1021,15 +1050,18 @@ var WitniumchainClient = class {
1021
1050
  if (this.refreshToken) {
1022
1051
  form.set("refresh_token", this.refreshToken);
1023
1052
  }
1053
+ let tokens;
1024
1054
  try {
1025
- const tokens = await this.postTokenEndpoint(discovery.token_endpoint, form);
1026
- return this.persistTokenResponse(tokens);
1055
+ tokens = await this.postTokenEndpoint(discovery.token_endpoint, form);
1027
1056
  } catch (err) {
1057
+ this.assertCredentialGeneration(generation);
1028
1058
  this.accessToken = void 0;
1029
1059
  this.refreshToken = void 0;
1030
1060
  this.hasRefreshCookie = false;
1031
1061
  throw err;
1032
1062
  }
1063
+ this.assertCredentialGeneration(generation);
1064
+ return this.persistTokenResponse(tokens);
1033
1065
  }
1034
1066
  // ────────────────────────────────────────────────────────────────────────
1035
1067
  // Internal: fetch wrapper that maps non-2xx → WitniumchainApiError