Angular Authentication with Rails4 and Devise

Intro

One of my latest projects at Gogobot is an Angular
application (mostly for internal editorial use).

Right off the bat, I’ve decided that this will be a complete separate app,
based on Angular in the client side and Rails 4 on the server side.

The first challenge you go through when you have an application built this way
is “How am I going to manage authentication?”

One of the best resources I have encountered on the subject is
Techniques for authentication in AngularJS applications, however, this only explains the client side part of things and leaves out the server side setup.

Most of the solutions I tried online did not work or the solutions were not
current for Rails 4.

CORS

The one thing that is challenging here is setting up the CORS for Angular and
Rails to work together, but first, lets explain what the problem here.

Our rails app is started on http://localhost:3000 and the Angular grunt
server is running on http://localhost:9000.

This is what’s called a Cross Origin request, meaning, it’s not coming from the
same origin.

Before the browser creates a POST request, it will send an OPTIONS request
to the same page. That response is than checked to see whether this origin is
allowed on the server.

If for some reason the OPTIONS request fails, the browser will not attemt the
POST request.

Here’s what it looks like on the browser

And here’s the server’s response with the important part highlighted

Setting up your rails application

The rails application is pretty much standard rails application, once the
application is created with rails new your-app-name I just added gem 'devise' to the Gemfile and ran bundle install.

This is all pretty basic and you just need to follow the README from the devise
repository on Github.

If you Google “Rails 4 CORS” or anything remotely resembling that search term,
you will likely reach articles/blogs that recommend you include rack-cors and
some configuration.

This did not work for me at all so I ended up rolling my own with some tips
from @elado

After the app is set up I created a file lib/cors.rb

class Cors
  def initialize(app)
    @app = app
  end

  def call(env)
    status, headers, body = @app.call(env)

    headers['Access-Control-Allow-Origin'] = '*'
    headers['Access-Control-Allow-Methods'] = "GET, POST, PUT, DELETE, OPTIONS"
    headers['Access-Control-Allow-Headers'] = "Origin, X-Requested-With, Content-Type, Accept, Authorization"

    [status, headers, body]
  end
end

In ApplicationController I have this code

  before_filter :set_headers

  def set_headers
    headers['Access-Control-Allow-Origin'] = '*'
    headers['Access-Control-Allow-Methods'] = "GET, POST, PUT, DELETE, OPTIONS"
    headers['Access-Control-Allow-Headers'] = "Origin, X-Requested-With, Content-Type, Accept, Authorization"
  end

NOTE: in the Access-Control-Allow-Origin instead of the * it is very
important that you put your domain (where the client code will run from). I do
not recommend having * in your production code.

My SessionsController looks like this

class SessionsController < Devise::SessionsController
  respond_to :json, :html

  def destroy
    current_user.authentication_token = nil
    super
  end

  def options
    set_headers
    render :text => '', :content_type => 'text/plain'
  end


  protected
    def verified_request?
      request.content_type == "application/json" || super
    end
end

Notice that I configured devise to respond to json requests as well

In routes.rb I have devise configured like this

devise_for :users, controllers: { sessions: "sessions"  }

devise_scope :user do
  match "/users/sign_in" => "sessions#options", via: :options
end

Now that you have all the code in place, you just need to make sure your app
users the new middleware.

In config.ru just add these lines before the run

require 'cors'
use Cors

Now you are ready to authenticate Angular with Devise.

If you follow the medium post I linked to, you should be up and running in
minutes.

Enjoy!