My Rails app uses Devise for authentication. It has a sister iOS app, and users can log in to the iOS app using the same credentials that they use for the web app. So I need
You don't want to disable CSRF, I have read that people think it doesn't apply to JSON APIs for some reason, but this is a misunderstanding. To keep it enabled, you want to make a few changes:
on there server side add a after_filter to your sessions controller:
after_filter :set_csrf_header, only: [:new, :create]
protected
def set_csrf_header
response.headers['X-CSRF-Token'] = form_authenticity_token
end
This will generate a token, put it in your session and copy it in the response header for selected actions.
client side (iOS) you need to make sure two things are in place.
your client needs to scan all server responses for this header and retain it when it is passed along.
... get ahold of response object
// response may be a NSURLResponse object, so convert:
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
// grab token if present, make sure you have a config object to store it in
NSString *token = [[httpResponse allHeaderFields] objectForKey:@"X-CSRF-Token"];
if (token)
[yourConfig setCsrfToken:token];
finally, your client needs to add this token to all 'non GET' requests it sends out:
... get ahold of your request object
if (yourConfig.csrfToken && ![request.httpMethod isEqualToString:@"GET"])
[request setValue:yourConfig.csrfToken forHTTPHeaderField:@"X-CSRF-Token"];
Final piece of the puzzle is to understand that when logging in to devise, two subsequent sessions/csrf tokens are being used. A login flow would look like this:
GET /users/sign_in ->
// new action is called, initial token is set
// now send login form on callback:
POST /users/sign_in ->
// create action called, token is reset
// when login is successful, session and token are replaced
// and you can send authenticated requests