@opexa/portal-sdk 0.59.94 → 0.59.96

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
@@ -1,8 +1,9 @@
1
- import { CmsPortalService, GameService, ExtensionService, FileService, WalletService, AccountService, ReportService, PortalService, TriggerService, AuthService, getFingerPrint } from './chunk-4TI5IXXM.js';
2
- import { GraphQLClient, isPlainObject } from './chunk-5WRVWQBT.js';
3
- import './chunk-ROBGEUSE.js';
1
+ import { CmsPortalService, GameService, ExtensionService, FileService, WalletService, AccountService, ReportService, PortalService, TriggerService, AuthService, getFingerPrint, isIOSPlatform } from './chunk-MJLXQ6HC.js';
2
+ import { GraphQLClient, isPlainObject } from './chunk-7APXFZ5G.js';
3
+ import './chunk-WVFSGB7Y.js';
4
4
  import { ObjectId } from '@opexa/object-id';
5
5
  export { ObjectId } from '@opexa/object-id';
6
+ import { App } from '@capacitor/app';
6
7
  import { Capacitor } from '@capacitor/core';
7
8
  import cookies from 'js-cookie';
8
9
 
@@ -335,6 +336,48 @@ function pollable(func, config) {
335
336
  };
336
337
  }
337
338
 
339
+ // src/sdk/refresh-token-store.ts
340
+ var PreferencesRefreshTokenStore = class {
341
+ constructor(key, logger) {
342
+ this.key = key;
343
+ this.logger = logger;
344
+ }
345
+ async preferences() {
346
+ const { Preferences } = await import('@capacitor/preferences');
347
+ return Preferences;
348
+ }
349
+ async get() {
350
+ if (!isIOSPlatform()) return null;
351
+ try {
352
+ const { value } = await (await this.preferences()).get({ key: this.key });
353
+ return value ?? null;
354
+ } catch (err) {
355
+ this.logger.error(`Failed to read the refresh token: ${err}`);
356
+ return null;
357
+ }
358
+ }
359
+ async set(token) {
360
+ if (!isIOSPlatform()) return;
361
+ try {
362
+ await (await this.preferences()).set({ key: this.key, value: token });
363
+ } catch (err) {
364
+ this.logger.error(`Failed to persist the refresh token: ${err}`);
365
+ }
366
+ }
367
+ async remove() {
368
+ if (!isIOSPlatform()) return;
369
+ try {
370
+ await (await this.preferences()).remove({ key: this.key });
371
+ } catch (err) {
372
+ this.logger.error(`Failed to clear the refresh token: ${err}`);
373
+ }
374
+ }
375
+ };
376
+ function createRefreshTokenStore(logger, sessionPrefix) {
377
+ const key = sessionPrefix ? `${sessionPrefix}/session/refresh-token` : "session/refresh-token";
378
+ return new PreferencesRefreshTokenStore(key, logger);
379
+ }
380
+
338
381
  // src/sdk/session-manager.ts
