@opexa/portal-sdk 0.59.95 → 0.59.97

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
@@ -4709,6 +4709,20 @@ async function getFingerPrint() {
4709
4709
  return null;
4710
4710
  }
4711
4711
  }
4712
+ function isWebPlatform() {
4713
+ try {
4714
+ return core.Capacitor.getPlatform() === "web";
4715
+ } catch {
4716
+ return true;
4717
+ }
4718
+ }
4719
+ function isIOSPlatform() {
4720
+ try {
4721
+ return core.Capacitor.getPlatform() === "ios";
4722
+ } catch {
4723
+ return false;
4724
+ }
4725
+ }
4712
4726
 
4713
4727
  // src/utils/status-code-to-operation-error.ts
4714
4728
  function statusCodeToOperationError(value, message) {
@@ -4729,13 +4743,6 @@ var ERROR_MAP = {
4729
4743
  };
4730
4744
 
4731
4745
  // src/services/auth.service.ts
4732
- function isWebPlatform() {
4733
- try {
4734
- return core.Capacitor.getPlatform() === "web";
4735
- } catch {
4736
- return false;
4737
- }
4738
- }
4739
4746
  var AuthService = class {
4740
4747
  url;
4741
4748
  options;
@@ -4791,12 +4798,15 @@ var AuthService = class {
4791
4798
  try {
4792
4799
  const isWeb = isWebPlatform();
4793
4800
  const queryParam = isWeb && version === 4 ? "?type=web" : "";
4794
- const res = await fetch(`${this.url}${version !== 1 ? `/v${version}/sessions` : "/sessions"}${queryParam}`, {
4795
- method: "POST",
4796
- headers,
4797
- body: JSON.stringify(input),
4798
- ...version === 4 && { credentials: "include" }
4799
- });
4801
+ const res = await fetch(
4802
+ `${this.url}${version !== 1 ? `/v${version}/sessions` : "/sessions"}${queryParam}`,
4803
+ {
4804
+ method: "POST",
4805
+ headers,
4806
+ body: JSON.stringify(input),
4807
+ ...version === 4 && { credentials: "include" }
4808
+ }
4809
+ );
4800
4810
  const body = await res.json();
4801
4811
  if (res.ok) {
4802
4812
  return {
@@ -4879,7 +4889,7 @@ var AuthService = class {
4879
4889
  }
4880
4890
  async refreshSession(refreshToken, version = 1) {
4881
4891
  const headers = new Headers(this.headers);
4882
- if (version !== 4) {
4892
+ if (version !== 4 || refreshToken) {
4883
4893
  headers.append("Authorization", `Bearer ${refreshToken}`);
4884
4894
  }
4885
4895
  try {
@@ -4993,11 +5003,14 @@ var AuthService = class {
4993
5003
  }
4994
5004
  }
4995
5005
  try {
4996
- const res = await fetch(`${this.url}${version !== 1 ? `/v${version}/otps` : "/otps"}`, {
4997
- method: "POST",
4998
- headers: this.headers,
4999
- body: JSON.stringify(input)
5000
- });
5006
+ const res = await fetch(
5007
+ `${this.url}${version !== 1 ? `/v${version}/otps` : "/otps"}`,
5008
+ {
5009
+ method: "POST",
5010
+ headers: this.headers,
5011
+ body: JSON.stringify(input)
5012
+ }
5013
+ );
5001
5014
  if (res.status === 403) {
5002
5015
  const data = await res.json();
5003
5016
  const code = getErrorCode(data.message);
@@ -7457,6 +7470,48 @@ function pollable(func, config) {
7457
7470
  };
7458
7471
  }
7459
7472
 
7473
+ // src/sdk/refresh-token-store.ts
7474
+ var PreferencesRefreshTokenStore = class {
7475
+ constructor(key, logger) {
7476
+ this.key = key;
7477
+ this.logger = logger;
7478
+ }
7479
+ async preferences() {
7480
+ const { Preferences } = await import('@capacitor/preferences');
7481
+ return Preferences;
7482
+ }
7483
+ async get() {
7484
+ if (!isIOSPlatform()) return null;
7485
+ try {
7486
+ const { value } = await (await this.preferences()).get({ key: this.key });
7487
+ return value ?? null;
7488
+ } catch (err) {
7489
+ this.logger.error(`Failed to read the refresh token: ${err}`);
7490
+ return null;
7491
+ }
7492
+ }
7493
+ async set(token) {
7494
+ if (!isIOSPlatform()) return;
7495
+ try {
7496
+ await (await this.preferences()).set({ key: this.key, value: token });
7497
+ } catch (err) {
7498
+ this.logger.error(`Failed to persist the refresh token: ${err}`);
7499
+ }
7500
+ }
7501
+ async remove() {
7502
+ if (!isIOSPlatform()) return;
7503
+ try {
7504
+ await (await this.preferences()).remove({ key: this.key });
7505
+ } catch (err) {
7506
+ this.logger.error(`Failed to clear the refresh token: ${err}`);
7507
+ }
7508
+ }
7509
+ };
7510
+ function createRefreshTokenStore(logger, sessionPrefix) {
7511
+ const key = sessionPrefix ? `${sessionPrefix}/session/refresh-token` : "session/refresh-token";
7512
+ return new PreferencesRefreshTokenStore(key, logger);
7513
+ }
7514
+
7460
7515
  // src/sdk/session-manager.ts
7461
7516
  var SessionManager = class {
7462
7517
  logger;
@@ -7466,17 +7521,23 @@ var SessionManager = class {
7466
7521
  walletService;
7467
7522
  _refreshing = false;
7468
7523
  /*
7469
- * v4 access tokens are never persisted (localStorage/cookie) so that an
7470
- * XSS-readable storage layer never holds a live access token. They only
7471
- * live in memory and get re-minted from the HttpOnly refresh cookie.
7524
+ * v4 access tokens live in memory only, so an XSS-readable storage layer
7525
+ * never holds a live one. Re-minted from the HttpOnly cookie, or from
7526
+ * `refreshTokenStore` on iOS.
7472
7527
  */
7473
7528
  v4AccessToken = null;
7474
7529
  v4AccessTokenExpiresAt = null;
7475
7530
  v4RefreshPromise = null;
7531
+ invalidReason = null;
7532
+ refreshTokenStore;
7476
7533
  constructor(config) {
7477
7534
  this.authService = config.authService;
7478
7535
  this.walletService = config.walletService;
7479
7536
  this.logger = config.logger;
7537
+ this.refreshTokenStore = createRefreshTokenStore(
7538
+ config.logger,
7539
+ config.sessionPrefix
7540
+ );
7480
7541
  if (config.sessionPrefix) {
7481
7542
  this.storageKey = `${config.sessionPrefix}/${this.storageKey}`;
7482
7543
  this.platformStorageKey = `${config.sessionPrefix}/${this.platformStorageKey}`;
@@ -7488,7 +7549,7 @@ var SessionManager = class {
7488
7549
  set refreshing(value) {
7489
7550
  this._refreshing = value;
7490
7551
  }
7491
- persist(data, version) {
7552
+ async persist(data, version) {
7492
7553
  const now = /* @__PURE__ */ new Date();
7493
7554
  if (version === 4) {
7494
7555
  if (!data.session || !data.accessToken) {
@@ -7499,12 +7560,17 @@ var SessionManager = class {
7499
7560
  }
7500
7561
  this.v4AccessToken = data.accessToken;
7501
7562
  this.v4AccessTokenExpiresAt = addMinutes(now, 4).getTime();
7563
+ this.invalidReason = null;
7564
+ if (data.refreshToken) {
7565
+ await this.refreshTokenStore.set(data.refreshToken);
7566
+ }
7502
7567
  localStorage.setItem(
7503
7568
  this.storageKey,
7504
7569
  JSON.stringify({ version, session: data.session })
7505
7570
  );
7506
7571
  return true;
7507
7572
  }
7573
+ this.invalidReason = null;
7508
7574
  localStorage.setItem(
7509
7575
  this.storageKey,
7510
7576
  JSON.stringify({
@@ -7561,7 +7627,8 @@ var SessionManager = class {
7561
7627
  maxAttempt: 5
7562
7628
  })();
7563
7629
  if (!r1.ok) return r1;
7564
- if (!this.persist(r1.data, version)) return this.malformedResponseError();
7630
+ if (!await this.persist(r1.data, version))
7631
+ return this.malformedResponseError();
7565
7632
  return {
7566
7633
  ok: true,
7567
7634
  data: null
@@ -7570,7 +7637,7 @@ var SessionManager = class {
7570
7637
  if (input.type === "MOBILE_NUMBER") {
7571
7638
  const res2 = await this.authService.createSession(input, version);
7572
7639
  if (res2.ok) {
7573
- if (!this.persist(res2.data, version))
7640
+ if (!await this.persist(res2.data, version))
7574
7641
  return this.malformedResponseError();
7575
7642
  return {
7576
7643
  ok: true,
@@ -7589,7 +7656,7 @@ var SessionManager = class {
7589
7656
  version
7590
7657
  );
7591
7658
  if (res2.ok) {
7592
- if (!this.persist(res2.data, version))
7659
+ if (!await this.persist(res2.data, version))
7593
7660
  return this.malformedResponseError();
7594
7661
  return {
7595
7662
  ok: true,
@@ -7602,7 +7669,7 @@ var SessionManager = class {
7602
7669
  localStorage.setItem(this.platformStorageKey, "CABINET");
7603
7670
  const res2 = await this.authService.createSession(input, version);
7604
7671
  if (res2.ok) {
7605
- if (!this.persist(res2.data, version))
7672
+ if (!await this.persist(res2.data, version))
7606
7673
  return this.malformedResponseError();
7607
7674
  return {
7608
7675
  ok: true,
@@ -7621,7 +7688,8 @@ var SessionManager = class {
7621
7688
  }
7622
7689
  };
7623
7690
  }
7624
- if (!this.persist(res.data, version)) return this.malformedResponseError();
7691
+ if (!await this.persist(res.data, version))
7692
+ return this.malformedResponseError();
7625
7693
  return {
7626
7694
  ok: true,
7627
7695
  data: null
@@ -7632,7 +7700,7 @@ var SessionManager = class {
7632
7700
  if (res.ok) {
7633
7701
  if (this.isServer) {
7634
7702
  this.logger.warn("'localStorage' is not available on the server.");
7635
- } else if (!this.persist(res.data, 1)) {
7703
+ } else if (!await this.persist(res.data, 1)) {
7636
7704
  return this.malformedResponseError();
7637
7705
  }
7638
7706
  return { ok: true };
@@ -7728,6 +7796,20 @@ var SessionManager = class {
7728
7796
  };
7729
7797
  }
7730
7798
  }
7799
+ async getV4(session) {
7800
+ const now = /* @__PURE__ */ new Date();
7801
+ if (this.v4AccessToken && this.v4AccessTokenExpiresAt && !isAfter(now, new Date(this.v4AccessTokenExpiresAt))) {
7802
+ return {
7803
+ ok: true,
7804
+ data: {
7805
+ session,
7806
+ accessToken: this.v4AccessToken,
7807
+ accessTokenExpiresAt: this.v4AccessTokenExpiresAt
7808
+ }
7809
+ };
7810
+ }
7811
+ return await this.refreshV4(session);
7812
+ }
7731
7813
  async refresh() {
7732
7814
  if (this.isServer) {
7733
7815
  this.logger.warn("'localStorage' is not available on the server.");
@@ -7797,20 +7879,6 @@ var SessionManager = class {
7797
7879
  };
7798
7880
  }
7799
7881
  }
7800
- async getV4(session) {
7801
- const now = /* @__PURE__ */ new Date();
7802
- if (this.v4AccessToken && this.v4AccessTokenExpiresAt && !isAfter(now, new Date(this.v4AccessTokenExpiresAt))) {
7803
- return {
7804
- ok: true,
7805
- data: {
7806
- session,
7807
- accessToken: this.v4AccessToken,
7808
- accessTokenExpiresAt: this.v4AccessTokenExpiresAt
7809
- }
7810
- };
7811
- }
7812
- return await this.refreshV4(session);
7813
- }
7814
7882
  async refreshV4(session) {
7815
7883
  if (this.v4RefreshPromise) return await this.v4RefreshPromise;
7816
7884
  this.v4RefreshPromise = this.requestV4Refresh(session);
@@ -7822,21 +7890,48 @@ var SessionManager = class {
7822
7890
  }
7823
7891
  async requestV4Refresh(session) {
7824
7892
  this.logger.info("Refreshing session...");
7893
+ let refreshToken;
7894
+ if (isIOSPlatform()) {
7895
+ refreshToken = await this.refreshTokenStore.get() ?? void 0;
7896
+ if (!refreshToken) {
7897
+ this.logger.warn("No stored refresh token. Session expired.");
7898
+ this.v4AccessToken = null;
7899
+ this.v4AccessTokenExpiresAt = null;
7900
+ this.invalidReason = "REFRESH_TOKEN_EXPIRED";
7901
+ localStorage.removeItem(this.storageKey);
7902
+ return {
7903
+ ok: false,
7904
+ error: {
7905
+ name: "SessionExpiredError",
7906
+ message: "Session expired."
7907
+ }
7908
+ };
7909
+ }
7910
+ }
7825
7911
  this.refreshing = true;
7826
- const res = await this.authService.refreshSession(void 0, 4);
7827
- this.refreshing = false;
7912
+ const res = await this.authService.refreshSession(refreshToken, 4);
7828
7913
  if (!res.ok) {
7829
7914
  this.logger.error(`Failed to refresh session: ${res.error.message}`);
7830
- if (res.error.name === "InvalidTokenError" || res.error.name === "AccountBlacklistedError" || res.error.name === "VerificationLockedError" || res.error.name === "SessionExpiredError") {
7915
+ const terminal = res.error.name === "InvalidTokenError" || res.error.name === "AccountBlacklistedError" || res.error.name === "VerificationLockedError" || res.error.name === "SessionExpiredError";
7916
+ if (terminal) {
7831
7917
  this.v4AccessToken = null;
7832
7918
  this.v4AccessTokenExpiresAt = null;
7919
+ this.invalidReason = res.error.name === "SessionExpiredError" ? "REFRESH_TOKEN_EXPIRED" : "INVALID_SESSION";
7920
+ localStorage.removeItem(this.storageKey);
7833
7921
  }
7922
+ this.refreshing = false;
7923
+ if (terminal) await this.refreshTokenStore.remove();
7834
7924
  return {
7835
7925
  ok: false,
7836
7926
  error: res.error
7837
7927
  };
7838
7928
  }
7929
+ this.refreshing = false;
7839
7930
  this.logger.success("Session refreshed!");
7931
+ this.invalidReason = null;
7932
+ if (res.data.refreshToken) {
7933
+ await this.refreshTokenStore.set(res.data.refreshToken);
7934
+ }
7840
7935
  const now = /* @__PURE__ */ new Date();
7841
7936
  this.v4AccessToken = res.data.accessToken;
7842
7937
  this.v4AccessTokenExpiresAt = addMinutes(now, 4).getTime();
@@ -7869,6 +7964,8 @@ var SessionManager = class {
7869
7964
  }
7870
7965
  this.v4AccessToken = null;
7871
7966
  this.v4AccessTokenExpiresAt = null;
7967
+ this.invalidReason = null;
7968
+ await this.refreshTokenStore.remove();
7872
7969
  localStorage.removeItem(this.storageKey);
7873
7970
  }
7874
7971
  async verify() {
@@ -7876,6 +7973,9 @@ var SessionManager = class {
7876
7973
  this.logger.warn("'localStorage' is not available on the server.");
7877
7974
  return { valid: true };
7878
7975
  }
7976
+ if (this.invalidReason) {
7977
+ return { valid: false, reason: this.invalidReason };
7978
+ }
7879
7979
  const val = localStorage.getItem(this.storageKey);
7880
7980
  if (val) {
7881
7981
  try {
@@ -7890,11 +7990,13 @@ var SessionManager = class {
7890
7990
  }
7891
7991
  const s = await this.get();
7892
7992
  if (s.error?.name === "InvalidTokenError" || s.error?.name === "SessionExpiredError" || s.error?.name === "AccountBlacklistedError") {
7993
+ this.invalidReason = "INVALID_SESSION";
7893
7994
  return { valid: false, reason: "INVALID_SESSION" };
7894
7995
  }
7895
7996
  if (!s.data) return { valid: true };
7896
7997
  const v = await this.authService.verifySession(s.data.accessToken);
7897
7998
  if (!v) {
7999
+ this.invalidReason = "INVALID_SESSION";
7898
8000
  localStorage.removeItem(this.storageKey);
7899
8001
  }
7900
8002
  return v ? { valid: true } : { valid: false, reason: "INVALID_SESSION" };
@@ -7902,16 +8004,17 @@ var SessionManager = class {
7902
8004
  async verifyV4(session) {
7903
8005
  if (!this.v4AccessToken || !this.v4AccessTokenExpiresAt || isAfter(/* @__PURE__ */ new Date(), new Date(this.v4AccessTokenExpiresAt))) {
7904
8006
  const res = await this.refreshV4(session);
7905
- return res.ok ? { valid: true } : {
7906
- valid: false,
7907
- reason: res.error.name === "SessionExpiredError" ? "REFRESH_TOKEN_EXPIRED" : "INVALID_SESSION"
7908
- };
8007
+ if (res.ok) return { valid: true };
8008
+ if (!this.invalidReason) return { valid: true };
8009
+ return { valid: false, reason: this.invalidReason };
7909
8010
  }
7910
8011
  const v = await this.authService.verifySession(this.v4AccessToken);
7911
8012
  if (!v) {
7912
8013
  localStorage.removeItem(this.storageKey);
7913
8014
  this.v4AccessToken = null;
7914
8015
  this.v4AccessTokenExpiresAt = null;
8016
+ this.invalidReason = "INVALID_SESSION";
8017
+ await this.refreshTokenStore.remove();
7915
8018
  }
7916
8019
  return v ? { valid: true } : { valid: false, reason: "INVALID_SESSION" };
7917
8020
  }
@@ -7941,16 +8044,20 @@ var SessionManagerCookie = class {
7941
8044
  walletService;
7942
8045
  _refreshing = false;
7943
8046
  /*
7944
- * v4 access tokens are never persisted (localStorage/cookie) so that an
7945
- * XSS-readable storage layer never holds a live access token. They only
7946
- * live in memory and get re-minted from the HttpOnly refresh cookie.
8047
+ * v4 access tokens live in memory only, so an XSS-readable storage layer
8048
+ * never holds a live one. Re-minted from the HttpOnly cookie, or from
8049
+ * `refreshTokenStore` on iOS.
7947
8050
  */
7948
8051
  v4AccessToken = null;
7949
8052
  v4AccessTokenExpiresAt = null;
8053
+ v4RefreshPromise = null;
8054
+ invalidReason = null;
8055
+ refreshTokenStore;
7950
8056
  constructor(config) {
7951
8057
  this.authService = config.authService;
7952
8058
  this.walletService = config.walletService;
7953
8059
  this.logger = config.logger;
8060
+ this.refreshTokenStore = createRefreshTokenStore(config.logger);
7954
8061
  }
7955
8062
  get refreshing() {
7956
8063
  return this._refreshing;
@@ -7958,15 +8065,19 @@ var SessionManagerCookie = class {
7958
8065
  set refreshing(value) {
7959
8066
  this._refreshing = value;
7960
8067
  }
7961
- persist(data, version) {
8068
+ async persist(data, version) {
7962
8069
  const now = /* @__PURE__ */ new Date();
8070
+ this.invalidReason = null;
7963
8071
  if (version === 4) {
7964
8072
  this.v4AccessToken = data.accessToken ?? null;
7965
8073
  this.v4AccessTokenExpiresAt = addMinutes(now, 5).getTime();
8074
+ if (data.refreshToken) {
8075
+ await this.refreshTokenStore.set(data.refreshToken);
8076
+ }
7966
8077
  cookies__default.default.set(
7967
8078
  this.storageKey,
7968
8079
  JSON.stringify({ version, session: data.session }),
7969
- { expires: subMinutes(addDays(now, 15), 2).getTime() }
8080
+ { expires: subMinutes(addDays(now, 15), 2) }
7970
8081
  );
7971
8082
  return;
7972
8083
  }
@@ -7978,7 +8089,7 @@ var SessionManagerCookie = class {
7978
8089
  accessTokenExpiresAt: addMinutes(now, 8).getTime(),
7979
8090
  refreshTokenExpiresAt: subMinutes(addDays(now, 30), 2).getTime()
7980
8091
  }),
7981
- { expires: subMinutes(addDays(now, 30), 2).getTime() }
8092
+ { expires: subMinutes(addDays(now, 30), 2) }
7982
8093
  );
7983
8094
  }
7984
8095
  async create(input, version = 1) {
@@ -8007,7 +8118,7 @@ var SessionManagerCookie = class {
8007
8118
  maxAttempt: 5
8008
8119
  })();
8009
8120
  if (!r1.ok) return r1;
8010
- this.persist(r1.data, version);
8121
+ await this.persist(r1.data, version);
8011
8122
  return {
8012
8123
  ok: true,
8013
8124
  data: null
@@ -8016,7 +8127,7 @@ var SessionManagerCookie = class {
8016
8127
  if (input.type === "MOBILE_NUMBER") {
8017
8128
  const res2 = await this.authService.createSession(input, version);
8018
8129
  if (res2.ok) {
8019
- this.persist(res2.data, version);
8130
+ await this.persist(res2.data, version);
8020
8131
  return {
8021
8132
  ok: true,
8022
8133
  data: null
@@ -8033,7 +8144,7 @@ var SessionManagerCookie = class {
8033
8144
  version
8034
8145
  );
8035
8146
  if (res2.ok) {
8036
- this.persist(res2.data, version);
8147
+ await this.persist(res2.data, version);
8037
8148
  return {
8038
8149
  ok: true,
8039
8150
  data: null
@@ -8045,7 +8156,7 @@ var SessionManagerCookie = class {
8045
8156
  localStorage.setItem(this.platformStorageKey, "CABINET");
8046
8157
  const res2 = await this.authService.createSession(input, version);
8047
8158
  if (res2.ok) {
8048
- this.persist(res2.data, version);
8159
+ await this.persist(res2.data, version);
8049
8160
  return {
8050
8161
  ok: true,
8051
8162
  data: null
@@ -8063,7 +8174,7 @@ var SessionManagerCookie = class {
8063
8174
  }
8064
8175
  };
8065
8176
  }
8066
- this.persist(res.data, version);
8177
+ await this.persist(res.data, version);
8067
8178
  return {
8068
8179
  ok: true,
8069
8180
  data: null
@@ -8075,7 +8186,7 @@ var SessionManagerCookie = class {
8075
8186
  if (this.isServer) {
8076
8187
  this.logger.warn("'client cookies' is not available on the server.");
8077
8188
  } else {
8078
- this.persist(res.data, 1);
8189
+ await this.persist(res.data, 1);
8079
8190
  }
8080
8191
  return { ok: true };
8081
8192
  } else {
@@ -8155,7 +8266,7 @@ var SessionManagerCookie = class {
8155
8266
  refreshTokenExpiresAt: subMinutes(addDays(now, 30), 2).getTime()
8156
8267
  };
8157
8268
  cookies__default.default.set(this.storageKey, JSON.stringify(obj), {
8158
- expires: subMinutes(addDays(now, 30), 2).getTime()
8269
+ expires: subMinutes(addDays(now, 30), 2)
8159
8270
  });
8160
8271
  }
8161
8272
  return {
@@ -8172,6 +8283,20 @@ var SessionManagerCookie = class {
8172
8283
  };
8173
8284
  }
8174
8285
  }
8286
+ async getV4(session) {
8287
+ const now = /* @__PURE__ */ new Date();
8288
+ if (this.v4AccessToken && this.v4AccessTokenExpiresAt && !isAfter(now, new Date(this.v4AccessTokenExpiresAt))) {
8289
+ return {
8290
+ ok: true,
8291
+ data: {
8292
+ session,
8293
+ accessToken: this.v4AccessToken,
8294
+ accessTokenExpiresAt: this.v4AccessTokenExpiresAt
8295
+ }
8296
+ };
8297
+ }
8298
+ return await this.refreshV4(session);
8299
+ }
8175
8300
  async refresh() {
8176
8301
  if (this.isServer) {
8177
8302
  this.logger.warn("'client cookies' is not available on the server.");
@@ -8227,7 +8352,7 @@ var SessionManagerCookie = class {
8227
8352
  refreshTokenExpiresAt: subMinutes(addDays(now, 30), 2).getTime()
8228
8353
  };
8229
8354
  cookies__default.default.set(this.storageKey, JSON.stringify(obj), {
8230
- expires: subMinutes(addDays(now, 30), 2).getTime()
8355
+ expires: subMinutes(addDays(now, 30), 2)
8231
8356
  });
8232
8357
  return {
8233
8358
  ok: true,
@@ -8243,37 +8368,59 @@ var SessionManagerCookie = class {
8243
8368
  };
8244
8369
  }
8245
8370
  }
8246
- async getV4(session) {
8247
- const now = /* @__PURE__ */ new Date();
8248
- if (this.v4AccessToken && this.v4AccessTokenExpiresAt && !isAfter(now, new Date(this.v4AccessTokenExpiresAt))) {
8249
- return {
8250
- ok: true,
8251
- data: {
8252
- session,
8253
- accessToken: this.v4AccessToken,
8254
- accessTokenExpiresAt: this.v4AccessTokenExpiresAt
8255
- }
8256
- };
8371
+ async refreshV4(session) {
8372
+ if (this.v4RefreshPromise) return await this.v4RefreshPromise;
8373
+ this.v4RefreshPromise = this.requestV4Refresh(session);
8374
+ try {
8375
+ return await this.v4RefreshPromise;
8376
+ } finally {
8377
+ this.v4RefreshPromise = null;
8257
8378
  }
8258
- return await this.refreshV4(session);
8259
8379
  }
8260
- async refreshV4(session) {
8380
+ async requestV4Refresh(session) {
8261
8381
  this.logger.info("Refreshing session...");
8382
+ let refreshToken;
8383
+ if (isIOSPlatform()) {
8384
+ refreshToken = await this.refreshTokenStore.get() ?? void 0;
8385
+ if (!refreshToken) {
8386
+ this.logger.warn("No stored refresh token. Session expired.");
8387
+ this.v4AccessToken = null;
8388
+ this.v4AccessTokenExpiresAt = null;
8389
+ this.invalidReason = "REFRESH_TOKEN_EXPIRED";
8390
+ cookies__default.default.remove(this.storageKey);
8391
+ return {
8392
+ ok: false,
8393
+ error: {
8394
+ name: "SessionExpiredError",
8395
+ message: "Session expired."
8396
+ }
8397
+ };
8398
+ }
8399
+ }
8262
8400
  this.refreshing = true;
8263
- const res = await this.authService.refreshSession(void 0, 4);
8264
- this.refreshing = false;
8401
+ const res = await this.authService.refreshSession(refreshToken, 4);
8265
8402
  if (!res.ok) {
8266
8403
  this.logger.error(`Failed to refresh session: ${res.error.message}`);
8267
- if (res.error.name === "InvalidTokenError" || res.error.name === "AccountBlacklistedError" || res.error.name === "VerificationLockedError" || res.error.name === "SessionExpiredError") {
8404
+ const terminal = res.error.name === "InvalidTokenError" || res.error.name === "AccountBlacklistedError" || res.error.name === "VerificationLockedError" || res.error.name === "SessionExpiredError";
8405
+ if (terminal) {
8268
8406
  this.v4AccessToken = null;
8269
8407
  this.v4AccessTokenExpiresAt = null;
8408
+ this.invalidReason = res.error.name === "SessionExpiredError" ? "REFRESH_TOKEN_EXPIRED" : "INVALID_SESSION";
8409
+ cookies__default.default.remove(this.storageKey);
8270
8410
  }
8411
+ this.refreshing = false;
8412
+ if (terminal) await this.refreshTokenStore.remove();
8271
8413
  return {
8272
8414
  ok: false,
8273
8415
  error: res.error
8274
8416
  };
8275
8417
  }
8418
+ this.refreshing = false;
8276
8419
  this.logger.success("Session refreshed!");
8420
+ this.invalidReason = null;
8421
+ if (res.data.refreshToken) {
8422
+ await this.refreshTokenStore.set(res.data.refreshToken);
8423
+ }
8277
8424
  const now = /* @__PURE__ */ new Date();
8278
8425
  this.v4AccessToken = res.data.accessToken;
8279
8426
  this.v4AccessTokenExpiresAt = addMinutes(now, 5).getTime();
@@ -8306,6 +8453,8 @@ var SessionManagerCookie = class {
8306
8453
  }
8307
8454
  this.v4AccessToken = null;
8308
8455
  this.v4AccessTokenExpiresAt = null;
8456
+ this.invalidReason = null;
8457
+ await this.refreshTokenStore.remove();
8309
8458
  cookies__default.default.remove(this.storageKey);
8310
8459
  }
8311
8460
  async verify() {
@@ -8313,6 +8462,9 @@ var SessionManagerCookie = class {
8313
8462
  this.logger.warn("'client cookies' is not available on the server.");
8314
8463
  return { valid: true };
8315
8464
  }
8465
+ if (this.invalidReason) {
8466
+ return { valid: false, reason: this.invalidReason };
8467
+ }
8316
8468
  const val = cookies__default.default.get(this.storageKey);
8317
8469
  if (val) {
8318
8470
  try {
@@ -8327,11 +8479,13 @@ var SessionManagerCookie = class {
8327
8479
  }
8328
8480
  const s = await this.get();
8329
8481
  if (s.error?.name === "InvalidTokenError" || s.error?.name === "SessionExpiredError" || s.error?.name === "AccountBlacklistedError") {
8482
+ this.invalidReason = "INVALID_SESSION";
8330
8483
  return { valid: false, reason: "INVALID_SESSION" };
8331
8484
  }
8332
8485
  if (!s.data) return { valid: true };
8333
8486
  const v = await this.authService.verifySession(s.data.accessToken);
8334
8487
  if (!v) {
8488
+ this.invalidReason = "INVALID_SESSION";
8335
8489
  cookies__default.default.remove(this.storageKey);
8336
8490
  }
8337
8491
  return v ? { valid: true } : { valid: false, reason: "INVALID_SESSION" };
@@ -8339,16 +8493,17 @@ var SessionManagerCookie = class {
8339
8493
  async verifyV4(session) {
8340
8494
  if (!this.v4AccessToken || !this.v4AccessTokenExpiresAt || isAfter(/* @__PURE__ */ new Date(), new Date(this.v4AccessTokenExpiresAt))) {
8341
8495
  const res = await this.refreshV4(session);
8342
- return res.ok ? { valid: true } : {
8343
- valid: false,
8344
- reason: res.error.name === "SessionExpiredError" ? "REFRESH_TOKEN_EXPIRED" : "INVALID_SESSION"
8345
- };
8496
+ if (res.ok) return { valid: true };
8497
+ if (!this.invalidReason) return { valid: true };
8498
+ return { valid: false, reason: this.invalidReason };
8346
8499
  }
8347
8500
  const v = await this.authService.verifySession(this.v4AccessToken);
8348
8501
  if (!v) {
8349
8502
  cookies__default.default.remove(this.storageKey);
8350
8503
  this.v4AccessToken = null;
8351
8504
  this.v4AccessTokenExpiresAt = null;
8505
+ this.invalidReason = "INVALID_SESSION";
8506
+ await this.refreshTokenStore.remove();
8352
8507
  }
8353
8508
  return v ? { valid: true } : { valid: false, reason: "INVALID_SESSION" };
8354
8509
  }
@@ -10653,9 +10808,18 @@ var Sdk = class {
10653
10808
  let counter = 0;
10654
10809
  let timeout = null;
10655
10810
  let isForeground = true;
10811
+ let stopped = false;
10656
10812
  const listener = app.App.addListener("appStateChange", ({ isActive }) => {
10657
10813
  isForeground = isActive;
10658
10814
  });
10815
+ const stop = () => {
10816
+ stopped = true;
10817
+ if (timeout) {
10818
+ clearTimeout(timeout);
10819
+ timeout = null;
10820
+ }
10821
+ listener.then((handle) => handle.remove());
10822
+ };
10659
10823
  const assignTimeout = () => {
10660
10824
  const duration = counter <= 0 ? interval * 0.5 : interval;
10661
10825
  return setTimeout(async () => {
@@ -10664,10 +10828,13 @@ var Sdk = class {
10664
10828
  try {
10665
10829
  res = await this.sessionManager.verify();
10666
10830
  } catch {
10667
- res = { valid: false, reason: "INVALID_SESSION" };
10831
+ res = null;
10668
10832
  }
10669
- if (!res.valid) {
10833
+ if (stopped) return;
10834
+ if (res && !res.valid) {
10835
+ stop();
10670
10836
  await input.onInvalid(res.reason ?? "INVALID_SESSION");
10837
+ return;
10671
10838
  }
10672
10839
  }
10673
10840
  counter += 1;
@@ -10676,10 +10843,7 @@ var Sdk = class {
10676
10843
  };
10677
10844
  timeout = assignTimeout();
10678
10845
  return function unsubscribe() {
10679
- if (timeout) {
10680
- clearTimeout(timeout);
10681
- }
10682
- listener.then((handle) => handle.remove());
10846
+ stop();
10683
10847
  };
10684
10848
  }
10685
10849
  async session() {