@frontegg/redux-store 7.128.0 → 7.129.0

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.
@@ -80,15 +80,19 @@ export default ((store, api, sharedActions) => {
80
80
  logoutUrl,
81
81
  socialLoginCallbackUrl,
82
82
  activateUrl,
83
- activateWithOTCUrl
83
+ activateWithOTCUrl,
84
+ oidcRedirectUrl
84
85
  } = routes;
85
86
  let finalUrl = urlFromLocalStorage;
86
87
  if (!finalUrl || [loginUrl, logoutUrl, socialLoginCallbackUrl, activateUrl, activateWithOTCUrl].includes(finalUrl)) {
87
88
  finalUrl = authenticatedUrl;
88
89
  }
90
+
91
+ // The OIDC callback's query is the IdP's code and state; the user's own query was saved with the URL.
92
+ const isOidcCallback = !!oidcRedirectUrl && window.location.pathname.endsWith(oidcRedirectUrl);
89
93
  const redirectUrl = getRedirectUrl({
90
94
  authenticatedUrl: finalUrl,
91
- includeQueryParam,
95
+ includeQueryParam: includeQueryParam && !isOidcCallback,
92
96
  enforceRedirectToSameSite,
93
97
  allowedRedirectOrigins
94
98
  });
@@ -32,11 +32,13 @@ import { clearHostedOAuthState } from '../oauthStorage';
32
32
  import { SHOULD_STEP_UP_KEY } from '../../StepUpState/consts';
33
33
  import { FronteggNativeModule, isEntitlementsDeeplyEqual } from '../../../toolkit';
34
34
  import { REQUEST_NAME, UserVerifiedOriginTypes } from '../../interfaces';
35
- import { authStrategyLoginStepMap } from '../consts';
35
+ import { authStrategyLoginStepMap, defaultOnRedirectTo } from '../consts';
36
36
  import { isMfaRequired, isResetPasswordRequired } from '../../helpers';
37
37
  import { MFAStep } from '../../MfaState/interfaces';
38
38
  import { SamlVendors } from '../../SSOState/interfaces';
39
39
  import { DEFAULT_RETRY_CONFIG } from '../../../constants';
40
+ const SSO_REDIRECT_DELAY_MS = 2000;
41
+ const isUsableRedirect = onRedirectTo => typeof onRedirectTo === 'function' && onRedirectTo !== defaultOnRedirectTo;
40
42
  export default ((store, api, sharedActions) => {
41
43
  const actions = sharedActions;
42
44
  const contextHolder = ContextHolder.for(store.root.appName);
@@ -387,6 +389,29 @@ export default ((store, api, sharedActions) => {
387
389
  callback == null ? void 0 : callback();
388
390
  }
389
391
  };
392
+
393
+ // v6 parity (takeLeading): a preLogin called while another is in flight is dropped.
394
+ let preLoginInFlight = false;
395
+ let latestPreLoginAttempt = 0;
396
+
397
+ // Read on use: the login box installs onRedirectTo lazily, logout() clears it and the store default is a no-op.
398
+ const redirectToFullPage = url => {
399
+ const onRedirectTo = [store.auth.onRedirectTo, contextHolder.onRedirectTo].find(isUsableRedirect);
400
+ if (onRedirectTo) {
401
+ onRedirectTo(url, {
402
+ refresh: true
403
+ });
404
+ } else {
405
+ window.location.href = url;
406
+ }
407
+ };
408
+ const scheduleSsoRedirect = (ssoRedirectUrl, attempt) => {
409
+ setTimeout(() => {
410
+ if (attempt === latestPreLoginAttempt && store.auth.loginState.step === LoginStep.redirectToSSO) {
411
+ redirectToFullPage(ssoRedirectUrl);
412
+ }
413
+ }, SSO_REDIRECT_DELAY_MS);
414
+ };
390
415
  const preLogin = async payload => {
391
416
  const {
392
417
  email,
@@ -395,11 +420,17 @@ export default ((store, api, sharedActions) => {
395
420
  invitationToken,
396
421
  callback
397
422
  } = payload;
423
+ if (preLoginInFlight) {
424
+ callback == null ? void 0 : callback();
425
+ return;
426
+ }
427
+ preLoginInFlight = true;
428
+ latestPreLoginAttempt += 1;
429
+ const attempt = latestPreLoginAttempt;
398
430
  setLoginState({
399
431
  loading: true
400
432
  });
401
433
  try {
402
- const onRedirectTo = store.auth.onRedirectTo;
403
434
  const tenantId = getSearchParam(TENANT_ID_PARAM_KEY);
404
435
  const preLoginResult = await api.auth.preLoginV2(_extends({}, email ? {
405
436
  email
@@ -420,9 +451,7 @@ export default ((store, api, sharedActions) => {
420
451
  setLoginState({
421
452
  loading: false
422
453
  });
423
- onRedirectTo(address, {
424
- refresh: true
425
- });
454
+ redirectToFullPage(address);
426
455
  return;
427
456
  }
428
457
  let ssoRedirectUrl = address;
@@ -445,11 +474,7 @@ export default ((store, api, sharedActions) => {
445
474
  loading: false,
446
475
  ssoRedirectUrl
447
476
  });
448
- setTimeout(() => {
449
- onRedirectTo(ssoRedirectUrl, {
450
- refresh: true
451
- });
452
- }, 2000);
477
+ scheduleSsoRedirect(ssoRedirectUrl, attempt);
453
478
  }
454
479
  } else {
455
480
  await ssoPreloginFailed(_extends({}, username ? {
@@ -473,6 +498,8 @@ export default ((store, api, sharedActions) => {
473
498
  invitationToken,
474
499
  error: e
475
500
  }));
501
+ } finally {
502
+ preLoginInFlight = false;
476
503
  }
477
504
  };
478
505
  const postLogin = async payload => {
@@ -6,5 +6,6 @@
6
6
  import { AuthStrategyEnum } from '@frontegg/rest-api';
7
7
  import { LoginStep } from './interfaces';
8
8
  import { AuthPageRoutes } from '../interfaces';
9
+ export declare const defaultOnRedirectTo: () => undefined;
9
10
  export declare const authStrategyLoginStepMap: Record<Exclude<AuthStrategyEnum, AuthStrategyEnum.NoLocalAuthentication | AuthStrategyEnum.WebAuthN>, LoginStep>;
10
11
  export declare const defaultFronteggRoutes: AuthPageRoutes;
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import { AuthStrategyEnum } from '@frontegg/rest-api';
7
7
  import { LoginStep } from './interfaces';
8
+ export const defaultOnRedirectTo = () => void 0;
8
9
  export const authStrategyLoginStepMap = {
9
10
  [AuthStrategyEnum.Code]: LoginStep.loginWithOtc,
10
11
  [AuthStrategyEnum.EmailAndPassword]: LoginStep.loginWithPassword,
package/auth/index.js CHANGED
@@ -37,7 +37,7 @@ import { buildTeamActions, createTeamState } from './TeamState';
37
37
  import { buildTenantsActions, createTenantsState } from './TenantsState';
38
38
  import { buildUsernamesActions, createUsernamesState } from './UsernamesState';
39
39
  import { buildUserEmailPolicyActions, createUserEmailPolicyState } from './UsersEmailsPolicyState';
40
- import { defaultFronteggRoutes } from './LoginState/consts';
40
+ import { defaultFronteggRoutes, defaultOnRedirectTo } from './LoginState/consts';
41
41
  import { deepResetState, isProxy } from '../helpers';
42
42
  import { buildEntitlementsActions } from './Entitlements';
43
43
  import { createProxy } from '../toolkit/proxy';
@@ -91,7 +91,7 @@ export const createAuthState = _overrideState => {
91
91
  overrideState = _objectWithoutPropertiesLoose(_ref, _excluded);
92
92
  return createProxy(_extends({
93
93
  routes: _extends({}, defaultFronteggRoutes, routes),
94
- onRedirectTo: () => void 0,
94
+ onRedirectTo: defaultOnRedirectTo,
95
95
  isAuthenticated: false,
96
96
  isLoading: true,
97
97
  isSSOAuth: false,
package/index.js CHANGED
@@ -1,4 +1,4 @@
1
- /** @license Frontegg v7.128.0
1
+ /** @license Frontegg v7.129.0
2
2
  *
3
3
  * This source code is licensed under the MIT license found in the
4
4
  * LICENSE file in the root directory of this source tree.
@@ -87,15 +87,19 @@ var _default = (store, api, sharedActions) => {
87
87
  logoutUrl,
88
88
  socialLoginCallbackUrl,
89
89
  activateUrl,
90
- activateWithOTCUrl
90
+ activateWithOTCUrl,
91
+ oidcRedirectUrl
91
92
  } = routes;
92
93
  let finalUrl = urlFromLocalStorage;
93
94
  if (!finalUrl || [loginUrl, logoutUrl, socialLoginCallbackUrl, activateUrl, activateWithOTCUrl].includes(finalUrl)) {
94
95
  finalUrl = authenticatedUrl;
95
96
  }
97
+
98
+ // The OIDC callback's query is the IdP's code and state; the user's own query was saved with the URL.
99
+ const isOidcCallback = !!oidcRedirectUrl && window.location.pathname.endsWith(oidcRedirectUrl);
96
100
  const redirectUrl = (0, _helpers.getRedirectUrl)({
97
101
  authenticatedUrl: finalUrl,
98
- includeQueryParam,
102
+ includeQueryParam: includeQueryParam && !isOidcCallback,
99
103
  enforceRedirectToSameSite,
100
104
  allowedRedirectOrigins
101
105
  });
@@ -44,6 +44,8 @@ const _excluded = ["callback", "error"],
44
44
  _excluded13 = ["callback"],
45
45
  _excluded14 = ["callback"],
46
46
  _excluded15 = ["callback"];
47
+ const SSO_REDIRECT_DELAY_MS = 2000;
48
+ const isUsableRedirect = onRedirectTo => typeof onRedirectTo === 'function' && onRedirectTo !== _consts2.defaultOnRedirectTo;
47
49
  var _default = (store, api, sharedActions) => {
48
50
  const actions = sharedActions;
49
51
  const contextHolder = _restApi.ContextHolder.for(store.root.appName);
@@ -394,6 +396,29 @@ var _default = (store, api, sharedActions) => {
394
396
  callback == null ? void 0 : callback();
395
397
  }
396
398
  };
399
+
400
+ // v6 parity (takeLeading): a preLogin called while another is in flight is dropped.
401
+ let preLoginInFlight = false;
402
+ let latestPreLoginAttempt = 0;
403
+
404
+ // Read on use: the login box installs onRedirectTo lazily, logout() clears it and the store default is a no-op.
405
+ const redirectToFullPage = url => {
406
+ const onRedirectTo = [store.auth.onRedirectTo, contextHolder.onRedirectTo].find(isUsableRedirect);
407
+ if (onRedirectTo) {
408
+ onRedirectTo(url, {
409
+ refresh: true
410
+ });
411
+ } else {
412
+ window.location.href = url;
413
+ }
414
+ };
415
+ const scheduleSsoRedirect = (ssoRedirectUrl, attempt) => {
416
+ setTimeout(() => {
417
+ if (attempt === latestPreLoginAttempt && store.auth.loginState.step === _interfaces.LoginStep.redirectToSSO) {
418
+ redirectToFullPage(ssoRedirectUrl);
419
+ }
420
+ }, SSO_REDIRECT_DELAY_MS);
421
+ };
397
422
  const preLogin = async payload => {
398
423
  const {
399
424
  email,
@@ -402,11 +427,17 @@ var _default = (store, api, sharedActions) => {
402
427
  invitationToken,
403
428
  callback
404
429
  } = payload;
430
+ if (preLoginInFlight) {
431
+ callback == null ? void 0 : callback();
432
+ return;
433
+ }
434
+ preLoginInFlight = true;
435
+ latestPreLoginAttempt += 1;
436
+ const attempt = latestPreLoginAttempt;
405
437
  setLoginState({
406
438
  loading: true
407
439
  });
408
440
  try {
409
- const onRedirectTo = store.auth.onRedirectTo;
410
441
  const tenantId = (0, _helpers2.getSearchParam)(_helpers2.TENANT_ID_PARAM_KEY);
411
442
  const preLoginResult = await api.auth.preLoginV2((0, _extends2.default)({}, email ? {
412
443
  email
@@ -427,9 +458,7 @@ var _default = (store, api, sharedActions) => {
427
458
  setLoginState({
428
459
  loading: false
429
460
  });
430
- onRedirectTo(address, {
431
- refresh: true
432
- });
461
+ redirectToFullPage(address);
433
462
  return;
434
463
  }
435
464
  let ssoRedirectUrl = address;
@@ -452,11 +481,7 @@ var _default = (store, api, sharedActions) => {
452
481
  loading: false,
453
482
  ssoRedirectUrl
454
483
  });
455
- setTimeout(() => {
456
- onRedirectTo(ssoRedirectUrl, {
457
- refresh: true
458
- });
459
- }, 2000);
484
+ scheduleSsoRedirect(ssoRedirectUrl, attempt);
460
485
  }
461
486
  } else {
462
487
  await ssoPreloginFailed((0, _extends2.default)({}, username ? {
@@ -480,6 +505,8 @@ var _default = (store, api, sharedActions) => {
480
505
  invitationToken,
481
506
  error: e
482
507
  }));
508
+ } finally {
509
+ preLoginInFlight = false;
483
510
  }
484
511
  };
485
512
  const postLogin = async payload => {
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.defaultFronteggRoutes = exports.authStrategyLoginStepMap = void 0;
6
+ exports.defaultOnRedirectTo = exports.defaultFronteggRoutes = exports.authStrategyLoginStepMap = void 0;
7
7
  var _restApi = require("@frontegg/rest-api");
8
8
  var _interfaces = require("./interfaces");
9
9
  /**
@@ -12,6 +12,8 @@ var _interfaces = require("./interfaces");
12
12
  * please don't import from redux or from other files that import from redux
13
13
  */
14
14
 
15
+ const defaultOnRedirectTo = () => void 0;
16
+ exports.defaultOnRedirectTo = defaultOnRedirectTo;
15
17
  const authStrategyLoginStepMap = {
16
18
  [_restApi.AuthStrategyEnum.Code]: _interfaces.LoginStep.loginWithOtc,
17
19
  [_restApi.AuthStrategyEnum.EmailAndPassword]: _interfaces.LoginStep.loginWithPassword,
@@ -559,7 +559,7 @@ const createAuthState = _overrideState => {
559
559
  overrideState = (0, _objectWithoutPropertiesLoose2.default)(_ref, _excluded);
560
560
  return (0, _proxy.createProxy)((0, _extends2.default)({
561
561
  routes: (0, _extends2.default)({}, _consts.defaultFronteggRoutes, routes),
562
- onRedirectTo: () => void 0,
562
+ onRedirectTo: _consts.defaultOnRedirectTo,
563
563
  isAuthenticated: false,
564
564
  isLoading: true,
565
565
  isSSOAuth: false,
package/node/index.js CHANGED
@@ -1,4 +1,4 @@
1
- /** @license Frontegg v7.128.0
1
+ /** @license Frontegg v7.129.0
2
2
  *
3
3
  * This source code is licensed under the MIT license found in the
4
4
  * LICENSE file in the root directory of this source tree.
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@frontegg/redux-store",
3
- "version": "7.128.0",
3
+ "version": "7.129.0",
4
4
  "main": "./node/index.js",
5
5
  "license": "MIT",
6
6
  "author": "Frontegg LTD",
7
7
  "dependencies": {
8
8
  "@babel/runtime": "^7.18.6",
9
9
  "@frontegg/entitlements-javascript-commons": "1.1.2",
10
- "@frontegg/rest-api": "7.128.0",
10
+ "@frontegg/rest-api": "7.129.0",
11
11
  "fast-deep-equal": "3.1.3",
12
12
  "get-value": "^3.0.1",
13
13
  "proxy-compare": "^3.0.0",