339
382
  var SessionManager = class {
340
383
  logger;
@@ -344,17 +387,22 @@ var SessionManager = class {
344
387
  walletService;
345
388
  _refreshing = false;
346
389
  /*
347
- * v4 access tokens are never persisted (localStorage/cookie) so that an
348
- * XSS-readable storage layer never holds a live access token. They only
349
- * live in memory and get re-minted from the HttpOnly refresh cookie.
390
+ * v4 access tokens live in memory only, so an XSS-readable storage layer
391
+ * never holds a live one. Re-minted from the HttpOnly cookie, or from
392
+ * `refreshTokenStore` on iOS.
350
393
  */
351
394
  v4AccessToken = null;
352
395
  v4AccessTokenExpiresAt = null;
353
396
  v4RefreshPromise = null;
397
+ refreshTokenStore;
354
398
  constructor(config) {
355
399
  this.authService = config.authService;
356
400
  this.walletService = config.walletService;
357
401
  this.logger = config.logger;
402
+ this.refreshTokenStore = createRefreshTokenStore(
403
+ config.logger,
404
+ config.sessionPrefix
405
+ );
358
406
  if (config.sessionPrefix) {
359
407
  this.storageKey = `${config.sessionPrefix}/${this.storageKey}`;
360
408
  this.platformStorageKey = `${config.sessionPrefix}/${this.platformStorageKey}`;
@@ -366,7 +414,7 @@ var SessionManager = class {
366
414
  set refreshing(value) {
367
415
  this._refreshing = value;
368
416
  }
369
- persist(data, version) {
417
+ async persist(data, version) {
370
418
  const now = /* @__PURE__ */ new Date();
371
419
  if (version === 4) {
372
420
  if (!data.session || !data.accessToken) {
@@ -377,6 +425,9 @@ var SessionManager = class {
377
425
  }
378
426
  this.v4AccessToken = data.accessToken;
379
427
  this.v4AccessTokenExpiresAt = addMinutes(now, 4).getTime();
428
+ if (data.refreshToken) {
429
+ await this.refreshTokenStore.set(data.refreshToken);
430
+ }
380
431
  localStorage.setItem(
381
432
  this.storageKey,
382
433
  JSON.stringify({ version, session: data.session })
@@ -439,7 +490,8 @@ var SessionManager = class {
439
490
  maxAttempt: 5
440
491
  })();
441
492
  if (!r1.ok) return r1;
442
- if (!this.persist(r1.data, version)) return this.malformedResponseError();
493
+ if (!await this.persist(r1.data, version))
494
+ return this.malformedResponseError();
443
495
  return {
444
496
  ok: true,
445
497
  data: null
@@ -448,7 +500,7 @@ var SessionManager = class {
448
500
  if (input.type === "MOBILE_NUMBER") {
449
501
  const res2 = await this.authService.createSession(input, version);
450
502
  if (res2.ok) {
451
- if (!this.persist(res2.data, version))
503
+ if (!await this.persist(res2.data, version))
452
504
  return this.malformedResponseError();
453
505
  return {
454
506
  ok: true,
@@ -467,7 +519,7 @@ var SessionManager = class {
467
519
  version
468
520
  );
469
521
  if (res2.ok) {
470
- if (!this.persist(res2.data, version))
522
+ if (!await this.persist(res2.data, version))
471
523
  return this.malformedResponseError();
472
524
  return {
473
525
  ok: true,
@@ -480,7 +532,7 @@ var SessionManager = class {
480
532
  localStorage.setItem(this.platformStorageKey, "CABINET");
481
533
  const res2 = await this.authService.createSession(input, version);
482
534
  if (res2.ok) {
483
- if (!this.persist(res2.data, version))
535
+ if (!await this.persist(res2.data, version))
484
536
  return this.malformedResponseError();
485
537
  return {
486
538
  ok: true,
@@ -499,7 +551,8 @@ var SessionManager = class {
499
551
  }
500
552
  };
501
553
  }
502
- if (!this.persist(res.data, version)) return this.malformedResponseError();
554
+ if (!await this.persist(res.data, version))
555
+ return this.malformedResponseError();
503
556
  return {
504
557
  ok: true,
505
558
  data: null
@@ -510,7 +563,7 @@ var SessionManager = class {
510
563
  if (res.ok) {
511
564
  if (this.isServer) {
512
565
  this.logger.warn("'localStorage' is not available on the server.");
513
- } else if (!this.persist(res.data, 1)) {
566
+ } else if (!await this.persist(res.data, 1)) {
514
567
  return this.malformedResponseError();
515
568
  }
516
569
  return { ok: true };
@@ -700,14 +753,31 @@ var SessionManager = class {
700
753
  }
701
754
  async requestV4Refresh(session) {
702
755
  this.logger.info("Refreshing session...");
756
+ let refreshToken;
757
+ if (isIOSPlatform()) {
758
+ refreshToken = await this.refreshTokenStore.get() ?? void 0;
759
+ if (!refreshToken) {
760
+ this.logger.warn("No stored refresh token. Session expired.");
761
+ this.v4AccessToken = null;
762
+ this.v4AccessTokenExpiresAt = null;
763
+ return {
764
+ ok: false,
765
+ error: {
766
+ name: "SessionExpiredError",
767
+ message: "Session expired."
768
+ }
769
+ };
770
+ }
771
+ }
703
772
  this.refreshing = true;
704
- const res = await this.authService.refreshSession(void 0, 4);
773
+ const res = await this.authService.refreshSession(refreshToken, 4);
705
774
  this.refreshing = false;
706
775
  if (!res.ok) {
707
776
  this.logger.error(`Failed to refresh session: ${res.error.message}`);
708
777
  if (res.error.name === "InvalidTokenError" || res.error.name === "AccountBlacklistedError" || res.error.name === "VerificationLockedError" || res.error.name === "SessionExpiredError") {
709
778
  this.v4AccessToken = null;
710
779
  this.v4AccessTokenExpiresAt = null;
780
+ await this.refreshTokenStore.remove();
711
781
  }
712
782
  return {
713
783
  ok: false,
@@ -715,6 +785,9 @@ var SessionManager = class {
715
785
  };
716
786
  }
717
787
  this.logger.success("Session refreshed!");
788
+ if (res.data.refreshToken) {
789
+ await this.refreshTokenStore.set(res.data.refreshToken);
790
+ }
718
791
  const now = /* @__PURE__ */ new Date();
719
792
  this.v4AccessToken = res.data.accessToken;
720
793
  this.v4AccessTokenExpiresAt = addMinutes(now, 4).getTime();
@@ -747,6 +820,7 @@ var SessionManager = class {
747
820
  }
748
821
  this.v4AccessToken = null;
749
822
  this.v4AccessTokenExpiresAt = null;
823
+ await this.refreshTokenStore.remove();
750
824
  localStorage.removeItem(this.storageKey);
751
825
  }
752
826
  async verify() {
@@ -790,6 +864,7 @@ var SessionManager = class {
790
864
  localStorage.removeItem(this.storageKey);
791
865
  this.v4AccessToken = null;
792
866
  this.v4AccessTokenExpiresAt = null;
867
+ await this.refreshTokenStore.remove();
793
868
  }
794
869
  return v ? { valid: true } : { valid: false, reason: "INVALID_SESSION" };
795
870
  }
@@ -819,16 +894,19 @@ var SessionManagerCookie = class {
819
894
  walletService;
820
895
  _refreshing = false;
821
896
  /*
822
- * v4 access tokens are never persisted (localStorage/cookie) so that an
823
- * XSS-readable storage layer never holds a live access token. They only
824
- * live in memory and get re-minted from the HttpOnly refresh cookie.
897
+ * v4 access tokens live in memory only, so an XSS-readable storage layer
898
+ * never holds a live one. Re-minted from the HttpOnly cookie, or from
899
+ * `refreshTokenStore` on iOS.
825
900
  */
826
901
  v4AccessToken = null;
827
902
  v4AccessTokenExpiresAt = null;
903
+ v4RefreshPromise = null;
904
+ refreshTokenStore;
828
905
  constructor(config) {
829
906
  this.authService = config.authService;
830
907
  this.walletService = config.walletService;
831
908
  this.logger = config.logger;
909
+ this.refreshTokenStore = createRefreshTokenStore(config.logger);
832
910
  }
833
911
  get refreshing() {
834
912
  return this._refreshing;
@@ -836,15 +914,18 @@ var SessionManagerCookie = class {
836
914
  set refreshing(value) {
837
915
  this._refreshing = value;
838
916
  }
839
- persist(data, version) {
917
+ async persist(data, version) {
840
918
  const now = /* @__PURE__ */ new Date();
841
919
  if (version === 4) {
842
920
  this.v4AccessToken = data.accessToken ?? null;
843
921
  this.v4AccessTokenExpiresAt = addMinutes(now, 5).getTime();
922
+ if (data.refreshToken) {
923
+ await this.refreshTokenStore.set(data.refreshToken);
924
+ }
844
925
  cookies.set(
845
926
  this.storageKey,
846
927
  JSON.stringify({ version, session: data.session }),
847
- { expires: subMinutes(addDays(now, 15), 2).getTime() }
928
+ { expires: subMinutes(addDays(now, 15), 2) }
848
929
  );
849
930
  return;
850
931
  }
@@ -856,7 +937,7 @@ var SessionManagerCookie = class {
856
937
  accessTokenExpiresAt: addMinutes(now, 8).getTime(),
857
938
  refreshTokenExpiresAt: subMinutes(addDays(now, 30), 2).getTime()
858
939
  }),
859
- { expires: subMinutes(addDays(now, 30), 2).getTime() }
940
+ { expires: subMinutes(addDays(now, 30), 2) }
860
941
  );
861
942
  }
862
943
  async create(input, version = 1) {
@@ -885,7 +966,7 @@ var SessionManagerCookie = class {
885
966
  maxAttempt: 5
886
967
  })();
887
968
  if (!r1.ok) return r1;
888
- this.persist(r1.data, version);
969
+ await this.persist(r1.data, version);
889
970
  return {
890
971
  ok: true,
891
972
  data: null
@@ -894,7 +975,7 @@ var SessionManagerCookie = class {
894
975
  if (input.type === "MOBILE_NUMBER") {
895
976
  const res2 = await this.authService.createSession(input, version);
896
977
  if (res2.ok) {
897
- this.persist(res2.data, version);
978
+ await this.persist(res2.data, version);
898
979
  return {
899
980
  ok: true,
900
981
  data: null
@@ -911,7 +992,7 @@ var SessionManagerCookie = class {
911
992
  version
912
993
  );
913
994
  if (res2.ok) {
914
- this.persist(res2.data, version);
995
+ await this.persist(res2.data, version);
915
996
  return {
916
997
  ok: true,
917
998
  data: null
@@ -923,7 +1004,7 @@ var SessionManagerCookie = class {
923
1004
  localStorage.setItem(this.platformStorageKey, "CABINET");
924
1005
  const res2 = await this.authService.createSession(input, version);
925
1006
  if (res2.ok) {
926
- this.persist(res2.data, version);
1007
+ await this.persist(res2.data, version);
927
1008
  return {
928
1009
  ok: true,
929
1010
  data: null
@@ -941,7 +1022,7 @@ var SessionManagerCookie = class {
941
1022
  }
942
1023
  };
943
1024
  }
944
- this.persist(res.data, version);
1025
+ await this.persist(res.data, version);
945
1026
  return {
946
1027
  ok: true,
947
1028
  data: null
@@ -953,7 +1034,7 @@ var SessionManagerCookie = class {
953
1034
  if (this.isServer) {
954
1035
  this.logger.warn("'client cookies' is not available on the server.");
955
1036
  } else {
956
- this.persist(res.data, 1);
1037
+ await this.persist(res.data, 1);
957
1038
  }
958
1039
  return { ok: true };
959
1040
  } else {
@@ -1033,7 +1114,7 @@ var SessionManagerCookie = class {
1033
1114
  refreshTokenExpiresAt: subMinutes(addDays(now, 30), 2).getTime()
1034
1115
  };
1035
1116
  cookies.set(this.storageKey, JSON.stringify(obj), {
1036
- expires: subMinutes(addDays(now, 30), 2).getTime()
1117
+ expires: subMinutes(addDays(now, 30), 2)
1037
1118
  });
1038
1119
  }
1039
1120
  return {
@@ -1105,7 +1186,7 @@ var SessionManagerCookie = class {
1105
1186
  refreshTokenExpiresAt: subMinutes(addDays(now, 30), 2).getTime()
1106
1187
  };
1107
1188
  cookies.set(this.storageKey, JSON.stringify(obj), {
1108
- expires: subMinutes(addDays(now, 30), 2).getTime()
1189
+ expires: subMinutes(addDays(now, 30), 2)
1109
1190
  });
1110
1191
  return {
1111
1192
  ok: true,
@@ -1136,15 +1217,41 @@ var SessionManagerCookie = class {
1136
1217
  return await this.refreshV4(session);
1137
1218
  }
1138
1219
  async refreshV4(session) {
1220
+ if (this.v4RefreshPromise) return await this.v4RefreshPromise;
1221
+ this.v4RefreshPromise = this.requestV4Refresh(session);
1222
+ try {
1223
+ return await this.v4RefreshPromise;
1224
+ } finally {
1225
+ this.v4RefreshPromise = null;
1226
+ }
1227
+ }
1228
+ async requestV4Refresh(session) {
1139
1229
  this.logger.info("Refreshing session...");
1230
+ let refreshToken;
1231
+ if (isIOSPlatform()) {
1232
+ refreshToken = await this.refreshTokenStore.get() ?? void 0;
1233
+ if (!refreshToken) {
1234
+ this.logger.warn("No stored refresh token. Session expired.");
1235
+ this.v4AccessToken = null;
1236
+ this.v4AccessTokenExpiresAt = null;
1237
+ return {
1238
+ ok: false,
1239
+ error: {
1240
+ name: "SessionExpiredError",
1241
+ message: "Session expired."
1242
+ }
1243
+ };
1244
+ }
1245
+ }
1140
1246
  this.refreshing = true;
1141
- const res = await this.authService.refreshSession(void 0, 4);
1247
+ const res = await this.authService.refreshSession(refreshToken, 4);
1142
1248
  this.refreshing = false;
1143
1249
  if (!res.ok) {
1144
1250
  this.logger.error(`Failed to refresh session: ${res.error.message}`);
1145
1251
  if (res.error.name === "InvalidTokenError" || res.error.name === "AccountBlacklistedError" || res.error.name === "VerificationLockedError" || res.error.name === "SessionExpiredError") {
1146
1252
  this.v4AccessToken = null;
1147
1253
  this.v4AccessTokenExpiresAt = null;
1254
+ await this.refreshTokenStore.remove();
1148
1255
  }
1149
1256
  return {
1150
1257
  ok: false,
@@ -1152,6 +1259,9 @@ var SessionManagerCookie = class {
1152
1259
  };
1153
1260
  }
1154
1261
  this.logger.success("Session refreshed!");
1262
+ if (res.data.refreshToken) {
1263
+ await this.refreshTokenStore.set(res.data.refreshToken);
1264
+ }
1155
1265
  const now = /* @__PURE__ */ new Date();
1156
1266
  this.v4AccessToken = res.data.accessToken;
1157
1267
  this.v4AccessTokenExpiresAt = addMinutes(now, 5).getTime();
@@ -1184,6 +1294,7 @@ var SessionManagerCookie = class {
1184
1294
  }
1185
1295
  this.v4AccessToken = null;
1186
1296
  this.v4AccessTokenExpiresAt = null;
1297
+ await this.refreshTokenStore.remove();
1187
1298
  cookies.remove(this.storageKey);
1188
1299
  }
1189
1300
  async verify() {
@@ -1227,6 +1338,7 @@ var SessionManagerCookie = class {
1227
1338
  cookies.remove(this.storageKey);
1228
1339
  this.v4AccessToken = null;
1229
1340
  this.v4AccessTokenExpiresAt = null;
1341
+ await this.refreshTokenStore.remove();
1230
1342
  }
1231
1343
  return v ? { valid: true } : { valid: false, reason: "INVALID_SESSION" };
1232
1344
  }
@@ -3530,10 +3642,14 @@ var Sdk = class {
3530
3642
  const interval = clamp(input.interval ?? 3e4, 5e3, 6e4);
3531
3643
  let counter = 0;
3532
3644
  let timeout = null;
3645
+ let isForeground = true;
3646
+ const listener = App.addListener("appStateChange", ({ isActive }) => {
3647
+ isForeground = isActive;
3648
+ });
3533
3649
  const assignTimeout = () => {
3534
3650
  const duration = counter <= 0 ? interval * 0.5 : interval;
3535
3651
  return setTimeout(async () => {
3536
- {
3652
+ if (isForeground) {
3537
3653
  let res;
3538
3654
  try {
3539
3655
  res = await this.sessionManager.verify();
@@ -3553,6 +3669,7 @@ var Sdk = class {
3553
3669
  if (timeout) {
3554
3670
  clearTimeout(timeout);
3555
3671
  }
3672
+ listener.then((handle) => handle.remove());
3556
3673
  };
3557
3674
  }
3558
3675
  async session() {