I'm doing a singe-page application using Rails. When signing in and out Devise controllers are invoked using ajax. The problem I'm getting is that when I 1) sign in 2) sign out then signing in again doesn't work.
I think it's related to CSRF token which gets reset when I sign out (though it shouldn't afaik) and since it's single page, the old CSRF token is being sent in xhr request thus resetting the session.
To be more concrete this is the workflow:
WARNING: Can't verify CSRF token authenticity
in server logs)Any clues very much appreciated! Let me know if I can add any more details.
Jimbo did an awesome job explaining the "why" behind the issue you're running into. There are two approaches you can take to resolve the issue:
(As recommended by Jimbo) Override Devise::SessionsController to return the new csrf-token:
class SessionsController < Devise::SessionsController
def destroy # Assumes only JSON requests
signed_out = (Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name))
render :json => {
'csrfParam' => request_forgery_protection_token,
'csrfToken' => form_authenticity_token
}
end
end
And create a success handler for your sign_out request on the client side (likely needs some tweaks based on your setup, e.g. GET vs DELETE):
signOut: function() {
var params = {
dataType: "json",
type: "GET",
url: this.urlRoot + "/sign_out.json"
};
var self = this;
return $.ajax(params).done(function(data) {
self.set("csrf-token", data.csrfToken);
self.unset("user");
});
}
This also assumes you're including the CSRF token automatically with all AJAX requests with something like this:
$(document).ajaxSend(function (e, xhr, options) {
xhr.setRequestHeader("X-CSRF-Token", MyApp.session.get("csrf-token"));
});
Much more simply, if it is appropriate for your application, you can simply override the Devise::SessionsController
and override the token check with skip_before_filter :verify_authenticity_token
.