Authentication via AJAX / POST?

1,301 views
Skip to first unread message

Keegan Poppen

unread,
Aug 13, 2010, 8:52:10 PM8/13/10
to Devise
Hi--

So I know that this question has come up a couple of different times
over this list, but it doesn't seem like there has really been much
response / activity so I'd figured again:

In a mobile web application that I'm developing right now I need to be
able to send an AJAX request to log in and get a JSON response back,
rather than getting a 302 redirect. I've got a pretty hacked-together
solution nearly working (although I'm still having problems keeping
the browser from asking for HTTP authentication when the login
credentials are incorrect), but I was wondering if anyone had come up
with / had any thoughts about a good way to handle this situation.

The problem that I've been having (which is probably related to my
inexperience / lack of grokking of devise/warden I'm sure) is that
it's unclear where the best place / what the best way is to override
devise to keep it from trying to redirect after authenticating
(whether the attempt was successful or not). So if anyone has any
ideas, please let me know!

Thanks,
-keegan

José Valim

unread,
Aug 13, 2010, 10:12:38 PM8/13/10
to plataforma...@googlegroups.com
The redirect after authentication happens in Devise::SessionsController. All you need to do is to change your controller to redirect from there.

--
José Valim

Director of Engineering - Plataforma Tecnologia
Know more about us: http://plataformatec.com.br/en/
Message has been deleted
Message has been deleted

tekknikk

unread,
Aug 16, 2010, 7:17:12 AM8/16/10
to Devise
Here's my approach re json response for both success and failure.
for
rails 2.3 / devise 1.0.8, i've copied the sessions controller
completely, changed the create and added sign_in_and_redirect. I've
also done something similar with the registrations controller for
sign_up.
Would like to compare notes with you.
def sign_in_and_redirect(resource_or_scope, resource=nil,
skip=false)
scope = Devise::Mapping.find_scope!(resource_or_scope)
resource ||= resource_or_scope
sign_in(scope, resource) unless skip
respond_to do |format|
format.html {redirect_to stored_location_for(scope) ||
after_sign_in_path_for(resource) }
format.json { render :json => { :success => true, :session_id
=>
request.session_options[:id], :resource => resource,
:redirect => $some_path } }
end
end
def create
build_resource
if resource = authenticate(resource_name)
set_flash_message :notice, :signed_in
sign_in_and_redirect(resource_name, resource, true)
elsif [:custom, :redirect].include?(warden.result)
throw :warden, :scope => resource_name
else
set_now_flash_message :alert, (warden.message || :invalid)
clean_up_passwords(build_resource)
respond_to do |format|
format.html { render_with_scope :new }
format.json { render :json => {:success => false, :status =>
warden.message}}
end
end
end
Message has been deleted

jjm

unread,
Sep 21, 2010, 3:12:43 PM9/21/10
to Devise

I have a question about this, but first some givens:

I understand that in this example that the only response is in json
which can be tested with the browser, and I've done this. So the
session controller is being executed, and json back is good to go.

However, if I try to run a curl test it doesn't work for me. I get a
json response back saying saying that i need to sign in:
<code:json>
{"error":"You need to sign in or sign up before continuing."}
</code:json>

Here is what I used to test:

<code:commandline>
curl -H "Content-Type:application/json" -H "Accept:application/json" \
-d "{\"user\":{\"user_email\":\"us...@test.com\",\"user_password\":
\"please\"}}" \
http://localhost:3000/users/sign_in
</code:commandline>

What the heck, i tried a non resource block version too :-P
(result=nada)
<code:commandline>
curl -H "Content-Type:application/json" -H "Accept:application/json" \
-d "{\"user_email\":\"us...@test.com\",\"user_password\":\"please\"}" \
http://localhost:3000/users/sign_in
</code:commandline>

Anyone have any answers? Would appreciate the help, thanks in advance.

On Aug 16, 7:17 am, tekknikk <tekkn...@gmail.com> wrote:
> Here's my approach rejsonresponse for both success and failure.
> for
> rails 2.3 / devise 1.0.8, i've copied the sessions controller
> completely, changed the create and added sign_in_and_redirect.  I've
> also done something similar with the registrations controller for
> sign_up.
> Would like to compare notes with you.
>   def sign_in_and_redirect(resource_or_scope, resource=nil,
> skip=false)
>     scope      = Devise::Mapping.find_scope!(resource_or_scope)
>     resource ||= resource_or_scope
>     sign_in(scope, resource) unless skip
>     respond_to do |format|
>       format.html {redirect_to stored_location_for(scope) ||
> after_sign_in_path_for(resource) }
>       format.json{ render :json=> { :success => true, :session_id
> =>
> request.session_options[:id], :resource => resource,
>         :redirect  => $some_path } }
>     end
>   end
>   def create
>     build_resource
>     if resource = authenticate(resource_name)
>       set_flash_message :notice, :signed_in
>       sign_in_and_redirect(resource_name, resource, true)
>     elsif [:custom, :redirect].include?(warden.result)
>       throw :warden, :scope => resource_name
>     else
>       set_now_flash_message :alert, (warden.message || :invalid)
>       clean_up_passwords(build_resource)
>       respond_to do |format|
>         format.html { render_with_scope :new }
>         format.json{ render :json=> {:success => false, :status =>
> warden.message}}
>       end
>     end
>   end
>
> On Aug 14, 10:52 am, Keegan Poppen <keegan.pop...@gmail.com> wrote:
>
>
>
> > Hi--
>
> > So I know that this question has come up a couple of different times
> > over this list, but it doesn't seem like there has really been much
> > response / activity so I'd figured again:
>
> > In a mobile web application that I'm developing right now I need to be
> > able to send anAJAXrequest to log in and get aJSONresponse back,

jjm

unread,
Sep 21, 2010, 3:20:08 PM9/21/10
to Devise
BTW, i removed the HTML render part for testing. Thats why I said the
response is in json only.

On Sep 21, 3:12 pm, jjm <jasonjunkmail.m...@gmail.com> wrote:
> I have a question about this, but first some givens:
>
> I understand that in this example that the only response is injson
> which can be tested with the browser, and I've done this. So the
> session controller is being executed, andjsonback is good to go.
>
> However, if I try to run a curl test it doesn't work for me. I get ajsonresponse back saying saying that i need to sign in:
> <code:json>
> {"error":"You need to sign in or sign up before continuing."}
> </code:json>
>
> Here is what I used to test:
>
> <code:commandline>
> curl -H "Content-Type:application/json" -H "Accept:application/json" \
> -d "{\"user\":{\"user_email\":\"u...@test.com\",\"user_password\":
> \"please\"}}" \http://localhost:3000/users/sign_in
> </code:commandline>
>
> What the heck, i tried a non resource block version too :-P
> (result=nada)
> <code:commandline>
> curl -H "Content-Type:application/json" -H "Accept:application/json" \
> -d "{\"user_email\":\"u...@test.com\",\"user_password\":\"please\"}" \http://localhost:3000/users/sign_in

jjm

unread,
Sep 21, 2010, 3:22:47 PM9/21/10
to Devise
Oops. I screwed up the resource scope.
Here is the working curl:

<code:commandline>
curl -H "Content-Type:application/json" -H "Accept:application/json" \
-d "{\"user\":{\"email\":\"us...@test.com\",\"password\":\"please\"}}"
On Sep 21, 3:12 pm, jjm <jasonjunkmail.m...@gmail.com> wrote:
> I have a question about this, but first some givens:
>
> I understand that in this example that the only response is injson
> which can be tested with the browser, and I've done this. So the
> session controller is being executed, andjsonback is good to go.
>
> However, if I try to run a curl test it doesn't work for me. I get ajsonresponse back saying saying that i need to sign in:
> <code:json>
> {"error":"You need to sign in or sign up before continuing."}
> </code:json>
>
> Here is what I used to test:
>
> <code:commandline>
> curl -H "Content-Type:application/json" -H "Accept:application/json" \
> -d "{\"user\":{\"user_email\":\"u...@test.com\",\"user_password\":
> \"please\"}}" \http://localhost:3000/users/sign_in
> </code:commandline>
>
> What the heck, i tried a non resource block version too :-P
> (result=nada)
> <code:commandline>
> curl -H "Content-Type:application/json" -H "Accept:application/json" \
> -d "{\"user_email\":\"u...@test.com\",\"user_password\":\"please\"}" \http://localhost:3000/users/sign_in

radhames brito

unread,
Sep 21, 2010, 5:00:56 PM9/21/10
to plataforma...@googlegroups.com

it appears that you and i want to do the same thing, i want to put a login box in the layout, it will be present in the entire site, i need the data to be encrypted but i cant use ssl on the whole site, are you trying to do something similar? becuase i think i found out how to do it. I have an almost working ajax login for , but have had a lot of problem setting up apache in my dev box. webrick nad mongrel don have encryption support and i cant test the ssl part of the code.

jjm

unread,
Sep 21, 2010, 5:39:31 PM9/21/10
to Devise
I'm just trying to do json sign_in and sign_up atm.

How have you handled the sign_up portion?

radhames brito

unread,
Sep 21, 2010, 5:58:32 PM9/21/10
to plataforma...@googlegroups.com

well registration is simple, i have a separate page for that, is log in the problem. I was doing what you are doing but when the time came to send the login form, since i have it in the layout , i need to encrypt it some how, this may not be your problem is you only want add ajax to the login but you have a separate page for it, the big problem comes when you try to send https ajax with a http conection.

Chanpory Rith

unread,
Nov 5, 2010, 7:47:40 PM11/5/10
to Devise
Hi, I'm having the same problem with the browser asking for HTTP
Authentication, when login credentials are incorrect. I found this on
another thread, but still not working for me:

class Users::SessionsController <
Devise::SessionsController

# Have to reimplement :recall => "failure"
# for warden to redirect to some action that will return what I want
def create
resource = warden.authenticate!(:scope => resource_name, :recall
=> "failure")
# set_flash_message :notice, :signed_in
sign_in_and_redirect(resource_name, resource)
end

# Example of JSON response
def sign_in_and_redirect(resource_or_scope, resource=nil)
scope = Devise::Mapping.find_scope!(resource_or_scope)
resource ||= resource_or_scope
sign_in(scope, resource) unless warden.user(scope) == resource
render :json => { :success => true, :redirect =>
stored_location_for(scope) || after_sign_in_path_for(resource) }
end

# JSON login failure
message
def failure
render :json => {:success => false, :errors => {:reason => "Login
failed. Try again"}}
end

end

Any other insight?

Rich

unread,
Dec 6, 2010, 1:13:59 AM12/6/10
to Devise
I am also looking for a way to authenticate via Ajax, however I am
using devise 1.1.3 with Rails 3 and the previously mentioned methods
do not seem to work. Any ideas?
> > this may not be your problem is you only want addajaxto the login but you

Ian Hunter

unread,
Dec 6, 2010, 8:33:16 AM12/6/10
to plataforma...@googlegroups.com, Devise
What's your routes file look like

Rich

unread,
Dec 6, 2010, 11:08:34 AM12/6/10
to Devise
TimeCard::Application.routes.draw do |map|
devise_for :users

resources :users
resources :managers
resources :cards

get 'home(.:format)' => 'users#show', :as => :home
get 'manager/approve(.:format)' => 'manager#approve'

root :to => 'users#show'

end

On Dec 6, 6:33 am, Ian Hunter <ianhun...@gmail.com> wrote:
> What's your routes file look like
>
> On Dec 6, 2010, at 1:13 AM, Rich <bngb...@gmail.com> wrote:
>
>
>
>
>
>
>
> > I am also looking for a way to authenticate viaAjax, however I am

Ian Hunter

unread,
Dec 6, 2010, 11:28:32 AM12/6/10
to plataforma...@googlegroups.com

You need to tell it to use your controllers, not the default devise ones:

  devise_for :user, :controllers => { 
:sessions => "auth/sessions",
:registrations => "auth/registrations",
        :oauth_callbacks => "auth/oauth_callbacks" } do 
...

-ian



On Monday, December 6, 2010 at 11:08 AM, Rich wrote:

TimeCard::Application.routes.draw do |map|
devise_for :users

resources :users
resources :managers
resources :cards

get 'home(.:format)' => 'users#show', :as => :home
get 'manager/approve(.:format)' => 'manager#approve'

root :to => 'users#show'

end

On Dec 6, 6:33 am, Ian Hunter <ianhun...@gmail.com> wrote:
What's your routes file look like

On Dec 6, 2010, at 1:13 AM, Rich wrote:







> I am also looking for a way to authenticate viaAjax, however I am
> using devise 1.1.3 with Rails 3 and the previously mentioned methods
> do not seem to work. Any ideas?

Rich

unread,
Dec 6, 2010, 11:30:26 AM12/6/10
to plataforma...@googlegroups.com
Thanks Ian, I will give that a shot when I get home tonight.

(I'm a total Rails/Devise newb, if you couldn't tell!)

-Rich

Rich

unread,
Dec 7, 2010, 1:17:28 AM12/7/10
to Devise
That really helped. Now my controllers are definitely being called.

The problem now is that my create action always seems to bail at
authentication and return a 401 to the browser, regardless of wether
the supplied credentials are valid.

def create
begin
puts "THIS IS PRINTED"
resource = warden.authenticate!(:scope => resource_name, :recall
=> "failure")
puts "BUT THIS IS NOT"
rescue
puts "NOTHING RESCUED EITHER"
end
# set_flash_message :notice, :signed_in
sign_in_and_redirect(resource_name, resource)
end

Why does execution seem to halt after the warden call? If I login via
the devise-generated view, it works fine and even returns JSON like I
want.

Thanks,

Rich

Walter Smith

unread,
Dec 7, 2010, 10:00:04 PM12/7/10
to Devise
You have to disable the automatic 401 response that is sent by
Devise::FailureApp when authentication fails. Change these lines in
config/devise.rb to turn it off:

config.http_authenticatable_on_xhr = false
config.navigational_formats = [:html, :json]

Then XHR JSON requests will be treated the same as regular browser
HTML requests. Note that this means *none* of your JSON requests will
use HTTP authentication, which is probably what you want, but just so
you know...

BTW, Warden uses throw/catch to do this, which is why your rescue
clause isn't seeing it.

- Walter

Rich

unread,
Dec 8, 2010, 7:03:53 PM12/8/10
to plataforma...@googlegroups.com
Thanks Walter. That did the trick.

-Rich

Rich

unread,
Dec 8, 2010, 11:57:42 PM12/8/10
to plataforma...@googlegroups.com
Sorry Walter, I managed to confuse myself (very easy). What you suggested doesn't seem to work. But it did help me resolve another issue. Now, valid logins work fine but invalid ones still return a 401.

Here is my devise.rb, sans comments:

Devise.setup do |config|
config.mailer_sender = "bng...@gmail.com"
require 'devise/orm/mongoid'
config.authentication_keys = [:login]
config.http_authenticatable_on_xhr = false
config.stretches = 10
config.encryptor = :bcrypt
config.pepper = "efa78310b9ca4d88942fa56eca88fa7ed340433e7500f6b9f085828955214b629e3c9398a7a74938699c9fcba5b09cf8cafb8179d3703b2094f60a354edc3fc2"
config.navigational_formats = [:html, :json]
end

Any wrong there?

-Rich

On Dec 7, 2010, at 8:00 PM, Walter Smith wrote:

Reply all
Reply to author
Forward
0 new messages