AngularJS -- Quick Role-Based Authentication
We’ve recently started on a new product at work using the AngularJS framework. One of the most useful resources when starting out was the #angularjs room on freenode. There seems to be a really large, vibrant community around AngularJS and that’s always helpful when starting from scratch on a new framework. One of the best resources in the room was David Mosher, who helped us get set up with Lineman – an awesome build framework based on Grunt – and pointed us to some of his video tutorials. Honestly, these were a great resource for getting started and I highly recommend watching them. The following tutorial is heavily inspired by the examples David provided.
Before starting with authentication you’ll need to have some routes. Let’s start with a logged out state, and a logged in state:
In this simple example we have a state for logging in and another state for seeing the app once logged in. In order to limit access to authenticated users we are going to have to check their status before allowing them to view the app state.
This is the real meat of the solution. They key is adding this new function that watches the $routeChangeStart event and reacts on it. Once this event fires, we check if the route is clean (using some fancy underscore.js). It’s easier to enumerate all the routes that don’t require rather than all the ones that do. Presumably, there are significantly more that do.
As you can see it’s fairly intuitive. If the route needs to be authenticated and the user is not logged in, then redirect them back to the login page. This doesn’t necessarily have to be a redirect, this can be an API request to see if their session cookie is still active. You can build on this and make it as sophisticated as you need – but the skeleton will probably remain the same.
Handling Failed Authentication from the API
There is another possibility, of course, in which the user is not authenticated and initiates an action that results in API request. The result will be a 401 response and possibly some sort of authentication exception. Handling this 401 should be done as follows (all credit goes to David Mosher on this one):
As you can see, we add an interceptor to capture the promises from the $httpProvider and validate them to make sure they are not 401 errors. If they happen to be 401 then we send the user back to the login page.
Mixing in UI-Router
One of the more useful plugins for angular is ui-router. This plugin allows you to create more powerful constructs with your states by using inheritance and substate transitions. Using ui-router, our route definition would be as follows:
Similarly, we’ll need to make some minor changes to the authentication watch function – from watching route change, to state change.
Fortunately, we don’t have to change the 401 intercepter and everything works just as before.
Expanding into Role-Based Authentication
The last step that you may want to take is role-based authentication. This is starting to add a bit more complexity to your app, and may not be necessary on the JS side. However, if you do need it, it’s a critical component.
The way to approach this would be have some sort of user object with roles, ideally specified in an injectable service:
Then inject this UserService into the authentication watcher for further authorization, after the authentication step. Or, probably more ideally, you would want to create an AuthorizationService and separate it from the UserService (i.e. separation of concerns). It would look something like this:
I have had quite a few requests for a working code sample for this post. I spent some time putting one together and you can find it here. Note that the working code is slightly different than the code above, but the main idea is the same.