Authenticated Routes with Meteor and React Router

With React Router and Meteor, it's very easy to create applications that have views that require an authenticated user. Meteor has helpful methods built in to check if the user is authenticated and the lifecycle of React components will help us manage what should be rendered. These two aspects together will help us created protected views with very little code.

Keep in mind that protected views is only one part of securing your application. Methods and collection publications/subscriptions still need to be secured server side.

If you need a quick start with React, React Router, and Meteor, take a look at my previous blog posts on the matter.

A Look at the Routes

Let's look at our original routes component.

const {  
  Router,
  Route,
  IndexRoute,
  history
} = ReactRouter;

const browserHistory = history.createHistory();

Routes = React.createClass({  
  getInitialState: function() {
    return {};
  },
  render: function () {
    return (
      <Router history={browserHistory}>
        <Route path="/" component={App}>
          <IndexRoute component={Index}/>
          <Route path="items" component={Items}/>
        </Route>
      </Router>
    );
  }
});

Notice how the Items component and Index component are both children of our App component. Those components cannot be rendered without the App component being rendered. In React, any child component of our App component is inherently bound to the lifecycle of the App component. This is great for us because we can add logic to different parts of the lifecycle and choose what will be render and won't be rendered. But does that mean? It means that we can easily check for an authenticated user in our components with minimal code thanks to Meteor and React.

Adding Authenticated Routes

Meteor comes with a reactive method, Meteor.userId(), which should return a user's id if they are currently authenticated. With the ReactMeteorData mixin for our React components, we can perform actions on data reactively when it changes. We will use Meteor.userId() as our method for protecting our routes.

Let's create a container component that will display all of our authentication-protected views. We will need to check to see if a user id is present. If it isn't, navigate to a page where the user can authenticate.

AuthenticatedApp = React.createClass({  
  mixins: [ReactMeteorData, ReactRouter.History],
  getMeteorData: function() {
    return {
      isAuthenticated: Meteor.userId() !== null
    };
  },
  componentWillMount: function() {
    // Check that the user is logged in before the component mounts
    if (!this.data.isAuthenticated) {
      this.history.pushState(null, '/signin');
    }
  },
  componentDidUpdate: function(prevProps, prevState) {
    // Navigate to a sign in page if the user isn't authenticated when data changes
    if (!this.data.isAuthenticated) {
      this.history.pushState(null, '/signin');
    }
  },
  getInitialState: function() {
    return {};
  },
  render: function() {
    return (
      <div>
        {/* Views will be rendered here */}
        {this.props.children}
      </div>
    );
  }
});

Now we need to update our Routes component to use the component for auth-protected views as well as a view for the user to authenticate. Additional views requiring auth can simply be added as a child to our route checking auth.

Routes = React.createClass({  
  getInitialState: function() {
    return {};
  },
  render: function () {
    return (
      <Router history={browserHistory}>
        <Route path="/" component={App}>
          <IndexRoute component={Index}/>
          <Route path="items" component={Items}/>
          <Route path="signin" component={SignIn}/>
        </Route>
        <Route path="/app" component={AuthenticatedApp}>
          {/* Authentication-protected routes go here */}
          <IndexRoute component={AuthenticatedAppIndex}/>
        </Route>
      </Router>
    );
  }
});

When a user tries to navigate to /app/* and they haven't been authenticated, they will automatically be redirected to the sign in page. Additionally, signing out will redirect the user to the sign in page if they viewing an auth-protected view.

Conclusion

Authentication-protected views are easy to add with Meteor and React. Meteor gives us all the tools we need to do so while React and React Router makes it simple to manage views.

Alex Garibay's Picture

Alex Garibay

Elixir developer. Maker of things at DockYard.