Rails api and native mobile app authentication Rails api and native mobile app authentication ios ios

Rails api and native mobile app authentication


Gist of a solution from my research. Feel free to edit, correct, invalidate, etc.

SessionsController < ApplicationController  skip_before_filter :authenticate_user, :only => [:create]  def create    user = User.where("username = ? OR email = ?", params[:username_or_email], params[:username_or_email]).first    if user && user.authenticate(params[:password])      api_key = user.find_api_key      if !api_key.secret_key || api_key.is_expired?        api_key.set_expiry_date        api_key.generate_secret_key      end      api_key.save      render json: api_key, status: 201         else      status: 401    end  end

Note the ApiAuth.authentic? method and the request object. The request must be signed with an HMAC algorithm on the client.

ApplicationController < ActionController::Base  respond_to :json  force_ssl  protect_from_forgery with: :null_session   before_filter :authenticate_user  private  def authenticate_user    if authenticate_user_from_secret_key      return true    else      head :unauthorized     end  end  def authenticate_user_from_secret_key    userid = ApiAuth.access_id(request)    currentuser = userid && User.find_by_id(userid)    if ApiAuth.authentic?(request, currentuser.find_api_key.secret_key)      return true    else      return false    end    false  end

User creation/registration

UsersController < ApplicationController  skip_before_filter :authenticate_user, :only => [:create]  def create      user = User.create(user_params)      if !user.new_record?        render json: user.find_apit_key, status: 201      else       # error      end  end

Api key model. Similar to api key model in #352 railscast only difference is ApiAuth key generation.

class ApiKey < ActiveRecord::Base  before_create :generate_secret_key, :set_expiry_date  belongs_to :user     def generate_secret_key    begin    self.secret_key = ApiAuth.generate_secret_key    end while self.class.exists?(secret_key: secret_key)  end

User model.

class User < ActiveRecord::Base  has_secure_password  before_save :ensure_api_key   has_many :api_keys   def find_api_key   self.api_keys.active.ios.first_or_create  end

On the client side the HMAC algorithm must be used to sign requests.

The code is from:[SHA1 HMAC Key generation/authentication] https://github.com/mgomes/api_auth[Controllers & Models] https://github.com/danahartweg/authenticatable_rest_api


I've had this issue, I'm an API developer. You could do it the hard way with tokens and custom authorization, but I will tell you what we do with our application, which serves users in the six digit figure.

At least for iOS, the device will handle sessions for you, meaning that if a user on an iOS app makes a POST request to /users/sign_in with the parameters

user: {   password: 'mypassword',  email: 'testuser@example.com',  remember_me: true # optional}

the iOS device will store the session for you, safely and persistently.

Now, if you want to go the OAuth 2 route, I actually maintain a gem for rails 4 called OAuth 2 providable, to which I added a pretty cool feature that allows you to have the user pass through the "authorization" screen, because obviously if you developed the software you don't need the user to confirm that they trust you.

If you do decide to use OAuth 2, you will need to use what is call the implicit access token.This is the long and very boring OAuth2 spec for that

The rails 4 project can be found on githubhttps://github.com/bwheeler96/devise-oauth2-provider-rails4

If you're not on rails 4, you can use the original gemhttps://github.com/socialcast/devise_oauth2_providable

By the way, the gem needs work so if there's anyone reading this who wants to help make it better, please by all means fork this repository


You are on the right track, but the user's token should only be used to identify which user is making the request. You still need some kind of authentication since, as you speculate with changing the token on each request, a hacker could intercept the data stream, get the token, and then "be" that user in subsequent requests.

By changing the token on each request, you eliminate the interception issue, but once someone has intercepted the token, they can then further exploit the system by continuing to intercept it and even modifying the response. One solution to this is to use HMAC (which is used by Amazon Web Services). It is an algorithm that provides a signature (hash) for your request that is unique for every request, doesn't require a changing key, and cannot be predicted for future requests.

There is a ruby gem for rails that implements HMAC on the server side for signing HMAC requests as well as generating them when doing server-to-server communications. For client-to-server requests as in your case, you need to generate the signature on the iOS or Android side and authenticate them on the server.

Consider the ApiAuth gem to do the work on the server side. On the iOS client side, consider the HBHMAC library for generating the signature. Take a look at the ApiAuth's specific implementation as it adds a timestamp to the data to prevent replay attacks, so you may need to add a field to your data before passing it to HBHMAC.

In summary, using HMAC authentication will avoid man in the middle attacks and replay attacks by utilizing a one-way hashing algorithm that prevents attackers from generating authentic requests even if they are able to intercept valid requests.