Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails API : Best way to implement authentication?

I'm writing a Rails 4 app that will expose an API for a mobile app that's yet to be developed. Users will authenticate using an e-mail and password from the mobile app.

While I've found quite a bit of information on the topic. It's hard to discern what's dated or non-optimal. I've read about HTTP Basic Auth, which doesn't seem too secure, and HTTP Token-based Auth, but I'm not sure on how to couple that with regular e-mail and password authentication (I'm using Devise by the way).

I'd just like to know what's the current best practice on how to implement this, so I'll be sure to be going the right way.

like image 356
Roma149 Avatar asked May 04 '15 19:05

Roma149


People also ask

What is the best method for API authentication?

Common API Authentication Methods The simplest way to handle authentication is through the use of HTTP, where the username and password are sent alongside every API call. You can use an HTTP header and encode the username and password.

How does API authentication work in Rails?

The token-based verification method works simply. The user enters his details and sends the request to the server. If the information is correct, the server creates a unique HMACSHA256 encoded token, also known as the JSON (JWT) web token.


2 Answers

The important point, from a security perspective, is to exchange the user's email and password for a token once, and then use that token for subsequent requests. This is because:

  1. You don't want the client app to be responsible for holding onto the user's password, where a bug or attack could cause it to be leaked; and
  2. A server-issued token gives you (and your users) the ability to expire a token if necessary, e.g. to lock out a stolen device or block a misbehaving API client.

There are many ways to accomplish this with varying levels of complexity.

Here is a tutorial that is very recent and has a thorough walkthrough for creating an API in Rails with token-based authentication (not using Devise, but still relevant to understand the concepts): https://labs.kollegorna.se/blog/2015/04/build-an-api-now/

like image 50
Matt Brictson Avatar answered Sep 21 '22 17:09

Matt Brictson


Another option is to include the module below in your devise MODEL and add the auth_token to you table.

app/models/concerns/token_authenticable.rb

module TokenAuthenticatable   extend ActiveSupport::Concern    included do     before_save :ensure_auth_token   end    module ClassMethods     def find_by_token(token)       find_by(auth_token: token)     end   end    def ensure_auth_token     self.auth_token = generate_auth_token if auth_token.blank?   end    private    def generate_auth_token     loop do       token = Devise.friendly_token       break token unless self.class.exists?(auth_token: token)     end   end end 

app/controllers/api/v1/login_controller.rb

...  def login_user(params)     if params[:authentication]       @user = User.find_by(auth_token: params[:authentication])       if @user.nil?         render json: err('login user by token failed', ERR_USER_NOT_FOUND), status: :not_found         event('login_user_by_auth_failed', 'token', params[:authentication])         return       else         render status: :ok, json: @user         return       end     else       user = user.find_by(email: params[:email])       if user.nil?         event('login_user_failed_not_found', 'user_email', params[:email])         render json: err("login user not found #{params[:email]}", ERR_USER_NOT_FOUND), status: :not_found         return       end       if user.access_locked?         event('login_user_blocked', 'user_id', user.id)         render json: err("login user account is locked : #{user.id}", ERR_USER_LOCKED), status: :unauthorized         return       end       unless user.try(:valid_password?, params[:password])         event("login_user_password_does_not_match #{user.id}", 'user_id',  user.id)         render json: err('login user password does not match', ERR_PASSWORD_NOT_MATCH), status: :unauthorized         return       end       event('login_user_succeeded', 'user_id', user.id)       @user= user       if @user.save         response.headers['authentication'] = @user.auth_token         render status: :ok, json: @user         return       else         render json: @user.errors, status: :unprocessable_entity         return       end     end   end ... 

Edit: Corrected code-breaking typo

like image 43
Antarr Byrd Avatar answered Sep 22 '22 17:09

Antarr Byrd