@frontegg/redux-store 6.155.0-alpha.1 → 6.155.0-alpha.3
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/ActivateState/saga.js +3 -2
- package/auth/ApiTokensState/index.d.ts +0 -8
- package/auth/LoginState/index.d.ts +1 -1
- package/auth/LoginState/index.js +1 -1
- package/auth/LoginState/interfaces.d.ts +5 -0
- package/auth/LoginState/saga.d.ts +3 -17
- package/auth/LoginState/saga.js +19 -383
- package/auth/LoginState/saga.utils.d.ts +12 -0
- package/auth/LoginState/saga.utils.js +47 -0
- package/auth/LoginState/sagas/afterAuthNavigation.saga.d.ts +27 -0
- package/auth/LoginState/sagas/afterAuthNavigation.saga.js +115 -0
- package/auth/LoginState/sagas/handleVerifyMFAResponse.saga.d.ts +54 -0
- package/auth/LoginState/sagas/handleVerifyMFAResponse.saga.js +80 -0
- package/auth/LoginState/sagas/index.d.ts +7 -0
- package/auth/LoginState/sagas/index.js +7 -0
- package/auth/LoginState/{mfaRequiredState.saga.d.ts → sagas/mfaRequiredState.saga.d.ts} +3 -3
- package/auth/LoginState/{mfaRequiredState.saga.js → sagas/mfaRequiredState.saga.js} +3 -3
- package/auth/LoginState/sagas/mfaWithAuthenticator.saga.d.ts +42 -0
- package/auth/LoginState/sagas/mfaWithAuthenticator.saga.js +140 -0
- package/auth/LoginState/sagas/mfaWithEmailCode.saga.d.ts +73 -0
- package/auth/LoginState/sagas/mfaWithEmailCode.saga.js +96 -0
- package/auth/LoginState/sagas/mfaWithSMS.saga.d.ts +79 -0
- package/auth/LoginState/sagas/mfaWithSMS.saga.js +107 -0
- package/auth/LoginState/sagas/mfaWithWebAuthn.saga.d.ts +83 -0
- package/auth/LoginState/sagas/mfaWithWebAuthn.saga.js +119 -0
- package/auth/LoginState/utils.d.ts +2 -1
- package/auth/LoginState/utils.js +6 -1
- package/auth/MfaState/interfaces.d.ts +2 -1
- package/auth/MfaState/interfaces.js +1 -0
- package/auth/MfaState/saga.js +1 -1
- package/auth/SignUp/saga.js +1 -1
- package/auth/StepUpState/consts.d.ts +17 -0
- package/auth/StepUpState/consts.js +20 -0
- package/auth/StepUpState/generateStepUpSession.saga.d.ts +14 -0
- package/auth/StepUpState/generateStepUpSession.saga.js +102 -0
- package/auth/StepUpState/index.d.ts +17 -3
- package/auth/StepUpState/index.js +22 -2
- package/auth/StepUpState/interfaces.d.ts +9 -0
- package/auth/StepUpState/saga.d.ts +125 -11
- package/auth/StepUpState/saga.js +95 -70
- package/auth/StepUpState/utils.d.ts +15 -3
- package/auth/StepUpState/utils.js +24 -10
- package/auth/index.d.ts +6 -0
- package/auth/interfaces.d.ts +1 -0
- package/auth/reducer.d.ts +6 -0
- package/index.d.ts +1 -1
- package/index.js +2 -2
- package/node/auth/ActivateState/saga.js +3 -2
- package/node/auth/LoginState/index.js +6 -0
- package/node/auth/LoginState/saga.js +54 -410
- package/node/auth/LoginState/saga.utils.js +55 -0
- package/node/auth/LoginState/sagas/afterAuthNavigation.saga.js +122 -0
- package/node/auth/LoginState/sagas/handleVerifyMFAResponse.saga.js +87 -0
- package/node/auth/LoginState/sagas/index.js +82 -0
- package/node/auth/LoginState/{mfaRequiredState.saga.js → sagas/mfaRequiredState.saga.js} +3 -3
- package/node/auth/LoginState/sagas/mfaWithAuthenticator.saga.js +147 -0
- package/node/auth/LoginState/sagas/mfaWithEmailCode.saga.js +106 -0
- package/node/auth/LoginState/sagas/mfaWithSMS.saga.js +116 -0
- package/node/auth/LoginState/sagas/mfaWithWebAuthn.saga.js +128 -0
- package/node/auth/LoginState/utils.js +6 -1
- package/node/auth/MfaState/interfaces.js +1 -0
- package/node/auth/MfaState/saga.js +1 -1
- package/node/auth/SignUp/saga.js +1 -1
- package/node/auth/StepUpState/consts.js +30 -0
- package/node/auth/StepUpState/generateStepUpSession.saga.js +107 -0
- package/node/auth/StepUpState/index.js +37 -7
- package/node/auth/StepUpState/saga.js +99 -71
- package/node/auth/StepUpState/utils.js +26 -14
- package/node/index.js +5 -5
- package/package.json +2 -2
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { ContextHolder } from '@frontegg/rest-api';
|
|
2
|
+
import { delay, put, select, call } from 'redux-saga/effects';
|
|
3
|
+
import { loadCustomLoginRoutes } from '../../CustomLoginState/saga';
|
|
4
|
+
import { actions } from '../../reducer';
|
|
5
|
+
import { getPathAndSearchParamsFromUrl, getRedirectUrl } from '../utils';
|
|
6
|
+
import { FRONTEGG_AFTER_AUTH_REDIRECT_URL } from '../../../constants';
|
|
7
|
+
import { isSteppedUp } from '../../StepUpState';
|
|
8
|
+
import { SHOULD_STEP_UP_KEY } from '../../StepUpState/consts';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param customLoginAuthenticatedUrl custom login authenticated url if exists
|
|
12
|
+
* @returns the authenticated url to redirect to after auth navigation
|
|
13
|
+
*/
|
|
14
|
+
function* getUrlForAfterAuthNavigation(customLoginAuthenticatedUrl) {
|
|
15
|
+
const {
|
|
16
|
+
routes,
|
|
17
|
+
includeQueryParam,
|
|
18
|
+
enforceRedirectToSameSite = false,
|
|
19
|
+
allowedRedirectOrigins = []
|
|
20
|
+
} = yield select(state => state.auth);
|
|
21
|
+
if (customLoginAuthenticatedUrl) {
|
|
22
|
+
return getPathAndSearchParamsFromUrl(customLoginAuthenticatedUrl);
|
|
23
|
+
}
|
|
24
|
+
const {
|
|
25
|
+
authenticatedUrl,
|
|
26
|
+
loginUrl,
|
|
27
|
+
logoutUrl,
|
|
28
|
+
socialLoginCallbackUrl,
|
|
29
|
+
activateUrl
|
|
30
|
+
} = routes;
|
|
31
|
+
let finalUrl = window.localStorage.getItem(FRONTEGG_AFTER_AUTH_REDIRECT_URL);
|
|
32
|
+
if (!finalUrl || [loginUrl, logoutUrl, socialLoginCallbackUrl, activateUrl].includes(finalUrl)) {
|
|
33
|
+
finalUrl = authenticatedUrl;
|
|
34
|
+
}
|
|
35
|
+
return getRedirectUrl({
|
|
36
|
+
authenticatedUrl: finalUrl,
|
|
37
|
+
includeQueryParam,
|
|
38
|
+
enforceRedirectToSameSite,
|
|
39
|
+
allowedRedirectOrigins
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Utility to share after auth navigation flow between login and step up
|
|
44
|
+
* @param resetStateAction reset state action
|
|
45
|
+
* @param customLoginAuthenticatedUrl custom login authenticated url if exists
|
|
46
|
+
*/
|
|
47
|
+
export function* afterAuthNavigationUtil(resetStateAction, {
|
|
48
|
+
customLoginAuthenticatedUrl,
|
|
49
|
+
forceStepUpUrl
|
|
50
|
+
} = {}) {
|
|
51
|
+
const onRedirectTo = ContextHolder.onRedirectTo;
|
|
52
|
+
let redirectUrl;
|
|
53
|
+
if (forceStepUpUrl) {
|
|
54
|
+
// scenario to get to here: invalid max age, try to step up -> logout, login with magic code/link -> redirect to step up page for email code as the second factor
|
|
55
|
+
// we don't want to remove the FRONTEGG_AFTER_AUTH_REDIRECT_URL when we are in the step up flow
|
|
56
|
+
redirectUrl = forceStepUpUrl;
|
|
57
|
+
} else {
|
|
58
|
+
var _window;
|
|
59
|
+
redirectUrl = yield call(getUrlForAfterAuthNavigation, customLoginAuthenticatedUrl);
|
|
60
|
+
(_window = window) == null ? void 0 : _window.localStorage.removeItem(FRONTEGG_AFTER_AUTH_REDIRECT_URL);
|
|
61
|
+
}
|
|
62
|
+
yield delay(200);
|
|
63
|
+
put(resetStateAction());
|
|
64
|
+
onRedirectTo(redirectUrl, {
|
|
65
|
+
refresh: redirectUrl.startsWith('http')
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* After auth navigation for login flow
|
|
71
|
+
* Handling also step up scenario when user silently logout to continue to step up
|
|
72
|
+
*/
|
|
73
|
+
export function* afterAuthNavigation() {
|
|
74
|
+
var _window2;
|
|
75
|
+
const {
|
|
76
|
+
routes: {
|
|
77
|
+
customLoginAuthenticatedUrl,
|
|
78
|
+
stepUpUrl
|
|
79
|
+
}
|
|
80
|
+
} = yield select(state => state.auth);
|
|
81
|
+
|
|
82
|
+
// login with magic code, try to step up, no other mfa, invalid max age, force_enroll -> logout, login with first factor, not-stepped up jwt -> navigate to step up
|
|
83
|
+
const shouldStepUp = (_window2 = window) == null ? void 0 : _window2.localStorage.getItem(SHOULD_STEP_UP_KEY);
|
|
84
|
+
const user = yield select(({
|
|
85
|
+
auth
|
|
86
|
+
}) => auth.user);
|
|
87
|
+
if (shouldStepUp) {
|
|
88
|
+
var _window3;
|
|
89
|
+
(_window3 = window) == null ? void 0 : _window3.localStorage.removeItem(SHOULD_STEP_UP_KEY);
|
|
90
|
+
}
|
|
91
|
+
if (stepUpUrl && shouldStepUp && !isSteppedUp(user)) {
|
|
92
|
+
yield call(afterAuthNavigationUtil, actions.resetLoginState, {
|
|
93
|
+
forceStepUpUrl: stepUpUrl
|
|
94
|
+
});
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
let customLoginURL = customLoginAuthenticatedUrl;
|
|
98
|
+
if (!customLoginAuthenticatedUrl) {
|
|
99
|
+
yield call(loadCustomLoginRoutes);
|
|
100
|
+
customLoginURL = yield select(state => {
|
|
101
|
+
var _state$auth$routes;
|
|
102
|
+
return (_state$auth$routes = state.auth.routes) == null ? void 0 : _state$auth$routes.customLoginAuthenticatedUrl;
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
yield call(afterAuthNavigationUtil, actions.resetLoginState, {
|
|
106
|
+
customLoginAuthenticatedUrl: customLoginURL
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* After auth navigation for step up flow
|
|
112
|
+
*/
|
|
113
|
+
export function* afterStepUpAuthNavigation() {
|
|
114
|
+
yield call(afterAuthNavigationUtil, actions.resetStepUpState);
|
|
115
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { ILoginResponseV3 } from '@frontegg/rest-api';
|
|
2
|
+
import { AuthState } from '../../interfaces';
|
|
3
|
+
/**
|
|
4
|
+
* Additional steps for after MFA authentication with authenticator app handler for step up flow
|
|
5
|
+
*/
|
|
6
|
+
export declare function postHandleVerifyMFAResponseForStepUp(): Generator<import("redux-saga/effects").CallEffect<void>, void, unknown>;
|
|
7
|
+
/**
|
|
8
|
+
* Additional steps for after MFA authentication with authenticator app handler for login flow
|
|
9
|
+
* @param isAuthenticated
|
|
10
|
+
*/
|
|
11
|
+
export declare function postHandleVerifyMFAResponseForLogin(isAuthenticated: boolean): Generator<import("redux-saga/effects").SelectEffect | import("redux-saga/effects").CallEffect<boolean[]> | Generator<import("redux-saga/effects").SelectEffect | import("redux-saga/effects").CallEffect<void>, void, {
|
|
12
|
+
routes: {
|
|
13
|
+
customLoginAuthenticatedUrl: any;
|
|
14
|
+
stepUpUrl: any;
|
|
15
|
+
};
|
|
16
|
+
} & import("../../interfaces").User & string> | import("redux-saga/effects").PutEffect<{
|
|
17
|
+
payload: Partial<import("../interfaces").LoginState>;
|
|
18
|
+
type: string;
|
|
19
|
+
}> | import("redux-saga/effects").CallEffect<boolean>, void, (AuthState & {
|
|
20
|
+
step: any;
|
|
21
|
+
} & boolean[] & false) | (AuthState & {
|
|
22
|
+
step: any;
|
|
23
|
+
} & boolean[] & true)>;
|
|
24
|
+
/**
|
|
25
|
+
* Handle after MFA authentication with authenticator app
|
|
26
|
+
* @param payload.user
|
|
27
|
+
* @param payload.tenants
|
|
28
|
+
* @param payload.activeTenant
|
|
29
|
+
* @param isStepUp
|
|
30
|
+
*
|
|
31
|
+
* When using this saga, you should wrap it with try/catch block and handle according to your logic
|
|
32
|
+
*/
|
|
33
|
+
export declare function handleVerifyMFAResponse({ user, tenants, activeTenant }: ILoginResponseV3, isStepUp?: boolean): Generator<import("redux-saga/effects").PutEffect<{
|
|
34
|
+
payload: Partial<AuthState>;
|
|
35
|
+
type: string;
|
|
36
|
+
}> | import("redux-saga/effects").CallEffect<void> | import("redux-saga/effects").PutEffect<{
|
|
37
|
+
payload: import("../../interfaces").User;
|
|
38
|
+
type: string;
|
|
39
|
+
}> | import("redux-saga/effects").PutEffect<{
|
|
40
|
+
payload: Partial<import("../..").TenantsState>;
|
|
41
|
+
type: string;
|
|
42
|
+
}> | import("redux-saga/effects").CallEffect<Generator<import("redux-saga/effects").SelectEffect | import("redux-saga/effects").CallEffect<boolean[]> | Generator<import("redux-saga/effects").SelectEffect | import("redux-saga/effects").CallEffect<void>, void, {
|
|
43
|
+
routes: {
|
|
44
|
+
customLoginAuthenticatedUrl: any;
|
|
45
|
+
stepUpUrl: any;
|
|
46
|
+
};
|
|
47
|
+
} & import("../../interfaces").User & string> | import("redux-saga/effects").PutEffect<{
|
|
48
|
+
payload: Partial<import("../interfaces").LoginState>;
|
|
49
|
+
type: string;
|
|
50
|
+
}> | import("redux-saga/effects").CallEffect<boolean>, void, (AuthState & {
|
|
51
|
+
step: any;
|
|
52
|
+
} & boolean[] & false) | (AuthState & {
|
|
53
|
+
step: any;
|
|
54
|
+
} & boolean[] & true)>>, void, unknown>;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { select, put, call } from 'redux-saga/effects';
|
|
2
|
+
import { MFAStep } from '../../MfaState/interfaces';
|
|
3
|
+
import { actions } from '../../reducer';
|
|
4
|
+
import { LoginFlow, LoginStep } from '../interfaces';
|
|
5
|
+
import { shouldShowPromptPasskeys } from '../saga.utils';
|
|
6
|
+
import { afterStepUpAuthNavigation, afterAuthNavigation } from './afterAuthNavigation.saga';
|
|
7
|
+
import { getFeatureFlags } from '../../../helpers';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Additional steps for after MFA authentication with authenticator app handler for step up flow
|
|
11
|
+
*/
|
|
12
|
+
export function* postHandleVerifyMFAResponseForStepUp() {
|
|
13
|
+
yield call(afterStepUpAuthNavigation);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Additional steps for after MFA authentication with authenticator app handler for login flow
|
|
18
|
+
* @param isAuthenticated
|
|
19
|
+
*/
|
|
20
|
+
export function* postHandleVerifyMFAResponseForLogin(isAuthenticated) {
|
|
21
|
+
const {
|
|
22
|
+
loginState
|
|
23
|
+
} = yield select(state => state.auth);
|
|
24
|
+
const {
|
|
25
|
+
step: mfaStep
|
|
26
|
+
} = yield select(state => state.auth.mfaState);
|
|
27
|
+
const [securityCenterLoginFlows] = yield call(getFeatureFlags, ['security-center-show-login-flows']);
|
|
28
|
+
if (loginState.flow === LoginFlow.Login) {
|
|
29
|
+
if (securityCenterLoginFlows && loginState.isBreachedPassword && !isAuthenticated) {
|
|
30
|
+
yield put(actions.setLoginState({
|
|
31
|
+
step: LoginStep.breachedPassword,
|
|
32
|
+
loading: false
|
|
33
|
+
}));
|
|
34
|
+
} else {
|
|
35
|
+
const shouldShowPrompt = yield call(shouldShowPromptPasskeys);
|
|
36
|
+
if (mfaStep === MFAStep.smsVerifyCode && shouldShowPrompt) {
|
|
37
|
+
yield put(actions.setLoginState({
|
|
38
|
+
step: LoginStep.promptPasskeys,
|
|
39
|
+
loading: false
|
|
40
|
+
}));
|
|
41
|
+
} else {
|
|
42
|
+
yield afterAuthNavigation();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Handle after MFA authentication with authenticator app
|
|
50
|
+
* @param payload.user
|
|
51
|
+
* @param payload.tenants
|
|
52
|
+
* @param payload.activeTenant
|
|
53
|
+
* @param isStepUp
|
|
54
|
+
*
|
|
55
|
+
* When using this saga, you should wrap it with try/catch block and handle according to your logic
|
|
56
|
+
*/
|
|
57
|
+
export function* handleVerifyMFAResponse({
|
|
58
|
+
user,
|
|
59
|
+
tenants,
|
|
60
|
+
activeTenant
|
|
61
|
+
}, isStepUp = false) {
|
|
62
|
+
yield put(actions.setUser(user));
|
|
63
|
+
yield put(actions.setTenantsState({
|
|
64
|
+
tenants,
|
|
65
|
+
activeTenant,
|
|
66
|
+
loading: false
|
|
67
|
+
}));
|
|
68
|
+
if (user.id) {
|
|
69
|
+
localStorage.setItem('userId', user.id);
|
|
70
|
+
}
|
|
71
|
+
const isAuthenticated = !!user.accessToken;
|
|
72
|
+
yield put(actions.setState({
|
|
73
|
+
isAuthenticated
|
|
74
|
+
}));
|
|
75
|
+
if (isStepUp) {
|
|
76
|
+
yield call(postHandleVerifyMFAResponseForStepUp);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
yield call(postHandleVerifyMFAResponseForLogin, isAuthenticated);
|
|
80
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from './afterAuthNavigation.saga';
|
|
2
|
+
export * from './handleVerifyMFAResponse.saga';
|
|
3
|
+
export * from './mfaWithSMS.saga';
|
|
4
|
+
export * from './mfaRequiredState.saga';
|
|
5
|
+
export * from './mfaWithAuthenticator.saga';
|
|
6
|
+
export * from './mfaWithEmailCode.saga';
|
|
7
|
+
export * from './mfaWithWebAuthn.saga';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from './afterAuthNavigation.saga';
|
|
2
|
+
export * from './handleVerifyMFAResponse.saga';
|
|
3
|
+
export * from './mfaWithSMS.saga';
|
|
4
|
+
export * from './mfaRequiredState.saga';
|
|
5
|
+
export * from './mfaWithAuthenticator.saga';
|
|
6
|
+
export * from './mfaWithEmailCode.saga';
|
|
7
|
+
export * from './mfaWithWebAuthn.saga';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { LoginStep, QuickLoginStrategy, LoginFlow } from '
|
|
2
|
-
import { MFAState } from '
|
|
3
|
-
import { AuthState } from '
|
|
1
|
+
import { LoginStep, QuickLoginStrategy, LoginFlow } from '../interfaces';
|
|
2
|
+
import { MFAState } from '../../MfaState/interfaces';
|
|
3
|
+
import { AuthState } from '../../interfaces';
|
|
4
4
|
export declare function getMfaRequiredState(user: any): Generator<import("redux-saga/effects").SelectEffect | import("redux-saga/effects").CallEffect<import("@frontegg/rest-api").IAllowedToRememberMfaDevice>, {
|
|
5
5
|
user: undefined;
|
|
6
6
|
isAuthenticated: boolean;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
2
2
|
import { call, select } from 'redux-saga/effects';
|
|
3
3
|
import { api } from '@frontegg/rest-api';
|
|
4
|
-
import { getMfaStepForEnrolledUsers, getMfaStepForNotEnrolledUsers } from '
|
|
5
|
-
import { LoginStep, LoginFlow } from '
|
|
6
|
-
// Separated
|
|
4
|
+
import { getMfaStepForEnrolledUsers, getMfaStepForNotEnrolledUsers } from '../utils';
|
|
5
|
+
import { LoginStep, LoginFlow } from '../interfaces';
|
|
6
|
+
// Separated folder due to circular dependency
|
|
7
7
|
|
|
8
8
|
export function* getMfaRequiredState(user) {
|
|
9
9
|
let step = LoginStep.loginWithTwoFactor;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { PayloadAction } from '@reduxjs/toolkit';
|
|
2
|
+
import { ILoginWithMfa, ILoginResponseV3 } from '@frontegg/rest-api';
|
|
3
|
+
import { AuthState } from '../../interfaces';
|
|
4
|
+
import { SetLoadingAction } from '../interfaces';
|
|
5
|
+
import { WithCallback } from '../../../interfaces';
|
|
6
|
+
/**
|
|
7
|
+
* Utility function to handle MFA authentication with authenticator app
|
|
8
|
+
* @param payload MFA with authenticator action payload
|
|
9
|
+
* @param setLoadingAction set loading action
|
|
10
|
+
* @param isStepUp true if this is a step up authentication
|
|
11
|
+
* @returns
|
|
12
|
+
*/
|
|
13
|
+
export declare function mfaWithAuthenticator({ callback, ...loginWithMfaPayload }: WithCallback<ILoginWithMfa>, setLoadingAction: SetLoadingAction, isStepUp: boolean): Generator<import("redux-saga/effects").CallEffect<void> | import("redux-saga/effects").CallEffect<ILoginResponseV3> | import("redux-saga/effects").PutEffect<import("redux").Action<any>> | import("redux-saga/effects").CallEffect<{
|
|
14
|
+
isAuthenticated: boolean;
|
|
15
|
+
}> | import("redux-saga/effects").CallEffect<Generator<import("redux-saga/effects").SelectEffect | import("redux-saga/effects").CallEffect<boolean[]> | Generator<import("redux-saga/effects").SelectEffect | import("redux-saga/effects").CallEffect<void>, void, {
|
|
16
|
+
routes: {
|
|
17
|
+
customLoginAuthenticatedUrl: any;
|
|
18
|
+
stepUpUrl: any;
|
|
19
|
+
};
|
|
20
|
+
} & import("../../interfaces").User & string> | import("redux-saga/effects").PutEffect<{
|
|
21
|
+
payload: Partial<import("../interfaces").LoginState>;
|
|
22
|
+
type: string;
|
|
23
|
+
}> | import("redux-saga/effects").CallEffect<boolean>, void, (AuthState & boolean[] & false) | (AuthState & boolean[] & true)>>, void, ILoginResponseV3 & Partial<AuthState>>;
|
|
24
|
+
/**
|
|
25
|
+
* Login with MFA with authenticator app - external saga
|
|
26
|
+
* @param payload.mfaToken
|
|
27
|
+
* @param payload.mfaDevices
|
|
28
|
+
* @param payload.rememberDevice
|
|
29
|
+
* @param payload.invitationToken
|
|
30
|
+
* @param payload.callback - The callback function to be called after the request is done
|
|
31
|
+
*/
|
|
32
|
+
export declare function loginWithMfa({ payload }: PayloadAction<WithCallback<ILoginWithMfa>>): Generator<Generator<import("redux-saga/effects").CallEffect<void> | import("redux-saga/effects").CallEffect<ILoginResponseV3> | import("redux-saga/effects").PutEffect<import("redux").Action<any>> | import("redux-saga/effects").CallEffect<{
|
|
33
|
+
isAuthenticated: boolean;
|
|
34
|
+
}> | import("redux-saga/effects").CallEffect<Generator<import("redux-saga/effects").SelectEffect | import("redux-saga/effects").CallEffect<boolean[]> | Generator<import("redux-saga/effects").SelectEffect | import("redux-saga/effects").CallEffect<void>, void, {
|
|
35
|
+
routes: {
|
|
36
|
+
customLoginAuthenticatedUrl: any;
|
|
37
|
+
stepUpUrl: any;
|
|
38
|
+
};
|
|
39
|
+
} & import("../../interfaces").User & string> | import("redux-saga/effects").PutEffect<{
|
|
40
|
+
payload: Partial<import("../interfaces").LoginState>;
|
|
41
|
+
type: string;
|
|
42
|
+
}> | import("redux-saga/effects").CallEffect<boolean>, void, (AuthState & boolean[] & false) | (AuthState & boolean[] & true)>>, void, ILoginResponseV3 & Partial<AuthState>>, void, unknown>;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
|
|
2
|
+
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
3
|
+
const _excluded = ["callback"];
|
|
4
|
+
import { select, put, call } from 'redux-saga/effects';
|
|
5
|
+
import { api } from '@frontegg/rest-api';
|
|
6
|
+
import { actions } from '../../reducer';
|
|
7
|
+
import { LoginFlow, LoginStep } from '../interfaces';
|
|
8
|
+
import { shouldShowPromptPasskeys, afterAuthenticationStateUpdate } from '../saga.utils';
|
|
9
|
+
import { afterAuthNavigation, afterStepUpAuthNavigation } from './afterAuthNavigation.saga';
|
|
10
|
+
import { getFeatureFlags } from '../../../helpers';
|
|
11
|
+
import { errorHandler } from '../../../utils';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param tenants
|
|
15
|
+
* @param isAuthenticated
|
|
16
|
+
* @param isStepUp
|
|
17
|
+
* @returns additional update object for the afterAuthenticationStateUpdate saga as part of MFA auth with authenticator app
|
|
18
|
+
*/
|
|
19
|
+
function* buildPostAuthStateUpdate(tenants, isAuthenticated, isStepUp) {
|
|
20
|
+
const {
|
|
21
|
+
loginState
|
|
22
|
+
} = yield select(state => state.auth);
|
|
23
|
+
let additionalUpdate = {};
|
|
24
|
+
if (!isStepUp) {
|
|
25
|
+
const step = loginState.flow === LoginFlow.Login ? LoginStep.success : loginState.step;
|
|
26
|
+
additionalUpdate = {
|
|
27
|
+
loginState: {
|
|
28
|
+
flow: loginState.flow,
|
|
29
|
+
quickLoginToRegister: loginState.quickLoginToRegister,
|
|
30
|
+
loading: false,
|
|
31
|
+
step,
|
|
32
|
+
error: undefined,
|
|
33
|
+
tenants
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return _extends({}, additionalUpdate, {
|
|
38
|
+
isAuthenticated
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Handle after MFA authentication with authenticator app for login
|
|
44
|
+
*/
|
|
45
|
+
function* postLoginMfaAuthenticator(isAuthenticated, callback) {
|
|
46
|
+
const {
|
|
47
|
+
loginState
|
|
48
|
+
} = yield select(state => state.auth);
|
|
49
|
+
if (loginState.flow !== LoginFlow.Login) return;
|
|
50
|
+
const [securityCenterLoginFlows] = yield call(getFeatureFlags, ['security-center-show-login-flows']);
|
|
51
|
+
if (securityCenterLoginFlows && loginState.isBreachedPassword && !isAuthenticated) {
|
|
52
|
+
yield put(actions.setLoginState({
|
|
53
|
+
step: LoginStep.breachedPassword,
|
|
54
|
+
loading: false
|
|
55
|
+
}));
|
|
56
|
+
} else {
|
|
57
|
+
const shouldShowPrompt = yield call(shouldShowPromptPasskeys);
|
|
58
|
+
if (shouldShowPrompt) {
|
|
59
|
+
yield put(actions.setLoginState({
|
|
60
|
+
step: LoginStep.promptPasskeys,
|
|
61
|
+
loading: false
|
|
62
|
+
}));
|
|
63
|
+
} else {
|
|
64
|
+
yield afterAuthNavigation();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
callback == null ? void 0 : callback(true);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Handle after MFA authentication with authenticator app for step up
|
|
72
|
+
*/
|
|
73
|
+
function* postStepUpMfaAuthenticator(callback) {
|
|
74
|
+
yield afterStepUpAuthNavigation();
|
|
75
|
+
callback == null ? void 0 : callback(true);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Utility function to handle MFA authentication with authenticator app
|
|
80
|
+
* @param payload MFA with authenticator action payload
|
|
81
|
+
* @param setLoadingAction set loading action
|
|
82
|
+
* @param isStepUp true if this is a step up authentication
|
|
83
|
+
* @returns
|
|
84
|
+
*/
|
|
85
|
+
export function* mfaWithAuthenticator(_ref, setLoadingAction, isStepUp) {
|
|
86
|
+
let {
|
|
87
|
+
callback
|
|
88
|
+
} = _ref,
|
|
89
|
+
loginWithMfaPayload = _objectWithoutPropertiesLoose(_ref, _excluded);
|
|
90
|
+
yield put(setLoadingAction({
|
|
91
|
+
loading: true,
|
|
92
|
+
error: undefined
|
|
93
|
+
}));
|
|
94
|
+
try {
|
|
95
|
+
const {
|
|
96
|
+
user,
|
|
97
|
+
tenants = [],
|
|
98
|
+
activeTenant
|
|
99
|
+
} = yield call(api.auth.loginWithMfaV2, loginWithMfaPayload);
|
|
100
|
+
const isAuthenticated = !!user.accessToken;
|
|
101
|
+
const additionalUpdate = yield call(buildPostAuthStateUpdate, tenants, isAuthenticated, isStepUp);
|
|
102
|
+
yield call(afterAuthenticationStateUpdate, {
|
|
103
|
+
user,
|
|
104
|
+
tenants,
|
|
105
|
+
activeTenant
|
|
106
|
+
}, additionalUpdate);
|
|
107
|
+
if (user.id) {
|
|
108
|
+
localStorage.setItem('userId', user.id);
|
|
109
|
+
}
|
|
110
|
+
yield put(setLoadingAction({
|
|
111
|
+
loading: false,
|
|
112
|
+
error: undefined
|
|
113
|
+
}));
|
|
114
|
+
if (isStepUp) {
|
|
115
|
+
yield call(postStepUpMfaAuthenticator, callback);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
yield call(postLoginMfaAuthenticator, isAuthenticated, callback);
|
|
119
|
+
} catch (e) {
|
|
120
|
+
yield put(setLoadingAction({
|
|
121
|
+
loading: false,
|
|
122
|
+
error: errorHandler(e)
|
|
123
|
+
}));
|
|
124
|
+
callback == null ? void 0 : callback(false, e);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Login with MFA with authenticator app - external saga
|
|
130
|
+
* @param payload.mfaToken
|
|
131
|
+
* @param payload.mfaDevices
|
|
132
|
+
* @param payload.rememberDevice
|
|
133
|
+
* @param payload.invitationToken
|
|
134
|
+
* @param payload.callback - The callback function to be called after the request is done
|
|
135
|
+
*/
|
|
136
|
+
export function* loginWithMfa({
|
|
137
|
+
payload
|
|
138
|
+
}) {
|
|
139
|
+
yield mfaWithAuthenticator(payload, actions.setLoginState, false);
|
|
140
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { PayloadAction } from '@reduxjs/toolkit';
|
|
2
|
+
import { IPreVerifyMFA, IPreVerifyMFAEmailCodeResponse, IVerifyMFAEmailCode, ILoginResponseV3 } from '@frontegg/rest-api';
|
|
3
|
+
import { WithCallback } from '../../../interfaces';
|
|
4
|
+
import { SetLoadingAction } from '../interfaces';
|
|
5
|
+
/**
|
|
6
|
+
* Shared logic for MFA Email code pre-verify step
|
|
7
|
+
* @param payload.callback callback function to be called after the verification is done
|
|
8
|
+
* @param payload.mfaToken
|
|
9
|
+
* @param setLoadingAction loading setter action (e.g. actions.setLoginState)
|
|
10
|
+
*/
|
|
11
|
+
export declare function preVerifyMFAEmailCode({ callback, ...payload }: WithCallback<IPreVerifyMFA>, setLoadingAction: SetLoadingAction): Generator<import("redux-saga/effects").PutEffect<import("redux").Action<any>> | import("redux-saga/effects").CallEffect<IPreVerifyMFAEmailCodeResponse>, void, IPreVerifyMFAEmailCodeResponse>;
|
|
12
|
+
/**
|
|
13
|
+
* Shared logic for MFA Email code verify step
|
|
14
|
+
* @param payload.otcToken
|
|
15
|
+
* @param payload.callback callback function to be called after the verification is done with true for success, o.w false
|
|
16
|
+
* @param payload.code 6 digits code input by the user
|
|
17
|
+
*/
|
|
18
|
+
export declare function verifyMFAEmailCode({ callback, ...payload }: WithCallback<IVerifyMFAEmailCode>, setLoadingAction: SetLoadingAction): Generator<import("redux-saga/effects").CallEffect<ILoginResponseV3> | import("redux-saga/effects").PutEffect<import("redux").Action<any>> | Generator<import("redux-saga/effects").PutEffect<{
|
|
19
|
+
payload: Partial<import("../..").AuthState>;
|
|
20
|
+
type: string;
|
|
21
|
+
}> | import("redux-saga/effects").CallEffect<void> | import("redux-saga/effects").PutEffect<{
|
|
22
|
+
payload: import("../..").User;
|
|
23
|
+
type: string;
|
|
24
|
+
}> | import("redux-saga/effects").PutEffect<{
|
|
25
|
+
payload: Partial<import("../..").TenantsState>;
|
|
26
|
+
type: string;
|
|
27
|
+
}> | import("redux-saga/effects").CallEffect<Generator<import("redux-saga/effects").SelectEffect | import("redux-saga/effects").CallEffect<boolean[]> | Generator<import("redux-saga/effects").SelectEffect | import("redux-saga/effects").CallEffect<void>, void, {
|
|
28
|
+
routes: {
|
|
29
|
+
customLoginAuthenticatedUrl: any;
|
|
30
|
+
stepUpUrl: any;
|
|
31
|
+
};
|
|
32
|
+
} & import("../..").User & string> | import("redux-saga/effects").PutEffect<{
|
|
33
|
+
payload: Partial<import("../interfaces").LoginState>;
|
|
34
|
+
type: string;
|
|
35
|
+
}> | import("redux-saga/effects").CallEffect<boolean>, void, (import("../..").AuthState & {
|
|
36
|
+
step: any;
|
|
37
|
+
} & boolean[] & false) | (import("../..").AuthState & {
|
|
38
|
+
step: any;
|
|
39
|
+
} & boolean[] & true)>>, void, unknown>, void, ILoginResponseV3>;
|
|
40
|
+
/**
|
|
41
|
+
* Pre verify step for MFA Email login
|
|
42
|
+
* @param payload.mfaToken
|
|
43
|
+
* @param payload.callback callback function to be called after the verification is done
|
|
44
|
+
*/
|
|
45
|
+
export declare function preVerifyMFAEmailCodeForLogin({ payload }: PayloadAction<WithCallback<IPreVerifyMFA>>): Generator<Generator<import("redux-saga/effects").PutEffect<import("redux").Action<any>> | import("redux-saga/effects").CallEffect<IPreVerifyMFAEmailCodeResponse>, void, IPreVerifyMFAEmailCodeResponse>, void, unknown>;
|
|
46
|
+
/**
|
|
47
|
+
* Verify step for MFA Email login
|
|
48
|
+
* @param payload.otcToken
|
|
49
|
+
* @param payload.callback callback function to be called after the verification is done with true for success, o.w false
|
|
50
|
+
* @param payload.code 6 digits code input by the user
|
|
51
|
+
*/
|
|
52
|
+
export declare function verifyMFAEmailCodeForLogin({ payload }: PayloadAction<WithCallback<IVerifyMFAEmailCode>>): Generator<Generator<import("redux-saga/effects").CallEffect<ILoginResponseV3> | import("redux-saga/effects").PutEffect<import("redux").Action<any>> | Generator<import("redux-saga/effects").PutEffect<{
|
|
53
|
+
payload: Partial<import("../..").AuthState>;
|
|
54
|
+
type: string;
|
|
55
|
+
}> | import("redux-saga/effects").CallEffect<void> | import("redux-saga/effects").PutEffect<{
|
|
56
|
+
payload: import("../..").User;
|
|
57
|
+
type: string;
|
|
58
|
+
}> | import("redux-saga/effects").PutEffect<{
|
|
59
|
+
payload: Partial<import("../..").TenantsState>;
|
|
60
|
+
type: string;
|
|
61
|
+
}> | import("redux-saga/effects").CallEffect<Generator<import("redux-saga/effects").SelectEffect | import("redux-saga/effects").CallEffect<boolean[]> | Generator<import("redux-saga/effects").SelectEffect | import("redux-saga/effects").CallEffect<void>, void, {
|
|
62
|
+
routes: {
|
|
63
|
+
customLoginAuthenticatedUrl: any;
|
|
64
|
+
stepUpUrl: any;
|
|
65
|
+
};
|
|
66
|
+
} & import("../..").User & string> | import("redux-saga/effects").PutEffect<{
|
|
67
|
+
payload: Partial<import("../interfaces").LoginState>;
|
|
68
|
+
type: string;
|
|
69
|
+
}> | import("redux-saga/effects").CallEffect<boolean>, void, (import("../..").AuthState & {
|
|
70
|
+
step: any;
|
|
71
|
+
} & boolean[] & false) | (import("../..").AuthState & {
|
|
72
|
+
step: any;
|
|
73
|
+
} & boolean[] & true)>>, void, unknown>, void, ILoginResponseV3>, void, unknown>;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
|
|
2
|
+
const _excluded = ["callback"],
|
|
3
|
+
_excluded2 = ["callback"];
|
|
4
|
+
import { call, put } from 'redux-saga/effects';
|
|
5
|
+
import { api } from '@frontegg/rest-api';
|
|
6
|
+
import { errorHandler } from '../../../utils';
|
|
7
|
+
import { MFAStep } from '../../MfaState/interfaces';
|
|
8
|
+
import { actions } from '../../reducer';
|
|
9
|
+
import { handleVerifyMFAResponse } from './handleVerifyMFAResponse.saga';
|
|
10
|
+
/**
|
|
11
|
+
* Shared logic for MFA Email code pre-verify step
|
|
12
|
+
* @param payload.callback callback function to be called after the verification is done
|
|
13
|
+
* @param payload.mfaToken
|
|
14
|
+
* @param setLoadingAction loading setter action (e.g. actions.setLoginState)
|
|
15
|
+
*/
|
|
16
|
+
export function* preVerifyMFAEmailCode(_ref, setLoadingAction) {
|
|
17
|
+
let {
|
|
18
|
+
callback
|
|
19
|
+
} = _ref,
|
|
20
|
+
payload = _objectWithoutPropertiesLoose(_ref, _excluded);
|
|
21
|
+
yield put(setLoadingAction({
|
|
22
|
+
loading: true,
|
|
23
|
+
error: undefined
|
|
24
|
+
}));
|
|
25
|
+
try {
|
|
26
|
+
const data = yield call(api.auth.preVerifyMFAEmailCode, payload);
|
|
27
|
+
yield put(actions.setMfaState({
|
|
28
|
+
otcToken: data.otcToken,
|
|
29
|
+
step: MFAStep.emailVerifyCode
|
|
30
|
+
}));
|
|
31
|
+
yield put(setLoadingAction({
|
|
32
|
+
loading: false,
|
|
33
|
+
error: undefined
|
|
34
|
+
}));
|
|
35
|
+
callback == null ? void 0 : callback(true);
|
|
36
|
+
} catch (e) {
|
|
37
|
+
yield put(setLoadingAction({
|
|
38
|
+
error: errorHandler(e)
|
|
39
|
+
}));
|
|
40
|
+
callback == null ? void 0 : callback(null);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Shared logic for MFA Email code verify step
|
|
46
|
+
* @param payload.otcToken
|
|
47
|
+
* @param payload.callback callback function to be called after the verification is done with true for success, o.w false
|
|
48
|
+
* @param payload.code 6 digits code input by the user
|
|
49
|
+
*/
|
|
50
|
+
export function* verifyMFAEmailCode(_ref2, setLoadingAction) {
|
|
51
|
+
let {
|
|
52
|
+
callback
|
|
53
|
+
} = _ref2,
|
|
54
|
+
payload = _objectWithoutPropertiesLoose(_ref2, _excluded2);
|
|
55
|
+
yield put(setLoadingAction({
|
|
56
|
+
loading: true
|
|
57
|
+
}));
|
|
58
|
+
try {
|
|
59
|
+
const data = yield call(api.auth.verifyMFAEmailCodeV2, payload);
|
|
60
|
+
yield handleVerifyMFAResponse(data);
|
|
61
|
+
yield put(setLoadingAction({
|
|
62
|
+
loading: false,
|
|
63
|
+
error: undefined
|
|
64
|
+
}));
|
|
65
|
+
callback == null ? void 0 : callback(true);
|
|
66
|
+
} catch (e) {
|
|
67
|
+
yield put(setLoadingAction({
|
|
68
|
+
loading: false,
|
|
69
|
+
error: errorHandler(e)
|
|
70
|
+
}));
|
|
71
|
+
callback == null ? void 0 : callback(null);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Pre verify step for MFA Email login
|
|
77
|
+
* @param payload.mfaToken
|
|
78
|
+
* @param payload.callback callback function to be called after the verification is done
|
|
79
|
+
*/
|
|
80
|
+
export function* preVerifyMFAEmailCodeForLogin({
|
|
81
|
+
payload
|
|
82
|
+
}) {
|
|
83
|
+
yield preVerifyMFAEmailCode(payload, actions.setLoginState);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Verify step for MFA Email login
|
|
88
|
+
* @param payload.otcToken
|
|
89
|
+
* @param payload.callback callback function to be called after the verification is done with true for success, o.w false
|
|
90
|
+
* @param payload.code 6 digits code input by the user
|
|
91
|
+
*/
|
|
92
|
+
export function* verifyMFAEmailCodeForLogin({
|
|
93
|
+
payload
|
|
94
|
+
}) {
|
|
95
|
+
yield verifyMFAEmailCode(payload, actions.setLoginState);
|
|
96
|
+
}
|