Skip to content

Commit

Permalink
Connects to #102. Auth0 redirecting non-authorized users to error pag…
Browse files Browse the repository at this point in the history
…e. (#115)

* Updated imports that use react-redux hooks

* Refactored and simplified component

The logic to handle Auth0-authenticated users who are not authorized to access data has been moved to the implementation of a Auth0 rule

* Refactored component to handle Auth0 callback and redirect

* Removed un-needed redux action and action type

* Refactored private routes implementation

* Refactored component to let the privateRoute component to handle protected access

* Refactored component to remove redirect workaround

* Refactored component rendering

* Updated Storybook component due to ErrorPage component refactoring

* Formatting per eslint

* Removed unnecessary export syntax

* Revert to prior module import syntax

* Updated default module import

* Applied patch updates of dependency packages

* Refactored and avoiding mixing <Route component /> with <Route render />

* Defining defaultProps

* Reverting to previous Redux implementation to avoid creating separate Redux store in Storybook component

* Removed Redux store configs

* Minor styling tweak

* Redirect authorized users to dashboard instead of returning them to previous page when they try to load this error page

The useHistory hook conflicts with the 'history' argument used in main app test suite and causes the tests to fail.

* Refactored UI handling while fetching is still in progress
  • Loading branch information
jimmyzhen committed Feb 25, 2021
1 parent 2bc1dcc commit a3b7b7b
Show file tree
Hide file tree
Showing 11 changed files with 195 additions and 291 deletions.
16 changes: 8 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"auth0-js": "9.14.2",
"auth0-js": "9.14.3",
"axios": "^0.21.1",
"bootstrap": "4.6.0",
"chart.js": "^2.9.3",
Expand All @@ -23,7 +23,7 @@
"react-particles-js": "3.4.1",
"react-redux": "^7.2.1",
"react-router-dom": "^5.1.2",
"react-scripts": "4.0.1",
"react-scripts": "4.0.2",
"react-spring": "^8.0.20",
"redux": "^4.0.1",
"redux-thunk": "^2.3.0",
Expand All @@ -49,12 +49,12 @@
"not op_mini all"
],
"devDependencies": {
"@storybook/addon-actions": "6.1.15",
"@storybook/addon-links": "6.1.15",
"@storybook/addon-storyshots": "6.1.15",
"@storybook/addons": "6.1.15",
"@storybook/addon-actions": "6.1.18",
"@storybook/addon-links": "6.1.18",
"@storybook/addon-storyshots": "6.1.18",
"@storybook/addons": "6.1.18",
"@storybook/preset-create-react-app": "^3.1.4",
"@storybook/react": "6.1.15",
"@storybook/react": "6.1.18",
"@wojtekmaj/enzyme-adapter-react-17": "^0.4.1",
"enzyme": "3.11.0",
"enzyme-adapter-react-16": "1.15.6",
Expand All @@ -64,7 +64,7 @@
"eslint-plugin-react-hooks": "^4.0.8",
"jest-canvas-mock": "^2.2.0",
"prettier": "2.2.1",
"sass": "^1.25.0",
"sass": "1.32.8",
"storybook-react-router": "1.0.8",
"stylelint": "^13.2.0",
"stylelint-config-recommended": "^3.0.0"
Expand Down
9 changes: 1 addition & 8 deletions src/Auth/authActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ const auth = new Auth();

// Possible states for login and logout
export const LOGIN_REQUEST = 'LOGIN_REQUEST';
export const LOGIN_PENDING = 'LOGIN_PENDING';
export const LOGIN_SUCCESS = 'LOGIN_SUCCESS';
export const LOGIN_FAILURE = 'LOGIN_FAILURE';
export const LOGOUT_SUCCESS = 'LOGOUT_SUCCESS';
Expand All @@ -16,12 +15,6 @@ export function requestLogin() {
};
}

export function loginPending() {
return {
type: LOGIN_PENDING,
};
}

