In my last blog I covered setting up a basic Rails + Angular.js webapp with Rails-API and Yeoman. As promised in this post we will look at building a simple Authentication and Authorization mechanism on top of our application.
Plase note, the code shown in this blog is available on Github.
For authentication we will use Devise and for Authorization we will use Pundit, which are pretty standard gems used for these purposes.
###Devise
Devise and Rails are pretty easy to setup with the vanilla installations but since we are using rails-api we have a few issues -
- Rails-API has a few middlewares missing which may cause a problem
- We cannot use cookies for API based authentication (middleware are missing and APIs should not rely on cookies)
- Devise 3.1+ removed support for token based authentication which would have been the default choice for API based authentication
For issue one, following the basic Devise installation you will encounter this error -
NameError (undefined local variable or method `mimes_for_respond_to' for DeviseController:Class):
devise (3.1.1) app/controllers/devise_controller.rb:13:in `<class:DeviseController>'
This is easily fixable by adding the middleware to our ApplicationController -
class ApplicationController < ActionController::API
include ActionController::MimeResponds
Since we cannot use cookies we will setup a simple token based authentication mechanism, which works like this -
- User enters a username and password and sends a login request to the server.
- Devise authenticates the user, and sends back a token and encrypted email. The token is also persisted on the server for the particular user.
- We store the returned token and encrypted email on the client side as well (lets say in localStorage / or just in memory using an Angular.js service).
- Subsequent requests from the client add the token and encrypted email in the request header.
- On receiving a request, the server extracts and matches the token & the user and authenticates the request.
- When the user logs out, we delete the token from the client side and the server side.
Please note that this approach is not battle-tested but is pretty standard and should be fine over HTTPS and non-critical applications.
Now devise has removed the out-of-the-box token based auth as mentioned here so we will have to do some work to set this up. Thankfully José Valim being the nice guy that he is, also pointed us to a possible solution here. Plus, I am no genuius, I merely stand on the shoulder of giants.
There is a lot of code to get this all working so I will recommend that you have a look at the related files on Github.
- Client side controller
- Client side web service
- Routes file for our custom login / logout paths
- Application Controller changes for Devise setup
- Our own custom Devise controller
- A security "concern" for the user
With all this in place we will get our desired authentication mechanism working.
###Pundit
To test authorization we will use Pundit and the very original User has many Posts data model. Pundit is pretty easy to setup out of the box since we have done the hard work of setting up rails-api and devise. I pretty much followed the documentation and got it working. You can see the code changes made in ApplicationController and sample Post Policy.
Finally, we want to write a reponse interceptor on the client side so that if the response code from the server is 401 we show the user the login page instead of throwing an error. This is easily done with Angular.js config as done here.
That's it, we now a basic and secure Angular.js + Rails-API application working. All you need to do is to add business logic and you have a fast*, clean working application built in real quick time.
*I would also throw in Redis for the User / Token based lookup.