asab_webui_shell 25.2.6 → 25.2.7

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.
@@ -105,6 +105,16 @@ class SeaCatAuthApi {
105
105
  });
106
106
  return this.OidcAPI.post('/token', qs.toString());
107
107
  }
108
+
109
+ // Method for token refresh
110
+ token_refresh(refresh_token) {
111
+ var qs = new URLSearchParams({
112
+ grant_type: "refresh_token",
113
+ refresh_token: refresh_token,
114
+ client_id: this.ClientId
115
+ });
116
+ return this.OidcAPI.post('/token', qs.toString());
117
+ }
108
118
  }
109
119
  exports.SeaCatAuthApi = SeaCatAuthApi;
110
120
  ;
@@ -21,7 +21,7 @@ var InvitationScreen = /*#__PURE__*/(0, _react.lazy)(() => Promise.resolve().the
21
21
  class AuthModule extends _asab_webui_components.Module {
22
22
  constructor(app, name) {
23
23
  super(app, "AuthModule");
24
- this.OAuthToken = JSON.parse(sessionStorage.getItem('SeaCatOAuth2Token'));
24
+ this.OAuthTokens = JSON.parse(sessionStorage.getItem('SeaCatOAuth2Tokens'));
25
25
  this.UserInfo = null;
26
26
  this.Api = new _api.SeaCatAuthApi(app);
27
27
  this.RedirectURL = window.location.href;
@@ -89,7 +89,7 @@ class AuthModule extends _asab_webui_components.Module {
89
89
  return;
90
90
  }
91
91
  if (authorization_code !== null) {
92
- yield _this._updateToken(authorization_code);
92
+ yield _this._exchangeCodeForTokens(authorization_code);
93
93
  // Remove 'code' from a query string
94
94
  qs.delete('code');
95
95
  var stateIndex = qs.get("state");
@@ -121,12 +121,12 @@ class AuthModule extends _asab_webui_components.Module {
121
121
  }
122
122
 
123
123
  // Do we have an oauth token (we are authorized to use the app)
124
- if (_this.OAuthToken != null) {
124
+ if (_this.OAuthTokens != null) {
125
125
  // Update the user info
126
126
  var result = yield _this.updateUserInfo();
127
127
  if (!result) {
128
128
  // User info not found - go to login
129
- sessionStorage.removeItem('SeaCatOAuth2Token');
129
+ sessionStorage.removeItem('SeaCatOAuth2Tokens');
130
130
  var force_login_prompt = true;
131
131
  yield _this.Api.login(_this.RedirectURL, force_login_prompt);
132
132
  return;
@@ -188,13 +188,13 @@ class AuthModule extends _asab_webui_components.Module {
188
188
  }
189
189
  authInterceptor() {
190
190
  var interceptor = config => {
191
- config.headers['Authorization'] = 'Bearer ' + this.OAuthToken['access_token'];
191
+ config.headers['Authorization'] = 'Bearer ' + this.OAuthTokens['access_token'];
192
192
  return config;
193
193
  };
194
194
  return interceptor;
195
195
  }
196
196
  webSocketAuthInterceptor() {
197
- return "access_token_".concat(this.OAuthToken['access_token']);
197
+ return "access_token_".concat(this.OAuthTokens['access_token']);
198
198
  }
199
199
  simulateUserinfo(mock_userinfo) {
200
200
  var _this2 = this;
@@ -250,8 +250,8 @@ class AuthModule extends _asab_webui_components.Module {
250
250
  this.App.addSplashScreenRequestor(this);
251
251
  this._stopSessionExpirationValidation(); // Stop session validation and clear the timeout
252
252
 
253
- sessionStorage.removeItem('SeaCatOAuth2Token');
254
- var promise = this.Api.logout(this.OAuthToken['access_token']);
253
+ sessionStorage.removeItem('SeaCatOAuth2Tokens');
254
+ var promise = this.Api.logout(this.OAuthTokens['access_token']);
255
255
  if (promise == null) {
256
256
  window.location.reload();
257
257
  }
@@ -362,7 +362,7 @@ class AuthModule extends _asab_webui_components.Module {
362
362
  var _response$data;
363
363
  var response;
364
364
  try {
365
- response = yield _this5.Api.userinfo(_this5.OAuthToken.access_token);
365
+ response = yield _this5.Api.userinfo(_this5.OAuthTokens.access_token);
366
366
  } catch (err) {
367
367
  console.error("Failed to update user info", err);
368
368
  _this5.UserInfo = null;
@@ -391,19 +391,41 @@ class AuthModule extends _asab_webui_components.Module {
391
391
  return true;
392
392
  })();
393
393
  }
394
- _updateToken(authorization_code) {
394
+
395
+ // Method for obtaining and storing the OAuth tokens based on authorization code
396
+ _exchangeCodeForTokens(authorization_code) {
395
397
  var _this6 = this;
396
398
  return (0, _asyncToGenerator2.default)(function* () {
397
- var response;
398
399
  try {
399
- response = yield _this6.Api.token_authorization_code(authorization_code, _this6.RedirectURL);
400
+ var response = yield _this6.Api.token_authorization_code(authorization_code, _this6.RedirectURL);
401
+ _this6.OAuthTokens = response.data;
402
+ sessionStorage.setItem('SeaCatOAuth2Tokens', JSON.stringify(response.data));
403
+ return true;
400
404
  } catch (err) {
401
405
  console.error("Failed to update token", err);
402
406
  return false;
403
407
  }
404
- _this6.OAuthToken = response.data;
405
- sessionStorage.setItem('SeaCatOAuth2Token', JSON.stringify(response.data));
406
- return true;
408
+ })();
409
+ }
410
+
411
+ // Method for refreshing OAuth tokens
412
+ _refreshTokens() {
413
+ var _this7 = this;
414
+ return (0, _asyncToGenerator2.default)(function* () {
415
+ var _this7$OAuthTokens;
416
+ // If no refresh_token found, return false
417
+ if (!((_this7$OAuthTokens = _this7.OAuthTokens) !== null && _this7$OAuthTokens !== void 0 && _this7$OAuthTokens.refresh_token)) {
418
+ return false;
419
+ }
420
+ try {
421
+ var response = yield _this7.Api.token_refresh(_this7.OAuthTokens.refresh_token);
422
+ _this7.OAuthTokens = response.data;
423
+ sessionStorage.setItem('SeaCatOAuth2Tokens', JSON.stringify(response.data));
424
+ return true;
425
+ } catch (err) {
426
+ console.error("Failed to refresh token", err);
427
+ return false;
428
+ }
407
429
  })();
408
430
  }
409
431
  _getAuthorizedTenant(userInfo) {
@@ -424,48 +446,89 @@ class AuthModule extends _asab_webui_components.Module {
424
446
 
425
447
  // Loop validating session expiration
426
448
  _startSessionExpirationValidation() {
427
- var _this7 = this;
449
+ var _this8 = this;
428
450
  return (0, _asyncToGenerator2.default)(function* () {
429
- if (!_this7.SessionExpiration) {
451
+ if (!_this8.SessionExpiration) {
430
452
  console.warn("Session expiration is not set.");
431
453
  return;
432
454
  }
455
+
456
+ // Max session duration
457
+ var MAX_SESSION_DURATION = Math.pow(2, 31) - 1;
458
+
459
+ // Proactivelly validate session expiration
460
+ var lastKnownExpiration = _this8.SessionExpiration; // Last known session expiration
461
+ var sessionStartTime = Date.now() / 1000; // Session start time
462
+ var sessionDuration = lastKnownExpiration - sessionStartTime; // Session duration
463
+ // Prevent glitches on very large time remaining values
464
+ if (sessionDuration > MAX_SESSION_DURATION) {
465
+ // Set timeout to maximum allowed value
466
+ sessionDuration = MAX_SESSION_DURATION;
467
+ }
468
+ var sessionMidpoint = sessionStartTime + sessionDuration / 2; // Session midpoint value
469
+ var refreshSessionDone = false; // Tracks if the session has been proactivelly refreshed
433
470
  var warningDisplayed = false; // Tracks if the "about to expire" warning has been shown
434
471
 
435
472
  var _validateSession = /*#__PURE__*/function () {
436
473
  var _ref3 = (0, _asyncToGenerator2.default)(function* () {
437
474
  var currentTime = Date.now() / 1000; // Convert milliseconds to seconds
438
- var timeRemaining = _this7.SessionExpiration - currentTime; // Time difference for triggering "about to expire" warning
475
+ var timeRemaining = _this8.SessionExpiration - currentTime; // Time difference for triggering "about to expire" warning
439
476
  // Prevent glitches on very large time remaining values
440
- if (timeRemaining >= Math.pow(2, 31)) {
477
+ if (timeRemaining > MAX_SESSION_DURATION) {
441
478
  // Set timeout to maximum allowed value
442
- timeRemaining = Math.pow(2, 31) - 1;
479
+ timeRemaining = MAX_SESSION_DURATION;
480
+ }
481
+
482
+ // Validate session on half of the session expiration or if the remaining time is <= 5min
483
+ if (!refreshSessionDone && (timeRemaining <= 300 || currentTime >= sessionMidpoint)) {
484
+ refreshSessionDone = true;
485
+ var tokensRefreshed = yield _this8._refreshTokens();
486
+ // Recalculate if session expiration was extended
487
+ if (tokensRefreshed) {
488
+ yield _this8.updateUserInfo(); // Update userinfo
489
+ // If current session expiration differs from last known expiration, recalculate session variables
490
+ if (_this8.SessionExpiration !== lastKnownExpiration) {
491
+ lastKnownExpiration = _this8.SessionExpiration;
492
+ sessionStartTime = currentTime;
493
+ sessionDuration = lastKnownExpiration - sessionStartTime;
494
+ // Prevent glitches on very large time remaining values
495
+ if (sessionDuration > MAX_SESSION_DURATION) {
496
+ // Set timeout to maximum allowed value
497
+ sessionDuration = MAX_SESSION_DURATION;
498
+ }
499
+ sessionMidpoint = sessionStartTime + sessionDuration / 2;
500
+ refreshSessionDone = false;
501
+ }
502
+ }
443
503
  }
444
504
 
445
505
  // If remaining time is between 1 and 60s, repetitivelly ask for userInfo and trigger "about to expire" warning
446
506
  if (timeRemaining <= 60 && timeRemaining > 0) {
447
- if (!warningDisplayed) {
448
- _this7.App.addAlert("info", "General|Your session will expire soon", 30, true);
507
+ var _tokensRefreshed = yield _this8._refreshTokens(); // Returns true/false if token is refreshed/not refreshed
508
+ yield _this8.updateUserInfo(); // Continue updating user info
509
+ if (!_tokensRefreshed && !warningDisplayed) {
510
+ _this8.App.addAlert("info", "General|Your session will expire soon", 30, true);
449
511
  warningDisplayed = true; // Mark the warning as displayed
450
512
  }
451
- yield _this7.updateUserInfo(); // Continue updating user info
452
513
  }
453
514
 
454
515
  // Validation on expired session
455
- if (_this7.SessionExpiration <= currentTime) {
516
+ if (_this8.SessionExpiration <= currentTime) {
517
+ // Handle refresh token prior updating user info
518
+ yield _this8._refreshTokens();
456
519
  // Handle session expiration
457
- var isUserInfoUpdated = yield _this7.updateUserInfo();
520
+ var isUserInfoUpdated = yield _this8.updateUserInfo();
458
521
  if (!isUserInfoUpdated) {
459
522
  // Stop further checks
460
- clearTimeout(_this7.sessionValidationInterval);
461
- _this7.sessionValidationInterval = null;
462
- _this7.App.addAlert("info", "ASABAuthModule|Your session has expired.", 3600 * 1000, true, (alert, store) => /*#__PURE__*/_react.default.createElement(_SessionExpirationAlert.SessionExpirationAlert, {
523
+ clearTimeout(_this8.sessionValidationInterval);
524
+ _this8.sessionValidationInterval = null;
525
+ _this8.App.addAlert("info", "ASABAuthModule|Your session has expired.", 3600 * 1000, true, (alert, store) => /*#__PURE__*/_react.default.createElement(_SessionExpirationAlert.SessionExpirationAlert, {
463
526
  alert: alert,
464
527
  store: store
465
528
  }));
466
529
  // Disable UI elements
467
- if (_this7.App.Store) {
468
- _this7.App.Store.dispatch({
530
+ if (_this8.App.Store) {
531
+ _this8.App.Store.dispatch({
469
532
  type: _actions.types.AUTH_SESSION_EXPIRATION,
470
533
  sessionExpired: true
471
534
  });
@@ -486,7 +549,7 @@ class AuthModule extends _asab_webui_components.Module {
486
549
  }
487
550
 
488
551
  // Re-trigger validation after 10 seconds
489
- _this7.sessionValidationInterval = setTimeout(_validateSession, 10000);
552
+ _this8.sessionValidationInterval = setTimeout(_validateSession, 10000);
490
553
  });
491
554
  return function validateSession() {
492
555
  return _ref3.apply(this, arguments);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "asab_webui_shell",
3
- "version": "25.2.6",
3
+ "version": "25.2.7",
4
4
  "license": "BSD-3-Clause",
5
5
  "description": "TeskaLabs ASAB WebUI Shell Application",
6
6
  "contributors": [