Devise is a commonly used authentication gem for Rails projects. It comes with controllers, data model and html templates that don’t require much time for configuration. Basically it works just out of the box for web projects. However, to my surprise, Devise is not that friendly with non-web clients. Say, you’ve built a REST API that works with your web app and now you want to reuse it with mobile native apps. Sounds reasonable and easy to do at first glance, isn’t it? Well, it’s not that easy with Devise.
If you try to google solutions for this matter, most likely you’ll stumble upon articles, talking about token authentication approach, similar to this one:
http://matteomelani.wordpress.com/2011/10/17/authentication-for-mobile-devices/
Unfortunately, this solution does not work anymore, because Devise changed.
It’s still possible to use token authentication, but you have to modify the code of your Session controller and Devise itself. I found that using token is not necessary as authentication cookies were working too. I did not need to change the Devise and append every single URL with ?auth_token=.
In one of the Devise own examples called “Simple Token Authentication” you can find following message:
Note: these examples are out of date, TokenAuthenticatable has been removed from Devise. See this gist for alternatives.
Here’s the gist it’s referencing to:
https://gist.github.com/josevalim/fb706b1e933ef01e4fb6
New article on how to build an API with Devise authentication:
http://www.soryy.com/ruby/api/rails/authentication/2014/03/16/apis-with-devise.html
Good article with the example that returns authentication token and result of the authentication back to the client:
http://jessewolgamott.com/blog/2012/01/19/the-one-with-a-json-api-login-using-devise
Basically, in order to make token authentication work again, you need to merge modifications for ApplicationController from gist I mentioned above and SessionController the way it’s shown in the last article.
My SessionController looks like this:
class SessionsController < Devise::SessionsController
skip_before_filter :verify_authenticity_token
def create
respond_to do |format|
format.html do
params[:user].merge!(remember_me: 1)
super
end
format.json do
resource = User.find_for_database_authentication(:email => params[:email])
return invalid_login_attempt unless resource
resource.ensure_authentication_token
if resource.valid_password?(params[:password])
sign_in(:user, resource)
render :json => {:success => true, :auth_token => resource.authentication_token, :email => resource.email}
else
invalid_login_attempt
end
end
end
end
def invalid_login_attempt
render :json => {:success => false, :error => "invalid login"}
end
end