@opexa/portal-sdk 0.59.95 → 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,4 +1,4 @@
1
- import { CmsPortalService, GameService, ExtensionService, FileService, WalletService, AccountService, ReportService, PortalService, TriggerService, AuthService, getFingerPrint } from './chunk-RCMZBLLX.js';
1
+ import { CmsPortalService, GameService, ExtensionService, FileService, WalletService, AccountService, ReportService, PortalService, TriggerService, AuthService, getFingerPrint, isIOSPlatform } from './chunk-MJLXQ6HC.js';
2
2
  import { GraphQLClient, isPlainObject } from './chunk-7APXFZ5G.js';
3
3
  import './chunk-WVFSGB7Y.js';
4
4
  import { ObjectId } from '@opexa/object-id';
@@ -336,6 +336,48 @@ function pollable(func, config) {
336
336
  };
337
337
  }
338
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
+
339
381
  // src/sdk/session-manager.ts
340
382
  var SessionManager = class {
341
383
  logger;
@@ -345,17 +387,22 @@ var SessionManager = class {
345
387
  walletService;
346
388
  _refreshing = false;
347
389
  /*
348
- * v4 access tokens are never persisted (localStorage/cookie) so that an
349
- * XSS-readable storage layer never holds a live access token. They only
350
- * 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.
351
393
  */
352
394
  v4AccessToken = null;
353
395
  v4AccessTokenExpiresAt = null;
354
396
  v4RefreshPromise = null;
397
+ refreshTokenStore;
355
398
  constructor(config) {
356
399
  this.authService = config.authService;
357
400
  this.walletService = config.walletService;
358
401
  this.logger = config.logger;
402
+ this.refreshTokenStore = createRefreshTokenStore(
403
+ config.logger,
404
+ config.sessionPrefix
405
+ );
359
406
  if (config.sessionPrefix) {
360
407
  this.storageKey = `${config.sessionPrefix}/${this.storageKey}`;
361
408
  this.platformStorageKey = `${config.sessionPrefix}/${this.platformStorageKey}`;
@@ -367,7 +414,7 @@ var SessionManager = class {
367
414
  set refreshing(value) {
368
415
  this._refreshing = value;
369
416
  }
370
- persist(data, version) {
417
+ async persist(data, version) {
371
418
  const now = /* @__PURE__ */ new Date();
372
419
  if (version === 4) {
373
420
  if (!data.session || !data.accessToken) {
@@ -378,6 +425,9 @@ var SessionManager = class {
378
425
  }
379
426
  this.v4AccessToken = data.accessToken;
380
427
  this.v4AccessTokenExpiresAt = addMinutes(now, 4).getTime();
428
+ if (data.refreshToken) {
429
+ await this.refreshTokenStore.set(data.refreshToken);
430
+ }
381
431
  localStorage.setItem(
382
432
  this.storageKey,
383
433
  JSON.stringify({ version, session: data.session })
@@ -440,7 +490,8 @@ var SessionManager = class {
440
490
  maxAttempt: 5
441
491
  })();
442
492
  if (!r1.ok) return r1;
443
- if (!this.persist(r1.data, version)) return this.malformedResponseError();
493
+ if (!await this.persist(r1.data, version))
494
+ return this.malformedResponseError();
444
495
  return {
445
496
  ok: true,
446
497
  data: null
@@ -449,7 +500,7 @@ var SessionManager = class {
449
500
  if (input.type === "MOBILE_NUMBER") {
450
501
  const res2 = await this.authService.createSession(input, version);
451
502
  if (res2.ok) {
452
- if (!this.persist(res2.data, version))
503
+ if (!await this.persist(res2.data, version))
453
504
  return this.malformedResponseError();
454
505
  return {
455
506
  ok: true,
@@ -468,7 +519,7 @@ var SessionManager = class {
468
519
  version
469
520
  );
470
521
  if (res2.ok) {
471
- if (!this.persist(res2.data, version))
522
+ if (!await this.persist(res2.data, version))
472
523
  return this.malformedResponseError();
473
524
  return {
474
525
  ok: true,
@@ -481,7 +532,7 @@ var SessionManager = class {
481
532
  localStorage.setItem(this.platformStorageKey, "CABINET");
482
533
  const res2 = await this.authService.createSession(input, version);
483
534
  if (res2.ok) {
484
- if (!this.persist(res2.data, version))
535
+ if (!await this.persist(res2.data, version))
485
536
  return this.malformedResponseError();
486
537
  return {
487
538
  ok: true,
@@ -500,7 +551,8 @@ var SessionManager = class {
500
551
  }
501
552
  };
502
553
  }
503
- if (!this.persist(res.data, version)) return this.malformedResponseError();
554
+ if (!await this.persist(res.data, version))
555
+ return this.malformedResponseError();
504
556
  return {
505
557
  ok: true,
506
558
  data: null
@@ -511,7 +563,7 @@ var SessionManager = class {
511
563
  if (res.ok) {
512
564
  if (this.isServer) {
513
565
  this.logger.warn("'localStorage' is not available on the server.");
514
- } else if (!this.persist(res.data, 1)) {
566
+ } else if (!await this.persist(res.data, 1)) {
515
567
  return this.malformedResponseError();
516
568
  }
517
569
  return { ok: true };
@@ -701,14 +753,31 @@ var SessionManager = class {
701
753
  }
702
754
  async requestV4Refresh(session) {
703
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
+ }
704
772
  this.refreshing = true;
705
- const res = await this.authService.refreshSession(void 0, 4);
773
+ const res = await this.authService.refreshSession(refreshToken, 4);
706
774
  this.refreshing = false;
707
775
  if (!res.ok) {
708
776
  this.logger.error(`Failed to refresh session: ${res.error.message}`);
709
777
  if (res.error.name === "InvalidTokenError" || res.error.name === "AccountBlacklistedError" || res.error.name === "VerificationLockedError" || res.error.name === "SessionExpiredError") {
710
778
  this.v4AccessToken = null;
711
779
  this.v4AccessTokenExpiresAt = null;
780
+ await this.refreshTokenStore.remove();
712
781
  }
713
782
  return {
714
783
  ok: false,
@@ -716,6 +785,9 @@ var SessionManager = class {
716
785
  };
717
786
  }
718
787
  this.logger.success("Session refreshed!");
788
+ if (res.data.refreshToken) {
789
+ await this.refreshTokenStore.set(res.data.refreshToken);
790
+ }
719
791
  const now = /* @__PURE__ */ new Date();
720
792
  this.v4AccessToken = res.data.accessToken;
721
793
  this.v4AccessTokenExpiresAt = addMinutes(now, 4).getTime();
@@ -748,6 +820,7 @@ var SessionManager = class {
748
820
  }
749
821
  this.v4AccessToken = null;
750
822
  this.v4AccessTokenExpiresAt = null;
823
+ await this.refreshTokenStore.remove();
751
824
  localStorage.removeItem(this.storageKey);
752
825
  }
753
826
  async verify() {
@@ -791,6 +864,7 @@ var SessionManager = class {
791
864
  localStorage.removeItem(this.storageKey);
792
865
  this.v4AccessToken = null;
793
866
  this.v4AccessTokenExpiresAt = null;
867
+ await this.refreshTokenStore.remove();
794
868
  }
795
869
  return v ? { valid: true } : { valid: false, reason: "INVALID_SESSION" };
796
870
  }
@@ -820,16 +894,19 @@ var SessionManagerCookie = class {
820
894
  walletService;
821
895
  _refreshing = false;
822
896
  /*
823
- * v4 access tokens are never persisted (localStorage/cookie) so that an
824
- * XSS-readable storage layer never holds a live access token. They only
825
- * 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.
826
900
  */
827
901
  v4AccessToken = null;
828
902
  v4AccessTokenExpiresAt = null;
903
+ v4RefreshPromise = null;
904
+ refreshTokenStore;
829
905
  constructor(config) {
830
906
  this.authService = config.authService;
831
907
  this.walletService = config.walletService;
832
908
  this.logger = config.logger;
909
+ this.refreshTokenStore = createRefreshTokenStore(config.logger);
833
910
  }
834
911
  get refreshing() {
835
912
  return this._refreshing;
@@ -837,15 +914,18 @@ var SessionManagerCookie = class {
837
914
  set refreshing(value) {
838
915
  this._refreshing = value;
839
916
  }
840
- persist(data, version) {
917
+ async persist(data, version) {
841
918
  const now = /* @__PURE__ */ new Date();
842
919
  if (version === 4) {
843
920
  this.v4AccessToken = data.accessToken ?? null;
844
921
  this.v4AccessTokenExpiresAt = addMinutes(now, 5).getTime();
922
+ if (data.refreshToken) {
923
+ await this.refreshTokenStore.set(data.refreshToken);
924
+ }
845
925
  cookies.set(
846
926
  this.storageKey,
847
927
  JSON.stringify({ version, session: data.session }),
848
- { expires: subMinutes(addDays(now, 15), 2).getTime() }
928
+ { expires: subMinutes(addDays(now, 15), 2) }
849
929
  );
850
930
  return;
851
931
  }
@@ -857,7 +937,7 @@ var SessionManagerCookie = class {
857
937
  accessTokenExpiresAt: addMinutes(now, 8).getTime(),
858
938
  refreshTokenExpiresAt: subMinutes(addDays(now, 30), 2).getTime()
859
939
  }),
860
- { expires: subMinutes(addDays(now, 30), 2).getTime() }
940
+ { expires: subMinutes(addDays(now, 30), 2) }
861
941
  );
862
942
  }
863
943
  async create(input, version = 1) {
@@ -886,7 +966,7 @@ var SessionManagerCookie = class {
886
966
  maxAttempt: 5
887
967
  })();
888
968
  if (!r1.ok) return r1;
889
- this.persist(r1.data, version);
969
+ await this.persist(r1.data, version);
890
970
  return {
891
971
  ok: true,
892
972
  data: null
@@ -895,7 +975,7 @@ var SessionManagerCookie = class {
895
975
  if (input.type === "MOBILE_NUMBER") {
896
976
  const res2 = await this.authService.createSession(input, version);
897
977
  if (res2.ok) {
898
- this.persist(res2.data, version);
978
+ await this.persist(res2.data, version);
899
979
  return {
900
980
  ok: true,
901
981
  data: null
@@ -912,7 +992,7 @@ var SessionManagerCookie = class {
912
992
  version
913
993
  );
914
994
  if (res2.ok) {
915
- this.persist(res2.data, version);
995
+ await this.persist(res2.data, version);
916
996
  return {
917
997
  ok: true,
918
998
  data: null
@@ -924,7 +1004,7 @@ var SessionManagerCookie = class {
924
1004
  localStorage.setItem(this.platformStorageKey, "CABINET");
925
1005
  const res2 = await this.authService.createSession(input, version);
926
1006
  if (res2.ok) {
927
- this.persist(res2.data, version);
1007
+ await this.persist(res2.data, version);
928
1008
  return {
929
1009
  ok: true,
930
1010
  data: null
@@ -942,7 +1022,7 @@ var SessionManagerCookie = class {
942
1022
  }
943
1023
  };
944
1024
  }
945
- this.persist(res.data, version);
1025
+ await this.persist(res.data, version);
946
1026
  return {
947
1027
  ok: true,
948
1028
  data: null
@@ -954,7 +1034,7 @@ var SessionManagerCookie = class {
954
1034
  if (this.isServer) {
955
1035
  this.logger.warn("'client cookies' is not available on the server.");
956
1036
  } else {
957
- this.persist(res.data, 1);
1037
+ await this.persist(res.data, 1);
958
1038
  }
959
1039
  return { ok: true };
960
1040
  } else {
@@ -1034,7 +1114,7 @@ var SessionManagerCookie = class {
1034
1114
  refreshTokenExpiresAt: subMinutes(addDays(now, 30), 2).getTime()
1035
1115
  };
1036
1116
  cookies.set(this.storageKey, JSON.stringify(obj), {
1037
- expires: subMinutes(addDays(now, 30), 2).getTime()
1117
+ expires: subMinutes(addDays(now, 30), 2)
1038
1118
  });
1039
1119
  }
1040
1120
  return {
@@ -1106,7 +1186,7 @@ var SessionManagerCookie = class {
1106
1186
  refreshTokenExpiresAt: subMinutes(addDays(now, 30), 2).getTime()
1107
1187
  };
1108
1188
  cookies.set(this.storageKey, JSON.stringify(obj), {
1109
- expires: subMinutes(addDays(now, 30), 2).getTime()
1189
+ expires: subMinutes(addDays(now, 30), 2)
1110
1190
  });
1111
1191
  return {
1112
1192
  ok: true,
@@ -1137,15 +1217,41 @@ var SessionManagerCookie = class {
1137
1217
  return await this.refreshV4(session);
1138
1218
  }
1139
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) {
1140
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
+ }
1141
1246
  this.refreshing = true;
1142
- const res = await this.authService.refreshSession(void 0, 4);
1247
+ const res = await this.authService.refreshSession(refreshToken, 4);
1143
1248
  this.refreshing = false;
1144
1249
  if (!res.ok) {
1145
1250
  this.logger.error(`Failed to refresh session: ${res.error.message}`);
1146
1251
  if (res.error.name === "InvalidTokenError" || res.error.name === "AccountBlacklistedError" || res.error.name === "VerificationLockedError" || res.error.name === "SessionExpiredError") {
1147
1252
  this.v4AccessToken = null;
1148
1253
  this.v4AccessTokenExpiresAt = null;
1254
+ await this.refreshTokenStore.remove();
1149
1255
  }
1150
1256
  return {
1151
1257
  ok: false,
@@ -1153,6 +1259,9 @@ var SessionManagerCookie = class {
1153
1259
  };
1154
1260
  }
1155
1261
  this.logger.success("Session refreshed!");
1262
+ if (res.data.refreshToken) {
1263
+ await this.refreshTokenStore.set(res.data.refreshToken);
1264
+ }
1156
1265
  const now = /* @__PURE__ */ new Date();
1157
1266
  this.v4AccessToken = res.data.accessToken;
1158
1267
  this.v4AccessTokenExpiresAt = addMinutes(now, 5).getTime();
@@ -1185,6 +1294,7 @@ var SessionManagerCookie = class {
1185
1294
  }
1186
1295
  this.v4AccessToken = null;
1187
1296
  this.v4AccessTokenExpiresAt = null;
1297
+ await this.refreshTokenStore.remove();
1188
1298
  cookies.remove(this.storageKey);
1189
1299
  }
1190
1300
  async verify() {
@@ -1228,6 +1338,7 @@ var SessionManagerCookie = class {
1228
1338
  cookies.remove(this.storageKey);
1229
1339
  this.v4AccessToken = null;
1230
1340
  this.v4AccessTokenExpiresAt = null;
1341
+ await this.refreshTokenStore.remove();
1231
1342
  }
1232
1343
  return v ? { valid: true } : { valid: false, reason: "INVALID_SESSION" };
1233
1344
  }