How to implement authenticated routes in React Router 4? How to implement authenticated routes in React Router 4? javascript javascript

How to implement authenticated routes in React Router 4?


You're going to want to use the Redirect component. There's a few different approaches to this problem. Here's one I like, have a PrivateRoute component that takes in an authed prop and then renders based on that props.

function PrivateRoute ({component: Component, authed, ...rest}) {  return (    <Route      {...rest}      render={(props) => authed === true        ? <Component {...props} />        : <Redirect to={{pathname: '/login', state: {from: props.location}}} />}    />  )}

Now your Routes can look something like this

<Route path='/' exact component={Home} /><Route path='/login' component={Login} /><Route path='/register' component={Register} /><PrivateRoute authed={this.state.authed} path='/dashboard' component={Dashboard} />

If you're still confused, I wrote this post that may help - Protected routes and authentication with React Router v4


Tnx Tyler McGinnis for solution.I make my idea from Tyler McGinnis idea.

const DecisionRoute = ({ trueComponent, falseComponent, decisionFunc, ...rest }) => {  return (    <Route      {...rest}      render={        decisionFunc()          ? trueComponent          : falseComponent      }    />  )}

You can implement that like this

<DecisionRoute path="/signin" exact={true}            trueComponent={redirectStart}            falseComponent={SignInPage}            decisionFunc={isAuth}          />

decisionFunc just a function that return true or false

const redirectStart = props => <Redirect to="/orders" />


(Using Redux for state management)

If user try to access any url, first i am going to check if access token available, if not redirect to login page, Once user logs in using login page, we do store that in localstorage as well as in our redux state. (localstorage or cookies..we keep this topic out of context for now).
since redux state as updated and privateroutes will be rerendered. now we do have access token so we gonna redirect to home page.

Store the decoded authorization payload data as well in redux state and pass it to react context. (We dont have to use context but to access authorization in any of our nested child components it makes easy to access from context instead connecting each and every child component to redux)..

All the routes that don't need special roles can be accessed directly after login.. If it need role like admin (we made a protected route which checks whether he had desired role if not redirects to unauthorized component)

similarly in any of your component if you have to disable button or something based on role.

simply you can do in this way

const authorization = useContext(AuthContext);const [hasAdminRole] = checkAuth({authorization, roleType:"admin"});const [hasLeadRole] = checkAuth({authorization, roleType:"lead"});<Button disable={!hasAdminRole} />Admin can access</Button><Button disable={!hasLeadRole || !hasAdminRole} />admin or lead can access</Button>

So what if user try to insert dummy token in localstorage. As we do have access token, we will redirect to home component. My home component will make rest call to grab data, since jwt token was dummy, rest call will return unauthorized user. So i do call logout (which will clear localstorage and redirect to login page again). If home page has static data and not making any api calls(then you should have token-verify api call in the backend so that you can check if token is REAL before loading home page)

index.js

import React from 'react';import ReactDOM from 'react-dom';import { Router, Route, Switch } from 'react-router-dom';import history from './utils/history';import Store from './statemanagement/store/configureStore';import Privateroutes from './Privateroutes';import Logout from './components/auth/Logout';ReactDOM.render(  <Store>    <Router history={history}>      <Switch>        <Route path="/logout" exact component={Logout} />        <Route path="/" exact component={Privateroutes} />        <Route path="/:someParam" component={Privateroutes} />      </Switch>    </Router>  </Store>,  document.querySelector('#root'));

History.js

import { createBrowserHistory as history } from 'history';export default history({});

Privateroutes.js

import React, { Fragment, useContext } from 'react';import { Route, Switch, Redirect } from 'react-router-dom';import { connect } from 'react-redux';import { AuthContext, checkAuth } from './checkAuth';import App from './components/App';import Home from './components/home';import Admin from './components/admin';import Login from './components/auth/Login';import Unauthorized from './components/Unauthorized ';import Notfound from './components/404';const ProtectedRoute = ({ component: Component, roleType, ...rest })=> { const authorization = useContext(AuthContext);const [hasRequiredRole] = checkAuth({authorization, roleType});return (<Route  {...rest}  render={props => hasRequiredRole ?   <Component {...props} /> :   <Unauthorized {...props} />  } />)}; const Privateroutes = props => {  const { accessToken, authorization } = props.authData;  if (accessToken) {    return (      <Fragment>       <AuthContext.Provider value={authorization}>        <App>          <Switch>            <Route exact path="/" component={Home} />            <Route path="/login" render={() => <Redirect to="/" />} />            <Route exact path="/home" component={Home} />            <ProtectedRoute            exact            path="/admin"            component={Admin}            roleType="admin"          />            <Route path="/404" component={Notfound} />            <Route path="*" render={() => <Redirect to="/404" />} />          </Switch>        </App>        </AuthContext.Provider>      </Fragment>    );  } else {    return (      <Fragment>        <Route exact path="/login" component={Login} />        <Route exact path="*" render={() => <Redirect to="/login" />} />      </Fragment>    );  }};// my user reducer sample// const accessToken = localStorage.getItem('token')//   ? JSON.parse(localStorage.getItem('token')).accessToken//   : false;// const initialState = {//   accessToken: accessToken ? accessToken : null,//   authorization: accessToken//     ? jwtDecode(JSON.parse(localStorage.getItem('token')).accessToken)//         .authorization//     : null// };// export default function(state = initialState, action) {// switch (action.type) {// case actionTypes.FETCH_LOGIN_SUCCESS://   let token = {//                  accessToken: action.payload.token//               };//   localStorage.setItem('token', JSON.stringify(token))//   return {//     ...state,//     accessToken: action.payload.token,//     authorization: jwtDecode(action.payload.token).authorization//   };//    default://         return state;//    }//    }const mapStateToProps = state => {  const { authData } = state.user;  return {    authData: authData  };};export default connect(mapStateToProps)(Privateroutes);

checkAuth.js

import React from 'react';export const AuthContext = React.createContext();export const checkAuth = ({ authorization, roleType }) => {  let hasRequiredRole = false;  if (authorization.roles ) {    let roles = authorization.roles.map(item =>      item.toLowerCase()    );    hasRequiredRole = roles.includes(roleType);  }  return [hasRequiredRole];};

DECODED JWT TOKEN SAMPLE

{  "authorization": {    "roles": [      "admin",      "operator"    ]  },  "exp": 1591733170,  "user_id": 1,  "orig_iat": 1591646770,  "email": "hemanthvrm@stackoverflow",  "username": "hemanthvrm"}