@frontegg/redux-store 7.128.0 → 7.129.0-alpha.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.
@@ -1,7 +1,8 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
+ import { ENFORCE_SSO_ON_TENANT_ACCESS_FF, FeatureFlags } from '@frontegg/rest-api';
2
3
  import { getPathAndSearchParamsFromUrl, getRedirectUrl, isAbsoluteUrl } from '../helpers';
3
4
  import { shouldPromptChooseTenant } from '../helpers/tenantSelect';
4
- import { FRONTEGG_AFTER_AUTH_REDIRECT_URL } from '../../../constants';
5
+ import { FRONTEGG_AFTER_AUTH_REDIRECT_URL, FRONTEGG_PENDING_SWITCH_TENANT } from '../../../constants';
5
6
  import { LoginStep } from '../interfaces';
6
7
  import { SHOULD_STEP_UP_KEY } from '../../StepUpState/consts';
7
8
  import { delay } from '../../../helpers';
@@ -56,6 +57,33 @@ export default ((store, api, sharedActions) => {
56
57
  }, additionalUpdate));
57
58
  }
58
59
 
60
+ /**
61
+ * A switch that needed SSO saved its target tenant (in this tab) before sending the user to SSO.
62
+ * Once logged in again, switch to it once if the login landed in another tenant.
63
+ * @returns true when the switch left the user without a session (MFA step for the target tenant, or logout)
64
+ */
65
+ async function switchToPendingSsoTenant() {
66
+ var _store$auth$user, _store$auth$user2;
67
+ const [isEnforceSsoOnTenantAccessEnabled] = FeatureFlags.getFeatureFlags([ENFORCE_SSO_ON_TENANT_ACCESS_FF], store.root.appName);
68
+ const pendingTenantId = window.sessionStorage.getItem(FRONTEGG_PENDING_SWITCH_TENANT);
69
+ if (!isEnforceSsoOnTenantAccessEnabled || !pendingTenantId || !((_store$auth$user = store.auth.user) != null && _store$auth$user.accessToken)) {
70
+ return false;
71
+ }
72
+ if (store.auth.user.tenantId === pendingTenantId) {
73
+ window.sessionStorage.removeItem(FRONTEGG_PENDING_SWITCH_TENANT);
74
+ return false;
75
+ }
76
+ // silentReload keeps switchingTenant set, so the switch's own refresh doesn't re-run this navigation
77
+ await actions.switchTenant({
78
+ tenantId: pendingTenantId,
79
+ silentReload: true,
80
+ keepLoginState: true
81
+ });
82
+ // removed only after the switch: a refused re-run must not send the user to SSO again
83
+ window.sessionStorage.removeItem(FRONTEGG_PENDING_SWITCH_TENANT);
84
+ return !((_store$auth$user2 = store.auth.user) != null && _store$auth$user2.accessToken);
85
+ }
86
+
59
87
  /**
60
88
  * @param customLoginAuthenticatedUrl custom login authenticated url if exists
61
89
  * @returns the authenticated url to redirect to after auth navigation
@@ -159,7 +187,7 @@ export default ((store, api, sharedActions) => {
159
187
  * until the resolved-guard, the LD flag, and the option all pass.
160
188
  */
