Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Universal Auth Redux & React Router

I'm having some trouble with authentication with redux and react router and am hoping for some clarification on how to best solve my problem.

I have a login component that when submitted dispatches the following action:

export const loginUser = (creds) => {
  return dispatch => {

    dispatch(requestLogin(creds))

    return fetch('http://127.0.0.1:4000/api/login', {
      ...
    })
      .then(res => {
        dispatch(receiveLogin())
        browserHistory.push('/admin')
        ...
      })
      ...
  }
}

The receiveLogin action updates the isAuthenticated flag in the state to true. My protected route has the following onEnter hook:

export default (state) => {
  return {
    path: '/',
    component: App,
    childRoutes: [
      {
        path: 'protected',
        component: Protected,
        ...
        onEnter(nextState, replaceState) {
          const loggedIn = //check state.isAuthenticated
          if (!loggedIn) {
            replaceState(null, 'login')
          }
        }
      }
    ]
  }
}

As you can see, I am passing the state in as an argument. This gives me access to the initial state from the server as well as on the client. However, after my login action happens, obviously the onEnter hook will still be using the initial state.

What I want to know is the best way to get the current state after the isAuthenticated flag is updated and use that in my onEnter hook instead. Or, is my approach totally flawed and should I be doing this a different way entirely?

like image 491
Andrew Avatar asked Jan 21 '16 19:01

Andrew


Video Answer


1 Answers

I have found that using the onEnter hooks is not the best approach in my applications for handling this type of auth logic and connecting to a redux store.

One of the (several) pitfalls here is that even if you could login a user to your 'protected' route, if they were to logout while in 'protected' they would never get redirected to 'login' because it wouldn't trigger the onEnter.

An alternative approach is to use Higher Order Components, see Dan Abramov's post about using them over mixins, and I find them really useful in this context as well. There have been a few gists/example repos out there of how to use them, but I've recently created a library redux-auth-wrapper specifically for this purpose. Its pretty new but I think it might be useful and avoids some dangers when using HOC's incorrectly for auth which can result in infinite-loop-redirects.

The idea of using a HOC is that it is a function that when applied to a components extends its functionality in some way. Users of redux are most likely familiar with connect to enhance Components with the subscription for the store.

In this case, the Component you write is unaware of being protected by authentication/authorization and when applied the HOC function, a new component with the given checks is returned. Should those pass, the wrapped component is returned, else the user is redirected to a place to login (or somewhere else if its an authorization issue). The HOC makes use of componentWillMount and componentWillReceiveProps lifecycle methods to determine wether or not the user is allowed to see the given component. This allows support for redirecting out of components on authentication change even if the route does not.

I'd also check out examples of a similar strategy here: https://github.com/joshgeller/react-redux-jwt-auth-example.

And here is an example that uses the onEnter for auth with some store trickery, but I believe it will not handle the edge case described above: https://github.com/CrocoDillon/universal-react-redux-boilerplate/blob/master/src/routes.jsx.

like image 100
mjrussell Avatar answered Oct 14 '22 12:10

mjrussell