mjrussell / redux-auth-wrapper

A React Higher Order Component (HOC) for handling Authentication and Authorization with Routing and Redux
https://mjrussell.github.io/redux-auth-wrapper
MIT License
2.2k stars 242 forks source link

Best place to load existing JWT token #158

Open james-lukensow opened 7 years ago

james-lukensow commented 7 years ago

I've been looking at the following example (https://github.com/mjrussell/react-redux-jwt-auth-example/tree/react-router-redux) since it's the closest to my existing setup (react-boilerplate).

For users who refresh the page or come back, I'm curious as to what is the preferred way to load an existing JWT token.

I noticed inside /src/index.js, we're loading the token then calling the action:

let token = localStorage.getItem('token');
if (token !== null) {
    store.dispatch(loginUserSuccess(token));
}

Is this a fairly standard way of loading existing tokens and setting the state.auth?

Lastly, all of the examples I've found seem to following the same approach...

export const requireAuthentication = UserAuthWrapper({
  authSelector: state => state.auth,
  predicate: auth => auth.isAuthenticated,
  redirectAction: push,
  wrapperDisplayName: 'UserIsJWTAuthenticated'
})

My question is, authSelector and predicate, I'm not entirely sure what I should be referencing? Is this the state that my Login action/reducer sets after a successful login?

james-lukensow commented 7 years ago

Not sure if I'm doing this correctly or not, but I'm using the following https://github.com/react-boilerplate with redux-auth-wrapper.

The only way I can get the authSelector to work is via the following:

export const UserIsAuthenticated = UserAuthWrapper({
  authSelector: state => state.toJS().global.user,
  wrapperDisplayName: 'UserIsAuthenticated',
  redirectAction: push,
});

I was under the impression, that since react-boilerplate uses immutable objects, I could get the data via: state.get().

The state I'm attaching auth (which is an object of the user token decoded) and token to, are found in global.

I tried using a selector, but I kept getting errors as well.

Is it okay practice to use ".toJS()" to pull that variables out of the state?

mgambati commented 7 years ago

I'm doing this way, my authReducer default case returns a Object with isLoading key set to true.

Then on index.js :

// ... imports above
const authSession = findSavedAuthSession()
if (authSession) {
  store.dispatch(loginUserSuccess(authSession))
} else {
  store.dispatch(loginUserFail())
}

findSavedAuthSession function

export function findSavedAuthSession () : AuthState | boolean {
  const token: ?string = localStorage.getItem('auth_token')
  if (token) {
    const decoded: Object = jwt(token)
    decoded.token = token //
    const stillValid: bool = decoded.exp > Date.now() / 1000
    return stillValid ? Object.assign(decoded, { token }): false
  }
  return false
}

Note ta AUTH_LOGIN_SUCCESS and AUTH_LOGIN_FAIL actions set isLoading to false. This way redux-auth-wrapper can know when it ended and redirect to the right routes.

james-lukensow commented 7 years ago

@mgambati Thanks for the insight!