Specifying whether or not routes should check for authentication


#1

I’ve been writing more and more apps in Ember that require a login of some sort. So far I’ve done one of two things to make sure a user is signed in before letting them actually see anything.

  1. Have a base route (usually called SecureRoute) that checks for the presence of an auth token in localStorage. If it’s not there, then redirect them to sign in. All routes that require an authenticated user would extend from SecureRoute instead of just Route.
  2. Nest all authenticated routes under a resource that does the authentication in its beforeModel (usually like this.resource('default', {path: '/'}). This has the advantage of not forcing me to specify authenticated routes with inheritance, which makes refactoring easier.

Option 2 is my preferred approach at the moment, but it has a significant downside: if I want to have a public page on ‘/’ (say a marketing page?) then it will redirect to sign in, and there’s not much I can do about it.

The approach I take to this problem in Rails is to have a before_filter :authenticate_user! on my base controller and any public controllers can opt out using skip_before_filter. AFAIK, there’s no way to make my DefaultIndexRoute skip it’s parent’s beforeModel, and I don’t think there should be. It would be nice, though, to be able to specify whether a route is secure without having to change the inheritance of all my routes.

Has anyone found a better approach to this? Is there something I’m missing here?


#2

How about extending each route that needs authentication with a mixin? Let’s say AuthMixin, and that contains the auth logic in the before model call?


#3

Check out how Ember-Simple-Auth does their AuthenticatedRouteMixin: http://ember-simple-auth.simplabs.com/api.html#Ember-SimpleAuth-AuthenticatedRouteMixin


#4

My point here is that I want authentication to be the default in my routes, I’d rather have public routes opt out. The way I’ve solved it for the time being is by allowing routes to have a skipsAuthentication property and having ApplicationRoute check the leaf route for the truthiness of that property:

App.SomePublicRoute = Ember.Route.extend({
  skipsAuthentication: true
});

App.SomePrivateRoute = Ember.Route.extend({
});

App.ApplicationRoute = Ember.Route.extend({
  beforeModel: function(transition) {
    var leafRoute = transition.state.handlerInfos.slice(-1)[0];
    
    if (leafRoute && leafRoute.handler.skipsAuthentication) {
      return;
    }
    
    if (window.localStorage.getItem('auth_token')) {
      var isSignedIn = true;
      this.controllerFor('application').set('isSignedIn', isSignedIn);
    } else {
      this.transitionTo('session');
    }
  }

It feels somewhat hacky, but leaves us with the ability to have all routes authenticated by default, which is what we were going for. Is there a better way to do this?


#5

You can change the generated Ember.Route's default type, and for those you generate would extend App.AuthRoute.

Here is a sample how you might achieve this: http://jsfiddle.net/NQKvy/670/

The takeaway is the generated Index route is an instance of of App.AuthRoute


#6

Related links to recent discussions and recipes for implementing secure-by-default routes with Ember Simple Auth: