66

What are the best practices for authorization checking prior to a component mounting?

I use react-router 1.x

Here are my routes

React.render((
  <Router history={History.createHistory()}>
    <Route path="/" component={Dashboard}></Route>
    <Route path="/login" component={LoginForm}></Route>
  </Router>
), document.body);

Here is my Dashboard component:

var Dashboard = React.createClass({
  componentWillMount: function () {
    // I want to check authorization here
    // If the user is not authorized they should be redirected to the login page.
    // What is the right way to perform this check?
  },

  render: function () {
    return (
      <h1>Welcome</h1>
    );
  }
});
Brickgao
  • 35
  • 4
theo
  • 704
  • 1
  • 5
  • 9
  • 3
    https://github.com/rackt/react-router/tree/master/examples/auth-flow how are you checking though? from a cookie? from a server call? I think it's typically done in the `onEnter` of the `Route` though, rather than `componentWillMount`. ` – Dylan Oct 02 '15 at 00:08

3 Answers3

71

Updated solution for React router v4

<Route 
  path="/some-path" 
  render={() => !isAuthenticated ?
    <Login/> :
    <Redirect to="/some-path" />
}/>

React router up to v3

Use 'onEnter' event and in callback check if the user is authorized:

<Route path="/" component={App} onEnter={someAuthCheck}>  

const someAuthCheck = (nextState, transition) => { ... }
Pawel
  • 1,672
  • 2
  • 18
  • 24
  • 7
    Things have got worse in terms of examples and documentation. The "auth-flow" example doesn't work for me and it's not easy to find information about what the second param of the handler should accept so I can try different things out. – backdesk Jan 19 '16 at 13:18
  • onEnter(nextState, replace, callback?) "Called when a route is about to be entered. It provides the next router state and *a function* to redirect to another path. this will be the route instance that triggered the hook." – backdesk Jan 19 '16 at 13:19
  • @Pawel - v1.0.3. I have to revert to using the old example as per: https://github.com/rackt/react-router/commit/a6028e258724e24cad333349261e877d30243a71. So replaceState({ nextPathname: nextState.location.pathname }, '/login') instead. – backdesk Jan 19 '16 at 13:51
  • how can i use the onEnter check with require.ensure – jasan Sep 28 '16 at 06:27
  • 2
    No. Not a good solution. Usually you keep info about authorization in your state. Like that, for example: 'isAuth: true'. And you have this variable in your components as a prop, you simply pass it yourself to where you need it. But you can't pass any state variable to 'Route'. So you are compelled to make 'fetch' calls, Ajax requests to your server, from 'Route' to figure out if the user is logged in. It is nonsense and bad design having a simple state variable and make Ajax requests with the same purpose. – Green Oct 07 '16 at 19:57
  • 2
    @Green There might be some confusion here: you're saying that you should use information from the state. But in an `onEnter` function you can simply access your simple state variable (state is first argument of function), making an extra ajax request unnecessary – Laurens Rietveld Oct 13 '16 at 15:11
  • in the cited example, these specific lines (L120-L138) show how to change the "next state" when the person is not authenticated: https://github.com/ReactTraining/react-router/blob/4f2746155ce1cb6bd194864f9feb1599b2e46c51/examples/auth-flow/app.js#L120-L138 – broc.seib Oct 15 '16 at 22:48
  • 3
    For those coming from a Google Search: `onEnter` no longer exists on `react-router-4`. See: https://stackoverflow.com/questions/42768620/onenter-not-called-in-react-router – Alex Johnson Sep 19 '17 at 16:38
6

With react-router 4 you have access to the Route props inside the component. To redirect a user you just have to push the new URL to the history. In your example, the code would be:

var Dashboard = React.createClass({
  componentWillMount: function () {
    const history = this.props.history; // you'll have this available
    // You have your user information, probably from the state
    // We let the user in only if the role is 'admin'
    if (user.role !== 'admin') {
      history.push('/'); // redirects the user to '/'
    }
  },

  render: function () {
    return (
      <h1>Welcome</h1>
    );
  }
});

At the docs, they show another way to do it, by using the render property, instead of component. They define a PrivateRoute, that makes the code very explicit when you define all your routes.

Daniel Reina
  • 5,764
  • 1
  • 37
  • 50
-1

If you want to apply authorization on multiple components then you can do it like this.

<Route onEnter={requireAuth} component={Header}>
    <Route path='dashboard' component={Dashboard} />
    <Route path='events' component={Events} />
</Route>

For single component you can do

<Route onEnter={requireAuth} component={Header}/>

function requireAuth(nextState, replaceState) {
  if (token || or your any condition to pass login test)
  replaceState({ nextPathname: nextState.location.pathname }, 
  '/login')
}
Zaman Afzal
  • 2,009
  • 17
  • 23