@frontegg/redux-store 7.131.0-alpha.0 → 7.131.0-alpha.2
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.js +10 -1
- package/auth/LoginState/actions/index.d.ts +15 -1
- package/auth/LoginState/actions/index.js +79 -37
- package/auth/MfaState/actions.js +3 -0
- package/auth/StepUpState/actions/generateStepUpSession.actions.js +4 -0
- package/auth/StepUpState/helpers.d.ts +2 -1
- package/auth/StepUpState/helpers.js +3 -2
- package/auth/TenantsState/actions.js +2 -2
- package/auth/helpers.d.ts +16 -0
- package/auth/helpers.js +65 -2
- package/index.js +1 -1
- package/node/auth/ActivateAccountState/actions.js +6 -0
- package/node/auth/LoginState/actions/hostedLoginAuthorize.actions.js +9 -0
- package/node/auth/LoginState/actions/index.js +77 -35
- package/node/auth/MfaState/actions.js +3 -0
- package/node/auth/StepUpState/actions/generateStepUpSession.actions.js +4 -0
- package/node/auth/StepUpState/helpers.js +3 -2
- package/node/auth/TenantsState/actions.js +1 -1
- package/node/auth/helpers.js +71 -2
- package/node/index.js +1 -1
- package/package.json +2 -2
|
@@ -151,6 +151,9 @@ export default ((store, api, sharedActions) => {
|
|
|
151
151
|
});
|
|
152
152
|
callback == null ? void 0 : callback(true);
|
|
153
153
|
} catch (e) {
|
|
154
|
+
if (await actions.__redirectToSsoIfRequired(e)) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
154
157
|
setActivateState({
|
|
155
158
|
loading: false,
|
|
156
159
|
error: errorHandler(e)
|
|
@@ -188,6 +191,9 @@ export default ((store, api, sharedActions) => {
|
|
|
188
191
|
});
|
|
189
192
|
callback == null ? void 0 : callback(true);
|
|
190
193
|
} catch (e) {
|
|
194
|
+
if (await actions.__redirectToSsoIfRequired(e)) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
191
197
|
setActivateState({
|
|
192
198
|
submitting: false,
|
|
193
199
|
loading: false,
|
|
@@ -3,7 +3,7 @@ import { createRandomString, generateCodeChallenge, GTMEventAction, isSsoEnforce
|
|
|
3
3
|
import { HOSTED_LOGIN_VERIFIER_KEY } from '../../../constants';
|
|
4
4
|
import { isEnforceSsoOnTenantAccessEnabled } from '@frontegg/rest-api';
|
|
5
5
|
import { getBaseNameWithoutSlashSuffix, getSearchParam, isOauthCallbackRoute, TENANT_ID_PARAM_KEY } from '../helpers';
|
|
6
|
-
import { getUri, getUrlDerivedStepUp, isSteppedUp, redirectByStepUpUrl } from '../../helpers';
|
|
6
|
+
import { getUri, getUrlDerivedStepUp, isSteppedUp, isOnLoginCodeCallbackRoute, redirectByStepUpUrl, restoreTabTenantUnlessSsoCallback } from '../../helpers';
|
|
7
7
|
import { SHOULD_STEP_UP_KEY } from '../../StepUpState/consts';
|
|
8
8
|
import { FronteggNativeModule } from '../../../toolkit';
|
|
9
9
|
export default ((store, api, sharedActions) => {
|
|
@@ -15,6 +15,7 @@ export default ((store, api, sharedActions) => {
|
|
|
15
15
|
const __requestHostedLoginSilentAuthorize = async () => {
|
|
16
16
|
var _response;
|
|
17
17
|
let response;
|
|
18
|
+
restoreTabTenantUnlessSsoCallback(store);
|
|
18
19
|
if (FronteggNativeModule.isGetTokensAvailable()) {
|
|
19
20
|
const authApi = api.auth;
|
|
20
21
|
response = await refreshTokenWithRetry(async () => {
|
|
@@ -26,6 +27,14 @@ export default ((store, api, sharedActions) => {
|
|
|
26
27
|
response = await api.auth.silentOAuthRefreshTokenV2();
|
|
27
28
|
} catch (error) {
|
|
28
29
|
var _store$auth$user, _error$errorContext;
|
|
30
|
+
// on the hosted login callback the page finishes the login with its code: fail as with the flag off
|
|
31
|
+
if (isOnLoginCodeCallbackRoute(store)) {
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
// the tenant requires SSO: hosted login for that tenant, not the plain one the caller falls back to
|
|
35
|
+
if (await actions.__redirectToSsoIfRequired(error)) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
29
38
|
const tenantId = (_store$auth$user = store.auth.user) == null ? void 0 : _store$auth$user.tenantId;
|
|
30
39
|
if (!isEnforceSsoOnTenantAccessEnabled(store.root.appName) || !isSsoEnforcementError(error) || ((_error$errorContext = error.errorContext) == null ? void 0 : _error$errorContext.ssoRequired) !== false || !tenantId) {
|
|
31
40
|
throw error;
|
|
@@ -33,7 +33,21 @@ 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
|
|
36
|
+
shouldRedirectToLogin
|
|
37
|
+
/**
|
|
38
|
+
* Handles a hosted (OAuth) token refresh refused because the tenant requires SSO.
|
|
39
|
+
* ssoRequired: true sends the user to SSO, like login. ssoRequired: false renews once more for the tenant of the
|
|
40
|
+
* current token, which keeps the session: OAuth doesn't use up the refresh token on this refusal.
|
|
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
|
|
50
|
+
*/: boolean;
|
|
37
51
|
firstTime?: boolean;
|
|
38
52
|
loginDirectAction?: import("../interfaces").LoginDirectAction;
|
|
39
53
|
}) => Promise<void>;
|
|
@@ -1,21 +1,20 @@
|
|
|
1
|
-
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
2
1
|
import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
3
|
+
const _excluded = ["callback", "error"],
|
|
4
|
+
_excluded2 = ["callback"],
|
|
5
5
|
_excluded3 = ["callback"],
|
|
6
6
|
_excluded4 = ["callback"],
|
|
7
7
|
_excluded5 = ["callback"],
|
|
8
|
-
_excluded6 = ["callback"],
|
|
9
|
-
_excluded7 = ["callback"
|
|
8
|
+
_excluded6 = ["callback", "events"],
|
|
9
|
+
_excluded7 = ["callback"],
|
|
10
10
|
_excluded8 = ["callback"],
|
|
11
|
-
_excluded9 = ["
|
|
12
|
-
_excluded10 = ["
|
|
11
|
+
_excluded9 = ["user"],
|
|
12
|
+
_excluded10 = ["callback"],
|
|
13
13
|
_excluded11 = ["callback"],
|
|
14
14
|
_excluded12 = ["callback"],
|
|
15
15
|
_excluded13 = ["callback"],
|
|
16
16
|
_excluded14 = ["callback"],
|
|
17
|
-
_excluded15 = ["callback"]
|
|
18
|
-
_excluded16 = ["callback"];
|
|
17
|
+
_excluded15 = ["callback"];
|
|
19
18
|
import afterAuthNavigationActions from './afterAuthNavigation.actions';
|
|
20
19
|
import handleVerifyMFAResponseActions from './handleVerifyMFAResponse.actions';
|
|
21
20
|
import mfaRequiredStateActions from './mfaRequiredState.actions';
|
|
@@ -33,8 +32,8 @@ import { clearHostedOAuthState } from '../oauthStorage';
|
|
|
33
32
|
import { SHOULD_STEP_UP_KEY } from '../../StepUpState/consts';
|
|
34
33
|
import { FronteggNativeModule, isEntitlementsDeeplyEqual } from '../../../toolkit';
|
|
35
34
|
import { REQUEST_NAME, UserVerifiedOriginTypes } from '../../interfaces';
|
|
36
|
-
import { authStrategyLoginStepMap,
|
|
37
|
-
import { isMfaRequired, isResetPasswordRequired, setAfterAuthRedirectUrlForStepUp } from '../../helpers';
|
|
35
|
+
import { authStrategyLoginStepMap, defaultOnRedirectTo } from '../consts';
|
|
36
|
+
import { isMfaRequired, isOnAuthPageRoute, isOnLoginCodeCallbackRoute, isResetPasswordRequired, restoreTabTenantUnlessSsoCallback, setAfterAuthRedirectUrlForStepUp } from '../../helpers';
|
|
38
37
|
import { MFAStep } from '../../MfaState/interfaces';
|
|
39
38
|
import { SamlVendors } from '../../SSOState/interfaces';
|
|
40
39
|
import { DEFAULT_RETRY_CONFIG } from '../../../constants';
|
|
@@ -48,17 +47,44 @@ export default ((store, api, sharedActions) => {
|
|
|
48
47
|
* Sends the user to log in through the tenant's SSO and back to the current page afterwards.
|
|
49
48
|
* @protected
|
|
50
49
|
*/
|
|
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
|
|
52
|
+
let ssoRedirectInFlight = false;
|
|
51
53
|
const __redirectToSsoForTenant = async ({
|
|
52
54
|
tenantId,
|
|
53
55
|
address
|
|
54
56
|
}) => {
|
|
57
|
+
if (ssoRedirectInFlight) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
ssoRedirectInFlight = true;
|
|
61
|
+
try {
|
|
62
|
+
await __redirectToSsoForTenantNow({
|
|
63
|
+
tenantId,
|
|
64
|
+
address
|
|
65
|
+
});
|
|
66
|
+
} finally {
|
|
67
|
+
ssoRedirectInFlight = false;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
const __redirectToSsoForTenantNow = async ({
|
|
71
|
+
tenantId,
|
|
72
|
+
address
|
|
73
|
+
}) => {
|
|
74
|
+
var _store$root$urlStrate;
|
|
75
|
+
// the login box shows login's "redirecting to SSO" step instead of the screen that was refused (its spinner and
|
|
76
|
+
// a previous error)
|
|
77
|
+
setLoginState({
|
|
78
|
+
step: LoginStep.redirectToSSO,
|
|
79
|
+
loading: false,
|
|
80
|
+
error: undefined
|
|
81
|
+
});
|
|
55
82
|
// on an auth page (login, tenant chooser, social / magic link / SSO callback) keep the destination the login
|
|
56
83
|
// already saved: the page itself is not one to come back to
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
const isOnAuthPage = Object.values(authRoutes).some(route => typeof route === 'string' && !!route && window.location.pathname.endsWith(route));
|
|
84
|
+
const urlStrategy = (_store$root$urlStrate = store.root.urlStrategy) != null ? _store$root$urlStrate : 'path';
|
|
85
|
+
const isOnAuthPage = isOnAuthPageRoute(urlStrategy, store.auth.routes);
|
|
60
86
|
if (!isOnAuthPage) {
|
|
61
|
-
setAfterAuthRedirectUrlForStepUp();
|
|
87
|
+
setAfterAuthRedirectUrlForStepUp(urlStrategy);
|
|
62
88
|
}
|
|
63
89
|
if (store.auth.hostedLoginBox) {
|
|
64
90
|
var _window$localStorage;
|
|
@@ -72,9 +98,12 @@ export default ((store, api, sharedActions) => {
|
|
|
72
98
|
});
|
|
73
99
|
return;
|
|
74
100
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
101
|
+
// as login does for an OIDC SSO page without redirect_uri; the refusal has no idpType, but an OIDC page asks for a code
|
|
102
|
+
let ssoRedirectUrl = address;
|
|
103
|
+
if (/[?&]response_type=code(&|$)/.test(address) && !address.includes('redirect_uri')) {
|
|
104
|
+
ssoRedirectUrl += `&redirect_uri=${window.location.origin}${store.auth.routes.oidcRedirectUrl}`;
|
|
105
|
+
}
|
|
106
|
+
redirectToFullPage(ssoRedirectUrl);
|
|
78
107
|
};
|
|
79
108
|
|
|
80
109
|
/**
|
|
@@ -84,7 +113,8 @@ export default ((store, api, sharedActions) => {
|
|
|
84
113
|
* @protected
|
|
85
114
|
*/
|
|
86
115
|
const __redirectToSsoIfRequired = async e => {
|
|
87
|
-
|
|
116
|
+
var _e$errorContext;
|
|
117
|
+
if (!isEnforceSsoOnTenantAccessEnabled(store.root.appName) || !isSsoEnforcementError(e) || !((_e$errorContext = e.errorContext) != null && _e$errorContext.ssoRequired)) {
|
|
88
118
|
return false;
|
|
89
119
|
}
|
|
90
120
|
await __redirectToSsoForTenant(e.errorContext);
|
|
@@ -99,7 +129,8 @@ export default ((store, api, sharedActions) => {
|
|
|
99
129
|
*/
|
|
100
130
|
const __handleSsoEnforcementRefreshError = async (e, isSsoEnforcementRetry, refreshForTenant) => {
|
|
101
131
|
var _store$auth$user;
|
|
102
|
-
|
|
132
|
+
// on a login callback the page finishes the login with its code: log out as with the flag off, no new SSO trip
|
|
133
|
+
if (!isEnforceSsoOnTenantAccessEnabled(store.root.appName) || !isSsoEnforcementError(e) || isOnLoginCodeCallbackRoute(store)) {
|
|
103
134
|
return false;
|
|
104
135
|
}
|
|
105
136
|
if (await __redirectToSsoIfRequired(e)) {
|
|
@@ -215,8 +246,9 @@ export default ((store, api, sharedActions) => {
|
|
|
215
246
|
});
|
|
216
247
|
}
|
|
217
248
|
} catch (e) {
|
|
218
|
-
// a refused identity refresh has already used up the refresh token: no retry, only the SSO redirect
|
|
219
|
-
|
|
249
|
+
// a refused identity refresh has already used up the refresh token: no retry, only the SSO redirect, except on
|
|
250
|
+
// a login callback, where the page itself completes the login
|
|
251
|
+
if (isEnforceSsoOnTenantAccessEnabled(store.root.appName) && !isOnLoginCodeCallbackRoute(store) && (await __redirectToSsoIfRequired(e))) {
|
|
220
252
|
return;
|
|
221
253
|
}
|
|
222
254
|
contextHolder.setAccessToken(null);
|
|
@@ -263,6 +295,7 @@ export default ((store, api, sharedActions) => {
|
|
|
263
295
|
|
|
264
296
|
/** @protected */
|
|
265
297
|
const __refreshToken = async () => {
|
|
298
|
+
restoreTabTenantUnlessSsoCallback(store);
|
|
266
299
|
if (FronteggNativeModule.isGetTokensAvailable()) {
|
|
267
300
|
await __refreshTokenWithNativeTokens();
|
|
268
301
|
return;
|
|
@@ -455,7 +488,7 @@ export default ((store, api, sharedActions) => {
|
|
|
455
488
|
callback,
|
|
456
489
|
error
|
|
457
490
|
} = _ref3,
|
|
458
|
-
body = _objectWithoutPropertiesLoose(_ref3,
|
|
491
|
+
body = _objectWithoutPropertiesLoose(_ref3, _excluded);
|
|
459
492
|
const publicPolicy = store.auth.securityPolicyState.publicPolicy.policy;
|
|
460
493
|
if (!(publicPolicy != null && publicPolicy.authStrategy)) {
|
|
461
494
|
actions.setLoginState({
|
|
@@ -851,7 +884,7 @@ export default ((store, api, sharedActions) => {
|
|
|
851
884
|
const {
|
|
852
885
|
callback
|
|
853
886
|
} = _payload,
|
|
854
|
-
payload = _objectWithoutPropertiesLoose(_payload,
|
|
887
|
+
payload = _objectWithoutPropertiesLoose(_payload, _excluded2);
|
|
855
888
|
try {
|
|
856
889
|
setLoginState({
|
|
857
890
|
loading: true
|
|
@@ -881,7 +914,7 @@ export default ((store, api, sharedActions) => {
|
|
|
881
914
|
const {
|
|
882
915
|
callback
|
|
883
916
|
} = _payload,
|
|
884
|
-
payload = _objectWithoutPropertiesLoose(_payload,
|
|
917
|
+
payload = _objectWithoutPropertiesLoose(_payload, _excluded3);
|
|
885
918
|
try {
|
|
886
919
|
setLoginState({
|
|
887
920
|
loading: true
|
|
@@ -906,7 +939,7 @@ export default ((store, api, sharedActions) => {
|
|
|
906
939
|
const {
|
|
907
940
|
callback
|
|
908
941
|
} = _payload,
|
|
909
|
-
payload = _objectWithoutPropertiesLoose(_payload,
|
|
942
|
+
payload = _objectWithoutPropertiesLoose(_payload, _excluded4);
|
|
910
943
|
try {
|
|
911
944
|
setLoginState({
|
|
912
945
|
loading: true
|
|
@@ -927,7 +960,7 @@ export default ((store, api, sharedActions) => {
|
|
|
927
960
|
const {
|
|
928
961
|
callback
|
|
929
962
|
} = _payload,
|
|
930
|
-
payload = _objectWithoutPropertiesLoose(_payload,
|
|
963
|
+
payload = _objectWithoutPropertiesLoose(_payload, _excluded5);
|
|
931
964
|
try {
|
|
932
965
|
var _username;
|
|
933
966
|
setLoginState({
|
|
@@ -971,7 +1004,7 @@ export default ((store, api, sharedActions) => {
|
|
|
971
1004
|
callback,
|
|
972
1005
|
events
|
|
973
1006
|
} = _payload,
|
|
974
|
-
payload = _objectWithoutPropertiesLoose(_payload,
|
|
1007
|
+
payload = _objectWithoutPropertiesLoose(_payload, _excluded6);
|
|
975
1008
|
try {
|
|
976
1009
|
setLoginState({
|
|
977
1010
|
loading: true
|
|
@@ -1102,7 +1135,7 @@ export default ((store, api, sharedActions) => {
|
|
|
1102
1135
|
const {
|
|
1103
1136
|
callback
|
|
1104
1137
|
} = payload,
|
|
1105
|
-
body = _objectWithoutPropertiesLoose(payload,
|
|
1138
|
+
body = _objectWithoutPropertiesLoose(payload, _excluded7);
|
|
1106
1139
|
try {
|
|
1107
1140
|
var _options$allowCredent;
|
|
1108
1141
|
actions.setPasskeysState({
|
|
@@ -1142,7 +1175,7 @@ export default ((store, api, sharedActions) => {
|
|
|
1142
1175
|
const {
|
|
1143
1176
|
callback
|
|
1144
1177
|
} = payload,
|
|
1145
|
-
body = _objectWithoutPropertiesLoose(payload,
|
|
1178
|
+
body = _objectWithoutPropertiesLoose(payload, _excluded8);
|
|
1146
1179
|
try {
|
|
1147
1180
|
var _publicKey$response$u;
|
|
1148
1181
|
actions.setPasskeysState({
|
|
@@ -1162,7 +1195,7 @@ export default ((store, api, sharedActions) => {
|
|
|
1162
1195
|
{
|
|
1163
1196
|
user
|
|
1164
1197
|
} = _await$api$auth$webAu,
|
|
1165
|
-
rest = _objectWithoutPropertiesLoose(_await$api$auth$webAu,
|
|
1198
|
+
rest = _objectWithoutPropertiesLoose(_await$api$auth$webAu, _excluded9);
|
|
1166
1199
|
if (isMfaRequired(user, store.root.appName)) {
|
|
1167
1200
|
const {
|
|
1168
1201
|
routes,
|
|
@@ -1249,7 +1282,7 @@ export default ((store, api, sharedActions) => {
|
|
|
1249
1282
|
const {
|
|
1250
1283
|
callback
|
|
1251
1284
|
} = payload,
|
|
1252
|
-
body = _objectWithoutPropertiesLoose(payload,
|
|
1285
|
+
body = _objectWithoutPropertiesLoose(payload, _excluded10);
|
|
1253
1286
|
try {
|
|
1254
1287
|
setLoginState({
|
|
1255
1288
|
loading: true
|
|
@@ -1314,7 +1347,7 @@ export default ((store, api, sharedActions) => {
|
|
|
1314
1347
|
const {
|
|
1315
1348
|
callback
|
|
1316
1349
|
} = _payload,
|
|
1317
|
-
payload = _objectWithoutPropertiesLoose(_payload,
|
|
1350
|
+
payload = _objectWithoutPropertiesLoose(_payload, _excluded11);
|
|
1318
1351
|
setLoginState({
|
|
1319
1352
|
loading: true
|
|
1320
1353
|
});
|
|
@@ -1349,7 +1382,7 @@ export default ((store, api, sharedActions) => {
|
|
|
1349
1382
|
const {
|
|
1350
1383
|
callback
|
|
1351
1384
|
} = _payload,
|
|
1352
|
-
payload = _objectWithoutPropertiesLoose(_payload,
|
|
1385
|
+
payload = _objectWithoutPropertiesLoose(_payload, _excluded12);
|
|
1353
1386
|
setLoginState({
|
|
1354
1387
|
loading: true
|
|
1355
1388
|
});
|
|
@@ -1364,6 +1397,9 @@ export default ((store, api, sharedActions) => {
|
|
|
1364
1397
|
});
|
|
1365
1398
|
callback == null ? void 0 : callback(true);
|
|
1366
1399
|
} catch (e) {
|
|
1400
|
+
if (await __redirectToSsoIfRequired(e)) {
|
|
1401
|
+
return;
|
|
1402
|
+
}
|
|
1367
1403
|
setLoginState({
|
|
1368
1404
|
loading: false,
|
|
1369
1405
|
error: errorHandler(e)
|
|
@@ -1375,7 +1411,7 @@ export default ((store, api, sharedActions) => {
|
|
|
1375
1411
|
const {
|
|
1376
1412
|
callback
|
|
1377
1413
|
} = _payload,
|
|
1378
|
-
payload = _objectWithoutPropertiesLoose(_payload,
|
|
1414
|
+
payload = _objectWithoutPropertiesLoose(_payload, _excluded13);
|
|
1379
1415
|
setLoginState({
|
|
1380
1416
|
loading: true
|
|
1381
1417
|
});
|
|
@@ -1387,6 +1423,9 @@ export default ((store, api, sharedActions) => {
|
|
|
1387
1423
|
});
|
|
1388
1424
|
callback == null ? void 0 : callback(true);
|
|
1389
1425
|
} catch (e) {
|
|
1426
|
+
if (await __redirectToSsoIfRequired(e)) {
|
|
1427
|
+
return;
|
|
1428
|
+
}
|
|
1390
1429
|
setLoginState({
|
|
1391
1430
|
loading: false,
|
|
1392
1431
|
error: errorHandler(e)
|
|
@@ -1398,7 +1437,7 @@ export default ((store, api, sharedActions) => {
|
|
|
1398
1437
|
const {
|
|
1399
1438
|
callback
|
|
1400
1439
|
} = _payload,
|
|
1401
|
-
payload = _objectWithoutPropertiesLoose(_payload,
|
|
1440
|
+
payload = _objectWithoutPropertiesLoose(_payload, _excluded14);
|
|
1402
1441
|
setLoginState({
|
|
1403
1442
|
loading: true
|
|
1404
1443
|
});
|
|
@@ -1425,7 +1464,7 @@ export default ((store, api, sharedActions) => {
|
|
|
1425
1464
|
const {
|
|
1426
1465
|
callback
|
|
1427
1466
|
} = _payload,
|
|
1428
|
-
payload = _objectWithoutPropertiesLoose(_payload,
|
|
1467
|
+
payload = _objectWithoutPropertiesLoose(_payload, _excluded15);
|
|
1429
1468
|
setLoginState({
|
|
1430
1469
|
loading: true
|
|
1431
1470
|
});
|
|
@@ -1437,6 +1476,9 @@ export default ((store, api, sharedActions) => {
|
|
|
1437
1476
|
});
|
|
1438
1477
|
callback == null ? void 0 : callback(true);
|
|
1439
1478
|
} catch (e) {
|
|
1479
|
+
if (await __redirectToSsoIfRequired(e)) {
|
|
1480
|
+
return;
|
|
1481
|
+
}
|
|
1440
1482
|
setLoginState({
|
|
1441
1483
|
loading: false,
|
|
1442
1484
|
error: errorHandler(e)
|
package/auth/MfaState/actions.js
CHANGED
|
@@ -169,6 +169,9 @@ export default ((store, api, sharedActions) => {
|
|
|
169
169
|
}
|
|
170
170
|
callback == null ? void 0 : callback(mfaState.recoveryCode);
|
|
171
171
|
} catch (e) {
|
|
172
|
+
if (await actions.__redirectToSsoIfRequired(e)) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
172
175
|
setMfaState({
|
|
173
176
|
loading: false,
|
|
174
177
|
error: errorHandler(e)
|
|
@@ -94,6 +94,10 @@ export default ((store, api, sharedActions) => {
|
|
|
94
94
|
}
|
|
95
95
|
callback == null ? void 0 : callback(true);
|
|
96
96
|
} catch (e) {
|
|
97
|
+
// the session's tenant requires SSO: its SSO login steps the user up, instead of logging them out
|
|
98
|
+
if (await actions.__redirectToSsoIfRequired(e)) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
97
101
|
callback == null ? void 0 : callback(false);
|
|
98
102
|
await __handleStepUpError(e);
|
|
99
103
|
}
|
|
@@ -22,8 +22,9 @@ export interface UrlDerivedStepUp {
|
|
|
22
22
|
export declare const getUrlDerivedStepUp: () => UrlDerivedStepUp;
|
|
23
23
|
/**
|
|
24
24
|
* Set the url and query params in the local storage FRONTEGG_AFTER_AUTH_REDIRECT_URL value
|
|
25
|
+
* @param urlStrategy 'hash' saves the route in the hash, which is the page the app shows
|
|
25
26
|
*/
|
|
26
|
-
export declare function setAfterAuthRedirectUrlForStepUp(): void;
|
|
27
|
+
export declare function setAfterAuthRedirectUrlForStepUp(urlStrategy?: 'path' | 'hash'): void;
|
|
27
28
|
/**
|
|
28
29
|
* Redirects to the step up url with the max age param and set the redirect url in the local storage
|
|
29
30
|
* The redirect url will be used after the step up flow is done
|
|
@@ -51,9 +51,10 @@ export const getUrlDerivedStepUp = () => {
|
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
53
|
* Set the url and query params in the local storage FRONTEGG_AFTER_AUTH_REDIRECT_URL value
|
|
54
|
+
* @param urlStrategy 'hash' saves the route in the hash, which is the page the app shows
|
|
54
55
|
*/
|
|
55
|
-
export function setAfterAuthRedirectUrlForStepUp() {
|
|
56
|
-
const encodedRedirectUrl = window.location.pathname + window.location.search;
|
|
56
|
+
export function setAfterAuthRedirectUrlForStepUp(urlStrategy = 'path') {
|
|
57
|
+
const encodedRedirectUrl = urlStrategy === 'hash' ? window.location.hash.substring(1) || '/' : window.location.pathname + window.location.search;
|
|
57
58
|
window.localStorage.setItem(FRONTEGG_AFTER_AUTH_REDIRECT_URL, encodedRedirectUrl);
|
|
58
59
|
}
|
|
59
60
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { initialState } from './state';
|
|
2
2
|
import { deepResetState } from '../../helpers';
|
|
3
|
-
import { ContextHolder, getCurrentUserTenantsFunction,
|
|
3
|
+
import { ContextHolder, getCurrentUserTenantsFunction, setTabTenantForSsoSwitch } from '@frontegg/rest-api';
|
|
4
4
|
export default ((store, api, sharedActions) => {
|
|
5
5
|
const actions = sharedActions;
|
|
6
6
|
const setTenantsState = state => {
|
|
@@ -54,7 +54,7 @@ export default ((store, api, sharedActions) => {
|
|
|
54
54
|
if (await actions.__redirectToSsoIfRequired(e)) {
|
|
55
55
|
// the tab comes back from SSO in the target tenant, not the one the failed switch restored
|
|
56
56
|
if (ContextHolder.for(store.root.appName).isSessionPerTenantEnabled()) {
|
|
57
|
-
|
|
57
|
+
setTabTenantForSsoSwitch(tenantId);
|
|
58
58
|
}
|
|
59
59
|
return;
|
|
60
60
|
}
|
package/auth/helpers.d.ts
CHANGED
|
@@ -8,5 +8,21 @@ export declare const isMfaRequired: (user: ILoginResponse, appName: string) => b
|
|
|
8
8
|
export declare const isResetPasswordRequired: (user: ILoginResponse, appName: string) => boolean;
|
|
9
9
|
export declare const mapMetaDataObjectToActions: (obj: any, path?: string[]) => CommitChangeDto[];
|
|
10
10
|
export declare const getUri: (urlStrategy: FronteggState["root"]["urlStrategy"]) => string;
|
|
11
|
+
/**
|
|
12
|
+
* On the OIDC or hosted login callback the page still has to finish the login with its code: a refresh refused before
|
|
13
|
+
* that is not a reason to go to SSO again. On the OIDC callback the IdP would send the user straight back here, forever;
|
|
14
|
+
* on the hosted callback a new hosted login would replace the PKCE verifier the code exchange needs.
|
|
15
|
+
*/
|
|
16
|
+
export declare const isOnLoginCodeCallbackRoute: (store: FronteggState) => boolean;
|
|
17
|
+
/**
|
|
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
|
+
* keeps it; any other page load means the user didn't finish that login, so the tab goes back to its tenant.
|
|
20
|
+
*/
|
|
21
|
+
export declare const restoreTabTenantUnlessSsoCallback: (store: FronteggState) => void;
|
|
11
22
|
export declare const prepareIdentifiersForSignup: (payload: ISignUpUser) => ISignUpUser;
|
|
23
|
+
/**
|
|
24
|
+
* Whether the current route is an auth page (login, tenant chooser, social / magic link / SSO callback), not one to come
|
|
25
|
+
* back to. Unlike isAuthRoute: after-login / sign-up destinations don't count, and endsWith lets a basename prefix it.
|
|
26
|
+
*/
|
|
27
|
+
export declare const isOnAuthPageRoute: (urlStrategy: FronteggState["root"]["urlStrategy"], routes?: Partial<AuthPageRoutes>) => boolean;
|
|
12
28
|
export declare const isAuthRoute: (path: string, routes?: Partial<AuthPageRoutes>) => boolean;
|
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 } 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,57 @@ 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 or hosted login callback the page still has to finish the login with its code: a refresh refused before
|
|
71
|
+
* that is not a reason to go to SSO again. On the OIDC callback the IdP would send the user straight back here, forever;
|
|
72
|
+
* on the hosted callback a new hosted login would replace the PKCE verifier the code exchange needs.
|
|
73
|
+
*/
|
|
74
|
+
export const isOnLoginCodeCallbackRoute = store => {
|
|
75
|
+
var _store$root$urlStrate;
|
|
76
|
+
const {
|
|
77
|
+
oidcRedirectUrl,
|
|
78
|
+
hostedLoginRedirectUrl
|
|
79
|
+
} = _extends({}, defaultFronteggRoutes, store.auth.routes);
|
|
80
|
+
const currentRoutes = getCurrentRoutes((_store$root$urlStrate = store.root.urlStrategy) != null ? _store$root$urlStrate : 'path');
|
|
81
|
+
return [oidcRedirectUrl, hostedLoginRedirectUrl].some(route => !!route && currentRoutes.some(currentRoute => currentRoute.endsWith(route)));
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/**
|
|
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
|
+
* keeps it; any other page load means the user didn't finish that login, so the tab goes back to its tenant.
|
|
87
|
+
*/
|
|
88
|
+
export const restoreTabTenantUnlessSsoCallback = store => {
|
|
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
|
+
if (!ContextHolder.for(store.root.appName).isSessionPerTenantEnabled()) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const {
|
|
96
|
+
samlCallbackUrl,
|
|
97
|
+
oidcRedirectUrl,
|
|
98
|
+
hostedLoginRedirectUrl
|
|
99
|
+
} = _extends({}, defaultFronteggRoutes, store.auth.routes);
|
|
100
|
+
// matched like login matches its callback pages: endsWith, so a basename before the route still counts
|
|
101
|
+
const currentRoutes = getCurrentRoutes((_store$root$urlStrate2 = store.root.urlStrategy) != null ? _store$root$urlStrate2 : 'path');
|
|
102
|
+
const isSsoCallback = [samlCallbackUrl, oidcRedirectUrl, hostedLoginRedirectUrl].some(route => !!route && currentRoutes.some(currentRoute => currentRoute.endsWith(route)));
|
|
103
|
+
if (!isSsoCallback) {
|
|
104
|
+
restoreTabTenantBeforeSsoSwitch();
|
|
105
|
+
}
|
|
106
|
+
};
|
|
56
107
|
export const prepareIdentifiersForSignup = payload => {
|
|
57
108
|
const {
|
|
58
109
|
phoneNumber,
|
|
@@ -77,6 +128,18 @@ export const prepareIdentifiersForSignup = payload => {
|
|
|
77
128
|
}
|
|
78
129
|
return finalDto;
|
|
79
130
|
};
|
|
131
|
+
|
|
132
|
+
// Pages the user lands on after login or sign-up: destinations, not auth pages.
|
|
133
|
+
const AFTER_AUTH_DESTINATION_ROUTES = ['authenticatedUrl', 'signUpSuccessUrl', 'accountSignUpSuccessUrl', 'userSignUpSuccessUrl', 'customLoginAuthenticatedUrl'];
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Whether the current route is an auth page (login, tenant chooser, social / magic link / SSO callback), not one to come
|
|
137
|
+
* back to. Unlike isAuthRoute: after-login / sign-up destinations don't count, and endsWith lets a basename prefix it.
|
|
138
|
+
*/
|
|
139
|
+
export const isOnAuthPageRoute = (urlStrategy, routes) => {
|
|
140
|
+
const currentRoutes = getCurrentRoutes(urlStrategy);
|
|
141
|
+
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)));
|
|
142
|
+
};
|
|
80
143
|
export const isAuthRoute = (path, routes) => {
|
|
81
144
|
const _defaultFronteggRoute = _extends({}, defaultFronteggRoutes, routes),
|
|
82
145
|
authRoutes = _objectWithoutPropertiesLoose(_defaultFronteggRoute, _excluded2);
|
package/index.js
CHANGED
|
@@ -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,
|
|
@@ -22,6 +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
26
|
if (_toolkit.FronteggNativeModule.isGetTokensAvailable()) {
|
|
26
27
|
const authApi = api.auth;
|
|
27
28
|
response = await (0, _helpers.refreshTokenWithRetry)(async () => {
|
|
@@ -33,6 +34,14 @@ var _default = (store, api, sharedActions) => {
|
|
|
33
34
|
response = await api.auth.silentOAuthRefreshTokenV2();
|
|
34
35
|
} catch (error) {
|
|
35
36
|
var _store$auth$user, _error$errorContext;
|
|
37
|
+
// on the hosted login callback the page finishes the login with its code: fail as with the flag off
|
|
38
|
+
if ((0, _helpers3.isOnLoginCodeCallbackRoute)(store)) {
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
// the tenant requires SSO: hosted login for that tenant, not the plain one the caller falls back to
|
|
42
|
+
if (await actions.__redirectToSsoIfRequired(error)) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
36
45
|
const tenantId = (_store$auth$user = store.auth.user) == null ? void 0 : _store$auth$user.tenantId;
|
|
37
46
|
if (!(0, _restApi.isEnforceSsoOnTenantAccessEnabled)(store.root.appName) || !(0, _helpers.isSsoEnforcementError)(error) || ((_error$errorContext = error.errorContext) == null ? void 0 : _error$errorContext.ssoRequired) !== false || !tenantId) {
|
|
38
47
|
throw error;
|
|
@@ -5,8 +5,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
value: true
|
|
6
6
|
});
|
|
7
7
|
exports.default = void 0;
|
|
8
|
-
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
|
|
9
8
|
var _objectWithoutPropertiesLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutPropertiesLoose"));
|
|
9
|
+
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
|
|
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,22 +29,21 @@ 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 = ["
|
|
33
|
-
_excluded2 = ["callback"
|
|
32
|
+
const _excluded = ["callback", "error"],
|
|
33
|
+
_excluded2 = ["callback"],
|
|
34
34
|
_excluded3 = ["callback"],
|
|
35
35
|
_excluded4 = ["callback"],
|
|
36
36
|
_excluded5 = ["callback"],
|
|
37
|
-
_excluded6 = ["callback"],
|
|
38
|
-
_excluded7 = ["callback"
|
|
37
|
+
_excluded6 = ["callback", "events"],
|
|
38
|
+
_excluded7 = ["callback"],
|
|
39
39
|
_excluded8 = ["callback"],
|
|
40
|
-
_excluded9 = ["
|
|
41
|
-
_excluded10 = ["
|
|
40
|
+
_excluded9 = ["user"],
|
|
41
|
+
_excluded10 = ["callback"],
|
|
42
42
|
_excluded11 = ["callback"],
|
|
43
43
|
_excluded12 = ["callback"],
|
|
44
44
|
_excluded13 = ["callback"],
|
|
45
45
|
_excluded14 = ["callback"],
|
|
46
|
-
_excluded15 = ["callback"]
|
|
47
|
-
_excluded16 = ["callback"];
|
|
46
|
+
_excluded15 = ["callback"];
|
|
48
47
|
const SSO_REDIRECT_DELAY_MS = 2000;
|
|
49
48
|
const isUsableRedirect = onRedirectTo => typeof onRedirectTo === 'function' && onRedirectTo !== _consts2.defaultOnRedirectTo;
|
|
50
49
|
var _default = (store, api, sharedActions) => {
|
|
@@ -55,17 +54,44 @@ var _default = (store, api, sharedActions) => {
|
|
|
55
54
|
* Sends the user to log in through the tenant's SSO and back to the current page afterwards.
|
|
56
55
|
* @protected
|
|
57
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;
|
|
58
60
|
const __redirectToSsoForTenant = async ({
|
|
59
61
|
tenantId,
|
|
60
62
|
address
|
|
61
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
|
+
});
|
|
62
89
|
// on an auth page (login, tenant chooser, social / magic link / SSO callback) keep the destination the login
|
|
63
90
|
// already saved: the page itself is not one to come back to
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
const isOnAuthPage = Object.values(authRoutes).some(route => typeof route === 'string' && !!route && window.location.pathname.endsWith(route));
|
|
91
|
+
const urlStrategy = (_store$root$urlStrate = store.root.urlStrategy) != null ? _store$root$urlStrate : 'path';
|
|
92
|
+
const isOnAuthPage = (0, _helpers3.isOnAuthPageRoute)(urlStrategy, store.auth.routes);
|
|
67
93
|
if (!isOnAuthPage) {
|
|
68
|
-
(0, _helpers3.setAfterAuthRedirectUrlForStepUp)();
|
|
94
|
+
(0, _helpers3.setAfterAuthRedirectUrlForStepUp)(urlStrategy);
|
|
69
95
|
}
|
|
70
96
|
if (store.auth.hostedLoginBox) {
|
|
71
97
|
var _window$localStorage;
|
|
@@ -79,9 +105,12 @@ var _default = (store, api, sharedActions) => {
|
|
|
79
105
|
});
|
|
80
106
|
return;
|
|
81
107
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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);
|
|
85
114
|
};
|
|
86
115
|
|
|
87
116
|
/**
|
|
@@ -91,7 +120,8 @@ var _default = (store, api, sharedActions) => {
|
|
|
91
120
|
* @protected
|
|
92
121
|
*/
|
|
93
122
|
const __redirectToSsoIfRequired = async e => {
|
|
94
|
-
|
|
123
|
+
var _e$errorContext;
|
|
124
|
+
if (!(0, _restApi.isEnforceSsoOnTenantAccessEnabled)(store.root.appName) || !(0, _helpers.isSsoEnforcementError)(e) || !((_e$errorContext = e.errorContext) != null && _e$errorContext.ssoRequired)) {
|
|
95
125
|
return false;
|
|
96
126
|
}
|
|
97
127
|
await __redirectToSsoForTenant(e.errorContext);
|
|
@@ -106,7 +136,8 @@ var _default = (store, api, sharedActions) => {
|
|
|
106
136
|
*/
|
|
107
137
|
const __handleSsoEnforcementRefreshError = async (e, isSsoEnforcementRetry, refreshForTenant) => {
|
|
108
138
|
var _store$auth$user;
|
|
109
|
-
|
|
139
|
+
// on a login callback the page finishes the login with its code: log out as with the flag off, no new SSO trip
|
|
140
|
+
if (!(0, _restApi.isEnforceSsoOnTenantAccessEnabled)(store.root.appName) || !(0, _helpers.isSsoEnforcementError)(e) || (0, _helpers3.isOnLoginCodeCallbackRoute)(store)) {
|
|
110
141
|
return false;
|
|
111
142
|
}
|
|
112
143
|
if (await __redirectToSsoIfRequired(e)) {
|
|
@@ -222,8 +253,9 @@ var _default = (store, api, sharedActions) => {
|
|
|
222
253
|
});
|
|
223
254
|
}
|
|
224
255
|
} catch (e) {
|
|
225
|
-
// a refused identity refresh has already used up the refresh token: no retry, only the SSO redirect
|
|
226
|
-
|
|
256
|
+
// a refused identity refresh has already used up the refresh token: no retry, only the SSO redirect, except on
|
|
257
|
+
// a login callback, where the page itself completes the login
|
|
258
|
+
if ((0, _restApi.isEnforceSsoOnTenantAccessEnabled)(store.root.appName) && !(0, _helpers3.isOnLoginCodeCallbackRoute)(store) && (await __redirectToSsoIfRequired(e))) {
|
|
227
259
|
return;
|
|
228
260
|
}
|
|
229
261
|
contextHolder.setAccessToken(null);
|
|
@@ -270,6 +302,7 @@ var _default = (store, api, sharedActions) => {
|
|
|
270
302
|
|
|
271
303
|
/** @protected */
|
|
272
304
|
const __refreshToken = async () => {
|
|
305
|
+
(0, _helpers3.restoreTabTenantUnlessSsoCallback)(store);
|
|
273
306
|
if (_toolkit.FronteggNativeModule.isGetTokensAvailable()) {
|
|
274
307
|
await __refreshTokenWithNativeTokens();
|
|
275
308
|
return;
|
|
@@ -462,7 +495,7 @@ var _default = (store, api, sharedActions) => {
|
|
|
462
495
|
callback,
|
|
463
496
|
error
|
|
464
497
|
} = _ref3,
|
|
465
|
-
body = (0, _objectWithoutPropertiesLoose2.default)(_ref3,
|
|
498
|
+
body = (0, _objectWithoutPropertiesLoose2.default)(_ref3, _excluded);
|
|
466
499
|
const publicPolicy = store.auth.securityPolicyState.publicPolicy.policy;
|
|
467
500
|
if (!(publicPolicy != null && publicPolicy.authStrategy)) {
|
|
468
501
|
actions.setLoginState({
|
|
@@ -858,7 +891,7 @@ var _default = (store, api, sharedActions) => {
|
|
|
858
891
|
const {
|
|
859
892
|
callback
|
|
860
893
|
} = _payload,
|
|
861
|
-
payload = (0, _objectWithoutPropertiesLoose2.default)(_payload,
|
|
894
|
+
payload = (0, _objectWithoutPropertiesLoose2.default)(_payload, _excluded2);
|
|
862
895
|
try {
|
|
863
896
|
setLoginState({
|
|
864
897
|
loading: true
|
|
@@ -888,7 +921,7 @@ var _default = (store, api, sharedActions) => {
|
|
|
888
921
|
const {
|
|
889
922
|
callback
|
|
890
923
|
} = _payload,
|
|
891
|
-
payload = (0, _objectWithoutPropertiesLoose2.default)(_payload,
|
|
924
|
+
payload = (0, _objectWithoutPropertiesLoose2.default)(_payload, _excluded3);
|
|
892
925
|
try {
|
|
893
926
|
setLoginState({
|
|
894
927
|
loading: true
|
|
@@ -913,7 +946,7 @@ var _default = (store, api, sharedActions) => {
|
|
|
913
946
|
const {
|
|
914
947
|
callback
|
|
915
948
|
} = _payload,
|
|
916
|
-
payload = (0, _objectWithoutPropertiesLoose2.default)(_payload,
|
|
949
|
+
payload = (0, _objectWithoutPropertiesLoose2.default)(_payload, _excluded4);
|
|
917
950
|
try {
|
|
918
951
|
setLoginState({
|
|
919
952
|
loading: true
|
|
@@ -934,7 +967,7 @@ var _default = (store, api, sharedActions) => {
|
|
|
934
967
|
const {
|
|
935
968
|
callback
|
|
936
969
|
} = _payload,
|
|
937
|
-
payload = (0, _objectWithoutPropertiesLoose2.default)(_payload,
|
|
970
|
+
payload = (0, _objectWithoutPropertiesLoose2.default)(_payload, _excluded5);
|
|
938
971
|
try {
|
|
939
972
|
var _username;
|
|
940
973
|
setLoginState({
|
|
@@ -978,7 +1011,7 @@ var _default = (store, api, sharedActions) => {
|
|
|
978
1011
|
callback,
|
|
979
1012
|
events
|
|
980
1013
|
} = _payload,
|
|
981
|
-
payload = (0, _objectWithoutPropertiesLoose2.default)(_payload,
|
|
1014
|
+
payload = (0, _objectWithoutPropertiesLoose2.default)(_payload, _excluded6);
|
|
982
1015
|
try {
|
|
983
1016
|
setLoginState({
|
|
984
1017
|
loading: true
|
|
@@ -1109,7 +1142,7 @@ var _default = (store, api, sharedActions) => {
|
|
|
1109
1142
|
const {
|
|
1110
1143
|
callback
|
|
1111
1144
|
} = payload,
|
|
1112
|
-
body = (0, _objectWithoutPropertiesLoose2.default)(payload,
|
|
1145
|
+
body = (0, _objectWithoutPropertiesLoose2.default)(payload, _excluded7);
|
|
1113
1146
|
try {
|
|
1114
1147
|
var _options$allowCredent;
|
|
1115
1148
|
actions.setPasskeysState({
|
|
@@ -1149,7 +1182,7 @@ var _default = (store, api, sharedActions) => {
|
|
|
1149
1182
|
const {
|
|
1150
1183
|
callback
|
|
1151
1184
|
} = payload,
|
|
1152
|
-
body = (0, _objectWithoutPropertiesLoose2.default)(payload,
|
|
1185
|
+
body = (0, _objectWithoutPropertiesLoose2.default)(payload, _excluded8);
|
|
1153
1186
|
try {
|
|
1154
1187
|
var _publicKey$response$u;
|
|
1155
1188
|
actions.setPasskeysState({
|
|
@@ -1169,7 +1202,7 @@ var _default = (store, api, sharedActions) => {
|
|
|
1169
1202
|
{
|
|
1170
1203
|
user
|
|
1171
1204
|
} = _await$api$auth$webAu,
|
|
1172
|
-
rest = (0, _objectWithoutPropertiesLoose2.default)(_await$api$auth$webAu,
|
|
1205
|
+
rest = (0, _objectWithoutPropertiesLoose2.default)(_await$api$auth$webAu, _excluded9);
|
|
1173
1206
|
if ((0, _helpers3.isMfaRequired)(user, store.root.appName)) {
|
|
1174
1207
|
const {
|
|
1175
1208
|
routes,
|
|
@@ -1256,7 +1289,7 @@ var _default = (store, api, sharedActions) => {
|
|
|
1256
1289
|
const {
|
|
1257
1290
|
callback
|
|
1258
1291
|
} = payload,
|
|
1259
|
-
body = (0, _objectWithoutPropertiesLoose2.default)(payload,
|
|
1292
|
+
body = (0, _objectWithoutPropertiesLoose2.default)(payload, _excluded10);
|
|
1260
1293
|
try {
|
|
1261
1294
|
setLoginState({
|
|
1262
1295
|
loading: true
|
|
@@ -1321,7 +1354,7 @@ var _default = (store, api, sharedActions) => {
|
|
|
1321
1354
|
const {
|
|
1322
1355
|
callback
|
|
1323
1356
|
} = _payload,
|
|
1324
|
-
payload = (0, _objectWithoutPropertiesLoose2.default)(_payload,
|
|
1357
|
+
payload = (0, _objectWithoutPropertiesLoose2.default)(_payload, _excluded11);
|
|
1325
1358
|
setLoginState({
|
|
1326
1359
|
loading: true
|
|
1327
1360
|
});
|
|
@@ -1356,7 +1389,7 @@ var _default = (store, api, sharedActions) => {
|
|
|
1356
1389
|
const {
|
|
1357
1390
|
callback
|
|
1358
1391
|
} = _payload,
|
|
1359
|
-
payload = (0, _objectWithoutPropertiesLoose2.default)(_payload,
|
|
1392
|
+
payload = (0, _objectWithoutPropertiesLoose2.default)(_payload, _excluded12);
|
|
1360
1393
|
setLoginState({
|
|
1361
1394
|
loading: true
|
|
1362
1395
|
});
|
|
@@ -1371,6 +1404,9 @@ var _default = (store, api, sharedActions) => {
|
|
|
1371
1404
|
});
|
|
1372
1405
|
callback == null ? void 0 : callback(true);
|
|
1373
1406
|
} catch (e) {
|
|
1407
|
+
if (await __redirectToSsoIfRequired(e)) {
|
|
1408
|
+
return;
|
|
1409
|
+
}
|
|
1374
1410
|
setLoginState({
|
|
1375
1411
|
loading: false,
|
|
1376
1412
|
error: (0, _helpers.errorHandler)(e)
|
|
@@ -1382,7 +1418,7 @@ var _default = (store, api, sharedActions) => {
|
|
|
1382
1418
|
const {
|
|
1383
1419
|
callback
|
|
1384
1420
|
} = _payload,
|
|
1385
|
-
payload = (0, _objectWithoutPropertiesLoose2.default)(_payload,
|
|
1421
|
+
payload = (0, _objectWithoutPropertiesLoose2.default)(_payload, _excluded13);
|
|
1386
1422
|
setLoginState({
|
|
1387
1423
|
loading: true
|
|
1388
1424
|
});
|
|
@@ -1394,6 +1430,9 @@ var _default = (store, api, sharedActions) => {
|
|
|
1394
1430
|
});
|
|
1395
1431
|
callback == null ? void 0 : callback(true);
|
|
1396
1432
|
} catch (e) {
|
|
1433
|
+
if (await __redirectToSsoIfRequired(e)) {
|
|
1434
|
+
return;
|
|
1435
|
+
}
|
|
1397
1436
|
setLoginState({
|
|
1398
1437
|
loading: false,
|
|
1399
1438
|
error: (0, _helpers.errorHandler)(e)
|
|
@@ -1405,7 +1444,7 @@ var _default = (store, api, sharedActions) => {
|
|
|
1405
1444
|
const {
|
|
1406
1445
|
callback
|
|
1407
1446
|
} = _payload,
|
|
1408
|
-
payload = (0, _objectWithoutPropertiesLoose2.default)(_payload,
|
|
1447
|
+
payload = (0, _objectWithoutPropertiesLoose2.default)(_payload, _excluded14);
|
|
1409
1448
|
setLoginState({
|
|
1410
1449
|
loading: true
|
|
1411
1450
|
});
|
|
@@ -1432,7 +1471,7 @@ var _default = (store, api, sharedActions) => {
|
|
|
1432
1471
|
const {
|
|
1433
1472
|
callback
|
|
1434
1473
|
} = _payload,
|
|
1435
|
-
payload = (0, _objectWithoutPropertiesLoose2.default)(_payload,
|
|
1474
|
+
payload = (0, _objectWithoutPropertiesLoose2.default)(_payload, _excluded15);
|
|
1436
1475
|
setLoginState({
|
|
1437
1476
|
loading: true
|
|
1438
1477
|
});
|
|
@@ -1444,6 +1483,9 @@ var _default = (store, api, sharedActions) => {
|
|
|
1444
1483
|
});
|
|
1445
1484
|
callback == null ? void 0 : callback(true);
|
|
1446
1485
|
} catch (e) {
|
|
1486
|
+
if (await __redirectToSsoIfRequired(e)) {
|
|
1487
|
+
return;
|
|
1488
|
+
}
|
|
1447
1489
|
setLoginState({
|
|
1448
1490
|
loading: false,
|
|
1449
1491
|
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)
|
|
@@ -100,6 +100,10 @@ var _default = (store, api, sharedActions) => {
|
|
|
100
100
|
}
|
|
101
101
|
callback == null ? void 0 : callback(true);
|
|
102
102
|
} catch (e) {
|
|
103
|
+
// the session's tenant requires SSO: its SSO login steps the user up, instead of logging them out
|
|
104
|
+
if (await actions.__redirectToSsoIfRequired(e)) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
103
107
|
callback == null ? void 0 : callback(false);
|
|
104
108
|
await __handleStepUpError(e);
|
|
105
109
|
}
|
|
@@ -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
|
|
|
@@ -60,7 +60,7 @@ var _default = (store, api, sharedActions) => {
|
|
|
60
60
|
if (await actions.__redirectToSsoIfRequired(e)) {
|
|
61
61
|
// the tab comes back from SSO in the target tenant, not the one the failed switch restored
|
|
62
62
|
if (_restApi.ContextHolder.for(store.root.appName).isSessionPerTenantEnabled()) {
|
|
63
|
-
(0, _restApi.
|
|
63
|
+
(0, _restApi.setTabTenantForSsoSwitch)(tenantId);
|
|
64
64
|
}
|
|
65
65
|
return;
|
|
66
66
|
}
|
package/node/auth/helpers.js
CHANGED
|
@@ -9,12 +9,15 @@ var _exportNames = {
|
|
|
9
9
|
isResetPasswordRequired: true,
|
|
10
10
|
mapMetaDataObjectToActions: true,
|
|
11
11
|
getUri: true,
|
|
12
|
+
isOnLoginCodeCallbackRoute: true,
|
|
13
|
+
restoreTabTenantUnlessSsoCallback: true,
|
|
12
14
|
prepareIdentifiersForSignup: true,
|
|
15
|
+
isOnAuthPageRoute: true,
|
|
13
16
|
isAuthRoute: true
|
|
14
17
|
};
|
|
15
|
-
exports.prepareIdentifiersForSignup = exports.mapMetaDataObjectToActions = exports.isResetPasswordRequired = exports.isMfaRequired = exports.isAuthRoute = exports.getUri = void 0;
|
|
16
|
-
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
|
|
18
|
+
exports.restoreTabTenantUnlessSsoCallback = exports.prepareIdentifiersForSignup = exports.mapMetaDataObjectToActions = exports.isResetPasswordRequired = exports.isOnLoginCodeCallbackRoute = exports.isOnAuthPageRoute = exports.isMfaRequired = exports.isAuthRoute = exports.getUri = void 0;
|
|
17
19
|
var _objectWithoutPropertiesLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutPropertiesLoose"));
|
|
20
|
+
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
|
|
18
21
|
var _restApi = require("@frontegg/rest-api");
|
|
19
22
|
var _consts = require("./LoginState/consts");
|
|
20
23
|
var _helpers = require("./Entitlements/helpers");
|
|
@@ -104,7 +107,60 @@ const getUri = urlStrategy => {
|
|
|
104
107
|
}
|
|
105
108
|
}
|
|
106
109
|
};
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* The route the app shows and the page's path. With hash routing they differ: the app's pages live in the hash, but
|
|
113
|
+
* SAML, OIDC, social and magic link callbacks come back on the path, which is where login matches them.
|
|
114
|
+
*/
|
|
107
115
|
exports.getUri = getUri;
|
|
116
|
+
const getCurrentRoutes = urlStrategy => {
|
|
117
|
+
const hashOrPathRoute = getUri(urlStrategy);
|
|
118
|
+
const {
|
|
119
|
+
pathname
|
|
120
|
+
} = window.location;
|
|
121
|
+
return hashOrPathRoute === pathname ? [pathname] : [hashOrPathRoute, pathname];
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* On the OIDC or hosted login callback the page still has to finish the login with its code: a refresh refused before
|
|
126
|
+
* that is not a reason to go to SSO again. On the OIDC callback the IdP would send the user straight back here, forever;
|
|
127
|
+
* on the hosted callback a new hosted login would replace the PKCE verifier the code exchange needs.
|
|
128
|
+
*/
|
|
129
|
+
const isOnLoginCodeCallbackRoute = store => {
|
|
130
|
+
var _store$root$urlStrate;
|
|
131
|
+
const {
|
|
132
|
+
oidcRedirectUrl,
|
|
133
|
+
hostedLoginRedirectUrl
|
|
134
|
+
} = (0, _extends2.default)({}, _consts.defaultFronteggRoutes, store.auth.routes);
|
|
135
|
+
const currentRoutes = getCurrentRoutes((_store$root$urlStrate = store.root.urlStrategy) != null ? _store$root$urlStrate : 'path');
|
|
136
|
+
return [oidcRedirectUrl, hostedLoginRedirectUrl].some(route => !!route && currentRoutes.some(currentRoute => currentRoute.endsWith(route)));
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* After a switch refused for SSO the tab points at the SSO tenant. Its SSO login comes back on a callback route and
|
|
141
|
+
* keeps it; any other page load means the user didn't finish that login, so the tab goes back to its tenant.
|
|
142
|
+
*/
|
|
143
|
+
exports.isOnLoginCodeCallbackRoute = isOnLoginCodeCallbackRoute;
|
|
144
|
+
const restoreTabTenantUnlessSsoCallback = store => {
|
|
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
|
+
if (!_restApi.ContextHolder.for(store.root.appName).isSessionPerTenantEnabled()) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const {
|
|
152
|
+
samlCallbackUrl,
|
|
153
|
+
oidcRedirectUrl,
|
|
154
|
+
hostedLoginRedirectUrl
|
|
155
|
+
} = (0, _extends2.default)({}, _consts.defaultFronteggRoutes, store.auth.routes);
|
|
156
|
+
// matched like login matches its callback pages: endsWith, so a basename before the route still counts
|
|
157
|
+
const currentRoutes = getCurrentRoutes((_store$root$urlStrate2 = store.root.urlStrategy) != null ? _store$root$urlStrate2 : 'path');
|
|
158
|
+
const isSsoCallback = [samlCallbackUrl, oidcRedirectUrl, hostedLoginRedirectUrl].some(route => !!route && currentRoutes.some(currentRoute => currentRoute.endsWith(route)));
|
|
159
|
+
if (!isSsoCallback) {
|
|
160
|
+
(0, _restApi.restoreTabTenantBeforeSsoSwitch)();
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
exports.restoreTabTenantUnlessSsoCallback = restoreTabTenantUnlessSsoCallback;
|
|
108
164
|
const prepareIdentifiersForSignup = payload => {
|
|
109
165
|
const {
|
|
110
166
|
phoneNumber,
|
|
@@ -129,7 +185,20 @@ const prepareIdentifiersForSignup = payload => {
|
|
|
129
185
|
}
|
|
130
186
|
return finalDto;
|
|
131
187
|
};
|
|
188
|
+
|
|
189
|
+
// Pages the user lands on after login or sign-up: destinations, not auth pages.
|
|
132
190
|
exports.prepareIdentifiersForSignup = prepareIdentifiersForSignup;
|
|
191
|
+
const AFTER_AUTH_DESTINATION_ROUTES = ['authenticatedUrl', 'signUpSuccessUrl', 'accountSignUpSuccessUrl', 'userSignUpSuccessUrl', 'customLoginAuthenticatedUrl'];
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Whether the current route is an auth page (login, tenant chooser, social / magic link / SSO callback), not one to come
|
|
195
|
+
* back to. Unlike isAuthRoute: after-login / sign-up destinations don't count, and endsWith lets a basename prefix it.
|
|
196
|
+
*/
|
|
197
|
+
const isOnAuthPageRoute = (urlStrategy, routes) => {
|
|
198
|
+
const currentRoutes = getCurrentRoutes(urlStrategy);
|
|
199
|
+
return Object.entries((0, _extends2.default)({}, _consts.defaultFronteggRoutes, routes)).some(([name, route]) => !AFTER_AUTH_DESTINATION_ROUTES.includes(name) && typeof route === 'string' && !!route && currentRoutes.some(currentRoute => currentRoute.endsWith(route)));
|
|
200
|
+
};
|
|
201
|
+
exports.isOnAuthPageRoute = isOnAuthPageRoute;
|
|
133
202
|
const isAuthRoute = (path, routes) => {
|
|
134
203
|
const _defaultFronteggRoute = (0, _extends2.default)({}, _consts.defaultFronteggRoutes, routes),
|
|
135
204
|
authRoutes = (0, _objectWithoutPropertiesLoose2.default)(_defaultFronteggRoute, _excluded2);
|
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-alpha.
|
|
3
|
+
"version": "7.131.0-alpha.2",
|
|
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-alpha.
|
|
10
|
+
"@frontegg/rest-api": "7.131.0-alpha.2",
|
|
11
11
|
"fast-deep-equal": "3.1.3",
|
|
12
12
|
"get-value": "^3.0.1",
|
|
13
13
|
"proxy-compare": "^3.0.0",
|