@frontegg/redux-store 7.131.0-alpha.2 → 7.131.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.
- package/auth/LoginState/actions/hostedLoginAuthorize.actions.js +3 -3
- package/auth/LoginState/actions/index.d.ts +5 -18
- package/auth/LoginState/actions/index.js +43 -39
- package/auth/LoginState/helpers.d.ts +2 -0
- package/auth/LoginState/helpers.js +3 -0
- package/auth/TenantsState/actions.js +5 -2
- package/auth/helpers.d.ts +1 -1
- package/auth/helpers.js +7 -5
- package/index.js +1 -1
- package/mocks/auth-mocks/loginActions.mocks.d.ts +0 -4
- package/node/auth/LoginState/actions/hostedLoginAuthorize.actions.js +3 -3
- package/node/auth/LoginState/actions/index.js +41 -37
- package/node/auth/LoginState/helpers.js +5 -1
- package/node/auth/TenantsState/actions.js +4 -1
- package/node/auth/helpers.js +7 -5
- package/node/index.js +1 -1
- package/package.json +2 -2
|
@@ -15,7 +15,7 @@ export default ((store, api, sharedActions) => {
|
|
|
15
15
|
const __requestHostedLoginSilentAuthorize = async () => {
|
|
16
16
|
var _response;
|
|
17
17
|
let response;
|
|
18
|
-
restoreTabTenantUnlessSsoCallback(store);
|
|
18
|
+
await restoreTabTenantUnlessSsoCallback(store);
|
|
19
19
|
if (FronteggNativeModule.isGetTokensAvailable()) {
|
|
20
20
|
const authApi = api.auth;
|
|
21
21
|
response = await refreshTokenWithRetry(async () => {
|
|
@@ -27,7 +27,7 @@ export default ((store, api, sharedActions) => {
|
|
|
27
27
|
response = await api.auth.silentOAuthRefreshTokenV2();
|
|
28
28
|
} catch (error) {
|
|
29
29
|
var _store$auth$user, _error$errorContext;
|
|
30
|
-
// on the hosted login callback the page finishes the login with its code: fail as
|
|
30
|
+
// on the hosted login callback the page finishes the login with its code: fail as without SSO enforcement
|
|
31
31
|
if (isOnLoginCodeCallbackRoute(store)) {
|
|
32
32
|
throw error;
|
|
33
33
|
}
|
|
@@ -36,7 +36,7 @@ export default ((store, api, sharedActions) => {
|
|
|
36
36
|
return;
|
|
37
37
|
}
|
|
38
38
|
const tenantId = (_store$auth$user = store.auth.user) == null ? void 0 : _store$auth$user.tenantId;
|
|
39
|
-
if (!
|
|
39
|
+
if (!isSsoEnforcementError(error) || ((_error$errorContext = error.errorContext) == null ? void 0 : _error$errorContext.ssoRequired) !== false || !tenantId || !(await isEnforceSsoOnTenantAccessEnabled(store.root.appName))) {
|
|
40
40
|
throw error;
|
|
41
41
|
}
|
|
42
42
|
// one retry for the tenant of the current token (a cold reload has no user yet: no retry)
|
|
@@ -33,20 +33,11 @@ declare const _default: (store: FronteggState, api: RestApi, sharedActions: Shar
|
|
|
33
33
|
requestHostedLoginAuthorize: (additionalParams?: Record<string, string>) => Promise<void>;
|
|
34
34
|
requestHostedLoginAuthorizeV2: (payload?: {
|
|
35
35
|
additionalParams?: Record<string, string>;
|
|
36
|
-
shouldRedirectToLogin
|
|
37
|
-
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* @returns true when handled, false when the caller should log out
|
|
42
|
-
* @private
|
|
43
|
-
*/
|
|
44
|
-
? /**
|
|
45
|
-
* Handles a hosted (OAuth) token refresh refused because the tenant requires SSO.
|
|
46
|
-
* ssoRequired: true sends the user to SSO, like login. ssoRequired: false renews once more for the tenant of the
|
|
47
|
-
* current token, which keeps the session: OAuth doesn't use up the refresh token on this refusal.
|
|
48
|
-
* @returns true when handled, false when the caller should log out
|
|
49
|
-
* @private
|
|
36
|
+
shouldRedirectToLogin? /**
|
|
37
|
+
* Sends the user to the tenant's SSO when a login or tenant switch is refused because that tenant requires it
|
|
38
|
+
* (e.g. an invitation into a tenant whose SSO covers the user's email). The SSO login lands in that tenant.
|
|
39
|
+
* @returns true when redirected, so the caller doesn't show the error
|
|
40
|
+
* @protected
|
|
50
41
|
*/: boolean;
|
|
51
42
|
firstTime?: boolean;
|
|
52
43
|
loginDirectAction?: import("../interfaces").LoginDirectAction;
|
|
@@ -91,10 +82,6 @@ declare const _default: (store: FronteggState, api: RestApi, sharedActions: Shar
|
|
|
91
82
|
resetBreachedPassword: (payload: IForgotPassword) => Promise<void>;
|
|
92
83
|
refreshTokenForSocialLogins: () => Promise<void>;
|
|
93
84
|
__refreshToken: () => Promise<void>;
|
|
94
|
-
__redirectToSsoForTenant: ({ tenantId, address }: {
|
|
95
|
-
tenantId: string;
|
|
96
|
-
address: string;
|
|
97
|
-
}) => Promise<void>;
|
|
98
85
|
__redirectToSsoIfRequired: (e: unknown) => Promise<boolean>;
|
|
99
86
|
};
|
|
100
87
|
export default _default;
|
|
@@ -25,8 +25,8 @@ import mfaWithWebAuthnActions from './mfaWithWebAuthn.actions';
|
|
|
25
25
|
import { LoginFlow, LoginStep } from '../interfaces';
|
|
26
26
|
import { base64urlDecode, deepResetState, delay, errorHandler, errorTraceId, GTMEventAction, isSsoEnforcementError, publicKeyCredentialToJSON, refreshTokenWithRetry, reportGTMEvent, retryIfNeeded, withRetryConfig } from '../../../helpers';
|
|
27
27
|
import { initialState } from '../state';
|
|
28
|
-
import { getSearchParam, isEmailPayload, isUsernamePayload, shouldShowPasswordRotationPromptFunc, TENANT_ID_PARAM_KEY } from '../helpers';
|
|
29
|
-
import { AuthStrategyEnum, ContextHolder, isEnforceSsoOnTenantAccessEnabled, FeatureFlags, getTabTenantFromSessionStorage, removeTabTenantFromSessionStorage, setTabTenantInSessionStorage, WebAuthnDeviceType, PreLoginAddressType } from '@frontegg/rest-api';
|
|
28
|
+
import { getSearchParam, isEmailPayload, isUsernamePayload, shouldShowPasswordRotationPromptFunc, TENANT_ID_PARAM_KEY, withOidcRedirectUri } from '../helpers';
|
|
29
|
+
import { AuthStrategyEnum, ContextHolder, ENFORCE_SSO_ON_TENANT_ACCESS_FF, isEnforceSsoOnTenantAccessEnabled, FeatureFlags, clearTabTenantBeforeSsoSwitch, 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';
|
|
@@ -48,8 +48,10 @@ export default ((store, api, sharedActions) => {
|
|
|
48
48
|
* @protected
|
|
49
49
|
*/
|
|
50
50
|
// as preLogin (takeLeading): an SSO redirect started while another is in flight is dropped; the first one leaves the
|
|
51
|
-
// page, and a second hosted one would replace the PKCE verifier the first one's callback needs
|
|
51
|
+
// page, and a second hosted one would replace the PKCE verifier the first one's callback needs. Kept set after a
|
|
52
|
+
// redirect: the page is still there until the navigation commits.
|
|
52
53
|
let ssoRedirectInFlight = false;
|
|
54
|
+
let ssoPageShowListenerRegistered = false;
|
|
53
55
|
const __redirectToSsoForTenant = async ({
|
|
54
56
|
tenantId,
|
|
55
57
|
address
|
|
@@ -58,13 +60,25 @@ export default ((store, api, sharedActions) => {
|
|
|
58
60
|
return;
|
|
59
61
|
}
|
|
60
62
|
ssoRedirectInFlight = true;
|
|
63
|
+
// Only an enabled SSO redirect needs bfcache recovery. Do not attach listeners to ordinary or preview stores.
|
|
64
|
+
if (!ssoPageShowListenerRegistered && typeof window !== 'undefined' && !store.root.previewMode) {
|
|
65
|
+
window.addEventListener('pageshow', event => {
|
|
66
|
+
const [enforceSsoOnTenantAccess] = FeatureFlags.getFeatureFlags([ENFORCE_SSO_ON_TENANT_ACCESS_FF], store.root.appName);
|
|
67
|
+
if (event.persisted && enforceSsoOnTenantAccess) {
|
|
68
|
+
ssoRedirectInFlight = false;
|
|
69
|
+
void restoreTabTenantUnlessSsoCallback(store);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
ssoPageShowListenerRegistered = true;
|
|
73
|
+
}
|
|
61
74
|
try {
|
|
62
75
|
await __redirectToSsoForTenantNow({
|
|
63
76
|
tenantId,
|
|
64
77
|
address
|
|
65
78
|
});
|
|
66
|
-
}
|
|
79
|
+
} catch (e) {
|
|
67
80
|
ssoRedirectInFlight = false;
|
|
81
|
+
throw e;
|
|
68
82
|
}
|
|
69
83
|
};
|
|
70
84
|
const __redirectToSsoForTenantNow = async ({
|
|
@@ -98,12 +112,9 @@ export default ((store, api, sharedActions) => {
|
|
|
98
112
|
});
|
|
99
113
|
return;
|
|
100
114
|
}
|
|
101
|
-
// as login does for an OIDC SSO page
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
ssoRedirectUrl += `&redirect_uri=${window.location.origin}${store.auth.routes.oidcRedirectUrl}`;
|
|
105
|
-
}
|
|
106
|
-
redirectToFullPage(ssoRedirectUrl);
|
|
115
|
+
// as login does for an OIDC SSO page; the refusal has no idpType, but an OIDC page asks for a code
|
|
116
|
+
const isOidcPage = /[?&]response_type=code(&|$)/.test(address);
|
|
117
|
+
redirectToFullPage(isOidcPage ? withOidcRedirectUri(address, store.auth.routes.oidcRedirectUrl) : address);
|
|
107
118
|
};
|
|
108
119
|
|
|
109
120
|
/**
|
|
@@ -114,7 +125,8 @@ export default ((store, api, sharedActions) => {
|
|
|
114
125
|
*/
|
|
115
126
|
const __redirectToSsoIfRequired = async e => {
|
|
116
127
|
var _e$errorContext;
|
|
117
|
-
|
|
128
|
+
// Ordinary failures keep their existing handling even while the flags request is pending.
|
|
129
|
+
if (!isSsoEnforcementError(e) || !((_e$errorContext = e.errorContext) != null && _e$errorContext.ssoRequired) || !(await isEnforceSsoOnTenantAccessEnabled(store.root.appName))) {
|
|
118
130
|
return false;
|
|
119
131
|
}
|
|
120
132
|
await __redirectToSsoForTenant(e.errorContext);
|
|
@@ -128,14 +140,19 @@ export default ((store, api, sharedActions) => {
|
|
|
128
140
|
* @private
|
|
129
141
|
*/
|
|
130
142
|
const __handleSsoEnforcementRefreshError = async (e, isSsoEnforcementRetry, refreshForTenant) => {
|
|
131
|
-
var _store$auth$user;
|
|
132
|
-
// on a login callback the page finishes the login with its code: log out as
|
|
133
|
-
|
|
143
|
+
var _e$errorContext2, _store$auth$user;
|
|
144
|
+
// on a login callback the page finishes the login with its code: log out as without SSO enforcement, no new SSO
|
|
145
|
+
// trip
|
|
146
|
+
if (!isSsoEnforcementError(e) || isOnLoginCodeCallbackRoute(store) || !(await isEnforceSsoOnTenantAccessEnabled(store.root.appName))) {
|
|
134
147
|
return false;
|
|
135
148
|
}
|
|
136
149
|
if (await __redirectToSsoIfRequired(e)) {
|
|
137
150
|
return true;
|
|
138
151
|
}
|
|
152
|
+
// Only an explicit refusal without SSO permits recovery; missing context follows ordinary logout handling.
|
|
153
|
+
if (((_e$errorContext2 = e.errorContext) == null ? void 0 : _e$errorContext2.ssoRequired) !== false) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
139
156
|
const currentTenantId = (_store$auth$user = store.auth.user) == null ? void 0 : _store$auth$user.tenantId;
|
|
140
157
|
if (isSsoEnforcementRetry || !currentTenantId) {
|
|
141
158
|
return false;
|
|
@@ -246,9 +263,10 @@ export default ((store, api, sharedActions) => {
|
|
|
246
263
|
});
|
|
247
264
|
}
|
|
248
265
|
} catch (e) {
|
|
249
|
-
//
|
|
250
|
-
// a login callback, where the page itself completes the
|
|
251
|
-
|
|
266
|
+
// no retry: identity already refreshes for the session's own tenant when it can, so a refusal without SSO means
|
|
267
|
+
// there is none to keep. Only the SSO redirect, except on a login callback, where the page itself completes the
|
|
268
|
+
// login
|
|
269
|
+
if (!isOnLoginCodeCallbackRoute(store) && (await __redirectToSsoIfRequired(e))) {
|
|
252
270
|
return;
|
|
253
271
|
}
|
|
254
272
|
contextHolder.setAccessToken(null);
|
|
@@ -261,9 +279,7 @@ export default ((store, api, sharedActions) => {
|
|
|
261
279
|
};
|
|
262
280
|
|
|
263
281
|
/** @private */
|
|
264
|
-
const __refreshTokenWithNativeTokens = async ({
|
|
265
|
-
isSsoEnforcementRetry = false
|
|
266
|
-
} = {}) => {
|
|
282
|
+
const __refreshTokenWithNativeTokens = async () => {
|
|
267
283
|
try {
|
|
268
284
|
const authApi = api.auth;
|
|
269
285
|
const response = await refreshTokenWithRetry(async () => {
|
|
@@ -277,13 +293,6 @@ export default ((store, api, sharedActions) => {
|
|
|
277
293
|
isAuthenticated: true
|
|
278
294
|
});
|
|
279
295
|
} catch (e) {
|
|
280
|
-
// native tokens can't name a tenant: the retry relies on the tab tenant only
|
|
281
|
-
const retry = () => __refreshTokenWithNativeTokens({
|
|
282
|
-
isSsoEnforcementRetry: true
|
|
283
|
-
});
|
|
284
|
-
if (await __handleSsoEnforcementRefreshError(e, isSsoEnforcementRetry, retry)) {
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
296
|
contextHolder.setAccessToken(null);
|
|
288
297
|
contextHolder.setUser(null);
|
|
289
298
|
actions.setAuthState({
|
|
@@ -295,7 +304,7 @@ export default ((store, api, sharedActions) => {
|
|
|
295
304
|
|
|
296
305
|
/** @protected */
|
|
297
306
|
const __refreshToken = async () => {
|
|
298
|
-
restoreTabTenantUnlessSsoCallback(store);
|
|
307
|
+
await restoreTabTenantUnlessSsoCallback(store);
|
|
299
308
|
if (FronteggNativeModule.isGetTokensAvailable()) {
|
|
300
309
|
await __refreshTokenWithNativeTokens();
|
|
301
310
|
return;
|
|
@@ -583,13 +592,7 @@ export default ((store, api, sharedActions) => {
|
|
|
583
592
|
redirectToFullPage(address);
|
|
584
593
|
return;
|
|
585
594
|
}
|
|
586
|
-
|
|
587
|
-
if (idpType === SamlVendors.Oidc && !ssoRedirectUrl.includes('redirect_uri')) {
|
|
588
|
-
const {
|
|
589
|
-
oidcRedirectUrl
|
|
590
|
-
} = store.auth.routes;
|
|
591
|
-
ssoRedirectUrl += `&redirect_uri=${window.location.origin}${oidcRedirectUrl}`;
|
|
592
|
-
}
|
|
595
|
+
const ssoRedirectUrl = idpType === SamlVendors.Oidc ? withOidcRedirectUri(address, store.auth.routes.oidcRedirectUrl) : address;
|
|
593
596
|
if (FronteggNativeModule.isLoginWithSSOAvailable()) {
|
|
594
597
|
var _ref4;
|
|
595
598
|
FronteggNativeModule.loginWithSSO((_ref4 = email != null ? email : username) != null ? _ref4 : '');
|
|
@@ -808,11 +811,13 @@ export default ((store, api, sharedActions) => {
|
|
|
808
811
|
});
|
|
809
812
|
}
|
|
810
813
|
};
|
|
811
|
-
const clearLogoutLocalState = ({
|
|
814
|
+
const clearLogoutLocalState = async ({
|
|
812
815
|
keepHostedOAuthState = false
|
|
813
816
|
} = {}) => {
|
|
814
817
|
if (contextHolder.isSessionPerTenantEnabled()) {
|
|
815
818
|
removeTabTenantFromSessionStorage();
|
|
819
|
+
// a pending SSO switch would put a tenant of the logged-out session back on the next page load
|
|
820
|
+
await clearTabTenantBeforeSsoSwitch(store.root.appName);
|
|
816
821
|
}
|
|
817
822
|
// Step-up re-login goes through silentLogout but still needs the hosted OAuth state to finish the client flow.
|
|
818
823
|
if (!keepHostedOAuthState) {
|
|
@@ -833,7 +838,7 @@ export default ((store, api, sharedActions) => {
|
|
|
833
838
|
} catch {
|
|
834
839
|
/* empty */
|
|
835
840
|
}
|
|
836
|
-
clearLogoutLocalState();
|
|
841
|
+
await clearLogoutLocalState();
|
|
837
842
|
actions.resetAuthState();
|
|
838
843
|
await actions.requestAuthorize(true);
|
|
839
844
|
payload == null ? void 0 : payload();
|
|
@@ -846,7 +851,7 @@ export default ((store, api, sharedActions) => {
|
|
|
846
851
|
/* empty */
|
|
847
852
|
}
|
|
848
853
|
const isStepUpInProgress = !!((_window2 = window) != null && _window2.localStorage.getItem(SHOULD_STEP_UP_KEY));
|
|
849
|
-
clearLogoutLocalState({
|
|
854
|
+
await clearLogoutLocalState({
|
|
850
855
|
keepHostedOAuthState: isStepUpInProgress
|
|
851
856
|
});
|
|
852
857
|
setTimeout(() => {
|
|
@@ -1694,7 +1699,6 @@ export default ((store, api, sharedActions) => {
|
|
|
1694
1699
|
refreshTokenForSocialLogins,
|
|
1695
1700
|
// protected
|
|
1696
1701
|
__refreshToken,
|
|
1697
|
-
__redirectToSsoForTenant,
|
|
1698
1702
|
__redirectToSsoIfRequired
|
|
1699
1703
|
}, 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));
|
|
1700
1704
|
});
|
|
@@ -18,6 +18,8 @@ export declare const getPathAndSearchParamsFromUrl: (url: string, preventRedirec
|
|
|
18
18
|
*/
|
|
19
19
|
export declare const getSearchParam: (key: string) => string | undefined;
|
|
20
20
|
export declare const TENANT_ID_PARAM_KEY = "tenantId";
|
|
21
|
+
/** An OIDC SSO page without redirect_uri comes back to the OIDC callback, which completes the login with its code. */
|
|
22
|
+
export declare const withOidcRedirectUri: (address: string, oidcRedirectUrl?: string) => string;
|
|
21
23
|
export declare const getNumberOfMfaDevices: (mfaDevices: UserMFADevicesResponse) => {
|
|
22
24
|
numberOfWebAuthnDevices: number;
|
|
23
25
|
numberOfPhoneDevices: number;
|
|
@@ -75,6 +75,9 @@ export const getSearchParam = key => {
|
|
|
75
75
|
return param != null ? param : undefined;
|
|
76
76
|
};
|
|
77
77
|
export const TENANT_ID_PARAM_KEY = 'tenantId';
|
|
78
|
+
|
|
79
|
+
/** An OIDC SSO page without redirect_uri comes back to the OIDC callback, which completes the login with its code. */
|
|
80
|
+
export const withOidcRedirectUri = (address, oidcRedirectUrl) => address.includes('redirect_uri') ? address : `${address}&redirect_uri=${window.location.origin}${oidcRedirectUrl}`;
|
|
78
81
|
export const getNumberOfMfaDevices = mfaDevices => {
|
|
79
82
|
// Count platform / cross-platform separately — matches the MFA picker options.
|
|
80
83
|
const numberOfWebAuthnDevices = Number(mfaDevices.webauthn.some(device => device.deviceType === WebAuthnDeviceType.Platform)) + Number(mfaDevices.webauthn.some(device => device.deviceType === WebAuthnDeviceType.CrossPlatform));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { initialState } from './state';
|
|
2
2
|
import { deepResetState } from '../../helpers';
|
|
3
|
-
import { ContextHolder, getCurrentUserTenantsFunction, setTabTenantForSsoSwitch } from '@frontegg/rest-api';
|
|
3
|
+
import { ContextHolder, getCurrentUserTenantsFunction, getTabTenantFromSessionStorage, setTabTenantForSsoSwitch } from '@frontegg/rest-api';
|
|
4
4
|
export default ((store, api, sharedActions) => {
|
|
5
5
|
const actions = sharedActions;
|
|
6
6
|
const setTenantsState = state => {
|
|
@@ -46,6 +46,9 @@ export default ((store, api, sharedActions) => {
|
|
|
46
46
|
});
|
|
47
47
|
}
|
|
48
48
|
} catch (e) {
|
|
49
|
+
// the tab tenant the switch was refused from, read before any await: a parallel switch that passes moves the tab
|
|
50
|
+
// while this one goes to SSO
|
|
51
|
+
const tabTenantId = getTabTenantFromSessionStorage(store.root.appName);
|
|
49
52
|
silentReload ? actions.setTenantsState({
|
|
50
53
|
switchingTenant: false
|
|
51
54
|
}) : actions.setAuthState({
|
|
@@ -54,7 +57,7 @@ export default ((store, api, sharedActions) => {
|
|
|
54
57
|
if (await actions.__redirectToSsoIfRequired(e)) {
|
|
55
58
|
// the tab comes back from SSO in the target tenant, not the one the failed switch restored
|
|
56
59
|
if (ContextHolder.for(store.root.appName).isSessionPerTenantEnabled()) {
|
|
57
|
-
setTabTenantForSsoSwitch(tenantId);
|
|
60
|
+
setTabTenantForSsoSwitch(tenantId, tabTenantId);
|
|
58
61
|
}
|
|
59
62
|
return;
|
|
60
63
|
}
|
package/auth/helpers.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ export declare const isOnLoginCodeCallbackRoute: (store: FronteggState) => boole
|
|
|
18
18
|
* After a switch refused for SSO the tab points at the SSO tenant. Its SSO login comes back on a callback route and
|
|
19
19
|
* keeps it; any other page load means the user didn't finish that login, so the tab goes back to its tenant.
|
|
20
20
|
*/
|
|
21
|
-
export declare const restoreTabTenantUnlessSsoCallback: (store: FronteggState) => void
|
|
21
|
+
export declare const restoreTabTenantUnlessSsoCallback: (store: FronteggState) => Promise<void>;
|
|
22
22
|
export declare const prepareIdentifiersForSignup: (payload: ISignUpUser) => ISignUpUser;
|
|
23
23
|
/**
|
|
24
24
|
* Whether the current route is an auth page (login, tenant chooser, social / magic link / SSO callback), not one to come
|
package/auth/helpers.js
CHANGED
|
@@ -85,10 +85,8 @@ export const isOnLoginCodeCallbackRoute = store => {
|
|
|
85
85
|
* After a switch refused for SSO the tab points at the SSO tenant. Its SSO login comes back on a callback route and
|
|
86
86
|
* keeps it; any other page load means the user didn't finish that login, so the tab goes back to its tenant.
|
|
87
87
|
*/
|
|
88
|
-
export const restoreTabTenantUnlessSsoCallback = store => {
|
|
88
|
+
export const restoreTabTenantUnlessSsoCallback = async store => {
|
|
89
89
|
var _store$root$urlStrate2;
|
|
90
|
-
// Flags load in parallel with the first refresh. Recover an existing marker
|
|
91
|
-
// before choosing that refresh's tenant, even if flags are not available yet.
|
|
92
90
|
if (!ContextHolder.for(store.root.appName).isSessionPerTenantEnabled()) {
|
|
93
91
|
return;
|
|
94
92
|
}
|
|
@@ -99,9 +97,13 @@ export const restoreTabTenantUnlessSsoCallback = store => {
|
|
|
99
97
|
} = _extends({}, defaultFronteggRoutes, store.auth.routes);
|
|
100
98
|
// matched like login matches its callback pages: endsWith, so a basename before the route still counts
|
|
101
99
|
const currentRoutes = getCurrentRoutes((_store$root$urlStrate2 = store.root.urlStrategy) != null ? _store$root$urlStrate2 : 'path');
|
|
102
|
-
|
|
100
|
+
// a refused login comes back on its callback too, not finished: SAML with samlerrors (as SamlLoginSuccess reads them),
|
|
101
|
+
// OIDC and hosted without a code (e.g. the IdP's error=access_denied), which their pages need to finish the login
|
|
102
|
+
const params = new URLSearchParams(window.location.search);
|
|
103
|
+
const callbackRoutes = [...(params.has('samlerrors') ? [] : [samlCallbackUrl]), ...(params.has('code') ? [oidcRedirectUrl, hostedLoginRedirectUrl] : [])];
|
|
104
|
+
const isSsoCallback = callbackRoutes.some(route => !!route && currentRoutes.some(currentRoute => currentRoute.endsWith(route)));
|
|
103
105
|
if (!isSsoCallback) {
|
|
104
|
-
restoreTabTenantBeforeSsoSwitch();
|
|
106
|
+
await restoreTabTenantBeforeSsoSwitch(store.root.appName);
|
|
105
107
|
}
|
|
106
108
|
};
|
|
107
109
|
export const prepareIdentifiersForSignup = payload => {
|
package/index.js
CHANGED
|
@@ -73,10 +73,6 @@ declare const _default: (store: FronteggState, api: RestApi, sharedActions: Shar
|
|
|
73
73
|
resetBreachedPassword: (payload: import("dist/@frontegg/rest-api").IForgotPassword) => Promise<void>;
|
|
74
74
|
refreshTokenForSocialLogins: () => Promise<void>;
|
|
75
75
|
__refreshToken: () => Promise<void>;
|
|
76
|
-
__redirectToSsoForTenant: ({ tenantId, address }: {
|
|
77
|
-
tenantId: string;
|
|
78
|
-
address: string;
|
|
79
|
-
}) => Promise<void>;
|
|
80
76
|
__redirectToSsoIfRequired: (e: unknown) => Promise<boolean>;
|
|
81
77
|
};
|
|
82
78
|
export default _default;
|
|
@@ -22,7 +22,7 @@ var _default = (store, api, sharedActions) => {
|
|
|
22
22
|
const __requestHostedLoginSilentAuthorize = async () => {
|
|
23
23
|
var _response;
|
|
24
24
|
let response;
|
|
25
|
-
(0, _helpers3.restoreTabTenantUnlessSsoCallback)(store);
|
|
25
|
+
await (0, _helpers3.restoreTabTenantUnlessSsoCallback)(store);
|
|
26
26
|
if (_toolkit.FronteggNativeModule.isGetTokensAvailable()) {
|
|
27
27
|
const authApi = api.auth;
|
|
28
28
|
response = await (0, _helpers.refreshTokenWithRetry)(async () => {
|
|
@@ -34,7 +34,7 @@ var _default = (store, api, sharedActions) => {
|
|
|
34
34
|
response = await api.auth.silentOAuthRefreshTokenV2();
|
|
35
35
|
} catch (error) {
|
|
36
36
|
var _store$auth$user, _error$errorContext;
|
|
37
|
-
// on the hosted login callback the page finishes the login with its code: fail as
|
|
37
|
+
// on the hosted login callback the page finishes the login with its code: fail as without SSO enforcement
|
|
38
38
|
if ((0, _helpers3.isOnLoginCodeCallbackRoute)(store)) {
|
|
39
39
|
throw error;
|
|
40
40
|
}
|
|
@@ -43,7 +43,7 @@ var _default = (store, api, sharedActions) => {
|
|
|
43
43
|
return;
|
|
44
44
|
}
|
|
45
45
|
const tenantId = (_store$auth$user = store.auth.user) == null ? void 0 : _store$auth$user.tenantId;
|
|
46
|
-
if (!(0,
|
|
46
|
+
if (!(0, _helpers.isSsoEnforcementError)(error) || ((_error$errorContext = error.errorContext) == null ? void 0 : _error$errorContext.ssoRequired) !== false || !tenantId || !(await (0, _restApi.isEnforceSsoOnTenantAccessEnabled)(store.root.appName))) {
|
|
47
47
|
throw error;
|
|
48
48
|
}
|
|
49
49
|
// one retry for the tenant of the current token (a cold reload has no user yet: no retry)
|
|
@@ -55,8 +55,10 @@ var _default = (store, api, sharedActions) => {
|
|
|
55
55
|
* @protected
|
|
56
56
|
*/
|
|
57
57
|
// as preLogin (takeLeading): an SSO redirect started while another is in flight is dropped; the first one leaves the
|
|
58
|
-
// page, and a second hosted one would replace the PKCE verifier the first one's callback needs
|
|
58
|
+
// page, and a second hosted one would replace the PKCE verifier the first one's callback needs. Kept set after a
|
|
59
|
+
// redirect: the page is still there until the navigation commits.
|
|
59
60
|
let ssoRedirectInFlight = false;
|
|
61
|
+
let ssoPageShowListenerRegistered = false;
|
|
60
62
|
const __redirectToSsoForTenant = async ({
|
|
61
63
|
tenantId,
|
|
62
64
|
address
|
|
@@ -65,13 +67,25 @@ var _default = (store, api, sharedActions) => {
|
|
|
65
67
|
return;
|
|
66
68
|
}
|
|
67
69
|
ssoRedirectInFlight = true;
|
|
70
|
+
// Only an enabled SSO redirect needs bfcache recovery. Do not attach listeners to ordinary or preview stores.
|
|
71
|
+
if (!ssoPageShowListenerRegistered && typeof window !== 'undefined' && !store.root.previewMode) {
|
|
72
|
+
window.addEventListener('pageshow', event => {
|
|
73
|
+
const [enforceSsoOnTenantAccess] = _restApi.FeatureFlags.getFeatureFlags([_restApi.ENFORCE_SSO_ON_TENANT_ACCESS_FF], store.root.appName);
|
|
74
|
+
if (event.persisted && enforceSsoOnTenantAccess) {
|
|
75
|
+
ssoRedirectInFlight = false;
|
|
76
|
+
void (0, _helpers3.restoreTabTenantUnlessSsoCallback)(store);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
ssoPageShowListenerRegistered = true;
|
|
80
|
+
}
|
|
68
81
|
try {
|
|
69
82
|
await __redirectToSsoForTenantNow({
|
|
70
83
|
tenantId,
|
|
71
84
|
address
|
|
72
85
|
});
|
|
73
|
-
}
|
|
86
|
+
} catch (e) {
|
|
74
87
|
ssoRedirectInFlight = false;
|
|
88
|
+
throw e;
|
|
75
89
|
}
|
|
76
90
|
};
|
|
77
91
|
const __redirectToSsoForTenantNow = async ({
|
|
@@ -105,12 +119,9 @@ var _default = (store, api, sharedActions) => {
|
|
|
105
119
|
});
|
|
106
120
|
return;
|
|
107
121
|
}
|
|
108
|
-
// as login does for an OIDC SSO page
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
ssoRedirectUrl += `&redirect_uri=${window.location.origin}${store.auth.routes.oidcRedirectUrl}`;
|
|
112
|
-
}
|
|
113
|
-
redirectToFullPage(ssoRedirectUrl);
|
|
122
|
+
// as login does for an OIDC SSO page; the refusal has no idpType, but an OIDC page asks for a code
|
|
123
|
+
const isOidcPage = /[?&]response_type=code(&|$)/.test(address);
|
|
124
|
+
redirectToFullPage(isOidcPage ? (0, _helpers2.withOidcRedirectUri)(address, store.auth.routes.oidcRedirectUrl) : address);
|
|
114
125
|
};
|
|
115
126
|
|
|
116
127
|
/**
|
|
@@ -121,7 +132,8 @@ var _default = (store, api, sharedActions) => {
|
|
|
121
132
|
*/
|
|
122
133
|
const __redirectToSsoIfRequired = async e => {
|
|
123
134
|
var _e$errorContext;
|
|
124
|
-
|
|
135
|
+
// Ordinary failures keep their existing handling even while the flags request is pending.
|
|
136
|
+
if (!(0, _helpers.isSsoEnforcementError)(e) || !((_e$errorContext = e.errorContext) != null && _e$errorContext.ssoRequired) || !(await (0, _restApi.isEnforceSsoOnTenantAccessEnabled)(store.root.appName))) {
|
|
125
137
|
return false;
|
|
126
138
|
}
|
|
127
139
|
await __redirectToSsoForTenant(e.errorContext);
|
|
@@ -135,14 +147,19 @@ var _default = (store, api, sharedActions) => {
|
|
|
135
147
|
* @private
|
|
136
148
|
*/
|
|
137
149
|
const __handleSsoEnforcementRefreshError = async (e, isSsoEnforcementRetry, refreshForTenant) => {
|
|
138
|
-
var _store$auth$user;
|
|
139
|
-
// on a login callback the page finishes the login with its code: log out as
|
|
140
|
-
|
|
150
|
+
var _e$errorContext2, _store$auth$user;
|
|
151
|
+
// on a login callback the page finishes the login with its code: log out as without SSO enforcement, no new SSO
|
|
152
|
+
// trip
|
|
153
|
+
if (!(0, _helpers.isSsoEnforcementError)(e) || (0, _helpers3.isOnLoginCodeCallbackRoute)(store) || !(await (0, _restApi.isEnforceSsoOnTenantAccessEnabled)(store.root.appName))) {
|
|
141
154
|
return false;
|
|
142
155
|
}
|
|
143
156
|
if (await __redirectToSsoIfRequired(e)) {
|
|
144
157
|
return true;
|
|
145
158
|
}
|
|
159
|
+
// Only an explicit refusal without SSO permits recovery; missing context follows ordinary logout handling.
|
|
160
|
+
if (((_e$errorContext2 = e.errorContext) == null ? void 0 : _e$errorContext2.ssoRequired) !== false) {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
146
163
|
const currentTenantId = (_store$auth$user = store.auth.user) == null ? void 0 : _store$auth$user.tenantId;
|
|
147
164
|
if (isSsoEnforcementRetry || !currentTenantId) {
|
|
148
165
|
return false;
|
|
@@ -253,9 +270,10 @@ var _default = (store, api, sharedActions) => {
|
|
|
253
270
|
});
|
|
254
271
|
}
|
|
255
272
|
} catch (e) {
|
|
256
|
-
//
|
|
257
|
-
// a login callback, where the page itself completes the
|
|
258
|
-
|
|
273
|
+
// no retry: identity already refreshes for the session's own tenant when it can, so a refusal without SSO means
|
|
274
|
+
// there is none to keep. Only the SSO redirect, except on a login callback, where the page itself completes the
|
|
275
|
+
// login
|
|
276
|
+
if (!(0, _helpers3.isOnLoginCodeCallbackRoute)(store) && (await __redirectToSsoIfRequired(e))) {
|
|
259
277
|
return;
|
|
260
278
|
}
|
|
261
279
|
contextHolder.setAccessToken(null);
|
|
@@ -268,9 +286,7 @@ var _default = (store, api, sharedActions) => {
|
|
|
268
286
|
};
|
|
269
287
|
|
|
270
288
|
/** @private */
|
|
271
|
-
const __refreshTokenWithNativeTokens = async ({
|
|
272
|
-
isSsoEnforcementRetry = false
|
|
273
|
-
} = {}) => {
|
|
289
|
+
const __refreshTokenWithNativeTokens = async () => {
|
|
274
290
|
try {
|
|
275
291
|
const authApi = api.auth;
|
|
276
292
|
const response = await (0, _helpers.refreshTokenWithRetry)(async () => {
|
|
@@ -284,13 +300,6 @@ var _default = (store, api, sharedActions) => {
|
|
|
284
300
|
isAuthenticated: true
|
|
285
301
|
});
|
|
286
302
|
} catch (e) {
|
|
287
|
-
// native tokens can't name a tenant: the retry relies on the tab tenant only
|
|
288
|
-
const retry = () => __refreshTokenWithNativeTokens({
|
|
289
|
-
isSsoEnforcementRetry: true
|
|
290
|
-
});
|
|
291
|
-
if (await __handleSsoEnforcementRefreshError(e, isSsoEnforcementRetry, retry)) {
|
|
292
|
-
return;
|
|
293
|
-
}
|
|
294
303
|
contextHolder.setAccessToken(null);
|
|
295
304
|
contextHolder.setUser(null);
|
|
296
305
|
actions.setAuthState({
|
|
@@ -302,7 +311,7 @@ var _default = (store, api, sharedActions) => {
|
|
|
302
311
|
|
|
303
312
|
/** @protected */
|
|
304
313
|
const __refreshToken = async () => {
|
|
305
|
-
(0, _helpers3.restoreTabTenantUnlessSsoCallback)(store);
|
|
314
|
+
await (0, _helpers3.restoreTabTenantUnlessSsoCallback)(store);
|
|
306
315
|
if (_toolkit.FronteggNativeModule.isGetTokensAvailable()) {
|
|
307
316
|
await __refreshTokenWithNativeTokens();
|
|
308
317
|
return;
|
|
@@ -590,13 +599,7 @@ var _default = (store, api, sharedActions) => {
|
|
|
590
599
|
redirectToFullPage(address);
|
|
591
600
|
return;
|
|
592
601
|
}
|
|
593
|
-
|
|
594
|
-
if (idpType === _interfaces4.SamlVendors.Oidc && !ssoRedirectUrl.includes('redirect_uri')) {
|
|
595
|
-
const {
|
|
596
|
-
oidcRedirectUrl
|
|
597
|
-
} = store.auth.routes;
|
|
598
|
-
ssoRedirectUrl += `&redirect_uri=${window.location.origin}${oidcRedirectUrl}`;
|
|
599
|
-
}
|
|
602
|
+
const ssoRedirectUrl = idpType === _interfaces4.SamlVendors.Oidc ? (0, _helpers2.withOidcRedirectUri)(address, store.auth.routes.oidcRedirectUrl) : address;
|
|
600
603
|
if (_toolkit.FronteggNativeModule.isLoginWithSSOAvailable()) {
|
|
601
604
|
var _ref4;
|
|
602
605
|
_toolkit.FronteggNativeModule.loginWithSSO((_ref4 = email != null ? email : username) != null ? _ref4 : '');
|
|
@@ -815,11 +818,13 @@ var _default = (store, api, sharedActions) => {
|
|
|
815
818
|
});
|
|
816
819
|
}
|
|
817
820
|
};
|
|
818
|
-
const clearLogoutLocalState = ({
|
|
821
|
+
const clearLogoutLocalState = async ({
|
|
819
822
|
keepHostedOAuthState = false
|
|
820
823
|
} = {}) => {
|
|
821
824
|
if (contextHolder.isSessionPerTenantEnabled()) {
|
|
822
825
|
(0, _restApi.removeTabTenantFromSessionStorage)();
|
|
826
|
+
// a pending SSO switch would put a tenant of the logged-out session back on the next page load
|
|
827
|
+
await (0, _restApi.clearTabTenantBeforeSsoSwitch)(store.root.appName);
|
|
823
828
|
}
|
|
824
829
|
// Step-up re-login goes through silentLogout but still needs the hosted OAuth state to finish the client flow.
|
|
825
830
|
if (!keepHostedOAuthState) {
|
|
@@ -840,7 +845,7 @@ var _default = (store, api, sharedActions) => {
|
|
|
840
845
|
} catch {
|
|
841
846
|
/* empty */
|
|
842
847
|
}
|
|
843
|
-
clearLogoutLocalState();
|
|
848
|
+
await clearLogoutLocalState();
|
|
844
849
|
actions.resetAuthState();
|
|
845
850
|
await actions.requestAuthorize(true);
|
|
846
851
|
payload == null ? void 0 : payload();
|
|
@@ -853,7 +858,7 @@ var _default = (store, api, sharedActions) => {
|
|
|
853
858
|
/* empty */
|
|
854
859
|
}
|
|
855
860
|
const isStepUpInProgress = !!((_window2 = window) != null && _window2.localStorage.getItem(_consts.SHOULD_STEP_UP_KEY));
|
|
856
|
-
clearLogoutLocalState({
|
|
861
|
+
await clearLogoutLocalState({
|
|
857
862
|
keepHostedOAuthState: isStepUpInProgress
|
|
858
863
|
});
|
|
859
864
|
setTimeout(() => {
|
|
@@ -1701,7 +1706,6 @@ var _default = (store, api, sharedActions) => {
|
|
|
1701
1706
|
refreshTokenForSocialLogins,
|
|
1702
1707
|
// protected
|
|
1703
1708
|
__refreshToken,
|
|
1704
|
-
__redirectToSsoForTenant,
|
|
1705
1709
|
__redirectToSsoIfRequired
|
|
1706
1710
|
}, (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));
|
|
1707
1711
|
};
|
|
@@ -7,7 +7,7 @@ exports.isAbsoluteUrl = exports.getSearchParamsFromUrl = exports.getSearchParam
|
|
|
7
7
|
exports.isEmailPayload = isEmailPayload;
|
|
8
8
|
exports.isOauthCallbackRoute = void 0;
|
|
9
9
|
exports.isUsernamePayload = isUsernamePayload;
|
|
10
|
-
exports.shouldShowPasswordRotationPromptFunc = void 0;
|
|
10
|
+
exports.withOidcRedirectUri = exports.shouldShowPasswordRotationPromptFunc = void 0;
|
|
11
11
|
var _restApi = require("@frontegg/rest-api");
|
|
12
12
|
var _interfaces = require("../MfaState/interfaces");
|
|
13
13
|
const isAbsoluteUrl = path => {
|
|
@@ -90,7 +90,11 @@ const getSearchParam = key => {
|
|
|
90
90
|
};
|
|
91
91
|
exports.getSearchParam = getSearchParam;
|
|
92
92
|
const TENANT_ID_PARAM_KEY = 'tenantId';
|
|
93
|
+
|
|
94
|
+
/** An OIDC SSO page without redirect_uri comes back to the OIDC callback, which completes the login with its code. */
|
|
93
95
|
exports.TENANT_ID_PARAM_KEY = TENANT_ID_PARAM_KEY;
|
|
96
|
+
const withOidcRedirectUri = (address, oidcRedirectUrl) => address.includes('redirect_uri') ? address : `${address}&redirect_uri=${window.location.origin}${oidcRedirectUrl}`;
|
|
97
|
+
exports.withOidcRedirectUri = withOidcRedirectUri;
|
|
94
98
|
const getNumberOfMfaDevices = mfaDevices => {
|
|
95
99
|
// Count platform / cross-platform separately — matches the MFA picker options.
|
|
96
100
|
const numberOfWebAuthnDevices = Number(mfaDevices.webauthn.some(device => device.deviceType === _restApi.WebAuthnDeviceType.Platform)) + Number(mfaDevices.webauthn.some(device => device.deviceType === _restApi.WebAuthnDeviceType.CrossPlatform));
|
|
@@ -52,6 +52,9 @@ var _default = (store, api, sharedActions) => {
|
|
|
52
52
|
});
|
|
53
53
|
}
|
|
54
54
|
} catch (e) {
|
|
55
|
+
// the tab tenant the switch was refused from, read before any await: a parallel switch that passes moves the tab
|
|
56
|
+
// while this one goes to SSO
|
|
57
|
+
const tabTenantId = (0, _restApi.getTabTenantFromSessionStorage)(store.root.appName);
|
|
55
58
|
silentReload ? actions.setTenantsState({
|
|
56
59
|
switchingTenant: false
|
|
57
60
|
}) : actions.setAuthState({
|
|
@@ -60,7 +63,7 @@ var _default = (store, api, sharedActions) => {
|
|
|
60
63
|
if (await actions.__redirectToSsoIfRequired(e)) {
|
|
61
64
|
// the tab comes back from SSO in the target tenant, not the one the failed switch restored
|
|
62
65
|
if (_restApi.ContextHolder.for(store.root.appName).isSessionPerTenantEnabled()) {
|
|
63
|
-
(0, _restApi.setTabTenantForSsoSwitch)(tenantId);
|
|
66
|
+
(0, _restApi.setTabTenantForSsoSwitch)(tenantId, tabTenantId);
|
|
64
67
|
}
|
|
65
68
|
return;
|
|
66
69
|
}
|
package/node/auth/helpers.js
CHANGED
|
@@ -141,10 +141,8 @@ const isOnLoginCodeCallbackRoute = store => {
|
|
|
141
141
|
* keeps it; any other page load means the user didn't finish that login, so the tab goes back to its tenant.
|
|
142
142
|
*/
|
|
143
143
|
exports.isOnLoginCodeCallbackRoute = isOnLoginCodeCallbackRoute;
|
|
144
|
-
const restoreTabTenantUnlessSsoCallback = store => {
|
|
144
|
+
const restoreTabTenantUnlessSsoCallback = async store => {
|
|
145
145
|
var _store$root$urlStrate2;
|
|
146
|
-
// Flags load in parallel with the first refresh. Recover an existing marker
|
|
147
|
-
// before choosing that refresh's tenant, even if flags are not available yet.
|
|
148
146
|
if (!_restApi.ContextHolder.for(store.root.appName).isSessionPerTenantEnabled()) {
|
|
149
147
|
return;
|
|
150
148
|
}
|
|
@@ -155,9 +153,13 @@ const restoreTabTenantUnlessSsoCallback = store => {
|
|
|
155
153
|
} = (0, _extends2.default)({}, _consts.defaultFronteggRoutes, store.auth.routes);
|
|
156
154
|
// matched like login matches its callback pages: endsWith, so a basename before the route still counts
|
|
157
155
|
const currentRoutes = getCurrentRoutes((_store$root$urlStrate2 = store.root.urlStrategy) != null ? _store$root$urlStrate2 : 'path');
|
|
158
|
-
|
|
156
|
+
// a refused login comes back on its callback too, not finished: SAML with samlerrors (as SamlLoginSuccess reads them),
|
|
157
|
+
// OIDC and hosted without a code (e.g. the IdP's error=access_denied), which their pages need to finish the login
|
|
158
|
+
const params = new URLSearchParams(window.location.search);
|
|
159
|
+
const callbackRoutes = [...(params.has('samlerrors') ? [] : [samlCallbackUrl]), ...(params.has('code') ? [oidcRedirectUrl, hostedLoginRedirectUrl] : [])];
|
|
160
|
+
const isSsoCallback = callbackRoutes.some(route => !!route && currentRoutes.some(currentRoute => currentRoute.endsWith(route)));
|
|
159
161
|
if (!isSsoCallback) {
|
|
160
|
-
(0, _restApi.restoreTabTenantBeforeSsoSwitch)();
|
|
162
|
+
await (0, _restApi.restoreTabTenantBeforeSsoSwitch)(store.root.appName);
|
|
161
163
|
}
|
|
162
164
|
};
|
|
163
165
|
exports.restoreTabTenantUnlessSsoCallback = restoreTabTenantUnlessSsoCallback;
|
package/node/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontegg/redux-store",
|
|
3
|
-
"version": "7.131.0
|
|
3
|
+
"version": "7.131.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.131.0
|
|
10
|
+
"@frontegg/rest-api": "7.131.0",
|
|
11
11
|
"fast-deep-equal": "3.1.3",
|
|
12
12
|
"get-value": "^3.0.1",
|
|
13
13
|
"proxy-compare": "^3.0.0",
|