With devise one uses before_filter :authenticate_user!
to restrict access to authenticated users only.
When an unauthenticated user tries to visit a restricted page anyways, devise automatically causes a redirect to the sign in page.
So trying to open http://localhost:3000/users/edit will result in a redirect to http://localhost:3000/users/sign_in.
Now, if I define the link http://localhost:3000/users/edit as :remote => true
, devise will only issue a 401 status code via JS.
How can I elegantly cope with that situation and display the login dialog in an overlay OR redirect as the non-remote variant would do it?
Does devise offer a default strategy for that situation which I'd simply need to activate?
$(document).ajaxError(function (e, xhr, settings) { if (xhr.status == 401) { $('.selector').html(xhr.responseText); } });
This is the solution I chose for now (in CoffeeScript syntax):
$ -> $("a").bind "ajax:error", (event, jqXHR, ajaxSettings, thrownError) -> if jqXHR.status == 401 # thrownError is 'Unauthorized' window.location.replace('/users/sign_in')
However this (on it's own) just forgets about the page the user wanted to visit initially, which confines usability.
Additional (controller) logic is required for more elegant handling.
UPDATE: correct redirect
Within the function, this
holds the initial URL the user intended to go to.
By calling window.location.replace(this)
(instead of explicitly redirecting to the sign in page), the app will try to redirect the user to the initially intended destination.
Although still impossible (unauthorized), this will now be a GET call (instead of JS/AJAX). Therefore Devise is able to kick in and redirect the user to the sign in page.
From there on, Devise operates as usual, forwarding the user to the originally intended URL after successful sign in.
A version mixing event binding with location.reload()
:
$(function($) {
$("#new-user")
.bind("ajax:error", function(event, xhr, status, error) {
if (xhr.status == 401) { // probable Devise timeout
alert(xhr.responseText);
location.reload(); // reload whole page so Devise will redirect to signin
}
});
});
Testing with Devise 3.1.1, this does correctly set session["user_return_to"]
, so the user returns to the page after signing in again ().
I added the alert
as a simple way to address the inelegant message issue discussed here: Session Timeout Message in RoR using Devise
Here is my copy-past-hapy(tm) solution in CoffeScript. It redirects all 401 to login page.
<% environment.context_class.instance_eval { include Rails.application.routes.url_helpers } %>
$(document).ajaxError (_, xhr)->
window.location = '<%= new_user_session_path %>' if xhr.status == 401
and in Javascript:
<% environment.context_class.instance_eval { include Rails.application.routes.url_helpers } %>
$(document).ajaxError( function(event, xhr){
if (xhr.status == 401) {
window.location = '<%= new_user_session_path %>'
}
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With