Send CSRF token to React SPA from Rails - ruby-on-rails

I have a Rails API and I use devise for authentication. My client side is a React SPA.
I tried turning on the protect_from_forgery in my application_controller as:
class ApplicationController < ActionController::Base
protect_from_forgery with: :null_session
before_filter :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:name])
devise_parameter_sanitizer.permit(:sign_in, keys: [:otp_attempt])
end
end
But with every put/post request it throws an error:
Can't verify CSRF token authenticity
This makes sense because I am not sending back any CSRF token with my requests.
It seems like the way to do this for rails is to add <%= csrf_meta_tag %> in my application.html.erb but I am using jbuilder to convert everything to json before sending it to my SPA.
Is there a way I can access this information in the client side so I can turn on the protection for CSRF?

Related

JSON API Rails 6 with Devise - SignUp Problems

When i try to use the sign_up method of Devise, i get an internal server error but, after create the user.
My application.rb:
class ApplicationController < ActionController::Base
protect_from_forgery with: :null_session, only: Proc.new { |c| c.request.format.json? }
before_action :configure_permitted_parameters, if: :devise_controller?
respond_to :json
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [ :username ])
end
end
Here the output,
Any ideas? 🤔
I am supplementing this with Doorkeeper, but please do not alter the operation of Devise. I also did not use Warden on my own anywhere on the app.
This issue seems to be the problem you are having:
https://github.com/heartcombo/devise/issues/4603
They suggest clearing the cookies of your browser
this usually happens when you are upgrading a bunch of stuck including
devise in one branch And than you get back to some other branch for
something and you have this newer cookie in your browser. Simple
solution is to clear cookies in browser.
Other answers mention upgrading devise version

Devise and Devise Token Auth

I am trying to make rails web app along with rails API for mobile app. For this purpose I am using Devise along with Devise token auth.
I configured routes as it is written in Devise token auth gem so as I could have routes for regular Devise and Devise auth token.
I have 2 problems:
When I add include DeviseTokenAuth::Concerns::SetUserByToken to application_controller it overwrites Devise authenticate_user! and on web side I am being aunthenticated with token.
Possible solution: I created separet ApiApplicationController from which API controllers inherit.
class ApiApplicationController < ActionController::Base
include DeviseTokenAuth::Concerns::SetUserByToken
protect_from_forgery with: :null_session
end
For each POST request which I do in curl to my API I need to add CSRF token.
Possible solution: I could add to both ApplictionController and ApiApplicationController if: Proc.new { |c| c.request.format == 'application/json' } after protect_from_forgery with: :null_session
I used to get the same problem to yours, my solution which is currently working:
# application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :null_session, if: ->{request.format.json?}
end
# api_application_controller.rb
class ApiApplicationController < ActionController::Base
include DeviseTokenAuth::Concerns::SetUserByToken
before_action :authenticate_user!
end

Rails 5 devise_token_auth Can't verify CSRF token authenticity

I am working on a Rails 5 api project which is used by mobile client with gem devise_token_auth for authorization.
I am clear about what the warning means.
1st Question: CSRF protect should be turned OFF for api(JSON/XML)respond, correct?
I searched some on web it seems CSRF just happens on web application with cookie. But i read this from rails api document:
It's important to remember that XML or JSON requests are also affected >and if you're building an API you should change forgery protection >method in ApplicationController (by default: :exception):
class ApplicationController < ActionController::Base
protect_from_forgery unless: -> { request.format.json? }
end
So i still get the warning by adding like this:
class ApplicationController < ActionController::Base
protect_from_forgery unless: -> { request.format.json? }
include DeviseTokenAuth::Concerns::SetUserByToken
end
2nd Question: If API doesn't need CSRF protection, why
protect_from_forgery unless: -> { request.format.json? }
doesn't work?
Not sure if i understood something wrong. Thank you!
the code should be:
protect_from_forgery with: :null_session, if: ->{request.format.json?}
You might have to use null_session for API, it provides an empty session during request but doesn't reset it completely. Used as default if :with option is not specified.

Rails: What does exception and null_session mean in protect_from_forgery

I'm trying to implement token-based API and saw these snippets by google
However, it's hard to understand the meaning by the literal meaning.
Any direction or basic knowledge about this, Thanks ~~
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception, if: Proc.new { |c| c.request.format != 'application/json' }
protect_from_forgery with: :null_session, if: Proc.new { |c| c.request.format == 'application/json' }
end
Rails's document about null_session is here http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection/ProtectionMethods/NullSession.html#method-i-handle_unverified_request, if you check the source code of it's handle_unverified_request method:
def handle_unverified_request
request = #controller.request
request.session = NullSessionHash.new(request.env)
request.env['action_dispatch.request.flash_hash'] = nil
request.env['rack.session.options'] = { skip: true }
request.env['action_dispatch.cookies'] = NullCookieJar.build(request)
end
that means if the request dose't get through the verify_authenticity_token ,then rails will not fetch the session by cookie data, but create a new session for this request. And that session is a instance of NullSessionHash, so "null_session".
I had this concern when working on a Rails 6 API only application.
By default, Rails applies Cross-Site Request Forgery (CSRF) protection to all controllers that subclass from ApplicationController using the following line:
protect_from_forgery with: :exception
But are there instances where you’d want to respond differently, or even ignore CSRF protection altogether? The answer, of course, is “yes”.
If your project or a portion of your project uses an alternative method for authentication such as API tokens or any other “stateless” authentication, then you can safely remove the protect_from_forgery line from whatever base class those controllers inherit.
On the other hand, if your project uses stateful authentication and APIs, such as those projects with lots of AJAX requests, it can be advantageous to use :null_session with protect_from_forgery like so:
protect_from_forgery with: :null_session
That means the user won’t be logged in anymore for that action and can’t perform the change (if the action requires a signed-in user). However, after the action, the session values will be back and the session ID will be the same, so the user will be logged in.
Rather than throwing an exception, which your JavaScript may not be able to handle, it instead sets the session value to nil for the duration of the action. By doing this, any authorization or action scoped to the current user will result in errors that your JavaScript can more easily work with.
So, if you're working on a Rails API only application, you can use this to protect against Cross-Site Request Forgery (CSRF) attacks. This suffices for both cases when either of them arises:
class ApplicationController < ActionController::API
include ActionController::RequestForgeryProtection
if Proc.new { |c| c.request.format != 'application/json' }
protect_from_forgery with: :exception
end
if Proc.new { |c| c.request.format == 'application/json' }
protect_from_forgery with: :null_session
end
end
This implements protect_from_forgery with: :exception when your requests are not of the application/json format, and protect_from_forgery with: :null_session when your requests are of the application/json format.
Note: protect_from_forgery is a class method included in ActionController::RequestForgeryProtection, which is why include ActionController::RequestForgeryProtection was included.
Resources:
CSRF Protection and Ruby on Rails
Undefined method protect_from_forgery for Clearance::SessionsController:Class
That's all.
I hope this helps

Rails 4- Devise & User routes Clashing?

I set up Devise for authentication on a rails 4 app. I tested sign up, sign in and edit forms. Everything worked fine.
Then I needed a form to get additional inputs from users. So I created new user routes to allow me to update/edit user records. Now my devise edit user form doesn't do anything when I click on submit.
I'm guessing this is because of some clash with the new routes but I could be wrong. Should I check something else?
my routes:
devise_for :users
resources :users, only: [:update, :edit]
demo app at http://mktdemo.herokuapp.com/users/edit - Sign in as test#test.com pwd: test1234.
application controller:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) << :name
devise_parameter_sanitizer.for(:account_update) << :name
end
end

Resources