export function loginSuccess(payload) {
return {
type: LOGIN_SUCCESS,
Expand Down Expand Up @@ -69,6 +62,7 @@ export function logoutAsync() {

export function handleAuthCallbackAsync() {
return (dispatch) => {
dispatch(requestLogin());
auth.handleAuthentication((err, data) => {
if (err) {
return dispatch(loginError(`${err.error}: ${err.errorDescription}`));
Expand All @@ -88,5 +82,4 @@ export default {
loginError,
loginSuccess,
logout: logoutAsync,
loginPending,
};
10 changes: 0 additions & 10 deletions src/Auth/authReducer.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {
LOGIN_REQUEST,
LOGIN_PENDING,
LOGIN_SUCCESS,
LOGIN_FAILURE,
LOGOUT_SUCCESS,
Expand All @@ -12,7 +11,6 @@ export const defaultAuthState = {
profile: {},
message: '',
isFetching: false,
isPending: false,
isAuthenticated: false,
};

Expand All @@ -24,25 +22,17 @@ export function AuthReducer(state = defaultAuthState, action) {
isFetching: true,
isAuthenticated: false,
};
case LOGIN_PENDING:
return {
...state,
isPending: true,
isAuthenticated: false,
};
case LOGIN_SUCCESS:
return {
...state,
isFetching: false,
isPending: false,
isAuthenticated: true,
payload: action.payload,
};
case LOGIN_FAILURE:
return {
...state,
isFetching: false,
isPending: false,
isAuthenticated: false,
message: action.message,
};
Expand Down
49 changes: 17 additions & 32 deletions src/Auth/callback.jsx
Original file line number Diff line number Diff line change
@@ -1,56 +1,41 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { useSelector, useDispatch } from 'react-redux';
import { Redirect } from 'react-router-dom';
import actions, { loginPending } from './authActions';
import actions from './authActions';

export function Callback({
location,
message,
handleAuthCallback,
loginInProgress,
}) {
// FIXME: Workaround to make the <Redirect /> to work
loginInProgress();
function Callback({ location }) {
const { isAuthenticated, isFetching, message } = useSelector((state) => state.auth);
const dispatch = useDispatch();
// Handle authentication if expected values are in the URL.
if (/access_token|id_token|error/.test(location.hash)) {
handleAuthCallback();
return <Redirect to="/dashboard" />
dispatch(actions.handleAuthCallback());
}

const callbackMsg = message || 'Authenticating...';

return (
<div className="authLoading">
<span className="oi oi-shield" />
<h3>{callbackMsg}</h3>
</div>
<>
{isFetching && !isAuthenticated ? (
<div className="authLoading">
<span className="oi oi-shield" />
<h3>{message || 'Authenticating...'}</h3>
</div>
) : (
<Redirect to="/dashboard" />
)}
</>
);
}

Callback.propTypes = {
location: PropTypes.shape({
hash: PropTypes.string,
}),
message: PropTypes.string,
handleAuthCallback: PropTypes.func.isRequired,
loginInProgress: PropTypes.func.isRequired,
};

Callback.defaultProps = {
location: {
hash: '',
},
message: '',
};

const mapStateToProps = (state) => ({
message: state.auth.message,
});

const mapDispatchToProps = (dispatch) => ({
handleAuthCallback: () => dispatch(actions.handleAuthCallback()),
loginInProgress: () => dispatch(loginPending()),
});

export default connect(mapStateToProps, mapDispatchToProps)(Callback);
export default Callback;
33 changes: 15 additions & 18 deletions src/Auth/privateRoute.jsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,27 @@
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
import { useSelector } from 'react-redux';
import AnimatedLoadingIcon from '../lib/ui/loading';

function PrivateRoute({ component, ...args }) {
function PrivateRoute({ children, ...args }) {
const { isAuthenticated, isFetching, profile } = useSelector((state) => state.auth);

const { isAuthenticated, isFetching, isPending, profile } = useSelector((state) => state.auth);
const hasAccess = profile.user_metadata && profile.user_metadata.hasAccess;

// Route users back to homepage if not authenticated
if (!isPending && !isFetching && !isAuthenticated) {
return <Redirect to="/" />;
}

// Route users to error page if registered users are
// not allowed to have data access
if (!isPending && !isFetching && isAuthenticated) {
if (!hasAccess) {
return <Redirect to="/error" />;
function handleAccess() {
if (!isAuthenticated && !profile.user_metadata) {
return <Redirect exact to="/" />;
}

return <Route component={children} {...args} />;
}

return (
<Route
component={component}
{...args}
/>
<>
{!isFetching ? handleAccess() : (
<div className="mt-5 py-5">
<AnimatedLoadingIcon isFetching={isFetching} />
</div>
)}
</>
);
}

Expand Down

0 comments on commit a3b7b7b

Please sign in to comment.