a better pattern for authenticated React components and routes
<p>I'm working on adding <a href="https://auth0.com/docs/libraries/lock" rel="nofollow noreferrer">Auth0</a> authentication to my React app, and even though I have it working, I feel like there's a better way to approach this. I'm struggling to figure out a better pattern for the authentication logic.</p>

<p>The app setup is <a href="https://facebook.github.io/react/" rel="nofollow noreferrer">react</a> + <a href="https://github.com/reactjs/redux" rel="nofollow noreferrer">redux</a> + <a href="https://github.com/reactjs/react-router-redux" rel="nofollow noreferrer">react-router-redux</a> + <a href="https://github.com/yelouafi/redux-saga" rel="nofollow noreferrer">redux-saga</a> + <a href="https://github.com/facebook/immutable-js" rel="nofollow noreferrer">immutable</a> + <a href="https://github.com/auth0/lock" rel="nofollow noreferrer">auth0-lock</a>.</p>

<p>Beginning at the top, the <code>App</code> component defines the basic page layout, both <code>Builder</code> and <code>Editor</code> components require the user to be logged in, and <code>authenticated()</code> wraps each in a Higher Order Component responsible for handling authentication.</p>

<pre><code>// index.js

import App from './containers/App';
import Builder from './containers/Builder';
import Editor from './containers/Editor';
import Home from './containers/Home';
import Login from './containers/Login';

import AuthContainer from './containers/Auth0/AuthContainer';

...

ReactDOM.render(
  &lt;Provider store={reduxStore}&gt;
    &lt;Router history={syncedHistory}&gt;
      &lt;Route path={'/'} component={App}&gt;
        &lt;IndexRoute component={Home} /&gt;
        &lt;Route path={'login'} component={Login} /&gt;
        &lt;Route component={AuthContainer}&gt;
          &lt;Route path={'builder'} component={Builder} /&gt;
          &lt;Route path={'editor'} component={Editor} /&gt;
        &lt;/Route&gt;
      &lt;/Route&gt;
      &lt;Redirect from={'*'} to={'/'} /&gt;
    &lt;/Router&gt;
  &lt;/Provider&gt;,
  document.getElementById('app')
);
</code></pre>

<p>At the moment, <code>AuthContainer</code> doesn't do much except check the redux store for <code>isLoggedIn</code>. If <code>isLoggedIn</code> is false, the user is not allowed to view the component, and is redirected to <code>/login</code>.</p>

<pre><code>// containers/Auth0/AuthContainer.js

import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';

import { redirectToLogin } from './Auth0Actions';

class AuthContainer extends React.Component {
  componentWillMount() {
    if (!this.props.isLoggedIn) {
      this.props.actions.redirectToLogin();
    }
  }
  render() {
    if (!this.props.isLoggedIn) {
      return null;
    }
    return this.props.children;
  }
}

// mapStateToProps(), mapDispatchToProps()

export default connect(mapStateToProps, mapDispatchToProps)(AuthContainer);
</code></pre>

<p>The next piece is Auth0. The Auth0 Lock works in "redirect" mode, which means the user will leave the app to log in, and then be redirected back to the app at <code>/login</code>. As part of the redirect, Auth0 attaches a token as part of the URL, which needs to be parsed when the app loads.</p>

<pre><code>const lock = new Auth0Lock(__AUTH0_CLIENT_ID__, __AUTH0_DOMAIN__, {
  auth: {
    redirect: true,
    redirectUrl: `${window.location.origin}/login`,
    responseType: 'token'
  }
});
</code></pre>

<p>Since Auth0 will redirect to <code>/login</code>, the <code>Login</code> component also needs authentication logic. Similar to <code>AuthContainer</code>, it checks the redux store for <code>isLoggedIn</code>. If <code>isLoggedIn</code> is true, it redirects to the root <code>/</code>. If <code>isLoggedIn</code> is false, it'll attempt to authenticate.</p>

<pre><code>// containers/Login/index.js

import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';

import { authenticate, redirectToRoot } from '../Auth0/Auth0Actions';

class Login extends React.Component {
  componentDidMount() {
    if (!this.props.isLoggedIn) {
      this.props.actions.authenticate();
    }
    else {
      this.props.actions.redirectToRoot();
    }
  }
  render() {
    return (
      &lt;div&gt;Login Page&lt;/div&gt;
    );
  }
}

// mapStateToProps(), mapDispatchToProps()

export default connect(mapStateToProps, mapDispatchToProps)(Login);
</code></pre>

<p>With these pieces in place, my integration with Auth0 seems to be working. However, I now have <code>AuthContainer</code> and <code>Login</code> component, and they are very similar. I can't place the <code>Login</code> component as a child to <code>AuthContainer</code> since the login page does not actually require the user to be logged in.</p>

<p>Ideally, all authentication logic lives in one place, but I'm struggling to figure out another way to get it working, especially with the special case of the Auth0 redirect. I can't help but think that there must be a different approach, a better pattern for authentication flow in a react + redux app.</p>

<p>One thing that would be helpful is to better understand how to dispatch an async action on page load, before the app starts initializing. Since Auth0 works with callbacks, I'm forced to delay setting the redux initial state until after Auth0 invokes the registered callback. What is the recommended way to handle async actions on page load?</p>

<hr>

<p>I've left out some pieces for brevity, like the actions and sagas, but I'll be more than happy to provide those if it'll be helpful.</p>