I'm working with this stack:
I have read many articles, manuals, stackoverflow topics, google random results, blogs, etc, but are all very deprecated.
Using a practical approach (tl;dr here) I just need get a real session between Devise 3 and Backbone in different server stances and holding it, like two separate projects. Remote login, you know.
I'm really stuck with that so I would greatly appreciate your suggestions.
Thank you guys.
Personally I have the same situation in my project with Angular instead of Backbone as a front-end and Rails 4 API with Devise. I will try to sum things up for you in the assumption that I got your question right.
To work correctly with the sessions in your scenario you need to be sure that:
Please, read this article about CORS. If you are not familiar with CORS the article should provide necessary background for my answer. Some info about CSRF protection is here
Here is your scenario step-by-step:
GET
request such as http://yourserver/signin
POST
request with user credentials (name, password) and CSRF token in headers and current unauthorized session in cookies. It is crucial that request contains session information. Otherwise it will be granted different CSRF token on Rails side and you will get WARNING: Can't verify CSRF token authenticity
message.Here is what can be done to get it working:
Rails backend should respond correctly to requests from front-end. Which means it should:
OPTIONS
requests (preflight requests)Front end should:
The simplest way to teach your Rails back-end to respond to CORS requests is to use rack-cors gem. This will also provide correct CORS headers.
config.middleware.insert_before Warden::Manager, Rack::Cors do
allow do
origins '*' # it's highly recommended to specify the correct origin
resource '*',
:headers => :any,
:methods => [:get, :post, :options], # 'options' is really important
# for preflight requests
:expose => ['X-CSRF-Token'] #allows usage of token on the front-end
end
end
Last thing on a backend side is to provide CSRF token. Custom Devise controller should handle this task perfectly.
class SessionsController < Devise::SessionsController
after_action :set_csrf_header, only: [:new, :create, :destroy]
#...
protected
def set_csrf_header
response.headers['X-CSRF-Token'] = form_authenticity_token
end
end
Note that you need CSRF token when you send first GET
request (new
), when you submit credentials through POST
request (create
) and when you sign out of your application by sending DELETE
request (destroy
). If you don't send CSRF token on sign out you won't be able to sign in without reloading the page.
And somewhere in config/routes.rb don't forget to specify that you are now using custom controller:
/config/routes.rb
devise_for :users, :controllers => {:sessions => "sessions"}
Now, to the front-end. Please, have a look at this script that overrides standard Backbone.sync
and handles communication with Rails server.
It is almost good with couple of corrections needed:
beforeSend: function( xhr ) {
if (!options.noCSRF) {
// we dont have csrf-token in the document anymore
//var token = $('meta[name="csrf-token"]').attr('content');
// New Line #1
// we will get CSRF token from your application.
// See below for how it gets there.
var token = YourAppName.csrfToken;
if (token) xhr.setRequestHeader('X-CSRF-Token', token);
// New Line #2
// this will include session information in the requests
xhr.withCredentials = true;
}
//..some code omitted
//................
// Trigger the sync end event
var complete = options.complete;
params.complete = function(jqXHR, textStatus) {
// New Lines #3,4
// If response includes CSRF token we need to remember it
var token = jqXHR.getResponseHeader('X-CSRF-Token')
if (token) YourAppName.csrfToken = token;
model.trigger('sync:end');
if (complete) complete(jqXHR, textStatus);
};
}
I'm not sure this qualifies as a complete answer to your question, but at least it is something to start from. It might not be the best way, but it is the way. Let me know if you have any questions.
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