@frontegg/redux-store 7.130.0 → 7.131.0-alpha.1
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/ActivateAccountState/actions.js +6 -0
- package/auth/LoginState/actions/hostedLoginAuthorize.actions.d.ts +3 -1
- package/auth/LoginState/actions/hostedLoginAuthorize.actions.js +29 -6
- package/auth/LoginState/actions/index.d.ts +8 -1
- package/auth/LoginState/actions/index.js +172 -11
- package/auth/LoginState/actions/mfaWithAuthenticator.actions.js +3 -0
- package/auth/LoginState/actions/mfaWithEmailCode.actions.js +3 -0
- package/auth/LoginState/actions/mfaWithSMS.actions.js +3 -0
- package/auth/LoginState/actions/mfaWithWebAuthn.actions.js +3 -0
- package/auth/MfaState/actions.js +3 -0
- package/auth/SocialLoginState/actions.js +3 -0
- package/auth/StepUpState/actions/generateStepUpSession.actions.js +5 -0
- package/auth/StepUpState/helpers.d.ts +2 -1
- package/auth/StepUpState/helpers.js +3 -2
- package/auth/TenantsState/actions.js +8 -1
- package/auth/helpers.d.ts +15 -0
- package/auth/helpers.js +60 -2
- package/helpers/handlers.d.ts +4 -1
- package/helpers/handlers.js +4 -1
- package/index.js +1 -1
- package/mocks/auth-mocks/loginActions.mocks.d.ts +8 -1
- package/node/auth/ActivateAccountState/actions.js +6 -0
- package/node/auth/LoginState/actions/hostedLoginAuthorize.actions.js +27 -4
- package/node/auth/LoginState/actions/index.js +169 -8
- package/node/auth/LoginState/actions/mfaWithAuthenticator.actions.js +3 -0
- package/node/auth/LoginState/actions/mfaWithEmailCode.actions.js +3 -0
- package/node/auth/LoginState/actions/mfaWithSMS.actions.js +3 -0
- package/node/auth/LoginState/actions/mfaWithWebAuthn.actions.js +3 -0
- package/node/auth/MfaState/actions.js +3 -0
- package/node/auth/SocialLoginState/actions.js +3 -0
- package/node/auth/StepUpState/actions/generateStepUpSession.actions.js +5 -0
- package/node/auth/StepUpState/helpers.js +3 -2
- package/node/auth/TenantsState/actions.js +7 -0
- package/node/auth/helpers.js +66 -2
- package/node/helpers/handlers.js +5 -1
- package/node/index.js +1 -1
- package/package.json +2 -2
package/auth/helpers.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
2
1
|
import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
|
|
2
|
+
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
3
3
|
const _excluded = ["phoneNumber", "email", "username"],
|
|
4
4
|
_excluded2 = ["authenticatedUrl"];
|
|
5
|
-
import { ContextHolder } from '@frontegg/rest-api';
|
|
5
|
+
import { ContextHolder, restoreTabTenantBeforeSsoSwitch, isEnforceSsoOnTenantAccessEnabled } from '@frontegg/rest-api';
|
|
6
6
|
import { defaultFronteggRoutes } from './LoginState/consts';
|
|
7
7
|
export * from './Entitlements/helpers';
|
|
8
8
|
export * from './LoginState/helpers';
|
|
@@ -53,6 +53,52 @@ export const getUri = urlStrategy => {
|
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The route the app shows and the page's path. With hash routing they differ: the app's pages live in the hash, but
|
|
59
|
+
* SAML, OIDC, social and magic link callbacks come back on the path, which is where login matches them.
|
|
60
|
+
*/
|
|
61
|
+
const getCurrentRoutes = urlStrategy => {
|
|
62
|
+
const hashOrPathRoute = getUri(urlStrategy);
|
|
63
|
+
const {
|
|
64
|
+
pathname
|
|
65
|
+
} = window.location;
|
|
66
|
+
return hashOrPathRoute === pathname ? [pathname] : [hashOrPathRoute, pathname];
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* On the OIDC callback the page still has to finish the OIDC login with its code: a refresh refused before that is not
|
|
71
|
+
* a reason to go back to the IdP, which would send the user straight back here, forever.
|
|
72
|
+
*/
|
|
73
|
+
export const isOnOidcCallbackRoute = store => {
|
|
74
|
+
var _store$root$urlStrate;
|
|
75
|
+
const {
|
|
76
|
+
oidcRedirectUrl
|
|
77
|
+
} = _extends({}, defaultFronteggRoutes, store.auth.routes);
|
|
78
|
+
return !!oidcRedirectUrl && getCurrentRoutes((_store$root$urlStrate = store.root.urlStrategy) != null ? _store$root$urlStrate : 'path').some(currentRoute => currentRoute.endsWith(oidcRedirectUrl));
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* After a switch refused for SSO the tab points at the SSO tenant. Its SSO login comes back on a callback route and
|
|
83
|
+
* keeps it; any other page load means the user didn't finish that login, so the tab goes back to its tenant.
|
|
84
|
+
*/
|
|
85
|
+
export const restoreTabTenantUnlessSsoCallback = store => {
|
|
86
|
+
var _store$root$urlStrate2;
|
|
87
|
+
if (!isEnforceSsoOnTenantAccessEnabled(store.root.appName) || !ContextHolder.for(store.root.appName).isSessionPerTenantEnabled()) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const {
|
|
91
|
+
samlCallbackUrl,
|
|
92
|
+
oidcRedirectUrl,
|
|
93
|
+
hostedLoginRedirectUrl
|
|
94
|
+
} = _extends({}, defaultFronteggRoutes, store.auth.routes);
|
|
95
|
+
// matched like login matches its callback pages: endsWith, so a basename before the route still counts
|
|
96
|
+
const currentRoutes = getCurrentRoutes((_store$root$urlStrate2 = store.root.urlStrategy) != null ? _store$root$urlStrate2 : 'path');
|
|
97
|
+
const isSsoCallback = [samlCallbackUrl, oidcRedirectUrl, hostedLoginRedirectUrl].some(route => !!route && currentRoutes.some(currentRoute => currentRoute.endsWith(route)));
|
|
98
|
+
if (!isSsoCallback) {
|
|
99
|
+
restoreTabTenantBeforeSsoSwitch();
|
|
100
|
+
}
|
|
101
|
+
};
|
|
56
102
|
export const prepareIdentifiersForSignup = payload => {
|
|
57
103
|
const {
|
|
58
104
|
phoneNumber,
|
|
@@ -77,6 +123,18 @@ export const prepareIdentifiersForSignup = payload => {
|
|
|
77
123
|
}
|
|
78
124
|
return finalDto;
|
|
79
125
|
};
|
|
126
|
+
|
|
127
|
+
// Pages the user lands on after login or sign-up: destinations, not auth pages.
|
|
128
|
+
const AFTER_AUTH_DESTINATION_ROUTES = ['authenticatedUrl', 'signUpSuccessUrl', 'accountSignUpSuccessUrl', 'userSignUpSuccessUrl', 'customLoginAuthenticatedUrl'];
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Whether the current route is an auth page (login, tenant chooser, social / magic link / SSO callback), not one to come
|
|
132
|
+
* back to. Unlike isAuthRoute: after-login / sign-up destinations don't count, and endsWith lets a basename prefix it.
|
|
133
|
+
*/
|
|
134
|
+
export const isOnAuthPageRoute = (urlStrategy, routes) => {
|
|
135
|
+
const currentRoutes = getCurrentRoutes(urlStrategy);
|
|
136
|
+
return Object.entries(_extends({}, defaultFronteggRoutes, routes)).some(([name, route]) => !AFTER_AUTH_DESTINATION_ROUTES.includes(name) && typeof route === 'string' && !!route && currentRoutes.some(currentRoute => currentRoute.endsWith(route)));
|
|
137
|
+
};
|
|
80
138
|
export const isAuthRoute = (path, routes) => {
|
|
81
139
|
const _defaultFronteggRoute = _extends({}, defaultFronteggRoutes, routes),
|
|
82
140
|
authRoutes = _objectWithoutPropertiesLoose(_defaultFronteggRoute, _excluded2);
|
package/helpers/handlers.d.ts
CHANGED
|
@@ -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;
|
package/helpers/handlers.js
CHANGED
|
@@ -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
|
@@ -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
|
|
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;
|
|
@@ -158,6 +158,9 @@ var _default = (store, api, sharedActions) => {
|
|
|
158
158
|
});
|
|
159
159
|
callback == null ? void 0 : callback(true);
|
|
160
160
|
} catch (e) {
|
|
161
|
+
if (await actions.__redirectToSsoIfRequired(e)) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
161
164
|
setActivateState({
|
|
162
165
|
loading: false,
|
|
163
166
|
error: (0, _helpers.errorHandler)(e)
|
|
@@ -195,6 +198,9 @@ var _default = (store, api, sharedActions) => {
|
|
|
195
198
|
});
|
|
196
199
|
callback == null ? void 0 : callback(true);
|
|
197
200
|
} catch (e) {
|
|
201
|
+
if (await actions.__redirectToSsoIfRequired(e)) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
198
204
|
setActivateState({
|
|
199
205
|
submitting: false,
|
|
200
206
|
loading: false,
|
|
@@ -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");
|
|
@@ -21,6 +22,7 @@ var _default = (store, api, sharedActions) => {
|
|
|
21
22
|
const __requestHostedLoginSilentAuthorize = async () => {
|
|
22
23
|
var _response;
|
|
23
24
|
let response;
|
|
25
|
+
(0, _helpers3.restoreTabTenantUnlessSsoCallback)(store);
|
|
24
26
|
if (_toolkit.FronteggNativeModule.isGetTokensAvailable()) {
|
|
25
27
|
const authApi = api.auth;
|
|
26
28
|
response = await (0, _helpers.refreshTokenWithRetry)(async () => {
|
|
@@ -28,7 +30,21 @@ var _default = (store, api, sharedActions) => {
|
|
|
28
30
|
return await authApi.silentOAuthRefreshTokenV3(tokens);
|
|
29
31
|
});
|
|
30
32
|
} else {
|
|
31
|
-
|
|
33
|
+
try {
|
|
34
|
+
response = await api.auth.silentOAuthRefreshTokenV2();
|
|
35
|
+
} catch (error) {
|
|
36
|
+
var _store$auth$user, _error$errorContext;
|
|
37
|
+
// the tenant requires SSO: hosted login for that tenant, not the plain one the caller falls back to
|
|
38
|
+
if (await actions.__redirectToSsoIfRequired(error)) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const tenantId = (_store$auth$user = store.auth.user) == null ? void 0 : _store$auth$user.tenantId;
|
|
42
|
+
if (!(0, _restApi.isEnforceSsoOnTenantAccessEnabled)(store.root.appName) || !(0, _helpers.isSsoEnforcementError)(error) || ((_error$errorContext = error.errorContext) == null ? void 0 : _error$errorContext.ssoRequired) !== false || !tenantId) {
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
// one retry for the tenant of the current token (a cold reload has no user yet: no retry)
|
|
46
|
+
response = await api.auth.silentOAuthRefreshTokenV2(tenantId);
|
|
47
|
+
}
|
|
32
48
|
}
|
|
33
49
|
if ((_response = response) != null && _response.user) {
|
|
34
50
|
await actions.afterAuthenticationStateUpdate(response, {
|
|
@@ -39,7 +55,14 @@ var _default = (store, api, sharedActions) => {
|
|
|
39
55
|
throw new Error(`couldn't refresh user token with oauth service`);
|
|
40
56
|
}
|
|
41
57
|
};
|
|
42
|
-
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @param options.forceTenantId the tenantId in additionalParams wins over the resolved and the page's tenant
|
|
61
|
+
* (sending the user to that tenant's SSO); other callers keep their custom login tenant
|
|
62
|
+
*/
|
|
63
|
+
const requestHostedLoginAuthorize = async (additionalParams, {
|
|
64
|
+
forceTenantId = false
|
|
65
|
+
} = {}) => {
|
|
43
66
|
var _store$auth$customLog;
|
|
44
67
|
const {
|
|
45
68
|
routes,
|
|
@@ -86,7 +109,7 @@ var _default = (store, api, sharedActions) => {
|
|
|
86
109
|
code_challenge_method: 'S256',
|
|
87
110
|
nonce
|
|
88
111
|
}, additionalParams);
|
|
89
|
-
if (context.tenantResolver) {
|
|
112
|
+
if (context.tenantResolver && !forceTenantId) {
|
|
90
113
|
var _context$tenantResolv;
|
|
91
114
|
const resolvedTenantResult = await ((_context$tenantResolv = context.tenantResolver) == null ? void 0 : _context$tenantResolv.call(context));
|
|
92
115
|
if (resolvedTenantResult != null && resolvedTenantResult.tenant) {
|
|
@@ -94,7 +117,7 @@ var _default = (store, api, sharedActions) => {
|
|
|
94
117
|
}
|
|
95
118
|
}
|
|
96
119
|
const tenantId = (0, _helpers2.getSearchParam)(_helpers2.TENANT_ID_PARAM_KEY);
|
|
97
|
-
if (tenantId) {
|
|
120
|
+
if (tenantId && !forceTenantId) {
|
|
98
121
|
params['tenantId'] = tenantId;
|
|
99
122
|
}
|
|
100
123
|
if (_toolkit.FronteggNativeModule.isAvailable('closeWindow')) {
|
|
@@ -50,8 +50,114 @@ var _default = (store, api, sharedActions) => {
|
|
|
50
50
|
const actions = sharedActions;
|
|
51
51
|
const contextHolder = _restApi.ContextHolder.for(store.root.appName);
|
|
52
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Sends the user to log in through the tenant's SSO and back to the current page afterwards.
|
|
55
|
+
* @protected
|
|
56
|
+
*/
|
|
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
|
|
59
|
+
let ssoRedirectInFlight = false;
|
|
60
|
+
const __redirectToSsoForTenant = async ({
|
|
61
|
+
tenantId,
|
|
62
|
+
address
|
|
63
|
+
}) => {
|
|
64
|
+
if (ssoRedirectInFlight) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
ssoRedirectInFlight = true;
|
|
68
|
+
try {
|
|
69
|
+
await __redirectToSsoForTenantNow({
|
|
70
|
+
tenantId,
|
|
71
|
+
address
|
|
72
|
+
});
|
|
73
|
+
} finally {
|
|
74
|
+
ssoRedirectInFlight = false;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const __redirectToSsoForTenantNow = async ({
|
|
78
|
+
tenantId,
|
|
79
|
+
address
|
|
80
|
+
}) => {
|
|
81
|
+
var _store$root$urlStrate;
|
|
82
|
+
// the login box shows login's "redirecting to SSO" step instead of the screen that was refused (its spinner and
|
|
83
|
+
// a previous error)
|
|
84
|
+
setLoginState({
|
|
85
|
+
step: _interfaces.LoginStep.redirectToSSO,
|
|
86
|
+
loading: false,
|
|
87
|
+
error: undefined
|
|
88
|
+
});
|
|
89
|
+
// on an auth page (login, tenant chooser, social / magic link / SSO callback) keep the destination the login
|
|
90
|
+
// already saved: the page itself is not one to come back to
|
|
91
|
+
const urlStrategy = (_store$root$urlStrate = store.root.urlStrategy) != null ? _store$root$urlStrate : 'path';
|
|
92
|
+
const isOnAuthPage = (0, _helpers3.isOnAuthPageRoute)(urlStrategy, store.auth.routes);
|
|
93
|
+
if (!isOnAuthPage) {
|
|
94
|
+
(0, _helpers3.setAfterAuthRedirectUrlForStepUp)(urlStrategy);
|
|
95
|
+
}
|
|
96
|
+
if (store.auth.hostedLoginBox) {
|
|
97
|
+
var _window$localStorage;
|
|
98
|
+
// as loginWithRedirect does, but the SSO tenant wins over the custom login tenant
|
|
99
|
+
(_window$localStorage = window.localStorage) == null ? void 0 : _window$localStorage.removeItem(_consts.SHOULD_STEP_UP_KEY);
|
|
100
|
+
await actions.__requestHostedLoginAuthorize({
|
|
101
|
+
prompt: 'login',
|
|
102
|
+
tenantId
|
|
103
|
+
}, {
|
|
104
|
+
forceTenantId: true
|
|
105
|
+
});
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
// as login does for an OIDC SSO page without redirect_uri; the refusal has no idpType, but an OIDC page asks for a code
|
|
109
|
+
let ssoRedirectUrl = address;
|
|
110
|
+
if (/[?&]response_type=code(&|$)/.test(address) && !address.includes('redirect_uri')) {
|
|
111
|
+
ssoRedirectUrl += `&redirect_uri=${window.location.origin}${store.auth.routes.oidcRedirectUrl}`;
|
|
112
|
+
}
|
|
113
|
+
redirectToFullPage(ssoRedirectUrl);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Sends the user to the tenant's SSO when a login or tenant switch is refused because that tenant requires it
|
|
118
|
+
* (e.g. an invitation into a tenant whose SSO covers the user's email). The SSO login lands in that tenant.
|
|
119
|
+
* @returns true when redirected, so the caller doesn't show the error
|
|
120
|
+
* @protected
|
|
121
|
+
*/
|
|
122
|
+
const __redirectToSsoIfRequired = async e => {
|
|
123
|
+
var _e$errorContext;
|
|
124
|
+
if (!(0, _restApi.isEnforceSsoOnTenantAccessEnabled)(store.root.appName) || !(0, _helpers.isSsoEnforcementError)(e) || !((_e$errorContext = e.errorContext) != null && _e$errorContext.ssoRequired)) {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
await __redirectToSsoForTenant(e.errorContext);
|
|
128
|
+
return true;
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* Handles a hosted (OAuth) token refresh refused because the tenant requires SSO.
|
|
132
|
+
* ssoRequired: true sends the user to SSO, like login. ssoRequired: false renews once more for the tenant of the
|
|
133
|
+
* current token, which keeps the session: OAuth doesn't use up the refresh token on this refusal.
|
|
134
|
+
* @returns true when handled, false when the caller should log out
|
|
135
|
+
* @private
|
|
136
|
+
*/
|
|
137
|
+
const __handleSsoEnforcementRefreshError = async (e, isSsoEnforcementRetry, refreshForTenant) => {
|
|
138
|
+
var _store$auth$user;
|
|
139
|
+
if (!(0, _restApi.isEnforceSsoOnTenantAccessEnabled)(store.root.appName) || !(0, _helpers.isSsoEnforcementError)(e)) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
if (await __redirectToSsoIfRequired(e)) {
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
const currentTenantId = (_store$auth$user = store.auth.user) == null ? void 0 : _store$auth$user.tenantId;
|
|
146
|
+
if (isSsoEnforcementRetry || !currentTenantId) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
if (contextHolder.isSessionPerTenantEnabled()) {
|
|
150
|
+
(0, _restApi.setTabTenantInSessionStorage)(currentTenantId);
|
|
151
|
+
}
|
|
152
|
+
await refreshForTenant(currentTenantId);
|
|
153
|
+
return true;
|
|
154
|
+
};
|
|
155
|
+
|
|
53
156
|
/** @private */
|
|
54
|
-
const __refreshTokenHosted = async (
|
|
157
|
+
const __refreshTokenHosted = async ({
|
|
158
|
+
tenantId,
|
|
159
|
+
isSsoEnforcementRetry = false
|
|
160
|
+
} = {}) => {
|
|
55
161
|
const user = store.auth.user;
|
|
56
162
|
if (!(user != null && user.refreshToken)) {
|
|
57
163
|
contextHolder.setAccessToken(null);
|
|
@@ -63,7 +169,7 @@ var _default = (store, api, sharedActions) => {
|
|
|
63
169
|
return;
|
|
64
170
|
}
|
|
65
171
|
try {
|
|
66
|
-
const tabTenantId = (0, _restApi.getTabTenantFromSessionStorage)(store.root.appName);
|
|
172
|
+
const tabTenantId = tenantId != null ? tenantId : (0, _restApi.getTabTenantFromSessionStorage)(store.root.appName);
|
|
67
173
|
const body = (0, _extends2.default)({
|
|
68
174
|
grant_type: 'refresh_token',
|
|
69
175
|
refresh_token: user == null ? void 0 : user.refreshToken
|
|
@@ -78,6 +184,13 @@ var _default = (store, api, sharedActions) => {
|
|
|
78
184
|
isAuthenticated: true
|
|
79
185
|
});
|
|
80
186
|
} catch (e) {
|
|
187
|
+
const retry = tenantId => __refreshTokenHosted({
|
|
188
|
+
tenantId,
|
|
189
|
+
isSsoEnforcementRetry: true
|
|
190
|
+
});
|
|
191
|
+
if (await __handleSsoEnforcementRefreshError(e, isSsoEnforcementRetry, retry)) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
81
194
|
contextHolder.setAccessToken(null);
|
|
82
195
|
contextHolder.setUser(null);
|
|
83
196
|
actions.setAuthState({
|
|
@@ -139,6 +252,11 @@ var _default = (store, api, sharedActions) => {
|
|
|
139
252
|
});
|
|
140
253
|
}
|
|
141
254
|
} catch (e) {
|
|
255
|
+
// a refused identity refresh has already used up the refresh token: no retry, only the SSO redirect, except on
|
|
256
|
+
// the OIDC callback, where the page itself completes the OIDC login
|
|
257
|
+
if ((0, _restApi.isEnforceSsoOnTenantAccessEnabled)(store.root.appName) && !(0, _helpers3.isOnOidcCallbackRoute)(store) && (await __redirectToSsoIfRequired(e))) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
142
260
|
contextHolder.setAccessToken(null);
|
|
143
261
|
contextHolder.setUser(null);
|
|
144
262
|
actions.setAuthState({
|
|
@@ -149,7 +267,9 @@ var _default = (store, api, sharedActions) => {
|
|
|
149
267
|
};
|
|
150
268
|
|
|
151
269
|
/** @private */
|
|
152
|
-
const __refreshTokenWithNativeTokens = async (
|
|
270
|
+
const __refreshTokenWithNativeTokens = async ({
|
|
271
|
+
isSsoEnforcementRetry = false
|
|
272
|
+
} = {}) => {
|
|
153
273
|
try {
|
|
154
274
|
const authApi = api.auth;
|
|
155
275
|
const response = await (0, _helpers.refreshTokenWithRetry)(async () => {
|
|
@@ -163,6 +283,13 @@ var _default = (store, api, sharedActions) => {
|
|
|
163
283
|
isAuthenticated: true
|
|
164
284
|
});
|
|
165
285
|
} catch (e) {
|
|
286
|
+
// native tokens can't name a tenant: the retry relies on the tab tenant only
|
|
287
|
+
const retry = () => __refreshTokenWithNativeTokens({
|
|
288
|
+
isSsoEnforcementRetry: true
|
|
289
|
+
});
|
|
290
|
+
if (await __handleSsoEnforcementRefreshError(e, isSsoEnforcementRetry, retry)) {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
166
293
|
contextHolder.setAccessToken(null);
|
|
167
294
|
contextHolder.setUser(null);
|
|
168
295
|
actions.setAuthState({
|
|
@@ -174,6 +301,7 @@ var _default = (store, api, sharedActions) => {
|
|
|
174
301
|
|
|
175
302
|
/** @protected */
|
|
176
303
|
const __refreshToken = async () => {
|
|
304
|
+
(0, _helpers3.restoreTabTenantUnlessSsoCallback)(store);
|
|
177
305
|
if (_toolkit.FronteggNativeModule.isGetTokensAvailable()) {
|
|
178
306
|
await __refreshTokenWithNativeTokens();
|
|
179
307
|
return;
|
|
@@ -196,9 +324,9 @@ var _default = (store, api, sharedActions) => {
|
|
|
196
324
|
* @private
|
|
197
325
|
*/
|
|
198
326
|
const __handleUnnecessaryEntitlementsUpdate = async updatedUser => {
|
|
199
|
-
var _store$auth$
|
|
327
|
+
var _store$auth$user2;
|
|
200
328
|
// @ts-ignore TODO: fix entitlements state type
|
|
201
|
-
const oldEntitlements = (_store$auth$
|
|
329
|
+
const oldEntitlements = (_store$auth$user2 = store.auth.user) == null ? void 0 : _store$auth$user2.entitlements;
|
|
202
330
|
if ((0, _toolkit.isEntitlementsDeeplyEqual)(oldEntitlements, updatedUser == null ? void 0 : updatedUser.entitlements)) {
|
|
203
331
|
// set the previous entitlements object
|
|
204
332
|
return (0, _extends2.default)({}, updatedUser, {
|
|
@@ -675,6 +803,9 @@ var _default = (store, api, sharedActions) => {
|
|
|
675
803
|
} catch (e) {
|
|
676
804
|
contextHolder.setAccessToken(null);
|
|
677
805
|
contextHolder.setUser(null);
|
|
806
|
+
if (await __redirectToSsoIfRequired(e)) {
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
678
809
|
callback == null ? void 0 : callback(false, e);
|
|
679
810
|
setLoginState({
|
|
680
811
|
email,
|
|
@@ -970,6 +1101,9 @@ var _default = (store, api, sharedActions) => {
|
|
|
970
1101
|
callback == null ? void 0 : callback(true);
|
|
971
1102
|
}
|
|
972
1103
|
} catch (e) {
|
|
1104
|
+
if (await __redirectToSsoIfRequired(e)) {
|
|
1105
|
+
return;
|
|
1106
|
+
}
|
|
973
1107
|
setLoginState({
|
|
974
1108
|
error: (0, _helpers.errorHandler)(e, 'Failed to authenticate')
|
|
975
1109
|
});
|
|
@@ -1106,6 +1240,9 @@ var _default = (store, api, sharedActions) => {
|
|
|
1106
1240
|
}
|
|
1107
1241
|
// callback?.(true);
|
|
1108
1242
|
} catch (e) {
|
|
1243
|
+
if (await __redirectToSsoIfRequired(e)) {
|
|
1244
|
+
return;
|
|
1245
|
+
}
|
|
1109
1246
|
setLoginState({
|
|
1110
1247
|
error: (0, _helpers.errorHandler)(e)
|
|
1111
1248
|
});
|
|
@@ -1266,6 +1403,9 @@ var _default = (store, api, sharedActions) => {
|
|
|
1266
1403
|
});
|
|
1267
1404
|
callback == null ? void 0 : callback(true);
|
|
1268
1405
|
} catch (e) {
|
|
1406
|
+
if (await __redirectToSsoIfRequired(e)) {
|
|
1407
|
+
return;
|
|
1408
|
+
}
|
|
1269
1409
|
setLoginState({
|
|
1270
1410
|
loading: false,
|
|
1271
1411
|
error: (0, _helpers.errorHandler)(e)
|
|
@@ -1289,6 +1429,9 @@ var _default = (store, api, sharedActions) => {
|
|
|
1289
1429
|
});
|
|
1290
1430
|
callback == null ? void 0 : callback(true);
|
|
1291
1431
|
} catch (e) {
|
|
1432
|
+
if (await __redirectToSsoIfRequired(e)) {
|
|
1433
|
+
return;
|
|
1434
|
+
}
|
|
1292
1435
|
setLoginState({
|
|
1293
1436
|
loading: false,
|
|
1294
1437
|
error: (0, _helpers.errorHandler)(e)
|
|
@@ -1339,6 +1482,9 @@ var _default = (store, api, sharedActions) => {
|
|
|
1339
1482
|
});
|
|
1340
1483
|
callback == null ? void 0 : callback(true);
|
|
1341
1484
|
} catch (e) {
|
|
1485
|
+
if (await __redirectToSsoIfRequired(e)) {
|
|
1486
|
+
return;
|
|
1487
|
+
}
|
|
1342
1488
|
setLoginState({
|
|
1343
1489
|
loading: false,
|
|
1344
1490
|
error: (0, _helpers.errorHandler)(e)
|
|
@@ -1395,7 +1541,10 @@ var _default = (store, api, sharedActions) => {
|
|
|
1395
1541
|
};
|
|
1396
1542
|
|
|
1397
1543
|
/** @private */
|
|
1398
|
-
const __refreshTokenHostedSocialLogins = async (
|
|
1544
|
+
const __refreshTokenHostedSocialLogins = async ({
|
|
1545
|
+
tenantId,
|
|
1546
|
+
isSsoEnforcementRetry = false
|
|
1547
|
+
} = {}) => {
|
|
1399
1548
|
const {
|
|
1400
1549
|
user: currentUser
|
|
1401
1550
|
} = store.auth;
|
|
@@ -1409,7 +1558,7 @@ var _default = (store, api, sharedActions) => {
|
|
|
1409
1558
|
return;
|
|
1410
1559
|
}
|
|
1411
1560
|
try {
|
|
1412
|
-
const tabTenantId = (0, _restApi.getTabTenantFromSessionStorage)(store.root.appName);
|
|
1561
|
+
const tabTenantId = tenantId != null ? tenantId : (0, _restApi.getTabTenantFromSessionStorage)(store.root.appName);
|
|
1413
1562
|
const body = (0, _extends2.default)({
|
|
1414
1563
|
grant_type: 'refresh_token',
|
|
1415
1564
|
refresh_token: currentUser == null ? void 0 : currentUser.refreshToken
|
|
@@ -1424,6 +1573,13 @@ var _default = (store, api, sharedActions) => {
|
|
|
1424
1573
|
isAuthenticated: true
|
|
1425
1574
|
});
|
|
1426
1575
|
} catch (e) {
|
|
1576
|
+
const retry = tenantId => __refreshTokenHostedSocialLogins({
|
|
1577
|
+
tenantId,
|
|
1578
|
+
isSsoEnforcementRetry: true
|
|
1579
|
+
});
|
|
1580
|
+
if (await __handleSsoEnforcementRefreshError(e, isSsoEnforcementRetry, retry)) {
|
|
1581
|
+
return;
|
|
1582
|
+
}
|
|
1427
1583
|
contextHolder.setAccessToken(null);
|
|
1428
1584
|
contextHolder.setUser(null);
|
|
1429
1585
|
actions.setAuthState({
|
|
@@ -1494,6 +1650,9 @@ var _default = (store, api, sharedActions) => {
|
|
|
1494
1650
|
}
|
|
1495
1651
|
}
|
|
1496
1652
|
} catch (e) {
|
|
1653
|
+
if (await __redirectToSsoIfRequired(e)) {
|
|
1654
|
+
return;
|
|
1655
|
+
}
|
|
1497
1656
|
contextHolder.setAccessToken(null);
|
|
1498
1657
|
contextHolder.setUser(null);
|
|
1499
1658
|
actions.setAuthState({
|
|
@@ -1540,7 +1699,9 @@ var _default = (store, api, sharedActions) => {
|
|
|
1540
1699
|
resetBreachedPassword,
|
|
1541
1700
|
refreshTokenForSocialLogins,
|
|
1542
1701
|
// protected
|
|
1543
|
-
__refreshToken
|
|
1702
|
+
__refreshToken,
|
|
1703
|
+
__redirectToSsoForTenant,
|
|
1704
|
+
__redirectToSsoIfRequired
|
|
1544
1705
|
}, (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
1706
|
};
|
|
1546
1707
|
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)
|
|
@@ -176,6 +176,9 @@ var _default = (store, api, sharedActions) => {
|
|
|
176
176
|
}
|
|
177
177
|
callback == null ? void 0 : callback(mfaState.recoveryCode);
|
|
178
178
|
} catch (e) {
|
|
179
|
+
if (await actions.__redirectToSsoIfRequired(e)) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
179
182
|
setMfaState({
|
|
180
183
|
loading: false,
|
|
181
184
|
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'),
|
|
@@ -101,6 +101,11 @@ var _default = (store, api, sharedActions) => {
|
|
|
101
101
|
callback == null ? void 0 : callback(true);
|
|
102
102
|
} catch (e) {
|
|
103
103
|
callback == null ? void 0 : callback(false);
|
|
104
|
+
|
|
105
|
+
// the session's tenant requires SSO: its SSO login steps the user up, instead of logging them out
|
|
106
|
+
if (await actions.__redirectToSsoIfRequired(e)) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
104
109
|
await __handleStepUpError(e);
|
|
105
110
|
}
|
|
106
111
|
};
|
|
@@ -59,10 +59,11 @@ const getUrlDerivedStepUp = () => {
|
|
|
59
59
|
|
|
60
60
|
/**
|
|
61
61
|
* Set the url and query params in the local storage FRONTEGG_AFTER_AUTH_REDIRECT_URL value
|
|
62
|
+
* @param urlStrategy 'hash' saves the route in the hash, which is the page the app shows
|
|
62
63
|
*/
|
|
63
64
|
exports.getUrlDerivedStepUp = getUrlDerivedStepUp;
|
|
64
|
-
function setAfterAuthRedirectUrlForStepUp() {
|
|
65
|
-
const encodedRedirectUrl = window.location.pathname + window.location.search;
|
|
65
|
+
function setAfterAuthRedirectUrlForStepUp(urlStrategy = 'path') {
|
|
66
|
+
const encodedRedirectUrl = urlStrategy === 'hash' ? window.location.hash.substring(1) || '/' : window.location.pathname + window.location.search;
|
|
66
67
|
window.localStorage.setItem(_constants.FRONTEGG_AFTER_AUTH_REDIRECT_URL, encodedRedirectUrl);
|
|
67
68
|
}
|
|
68
69
|
|
|
@@ -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.setTabTenantForSsoSwitch)(tenantId);
|
|
64
|
+
}
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
60
67
|
callback == null ? void 0 : callback(false, e);
|
|
61
68
|
}
|
|
62
69
|
};
|