@frontegg/redux-store 7.130.0 → 7.131.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.
@@ -8,7 +8,9 @@ declare const _default: (store: FronteggState, api: RestApi, sharedActions: Shar
8
8
  firstTime?: boolean;
9
9
  loginDirectAction?: LoginDirectAction;
10
10
  }) => Promise<void>;
11
- __requestHostedLoginAuthorize: (additionalParams?: Record<string, string>) => Promise<void>;
11
+ __requestHostedLoginAuthorize: (additionalParams?: Record<string, string>, { forceTenantId }?: {
12
+ forceTenantId?: boolean;
13
+ }) => Promise<void>;
12
14
  __requestHostedLoginSilentAuthorize: () => Promise<void>;
13
15
  handleHostedLoginCallback: (payload: HostedLoginCallback) => Promise<void>;
14
16
  };
@@ -1,6 +1,7 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
- import { createRandomString, generateCodeChallenge, GTMEventAction, refreshTokenWithRetry, reportGTMEvent } from '../../../helpers';
2
+ import { createRandomString, generateCodeChallenge, GTMEventAction, isSsoEnforcementError, refreshTokenWithRetry, reportGTMEvent } from '../../../helpers';
3
3
  import { HOSTED_LOGIN_VERIFIER_KEY } from '../../../constants';
4
+ import { isEnforceSsoOnTenantAccessEnabled } 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';
@@ -21,7 +22,17 @@ export default ((store, api, sharedActions) => {
21
22
  return await authApi.silentOAuthRefreshTokenV3(tokens);
22
23
  });
23
24
  } else {
24
- response = await api.auth.silentOAuthRefreshTokenV2();
25
+ try {
26
+ response = await api.auth.silentOAuthRefreshTokenV2();
27
+ } catch (error) {
28
+ var _store$auth$user, _error$errorContext;
29
+ const tenantId = (_store$auth$user = store.auth.user) == null ? void 0 : _store$auth$user.tenantId;
30
+ if (!isEnforceSsoOnTenantAccessEnabled(store.root.appName) || !isSsoEnforcementError(error) || ((_error$errorContext = error.errorContext) == null ? void 0 : _error$errorContext.ssoRequired) !== false || !tenantId) {
31
+ throw error;
32
+ }
33
+ // one retry for the tenant of the current token (a cold reload has no user yet: no retry)
34
+ response = await api.auth.silentOAuthRefreshTokenV2(tenantId);
35
+ }
25
36
  }
