@vocollege/app 0.0.171 → 0.0.172

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/global.d.ts CHANGED
@@ -32,6 +32,7 @@ export declare type VoAppType = {
32
32
  export declare type VoTokenType = {
33
33
  access_token: string;
34
34
  refresh_token: string;
35
+ expiry_token: string;
35
36
  };
36
37
  export interface GeneralObject {
37
38
  [key: string]: any;
@@ -46,76 +46,97 @@ var react_toastify_1 = require("react-toastify");
46
46
  var js_cookie_1 = __importDefault(require("js-cookie"));
47
47
  // Custom.
48
48
  var I18n_1 = __importDefault(require("./modules/Services/I18n"));
49
- (function () {
50
- var _this = this;
51
- function redirect() {
52
- return __awaiter(this, void 0, void 0, function () {
53
- var error_1;
54
- return __generator(this, function (_a) {
55
- switch (_a.label) {
56
- case 0:
57
- _a.trys.push([0, 2, , 3]);
58
- //console.log("@vocollege/api->interceptor.ts->redirect()");
59
- js_cookie_1.default.set("voapp_redirectTo", window.location.origin, {
60
- domain: ".vo-college.se",
61
- sameSite: "Lax",
62
- });
63
- return [4 /*yield*/, VoAuth_1.default.logout()];
64
- case 1:
65
- _a.sent();
66
- VoRouter_1.default.redirectToLogout();
67
- return [3 /*break*/, 3];
68
- case 2:
69
- error_1 = _a.sent();
70
- console.error("Interceptor", error_1);
71
- VoAuth_1.default.resetSession();
72
- return [3 /*break*/, 3];
73
- case 3: return [2 /*return*/];
74
- }
75
- });
49
+ var isRefreshingToken = false;
50
+ var failedRequestQueue = [];
51
+ function retryFailedRequests() {
52
+ if (failedRequestQueue.length && !isRefreshingToken) {
53
+ failedRequestQueue.forEach(function (request) {
54
+ axios_1.default.request(request);
76
55
  });
56
+ failedRequestQueue = [];
77
57
  }
78
- //console.log("@vocollege/api->interceptor.ts");
58
+ }
59
+ function redirect() {
60
+ return __awaiter(this, void 0, void 0, function () {
61
+ var error_1;
62
+ return __generator(this, function (_a) {
63
+ switch (_a.label) {
64
+ case 0:
65
+ _a.trys.push([0, 2, , 3]);
66
+ js_cookie_1.default.set("voapp_redirectTo", window.location.origin, {
67
+ domain: ".vo-college.se",
68
+ sameSite: "Lax",
69
+ });
70
+ return [4 /*yield*/, VoAuth_1.default.logout()];
71
+ case 1:
72
+ _a.sent();
73
+ VoRouter_1.default.redirectToLogout();
74
+ return [3 /*break*/, 3];
75
+ case 2:
76
+ error_1 = _a.sent();
77
+ console.error("Interceptor", error_1);
78
+ VoAuth_1.default.resetSession();
79
+ return [3 /*break*/, 3];
80
+ case 3: return [2 /*return*/];
81
+ }
82
+ });
83
+ });
84
+ }
85
+ (function () {
86
+ var _this = this;
79
87
  axios_1.default.defaults.withCredentials = true;
80
88
  axios_1.default.defaults.headers.common["X-Requested-With"] = "XMLHttpRequest";
81
89
  axios_1.default.interceptors.request.use(function (req) {
82
- //console.log("Interceptor request", `${req.method} ${req.url}`);
90
+ var token = VoAuth_1.default.getToken();
91
+ if (!req.headers.Authorization && token) {
92
+ req.headers.Authorization = "Bearer ".concat(token.access_token);
93
+ }
83
94
  return req;
84
95
  });
85
96
  axios_1.default.interceptors.response.use(function (res) {
86
97
  return res;
87
98
  }, function (error) { return __awaiter(_this, void 0, void 0, function () {
88
- var config, isTokenRequest, token, error_2;
99
+ var config, isTokenRequest, newToken, refreshError_1, error_2;
89
100
  var _a;
90
101
  return __generator(this, function (_b) {
91
102
  switch (_b.label) {
92
103
  case 0:
93
104
  config = error.config;
94
105
  isTokenRequest = config.url.includes("oauth/token");
106
+ if (isTokenRequest) {
107
+ react_toastify_1.toast.error(I18n_1.default.get.messages.sessionExpired, { autoClose: false });
108
+ redirect();
109
+ return [2 /*return*/, Promise.reject(error)];
110
+ }
95
111
  _b.label = 1;
96
112
  case 1:
97
- _b.trys.push([1, 4, , 5]);
98
- if (!(!isTokenRequest &&
99
- [400, 401, 403].indexOf((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) > -1)) return [3 /*break*/, 3];
100
- return [4 /*yield*/, VoAuth_1.default.refreshToken()];
113
+ _b.trys.push([1, 7, , 8]);
114
+ if (![400, 401, 403].includes((_a = error.response) === null || _a === void 0 ? void 0 : _a.status)) return [3 /*break*/, 6];
115
+ if (!(!isRefreshingToken && !config._retry)) return [3 /*break*/, 6];
116
+ isRefreshingToken = true;
117
+ config._retry = true;
118
+ _b.label = 2;
101
119
  case 2:
102
- _b.sent();
103
- token = VoAuth_1.default.getToken();
104
- if (token) {
105
- config.headers["Authorization"] = "Bearer " + token.access_token;
106
- return [2 /*return*/, axios_1.default.request(config)];
107
- }
108
- _b.label = 3;
109
- case 3: return [3 /*break*/, 5];
120
+ _b.trys.push([2, 4, 5, 6]);
121
+ return [4 /*yield*/, VoAuth_1.default.refreshToken()];
122
+ case 3:
123
+ newToken = _b.sent();
124
+ config.headers.Authorization = "Bearer ".concat(newToken === null || newToken === void 0 ? void 0 : newToken.access_token);
125
+ retryFailedRequests();
126
+ return [2 /*return*/, (0, axios_1.default)(config)];
110
127
  case 4:
111
- error_2 = _b.sent();
112
- return [3 /*break*/, 5];
128
+ refreshError_1 = _b.sent();
129
+ redirect();
130
+ return [2 /*return*/, Promise.reject(refreshError_1)];
113
131
  case 5:
114
- if (isTokenRequest) {
115
- react_toastify_1.toast.error(I18n_1.default.get.messages.sessionExpired, { autoClose: false });
116
- redirect();
117
- }
118
- return [2 /*return*/, Promise.reject(error)];
132
+ isRefreshingToken = false;
133
+ return [7 /*endfinally*/];
134
+ case 6: return [3 /*break*/, 8];
135
+ case 7:
136
+ error_2 = _b.sent();
137
+ console.error("Interceptor error:", error_2);
138
+ return [3 /*break*/, 8];
139
+ case 8: return [2 /*return*/, Promise.reject(error)];
119
140
  }
120
141
  });
121
142
  }); });
@@ -1054,7 +1054,8 @@
1054
1054
  "createLanguageRepresentativeDiploma": "Skapa diplom för språkombud",
1055
1055
  "place": "Region/Ort",
1056
1056
  "educator1": "Utbildare 1",
1057
- "educator2": "Utbildare 2"
1057
+ "educator2": "Utbildare 2",
1058
+ "educator3": "Utbildare 3"
1058
1059
  },
1059
1060
  "messages": {
1060
1061
  "intro": "För diplom till de som genomgått utbildning till språkombud.",
@@ -8,6 +8,7 @@ declare class VoAuth extends VoBase {
8
8
  user: any;
9
9
  ability: AnyMongoAbility;
10
10
  globalAbility: AnyMongoAbility;
11
+ tokenRefreshTimeout: any;
11
12
  constructor(key: string);
12
13
  get getAppLoginUrl(): string;
13
14
  auth(): Promise<unknown>;
@@ -16,6 +17,7 @@ declare class VoAuth extends VoBase {
16
17
  setSession(token: any, updateCookie?: boolean): void;
17
18
  resetSession(): void;
18
19
  refreshToken(): Promise<void | VoTokenType>;
20
+ startTokenRefreshTimer(): void;
19
21
  check(forceRedirect?: boolean, forceLoad?: boolean): Promise<any>;
20
22
  loadUser(force?: boolean): Promise<any>;
21
23
  get currentUser(): any;
@@ -121,6 +121,7 @@ var VoAuth = /** @class */ (function (_super) {
121
121
  var _this = this;
122
122
  return new Promise(function (resolve, reject) { return __awaiter(_this, void 0, void 0, function () {
123
123
  var urlParams, code, state, initiatedAuth, params, url, response, state_1, verifier, error_1;
124
+ var _this = this;
124
125
  return __generator(this, function (_a) {
125
126
  switch (_a.label) {
126
127
  case 0:
@@ -159,6 +160,8 @@ var VoAuth = /** @class */ (function (_super) {
159
160
  return [4 /*yield*/, this.loadUser(true)];
160
161
  case 3:
161
162
  _a.sent();
163
+ this.startTokenRefreshTimer();
164
+ window.addEventListener("beforeunload", function () { return clearTimeout(_this.tokenRefreshTimeout); });
162
165
  resolve(true);
163
166
  return [3 /*break*/, 5];
164
167
  case 4:
@@ -198,33 +201,34 @@ var VoAuth = /** @class */ (function (_super) {
198
201
  };
199
202
  VoAuth.prototype.setSession = function (token, updateCookie) {
200
203
  if (updateCookie === void 0) { updateCookie = true; }
201
- // const { token_type, access_token, refresh_token, expires_in } = token;
202
- var access_token = token.access_token, refresh_token = token.refresh_token;
204
+ var access_token = token.access_token, refresh_token = token.refresh_token, expires_in = token.expires_in;
203
205
  if (updateCookie) {
204
- var refreshTokenKey = VoConfig_1.default.get.AUTH_STORAGE_REFRESH_TOKEN || "";
205
- js_cookie_1.default.set(refreshTokenKey, refresh_token, {
206
+ //console.log("setSession updatingCookie");
207
+ var expiryTime = new Date().getTime() + (expires_in || 3600) * 1000;
208
+ js_cookie_1.default.set(VoConfig_1.default.get.AUTH_STORAGE_EXPIRE_TOKEN || "", expiryTime.toString(), {
209
+ expires: 21,
210
+ sameSite: "Lax",
211
+ domain: VoConfig_1.default.get.AUTH_DOMAIN,
212
+ });
213
+ js_cookie_1.default.set(VoConfig_1.default.get.AUTH_STORAGE_REFRESH_TOKEN || "", refresh_token, {
206
214
  expires: 21,
207
215
  sameSite: "Lax",
208
216
  domain: VoConfig_1.default.get.AUTH_DOMAIN,
209
217
  });
210
218
  var expires = new Date(new Date().getTime() + 4 * 60 * 60 * 1000);
211
- var accessTokenKey = VoConfig_1.default.get.AUTH_STORAGE_ACCESS_TOKEN || "";
212
- js_cookie_1.default.set(accessTokenKey, access_token, {
219
+ js_cookie_1.default.set(VoConfig_1.default.get.AUTH_STORAGE_ACCESS_TOKEN || "", access_token, {
213
220
  expires: expires,
214
221
  sameSite: "Lax",
215
222
  domain: VoConfig_1.default.get.AUTH_DOMAIN,
216
223
  });
217
224
  }
218
- axios_1.default.defaults.headers.common["Authorization"] =
219
- // token_type + " " + access_token;
220
- "Bearer " + access_token;
225
+ axios_1.default.defaults.headers.common["Authorization"] = "Bearer " + access_token;
221
226
  var currentGroup = VoGroups_1.default.getCurrent(true);
222
- axios_1.default.defaults.headers.common["VoGroup"] = currentGroup
223
- ? currentGroup.id
224
- : "";
227
+ axios_1.default.defaults.headers.common["VoGroup"] = currentGroup ? currentGroup.id : "";
228
+ this.startTokenRefreshTimer();
225
229
  };
226
230
  VoAuth.prototype.resetSession = function () {
227
- console.log("Resetting session");
231
+ //console.log("Resetting session");
228
232
  var refreshTokenKey = VoConfig_1.default.get.AUTH_STORAGE_REFRESH_TOKEN || "";
229
233
  Helpers.localStorage.remove(refreshTokenKey);
230
234
  js_cookie_1.default.remove(refreshTokenKey, {
@@ -277,13 +281,47 @@ var VoAuth = /** @class */ (function (_super) {
277
281
  });
278
282
  });
279
283
  };
284
+ VoAuth.prototype.startTokenRefreshTimer = function () {
285
+ var _this = this;
286
+ var expiresAt = js_cookie_1.default.get(VoConfig_1.default.get.AUTH_STORAGE_EXPIRE_TOKEN);
287
+ if (!expiresAt)
288
+ return;
289
+ var expiresInMs = parseInt(expiresAt) - (new Date().getTime());
290
+ var refreshAt = expiresInMs - (30 * 1000); // 30 sec before expiration
291
+ clearTimeout(this.tokenRefreshTimeout);
292
+ this.tokenRefreshTimeout = setTimeout(function () { return __awaiter(_this, void 0, void 0, function () {
293
+ var newToken, error_3;
294
+ return __generator(this, function (_a) {
295
+ switch (_a.label) {
296
+ case 0:
297
+ _a.trys.push([0, 2, , 3]);
298
+ return [4 /*yield*/, this.refreshToken()];
299
+ case 1:
300
+ newToken = _a.sent();
301
+ this.setSession(newToken);
302
+ this.startTokenRefreshTimer();
303
+ return [3 /*break*/, 3];
304
+ case 2:
305
+ error_3 = _a.sent();
306
+ console.error("Token refresh failed:", error_3);
307
+ this.resetSession();
308
+ if (window.location.pathname !== "/login") {
309
+ window.location.href = "/login";
310
+ }
311
+ return [3 /*break*/, 3];
312
+ case 3: return [2 /*return*/];
313
+ }
314
+ });
315
+ }); }, refreshAt);
316
+ //console.log("Setting token refresh timeout in: ", refreshAt);
317
+ };
280
318
  VoAuth.prototype.check = function (forceRedirect, forceLoad) {
281
319
  var _this = this;
282
320
  if (forceRedirect === void 0) { forceRedirect = false; }
283
321
  if (forceLoad === void 0) { forceLoad = false; }
284
322
  this.checkConfig();
285
323
  return new Promise(function (resolve, reject) { return __awaiter(_this, void 0, void 0, function () {
286
- var currentCookies, cookieName, token, error_3;
324
+ var currentCookies, cookieName, token, error_4;
287
325
  return __generator(this, function (_a) {
288
326
  switch (_a.label) {
289
327
  case 0:
@@ -314,8 +352,8 @@ var VoAuth = /** @class */ (function (_super) {
314
352
  resolve(this.user);
315
353
  return [3 /*break*/, 4];
316
354
  case 3:
317
- error_3 = _a.sent();
318
- reject(error_3);
355
+ error_4 = _a.sent();
356
+ reject(error_4);
319
357
  return [3 /*break*/, 4];
320
358
  case 4: return [3 /*break*/, 6];
321
359
  case 5:
@@ -335,7 +373,7 @@ var VoAuth = /** @class */ (function (_super) {
335
373
  var _this = this;
336
374
  if (force === void 0) { force = false; }
337
375
  return new Promise(function (resolve, reject) { return __awaiter(_this, void 0, void 0, function () {
338
- var response, error_4;
376
+ var response, error_5;
339
377
  return __generator(this, function (_a) {
340
378
  switch (_a.label) {
341
379
  case 0:
@@ -354,8 +392,8 @@ var VoAuth = /** @class */ (function (_super) {
354
392
  resolve(this.user);
355
393
  return [3 /*break*/, 4];
356
394
  case 3:
357
- error_4 = _a.sent();
358
- reject(error_4);
395
+ error_5 = _a.sent();
396
+ reject(error_5);
359
397
  return [3 /*break*/, 4];
360
398
  case 4: return [2 /*return*/];
361
399
  }
@@ -382,10 +420,9 @@ var VoAuth = /** @class */ (function (_super) {
382
420
  });
383
421
  VoAuth.prototype.getToken = function () {
384
422
  try {
385
- var refreshTokenKey = VoConfig_1.default.get.AUTH_STORAGE_REFRESH_TOKEN || "";
386
- var refreshToken = js_cookie_1.default.get(refreshTokenKey);
387
- var accessTokenKey = VoConfig_1.default.get.AUTH_STORAGE_ACCESS_TOKEN || "";
388
- var accessToken = js_cookie_1.default.get(accessTokenKey);
423
+ var refreshToken = js_cookie_1.default.get(VoConfig_1.default.get.AUTH_STORAGE_REFRESH_TOKEN || "");
424
+ var accessToken = js_cookie_1.default.get(VoConfig_1.default.get.AUTH_STORAGE_ACCESS_TOKEN || "");
425
+ var expiryToken = js_cookie_1.default.get(VoConfig_1.default.get.AUTH_STORAGE_EXPIRE_TOKEN || "");
389
426
  // let tokenTypeKey = VoConfig.get.AUTH_STORAGE_TOKEN_TYPE || "";
390
427
  // const tokenType = JsCookies.get(tokenTypeKey);
391
428
  // let expiresInKey = VoConfig.get.AUTH_STORAGE_EXPIRES_IN || "";
@@ -395,6 +432,7 @@ var VoAuth = /** @class */ (function (_super) {
395
432
  return {
396
433
  refresh_token: refreshToken,
397
434
  access_token: accessToken || "",
435
+ expiry_token: expiryToken || "",
398
436
  // token_type: tokenType,
399
437
  // expires_in: expiresIn,
400
438
  };
@@ -404,9 +442,6 @@ var VoAuth = /** @class */ (function (_super) {
404
442
  throw error;
405
443
  }
406
444
  };
407
- // const getLoginUrl = () => {
408
- // return 'http://localhost:8000/oauth/authorize?client_id=91c51b49-e705-4993-a8c7-497ee47e869e&redirect_uri=http://localhost:3000/auth&response_type=code&scope=*&state=' + state + '&code_challenge=' + challenge + '&code_challenge_method=S256'
409
- // }
410
445
  VoAuth.prototype.initAuth = function () {
411
446
  try {
412
447
  var state = Helpers.createRandomString(40);
@@ -47,6 +47,7 @@ var VoConfig = /** @class */ (function () {
47
47
  AUTH_STORAGE_VERIFIER: "vo_verifier",
48
48
  AUTH_STORAGE_REFRESH_TOKEN: "vo_rtoken",
49
49
  AUTH_STORAGE_ACCESS_TOKEN: "vo_atoken",
50
+ AUTH_STORAGE_EXPIRE_TOKEN: "vo_etoken",
50
51
  // AUTH_STORAGE_TOKEN_TYPE: "voapp_tokentype",
51
52
  // AUTH_STORAGE_EXPIRES_IN: "voapp_expiresin",
52
53
  AUTH_DOMAIN: ".vo-college.se",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vocollege/app",
3
- "version": "0.0.171",
3
+ "version": "0.0.172",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -41,5 +41,6 @@
41
41
  "js-cookie": "^3.0.1",
42
42
  "react": "^18.3.1",
43
43
  "react-toastify": "^8.0.0"
44
- }
44
+ },
45
+ "dependencies": {}
45
46
  }
package/src/global.ts CHANGED
@@ -34,6 +34,7 @@ export type VoTokenType = {
34
34
  // token_type: string;
35
35
  access_token: string;
36
36
  refresh_token: string;
37
+ expiry_token: string;
37
38
  // expires_in: string;
38
39
  };
39
40
 
@@ -7,28 +7,41 @@ import Cookies from "js-cookie";
7
7
  // Custom.
8
8
  import I18n from "./modules/Services/I18n";
9
9
 
10
- (function () {
11
- async function redirect() {
12
- try {
13
- //console.log("@vocollege/api->interceptor.ts->redirect()");
14
- Cookies.set("voapp_redirectTo", window.location.origin, {
15
- domain: ".vo-college.se",
16
- sameSite: "Lax",
17
- });
18
- await VoAuth.logout();
19
- VoRouter.redirectToLogout();
20
- } catch (error) {
21
- console.error("Interceptor", error);
22
- VoAuth.resetSession();
23
- }
10
+ let isRefreshingToken = false;
11
+ let failedRequestQueue: any[] = [];
12
+
13
+ function retryFailedRequests() {
14
+ if (failedRequestQueue.length && !isRefreshingToken) {
15
+ failedRequestQueue.forEach((request) => {
16
+ axios.request(request);
17
+ });
18
+ failedRequestQueue = [];
24
19
  }
20
+ }
25
21
 
26
- //console.log("@vocollege/api->interceptor.ts");
22
+ async function redirect() {
23
+ try {
24
+ Cookies.set("voapp_redirectTo", window.location.origin, {
25
+ domain: ".vo-college.se",
26
+ sameSite: "Lax",
27
+ });
28
+ await VoAuth.logout();
29
+ VoRouter.redirectToLogout();
30
+ } catch (error) {
31
+ console.error("Interceptor", error);
32
+ VoAuth.resetSession();
33
+ }
34
+ }
35
+
36
+ (function () {
27
37
  axios.defaults.withCredentials = true;
28
38
  axios.defaults.headers.common["X-Requested-With"] = "XMLHttpRequest";
29
39
 
30
40
  axios.interceptors.request.use((req) => {
31
- //console.log("Interceptor request", `${req.method} ${req.url}`);
41
+ const token = VoAuth.getToken();
42
+ if (!req.headers.Authorization && token) {
43
+ req.headers.Authorization = `Bearer ${token.access_token}`;
44
+ }
32
45
  return req;
33
46
  });
34
47
 
@@ -39,28 +52,44 @@ import I18n from "./modules/Services/I18n";
39
52
  async (error) => {
40
53
  const { config } = error;
41
54
  let isTokenRequest = config.url.includes("oauth/token");
42
- /*console.log(
43
- "@vocollege/api->interceptor.js - isTokenRequest: ",
44
- isTokenRequest,
45
- );*/
55
+
56
+ if (isTokenRequest) {
57
+ toast.error(I18n.get.messages.sessionExpired, { autoClose: false });
58
+ redirect();
59
+ return Promise.reject(error);
60
+ }
61
+
46
62
  try {
47
- if (
48
- !isTokenRequest &&
49
- [400, 401, 403].indexOf(error.response?.status) > -1
50
- ) {
51
- await VoAuth.refreshToken();
63
+ if ([400, 401, 403].includes(error.response?.status)) {
64
+ if (!isRefreshingToken && !config._retry) {
65
+ isRefreshingToken = true;
66
+ config._retry = true;
67
+
68
+ try {
69
+ const newToken = await VoAuth.refreshToken();
70
+ config.headers.Authorization = `Bearer ${newToken?.access_token}`;
71
+ retryFailedRequests();
72
+ return axios(config);
73
+ } catch (refreshError) {
74
+ redirect();
75
+ return Promise.reject(refreshError);
76
+ } finally {
77
+ isRefreshingToken = false;
78
+ }
79
+ }
80
+ }
81
+ /*await VoAuth.refreshToken();
52
82
  const token: any = VoAuth.getToken();
53
83
  if (token) {
54
84
  config.headers["Authorization"] = "Bearer " + token.access_token;
55
85
  return axios.request(config);
56
86
  }
57
- }
58
- } catch (error) {}
59
-
60
- if (isTokenRequest) {
61
- toast.error(I18n.get.messages.sessionExpired, { autoClose: false });
62
- redirect();
87
+ }*/
88
+ } catch (error) {
89
+ console.error("Interceptor error:", error);
63
90
  }
91
+
92
+
64
93
  return Promise.reject(error);
65
94
  },
66
95
  );
@@ -1054,7 +1054,8 @@
1054
1054
  "createLanguageRepresentativeDiploma": "Skapa diplom för språkombud",
1055
1055
  "place": "Region/Ort",
1056
1056
  "educator1": "Utbildare 1",
1057
- "educator2": "Utbildare 2"
1057
+ "educator2": "Utbildare 2",
1058
+ "educator3": "Utbildare 3"
1058
1059
  },
1059
1060
  "messages": {
1060
1061
  "intro": "För diplom till de som genomgått utbildning till språkombud.",
@@ -18,6 +18,7 @@ class VoAuth extends VoBase {
18
18
  user: any = null;
19
19
  ability: AnyMongoAbility;
20
20
  globalAbility: AnyMongoAbility;
21
+ tokenRefreshTimeout: any;
21
22
 
22
23
  constructor(key: string) {
23
24
  super(key);
@@ -76,6 +77,8 @@ class VoAuth extends VoBase {
76
77
 
77
78
  this.setSession(response.data);
78
79
  await this.loadUser(true);
80
+ this.startTokenRefreshTimer();
81
+ window.addEventListener("beforeunload", () => clearTimeout(this.tokenRefreshTimeout));
79
82
  resolve(true);
80
83
  } else {
81
84
  reject("Authorization failed");
@@ -99,44 +102,48 @@ class VoAuth extends VoBase {
99
102
  }
100
103
 
101
104
  setSession(token: any, updateCookie = true) {
102
- // const { token_type, access_token, refresh_token, expires_in } = token;
103
- const { access_token, refresh_token } = token;
105
+ const { access_token, refresh_token, expires_in } = token;
104
106
 
105
107
  if (updateCookie) {
106
- let refreshTokenKey = VoConfig.get.AUTH_STORAGE_REFRESH_TOKEN || "";
107
- JsCookies.set(refreshTokenKey, refresh_token, {
108
+
109
+ //console.log("setSession updatingCookie");
110
+ const expiryTime = new Date().getTime() + (expires_in || 3600) * 1000;
111
+ JsCookies.set(VoConfig.get.AUTH_STORAGE_EXPIRE_TOKEN || "", expiryTime.toString(), {
112
+ expires: 21,
113
+ sameSite: "Lax",
114
+ domain: VoConfig.get.AUTH_DOMAIN,
115
+ });
116
+
117
+ JsCookies.set(VoConfig.get.AUTH_STORAGE_REFRESH_TOKEN || "", refresh_token, {
108
118
  expires: 21,
109
119
  sameSite: "Lax",
110
120
  domain: VoConfig.get.AUTH_DOMAIN,
111
121
  });
112
122
 
113
123
  let expires = new Date(new Date().getTime() + 4 * 60 * 60 * 1000);
114
- let accessTokenKey = VoConfig.get.AUTH_STORAGE_ACCESS_TOKEN || "";
115
- JsCookies.set(accessTokenKey, access_token, {
124
+ JsCookies.set(VoConfig.get.AUTH_STORAGE_ACCESS_TOKEN || "", access_token, {
116
125
  expires,
117
126
  sameSite: "Lax",
118
127
  domain: VoConfig.get.AUTH_DOMAIN,
119
128
  });
120
129
  }
121
130
 
122
- axios.defaults.headers.common["Authorization"] =
123
- // token_type + " " + access_token;
124
- "Bearer " + access_token;
131
+ axios.defaults.headers.common["Authorization"] = "Bearer " + access_token;
125
132
  let currentGroup = VoGroups.getCurrent(true);
126
- axios.defaults.headers.common["VoGroup"] = currentGroup
127
- ? currentGroup.id
128
- : "";
133
+ axios.defaults.headers.common["VoGroup"] = currentGroup ? currentGroup.id : "";
134
+ this.startTokenRefreshTimer();
129
135
  }
136
+
130
137
  resetSession() {
131
- console.log("Resetting session");
132
- let refreshTokenKey = VoConfig.get.AUTH_STORAGE_REFRESH_TOKEN || "";
138
+ //console.log("Resetting session");
139
+ const refreshTokenKey = VoConfig.get.AUTH_STORAGE_REFRESH_TOKEN || "";
133
140
  Helpers.localStorage.remove(refreshTokenKey);
134
141
  JsCookies.remove(refreshTokenKey, {
135
142
  domain: VoConfig.get.AUTH_DOMAIN,
136
143
  sameSite: "Lax",
137
144
  });
138
145
 
139
- let accessTokenKey = VoConfig.get.AUTH_STORAGE_ACCESS_TOKEN || "";
146
+ const accessTokenKey = VoConfig.get.AUTH_STORAGE_ACCESS_TOKEN || "";
140
147
  Helpers.localStorage.remove(accessTokenKey);
141
148
  JsCookies.remove(accessTokenKey, {
142
149
  domain: VoConfig.get.AUTH_DOMAIN,
@@ -149,6 +156,7 @@ class VoAuth extends VoBase {
149
156
 
150
157
  delete axios.defaults.headers.common["Authorization"];
151
158
  }
159
+
152
160
  async refreshToken() {
153
161
  try {
154
162
  const token: any = this.getToken();
@@ -175,6 +183,29 @@ class VoAuth extends VoBase {
175
183
  }
176
184
  }
177
185
 
186
+ startTokenRefreshTimer() {
187
+ const expiresAt = JsCookies.get(VoConfig.get.AUTH_STORAGE_EXPIRE_TOKEN);
188
+ if (!expiresAt) return;
189
+
190
+ const expiresInMs = parseInt(expiresAt) - (new Date().getTime());
191
+ const refreshAt = expiresInMs - (30 * 1000); // 30 sec before expiration
192
+ clearTimeout(this.tokenRefreshTimeout);
193
+ this.tokenRefreshTimeout = setTimeout(async () => {
194
+ try {
195
+ const newToken = await this.refreshToken();
196
+ this.setSession(newToken);
197
+ this.startTokenRefreshTimer();
198
+ } catch (error) {
199
+ console.error("Token refresh failed:", error);
200
+ this.resetSession();
201
+ if (window.location.pathname !== "/login") {
202
+ window.location.href = "/login";
203
+ }
204
+ }
205
+ }, refreshAt);
206
+ //console.log("Setting token refresh timeout in: ", refreshAt);
207
+ }
208
+
178
209
  check(forceRedirect = false, forceLoad = false): Promise<any> {
179
210
  this.checkConfig();
180
211
  return new Promise(async (resolve, reject) => {
@@ -214,6 +245,7 @@ class VoAuth extends VoBase {
214
245
  }
215
246
  });
216
247
  }
248
+
217
249
  loadUser(force = false): Promise<any> {
218
250
  return new Promise(async (resolve, reject) => {
219
251
  try {
@@ -231,9 +263,11 @@ class VoAuth extends VoBase {
231
263
  }
232
264
  });
233
265
  }
266
+
234
267
  get currentUser() {
235
268
  return this.user;
236
269
  }
270
+
237
271
  get isOnlyValidigCandidate() {
238
272
  if (this.user?.validig) {
239
273
  return this.user?.roles?.length == 1 && this.user?.roles.find((r: any) => r.name ==="candidate");
@@ -243,11 +277,9 @@ class VoAuth extends VoBase {
243
277
 
244
278
  getToken(): VoTokenType | void {
245
279
  try {
246
- let refreshTokenKey = VoConfig.get.AUTH_STORAGE_REFRESH_TOKEN || "";
247
- const refreshToken = JsCookies.get(refreshTokenKey);
248
-
249
- let accessTokenKey = VoConfig.get.AUTH_STORAGE_ACCESS_TOKEN || "";
250
- const accessToken = JsCookies.get(accessTokenKey);
280
+ const refreshToken = JsCookies.get(VoConfig.get.AUTH_STORAGE_REFRESH_TOKEN || "");
281
+ const accessToken = JsCookies.get(VoConfig.get.AUTH_STORAGE_ACCESS_TOKEN || "");
282
+ const expiryToken = JsCookies.get(VoConfig.get.AUTH_STORAGE_EXPIRE_TOKEN || "");
251
283
 
252
284
  // let tokenTypeKey = VoConfig.get.AUTH_STORAGE_TOKEN_TYPE || "";
253
285
  // const tokenType = JsCookies.get(tokenTypeKey);
@@ -260,6 +292,7 @@ class VoAuth extends VoBase {
260
292
  return {
261
293
  refresh_token: refreshToken,
262
294
  access_token: accessToken || "",
295
+ expiry_token: expiryToken || "",
263
296
  // token_type: tokenType,
264
297
  // expires_in: expiresIn,
265
298
  };
@@ -269,17 +302,13 @@ class VoAuth extends VoBase {
269
302
  }
270
303
  }
271
304
 
272
- // const getLoginUrl = () => {
273
- // return 'http://localhost:8000/oauth/authorize?client_id=91c51b49-e705-4993-a8c7-497ee47e869e&redirect_uri=http://localhost:3000/auth&response_type=code&scope=*&state=' + state + '&code_challenge=' + challenge + '&code_challenge_method=S256'
274
- // }
275
-
276
305
  initAuth(): { state: string; challenge: string } {
277
306
  try {
278
- let state = Helpers.createRandomString(40);
279
- let verifier = Helpers.createRandomString(128);
280
- let challenge = Helpers.base64Url(crypto.SHA256(verifier));
307
+ const state = Helpers.createRandomString(40);
308
+ const verifier = Helpers.createRandomString(128);
309
+ const challenge = Helpers.base64Url(crypto.SHA256(verifier));
281
310
 
282
- let stateKey = VoConfig.get.AUTH_STORAGE_STATE || "";
311
+ const stateKey = VoConfig.get.AUTH_STORAGE_STATE || "";
283
312
  Helpers.localStorage.remove(stateKey);
284
313
  JsCookies.set(stateKey, state, {
285
314
  expires: 1,
@@ -287,7 +316,7 @@ class VoAuth extends VoBase {
287
316
  domain: VoConfig.get.AUTH_DOMAIN,
288
317
  });
289
318
 
290
- let verifierKey = VoConfig.get.AUTH_STORAGE_VERIFIER || "";
319
+ const verifierKey = VoConfig.get.AUTH_STORAGE_VERIFIER || "";
291
320
  Helpers.localStorage.remove(verifierKey);
292
321
  JsCookies.set(verifierKey, verifier, {
293
322
  expires: 1,
@@ -295,7 +324,7 @@ class VoAuth extends VoBase {
295
324
  domain: VoConfig.get.AUTH_DOMAIN,
296
325
  });
297
326
 
298
- let accessTokenKey = VoConfig.get.AUTH_STORAGE_ACCESS_TOKEN || "";
327
+ const accessTokenKey = VoConfig.get.AUTH_STORAGE_ACCESS_TOKEN || "";
299
328
  Helpers.localStorage.remove(accessTokenKey);
300
329
  JsCookies.remove(accessTokenKey, {
301
330
  domain: VoConfig.get.AUTH_DOMAIN,
@@ -303,7 +332,7 @@ class VoAuth extends VoBase {
303
332
  expires: 1,
304
333
  });
305
334
 
306
- let refreshTokenKey = VoConfig.get.AUTH_STORAGE_REFRESH_TOKEN || "";
335
+ const refreshTokenKey = VoConfig.get.AUTH_STORAGE_REFRESH_TOKEN || "";
307
336
  Helpers.localStorage.remove(refreshTokenKey);
308
337
  JsCookies.remove(refreshTokenKey, {
309
338
  domain: VoConfig.get.AUTH_DOMAIN,
@@ -327,6 +356,7 @@ class VoAuth extends VoBase {
327
356
  throw error;
328
357
  }
329
358
  }
359
+
330
360
  getInitiatedAuth(): { state: string; verifier: string } {
331
361
  let stateKey = VoConfig.get.AUTH_STORAGE_STATE || "";
332
362
  // const state = Helpers.localStorage.get(stateKey);
@@ -47,6 +47,7 @@ class VoConfig {
47
47
  AUTH_STORAGE_VERIFIER: "vo_verifier",
48
48
  AUTH_STORAGE_REFRESH_TOKEN: "vo_rtoken",
49
49
  AUTH_STORAGE_ACCESS_TOKEN: "vo_atoken",
50
+ AUTH_STORAGE_EXPIRE_TOKEN: "vo_etoken",
50
51
  // AUTH_STORAGE_TOKEN_TYPE: "voapp_tokentype",
51
52
  // AUTH_STORAGE_EXPIRES_IN: "voapp_expiresin",
52
53
  AUTH_DOMAIN: ".vo-college.se",