161
189
  const shouldRouteToChooseTenant = async () => {
162
- var _store$auth$tenantSel, _store$auth$user, _store$auth$user$act, _store$auth$tenantsSt;
190
+ var _store$auth$tenantSel, _store$auth$user3, _store$auth$user3$act, _store$auth$tenantsSt;
163
191
  const loginState = store.auth.loginState;
164
192
  // Guard first so re-entry after the choice is resolved finalizes without re-reading the flag.
165
193
  if (loginState.tenantChoiceResolved) {
@@ -175,7 +203,7 @@ export default ((store, api, sharedActions) => {
175
203
  return false; // feature off — never read tenant data
176
204
  }
177
205
 
178
- const isImpersonation = !!((_store$auth$user = store.auth.user) != null && (_store$auth$user$act = _store$auth$user.act) != null && _store$auth$user$act.sub);
206
+ const isImpersonation = !!((_store$auth$user3 = store.auth.user) != null && (_store$auth$user3$act = _store$auth$user3.act) != null && _store$auth$user3$act.sub);
179
207
  if (isImpersonation) {
180
208
  return false;
181
209
  }
@@ -253,6 +281,14 @@ export default ((store, api, sharedActions) => {
253
281
  }
254
282
  try {
255
283
  var _window4, _payload$preventRedir;
284
+ const isSessionLostInPendingSwitch = await switchToPendingSsoTenant();
285
+ if (isSessionLostInPendingSwitch) {
286
+ // MFA for the target tenant or a failed refresh: continue on the login page
287
+ store.auth.onRedirectTo(store.auth.routes.loginUrl, {
288
+ preserveQueryParams: true
289
+ });
290
+ return;
291
+ }
256
292
  const {
257
293
  customLoginAuthenticatedUrl,
258
294
  stepUpUrl
@@ -1,6 +1,7 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import { createRandomString, generateCodeChallenge, GTMEventAction, refreshTokenWithRetry, reportGTMEvent } from '../../../helpers';
3
3
  import { HOSTED_LOGIN_VERIFIER_KEY } from '../../../constants';
4
+ import { ENFORCE_SSO_ON_TENANT_ACCESS_FF, FeatureFlags } from '@frontegg/rest-api';
4
5
  import { getBaseNameWithoutSlashSuffix, getSearchParam, isOauthCallbackRoute, TENANT_ID_PARAM_KEY } from '../helpers';
5
6
  import { getUri, getUrlDerivedStepUp, isSteppedUp, redirectByStepUpUrl } from '../../helpers';
6
7
  import { SHOULD_STEP_UP_KEY } from '../../StepUpState/consts';
@@ -79,7 +80,10 @@ export default ((store, api, sharedActions) => {
79
80
  code_challenge_method: 'S256',
80
81
  nonce
81
82
  }, additionalParams);
82
- if (context.tenantResolver) {
83
+ const [isEnforceSsoOnTenantAccessEnabled] = FeatureFlags.getFeatureFlags([ENFORCE_SSO_ON_TENANT_ACCESS_FF], store.root.appName);
84
+ // the tenant asked for (SSO for a refused switch or refresh) wins over the resolved and the page's tenant
85
+ const isTenantRequested = isEnforceSsoOnTenantAccessEnabled && !!(additionalParams != null && additionalParams.tenantId);
86
+ if (context.tenantResolver && !isTenantRequested) {
83
87
  var _context$tenantResolv;
84
88
  const resolvedTenantResult = await ((_context$tenantResolv = context.tenantResolver) == null ? void 0 : _context$tenantResolv.call(context));
85
89
  if (resolvedTenantResult != null && resolvedTenantResult.tenant) {
@@ -87,7 +91,7 @@ export default ((store, api, sharedActions) => {
87
91
  }
88
92
  }
89
93
  const tenantId = getSearchParam(TENANT_ID_PARAM_KEY);
90
- if (tenantId) {
94
+ if (tenantId && !isTenantRequested) {
91
95
  params['tenantId'] = tenantId;
92
96
  }
93
97
  if (FronteggNativeModule.isAvailable('closeWindow')) {
@@ -75,5 +75,9 @@ declare const _default: (store: FronteggState, api: RestApi, sharedActions: Shar
75
75
  resetBreachedPassword: (payload: IForgotPassword) => Promise<void>;
76
76
  refreshTokenForSocialLogins: () => Promise<void>;
77
77
  __refreshToken: () => Promise<void>;
78
+ __redirectToSsoForTenant: ({ tenantId, address }: {
79
+ tenantId: string;
80
+ address: string;
81
+ }) => Promise<void>;
78
82
  };
79
83
  export default _default;
@@ -23,26 +23,85 @@ import mfaWithEmailCodeActions from './mfaWithEmailCode.actions';
23
23
  import mfaWithSMSActions from './mfaWithSMS.actions';
24
24
  import mfaWithWebAuthnActions from './mfaWithWebAuthn.actions';
25
25
  import { LoginFlow, LoginStep } from '../interfaces';
26
- import { base64urlDecode, deepResetState, delay, errorHandler, errorTraceId, GTMEventAction, publicKeyCredentialToJSON, refreshTokenWithRetry, reportGTMEvent, retryIfNeeded, withRetryConfig } from '../../../helpers';
26
+ import { base64urlDecode, deepResetState, delay, errorHandler, errorTraceId, GTMEventAction, isSsoEnforcementError, publicKeyCredentialToJSON, refreshTokenWithRetry, reportGTMEvent, retryIfNeeded, withRetryConfig } from '../../../helpers';
27
27
  import { initialState } from '../state';
28
28
  import { getSearchParam, isEmailPayload, isUsernamePayload, shouldShowPasswordRotationPromptFunc, TENANT_ID_PARAM_KEY } from '../helpers';
29
- import { AuthStrategyEnum, ContextHolder, FeatureFlags, getTabTenantFromSessionStorage, removeTabTenantFromSessionStorage, WebAuthnDeviceType, PreLoginAddressType } from '@frontegg/rest-api';
29
+ import { AuthStrategyEnum, ContextHolder, ENFORCE_SSO_ON_TENANT_ACCESS_FF, FeatureFlags, getTabTenantFromSessionStorage, removeTabTenantFromSessionStorage, setTabTenantInSessionStorage, WebAuthnDeviceType, PreLoginAddressType } from '@frontegg/rest-api';
30
30
  import hostedLoginAuthorizeActions from './hostedLoginAuthorize.actions';
31
31
  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
35
  import { authStrategyLoginStepMap } from '../consts';
36
- import { isMfaRequired, isResetPasswordRequired } from '../../helpers';
36
+ import { isMfaRequired, isResetPasswordRequired, setAfterAuthRedirectUrlForStepUp } from '../../helpers';
37
37
  import { MFAStep } from '../../MfaState/interfaces';
38
38
  import { SamlVendors } from '../../SSOState/interfaces';
39
- import { DEFAULT_RETRY_CONFIG } from '../../../constants';
39
+ import { DEFAULT_RETRY_CONFIG, FRONTEGG_PENDING_SWITCH_TENANT } from '../../../constants';
40
40
  export default ((store, api, sharedActions) => {
41
41
  const actions = sharedActions;
42
42
  const contextHolder = ContextHolder.for(store.root.appName);
43
43
 
44
+ /**
45
+ * Sends the user to log in through the tenant's SSO and back to the current page afterwards.
46
+ * @protected
47
+ */
48
+ const __redirectToSsoForTenant = async ({
49
+ tenantId,
50
+ address
51
+ }) => {
52
+ // on the login page (tenant chooser) keep the destination the login already saved
53
+ if (!window.location.pathname.endsWith(store.auth.routes.loginUrl)) {
54
+ setAfterAuthRedirectUrlForStepUp();
55
+ }
56
+ if (store.auth.hostedLoginBox) {
57
+ await actions.requestHostedLoginAuthorize({
58
+ prompt: 'login',
59
+ tenantId
60
+ });
61
+ return;
62
+ }
63
+ store.auth.onRedirectTo(address, {
64
+ refresh: true
65
+ });
66
+ };
67
+ /**
68
+ * Handles a token refresh refused because the tenant requires SSO.
69
+ * ssoRequired: true sends the user to SSO. ssoRequired: false renews once more for the tenant of the
70
+ * current token, which keeps the session.
71
+ * @returns true when handled, false when the caller should log out
72
+ * @private
73
+ */
74
+ const __handleSsoEnforcementRefreshError = async (e, isSsoEnforcementRetry, refreshForTenant) => {
75
+ var _store$auth$user;
76
+ const [isEnforceSsoOnTenantAccessEnabled] = FeatureFlags.getFeatureFlags([ENFORCE_SSO_ON_TENANT_ACCESS_FF], store.root.appName);
77
+ if (!isEnforceSsoOnTenantAccessEnabled || !isSsoEnforcementError(e)) {
78
+ return false;
79
+ }
80
+ const {
81
+ errorContext
82
+ } = e;
83
+ // the switch re-run after coming back from SSO keeps the session instead of sending the user to SSO again
84
+ const isPendingSwitchRerun = !!window.sessionStorage.getItem(FRONTEGG_PENDING_SWITCH_TENANT);
85
+ if (errorContext.ssoRequired && !isPendingSwitchRerun) {
86
+ await __redirectToSsoForTenant(errorContext);
87
+ return true;
88
+ }
89
+ if (isSsoEnforcementRetry) {
90
+ return false;
91
+ }
92
+ const currentTenantId = (_store$auth$user = store.auth.user) == null ? void 0 : _store$auth$user.tenantId;
93
+ if (currentTenantId && contextHolder.isSessionPerTenantEnabled()) {
94
+ setTabTenantInSessionStorage(currentTenantId);
95
+ }
96
+ await refreshForTenant(currentTenantId);
97
+ return true;
98
+ };
99
+
44
100
  /** @private */
45
- const __refreshTokenHosted = async () => {
101
+ const __refreshTokenHosted = async ({
102
+ tenantId,
103
+ isSsoEnforcementRetry = false
104
+ } = {}) => {
46
105
  const user = store.auth.user;
47
106
  if (!(user != null && user.refreshToken)) {
48
107
  contextHolder.setAccessToken(null);
@@ -54,7 +113,7 @@ export default ((store, api, sharedActions) => {
54
113
  return;
55
114
  }
56
115
  try {
57
- const tabTenantId = getTabTenantFromSessionStorage(store.root.appName);
116
+ const tabTenantId = tenantId != null ? tenantId : getTabTenantFromSessionStorage(store.root.appName);
58
117
  const body = _extends({
59
118
  grant_type: 'refresh_token',
60
119
  refresh_token: user == null ? void 0 : user.refreshToken
@@ -69,6 +128,13 @@ export default ((store, api, sharedActions) => {
69
128
  isAuthenticated: true
70
129
  });
71
130
  } catch (e) {
131
+ const retry = tenantId => __refreshTokenHosted({
132
+ tenantId,
133
+ isSsoEnforcementRetry: true
134
+ });
135
+ if (await __handleSsoEnforcementRefreshError(e, isSsoEnforcementRetry, retry)) {
136
+ return;
137
+ }
72
138
  contextHolder.setAccessToken(null);
73
139
  contextHolder.setUser(null);
74
140
  actions.setAuthState({
@@ -88,7 +154,10 @@ export default ((store, api, sharedActions) => {
88
154
  };
89
155
 
90
156
  /** @private */
91
- const __refreshTokenEmbedded = async () => {
157
+ const __refreshTokenEmbedded = async ({
158
+ tenantId,
159
+ isSsoEnforcementRetry = false
160
+ } = {}) => {
92
161
  try {
93
162
  const {
94
163
  routes,
@@ -99,7 +168,7 @@ export default ((store, api, sharedActions) => {
99
168
  user,
100
169
  tenants = [],
101
170
  activeTenant
102
- } = await refreshTokenWithRetry(() => api.auth.refreshTokenV3());
171
+ } = await refreshTokenWithRetry(() => api.auth.refreshTokenV3(tenantId));
103
172
  if (isMfaRequired(user, store.root.appName)) {
104
173
  const mfaRequiredState = await actions.getMfaRequiredState(user);
105
174
  actions.setAuthState(mfaRequiredState);
@@ -130,6 +199,13 @@ export default ((store, api, sharedActions) => {
130
199
  });
131
200
  }
132
201
  } catch (e) {
202
+ const retry = tenantId => __refreshTokenEmbedded({
203
+ tenantId,
204
+ isSsoEnforcementRetry: true
205
+ });
206
+ if (await __handleSsoEnforcementRefreshError(e, isSsoEnforcementRetry, retry)) {
207
+ return;
208
+ }
133
209
  contextHolder.setAccessToken(null);
134
210
  contextHolder.setUser(null);
135
211
  actions.setAuthState({
@@ -140,7 +216,9 @@ export default ((store, api, sharedActions) => {
140
216
  };
141
217
 
142
218
  /** @private */
143
- const __refreshTokenWithNativeTokens = async () => {
219
+ const __refreshTokenWithNativeTokens = async ({
220
+ isSsoEnforcementRetry = false
221
+ } = {}) => {
144
222
  try {
145
223
  const authApi = api.auth;
146
224
  const response = await refreshTokenWithRetry(async () => {
@@ -154,6 +232,13 @@ export default ((store, api, sharedActions) => {
154
232
  isAuthenticated: true
155
233
  });
156
234
  } catch (e) {
235
+ // native tokens can't name a tenant: the retry relies on the tab tenant only
236
+ const retry = () => __refreshTokenWithNativeTokens({
237
+ isSsoEnforcementRetry: true
238
+ });
239
+ if (await __handleSsoEnforcementRefreshError(e, isSsoEnforcementRetry, retry)) {
240
+ return;
241
+ }
157
242
  contextHolder.setAccessToken(null);
158
243
  contextHolder.setUser(null);
159
244
  actions.setAuthState({
@@ -187,9 +272,9 @@ export default ((store, api, sharedActions) => {
187
272
  * @private
188
273
  */
189
274
  const __handleUnnecessaryEntitlementsUpdate = async updatedUser => {
190
- var _store$auth$user;
275
+ var _store$auth$user2;
191
276
  // @ts-ignore TODO: fix entitlements state type
192
- const oldEntitlements = (_store$auth$user = store.auth.user) == null ? void 0 : _store$auth$user.entitlements;
277
+ const oldEntitlements = (_store$auth$user2 = store.auth.user) == null ? void 0 : _store$auth$user2.entitlements;
193
278
  if (isEntitlementsDeeplyEqual(oldEntitlements, updatedUser == null ? void 0 : updatedUser.entitlements)) {
194
279
  // set the previous entitlements object
195
280
  return _extends({}, updatedUser, {
@@ -659,6 +744,7 @@ export default ((store, api, sharedActions) => {
659
744
  if (!keepHostedOAuthState) {
660
745
  clearHostedOAuthState();
661
746
  }
747
+ window.sessionStorage.removeItem(FRONTEGG_PENDING_SWITCH_TENANT);
662
748
  };
663
749
  const logout = async payload => {
664
750
  const hostedLoginBox = store.auth.hostedLoginBox;
@@ -1361,7 +1447,10 @@ export default ((store, api, sharedActions) => {
1361
1447
  };
1362
1448
 
1363
1449
  /** @private */
1364
- const __refreshTokenHostedSocialLogins = async () => {
1450
+ const __refreshTokenHostedSocialLogins = async ({
1451
+ tenantId,
1452
+ isSsoEnforcementRetry = false
1453
+ } = {}) => {
1365
1454
  const {
1366
1455
  user: currentUser
1367
1456
  } = store.auth;
@@ -1375,7 +1464,7 @@ export default ((store, api, sharedActions) => {
1375
1464
  return;
1376
1465
  }
1377
1466
  try {
1378
- const tabTenantId = getTabTenantFromSessionStorage(store.root.appName);
1467
+ const tabTenantId = tenantId != null ? tenantId : getTabTenantFromSessionStorage(store.root.appName);
1379
1468
  const body = _extends({
1380
1469
  grant_type: 'refresh_token',
1381
1470
  refresh_token: currentUser == null ? void 0 : currentUser.refreshToken
@@ -1390,6 +1479,13 @@ export default ((store, api, sharedActions) => {
1390
1479
  isAuthenticated: true
1391
1480
  });
1392
1481
  } catch (e) {
1482
+ const retry = tenantId => __refreshTokenHostedSocialLogins({
1483
+ tenantId,
1484
+ isSsoEnforcementRetry: true
1485
+ });
1486
+ if (await __handleSsoEnforcementRefreshError(e, isSsoEnforcementRetry, retry)) {
1487
+ return;
1488
+ }
1393
1489
  contextHolder.setAccessToken(null);
1394
1490
  contextHolder.setUser(null);
1395
1491
  actions.setAuthState({
@@ -1399,7 +1495,10 @@ export default ((store, api, sharedActions) => {
1399
1495
  }
1400
1496
  };
1401
1497
  /** @private */
1402
- const __refreshTokenEmbeddedSocialLogins = async () => {
1498
+ const __refreshTokenEmbeddedSocialLogins = async ({
1499
+ tenantId,
1500
+ isSsoEnforcementRetry = false
1501
+ } = {}) => {
1403
1502
  try {
1404
1503
  const {
1405
1504
  routes,
@@ -1410,7 +1509,7 @@ export default ((store, api, sharedActions) => {
1410
1509
  user,
1411
1510
  tenants = [],
1412
1511
  activeTenant
1413
- } = await refreshTokenWithRetry(() => api.auth.refreshTokenV3());
1512
+ } = await refreshTokenWithRetry(() => api.auth.refreshTokenV3(tenantId));
1414
1513
  if (isMfaRequired(user, store.root.appName)) {
1415
1514
  const mfaRequiredState = await actions.getMfaRequiredState(user);
1416
1515
  actions.setAuthState(mfaRequiredState);
@@ -1460,6 +1559,13 @@ export default ((store, api, sharedActions) => {
1460
1559
  }
1461
1560
  }
1462
1561
  } catch (e) {
1562
+ const retry = tenantId => __refreshTokenEmbeddedSocialLogins({
1563
+ tenantId,
1564
+ isSsoEnforcementRetry: true
1565
+ });
1566
+ if (await __handleSsoEnforcementRefreshError(e, isSsoEnforcementRetry, retry)) {
1567
+ return;
1568
+ }
1463
1569
  contextHolder.setAccessToken(null);
1464
1570
  contextHolder.setUser(null);
1465
1571
  actions.setAuthState({
@@ -1506,6 +1612,7 @@ export default ((store, api, sharedActions) => {
1506
1612
  resetBreachedPassword,
1507
1613
  refreshTokenForSocialLogins,
1508
1614
  // protected
1509
- __refreshToken
1615
+ __refreshToken,
1616
+ __redirectToSsoForTenant
1510
1617
  }, hostedLoginAuthorizeActions(store, api, sharedActions), afterAuthNavigationActions(store, api, sharedActions), handleVerifyMFAResponseActions(store, api, sharedActions), mfaRequiredStateActions(store, api, sharedActions), mfaWithAuthenticatorActions(store, api, sharedActions), mfaWithEmailCodeActions(store, api, sharedActions), mfaWithSMSActions(store, api, sharedActions), mfaWithWebAuthnActions(store, api, sharedActions));
1511
1618
  });
@@ -1,6 +1,7 @@
1
1
  import { initialState } from './state';
2
- import { deepResetState } from '../../helpers';
3
- import { getCurrentUserTenantsFunction } from '@frontegg/rest-api';
2
+ import { deepResetState, isSsoEnforcementError } from '../../helpers';
3
+ import { ENFORCE_SSO_ON_TENANT_ACCESS_FF, FeatureFlags, getCurrentUserTenantsFunction } from '@frontegg/rest-api';
4
+ import { FRONTEGG_PENDING_SWITCH_TENANT } from '../../constants';
4
5
  export default ((store, api, sharedActions) => {
5
6
  const actions = sharedActions;
6
7
  const setTenantsState = state => {
@@ -51,6 +52,15 @@ export default ((store, api, sharedActions) => {
51
52
  }) : actions.setAuthState({
52
53
  isLoading: false
53
54
  });
55
+ const [isEnforceSsoOnTenantAccessEnabled] = FeatureFlags.getFeatureFlags([ENFORCE_SSO_ON_TENANT_ACCESS_FF], store.root.appName);
56
+ // a switch re-run after coming back from SSO doesn't send the user to SSO again
57
+ const isPendingSwitchRerun = window.sessionStorage.getItem(FRONTEGG_PENDING_SWITCH_TENANT) === tenantId;
58
+ if (isEnforceSsoOnTenantAccessEnabled && isSsoEnforcementError(e) && e.errorContext.ssoRequired && !isPendingSwitchRerun) {
59
+ // saved to finish the switch after the SSO login, which may land in another tenant
60
+ window.sessionStorage.setItem(FRONTEGG_PENDING_SWITCH_TENANT, tenantId);
61
+ await actions.__redirectToSsoForTenant(e.errorContext);
62
+ return;
63
+ }
54
64
  callback == null ? void 0 : callback(false, e);
55
65
  }
56
66
  };
package/constants.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export declare const FRONTEGG_AFTER_AUTH_REDIRECT_URL = "FRONTEGG_AFTER_AUTH_REDIRECT_URL";
2
+ export declare const FRONTEGG_PENDING_SWITCH_TENANT = "FRONTEGG_PENDING_SWITCH_TENANT";
2
3
  export declare const HOSTED_LOGIN_VERIFIER_KEY = "HOSTED_LOGIN_VERIFIER_KEY";
3
4
  export declare const DEFAULT_RETRY_CONFIG: {
4
5
  retryCount: number;
package/constants.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export const FRONTEGG_AFTER_AUTH_REDIRECT_URL = 'FRONTEGG_AFTER_AUTH_REDIRECT_URL';
2
+ export const FRONTEGG_PENDING_SWITCH_TENANT = 'FRONTEGG_PENDING_SWITCH_TENANT';
2
3
  export const HOSTED_LOGIN_VERIFIER_KEY = 'HOSTED_LOGIN_VERIFIER_KEY';
3
4
  export const DEFAULT_RETRY_CONFIG = {
4
5
  retryCount: 3
@@ -1,4 +1,7 @@
1
- import { FronteggApiError } from '@frontegg/rest-api';
1
+ import { FronteggApiError, SsoEnforcementErrorContext } from '@frontegg/rest-api';
2
2
  export declare const isFronteggApiError: (error: unknown | Error | FronteggApiError) => error is FronteggApiError;
3
+ export declare const isSsoEnforcementError: (error: unknown) => error is FronteggApiError & {
4
+ errorContext: SsoEnforcementErrorContext;
5
+ };
3
6
  export declare const isError: (error: any) => error is Error;
4
7
  export declare const errorHandler: <T>(error: unknown, fallback?: T) => FronteggApiError | T;
@@ -1,5 +1,8 @@
1
- import { FronteggApiError, GENERIC_ERROR_MESSAGE, GENERIC_ERROR_CODE } from '@frontegg/rest-api';
1
+ import { FronteggApiError, GENERIC_ERROR_MESSAGE, GENERIC_ERROR_CODE, SSO_REQUIRED_FOR_TENANT_ERROR_CODE } from '@frontegg/rest-api';
2
2
  export const isFronteggApiError = error => Boolean(error == null ? void 0 : error.statusCode);
3
+
4
+ // ER-01079 (disabled user / expired tenant) is not this: it still logs out
5
+ export const isSsoEnforcementError = error => isFronteggApiError(error) && error.errorCode === SSO_REQUIRED_FOR_TENANT_ERROR_CODE;
3
6
  export const isError = error => {
4
7
  if (error instanceof Error) {
5
8
  return true;
package/index.js CHANGED
@@ -1,4 +1,4 @@
1
- /** @license Frontegg v7.128.0
1
+ /** @license Frontegg v7.129.0-alpha.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.
@@ -71,5 +71,9 @@ declare const _default: (store: FronteggState, api: RestApi, sharedActions: Shar
71
71
  resetBreachedPassword: (payload: import("dist/@frontegg/rest-api").IForgotPassword) => Promise<void>;
72
72
  refreshTokenForSocialLogins: () => Promise<void>;
73
73
  __refreshToken: () => Promise<void>;
74
+ __redirectToSsoForTenant: ({ tenantId, address }: {
75
+ tenantId: string;
76
+ address: string;
77
+ }) => Promise<void>;
74
78
  };
75
79
  export default _default;
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  });
7
7
  exports.default = void 0;
8
8
  var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
9
+ var _restApi = require("@frontegg/rest-api");
9
10
  var _helpers = require("../helpers");
10
11
  var _tenantSelect = require("../helpers/tenantSelect");
11
12
  var _constants = require("../../../constants");
@@ -63,6 +64,33 @@ var _default = (store, api, sharedActions) => {
63
64
  }, additionalUpdate));
64
65
  }
65
66
 
67
+ /**
68
+ * A switch that needed SSO saved its target tenant (in this tab) before sending the user to SSO.
69
+ * Once logged in again, switch to it once if the login landed in another tenant.
70
+ * @returns true when the switch left the user without a session (MFA step for the target tenant, or logout)
71
+ */
72
+ async function switchToPendingSsoTenant() {
73
+ var _store$auth$user, _store$auth$user2;
74
+ const [isEnforceSsoOnTenantAccessEnabled] = _restApi.FeatureFlags.getFeatureFlags([_restApi.ENFORCE_SSO_ON_TENANT_ACCESS_FF], store.root.appName);
75
+ const pendingTenantId = window.sessionStorage.getItem(_constants.FRONTEGG_PENDING_SWITCH_TENANT);
76
+ if (!isEnforceSsoOnTenantAccessEnabled || !pendingTenantId || !((_store$auth$user = store.auth.user) != null && _store$auth$user.accessToken)) {
77
+ return false;
78
+ }
79
+ if (store.auth.user.tenantId === pendingTenantId) {
80
+ window.sessionStorage.removeItem(_constants.FRONTEGG_PENDING_SWITCH_TENANT);
81
+ return false;
82
+ }
83
+ // silentReload keeps switchingTenant set, so the switch's own refresh doesn't re-run this navigation
84
+ await actions.switchTenant({
85
+ tenantId: pendingTenantId,
86
+ silentReload: true,
87
+ keepLoginState: true
88
+ });
89
+ // removed only after the switch: a refused re-run must not send the user to SSO again
90
+ window.sessionStorage.removeItem(_constants.FRONTEGG_PENDING_SWITCH_TENANT);
91
+ return !((_store$auth$user2 = store.auth.user) != null && _store$auth$user2.accessToken);
92
+ }
93
+
66
94
  /**
67
95
  * @param customLoginAuthenticatedUrl custom login authenticated url if exists
68
96
  * @returns the authenticated url to redirect to after auth navigation
@@ -166,7 +194,7 @@ var _default = (store, api, sharedActions) => {
166
194
  * until the resolved-guard, the LD flag, and the option all pass.
167
195
  */
168
196
  const shouldRouteToChooseTenant = async () => {
169
- var _store$auth$tenantSel, _store$auth$user, _store$auth$user$act, _store$auth$tenantsSt;
197
+ var _store$auth$tenantSel, _store$auth$user3, _store$auth$user3$act, _store$auth$tenantsSt;
170
198
  const loginState = store.auth.loginState;
171
199
  // Guard first so re-entry after the choice is resolved finalizes without re-reading the flag.
172
200
  if (loginState.tenantChoiceResolved) {
@@ -182,7 +210,7 @@ var _default = (store, api, sharedActions) => {
182
210
  return false; // feature off — never read tenant data
183
211
  }
184
212
 
185
- const isImpersonation = !!((_store$auth$user = store.auth.user) != null && (_store$auth$user$act = _store$auth$user.act) != null && _store$auth$user$act.sub);
213
+ const isImpersonation = !!((_store$auth$user3 = store.auth.user) != null && (_store$auth$user3$act = _store$auth$user3.act) != null && _store$auth$user3$act.sub);
186
214
  if (isImpersonation) {
187
215
  return false;
188
216
  }
@@ -260,6 +288,14 @@ var _default = (store, api, sharedActions) => {
260
288
  }
261
289
  try {
262
290
  var _window4, _payload$preventRedir;
291
+ const isSessionLostInPendingSwitch = await switchToPendingSsoTenant();
292
+ if (isSessionLostInPendingSwitch) {
293
+ // MFA for the target tenant or a failed refresh: continue on the login page
294
+ store.auth.onRedirectTo(store.auth.routes.loginUrl, {
295
+ preserveQueryParams: true
296
+ });
297
+ return;
298
+ }
263
299
  const {
264
300
  customLoginAuthenticatedUrl,
265
301
  stepUpUrl
@@ -8,6 +8,7 @@ exports.default = void 0;
8
8
  var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
9
9
  var _helpers = require("../../../helpers");
10
10
  var _constants = require("../../../constants");
11
+ var _restApi = require("@frontegg/rest-api");
11
12
  var _helpers2 = require("../helpers");
12
13
  var _helpers3 = require("../../helpers");
13
14
  var _consts = require("../../StepUpState/consts");
@@ -86,7 +87,10 @@ var _default = (store, api, sharedActions) => {
86
87
  code_challenge_method: 'S256',
87
88
  nonce
88
89
  }, additionalParams);
89
- if (context.tenantResolver) {
90
+ const [isEnforceSsoOnTenantAccessEnabled] = _restApi.FeatureFlags.getFeatureFlags([_restApi.ENFORCE_SSO_ON_TENANT_ACCESS_FF], store.root.appName);
91
+ // the tenant asked for (SSO for a refused switch or refresh) wins over the resolved and the page's tenant
92
+ const isTenantRequested = isEnforceSsoOnTenantAccessEnabled && !!(additionalParams != null && additionalParams.tenantId);
93
+ if (context.tenantResolver && !isTenantRequested) {
90
94
  var _context$tenantResolv;
91
95
  const resolvedTenantResult = await ((_context$tenantResolv = context.tenantResolver) == null ? void 0 : _context$tenantResolv.call(context));
92
96
  if (resolvedTenantResult != null && resolvedTenantResult.tenant) {
@@ -94,7 +98,7 @@ var _default = (store, api, sharedActions) => {
94
98
  }
95
99
  }
96
100
  const tenantId = (0, _helpers2.getSearchParam)(_helpers2.TENANT_ID_PARAM_KEY);
97
- if (tenantId) {
101
+ if (tenantId && !isTenantRequested) {
98
102
  params['tenantId'] = tenantId;
99
103
  }
100
104
  if (_toolkit.FronteggNativeModule.isAvailable('closeWindow')) {
@@ -48,8 +48,67 @@ var _default = (store, api, sharedActions) => {
48
48
  const actions = sharedActions;
49
49
  const contextHolder = _restApi.ContextHolder.for(store.root.appName);
50
50
 
51
+ /**
52
+ * Sends the user to log in through the tenant's SSO and back to the current page afterwards.
53
+ * @protected
54
+ */
55
+ const __redirectToSsoForTenant = async ({
56
+ tenantId,
57
+ address
58
+ }) => {
59
+ // on the login page (tenant chooser) keep the destination the login already saved
60
+ if (!window.location.pathname.endsWith(store.auth.routes.loginUrl)) {
61
+ (0, _helpers3.setAfterAuthRedirectUrlForStepUp)();
62
+ }
63
+ if (store.auth.hostedLoginBox) {
64
+ await actions.requestHostedLoginAuthorize({
65
+ prompt: 'login',
66
+ tenantId
67
+ });
68
+ return;
69
+ }
70
+ store.auth.onRedirectTo(address, {
71
+ refresh: true
72
+ });
73
+ };
74
+ /**
75
+ * Handles a token refresh refused because the tenant requires SSO.
76
+ * ssoRequired: true sends the user to SSO. ssoRequired: false renews once more for the tenant of the
77
+ * current token, which keeps the session.
78
+ * @returns true when handled, false when the caller should log out
79
+ * @private
80
+ */
81
+ const __handleSsoEnforcementRefreshError = async (e, isSsoEnforcementRetry, refreshForTenant) => {
82
+ var _store$auth$user;
83
+ const [isEnforceSsoOnTenantAccessEnabled] = _restApi.FeatureFlags.getFeatureFlags([_restApi.ENFORCE_SSO_ON_TENANT_ACCESS_FF], store.root.appName);
84
+ if (!isEnforceSsoOnTenantAccessEnabled || !(0, _helpers.isSsoEnforcementError)(e)) {
85
+ return false;
86
+ }
87
+ const {
88
+ errorContext
89
+ } = e;
90
+ // the switch re-run after coming back from SSO keeps the session instead of sending the user to SSO again
91
+ const isPendingSwitchRerun = !!window.sessionStorage.getItem(_constants.FRONTEGG_PENDING_SWITCH_TENANT);
92
+ if (errorContext.ssoRequired && !isPendingSwitchRerun) {
93
+ await __redirectToSsoForTenant(errorContext);
94
+ return true;
95
+ }
96
+ if (isSsoEnforcementRetry) {
97
+ return false;
98
+ }
99
+ const currentTenantId = (_store$auth$user = store.auth.user) == null ? void 0 : _store$auth$user.tenantId;
100
+ if (currentTenantId && contextHolder.isSessionPerTenantEnabled()) {
101
+ (0, _restApi.setTabTenantInSessionStorage)(currentTenantId);
102
+ }
103
+ await refreshForTenant(currentTenantId);
104
+ return true;
105
+ };
106
+
51
107
  /** @private */
52
- const __refreshTokenHosted = async () => {
108
+ const __refreshTokenHosted = async ({
109
+ tenantId,
110
+ isSsoEnforcementRetry = false
111
+ } = {}) => {
53
112
  const user = store.auth.user;
54
113
  if (!(user != null && user.refreshToken)) {
55
114
  contextHolder.setAccessToken(null);
@@ -61,7 +120,7 @@ var _default = (store, api, sharedActions) => {
61
120
  return;
62
121
  }
63
122
  try {
64
- const tabTenantId = (0, _restApi.getTabTenantFromSessionStorage)(store.root.appName);
123
+ const tabTenantId = tenantId != null ? tenantId : (0, _restApi.getTabTenantFromSessionStorage)(store.root.appName);
65
124
  const body = (0, _extends2.default)({
66
125
  grant_type: 'refresh_token',
67
126
  refresh_token: user == null ? void 0 : user.refreshToken
@@ -76,6 +135,13 @@ var _default = (store, api, sharedActions) => {
76
135
  isAuthenticated: true
77
136
  });
78
137
  } catch (e) {
138
+ const retry = tenantId => __refreshTokenHosted({
139
+ tenantId,
140
+ isSsoEnforcementRetry: true
141
+ });
142
+ if (await __handleSsoEnforcementRefreshError(e, isSsoEnforcementRetry, retry)) {
143
+ return;
144
+ }
79
145
  contextHolder.setAccessToken(null);
80
146
  contextHolder.setUser(null);
81
147
  actions.setAuthState({
@@ -95,7 +161,10 @@ var _default = (store, api, sharedActions) => {
95
161
  };
96
162
 
97
163
  /** @private */
98
- const __refreshTokenEmbedded = async () => {
164
+ const __refreshTokenEmbedded = async ({
165
+ tenantId,
166
+ isSsoEnforcementRetry = false
167
+ } = {}) => {
99
168
  try {
100
169
  const {
101
170
  routes,
@@ -106,7 +175,7 @@ var _default = (store, api, sharedActions) => {
106
175
  user,
107
176
  tenants = [],
108
177
  activeTenant
109
- } = await (0, _helpers.refreshTokenWithRetry)(() => api.auth.refreshTokenV3());
178
+ } = await (0, _helpers.refreshTokenWithRetry)(() => api.auth.refreshTokenV3(tenantId));
110
179
  if ((0, _helpers3.isMfaRequired)(user, store.root.appName)) {
111
180
  const mfaRequiredState = await actions.getMfaRequiredState(user);
112
181
  actions.setAuthState(mfaRequiredState);
@@ -137,6 +206,13 @@ var _default = (store, api, sharedActions) => {
137
206
  });
138
207
  }
139
208
  } catch (e) {
209
+ const retry = tenantId => __refreshTokenEmbedded({
210
+ tenantId,
211
+ isSsoEnforcementRetry: true
212
+ });
213
+ if (await __handleSsoEnforcementRefreshError(e, isSsoEnforcementRetry, retry)) {
214
+ return;
215
+ }
140
216
  contextHolder.setAccessToken(null);
141
217
  contextHolder.setUser(null);
142
218
  actions.setAuthState({
@@ -147,7 +223,9 @@ var _default = (store, api, sharedActions) => {
147
223
  };
148
224
 
149
225
  /** @private */
150
- const __refreshTokenWithNativeTokens = async () => {
226
+ const __refreshTokenWithNativeTokens = async ({
227
+ isSsoEnforcementRetry = false
228
+ } = {}) => {
151
229
  try {
152
230
  const authApi = api.auth;
153
231
  const response = await (0, _helpers.refreshTokenWithRetry)(async () => {
@@ -161,6 +239,13 @@ var _default = (store, api, sharedActions) => {
161
239
  isAuthenticated: true
162
240
  });
163
241
  } catch (e) {
242
+ // native tokens can't name a tenant: the retry relies on the tab tenant only
243
+ const retry = () => __refreshTokenWithNativeTokens({
244
+ isSsoEnforcementRetry: true
245
+ });
246
+ if (await __handleSsoEnforcementRefreshError(e, isSsoEnforcementRetry, retry)) {
247
+ return;
248
+ }
164
249
  contextHolder.setAccessToken(null);
165
250
  contextHolder.setUser(null);
166
251
  actions.setAuthState({
@@ -194,9 +279,9 @@ var _default = (store, api, sharedActions) => {
194
279
  * @private
195
280
  */
196
281
  const __handleUnnecessaryEntitlementsUpdate = async updatedUser => {
197
- var _store$auth$user;
282
+ var _store$auth$user2;
198
283
  // @ts-ignore TODO: fix entitlements state type
199
- const oldEntitlements = (_store$auth$user = store.auth.user) == null ? void 0 : _store$auth$user.entitlements;
284
+ const oldEntitlements = (_store$auth$user2 = store.auth.user) == null ? void 0 : _store$auth$user2.entitlements;
200
285
  if ((0, _toolkit.isEntitlementsDeeplyEqual)(oldEntitlements, updatedUser == null ? void 0 : updatedUser.entitlements)) {
201
286
  // set the previous entitlements object
202
287
  return (0, _extends2.default)({}, updatedUser, {
@@ -666,6 +751,7 @@ var _default = (store, api, sharedActions) => {
666
751
  if (!keepHostedOAuthState) {
667
752
  (0, _oauthStorage.clearHostedOAuthState)();
668
753
  }
754
+ window.sessionStorage.removeItem(_constants.FRONTEGG_PENDING_SWITCH_TENANT);
669
755
  };
670
756
  const logout = async payload => {
671
757
  const hostedLoginBox = store.auth.hostedLoginBox;
@@ -1368,7 +1454,10 @@ var _default = (store, api, sharedActions) => {
1368
1454
  };
1369
1455
 
1370
1456
  /** @private */
1371
- const __refreshTokenHostedSocialLogins = async () => {
1457
+ const __refreshTokenHostedSocialLogins = async ({
1458
+ tenantId,
1459
+ isSsoEnforcementRetry = false
1460
+ } = {}) => {
1372
1461
  const {
1373
1462
  user: currentUser
1374
1463
  } = store.auth;
@@ -1382,7 +1471,7 @@ var _default = (store, api, sharedActions) => {
1382
1471
  return;
1383
1472
  }
1384
1473
  try {
1385
- const tabTenantId = (0, _restApi.getTabTenantFromSessionStorage)(store.root.appName);
1474
+ const tabTenantId = tenantId != null ? tenantId : (0, _restApi.getTabTenantFromSessionStorage)(store.root.appName);
1386
1475
  const body = (0, _extends2.default)({
1387
1476
  grant_type: 'refresh_token',
1388
1477
  refresh_token: currentUser == null ? void 0 : currentUser.refreshToken
@@ -1397,6 +1486,13 @@ var _default = (store, api, sharedActions) => {
1397
1486
  isAuthenticated: true
1398
1487
  });
1399
1488
  } catch (e) {
1489
+ const retry = tenantId => __refreshTokenHostedSocialLogins({
1490
+ tenantId,
1491
+ isSsoEnforcementRetry: true
1492
+ });
1493
+ if (await __handleSsoEnforcementRefreshError(e, isSsoEnforcementRetry, retry)) {
1494
+ return;
1495
+ }
1400
1496
  contextHolder.setAccessToken(null);
1401
1497
  contextHolder.setUser(null);
1402
1498
  actions.setAuthState({
@@ -1406,7 +1502,10 @@ var _default = (store, api, sharedActions) => {
1406
1502
  }
1407
1503
  };
1408
1504
  /** @private */
1409
- const __refreshTokenEmbeddedSocialLogins = async () => {
1505
+ const __refreshTokenEmbeddedSocialLogins = async ({
1506
+ tenantId,
1507
+ isSsoEnforcementRetry = false
1508
+ } = {}) => {
1410
1509
  try {
1411
1510
  const {
1412
1511
  routes,
@@ -1417,7 +1516,7 @@ var _default = (store, api, sharedActions) => {
1417
1516
  user,
1418
1517
  tenants = [],
1419
1518
  activeTenant
1420
- } = await (0, _helpers.refreshTokenWithRetry)(() => api.auth.refreshTokenV3());
1519
+ } = await (0, _helpers.refreshTokenWithRetry)(() => api.auth.refreshTokenV3(tenantId));
1421
1520
  if ((0, _helpers3.isMfaRequired)(user, store.root.appName)) {
1422
1521
  const mfaRequiredState = await actions.getMfaRequiredState(user);
1423
1522
  actions.setAuthState(mfaRequiredState);
@@ -1467,6 +1566,13 @@ var _default = (store, api, sharedActions) => {
1467
1566
  }
1468
1567
  }
1469
1568
  } catch (e) {
1569
+ const retry = tenantId => __refreshTokenEmbeddedSocialLogins({
1570
+ tenantId,
1571
+ isSsoEnforcementRetry: true
1572
+ });
1573
+ if (await __handleSsoEnforcementRefreshError(e, isSsoEnforcementRetry, retry)) {
1574
+ return;
1575
+ }
1470
1576
  contextHolder.setAccessToken(null);
1471
1577
  contextHolder.setUser(null);
1472
1578
  actions.setAuthState({
@@ -1513,7 +1619,8 @@ var _default = (store, api, sharedActions) => {
1513
1619
  resetBreachedPassword,
1514
1620
  refreshTokenForSocialLogins,
1515
1621
  // protected
1516
- __refreshToken
1622
+ __refreshToken,
1623
+ __redirectToSsoForTenant
1517
1624
  }, (0, _hostedLoginAuthorize.default)(store, api, sharedActions), (0, _afterAuthNavigation.default)(store, api, sharedActions), (0, _handleVerifyMFAResponse.default)(store, api, sharedActions), (0, _mfaRequiredState.default)(store, api, sharedActions), (0, _mfaWithAuthenticator.default)(store, api, sharedActions), (0, _mfaWithEmailCode.default)(store, api, sharedActions), (0, _mfaWithSMS.default)(store, api, sharedActions), (0, _mfaWithWebAuthn.default)(store, api, sharedActions));
1518
1625
  };
1519
1626
  exports.default = _default;
@@ -7,6 +7,7 @@ exports.default = void 0;
7
7
  var _state = require("./state");
8
8
  var _helpers = require("../../helpers");
9
9
  var _restApi = require("@frontegg/rest-api");
10
+ var _constants = require("../../constants");
10
11
  var _default = (store, api, sharedActions) => {
11
12
  const actions = sharedActions;
12
13
  const setTenantsState = state => {
@@ -57,6 +58,15 @@ var _default = (store, api, sharedActions) => {
57
58
  }) : actions.setAuthState({
58
59
  isLoading: false
59
60
  });
61
+ const [isEnforceSsoOnTenantAccessEnabled] = _restApi.FeatureFlags.getFeatureFlags([_restApi.ENFORCE_SSO_ON_TENANT_ACCESS_FF], store.root.appName);
62
+ // a switch re-run after coming back from SSO doesn't send the user to SSO again
63
+ const isPendingSwitchRerun = window.sessionStorage.getItem(_constants.FRONTEGG_PENDING_SWITCH_TENANT) === tenantId;
64
+ if (isEnforceSsoOnTenantAccessEnabled && (0, _helpers.isSsoEnforcementError)(e) && e.errorContext.ssoRequired && !isPendingSwitchRerun) {
65
+ // saved to finish the switch after the SSO login, which may land in another tenant
66
+ window.sessionStorage.setItem(_constants.FRONTEGG_PENDING_SWITCH_TENANT, tenantId);
67
+ await actions.__redirectToSsoForTenant(e.errorContext);
68
+ return;
69
+ }
60
70
  callback == null ? void 0 : callback(false, e);
61
71
  }
62
72
  };
package/node/constants.js CHANGED
@@ -5,10 +5,11 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  var _exportNames = {
7
7
  FRONTEGG_AFTER_AUTH_REDIRECT_URL: true,
8
+ FRONTEGG_PENDING_SWITCH_TENANT: true,
8
9
  HOSTED_LOGIN_VERIFIER_KEY: true,
9
10
  DEFAULT_RETRY_CONFIG: true
10
11
  };
11
- exports.HOSTED_LOGIN_VERIFIER_KEY = exports.FRONTEGG_AFTER_AUTH_REDIRECT_URL = exports.DEFAULT_RETRY_CONFIG = void 0;
12
+ exports.HOSTED_LOGIN_VERIFIER_KEY = exports.FRONTEGG_PENDING_SWITCH_TENANT = exports.FRONTEGG_AFTER_AUTH_REDIRECT_URL = exports.DEFAULT_RETRY_CONFIG = void 0;
12
13
  var _consts = require("./auth/StepUpState/consts");
13
14
  Object.keys(_consts).forEach(function (key) {
14
15
  if (key === "default" || key === "__esModule") return;
@@ -35,6 +36,8 @@ Object.keys(_consts2).forEach(function (key) {
35
36
  });
36
37
  const FRONTEGG_AFTER_AUTH_REDIRECT_URL = 'FRONTEGG_AFTER_AUTH_REDIRECT_URL';
37
38
  exports.FRONTEGG_AFTER_AUTH_REDIRECT_URL = FRONTEGG_AFTER_AUTH_REDIRECT_URL;
39
+ const FRONTEGG_PENDING_SWITCH_TENANT = 'FRONTEGG_PENDING_SWITCH_TENANT';
40
+ exports.FRONTEGG_PENDING_SWITCH_TENANT = FRONTEGG_PENDING_SWITCH_TENANT;
38
41
  const HOSTED_LOGIN_VERIFIER_KEY = 'HOSTED_LOGIN_VERIFIER_KEY';
39
42
  exports.HOSTED_LOGIN_VERIFIER_KEY = HOSTED_LOGIN_VERIFIER_KEY;
40
43
  const DEFAULT_RETRY_CONFIG = {
@@ -3,10 +3,14 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.isFronteggApiError = exports.isError = exports.errorHandler = void 0;
6
+ exports.isSsoEnforcementError = exports.isFronteggApiError = exports.isError = exports.errorHandler = void 0;
7
7
  var _restApi = require("@frontegg/rest-api");
8
8
  const isFronteggApiError = error => Boolean(error == null ? void 0 : error.statusCode);
9
+
10
+ // ER-01079 (disabled user / expired tenant) is not this: it still logs out
9
11
  exports.isFronteggApiError = isFronteggApiError;
12
+ const isSsoEnforcementError = error => isFronteggApiError(error) && error.errorCode === _restApi.SSO_REQUIRED_FOR_TENANT_ERROR_CODE;
13
+ exports.isSsoEnforcementError = isSsoEnforcementError;
10
14
  const isError = error => {
11
15
  if (error instanceof Error) {
12
16
  return true;
package/node/index.js CHANGED
@@ -1,4 +1,4 @@
1
- /** @license Frontegg v7.128.0
1
+ /** @license Frontegg v7.129.0-alpha.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-alpha.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-alpha.0",
11
11
  "fast-deep-equal": "3.1.3",
12
12
  "get-value": "^3.0.1",
13
13
  "proxy-compare": "^3.0.0",