26
37
  if ((_response = response) != null && _response.user) {
27
38
  await actions.afterAuthenticationStateUpdate(response, {
@@ -32,7 +43,14 @@ export default ((store, api, sharedActions) => {
32
43
  throw new Error(`couldn't refresh user token with oauth service`);
33
44
  }
34
45
  };
35
- const requestHostedLoginAuthorize = async additionalParams => {
46
+
47
+ /**
48
+ * @param options.forceTenantId the tenantId in additionalParams wins over the resolved and the page's tenant
49
+ * (sending the user to that tenant's SSO); other callers keep their custom login tenant
50
+ */
51
+ const requestHostedLoginAuthorize = async (additionalParams, {
52
+ forceTenantId = false
53
+ } = {}) => {
36
54
  var _store$auth$customLog;
37
55
  const {
38
56
  routes,
@@ -79,7 +97,7 @@ export default ((store, api, sharedActions) => {
79
97
  code_challenge_method: 'S256',
80
98
  nonce
81
99
  }, additionalParams);
82
- if (context.tenantResolver) {
100
+ if (context.tenantResolver && !forceTenantId) {
83
101
  var _context$tenantResolv;
84
102
  const resolvedTenantResult = await ((_context$tenantResolv = context.tenantResolver) == null ? void 0 : _context$tenantResolv.call(context));
85
103
  if (resolvedTenantResult != null && resolvedTenantResult.tenant) {
@@ -87,7 +105,7 @@ export default ((store, api, sharedActions) => {
87
105
  }
88
106
  }
89
107
  const tenantId = getSearchParam(TENANT_ID_PARAM_KEY);
90
- if (tenantId) {
108
+ if (tenantId && !forceTenantId) {
91
109
  params['tenantId'] = tenantId;
92
110
  }
93
111
  if (FronteggNativeModule.isAvailable('closeWindow')) {
@@ -37,7 +37,9 @@ declare const _default: (store: FronteggState, api: RestApi, sharedActions: Shar
37
37
  firstTime?: boolean;
38
38
  loginDirectAction?: import("../interfaces").LoginDirectAction;
39
39
  }) => Promise<void>;
40
- __requestHostedLoginAuthorize: (additionalParams?: Record<string, string>) => Promise<void>;
40
+ __requestHostedLoginAuthorize: (additionalParams?: Record<string, string>, { forceTenantId }?: {
41
+ forceTenantId?: boolean;
42
+ }) => Promise<void>;
41
43
  __requestHostedLoginSilentAuthorize: () => Promise<void>;
42
44
  handleHostedLoginCallback: (payload: import("../interfaces").HostedLoginCallback) => Promise<void>;
43
45
  setLoginState: (state: Partial<LoginState>) => void;
@@ -75,5 +77,10 @@ declare const _default: (store: FronteggState, api: RestApi, sharedActions: Shar
75
77
  resetBreachedPassword: (payload: IForgotPassword) => Promise<void>;
76
78
  refreshTokenForSocialLogins: () => Promise<void>;
77
79
  __refreshToken: () => Promise<void>;
80
+ __redirectToSsoForTenant: ({ tenantId, address }: {
81
+ tenantId: string;
82
+ address: string;
83
+ }) => Promise<void>;
84
+ __redirectToSsoIfRequired: (e: unknown) => Promise<boolean>;
78
85
  };
79
86
  export default _default;
@@ -1,20 +1,21 @@
1
- import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
2
1
  import _extends from "@babel/runtime/helpers/esm/extends";
3
- const _excluded = ["callback", "error"],
4
- _excluded2 = ["callback"],
2
+ import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
3
+ const _excluded = ["authenticatedUrl"],
4
+ _excluded2 = ["callback", "error"],
5
5
  _excluded3 = ["callback"],
6
6
  _excluded4 = ["callback"],
7
7
  _excluded5 = ["callback"],
8
- _excluded6 = ["callback", "events"],
9
- _excluded7 = ["callback"],
8
+ _excluded6 = ["callback"],
9
+ _excluded7 = ["callback", "events"],
10
10
  _excluded8 = ["callback"],
11
- _excluded9 = ["user"],
12
- _excluded10 = ["callback"],
11
+ _excluded9 = ["callback"],
12
+ _excluded10 = ["user"],
13
13
  _excluded11 = ["callback"],
14
14
  _excluded12 = ["callback"],
15
15
  _excluded13 = ["callback"],
16
16
  _excluded14 = ["callback"],
17
- _excluded15 = ["callback"];
17
+ _excluded15 = ["callback"],
18
+ _excluded16 = ["callback"];
18
19
  import afterAuthNavigationActions from './afterAuthNavigation.actions';
19
20
  import handleVerifyMFAResponseActions from './handleVerifyMFAResponse.actions';
20
21
  import mfaRequiredStateActions from './mfaRequiredState.actions';
@@ -23,17 +24,17 @@ import mfaWithEmailCodeActions from './mfaWithEmailCode.actions';
23
24
  import mfaWithSMSActions from './mfaWithSMS.actions';
24
25
  import mfaWithWebAuthnActions from './mfaWithWebAuthn.actions';
25
26
  import { LoginFlow, LoginStep } from '../interfaces';
26
- import { base64urlDecode, deepResetState, delay, errorHandler, errorTraceId, GTMEventAction, publicKeyCredentialToJSON, refreshTokenWithRetry, reportGTMEvent, retryIfNeeded, withRetryConfig } from '../../../helpers';
27
+ import { base64urlDecode, deepResetState, delay, errorHandler, errorTraceId, GTMEventAction, isSsoEnforcementError, publicKeyCredentialToJSON, refreshTokenWithRetry, reportGTMEvent, retryIfNeeded, withRetryConfig } from '../../../helpers';
27
28
  import { initialState } from '../state';
28
29
  import { getSearchParam, isEmailPayload, isUsernamePayload, shouldShowPasswordRotationPromptFunc, TENANT_ID_PARAM_KEY } from '../helpers';
29
- import { AuthStrategyEnum, ContextHolder, FeatureFlags, getTabTenantFromSessionStorage, removeTabTenantFromSessionStorage, WebAuthnDeviceType, PreLoginAddressType } from '@frontegg/rest-api';
30
+ import { AuthStrategyEnum, ContextHolder, isEnforceSsoOnTenantAccessEnabled, FeatureFlags, getTabTenantFromSessionStorage, removeTabTenantFromSessionStorage, setTabTenantInSessionStorage, WebAuthnDeviceType, PreLoginAddressType } from '@frontegg/rest-api';
30
31
  import hostedLoginAuthorizeActions from './hostedLoginAuthorize.actions';
31
32
  import { clearHostedOAuthState } from '../oauthStorage';
32
33
  import { SHOULD_STEP_UP_KEY } from '../../StepUpState/consts';
33
34
  import { FronteggNativeModule, isEntitlementsDeeplyEqual } from '../../../toolkit';
34
35
  import { REQUEST_NAME, UserVerifiedOriginTypes } from '../../interfaces';
35
- import { authStrategyLoginStepMap, defaultOnRedirectTo } from '../consts';
36
- import { isMfaRequired, isResetPasswordRequired } from '../../helpers';
36
+ import { authStrategyLoginStepMap, defaultFronteggRoutes, defaultOnRedirectTo } from '../consts';
37
+ import { isMfaRequired, isResetPasswordRequired, setAfterAuthRedirectUrlForStepUp } from '../../helpers';
37
38
  import { MFAStep } from '../../MfaState/interfaces';
38
39
  import { SamlVendors } from '../../SSOState/interfaces';
39
40
  import { DEFAULT_RETRY_CONFIG } from '../../../constants';
@@ -43,8 +44,83 @@ export default ((store, api, sharedActions) => {
43
44
  const actions = sharedActions;
44
45
  const contextHolder = ContextHolder.for(store.root.appName);
45
46
 
47
+ /**
48
+ * Sends the user to log in through the tenant's SSO and back to the current page afterwards.
49
+ * @protected
50
+ */
51
+ const __redirectToSsoForTenant = async ({
52
+ tenantId,
53
+ address
54
+ }) => {
55
+ // on an auth page (login, tenant chooser, social / magic link / SSO callback) keep the destination the login
56
+ // already saved: the page itself is not one to come back to
57
+ const _defaultFronteggRoute = _extends({}, defaultFronteggRoutes, store.auth.routes),
58
+ authRoutes = _objectWithoutPropertiesLoose(_defaultFronteggRoute, _excluded);
59
+ const isOnAuthPage = Object.values(authRoutes).some(route => typeof route === 'string' && !!route && window.location.pathname.endsWith(route));
60
+ if (!isOnAuthPage) {
61
+ setAfterAuthRedirectUrlForStepUp();
62
+ }
63
+ if (store.auth.hostedLoginBox) {
64
+ var _window$localStorage;
65
+ // as loginWithRedirect does, but the SSO tenant wins over the custom login tenant
66
+ (_window$localStorage = window.localStorage) == null ? void 0 : _window$localStorage.removeItem(SHOULD_STEP_UP_KEY);
67
+ await actions.__requestHostedLoginAuthorize({
68
+ prompt: 'login',
69
+ tenantId
70
+ }, {
71
+ forceTenantId: true
72
+ });
73
+ return;
74
+ }
75
+ store.auth.onRedirectTo(address, {
76
+ refresh: true
77
+ });
78
+ };
79
+
80
+ /**
81
+ * Sends the user to the tenant's SSO when a login or tenant switch is refused because that tenant requires it
82
+ * (e.g. an invitation into a tenant whose SSO covers the user's email). The SSO login lands in that tenant.
83
+ * @returns true when redirected, so the caller doesn't show the error
84
+ * @protected
85
+ */
86
+ const __redirectToSsoIfRequired = async e => {
87
+ if (!isEnforceSsoOnTenantAccessEnabled(store.root.appName) || !isSsoEnforcementError(e) || !e.errorContext.ssoRequired) {
88
+ return false;
89
+ }
90
+ await __redirectToSsoForTenant(e.errorContext);
91
+ return true;
92
+ };
93
+ /**
94
+ * Handles a hosted (OAuth) token refresh refused because the tenant requires SSO.
95
+ * ssoRequired: true sends the user to SSO, like login. ssoRequired: false renews once more for the tenant of the
96
+ * current token, which keeps the session: OAuth doesn't use up the refresh token on this refusal.
97
+ * @returns true when handled, false when the caller should log out
98
+ * @private
99
+ */
100
+ const __handleSsoEnforcementRefreshError = async (e, isSsoEnforcementRetry, refreshForTenant) => {
101
+ var _store$auth$user;
102
+ if (!isEnforceSsoOnTenantAccessEnabled(store.root.appName) || !isSsoEnforcementError(e)) {
103
+ return false;
104
+ }
105
+ if (await __redirectToSsoIfRequired(e)) {
106
+ return true;
107
+ }
108
+ const currentTenantId = (_store$auth$user = store.auth.user) == null ? void 0 : _store$auth$user.tenantId;
109
+ if (isSsoEnforcementRetry || !currentTenantId) {
110
+ return false;
111
+ }
112
+ if (contextHolder.isSessionPerTenantEnabled()) {
113
+ setTabTenantInSessionStorage(currentTenantId);
114
+ }
115
+ await refreshForTenant(currentTenantId);
116
+ return true;
117
+ };
118
+
46
119
  /** @private */
47
- const __refreshTokenHosted = async () => {
120
+ const __refreshTokenHosted = async ({
121
+ tenantId,
122
+ isSsoEnforcementRetry = false
123
+ } = {}) => {
48
124
  const user = store.auth.user;
49
125
  if (!(user != null && user.refreshToken)) {
50
126
  contextHolder.setAccessToken(null);
@@ -56,7 +132,7 @@ export default ((store, api, sharedActions) => {
56
132
  return;
57
133
  }
58
134
  try {
59
- const tabTenantId = getTabTenantFromSessionStorage(store.root.appName);
135
+ const tabTenantId = tenantId != null ? tenantId : getTabTenantFromSessionStorage(store.root.appName);
60
136
  const body = _extends({
61
137
  grant_type: 'refresh_token',
62
138
  refresh_token: user == null ? void 0 : user.refreshToken
@@ -71,6 +147,13 @@ export default ((store, api, sharedActions) => {
71
147
  isAuthenticated: true
72
148
  });
73
149
  } catch (e) {
150
+ const retry = tenantId => __refreshTokenHosted({
151
+ tenantId,
152
+ isSsoEnforcementRetry: true
153
+ });
154
+ if (await __handleSsoEnforcementRefreshError(e, isSsoEnforcementRetry, retry)) {
155
+ return;
156
+ }
74
157
  contextHolder.setAccessToken(null);
75
158
  contextHolder.setUser(null);
76
159
  actions.setAuthState({
@@ -132,6 +215,10 @@ export default ((store, api, sharedActions) => {
132
215
  });
133
216
  }
134
217
  } catch (e) {
218
+ // a refused identity refresh has already used up the refresh token: no retry, only the SSO redirect
219
+ if (await __redirectToSsoIfRequired(e)) {
220
+ return;
221
+ }
135
222
  contextHolder.setAccessToken(null);
136
223
  contextHolder.setUser(null);
137
224
  actions.setAuthState({
@@ -142,7 +229,9 @@ export default ((store, api, sharedActions) => {
142
229
  };
143
230
 
144
231
  /** @private */
145
- const __refreshTokenWithNativeTokens = async () => {
232
+ const __refreshTokenWithNativeTokens = async ({
233
+ isSsoEnforcementRetry = false
234
+ } = {}) => {
146
235
  try {
147
236
  const authApi = api.auth;
148
237
  const response = await refreshTokenWithRetry(async () => {
@@ -156,6 +245,13 @@ export default ((store, api, sharedActions) => {
156
245
  isAuthenticated: true
157
246
  });
158
247
  } catch (e) {
248
+ // native tokens can't name a tenant: the retry relies on the tab tenant only
249
+ const retry = () => __refreshTokenWithNativeTokens({
250
+ isSsoEnforcementRetry: true
251
+ });
252
+ if (await __handleSsoEnforcementRefreshError(e, isSsoEnforcementRetry, retry)) {
253
+ return;
254
+ }
159
255
  contextHolder.setAccessToken(null);
160
256
  contextHolder.setUser(null);
161
257
  actions.setAuthState({
@@ -189,9 +285,9 @@ export default ((store, api, sharedActions) => {
189
285
  * @private
190
286
  */
191
287
  const __handleUnnecessaryEntitlementsUpdate = async updatedUser => {
192
- var _store$auth$user;
288
+ var _store$auth$user2;
193
289
  // @ts-ignore TODO: fix entitlements state type
194
- const oldEntitlements = (_store$auth$user = store.auth.user) == null ? void 0 : _store$auth$user.entitlements;
290
+ const oldEntitlements = (_store$auth$user2 = store.auth.user) == null ? void 0 : _store$auth$user2.entitlements;
195
291
  if (isEntitlementsDeeplyEqual(oldEntitlements, updatedUser == null ? void 0 : updatedUser.entitlements)) {
196
292
  // set the previous entitlements object
197
293
  return _extends({}, updatedUser, {
@@ -359,7 +455,7 @@ export default ((store, api, sharedActions) => {
359
455
  callback,
360
456
  error
361
457
  } = _ref3,
362
- body = _objectWithoutPropertiesLoose(_ref3, _excluded);
458
+ body = _objectWithoutPropertiesLoose(_ref3, _excluded2);
363
459
  const publicPolicy = store.auth.securityPolicyState.publicPolicy.policy;
364
460
  if (!(publicPolicy != null && publicPolicy.authStrategy)) {
365
461
  actions.setLoginState({
@@ -668,6 +764,9 @@ export default ((store, api, sharedActions) => {
668
764
  } catch (e) {
669
765
  contextHolder.setAccessToken(null);
670
766
  contextHolder.setUser(null);
767
+ if (await __redirectToSsoIfRequired(e)) {
768
+ return;
769
+ }
671
770
  callback == null ? void 0 : callback(false, e);
672
771
  setLoginState({
673
772
  email,
@@ -752,7 +851,7 @@ export default ((store, api, sharedActions) => {
752
851
  const {
753
852
  callback
754
853
  } = _payload,
755
- payload = _objectWithoutPropertiesLoose(_payload, _excluded2);
854
+ payload = _objectWithoutPropertiesLoose(_payload, _excluded3);
756
855
  try {
757
856
  setLoginState({
758
857
  loading: true
@@ -782,7 +881,7 @@ export default ((store, api, sharedActions) => {
782
881
  const {
783
882
  callback
784
883
  } = _payload,
785
- payload = _objectWithoutPropertiesLoose(_payload, _excluded3);
884
+ payload = _objectWithoutPropertiesLoose(_payload, _excluded4);
786
885
  try {
787
886
  setLoginState({
788
887
  loading: true
@@ -807,7 +906,7 @@ export default ((store, api, sharedActions) => {
807
906
  const {
808
907
  callback
809
908
  } = _payload,
810
- payload = _objectWithoutPropertiesLoose(_payload, _excluded4);
909
+ payload = _objectWithoutPropertiesLoose(_payload, _excluded5);
811
910
  try {
812
911
  setLoginState({
813
912
  loading: true
@@ -828,7 +927,7 @@ export default ((store, api, sharedActions) => {
828
927
  const {
829
928
  callback
830
929
  } = _payload,
831
- payload = _objectWithoutPropertiesLoose(_payload, _excluded5);
930
+ payload = _objectWithoutPropertiesLoose(_payload, _excluded6);
832
931
  try {
833
932
  var _username;
834
933
  setLoginState({
@@ -872,7 +971,7 @@ export default ((store, api, sharedActions) => {
872
971
  callback,
873
972
  events
874
973
  } = _payload,
875
- payload = _objectWithoutPropertiesLoose(_payload, _excluded6);
974
+ payload = _objectWithoutPropertiesLoose(_payload, _excluded7);
876
975
  try {
877
976
  setLoginState({
878
977
  loading: true
@@ -963,6 +1062,9 @@ export default ((store, api, sharedActions) => {
963
1062
  callback == null ? void 0 : callback(true);
964
1063
  }
965
1064
  } catch (e) {
1065
+ if (await __redirectToSsoIfRequired(e)) {
1066
+ return;
1067
+ }
966
1068
  setLoginState({
967
1069
  error: errorHandler(e, 'Failed to authenticate')
968
1070
  });
@@ -1000,7 +1102,7 @@ export default ((store, api, sharedActions) => {
1000
1102
  const {
1001
1103
  callback
1002
1104
  } = payload,
1003
- body = _objectWithoutPropertiesLoose(payload, _excluded7);
1105
+ body = _objectWithoutPropertiesLoose(payload, _excluded8);
1004
1106
  try {
1005
1107
  var _options$allowCredent;
1006
1108
  actions.setPasskeysState({
@@ -1040,7 +1142,7 @@ export default ((store, api, sharedActions) => {
1040
1142
  const {
1041
1143
  callback
1042
1144
  } = payload,
1043
- body = _objectWithoutPropertiesLoose(payload, _excluded8);
1145
+ body = _objectWithoutPropertiesLoose(payload, _excluded9);
1044
1146
  try {
1045
1147
  var _publicKey$response$u;
1046
1148
  actions.setPasskeysState({
@@ -1060,7 +1162,7 @@ export default ((store, api, sharedActions) => {
1060
1162
  {
1061
1163
  user
1062
1164
  } = _await$api$auth$webAu,
1063
- rest = _objectWithoutPropertiesLoose(_await$api$auth$webAu, _excluded9);
1165
+ rest = _objectWithoutPropertiesLoose(_await$api$auth$webAu, _excluded10);
1064
1166
  if (isMfaRequired(user, store.root.appName)) {
1065
1167
  const {
1066
1168
  routes,
@@ -1099,6 +1201,9 @@ export default ((store, api, sharedActions) => {
1099
1201
  }
1100
1202
  // callback?.(true);
1101
1203
  } catch (e) {
1204
+ if (await __redirectToSsoIfRequired(e)) {
1205
+ return;
1206
+ }
1102
1207
  setLoginState({
1103
1208
  error: errorHandler(e)
1104
1209
  });
@@ -1144,7 +1249,7 @@ export default ((store, api, sharedActions) => {
1144
1249
  const {
1145
1250
  callback
1146
1251
  } = payload,
1147
- body = _objectWithoutPropertiesLoose(payload, _excluded10);
1252
+ body = _objectWithoutPropertiesLoose(payload, _excluded11);
1148
1253
  try {
1149
1254
  setLoginState({
1150
1255
  loading: true
@@ -1209,7 +1314,7 @@ export default ((store, api, sharedActions) => {
1209
1314
  const {
1210
1315
  callback
1211
1316
  } = _payload,
1212
- payload = _objectWithoutPropertiesLoose(_payload, _excluded11);
1317
+ payload = _objectWithoutPropertiesLoose(_payload, _excluded12);
1213
1318
  setLoginState({
1214
1319
  loading: true
1215
1320
  });
@@ -1244,7 +1349,7 @@ export default ((store, api, sharedActions) => {
1244
1349
  const {
1245
1350
  callback
1246
1351
  } = _payload,
1247
- payload = _objectWithoutPropertiesLoose(_payload, _excluded12);
1352
+ payload = _objectWithoutPropertiesLoose(_payload, _excluded13);
1248
1353
  setLoginState({
1249
1354
  loading: true
1250
1355
  });
@@ -1270,7 +1375,7 @@ export default ((store, api, sharedActions) => {
1270
1375
  const {
1271
1376
  callback
1272
1377
  } = _payload,
1273
- payload = _objectWithoutPropertiesLoose(_payload, _excluded13);
1378
+ payload = _objectWithoutPropertiesLoose(_payload, _excluded14);
1274
1379
  setLoginState({
1275
1380
  loading: true
1276
1381
  });
@@ -1293,7 +1398,7 @@ export default ((store, api, sharedActions) => {
1293
1398
  const {
1294
1399
  callback
1295
1400
  } = _payload,
1296
- payload = _objectWithoutPropertiesLoose(_payload, _excluded14);
1401
+ payload = _objectWithoutPropertiesLoose(_payload, _excluded15);
1297
1402
  setLoginState({
1298
1403
  loading: true
1299
1404
  });
@@ -1320,7 +1425,7 @@ export default ((store, api, sharedActions) => {
1320
1425
  const {
1321
1426
  callback
1322
1427
  } = _payload,
1323
- payload = _objectWithoutPropertiesLoose(_payload, _excluded15);
1428
+ payload = _objectWithoutPropertiesLoose(_payload, _excluded16);
1324
1429
  setLoginState({
1325
1430
  loading: true
1326
1431
  });
@@ -1388,7 +1493,10 @@ export default ((store, api, sharedActions) => {
1388
1493
  };
1389
1494
 
1390
1495
  /** @private */
1391
- const __refreshTokenHostedSocialLogins = async () => {
1496
+ const __refreshTokenHostedSocialLogins = async ({
1497
+ tenantId,
1498
+ isSsoEnforcementRetry = false
1499
+ } = {}) => {
1392
1500
  const {
1393
1501
  user: currentUser
1394
1502
  } = store.auth;
@@ -1402,7 +1510,7 @@ export default ((store, api, sharedActions) => {
1402
1510
  return;
1403
1511
  }
1404
1512
  try {
1405
- const tabTenantId = getTabTenantFromSessionStorage(store.root.appName);
1513
+ const tabTenantId = tenantId != null ? tenantId : getTabTenantFromSessionStorage(store.root.appName);
1406
1514
  const body = _extends({
1407
1515
  grant_type: 'refresh_token',
1408
1516
  refresh_token: currentUser == null ? void 0 : currentUser.refreshToken
@@ -1417,6 +1525,13 @@ export default ((store, api, sharedActions) => {
1417
1525
  isAuthenticated: true
1418
1526
  });
1419
1527
  } catch (e) {
1528
+ const retry = tenantId => __refreshTokenHostedSocialLogins({
1529
+ tenantId,
1530
+ isSsoEnforcementRetry: true
1531
+ });
1532
+ if (await __handleSsoEnforcementRefreshError(e, isSsoEnforcementRetry, retry)) {
1533
+ return;
1534
+ }
1420
1535
  contextHolder.setAccessToken(null);
1421
1536
  contextHolder.setUser(null);
1422
1537
  actions.setAuthState({
@@ -1487,6 +1602,9 @@ export default ((store, api, sharedActions) => {
1487
1602
  }
1488
1603
  }
1489
1604
  } catch (e) {
1605
+ if (await __redirectToSsoIfRequired(e)) {
1606
+ return;
1607
+ }
1490
1608
  contextHolder.setAccessToken(null);
1491
1609
  contextHolder.setUser(null);
1492
1610
  actions.setAuthState({
@@ -1533,6 +1651,8 @@ export default ((store, api, sharedActions) => {
1533
1651
  resetBreachedPassword,
1534
1652
  refreshTokenForSocialLogins,
1535
1653
  // protected
1536
- __refreshToken
1654
+ __refreshToken,
1655
+ __redirectToSsoForTenant,
1656
+ __redirectToSsoIfRequired
1537
1657
  }, 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));
1538
1658
  });
@@ -145,6 +145,9 @@ export default ((store, api, sharedActions) => {
145
145
  }
146
146
  return await __postLoginMfaAuthenticator(isAuthenticated, user, callback);
147
147
  } catch (e) {
148
+ if (await actions.__redirectToSsoIfRequired(e)) {
149
+ return;
150
+ }
148
151
  setLoadingAction({
149
152
  loading: false,
150
153
  error: errorHandler(e)
@@ -68,6 +68,9 @@ export default ((store, api, sharedActions) => {
68
68
  });
69
69
  callback == null ? void 0 : callback(true);
70
70
  } catch (e) {
71
+ if (await actions.__redirectToSsoIfRequired(e)) {
72
+ return;
73
+ }
71
74
  setLoadingAction({
72
75
  loading: false,
73
76
  error: errorHandler(e)
@@ -73,6 +73,9 @@ export default ((store, api, sharedActions) => {
73
73
  });
74
74
  callback == null ? void 0 : callback(true);
75
75
  } catch (e) {
76
+ if (await actions.__redirectToSsoIfRequired(e)) {
77
+ return;
78
+ }
76
79
  setLoadingAction({
77
80
  loading: false,
78
81
  error: errorHandler(e)
@@ -87,6 +87,9 @@ export default ((store, api, sharedActions) => {
87
87
  });
88
88
  callback == null ? void 0 : callback(true);
89
89
  } catch (e) {
90
+ if (await actions.__redirectToSsoIfRequired(e)) {
91
+ return;
92
+ }
90
93
  setLoadingAction({
91
94
  loading: false,
92
95
  error: errorHandler(e)
@@ -157,6 +157,9 @@ export default ((store, api, sharedActions) => {
157
157
  loading: false
158
158
  });
159
159
  } catch (e) {
160
+ if (await actions.__redirectToSsoIfRequired(e)) {
161
+ return;
162
+ }
160
163
  setSocialLoginsState({
161
164
  loading: false,
162
165
  error: errorHandler(e, 'Failed to authenticate'),
@@ -1,6 +1,6 @@
1
1
  import { initialState } from './state';
2
2
  import { deepResetState } from '../../helpers';
3
- import { getCurrentUserTenantsFunction } from '@frontegg/rest-api';
3
+ import { ContextHolder, getCurrentUserTenantsFunction, setTabTenantInSessionStorage } from '@frontegg/rest-api';
4
4
  export default ((store, api, sharedActions) => {
5
5
  const actions = sharedActions;
6
6
  const setTenantsState = state => {
@@ -51,6 +51,13 @@ export default ((store, api, sharedActions) => {
51
51
  }) : actions.setAuthState({
52
52
  isLoading: false
53
53
  });
54
+ if (await actions.__redirectToSsoIfRequired(e)) {
55
+ // the tab comes back from SSO in the target tenant, not the one the failed switch restored
56
+ if (ContextHolder.for(store.root.appName).isSessionPerTenantEnabled()) {
57
+ setTabTenantInSessionStorage(tenantId);
58
+ }
59
+ return;
60
+ }
54
61
  callback == null ? void 0 : callback(false, e);
55
62
  }
56
63
  };
@@ -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.130.0
1
+ /** @license Frontegg v7.131.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.
@@ -33,7 +33,9 @@ declare const _default: (store: FronteggState, api: RestApi, sharedActions: Shar
33
33
  firstTime?: boolean;
34
34
  loginDirectAction?: import("../..").LoginDirectAction;
35
35
  }) => Promise<void>;
36
- __requestHostedLoginAuthorize: (additionalParams?: Record<string, string>) => Promise<void>;
36
+ __requestHostedLoginAuthorize: (additionalParams?: Record<string, string>, { forceTenantId }?: {
37
+ forceTenantId?: boolean;
38
+ }) => Promise<void>;
37
39
  __requestHostedLoginSilentAuthorize: () => Promise<void>;
38
40
  handleHostedLoginCallback: (payload: import("../..").HostedLoginCallback) => Promise<void>;
39
41
  setLoginState: (state: Partial<import("../..").LoginState>) => void;
@@ -71,5 +73,10 @@ declare const _default: (store: FronteggState, api: RestApi, sharedActions: Shar
71
73
  resetBreachedPassword: (payload: import("dist/@frontegg/rest-api").IForgotPassword) => Promise<void>;
72
74
  refreshTokenForSocialLogins: () => Promise<void>;
73
75
  __refreshToken: () => Promise<void>;
76
+ __redirectToSsoForTenant: ({ tenantId, address }: {
77
+ tenantId: string;
78
+ address: string;
79
+ }) => Promise<void>;
80
+ __redirectToSsoIfRequired: (e: unknown) => Promise<boolean>;
74
81
  };
75
82
  export default _default;
@@ -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");
@@ -28,7 +29,17 @@ var _default = (store, api, sharedActions) => {
28
29
  return await authApi.silentOAuthRefreshTokenV3(tokens);
29
30
  });
30
31
  } else {
31
- response = await api.auth.silentOAuthRefreshTokenV2();
32
+ try {
33
+ response = await api.auth.silentOAuthRefreshTokenV2();
34
+ } catch (error) {
35
+ var _store$auth$user, _error$errorContext;
36
+ const tenantId = (_store$auth$user = store.auth.user) == null ? void 0 : _store$auth$user.tenantId;
37
+ if (!(0, _restApi.isEnforceSsoOnTenantAccessEnabled)(store.root.appName) || !(0, _helpers.isSsoEnforcementError)(error) || ((_error$errorContext = error.errorContext) == null ? void 0 : _error$errorContext.ssoRequired) !== false || !tenantId) {
38
+ throw error;
39
+ }
40
+ // one retry for the tenant of the current token (a cold reload has no user yet: no retry)
41
+ response = await api.auth.silentOAuthRefreshTokenV2(tenantId);
42
+ }
32
43
  }
33
44
  if ((_response = response) != null && _response.user) {
34
45
  await actions.afterAuthenticationStateUpdate(response, {
@@ -39,7 +50,14 @@ var _default = (store, api, sharedActions) => {
39
50
  throw new Error(`couldn't refresh user token with oauth service`);
40
51
  }
41
52
  };
42
- const requestHostedLoginAuthorize = async additionalParams => {
53
+
54
+ /**
55
+ * @param options.forceTenantId the tenantId in additionalParams wins over the resolved and the page's tenant
56
+ * (sending the user to that tenant's SSO); other callers keep their custom login tenant
57
+ */
58
+ const requestHostedLoginAuthorize = async (additionalParams, {
59
+ forceTenantId = false
60
+ } = {}) => {
43
61
  var _store$auth$customLog;
44
62
  const {
45
63
  routes,
@@ -86,7 +104,7 @@ var _default = (store, api, sharedActions) => {
86
104
  code_challenge_method: 'S256',
87
105
  nonce
88
106
  }, additionalParams);
89
- if (context.tenantResolver) {
107
+ if (context.tenantResolver && !forceTenantId) {
90
108
  var _context$tenantResolv;
91
109
  const resolvedTenantResult = await ((_context$tenantResolv = context.tenantResolver) == null ? void 0 : _context$tenantResolv.call(context));
92
110
  if (resolvedTenantResult != null && resolvedTenantResult.tenant) {
@@ -94,7 +112,7 @@ var _default = (store, api, sharedActions) => {
94
112
  }
95
113
  }
96
114
  const tenantId = (0, _helpers2.getSearchParam)(_helpers2.TENANT_ID_PARAM_KEY);
97
- if (tenantId) {
115
+ if (tenantId && !forceTenantId) {
98
116
  params['tenantId'] = tenantId;
99
117
  }
100
118
  if (_toolkit.FronteggNativeModule.isAvailable('closeWindow')) {
@@ -5,8 +5,8 @@ Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
7
  exports.default = void 0;
8
- var _objectWithoutPropertiesLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutPropertiesLoose"));
9
8
  var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
9
+ var _objectWithoutPropertiesLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutPropertiesLoose"));
10
10
  var _afterAuthNavigation = _interopRequireDefault(require("./afterAuthNavigation.actions"));
11
11
  var _handleVerifyMFAResponse = _interopRequireDefault(require("./handleVerifyMFAResponse.actions"));
12
12
  var _mfaRequiredState = _interopRequireDefault(require("./mfaRequiredState.actions"));
@@ -29,29 +29,105 @@ var _helpers3 = require("../../helpers");
29
29
  var _interfaces3 = require("../../MfaState/interfaces");
30
30
  var _interfaces4 = require("../../SSOState/interfaces");
31
31
  var _constants = require("../../../constants");
32
- const _excluded = ["callback", "error"],
33
- _excluded2 = ["callback"],
32
+ const _excluded = ["authenticatedUrl"],
33
+ _excluded2 = ["callback", "error"],
34
34
  _excluded3 = ["callback"],
35
35
  _excluded4 = ["callback"],
36
36
  _excluded5 = ["callback"],
37
- _excluded6 = ["callback", "events"],
38
- _excluded7 = ["callback"],
37
+ _excluded6 = ["callback"],
38
+ _excluded7 = ["callback", "events"],
39
39
  _excluded8 = ["callback"],
40
- _excluded9 = ["user"],
41
- _excluded10 = ["callback"],
40
+ _excluded9 = ["callback"],
41
+ _excluded10 = ["user"],
42
42
  _excluded11 = ["callback"],
43
43
  _excluded12 = ["callback"],
44
44
  _excluded13 = ["callback"],
45
45
  _excluded14 = ["callback"],
46
- _excluded15 = ["callback"];
46
+ _excluded15 = ["callback"],
47
+ _excluded16 = ["callback"];
47
48
  const SSO_REDIRECT_DELAY_MS = 2000;
48
49
  const isUsableRedirect = onRedirectTo => typeof onRedirectTo === 'function' && onRedirectTo !== _consts2.defaultOnRedirectTo;
49
50
  var _default = (store, api, sharedActions) => {
50
51
  const actions = sharedActions;
51
52
  const contextHolder = _restApi.ContextHolder.for(store.root.appName);
52
53
 
54
+ /**
55
+ * Sends the user to log in through the tenant's SSO and back to the current page afterwards.
56
+ * @protected
57
+ */
58
+ const __redirectToSsoForTenant = async ({
59
+ tenantId,
60
+ address
61
+ }) => {
62
+ // on an auth page (login, tenant chooser, social / magic link / SSO callback) keep the destination the login
63
+ // already saved: the page itself is not one to come back to
64
+ const _defaultFronteggRoute = (0, _extends2.default)({}, _consts2.defaultFronteggRoutes, store.auth.routes),
65
+ authRoutes = (0, _objectWithoutPropertiesLoose2.default)(_defaultFronteggRoute, _excluded);
66
+ const isOnAuthPage = Object.values(authRoutes).some(route => typeof route === 'string' && !!route && window.location.pathname.endsWith(route));
67
+ if (!isOnAuthPage) {
68
+ (0, _helpers3.setAfterAuthRedirectUrlForStepUp)();
69
+ }
70
+ if (store.auth.hostedLoginBox) {
71
+ var _window$localStorage;
72
+ // as loginWithRedirect does, but the SSO tenant wins over the custom login tenant
73
+ (_window$localStorage = window.localStorage) == null ? void 0 : _window$localStorage.removeItem(_consts.SHOULD_STEP_UP_KEY);
74
+ await actions.__requestHostedLoginAuthorize({
75
+ prompt: 'login',
76
+ tenantId
77
+ }, {
78
+ forceTenantId: true
79
+ });
80
+ return;
81
+ }
82
+ store.auth.onRedirectTo(address, {
83
+ refresh: true
84
+ });
85
+ };
86
+
87
+ /**
88
+ * Sends the user to the tenant's SSO when a login or tenant switch is refused because that tenant requires it
89
+ * (e.g. an invitation into a tenant whose SSO covers the user's email). The SSO login lands in that tenant.
90
+ * @returns true when redirected, so the caller doesn't show the error
91
+ * @protected
92
+ */
93
+ const __redirectToSsoIfRequired = async e => {
94
+ if (!(0, _restApi.isEnforceSsoOnTenantAccessEnabled)(store.root.appName) || !(0, _helpers.isSsoEnforcementError)(e) || !e.errorContext.ssoRequired) {
95
+ return false;
96
+ }
97
+ await __redirectToSsoForTenant(e.errorContext);
98
+ return true;
99
+ };
100
+ /**
101
+ * Handles a hosted (OAuth) token refresh refused because the tenant requires SSO.
102
+ * ssoRequired: true sends the user to SSO, like login. ssoRequired: false renews once more for the tenant of the
103
+ * current token, which keeps the session: OAuth doesn't use up the refresh token on this refusal.
104
+ * @returns true when handled, false when the caller should log out
105
+ * @private
106
+ */
107
+ const __handleSsoEnforcementRefreshError = async (e, isSsoEnforcementRetry, refreshForTenant) => {
108
+ var _store$auth$user;
109
+ if (!(0, _restApi.isEnforceSsoOnTenantAccessEnabled)(store.root.appName) || !(0, _helpers.isSsoEnforcementError)(e)) {
110
+ return false;
111
+ }
112
+ if (await __redirectToSsoIfRequired(e)) {
113
+ return true;
114
+ }
115
+ const currentTenantId = (_store$auth$user = store.auth.user) == null ? void 0 : _store$auth$user.tenantId;
116
+ if (isSsoEnforcementRetry || !currentTenantId) {
117
+ return false;
118
+ }
119
+ if (contextHolder.isSessionPerTenantEnabled()) {
120
+ (0, _restApi.setTabTenantInSessionStorage)(currentTenantId);
121
+ }
122
+ await refreshForTenant(currentTenantId);
123
+ return true;
124
+ };
125
+
53
126
  /** @private */
54
- const __refreshTokenHosted = async () => {
127
+ const __refreshTokenHosted = async ({
128
+ tenantId,
129
+ isSsoEnforcementRetry = false
130
+ } = {}) => {
55
131
  const user = store.auth.user;
56
132
  if (!(user != null && user.refreshToken)) {
57
133
  contextHolder.setAccessToken(null);
@@ -63,7 +139,7 @@ var _default = (store, api, sharedActions) => {
63
139
  return;
64
140
  }
65
141
  try {
66
- const tabTenantId = (0, _restApi.getTabTenantFromSessionStorage)(store.root.appName);
142
+ const tabTenantId = tenantId != null ? tenantId : (0, _restApi.getTabTenantFromSessionStorage)(store.root.appName);
67
143
  const body = (0, _extends2.default)({
68
144
  grant_type: 'refresh_token',
69
145
  refresh_token: user == null ? void 0 : user.refreshToken
@@ -78,6 +154,13 @@ var _default = (store, api, sharedActions) => {
78
154
  isAuthenticated: true
79
155
  });
80
156
  } catch (e) {
157
+ const retry = tenantId => __refreshTokenHosted({
158
+ tenantId,
159
+ isSsoEnforcementRetry: true
160
+ });
161
+ if (await __handleSsoEnforcementRefreshError(e, isSsoEnforcementRetry, retry)) {
162
+ return;
163
+ }
81
164
  contextHolder.setAccessToken(null);
82
165
  contextHolder.setUser(null);
83
166
  actions.setAuthState({
@@ -139,6 +222,10 @@ var _default = (store, api, sharedActions) => {
139
222
  });
140
223
  }
141
224
  } catch (e) {
225
+ // a refused identity refresh has already used up the refresh token: no retry, only the SSO redirect
226
+ if (await __redirectToSsoIfRequired(e)) {
227
+ return;
228
+ }
142
229
  contextHolder.setAccessToken(null);
143
230
  contextHolder.setUser(null);
144
231
  actions.setAuthState({
@@ -149,7 +236,9 @@ var _default = (store, api, sharedActions) => {
149
236
  };
150
237
 
151
238
  /** @private */
152
- const __refreshTokenWithNativeTokens = async () => {
239
+ const __refreshTokenWithNativeTokens = async ({
240
+ isSsoEnforcementRetry = false
241
+ } = {}) => {
153
242
  try {
154
243
  const authApi = api.auth;
155
244
  const response = await (0, _helpers.refreshTokenWithRetry)(async () => {
@@ -163,6 +252,13 @@ var _default = (store, api, sharedActions) => {
163
252
  isAuthenticated: true
164
253
  });
165
254
  } catch (e) {
255
+ // native tokens can't name a tenant: the retry relies on the tab tenant only
256
+ const retry = () => __refreshTokenWithNativeTokens({
257
+ isSsoEnforcementRetry: true
258
+ });
259
+ if (await __handleSsoEnforcementRefreshError(e, isSsoEnforcementRetry, retry)) {
260
+ return;
261
+ }
166
262
  contextHolder.setAccessToken(null);
167
263
  contextHolder.setUser(null);
168
264
  actions.setAuthState({
@@ -196,9 +292,9 @@ var _default = (store, api, sharedActions) => {
196
292
  * @private
197
293
  */
198
294
  const __handleUnnecessaryEntitlementsUpdate = async updatedUser => {
199
- var _store$auth$user;
295
+ var _store$auth$user2;
200
296
  // @ts-ignore TODO: fix entitlements state type
201
- const oldEntitlements = (_store$auth$user = store.auth.user) == null ? void 0 : _store$auth$user.entitlements;
297
+ const oldEntitlements = (_store$auth$user2 = store.auth.user) == null ? void 0 : _store$auth$user2.entitlements;
202
298
  if ((0, _toolkit.isEntitlementsDeeplyEqual)(oldEntitlements, updatedUser == null ? void 0 : updatedUser.entitlements)) {
203
299
  // set the previous entitlements object
204
300
  return (0, _extends2.default)({}, updatedUser, {
@@ -366,7 +462,7 @@ var _default = (store, api, sharedActions) => {
366
462
  callback,
367
463
  error
368
464
  } = _ref3,
369
- body = (0, _objectWithoutPropertiesLoose2.default)(_ref3, _excluded);
465
+ body = (0, _objectWithoutPropertiesLoose2.default)(_ref3, _excluded2);
370
466
  const publicPolicy = store.auth.securityPolicyState.publicPolicy.policy;
371
467
  if (!(publicPolicy != null && publicPolicy.authStrategy)) {
372
468
  actions.setLoginState({
@@ -675,6 +771,9 @@ var _default = (store, api, sharedActions) => {
675
771
  } catch (e) {
676
772
  contextHolder.setAccessToken(null);
677
773
  contextHolder.setUser(null);
774
+ if (await __redirectToSsoIfRequired(e)) {
775
+ return;
776
+ }
678
777
  callback == null ? void 0 : callback(false, e);
679
778
  setLoginState({
680
779
  email,
@@ -759,7 +858,7 @@ var _default = (store, api, sharedActions) => {
759
858
  const {
760
859
  callback
761
860
  } = _payload,
762
- payload = (0, _objectWithoutPropertiesLoose2.default)(_payload, _excluded2);
861
+ payload = (0, _objectWithoutPropertiesLoose2.default)(_payload, _excluded3);
763
862
  try {
764
863
  setLoginState({
765
864
  loading: true
@@ -789,7 +888,7 @@ var _default = (store, api, sharedActions) => {
789
888
  const {
790
889
  callback
791
890
  } = _payload,
792
- payload = (0, _objectWithoutPropertiesLoose2.default)(_payload, _excluded3);
891
+ payload = (0, _objectWithoutPropertiesLoose2.default)(_payload, _excluded4);
793
892
  try {
794
893
  setLoginState({
795
894
  loading: true
@@ -814,7 +913,7 @@ var _default = (store, api, sharedActions) => {
814
913
  const {
815
914
  callback
816
915
  } = _payload,
817
- payload = (0, _objectWithoutPropertiesLoose2.default)(_payload, _excluded4);
916
+ payload = (0, _objectWithoutPropertiesLoose2.default)(_payload, _excluded5);
818
917
  try {
819
918
  setLoginState({
820
919
  loading: true
@@ -835,7 +934,7 @@ var _default = (store, api, sharedActions) => {
835
934
  const {
836
935
  callback
837
936
  } = _payload,
838
- payload = (0, _objectWithoutPropertiesLoose2.default)(_payload, _excluded5);
937
+ payload = (0, _objectWithoutPropertiesLoose2.default)(_payload, _excluded6);
839
938
  try {
840
939
  var _username;
841
940
  setLoginState({
@@ -879,7 +978,7 @@ var _default = (store, api, sharedActions) => {
879
978
  callback,
880
979
  events
881
980
  } = _payload,
882
- payload = (0, _objectWithoutPropertiesLoose2.default)(_payload, _excluded6);
981
+ payload = (0, _objectWithoutPropertiesLoose2.default)(_payload, _excluded7);
883
982
  try {
884
983
  setLoginState({
885
984
  loading: true
@@ -970,6 +1069,9 @@ var _default = (store, api, sharedActions) => {
970
1069
  callback == null ? void 0 : callback(true);
971
1070
  }
972
1071
  } catch (e) {
1072
+ if (await __redirectToSsoIfRequired(e)) {
1073
+ return;
1074
+ }
973
1075
  setLoginState({
974
1076
  error: (0, _helpers.errorHandler)(e, 'Failed to authenticate')
975
1077
  });
@@ -1007,7 +1109,7 @@ var _default = (store, api, sharedActions) => {
1007
1109
  const {
1008
1110
  callback
1009
1111
  } = payload,
1010
- body = (0, _objectWithoutPropertiesLoose2.default)(payload, _excluded7);
1112
+ body = (0, _objectWithoutPropertiesLoose2.default)(payload, _excluded8);
1011
1113
  try {
1012
1114
  var _options$allowCredent;
1013
1115
  actions.setPasskeysState({
@@ -1047,7 +1149,7 @@ var _default = (store, api, sharedActions) => {
1047
1149
  const {
1048
1150
  callback
1049
1151
  } = payload,
1050
- body = (0, _objectWithoutPropertiesLoose2.default)(payload, _excluded8);
1152
+ body = (0, _objectWithoutPropertiesLoose2.default)(payload, _excluded9);
1051
1153
  try {
1052
1154
  var _publicKey$response$u;
1053
1155
  actions.setPasskeysState({
@@ -1067,7 +1169,7 @@ var _default = (store, api, sharedActions) => {
1067
1169
  {
1068
1170
  user
1069
1171
  } = _await$api$auth$webAu,
1070
- rest = (0, _objectWithoutPropertiesLoose2.default)(_await$api$auth$webAu, _excluded9);
1172
+ rest = (0, _objectWithoutPropertiesLoose2.default)(_await$api$auth$webAu, _excluded10);
1071
1173
  if ((0, _helpers3.isMfaRequired)(user, store.root.appName)) {
1072
1174
  const {
1073
1175
  routes,
@@ -1106,6 +1208,9 @@ var _default = (store, api, sharedActions) => {
1106
1208
  }
1107
1209
  // callback?.(true);
1108
1210
  } catch (e) {
1211
+ if (await __redirectToSsoIfRequired(e)) {
1212
+ return;
1213
+ }
1109
1214
  setLoginState({
1110
1215
  error: (0, _helpers.errorHandler)(e)
1111
1216
  });
@@ -1151,7 +1256,7 @@ var _default = (store, api, sharedActions) => {
1151
1256
  const {
1152
1257
  callback
1153
1258
  } = payload,
1154
- body = (0, _objectWithoutPropertiesLoose2.default)(payload, _excluded10);
1259
+ body = (0, _objectWithoutPropertiesLoose2.default)(payload, _excluded11);
1155
1260
  try {
1156
1261
  setLoginState({
1157
1262
  loading: true
@@ -1216,7 +1321,7 @@ var _default = (store, api, sharedActions) => {
1216
1321
  const {
1217
1322
  callback
1218
1323
  } = _payload,
1219
- payload = (0, _objectWithoutPropertiesLoose2.default)(_payload, _excluded11);
1324
+ payload = (0, _objectWithoutPropertiesLoose2.default)(_payload, _excluded12);
1220
1325
  setLoginState({
1221
1326
  loading: true
1222
1327
  });
@@ -1251,7 +1356,7 @@ var _default = (store, api, sharedActions) => {
1251
1356
  const {
1252
1357
  callback
1253
1358
  } = _payload,
1254
- payload = (0, _objectWithoutPropertiesLoose2.default)(_payload, _excluded12);
1359
+ payload = (0, _objectWithoutPropertiesLoose2.default)(_payload, _excluded13);
1255
1360
  setLoginState({
1256
1361
  loading: true
1257
1362
  });
@@ -1277,7 +1382,7 @@ var _default = (store, api, sharedActions) => {
1277
1382
  const {
1278
1383
  callback
1279
1384
  } = _payload,
1280
- payload = (0, _objectWithoutPropertiesLoose2.default)(_payload, _excluded13);
1385
+ payload = (0, _objectWithoutPropertiesLoose2.default)(_payload, _excluded14);
1281
1386
  setLoginState({
1282
1387
  loading: true
1283
1388
  });
@@ -1300,7 +1405,7 @@ var _default = (store, api, sharedActions) => {
1300
1405
  const {
1301
1406
  callback
1302
1407
  } = _payload,
1303
- payload = (0, _objectWithoutPropertiesLoose2.default)(_payload, _excluded14);
1408
+ payload = (0, _objectWithoutPropertiesLoose2.default)(_payload, _excluded15);
1304
1409
  setLoginState({
1305
1410
  loading: true
1306
1411
  });
@@ -1327,7 +1432,7 @@ var _default = (store, api, sharedActions) => {
1327
1432
  const {
1328
1433
  callback
1329
1434
  } = _payload,
1330
- payload = (0, _objectWithoutPropertiesLoose2.default)(_payload, _excluded15);
1435
+ payload = (0, _objectWithoutPropertiesLoose2.default)(_payload, _excluded16);
1331
1436
  setLoginState({
1332
1437
  loading: true
1333
1438
  });
@@ -1395,7 +1500,10 @@ var _default = (store, api, sharedActions) => {
1395
1500
  };
1396
1501
 
1397
1502
  /** @private */
1398
- const __refreshTokenHostedSocialLogins = async () => {
1503
+ const __refreshTokenHostedSocialLogins = async ({
1504
+ tenantId,
1505
+ isSsoEnforcementRetry = false
1506
+ } = {}) => {
1399
1507
  const {
1400
1508
  user: currentUser
1401
1509
  } = store.auth;
@@ -1409,7 +1517,7 @@ var _default = (store, api, sharedActions) => {
1409
1517
  return;
1410
1518
  }
1411
1519
  try {
1412
- const tabTenantId = (0, _restApi.getTabTenantFromSessionStorage)(store.root.appName);
1520
+ const tabTenantId = tenantId != null ? tenantId : (0, _restApi.getTabTenantFromSessionStorage)(store.root.appName);
1413
1521
  const body = (0, _extends2.default)({
1414
1522
  grant_type: 'refresh_token',
1415
1523
  refresh_token: currentUser == null ? void 0 : currentUser.refreshToken
@@ -1424,6 +1532,13 @@ var _default = (store, api, sharedActions) => {
1424
1532
  isAuthenticated: true
1425
1533
  });
1426
1534
  } catch (e) {
1535
+ const retry = tenantId => __refreshTokenHostedSocialLogins({
1536
+ tenantId,
1537
+ isSsoEnforcementRetry: true
1538
+ });
1539
+ if (await __handleSsoEnforcementRefreshError(e, isSsoEnforcementRetry, retry)) {
1540
+ return;
1541
+ }
1427
1542
  contextHolder.setAccessToken(null);
1428
1543
  contextHolder.setUser(null);
1429
1544
  actions.setAuthState({
@@ -1494,6 +1609,9 @@ var _default = (store, api, sharedActions) => {
1494
1609
  }
1495
1610
  }
1496
1611
  } catch (e) {
1612
+ if (await __redirectToSsoIfRequired(e)) {
1613
+ return;
1614
+ }
1497
1615
  contextHolder.setAccessToken(null);
1498
1616
  contextHolder.setUser(null);
1499
1617
  actions.setAuthState({
@@ -1540,7 +1658,9 @@ var _default = (store, api, sharedActions) => {
1540
1658
  resetBreachedPassword,
1541
1659
  refreshTokenForSocialLogins,
1542
1660
  // protected
1543
- __refreshToken
1661
+ __refreshToken,
1662
+ __redirectToSsoForTenant,
1663
+ __redirectToSsoIfRequired
1544
1664
  }, (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));
1545
1665
  };
1546
1666
  exports.default = _default;
@@ -152,6 +152,9 @@ var _default = (store, api, sharedActions) => {
152
152
  }
153
153
  return await __postLoginMfaAuthenticator(isAuthenticated, user, callback);
154
154
  } catch (e) {
155
+ if (await actions.__redirectToSsoIfRequired(e)) {
156
+ return;
157
+ }
155
158
  setLoadingAction({
156
159
  loading: false,
157
160
  error: (0, _helpers.errorHandler)(e)
@@ -75,6 +75,9 @@ var _default = (store, api, sharedActions) => {
75
75
  });
76
76
  callback == null ? void 0 : callback(true);
77
77
  } catch (e) {
78
+ if (await actions.__redirectToSsoIfRequired(e)) {
79
+ return;
80
+ }
78
81
  setLoadingAction({
79
82
  loading: false,
80
83
  error: (0, _helpers.errorHandler)(e)
@@ -80,6 +80,9 @@ var _default = (store, api, sharedActions) => {
80
80
  });
81
81
  callback == null ? void 0 : callback(true);
82
82
  } catch (e) {
83
+ if (await actions.__redirectToSsoIfRequired(e)) {
84
+ return;
85
+ }
83
86
  setLoadingAction({
84
87
  loading: false,
85
88
  error: (0, _helpers.errorHandler)(e)
@@ -94,6 +94,9 @@ var _default = (store, api, sharedActions) => {
94
94
  });
95
95
  callback == null ? void 0 : callback(true);
96
96
  } catch (e) {
97
+ if (await actions.__redirectToSsoIfRequired(e)) {
98
+ return;
99
+ }
97
100
  setLoadingAction({
98
101
  loading: false,
99
102
  error: (0, _helpers.errorHandler)(e)
@@ -164,6 +164,9 @@ var _default = (store, api, sharedActions) => {
164
164
  loading: false
165
165
  });
166
166
  } catch (e) {
167
+ if (await actions.__redirectToSsoIfRequired(e)) {
168
+ return;
169
+ }
167
170
  setSocialLoginsState({
168
171
  loading: false,
169
172
  error: (0, _helpers.errorHandler)(e, 'Failed to authenticate'),
@@ -57,6 +57,13 @@ var _default = (store, api, sharedActions) => {
57
57
  }) : actions.setAuthState({
58
58
  isLoading: false
59
59
  });
60
+ if (await actions.__redirectToSsoIfRequired(e)) {
61
+ // the tab comes back from SSO in the target tenant, not the one the failed switch restored
62
+ if (_restApi.ContextHolder.for(store.root.appName).isSessionPerTenantEnabled()) {
63
+ (0, _restApi.setTabTenantInSessionStorage)(tenantId);
64
+ }
65
+ return;
66
+ }
60
67
  callback == null ? void 0 : callback(false, e);
61
68
  }
62
69
  };
@@ -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.130.0
1
+ /** @license Frontegg v7.131.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.130.0",
3
+ "version": "7.131.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.130.0",
10
+ "@frontegg/rest-api": "7.131.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",