Angular Authentication with Rails4 and Devise
24 Aug 2015Intro
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!