Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
API JSON authentication with Devise
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
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
@Undistraction

This comment has been minimized.

Show comment Hide comment
@Undistraction

Undistraction Jan 23, 2012

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

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

@jwo

This comment has been minimized.

Show comment Hide comment
@jwo

jwo Jan 23, 2012

@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/

Owner

jwo commented Jan 23, 2012

@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/

@Undistraction

This comment has been minimized.

Show comment Hide comment
@Undistraction

Undistraction Jan 23, 2012

Nice. Thanks a lot.

Nice. Thanks a lot.

@dellerbie

This comment has been minimized.

Show comment Hide comment
@dellerbie

dellerbie Apr 20, 2012

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

@dellerbie

This comment has been minimized.

Show comment Hide comment
@dellerbie

dellerbie Apr 20, 2012

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?

@jwo

This comment has been minimized.

Show comment Hide comment
@jwo

jwo Apr 22, 2012

@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
Owner

jwo commented Apr 22, 2012

@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
@dellerbie

This comment has been minimized.

Show comment Hide comment
@dellerbie

dellerbie Apr 23, 2012

Nice, thanks for the response. Great blog post!

Nice, thanks for the response. Great blog post!

@Banta

This comment has been minimized.

Show comment Hide comment
@Banta

Banta May 8, 2012

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

Banta commented May 8, 2012

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

@jwo

This comment has been minimized.

Show comment Hide comment
@evanbeard

This comment has been minimized.

Show comment Hide comment
@evanbeard

evanbeard May 11, 2012

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

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

This comment has been minimized.

Show comment Hide comment
@Banta

Banta May 12, 2012

Banta commented May 12, 2012

@drblok

This comment has been minimized.

Show comment Hide comment
@drblok

drblok Jun 6, 2012

@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.

drblok commented Jun 6, 2012

@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.

@jwo

This comment has been minimized.

Show comment Hide comment
@jwo

jwo Jun 6, 2012

@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 Jun 6, 2012

@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.

@jwo

This comment has been minimized.

Show comment Hide comment
@jwo

jwo Jun 6, 2012

@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 
Owner

jwo commented Jun 6, 2012

@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

This comment has been minimized.

Show comment Hide comment
@drblok

drblok Jun 6, 2012

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

drblok commented Jun 6, 2012

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

@drblok

This comment has been minimized.

Show comment Hide comment
@drblok

drblok Jun 6, 2012

@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 Jun 6, 2012

@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

This comment has been minimized.

Show comment Hide comment
@drblok

drblok Jun 20, 2012

drblok commented Jun 20, 2012

@mmcc

This comment has been minimized.

Show comment Hide comment
@mmcc

mmcc Jun 20, 2012

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

mmcc commented Jun 20, 2012

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

This comment has been minimized.

Show comment Hide comment
@drblok

drblok Jun 20, 2012

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

drblok commented Jun 20, 2012

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

@mmcc

This comment has been minimized.

Show comment Hide comment
@mmcc

mmcc Jun 20, 2012

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

mmcc commented Jun 20, 2012

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

@drblok

This comment has been minimized.

Show comment Hide comment
@drblok

drblok Jun 20, 2012

np!

drblok commented Jun 20, 2012

np!

@hackfanatic

This comment has been minimized.

Show comment Hide comment
@hackfanatic

hackfanatic Feb 22, 2013

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 ?

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 ?

@dieb

This comment has been minimized.

Show comment Hide comment
@dieb

dieb Mar 8, 2013

dieb commented Mar 8, 2013

@natebird

This comment has been minimized.

Show comment Hide comment
@natebird

natebird Jul 16, 2013

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

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

@johnjohndoe

This comment has been minimized.

Show comment Hide comment
@johnjohndoe

johnjohndoe Aug 19, 2013

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

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

@bilby91

This comment has been minimized.

Show comment Hide comment
@bilby91

bilby91 Aug 30, 2013

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

bilby91 commented Aug 30, 2013

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

@onetom

This comment has been minimized.

Show comment Hide comment
@onetom

