| @@ -1,2 +1,2 @@ | |||
| REACT_APP_BASE_API_URL=https://portalgatewayapi.bullioninternational.info/ | |||
| REACT_APP_BASE_API_URL=http://localhost:4000/ | |||
| @@ -1,190 +1,189 @@ | |||
| /* eslint-disable */ | |||
| import React, { useEffect, useState } from 'react'; | |||
| import PropTypes from 'prop-types'; | |||
| import { useFormik } from 'formik'; | |||
| import { useDispatch, useSelector } from 'react-redux'; | |||
| import { NavLink } from 'react-router-dom'; | |||
| import * as Yup from 'yup'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import React, { useEffect, useState } from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { useFormik } from "formik"; | |||
| import { useDispatch, useSelector } from "react-redux"; | |||
| import { NavLink } from "react-router-dom"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import { | |||
| clearLoginErrors, | |||
| fetchUser, | |||
| } from '../../store/actions/login/loginActions'; | |||
| import { selectLoginError } from '../../store/selectors/loginSelectors'; | |||
| import { FORGOT_PASSWORD_PAGE, HOME_PAGE } from '../../constants/pages'; | |||
| clearLoginErrors, | |||
| fetchUser, | |||
| } from "../../store/actions/login/loginActions"; | |||
| import { selectLoginError } from "../../store/selectors/loginSelectors"; | |||
| import { FORGOT_PASSWORD_PAGE, HOME_PAGE } from "../../constants/pages"; | |||
| import { | |||
| Box, | |||
| Button, | |||
| Container, | |||
| Grid, | |||
| IconButton, | |||
| InputAdornment, | |||
| Link, | |||
| TextField, | |||
| Typography, | |||
| } from '@mui/material'; | |||
| import { Visibility, VisibilityOff } from '@mui/icons-material'; | |||
| import Backdrop from '../../components/MUI/BackdropComponent'; | |||
| import ErrorMessage from '../../components/MUI/ErrorMessageComponent'; | |||
| import { selectIsLoadingByActionType } from '../../store/selectors/loadingSelectors'; | |||
| import { LOGIN_USER_LOADING } from '../../store/actions/login/loginActionConstants'; | |||
| import loginValidation from '../../validations/loginValidation'; | |||
| import loginInitialValues from '../../initialValues/loginInitialValues'; | |||
| Box, | |||
| Button, | |||
| Container, | |||
| Grid, | |||
| IconButton, | |||
| InputAdornment, | |||
| Link, | |||
| TextField, | |||
| Typography, | |||
| } from "@mui/material"; | |||
| import { Visibility, VisibilityOff } from "@mui/icons-material"; | |||
| import Backdrop from "../../components/MUI/BackdropComponent"; | |||
| import ErrorMessage from "../../components/MUI/ErrorMessageComponent"; | |||
| import { selectIsLoadingByActionType } from "../../store/selectors/loadingSelectors"; | |||
| import { | |||
| LOGIN_USER_SCOPE, | |||
| } from "../../store/actions/login/loginActionConstants"; | |||
| import loginValidation from "../../validations/loginValidation"; | |||
| import loginInitialValues from "../../initialValues/loginInitialValues"; | |||
| const LoginPage = ({ history }) => { | |||
| const dispatch = useDispatch(); | |||
| const { t } = useTranslation(); | |||
| const error = useSelector(selectLoginError); | |||
| const dispatch = useDispatch(); | |||
| const { t } = useTranslation(); | |||
| const error = useSelector(selectLoginError); | |||
| const [showPassword, setShowPassword] = useState(false); | |||
| const handleClickShowPassword = () => setShowPassword(!showPassword); | |||
| const handleMouseDownPassword = () => setShowPassword(!showPassword); | |||
| const [showPassword, setShowPassword] = useState(false); | |||
| const handleClickShowPassword = () => setShowPassword(!showPassword); | |||
| const handleMouseDownPassword = () => setShowPassword(!showPassword); | |||
| // Clear login errors when user firstly enters the page | |||
| useEffect(() => { | |||
| dispatch(clearLoginErrors()) | |||
| }, []) | |||
| // Clear login errors when user firstly enters the page | |||
| useEffect(() => { | |||
| dispatch(clearLoginErrors()); | |||
| }, []); | |||
| const isLoading = useSelector( | |||
| selectIsLoadingByActionType(LOGIN_USER_LOADING) | |||
| ); | |||
| const isLoading = useSelector(selectIsLoadingByActionType(LOGIN_USER_SCOPE)); | |||
| const handleApiResponseSuccess = () => { | |||
| history.push({ | |||
| pathname: HOME_PAGE, | |||
| state: { | |||
| from: history.location.pathname, | |||
| }, | |||
| }); | |||
| }; | |||
| const handleApiResponseSuccess = () => { | |||
| history.push({ | |||
| pathname: HOME_PAGE, | |||
| state: { | |||
| from: history.location.pathname, | |||
| }, | |||
| }); | |||
| }; | |||
| const handleSubmit = (values) => { | |||
| const { email: Username, password: Password } = values; | |||
| dispatch(clearLoginErrors()); | |||
| dispatch( | |||
| fetchUser({ | |||
| Username, | |||
| Password, | |||
| handleApiResponseSuccess, | |||
| }) | |||
| ); | |||
| }; | |||
| const handleSubmit = (values) => { | |||
| const { email, password} = values; | |||
| dispatch(clearLoginErrors()); | |||
| dispatch( | |||
| fetchUser({ | |||
| email, | |||
| password, | |||
| handleApiResponseSuccess, | |||
| }) | |||
| ); | |||
| }; | |||
| const formik = useFormik({ | |||
| initialValues: loginInitialValues, | |||
| validationSchema: loginValidation, | |||
| onSubmit: handleSubmit, | |||
| validateOnBlur: true, | |||
| enableReinitialize: true, | |||
| }); | |||
| const formik = useFormik({ | |||
| initialValues: loginInitialValues, | |||
| validationSchema: loginValidation, | |||
| onSubmit: handleSubmit, | |||
| validateOnBlur: true, | |||
| enableReinitialize: true, | |||
| }); | |||
| return ( | |||
| <Container component="main" maxWidth="md"> | |||
| <Box | |||
| sx={{ | |||
| marginTop: 32, | |||
| display: 'flex', | |||
| flexDirection: 'column', | |||
| alignItems: 'center', | |||
| }} | |||
| > | |||
| <Typography component="h1" variant="h5"> | |||
| {t('login.logInTitle')} | |||
| </Typography> | |||
| {error && <ErrorMessage error={error} />} | |||
| <Box | |||
| component="form" | |||
| onSubmit={formik.handleSubmit} | |||
| sx={{ position: 'relative', mt: 1, p: 1 }} | |||
| > | |||
| <Backdrop position="absolute" isLoading={isLoading} /> | |||
| <TextField | |||
| name="email" | |||
| label={t('common.labelEmail')} | |||
| margin="normal" | |||
| value={formik.values.email} | |||
| onChange={formik.handleChange} | |||
| error={formik.touched.email && Boolean(formik.errors.email)} | |||
| helperText={formik.touched.email && formik.errors.email} | |||
| autoFocus | |||
| fullWidth | |||
| /> | |||
| <TextField | |||
| name="password" | |||
| label={t('common.labelPassword')} | |||
| margin="normal" | |||
| type={showPassword ? 'text' : 'password'} | |||
| value={formik.values.password} | |||
| onChange={formik.handleChange} | |||
| error={formik.touched.password && Boolean(formik.errors.password)} | |||
| helperText={formik.touched.password && formik.errors.password} | |||
| fullWidth | |||
| InputProps={{ | |||
| endAdornment: ( | |||
| <InputAdornment position="end"> | |||
| <IconButton | |||
| onClick={handleClickShowPassword} | |||
| onMouseDown={handleMouseDownPassword} | |||
| > | |||
| {showPassword ? <Visibility /> : <VisibilityOff />} | |||
| </IconButton> | |||
| </InputAdornment> | |||
| ), | |||
| }} | |||
| /> | |||
| <Button | |||
| type="submit" | |||
| variant="contained" | |||
| sx={{ mt: 3, mb: 2 }} | |||
| fullWidth | |||
| > | |||
| {t('login.logIn')} | |||
| </Button> | |||
| <Grid container> | |||
| <Grid | |||
| item | |||
| xs={12} | |||
| md={6} | |||
| sx={{ textAlign: { xs: 'center', md: 'left' } }} | |||
| > | |||
| <Link | |||
| to={FORGOT_PASSWORD_PAGE} | |||
| component={NavLink} | |||
| variant="body2" | |||
| underline="hover" | |||
| > | |||
| {t('login.forgotYourPassword')} | |||
| </Link> | |||
| </Grid> | |||
| <Grid | |||
| item | |||
| xs={12} | |||
| md={6} | |||
| sx={{ textAlign: { xs: 'center', md: 'right' } }} | |||
| > | |||
| <Link | |||
| to="#" | |||
| component={NavLink} | |||
| variant="body2" | |||
| underline="hover" | |||
| > | |||
| {t('login.dontHaveAccount')} | |||
| </Link> | |||
| </Grid> | |||
| </Grid> | |||
| </Box> | |||
| </Box> | |||
| </Container> | |||
| ); | |||
| return ( | |||
| <Container component="main" maxWidth="md"> | |||
| <Box | |||
| sx={{ | |||
| marginTop: 32, | |||
| display: "flex", | |||
| flexDirection: "column", | |||
| alignItems: "center", | |||
| }} | |||
| > | |||
| <Typography component="h1" variant="h5"> | |||
| {t("login.logInTitle")} | |||
| </Typography> | |||
| {error && <ErrorMessage error={error} />} | |||
| <Box | |||
| component="form" | |||
| onSubmit={formik.handleSubmit} | |||
| sx={{ position: "relative", mt: 1, p: 1 }} | |||
| > | |||
| <Backdrop position="absolute" isLoading={isLoading} /> | |||
| <TextField | |||
| name="email" | |||
| label={t("common.labelEmail")} | |||
| margin="normal" | |||
| value={formik.values.email} | |||
| onChange={formik.handleChange} | |||
| error={formik.touched.email && Boolean(formik.errors.email)} | |||
| helperText={formik.touched.email && formik.errors.email} | |||
| autoFocus | |||
| fullWidth | |||
| /> | |||
| <TextField | |||
| name="password" | |||
| label={t("common.labelPassword")} | |||
| margin="normal" | |||
| type={showPassword ? "text" : "password"} | |||
| value={formik.values.password} | |||
| onChange={formik.handleChange} | |||
| error={formik.touched.password && Boolean(formik.errors.password)} | |||
| helperText={formik.touched.password && formik.errors.password} | |||
| fullWidth | |||
| InputProps={{ | |||
| endAdornment: ( | |||
| <InputAdornment position="end"> | |||
| <IconButton | |||
| onClick={handleClickShowPassword} | |||
| onMouseDown={handleMouseDownPassword} | |||
| > | |||
| {showPassword ? <Visibility /> : <VisibilityOff />} | |||
| </IconButton> | |||
| </InputAdornment> | |||
| ), | |||
| }} | |||
| /> | |||
| <Button | |||
| type="submit" | |||
| variant="contained" | |||
| sx={{ mt: 3, mb: 2 }} | |||
| fullWidth | |||
| > | |||
| {t("login.logIn")} | |||
| </Button> | |||
| <Grid container> | |||
| <Grid | |||
| item | |||
| xs={12} | |||
| md={6} | |||
| sx={{ textAlign: { xs: "center", md: "left" } }} | |||
| > | |||
| <Link | |||
| to={FORGOT_PASSWORD_PAGE} | |||
| component={NavLink} | |||
| variant="body2" | |||
| underline="hover" | |||
| > | |||
| {t("login.forgotYourPassword")} | |||
| </Link> | |||
| </Grid> | |||
| <Grid | |||
| item | |||
| xs={12} | |||
| md={6} | |||
| sx={{ textAlign: { xs: "center", md: "right" } }} | |||
| > | |||
| <Link | |||
| to="#" | |||
| component={NavLink} | |||
| variant="body2" | |||
| underline="hover" | |||
| > | |||
| {t("login.dontHaveAccount")} | |||
| </Link> | |||
| </Grid> | |||
| </Grid> | |||
| </Box> | |||
| </Box> | |||
| </Container> | |||
| ); | |||
| }; | |||
| LoginPage.propTypes = { | |||
| history: PropTypes.shape({ | |||
| replace: PropTypes.func, | |||
| push: PropTypes.func, | |||
| location: PropTypes.shape({ | |||
| pathname: PropTypes.string, | |||
| }), | |||
| }), | |||
| history: PropTypes.shape({ | |||
| replace: PropTypes.func, | |||
| push: PropTypes.func, | |||
| location: PropTypes.shape({ | |||
| pathname: PropTypes.string, | |||
| }), | |||
| }), | |||
| }; | |||
| export default LoginPage; | |||
| @@ -17,12 +17,12 @@ export default { | |||
| }, | |||
| authentications: { | |||
| getUsernames: 'authenticate/usernames', | |||
| login: 'authenticate', | |||
| login: 'auth/token', | |||
| getUserSecurityQuestion: 'users/username/securityquestion', | |||
| confirmSecurityQuestion: 'authenticate/confirm', | |||
| confirmForgotPassword: 'users/passwords/reset_token', | |||
| resetPassword: 'users/passwords', | |||
| refreshToken: '/authenticate/refresh', | |||
| refreshToken: 'auth/refresh', | |||
| generateToken: '/authenticate/generate', | |||
| authenticate: | |||
| '/authenticate?fp={fp}&offer={offer}&landingPageUrl={landingPageUrl}®istrationFlowType={registrationFlowType}', | |||
| @@ -108,7 +108,7 @@ export default { | |||
| getRegistrationAccounts: '/users/{userUid}/accounts', | |||
| updateUser: '/users/{userUid}?updateUserActionType={actionType}', | |||
| updateUserPassword: '/users/{userUid}/passwords', | |||
| logout: '/users/{userUid}/logout', | |||
| logout: 'auth/logout', | |||
| getUsernames: '/users/email', | |||
| createUser: | |||
| '/users?fp={fp}&offer={offer}&landingPageUrl={landingPageUrl}®istrationFlowType={registrationFlowType}', | |||
| @@ -1,14 +1,14 @@ | |||
| import axios from 'axios'; | |||
| import queryString from 'qs'; | |||
| // import queryString from 'qs'; | |||
| const request = axios.create({ | |||
| baseURL: process.env.REACT_APP_BASE_API_URL, | |||
| headers: { | |||
| 'Content-Type': 'application/json', | |||
| }, | |||
| withCredentials: true, | |||
| paramsSerializer: (params) => | |||
| queryString.stringify(params, { arrayFormat: 'comma' }), | |||
| // withCredentials: true, | |||
| // paramsSerializer: (params) => | |||
| // queryString.stringify(params, { arrayFormat: 'comma' }), | |||
| }); | |||
| export const getRequest = (url, params = null, options = null) => | |||
| @@ -1,5 +1,5 @@ | |||
| import { getRequest, postRequest, replaceInUrl } from './index'; | |||
| import apiEndpoints from './apiEndpoints'; | |||
| import { getRequest, postRequest } from "./index"; | |||
| import apiEndpoints from "./apiEndpoints"; | |||
| export const getUsernames = (emailorusername) => | |||
| getRequest(apiEndpoints.authentications.getUsernames, { | |||
| @@ -15,12 +15,8 @@ export const updateSecurityAnswer = (payload) => | |||
| export const refreshTokenRequest = (payload) => | |||
| postRequest(apiEndpoints.authentications.refreshToken, payload); | |||
| export const logoutUserRequest = (userUid) => | |||
| postRequest( | |||
| replaceInUrl(apiEndpoints.users.logout, { | |||
| userUid, | |||
| }), | |||
| ); | |||
| export const logoutUserRequest = (payload) => | |||
| postRequest(apiEndpoints.users.logout, payload); | |||
| export const generateTokenRequest = (payload) => | |||
| postRequest(apiEndpoints.authentications.generateToken, payload); | |||
| @@ -1,3 +1,5 @@ | |||
| import { createLoadingType } from '../actionHelpers'; | |||
| import { createLoadingType } from "../actionHelpers"; | |||
| export const APP_LOADING = createLoadingType('APP_LOADING'); | |||
| export const APP_LOADING = createLoadingType("APP_LOADING"); | |||
| export const ADD_LOADER = createLoadingType("ADD_LOADER"); | |||
| export const REMOVE_LOADER = createLoadingType("REMOVE_LOADER"); | |||
| @@ -1,5 +1,11 @@ | |||
| import { APP_LOADING } from './appActionConstants'; | |||
| import { ADD_LOADER, REMOVE_LOADER } from "./appActionConstants"; | |||
| export const setAppReady = () => ({ | |||
| type: APP_LOADING, | |||
| export const addLoader = (payload) => ({ | |||
| type: ADD_LOADER, | |||
| payload, | |||
| }); | |||
| export const removeLoader = (payload) => ({ | |||
| type: REMOVE_LOADER, | |||
| payload, | |||
| }); | |||
| @@ -8,7 +8,7 @@ import { | |||
| } from '../actionHelpers'; | |||
| const LOGIN_USER_SCOPE = 'LOGIN_USER'; | |||
| export const LOGIN_USER_SCOPE = 'LOGIN_USER'; | |||
| export const LOGIN_USER_FETCH = createFetchType(LOGIN_USER_SCOPE); | |||
| export const LOGIN_USER_SUCCESS = createSuccessType(LOGIN_USER_SCOPE); | |||
| export const LOGIN_USER_ERROR = createErrorType(LOGIN_USER_SCOPE); | |||
| @@ -50,8 +50,9 @@ export const logoutUser = () => ({ | |||
| type: LOGOUT_USER, | |||
| }); | |||
| export const refreshUserToken = () => ({ | |||
| export const refreshUserToken = (payload) => ({ | |||
| type: REFRESH_TOKEN, | |||
| payload | |||
| }); | |||
| export const generateToken = (payload) => ({ | |||
| @@ -5,8 +5,8 @@ import rootSaga from "./saga"; | |||
| import loadingMiddleware from "./middleware/loadingMiddleware"; | |||
| import requestStatusMiddleware from "./middleware/requestStatusMiddleware"; | |||
| import internalServerErrorMiddleware from "./middleware/internalServerErrorMiddleware"; | |||
| // import accessTokenMiddleware from "./middleware/accessTokenMiddleware"; | |||
| // import authenticationMiddleware from "./middleware/authenticationMiddleware"; | |||
| import accessTokenMiddleware from "./middleware/accessTokenMiddleware"; | |||
| import authenticationMiddleware from "./middleware/authenticationMiddleware"; | |||
| const composeEnhancers = | |||
| (window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ && | |||
| @@ -23,7 +23,9 @@ export default createStore( | |||
| sagaMiddleware, | |||
| loadingMiddleware, | |||
| requestStatusMiddleware, | |||
| internalServerErrorMiddleware | |||
| internalServerErrorMiddleware, | |||
| accessTokenMiddleware, | |||
| authenticationMiddleware | |||
| ) | |||
| ) | |||
| ); | |||
| @@ -1,8 +1,11 @@ | |||
| import axios from "axios"; | |||
| import jwt from "jsonwebtoken"; | |||
| import { JWT_TOKEN } from "../../constants/localStorage"; | |||
| import { JWT_REFRESH_TOKEN, JWT_TOKEN } from "../../constants/localStorage"; | |||
| import { attachBeforeRequestListener } from "../../request/index"; | |||
| import { authScopeStringGetHelper } from "../../util/helpers/authScopeHelpers"; | |||
| import { refreshUserToken } from "../actions/login/loginActions"; | |||
| import { | |||
| authScopeStringGetHelper, | |||
| } from "../../util/helpers/authScopeHelpers"; | |||
| import { logoutUser, refreshUserToken } from "../actions/login/loginActions"; | |||
| export const accessTokensMiddlewareInterceptorName = "ACCESS_TOKEN_INTERCEPTOR"; | |||
| @@ -11,19 +14,37 @@ export default ({ dispatch }) => | |||
| (action) => { | |||
| attachBeforeRequestListener(async (response) => { | |||
| const jwtToken = authScopeStringGetHelper(JWT_TOKEN); | |||
| const refresh = authScopeStringGetHelper(JWT_REFRESH_TOKEN); | |||
| if (!jwtToken || !refresh) return Promise.resolve(response); | |||
| const jwtTokenDecoded = jwt.decode(jwtToken); | |||
| const refreshTokenDecoded = jwt.decode(refresh); | |||
| if (!response.headers?.Authorization) { | |||
| response.headers.Authorization = `Bearer ${jwtToken}`; | |||
| } | |||
| // If refresh token is expired, log out user | |||
| if (new Date() > new Date(refreshTokenDecoded?.exp * 1000)) { | |||
| dispatch(logoutUser()); | |||
| return Promise.resolve(response); | |||
| } | |||
| // If access token is expired, refresh access token | |||
| if (new Date() > new Date(jwtTokenDecoded.exp * 1000)) { | |||
| console.log('response', response) | |||
| dispatch(refreshUserToken()); | |||
| const axiosResponse = await axios.post( | |||
| `${process.env.REACT_APP_BASE_API_URL}auth/refresh`, | |||
| { | |||
| token: refresh, | |||
| } | |||
| ); | |||
| const newToken = axiosResponse.data.newAccessToken; | |||
| response.headers.Authorization = `Bearer ${newToken}`; | |||
| dispatch(refreshUserToken(newToken)); | |||
| } | |||
| return Promise.resolve(response); | |||
| }, accessTokensMiddlewareInterceptorName); | |||
| next(action); | |||
| }; | |||
| }; | |||
| @@ -6,6 +6,7 @@ import { | |||
| UPDATE, | |||
| SUBMIT, | |||
| } from '../actions/actionHelpers'; | |||
| import { addLoader, removeLoader } from '../actions/app/appActions'; | |||
| const promiseTypes = [FETCH, UPDATE, DELETE, SUBMIT]; | |||
| export default ({ dispatch }) => (next) => (action) => { | |||
| @@ -13,28 +14,16 @@ export default ({ dispatch }) => (next) => (action) => { | |||
| action.type.includes(promiseType), | |||
| ); | |||
| if (promiseType) { | |||
| dispatch({ | |||
| type: 'UPDATE_LOADER', | |||
| payload: { | |||
| actionType: action.type.replace(promiseType, '[LOADING]'), | |||
| isLoading: true, | |||
| }, | |||
| }); | |||
| dispatch(addLoader(action.type)); | |||
| return next(action); | |||
| } | |||
| if (action.type.includes(SUCCESS) || action.type.includes(ERROR)) { | |||
| const actionType = action.type.includes(SUCCESS) | |||
| ? action.type.replace(SUCCESS, '[LOADING]') | |||
| : action.type.replace(ERROR, '[LOADING]'); | |||
| // const actionType = action.type.includes(SUCCESS) | |||
| // ? action.type.replace(SUCCESS, '[LOADING]') | |||
| // : action.type.replace(ERROR, '[LOADING]'); | |||
| dispatch({ | |||
| type: 'UPDATE_LOADER', | |||
| payload: { | |||
| actionType, | |||
| isLoading: false, | |||
| }, | |||
| }); | |||
| dispatch(removeLoader(action.type)); | |||
| return next(action); | |||
| } | |||
| next(action); | |||
| @@ -16,7 +16,7 @@ export default ({ dispatch }) => | |||
| error.response.config.url !== apiEndpoints.authentications.login && | |||
| error.response.config.url !== | |||
| apiEndpoints.authentications.confirmSecurityQuestion && | |||
| error.response.status === 401 | |||
| error.response.status === 4003 | |||
| ) { | |||
| return dispatch(logoutUser()); | |||
| } | |||
| @@ -1,36 +1,45 @@ | |||
| import createReducer from '../../utils/createReducer'; | |||
| import { APP_LOADING } from '../../actions/app/appActionConstants'; | |||
| import { RESET_LOGIN_STATE } from '../../actions/login/loginActionConstants'; | |||
| import createReducer from "../../utils/createReducer"; | |||
| import { | |||
| ADD_LOADER, | |||
| REMOVE_LOADER, | |||
| } from "../../actions/app/appActionConstants"; | |||
| const initialState = { | |||
| [APP_LOADING]: true, | |||
| loaderCount: 0, | |||
| }; | |||
| export default createReducer( | |||
| { | |||
| UPDATE_LOADER: updateLoader, | |||
| [APP_LOADING]: updateAppReadyLoader, | |||
| [RESET_LOGIN_STATE]: setAppNotReady, | |||
| [ADD_LOADER]: addLoader, | |||
| [REMOVE_LOADER]: removeLoader, | |||
| }, | |||
| initialState, | |||
| initialState | |||
| ); | |||
| function updateLoader(state, action) { | |||
| function addLoader(state, action) { | |||
| let loaderCount = state.loaderCount; | |||
| let actionType = action.payload.replace("[FETCH]", ""); | |||
| if (!state[actionType]) { | |||
| loaderCount++; | |||
| } | |||
| return { | |||
| ...state, | |||
| [action.payload.actionType]: action.payload.isLoading, | |||
| [actionType]: true, | |||
| loaderCount, | |||
| }; | |||
| } | |||
| function updateAppReadyLoader(state) { | |||
| return { | |||
| ...state, | |||
| [APP_LOADING]: false, | |||
| }; | |||
| } | |||
| function removeLoader(state, action) { | |||
| let actionType = action.payload | |||
| .replace("[SUCCESS]", "") | |||
| .replace("[ERROR]", ""); | |||
| let loaderCount = state.loaderCount; | |||
| if (state[actionType] === true) { | |||
| loaderCount--; | |||
| } | |||
| function setAppNotReady(state) { | |||
| return { | |||
| ...state, | |||
| [APP_LOADING]: true, | |||
| [actionType]: false, | |||
| loaderCount, | |||
| }; | |||
| } | |||
| @@ -36,7 +36,11 @@ export default createReducer( | |||
| function setUser(state, action) { | |||
| return { | |||
| ...state, | |||
| token: action.payload, | |||
| token: { | |||
| ...state.token, | |||
| JwtToken: action.payload.token, | |||
| JwtRefreshToken: action.payload.refresh | |||
| }, | |||
| }; | |||
| } | |||
| @@ -1,57 +1,43 @@ | |||
| import { all, call, put, takeLatest } from '@redux-saga/core/effects'; | |||
| import jwt from 'jsonwebtoken'; | |||
| import history from '../utils/history'; | |||
| import { all, call, put, takeLatest } from "@redux-saga/core/effects"; | |||
| import jwt from "jsonwebtoken"; | |||
| import history from "../utils/history"; | |||
| import { | |||
| AUTHENTICATE_USER, | |||
| LOGIN_USER_FETCH, | |||
| LOGOUT_USER, | |||
| REFRESH_TOKEN, | |||
| GENERATE_TOKEN, | |||
| } from '../actions/login/loginActionConstants'; | |||
| import { | |||
| attemptLogin, | |||
| logoutUserRequest, | |||
| refreshTokenRequest, | |||
| generateTokenRequest, | |||
| } from '../../request/loginRequest'; | |||
| } from "../actions/login/loginActionConstants"; | |||
| import { attemptLogin, logoutUserRequest } from "../../request/loginRequest"; | |||
| import { | |||
| fetchUserError, | |||
| fetchUserSuccess, | |||
| resetLoginState, | |||
| updateUserToken, | |||
| } from '../actions/login/loginActions'; | |||
| import { LOGIN_PAGE } from '../../constants/pages'; | |||
| import { setUser } from '../actions/user/userActions'; | |||
| import { | |||
| addHeaderToken, | |||
| removeHeaderToken, | |||
| } from '../../request'; | |||
| import { | |||
| IMPERSONATE_USER_UID, | |||
| REGISTRATION_USER_UID, | |||
| } from '../../constants/sessionStorage'; | |||
| } from "../actions/login/loginActions"; | |||
| import { LOGIN_PAGE } from "../../constants/pages"; | |||
| import { setUser } from "../actions/user/userActions"; | |||
| import { addHeaderToken, removeHeaderToken } from "../../request"; | |||
| import { | |||
| JWT_REFRESH_TOKEN, | |||
| JWT_TOKEN, | |||
| REFRESH_TOKEN_CONST, | |||
| } from '../../constants/localStorage'; | |||
| } from "../../constants/localStorage"; | |||
| import { | |||
| authScopeClearHelper, | |||
| authScopeStringGetHelper, | |||
| authScopeRemoveHelper, | |||
| authScopeSetHelper, | |||
| } from '../../util/helpers/authScopeHelpers'; | |||
| import { rejectErrorCodeHelper } from '../../util/helpers/rejectErrorCodeHelper'; | |||
| } from "../../util/helpers/authScopeHelpers"; | |||
| import { rejectErrorCodeHelper } from "../../util/helpers/rejectErrorCodeHelper"; | |||
| function* fetchUser({ payload }) { | |||
| try { | |||
| const { data } = yield call(attemptLogin, payload); | |||
| if (data.JwtToken) { | |||
| const user = jwt.decode(data.JwtToken); | |||
| yield call(authScopeSetHelper, JWT_TOKEN, data.JwtToken); | |||
| yield call(authScopeSetHelper, JWT_REFRESH_TOKEN, data.JwtRefreshToken); | |||
| yield call(authScopeSetHelper, REFRESH_TOKEN_CONST, data.RefreshToken); | |||
| yield call(addHeaderToken, data.JwtToken); | |||
| if (data?.token) { | |||
| const user = jwt.decode(data.token); | |||
| yield call(authScopeSetHelper, JWT_TOKEN, data.token); | |||
| yield call(authScopeSetHelper, JWT_REFRESH_TOKEN, data.refresh); | |||
| yield call(addHeaderToken, data.token); | |||
| yield put(setUser(user)); | |||
| } | |||
| yield put(fetchUserSuccess(data)); | |||
| @@ -76,7 +62,7 @@ function* authenticateUser() { | |||
| return yield put( | |||
| fetchUserSuccess({ | |||
| JwtToken, | |||
| }), | |||
| }) | |||
| ); | |||
| } catch (error) { | |||
| const errorMessage = yield call(rejectErrorCodeHelper, error); | |||
| @@ -89,10 +75,11 @@ function* authenticateUser() { | |||
| function* logoutUser() { | |||
| try { | |||
| const JwtToken = yield call(authScopeStringGetHelper, JWT_TOKEN); | |||
| const user = jwt.decode(JwtToken); | |||
| const token = yield call(authScopeStringGetHelper, JWT_REFRESH_TOKEN); | |||
| const user = yield call(jwt.decode, token); | |||
| if (user) { | |||
| yield call(logoutUserRequest, user.UserUid); | |||
| let requestData = { token }; | |||
| yield call(logoutUserRequest, requestData); | |||
| } | |||
| } catch (error) { | |||
| console.log(error); // eslint-disable-line | |||
| @@ -104,64 +91,18 @@ function* logoutUser() { | |||
| } | |||
| } | |||
| export function* refreshToken() { | |||
| try { | |||
| const JwtToken = yield call(authScopeStringGetHelper, JWT_TOKEN); | |||
| const JwtRefreshToken = yield call( | |||
| authScopeStringGetHelper, | |||
| JWT_REFRESH_TOKEN, | |||
| ); | |||
| if (JwtToken && JwtRefreshToken) { | |||
| const { data } = yield call(refreshTokenRequest, { | |||
| JwtRefreshToken, | |||
| JwtToken, | |||
| }); | |||
| yield call(authScopeSetHelper, JWT_TOKEN, data.JwtToken); | |||
| yield call(authScopeSetHelper, JWT_REFRESH_TOKEN, data.JwtRefreshToken); | |||
| const user = jwt.decode(data.JwtToken); | |||
| addHeaderToken(data.JwtToken); | |||
| yield put(setUser(user)); | |||
| yield put(updateUserToken(data.JwtToken)); | |||
| } | |||
| } catch (error) { | |||
| yield call(logoutUser); | |||
| console.log(error); // eslint-disable-line | |||
| } | |||
| } | |||
| export function* generateToken({ payload }) { | |||
| export function* refreshToken({ payload }) { | |||
| try { | |||
| const { data } = yield call(generateTokenRequest, payload.data); | |||
| const { JwtToken, JwtRefreshToken } = data; | |||
| const newTokenDecoded = jwt.decode(payload); | |||
| if (JwtToken && JwtRefreshToken) { | |||
| yield call(authScopeSetHelper, JWT_TOKEN, data.JwtToken); | |||
| yield call(authScopeSetHelper, JWT_REFRESH_TOKEN, data.JwtRefreshToken); | |||
| if (payload.impersonate) { | |||
| sessionStorage.setItem(IMPERSONATE_USER_UID, payload.accountUid); | |||
| } | |||
| if (payload.registration) { | |||
| sessionStorage.setItem(REGISTRATION_USER_UID, payload.accountUid); | |||
| } | |||
| const user = jwt.decode(data.JwtToken); | |||
| addHeaderToken(data.JwtToken); | |||
| if (user) { | |||
| yield put(setUser(user)); | |||
| } | |||
| yield put(updateUserToken(data.JwtToken)); | |||
| if (payload.onSuccess) { | |||
| yield call(payload.onSuccess); | |||
| } | |||
| } | |||
| yield call(authScopeSetHelper, JWT_TOKEN, payload); | |||
| addHeaderToken(payload); | |||
| yield put(setUser(newTokenDecoded)); | |||
| yield put(updateUserToken(payload)); | |||
| return true; | |||
| } catch (error) { | |||
| yield call(logoutUser); | |||
| console.log(error); // eslint-disable-line | |||
| return false; | |||
| } | |||
| } | |||
| @@ -171,6 +112,5 @@ export default function* loginSaga() { | |||
| takeLatest(AUTHENTICATE_USER, authenticateUser), | |||
| takeLatest(LOGOUT_USER, logoutUser), | |||
| takeLatest(REFRESH_TOKEN, refreshToken), | |||
| takeLatest(GENERATE_TOKEN, generateToken), | |||
| ]); | |||
| } | |||
| @@ -1,14 +1,16 @@ | |||
| import { createSelector } from 'reselect'; | |||
| import { createSelector } from "reselect"; | |||
| const loadingSelector = (state) => state.loading; | |||
| export const selectIsLoadingByActionType = (loadingActionType) => | |||
| createSelector( | |||
| loadingSelector, | |||
| (state) => state[`${loadingActionType}`] || false, | |||
| ); | |||
| createSelector(loadingSelector, (state) => state[`${loadingActionType}`]); | |||
| export const selectIsLoadingByActionTypes = (actionTypes) => | |||
| createSelector(loadingSelector, (state) => | |||
| actionTypes.some((actionType) => state[`${actionType}`]), | |||
| actionTypes.some((actionType) => state[`${actionType}`]) | |||
| ); | |||
| export const selectLoaderCount = createSelector( | |||
| loadingSelector, | |||
| (state) => state.loaderCount | |||
| ); | |||