shakacode / react_on_rails

Integration of React + Webpack + Rails + rails/webpacker including server-side rendering of React, enabling a better developer experience and faster client performance.
https://www.shakacode.com/react-on-rails/docs/
MIT License
5.08k stars 627 forks source link

How to refresh CSRF token? #1615

Open Talha345 opened 1 month ago

Talha345 commented 1 month ago

I am using ReactOnRails.authenticityToken(); to get the CSRF token generated by csrf_meta_tags. The problem is that when I send first request, it works fine as the token is valid. Whenever I send any subsequent API request, it returns the previously invalidated CSRF token and therefore I get ActionController::InvalidAuthenticityToken. How can I refresh the CSRF token after each API request?

justin808 commented 1 month ago

@Talha345 nothing has changed around this for many years. I think there is something specific to your app.

Talha345 commented 1 month ago

@justin808 Could be but do you have any suggestion on how to deal with this scenario. I will try to explain my specific scenario in detail:

  1. React SPA endpoint is triggered and component is mounted and csrf_meta_tags are added via the layout.
  2. I use the CSRF token and execute a login API. On success response, I update the user in the SPA and therefore there is no page reload.
  3. Since the user is logged in, I show a link to Logout and upon clicking on it another API is triggered but of course the CSRF token is already invalidated and I get an error.

NOTE: In older versions of Rails, a single CSRF token was used for each session but since recent versions, we have a new CSRF token for each new request.

Talha345 commented 1 month ago

Solution for anyone having the same issue:

  1. Added a after_action method in ApplicationController:
after_action :add_csrf_token_to_json_request_header

private

  def add_csrf_token_to_json_request_header
    if request.format == :json && !request.get? && protect_against_forgery?
      response.headers['X-CSRF-Token'] = form_authenticity_token
    end
  end
  1. On React side:
    if (response.headers['x-csrf-token']) {
      setAuthenticityToken(response.headers['x-csrf-token'])
    }

    export function setAuthenticityToken(token) {
     const metaTag = document.querySelector("meta[name='csrf-token']");
     metaTag.setAttribute('content', token)
    }

Took inspiration from https://stackoverflow.com/questions/33941864/rails-automatically-update-csrf-token-for-repeat-json-request

justin808 commented 1 month ago

@Talha345 @Judahmeek @alexeyr-ci Should this go into the docs? If so, could one of you submit a PR and I'll merge it.