code - lucatironi Code snippets and tutorials

Ruby on Rails and Android Authentication Part One

The Rails Web Application


Intro

In this two-three-part tutorial you’ll learn first how to build an authentication API that can allow external users to register, login and logout through JSON requests. After having successfully logged in, a user will receive an authentication token that could be used in following API requests to authorize the user, securing the access to your application’s resources.

In the second part, you will build an Android application that will be able to consume this API, allowing the user to register and login directly from the app.

With these tutorials I want to explain, with a step-by-step approach, how to build a complete solution that can be used as a base for a more complex scenario.

Supplemental bonuses will be given to enhance both the backend and the Android app with additional nice-to-have features.

Let’s start!

Ingredients

Here’s the list of what we are going to use in this tutorial:

The Rails Backend

Start by creating a new Rails app. At the moment I’m writing the latest version of rails is 3.2.8, check yours by using rails -v in the command line.

rails new authexample_webapp
cd authexample_webapp

Devise Setup

Add the Devise gem to your application:

# file: Gemfile
gem 'devise'

Install the gems and create the default user model with the Devise generators.

bundle install
rails generate devise:install
rails generate devise user

Uncomment the following lines in the migration that are relative to the token_authenticatable module:

# file: db/migrate/<timestamp>_devise_create_users.rb
## Token authenticatable
t.string :authentication_token
add_index :users, :authentication_token, :unique => true

Add the :token_authenticatable to the devise modules in the user model:

# file: app/models/user.rb
devise :database_authenticatable, :registerable,
       :recoverable, :rememberable, :trackable, :validatable,
       :token_authenticatable

Add the before filter :ensure_authentication_token to the user model:

# file: app/models/user.rb
before_save :ensure_authentication_token

And finally uncomment the following line in the Devise initializer to enable the auth_token:

# file: config/initializers/devise.rb
# ==> Configuration for :token_authenticatable
# Defines name of the authentication token params key
config.token_authentication_key = :auth_token

Bonus: add username to the user model

In our Android app we want to give possibility to the user to specify a username in the registration form. Let’s add this column to the table ‘users’ and the attribute to the attr_accesible list in the user model.

Add the following line to the change method in the migration.

# file: db/migrate/<timestamp>_devise_create_users.rb
t.string :name, :null => false, :default => ""
# file: app/models/user.rb
attr_accessible :name, :email, :password, :password_confirmation, :remember_me

Bonus: email confirmation from web, skip it from the Android app

The users of our Android app don’t want to wait to use it, so we skip the confirmation email check provided by Devise with the confirmable module. If you still want to use the module for the users that register from the webapp, just add this lines to the code.

Uncomment the following lines in the migration that are relative to the confirmable module:

# file: db/migrate/<timestamp>_devise_create_users.rb
## Confirmable
t.string   :confirmation_token
t.datetime :confirmed_at
t.datetime :confirmation_sent_at
add_index :users, :confirmation_token,   :unique => true

Add the :confirmable to the devise available modules in the user model:

# file: app/models/user.rb
devise :database_authenticatable, :registerable,
       :recoverable, :rememberable, :trackable, :validatable,
       :confirmable, :token_authenticatable

We need a way to bypass the confirmation step after the creation of a new user: setting a date to the confirmed_at will do this. We add a new method to the user model that will be used in our Api controller.

# file: app/models/user.rb
def skip_confirmation!
  self.confirmed_at = Time.now
end

We are done with the setup of our user model, we can now launch the rake tasks to create and migrate the database:

rake db:create db:migrate

API Sessions Controller (login and logout)

Let’s start coding the sessions controller that will be used by our Android app to authenticate the users. I want to use a namespace and a version for our API, so its controllers will be under the app/controller/api/v1/ folder.

The sessions controller has two actions: create for login and destroy for logout. The first accepts a user JSON object as POST data with an email and a password parameters and returns an auth_token if the user exists in the database and the password is correct. The logout action expects an auth_token parameter in the url.

# file: app/controller/api/v1/sessions_controller.rb
class Api::V1::SessionsController < Devise::SessionsController
  skip_before_filter :verify_authenticity_token,
                     :if => Proc.new { |c| c.request.format == 'application/json' }

  respond_to :json

  def create
    warden.authenticate!(:scope => resource_name, :recall => "#{controller_path}#failure")
    render :status => 200,
           :json => { :success => true,
                      :info => "Logged in",
                      :data => { :auth_token => current_user.authentication_token } }
  end

  def destroy
    warden.authenticate!(:scope => resource_name, :recall => "#{controller_path}#failure")
    current_user.update_column(:authentication_token, nil)
    render :status => 200,
           :json => { :success => true,
                      :info => "Logged out",
                      :data => {} }
  end

  def failure
    render :status => 401,
           :json => { :success => false,
                      :info => "Login Failed",
                      :data => {} }
  end
end

In the routes definition we add our namespace and the two login and logout routes.

# file: config/routes.rb
namespace :api do
  namespace :v1 do
    devise_scope :user do
      post 'sessions' => 'sessions#create', :as => 'login'
      delete 'sessions' => 'sessions#destroy', :as => 'logout'
    end
  end
end

Test the login

Let’s create a user to test the login with, open the rails console with rails console in the command line and write:

user = User.new(:name => 'testuser', :email => 'user@example.com', :password => 'secret', :password_confirmation => 'secret')
user.skip_confirmation!
user.save