onetom Sep 26, 2013

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

onetom commented Sep 26, 2013

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

@ricbermo

This comment has been minimized.

Show comment Hide comment
@ricbermo

ricbermo Jan 28, 2014

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?

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?

@minhtriet

This comment has been minimized.

Show comment Hide comment
@minhtriet

minhtriet May 19, 2014

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

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

@netwire88

This comment has been minimized.

Show comment Hide comment
@netwire88

netwire88 Jul 31, 2014

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

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

@DanielWright

This comment has been minimized.

Show comment Hide comment
@DanielWright

DanielWright Aug 11, 2014

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

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

@codemilan

This comment has been minimized.

Show comment Hide comment
@codemilan

codemilan Nov 19, 2014

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.

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.

@thandang

This comment has been minimized.

Show comment Hide comment
@thandang

thandang Jul 24, 2015

Dear,

It's nice post.
But I have one question.
How about if I setting for Devise with maximum_attempts = 5 and lock_strategy = :failed_attempts
Does it work like normal web base.
Currently I'm using device for connect to API,

It's really helpful to get answers.
Thanks a lot.

Dear,

It's nice post.
But I have one question.
How about if I setting for Devise with maximum_attempts = 5 and lock_strategy = :failed_attempts
Does it work like normal web base.
Currently I'm using device for connect to API,

It's really helpful to get answers.
Thanks a lot.

@mices

This comment has been minimized.

Show comment Hide comment
@mices

mices Feb 23, 2017

I get this error in rails
TypeError in Devise::ConfirmationsController#show
nil is not a symbol nor a string
Extracted source (around line #188):

186
187
188
189
190
191

# Handles <tt>*_was</tt> for +method_missing+.
def attribute_was(attr) # :nodoc:
  attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr)
end

# Handles <tt>*_previously_changed?</tt> for +method_missing+.

mices commented Feb 23, 2017

I get this error in rails
TypeError in Devise::ConfirmationsController#show
nil is not a symbol nor a string
Extracted source (around line #188):

186
187
188
189
190
191

# Handles <tt>*_was</tt> for +method_missing+.
def attribute_was(attr) # :nodoc:
  attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr)
end

# Handles <tt>*_previously_changed?</tt> for +method_missing+.
@jgonzalezd

This comment has been minimized.

Show comment Hide comment
@jgonzalezd

jgonzalezd May 9, 2017

@onetom:

namespace :api do
    namespace :v1 do
      devise_for :users, defaults: { format: :json }, as: :users
   end
end

 new_users_user_session GET    /api/v1/users/sign_in(.:format)  api/v1/sessions#new {:format=>:json}
 users_user_session POST   /api/v1/users/sign_in(.:format)  api/v1/sessions#create {:format=>:json}

jgonzalezd commented May 9, 2017

@onetom:

namespace :api do
    namespace :v1 do
      devise_for :users, defaults: { format: :json }, as: :users
   end
end

 new_users_user_session GET    /api/v1/users/sign_in(.:format)  api/v1/sessions#new {:format=>:json}
 users_user_session POST   /api/v1/users/sign_in(.:format)  api/v1/sessions#create {:format=>:json}

@avatarr

This comment has been minimized.

Show comment Hide comment
@avatarr

avatarr Jul 20, 2017

Good example but confused about where you set token?

avatarr commented Jul 20, 2017

Good example but confused about where you set token?

@ghost

This comment has been minimized.

Show comment Hide comment
@ghost

ghost Sep 24, 2017

In this way failed_attempts, doesn't work.

ghost commented Sep 24, 2017

In this way failed_attempts, doesn't work.

@xhocquet

This comment has been minimized.

Show comment Hide comment
@xhocquet

xhocquet Oct 24, 2017

@avatarr in this instance, the tokens are probably set or generated by Devise. This was removed from Devise after 3 for security reasons, though you can use a basic implementation like this one to get the functionality back

@avatarr in this instance, the tokens are probably set or generated by Devise. This was removed from Devise after 3 for security reasons, though you can use a basic implementation like this one to get the functionality back

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment