AJAX 401 response sending authentication headers

4,051 views
Skip to first unread message

empika

unread,
Dec 11, 2010, 5:56:20 PM12/11/10
to Devise
Hi

I have a before filter in my controller

before_filter: check_perms

def check_perms
@list = List.find(params[:id])
unless @list.user_id == current_user.id
respond_with({}, :status => :unauthorized)
end
end

If call an action using AJAX and check_perms fails, the server sends
authentication headers and a basic http auth box pops up in the
browser. This is not what id like, I just want to handle the failure
in the JS myself.

I've looked about on google and found a few postings here and there
that suggest setting this var in devise.rb init file

config.http_authenticatable = false

After restarting the server, this has not helped.

I notice the next option in the file is

# Set this to true to use Basic Auth for AJAX requests. True by
default.
config.http_authenticatable_on_xhr = true

So, i tried setting this to false and restarting the server but again
this was to no avail.

Any help on stopping this would be great.
Thanks

Botah

unread,
Dec 16, 2010, 1:59:02 PM12/16/10
to Devise
Bump.
I am having the same issue.
In my case the http auth dialog is showing when the login credentials
are wrong and the user could not get authenticated.
def create
resource = warden.authenticate!(:scope => resource_name, :recall
=> "failure")
# set_flash_message :notice, :signed_in
sign_in_and_redirect(resource_name, resource)
end

def failure
#NEVER GETS called on ajax posts w/ failed logins
#blah more stuff
end

Joey

unread,
Jan 11, 2011, 11:17:49 PM1/11/11
to Devise
Bump. Any ideas about this?

In my situation, I have a very simple remote link, as in:

<%= link_to "Update", update_blah_path(@blah), :method
=> :put, :remote => true %>

The controller calls authenticate_user! and I would like to handle the
resulting 401 in the client-side javascript, but I am prompted with
HTTP auth. (I am on rails 3.0.3 and using the jquery drop-in
replacement.)

Walter Lee Davis

unread,
Jan 12, 2011, 8:30:43 AM1/12/11
to plataforma...@googlegroups.com

On Jan 11, 2011, at 11:17 PM, Joey wrote:

> Bump. Any ideas about this?
>
> In my situation, I have a very simple remote link, as in:
>
> <%= link_to "Update", update_blah_path(@blah), :method
> => :put, :remote => true %>
>
> The controller calls authenticate_user! and I would like to handle the
> resulting 401 in the client-side javascript, but I am prompted with
> HTTP auth. (I am on rails 3.0.3 and using the jquery drop-in
> replacement.)

I don't know jQuery, but Prototype has the onError callback inside all
of its Ajax functions. You might want to ask on a jQuery list about
what the equivalent would be there. If you're truly doing an Ajax
request/response, it shouldn't ever get to a Basic Auth dialog, the
response should be trapped in the JavaScript layer.

Walter

Joey

unread,
Jan 12, 2011, 10:32:03 AM1/12/11
to Devise
Rails 3 abstracts the jquery/prototype differences and triggers
ajax:complete, and ajax:error or ajax:success. I am handling these
appropriately, and am fairly sure that jquery isn't related to the
issue. In the controller action, request.xhr? is true and devise is
configured with
config.http_authenticatable = false
and
config.http_authenticatable_on_xhr = false

Any help would be appreciated.

On Jan 12, 8:30 am, Walter Lee Davis <wa...@wdstudio.com> wrote:
> On Jan 11, 2011, at 11:17 PM, Joey wrote:
>
> > Bump. Any ideas about this?
>
> > In my situation, I have a very simple remote link, as in:
>
> > <%= link_to "Update", update_blah_path(@blah), :method
> > => :put, :remote => true %>
>
> > The controller calls authenticate_user! and I would like to handle the
> > resulting401in the client-side javascript, but I am prompted with

Walter Lee Davis

unread,
Jan 12, 2011, 11:39:30 AM1/12/11
to plataforma...@googlegroups.com

On Jan 12, 2011, at 10:32 AM, Joey wrote:

> Rails 3 abstracts the jquery/prototype differences and triggers
> ajax:complete, and ajax:error or ajax:success. I am handling these
> appropriately, and am fairly sure that jquery isn't related to the
> issue. In the controller action, request.xhr? is true and devise is
> configured with
> config.http_authenticatable = false
> and
> config.http_authenticatable_on_xhr = false
>
> Any help would be appreciated.


I wish I could help you, but then I wish I could get my
http_authenticatable question from three weeks ago answered as well. I
haven't seen this part actually working the way I would expect it to
yet, and I've been using Devise for several months now.

Walter

Hai

unread,
Jan 13, 2011, 9:57:52 AM1/13/11
to Devise
Try have these in the initializer file.

config.http_authenticatable = false
config.http_authenticatable_on_xhr = false
config.navigational_formats = [:"*/*", :html]

this will disable the browser popup box, however, if you want to
redirect to the sign_in page, authenticate_user! will not work since
it only handles http request not xhr request.
what i did is i created my own before filter to render a javascript
erb which contains the redirect when request.xhr? is true.

Joey

unread,
Jan 14, 2011, 12:33:03 AM1/14/11
to Devise
Hai,
I have those settings in place, but unfortunately I still get
prompted.

I don't want to redirect; the 401 would be just perfect for my
purposes, as I'd handle that on the client side. I think my use case
is actually pretty basic, but for some reason I can't seem to disable
the prompt.

Thanks for your help anyway,
J

AH Meij

unread,
Jan 14, 2011, 6:52:48 AM1/14/11
to Devise
Hi,

I'm struggling with the exact same problem. Most links are "remote"
links with jquery, and when user session times out before the user
clicks such a link an Basic Auth dialog pops up. This is the expected
browser response to a 401 status code.


The 401 status code is caused by this code I believe:
https://github.com/plataformatec/devise/blob/master/lib/devise/hooks/timeoutable.rb

When doing a 'normal' html request the user is redirected to the
signin page with the flash message set to the appropriate error:
(devise.failure.timeout)
'Your session expired, please sign in again to continue.'

However, when doing an 'js' request (like the jquery ajax requests)
warden renders the message as a simple "401 Your session expired,
please sign in again to continue."

The configuration flag: config.http_authenticatable_on_xhr = false
is apparently not consulted and the 401 is sent anyway. For 'js'
requests I think the best solution would be to send same redirect as
simple javascript: (window.location= xxx)

I'll continue my search to find where the rendering of the redirect /
401 is done, see if I can monkey patch it.

AH Meij

unread,
Jan 14, 2011, 7:30:09 AM1/14/11
to Devise
Not sure what happened to my previous message however, I had the same
problem (http auth dialogs for jquery ajax requests) and for the
solution is the following:

add config.navigational_formats = [:html, :js] to the devise
config.

and add this to the bottom of the file:
module Devise
class FailureApp
def redirect
store_location!
flash[:alert] = i18n_message

if request.xhr?
# TODO: this should not check xhr but 'javascript'
self.response_body = "window.location='#{redirect_url}';"
else
redirect_to redirect_url
end
end
end
end

Basically it will redirect the user via HTTP if it is an HTML request
and else use javascript to accomplish the same effect.

Joey

unread,
Jan 16, 2011, 11:33:46 AM1/16/11
to Devise
I had been hoping that devise could send the 401 response (signaling
authentication failure) without sending the WWW-Authenticate header
(causing the browser prompt). In fact, this is possible, if I
customize the http_auth method of devise's FailureApp. However, I've
since realized that HTTP actually requires that WWW-Authenticate
header with every 401 response, so that seems to be the wrong road to
go down.

One requirement I have is that the client-side code can choose how to
handle the auth failure as appropriate in different contexts. So,
always returning a "window.location = ..."-style redirect doesn't
quite work (e.g., when responding to a browser extension's request).
Instead, I'm currently returning a custom status code in the failure
case, as described here:
http://stackoverflow.com/questions/199099/how-to-manage-a-redirect-request-after-a-jquery-ajax-call

This is an ugly solution and I'm unsatisfied with it, but it's
certainly not a Devise bug. I'm just surprised there aren't better
conventions for this.

Piotr Gęga

unread,
Jan 16, 2011, 2:36:24 PM1/16/11
to plataforma...@googlegroups.com
One of the solutions is to use 403 instead of 401 and render empty resp. instead of doing redirect

render :nothing => true, :status => 403 

then, ie. you can handle 403 in ajax-based callback. In Jquery it's Jquery.ajaxError callback.
--
pozdrawiam / best regards,
Piotr Gęga (pietia)

microblog: http://twitter.com/piotrgega
open source: http://www.github.com/pietia
linkedin: http://pl.linkedin.com/in/piotrgega
goldenline: http://www.goldenline.pl/piotr-gega



Andre Meij

unread,
Jan 16, 2011, 3:30:52 PM1/16/11
to plataforma...@googlegroups.com
In Devise master the problem is fixed: the login.js.erb will be rendered instead of sending authorization headers. in the login.js one can put some javascript to deal with the situation // call some javascript function already available on the page.
For this to work :js must be added to the navigational_formats

Regards,
Andre

lfat

unread,
Jan 24, 2011, 8:46:46 AM1/24/11
to Devise
Thanks Andre, that helped a great deal but now i'm stuck catching the
401 and doing something with it.

Can you tell me where should i put this login.js.erb?

Thanks
Lars

On Jan 16, 8:30 pm, Andre Meij <ahm...@gmail.com> wrote:
> In Devise master the problem is fixed: the login.js.erb will be rendered instead of sending authorization headers. in the login.js one can put some javascript to deal with the situation // call some javascript function already available on the page.
> For this to work :js must be added to the navigational_formats
>
> Regards,
> Andre
>
> On Jan 16, 2011, at 8:36 PM, Piotr Gęga wrote:
>
> > One of the solutions is to use 403 instead of 401 and render empty resp. instead of doing redirect
>
> > render :nothing => true, :status => 403
>
> > then, ie. you can handle 403 in ajax-based callback. In Jquery it's Jquery.ajaxError callback.
>
> > On Sun, Jan 16, 2011 at 5:33 PM, Joey <j...@aghion.com> wrote:
> > I had been hoping that devise could send the 401 response (signaling
> > authentication failure) without sending the WWW-Authenticate header
> > (causing the browser prompt). In fact, this is possible, if I
> > customize the http_auth method of devise's FailureApp. However, I've
> > since realized that HTTP actually requires that WWW-Authenticate
> > header with every 401 response, so that seems to be the wrong road to
> > go down.
>
> > One requirement I have is that the client-side code can choose how to
> > handle the auth failure as appropriate in different contexts. So,
> > always returning a "window.location = ..."-style redirect doesn't
> > quite work (e.g., when responding to a browser extension's request).
> > Instead, I'm currently returning a custom status code in the failure
> > case, as described here:
> >http://stackoverflow.com/questions/199099/how-to-manage-a-redirect-re...

Mohan Zhang

unread,
Feb 5, 2011, 4:50:24 PM2/5/11
to Devise
I'm probably a little late to this party, but I thought I'd document
the steps I took to get this working in case anyone else is also
wondering about this issue.

So first off, thanks to Andre for pointing out that this change
already exists in Devise master; go ahead and use that branch in your
app. Next, in your devise initializer, set the following config:

# Set this to true to use Basic Auth for AJAX requests. True by
default.
config.http_authenticatable_on_xhr = false # Don't want basic auth
(we want redirect)

...
config.navigational_formats = [:html, :js]

I basically got this from looking at the code, especially
failure_app.rb. The respond method shows:

def respond
if http_auth?
http_auth
elsif warden_options[:recall]
recall
else
redirect
end
end

So if we want a redirect, we just have to make http_auth? return false
(ignoring warden_options[:recall] for now). That method shows:

def http_auth?
if request.xhr?
Devise.http_authenticatable_on_xhr
else
!(request.format && Devise.navigational_formats.include?
(request.format.to_sym))
end
end

That's how I came about the http_authenticatable_on_xhr setting.

Finally, take a look at redirect_url

def redirect_url
request_format = request.format.to_sym
if request_format == :html
send(:"new_#{scope}_session_path")
else
send(:"new_#{scope}_session_path", :format => request_format)
end
end

So this will basically send :js for new_user_session_path (if your
scope is user). Now, it should be pretty obvious that the file we need
to create is views/devise/sessions/new.js.erb (replace "devise" with
your scope if you have moved the views out into separate scopes).

In my new.js.erb, all I did was set the window.location to the sign in
path.

Hope this helps someone,
Mohan


On Jan 24, 7:46 am, lfat <larsf2...@gmail.com> wrote:
> Thanks Andre, that helped a great deal but now i'm stuck catching the
> 401 and doing something with it.
>
> Can you tell me where should i put this login.js.erb?
>
> Thanks
> Lars
>
> On Jan 16, 8:30 pm, Andre Meij <ahm...@gmail.com> wrote:
>
>
>
> > In Devise master the problem is fixed: the login.js.erb will be rendered instead of sending authorization headers. in the login.js one can put some javascript to deal with the situation // call some javascript function already available on the page.
> > For this to work :js must be added to the navigational_formats
>
> > Regards,
> > Andre
>
> > On Jan 16, 2011, at 8:36 PM, Piotr Gęga wrote:
>
> > > One of the solutions is to use 403 instead of 401 and render empty resp. instead of doing redirect
>
> > > render :nothing => true, :status => 403
>
> > > then, ie. you can handle 403 inajax-based callback. In Jquery it's Jquery.ajaxError callback.
>
> > > On Sun, Jan 16, 2011 at 5:33 PM, Joey <j...@aghion.com> wrote:
> > > I had been hoping that devise could send the 401 response (signaling
> > > authentication failure) without sending the WWW-Authenticate header
> > > (causing the browser prompt). In fact, this is possible, if I
> > > customize the http_auth method of devise's FailureApp. However, I've
> > > since realized that HTTP actually requires that WWW-Authenticate
> > > header with every 401 response, so that seems to be the wrong road to
> > > go down.
>
> > > One requirement I have is that the client-side code can choose how to
> > > handle the auth failure as appropriate in different contexts. So,
> > > always returning a "window.location = ..."-style redirect doesn't
> > > quite work (e.g., when responding to a browser extension's request).
> > > Instead, I'm currently returning a custom status code in the failure
> > > case, as described here:
> > >http://stackoverflow.com/questions/199099/how-to-manage-a-redirect-re...
>
> > > This is an ugly solution and I'm unsatisfied with it, but it's
> > > certainly not a Devise bug. I'm just surprised there aren't better
> > > conventions for this.
>
> > > On Jan 14, 7:30 am, AH Meij <ahm...@gmail.com> wrote:
> > > > Not sure what happened to my previous message however, I had the same
> > > > problem (http auth dialogs for jqueryajaxrequests) and for the
Reply all
Reply to author
Forward
0 new messages