Close the console and fire up the rails server and in another command line use curl to invoke our new login API:

curl -v -H 'Content-Type: application/json' -H 'Accept: application/json' -X POST http://localhost:3000/api/v1/sessions -d "{\"user\":{\"email\":\"user@example.com\",\"password\":\"secret\"}}"

If everything went fine, you should see the last line saying this (the auth_token will be different):

{"success":true,"info":"Logged in","data":{"auth_token":"JRYodzXgrLsk157ioYHf"}}

Test the logout

Using the auth_token that the API gave us back when we logged in and specifying the DELETE verb we can reset the authentication token of the user, logging him out.

curl -v -H 'Content-Type: application/json' -H 'Accept: application/json' -X DELETE http://localhost:3000/api/v1/sessions/\?auth_token\=JRYodzXgrLsk157ioYHf

The result will be a nice message informing us that we are logged out.

{"success":true,"info":"Logged out","data":{}}

API Registrations Controller (register a new user)

The registrations controller extends the Devise one and has only one action, create. As you can see we skip the confirmation step with the method we added previously to the user model, we then save the new user and automatically log it in, returning the auth_token associated. Our user can then start using the API already logged in after the registration.

# file: app/controllers/api/v1/registrations_controller.rb
class Api::V1::RegistrationsController < Devise::RegistrationsController
  skip_before_filter :verify_authenticity_token,
                     :if => Proc.new { |c| c.request.format == 'application/json' }

  respond_to :json

  def create
    build_resource
    resource.skip_confirmation!
    if resource.save
      sign_in resource
      render :status => 200,
           :json => { :success => true,
                      :info => "Registered",
                      :data => { :user => resource,
                                 :auth_token => current_user.authentication_token } }
    else
      render :status => :unprocessable_entity,
             :json => { :success => false,
                        :info => resource.errors,
                        :data => {} }
    end
  end
end

Add the register route to the API namespace:

# file: config/routes.rb
namespace :api do
  namespace :v1 do
    devise_scope :user do
      post 'registrations' => 'registrations#create', :as => 'register'
      post 'sessions' => 'sessions#create', :as => 'login'
      delete 'sessions' => 'sessions#destroy', :as => 'logout'
    end
  end
end

Test the registration

Using the code we just added, we can now register new users from the JSON API. Try it out opening a command line and pasting this code.

curl -v -H 'Content-Type: application/json' -H 'Accept: application/json' -X POST http://localhost:3000/api/v1/registrations -d "{\"user\":{\"email\":\"user1@example.com\",\"name\":\"anotheruser\",\"password\":\"secret\",\"password_confirmation\":\"secret\"}}"

If everything went fine, you should see the last line saying this (the id, dates and auth_token will be different):

{"success":true,"info":"Registered","data":{"user":{"created_at":"2012-10-08T19:57:20Z","email":"user1@example.com","id":3,"name":"anotheruser","updated_at":"2012-10-08T19:57:20Z"},"auth_token":"N8N5MPqFNdDz3G1jRsC9"}}

ActiveAdmin administrative interface

Just to ease the following steps, I wanted to introduce a simple administrative interface to our web application: let’s add the ActiveAdmin gem (with dependencies) and with few lines of code we’ll have a full featured admin area.

# file: Gemfile
gem 'activeadmin'
gem 'meta_search', '>= 1.1.0.pre' # activeadmin needs this if Rails >= 3.1

Don’t forget to launch the bundle install command. ActiveAdmin will then generate the configuration file and some migrations. You can find more information in the official documentation: www.activeadmin.info

bundle install
rails generate active_admin:install
rake db:migrate

Add a new file to the app/admin folder to configure the users admin interface:

# file: app/admin/user.rb
ActiveAdmin.register User do
  index do
    column :name
    column :email
    column :current_sign_in_at
    column :last_sign_in_at
    column :sign_in_count
    default_actions
  end

  filter :name
  filter :email

  form do |f|
    f.inputs "User Details" do
      f.input :name
      f.input :email
      f.input :password
      f.input :password_confirmation
    end
    f.buttons
  end

  show do
    attributes_table do
      row :name
      row :email
      row :authentication_token
      row :confirmed_at
      row :current_sign_in_at
      row :last_sign_in_at
      row :sign_in_count
    end
    active_admin_comments
  end
end

Launch the rails server and go to http://localhost:3000/admin and login with the default ActiveAdmin credentials: - User: admin@example.com - Password: password

Bonus: Deploy to Heroku

In the next steps we will be building an Android app from scratch that will consume our API. To better test it on our smartphones, we will need the web app reachable from the web, what better place than Heroku?

This tutorial won’t delve to much on the details of deploying a Rails app on the service, but you can read more on the official documentation: devcenter.heroku.com.

Download the Heroku toolbelt and create an account if you don’t have one.

Let’s start creating a Git repository and pushing it to Heroku. Take note of the app’s name that Heroku will create for you (something.herokuapp.com).

rm public/index.html
git init
git add .
git commit -m "Initial commit"
heroku apps:create
git push heroku master
heroku run rake db:migrate

Now go to the address that Heroku created for your app and see if everything worked or not. More details on issues can be spotted with the heroku logs command.

Part Two: The Android App

Proceed now to the second part of the tutorial, to learn how to build a fully featured Android App that can interact with the Ruby on Rails backend we just coded.

^Back to top