Question

I want to be able to simply determine if a user credentials are correctly supplied in an iOS app I'm creating.

The way I have it setup now is with a sessions_controller.rb that handles and returns a user token. The problem is if I want to still log on through the web (not just a check via curl or similar), it doesn't authenticate and spits out

{"success":false,"message":"Error with your login or password"}

So my question is...how can I do authentication and still keep my web-login operational? Here are my related files. My hope was I could curl to a url such as localhost:3000/auth_checks and get one type of authentication response, and continue to have my users login through localhost:3000/sign_in.

From devise.rb

config.skip_session_storage = [:http_auth, :token_auth]
config.token_authentication_key = :auth_token

From routes.rb

  resources :clippings
  root to: 'clippings#index'
  #devise_for :users

  resources :auth_checks
  devise_for(:users, :controllers => { :sessions => "sessions" })


  resources :posts do
  end

From auth_checks_controller.rb

class AuthChecksController < ApplicationController

before_filter :authenticate_user!

  # GET /auth_checks
  # GET /auth_checks.json
  def index
    @auth_checks = AuthCheck.all

    respond_to do |format|
      format.html # index.html.erb
      format.json { render json: @auth_checks }
    end
  end

  # GET /auth_checks/1
  # GET /auth_checks/1.json
  def show
    @auth_check = AuthCheck.find(params[:id])

    respond_to do |format|
      format.html # show.html.erb
      format.json { render json: @auth_check }
    end
  end

  # GET /auth_checks/new
  # GET /auth_checks/new.json
  def new
    @auth_check = AuthCheck.new

    respond_to do |format|
      format.html # new.html.erb
      format.json { render json: @auth_check }
    end
  end

  # GET /auth_checks/1/edit
  def edit
    @auth_check = AuthCheck.find(params[:id])
  end

  # POST /auth_checks
  # POST /auth_checks.json
  def create
    @auth_check = AuthCheck.new(params[:auth_check])

    respond_to do |format|
      if @auth_check.save
        format.html { redirect_to @auth_check, notice: 'Auth check was successfully created.' }
        format.json { render json: @auth_check, status: :created, location: @auth_check }
      else
        format.html { render action: "new" }
        format.json { render json: @auth_check.errors, status: :unprocessable_entity }
      end
    end
  end

  # PUT /auth_checks/1
  # PUT /auth_checks/1.json
  def update
    @auth_check = AuthCheck.find(params[:id])

    respond_to do |format|
      if @auth_check.update_attributes(params[:auth_check])
        format.html { redirect_to @auth_check, notice: 'Auth check was successfully updated.' }
        format.json { head :no_content }
      else
        format.html { render action: "edit" }
        format.json { render json: @auth_check.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /auth_checks/1
  # DELETE /auth_checks/1.json
  def destroy
    @auth_check = AuthCheck.find(params[:id])
    @auth_check.destroy

    respond_to do |format|
      format.html { redirect_to auth_checks_url }
      format.json { head :no_content }
    end
  end
end
Was it helpful?

Solution

Learned a lot...here's what I ended up doing. If you're in this position, I highly recommend putting in the time (not very much) to do this method. http://www.cocoahunter.com/blog/2013/02/13/restful-api-authentication/

If you're like me, you already have a userbase using the standard devise login structure.

I added this to my routes.rb

  namespace :api do
      namespace :v1  do
        resources :tokens,:only => [:create, :destroy]
      end
    end

Then created and added tokens_controller.rb inside of controllers/api/v1/ (which I created)

# encoding: utf-8
class Api::V1::TokensController  < ApplicationController
    skip_before_filter :verify_authenticity_token
    respond_to :json
    def create
      email = params[:email]
      password = params[:password]
      if request.format != :json
        render :status=>406, :json=>{:message=>"The request must be json"}
        return
       end

    if email.nil? or password.nil?
       render :status=>400,
              :json=>{:message=>"The request must contain the user email and password."}
       return
    end

    @user=User.find_by_email(email.downcase)

    if @user.nil?
      logger.info("User #{email} failed signin, user cannot be found.")
      render :status=>401, :json=>{:message=>"Invalid email or passoword."}
      return
    end

    # http://rdoc.info/github/plataformatec/devise/master/Devise/Models/TokenAuthenticatable
    @user.ensure_authentication_token!

    if not @user.valid_password?(password)
      logger.info("User #{email} failed signin, password \"#{password}\" is invalid")
      render :status=>401, :json=>{:message=>"Invalid email or password."}
    else
      render :status=>200, :json=>{:token=>@user.authentication_token}
    end
  end

  def destroy
    @user=User.find_by_authentication_token(params[:id])
    if @user.nil?
      #logger.info(“Token wasnot found.”)
      #render :status=>404, :json=>{:message=>”Invalid token.”}
    else
      @user.reset_authentication_token!
      render :status=>200, :json=>{:token=>params[:id]}
    end
  end

end

That's literally all I had to do. I can now test to this api for authentication from my iOS app. Hope that makes sense to someone out there!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top