Created

Embed URL

HTTPS clone URL

SSH clone URL

You can clone with HTTPS or SSH.

Download Gist

API JSON authentication with Devise

View registrations_controller.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
class Api::RegistrationsController < Api::BaseController
respond_to :json
def create
 
user = User.new(params[:user])
if user.save
render :json=> user.as_json(:auth_token=>user.authentication_token, :email=>user.email), :status=>201
return
else
warden.custom_failure!
render :json=> user.errors, :status=>422
end
end
end
View registrations_controller.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
class Api::SessionsController < Api::BaseController
prepend_before_filter :require_no_authentication, :only => [:create ]
include Devise::Controllers::InternalHelpers
before_filter :ensure_params_exist
 
respond_to :json
def create
build_resource
resource = User.find_for_database_authentication(:login=>params[:user_login][:login])
return invalid_login_attempt unless resource
 
if resource.valid_password?(params[:user_login][:password])
sign_in("user", resource)
render :json=> {:success=>true, :auth_token=>resource.authentication_token, :login=>resource.login, :email=>resource.email}
return
end
invalid_login_attempt
end
def destroy
sign_out(resource_name)
end
 
protected
def ensure_params_exist
return unless params[:user_login].blank?
render :json=>{:success=>false, :message=>"missing user_login parameter"}, :status=>422
end
 
def invalid_login_attempt
warden.custom_failure!
render :json=> {:success=>false, :message=>"Error with your login or password"}, :status=>401
end
end

How do you have your routes set up for this controller?

Owner
jwo commented

@1ndivisible:

My routes for api are:

namespace :api do
  devise_for :users
  resources :recipes, :only=>[:index, :show]
end  

I also go into more detail http://jessewolgamott.com/blog/2012/01/19/the-one-with-a-json-api-login-using-devise/

Nice. Thanks a lot.

How are you authorizing your API controllers? You don't need to add the before_filter :authorize_users! in your API::BaseController

How are you authorizing your API controllers? You don't need to add the before_filter :authorize_users! in your API::BaseController?

Owner
jwo commented

@dellerbie I go into more detail here: http://jessewolgamott.com/blog/2012/01/19/the-one-with-a-json-api-login-using-devise/ .. but I do this to authorize a controller that requires authorization. The above would not, since they are the sign-in and sign-up controllers.


class Api::RecipesController < Api::BaseApiController
  before_filter :authenticate_user!
  #...
end

Nice, thanks for the response. Great blog post!

Banta commented

Does anyone have a small sample to demonstrate this. Let me look around I'll make one when I understand.

I forked this to work with latest devise: https://gist.github.com/2662058

InternalHelpers file is renamed, and made some other changes that now pass my tests

Banta commented
drblok commented

@jwo Have you got an example of routes.rb when using a :api namespace with a devise_for :users together with the normal devise_for :users?

Got the same problem as mentioned here: https://groups.google.com/group/plataformatec-devise/browse_thread/thread/d2f4776e5109c0b3?pli=1#

Wondering if you solved this or could point me in the right direction.

Owner
jwo commented

@drblok -- I wouldn't have 2 devise_for... You can have separate resources :users, but I don't see the need for multiple registration paths.

Owner
jwo commented

@drblok -- actually, sorry, that's incorrect... here's a setup for multiple devise paths. This works for me:

  devise_for :chefs, :path => '', :path_names => { :sign_in => "login", :sign_out => "logout", :sign_up => "register" }

  namespace "api/v1", :as=>:api do
    devise_for :chefs                                                                                                         
    resources :recipes, :only=>[:index, :show]
  end  

  resources :chefs, :only=>[:show,:edit, :update, :delete] do
    resource :avatar, :only=>[:show, :new, :create]
  end 
drblok commented

@jwo Thanks, I'll check it out and post my findings.

drblok commented

@jwo - I decided to remove the devise_for from my :api namespace and write my own as it's nothing more than rendering a JSON message if authentication fails. Thanks anyway for giving me stuff to think about to come to this conclusion ;)

drblok commented
mmcc commented

Thanks for the response Dennis! I ended up using a lot of your registrations controller but I did remove the user.as_json in order to be able to return the token.

render :json=> user.as_json(:auth_token=>user.authentication_token, :email=>user.email), :status=>201

to

 render :json=> { :token=>user.authentication_token, :email=>user.email }, :status=>201
drblok commented

@sh1ps Errrr.. That's @jwo 's code, but glad it helps ;)

mmcc commented

@drblok Errr...Well I feel like an idiot... I completely misread the flow of conversation. Thanks for offering up the help anyway!

drblok commented

np!

Sorry to bring this back out from the dead, but doesn't before_filter :ensure_params_exist mean the app would also try to ensure parameters exist on destroy i.e. sign out ?

@hackfanatic yes. The line should be before_filter :ensure_params_exist, only: :create

I made some changes to the latest version of @Bomadeno.

What are the names of the helpers devise generates you? After adding namespaces i am forced to change my helpers.

namespace :api do
  namespace :v1 do
     devise_for :users
  end
end

Now i can access them like this current_api_v1_user or authenticate_api_v1_user!. Is there any way to maintain namespaces and access helper with just the name of the model?

thanks

I agree with @bilby91 that 2 namespaces are making the current_user quite ugly. What's the best practice to avoid this?

Hi there, I'm trying to make a API for a iOS app that will upload some videos to AWS s3. This is a great gist but, with the controllers are not extending from Devise::SessionsController and Devise::RegistrationsController?

Try another method, taken from my Spree Application

    user = Spree::User.find_by(:email => params[:email])
    unless user.nil?
      if user.valid_password? params[:password]
        render :json => '{"api_key": "#{user.spree_api_key}"}'
      end
    end
    render :json => '{"error": "invalid email and password combination"}'

The point is that you can use valid_password? in devise for your convenient

Thanks minhtriet, this is useful and simpler. Do you have the RegistrationsController gist?

Is there a reason not to use warden.authenticate instead of reinventing the wheel with your own finder and password-validation logic?

getting error
"undefined local variable or method `build_resource' for #Api::SessionsController:0x00000003f885b0"
i'm using devise 3.4.1 in rails 4.1.5, I have removed line 'include Devise::Controllers::InternalHelpers' and extended 'Api::SessionsController' < DeviseController, also I have not included the related file Api::RegistrationsController in my api does it affect ?
so what's the exact cause please clarify.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.