Search code examples
ruby-on-railsrubyruby-on-rails-5

Can someone explain why :null_session still returns this error?


I'm new to rails and having a tough time with the concept of protect_from_forgery, from what i understand this method uses :verify_authenticity_token to protect a route. The route I'm using this on is an API route; something like this:

class ApiController < ActionController::API
  protect_from_forgery with: :null, only: %i[execute]

  before_action :validate_csrf_token, only: %i[execute]

  def execute
  # action here
  end

  private

  def validate_csrf_token
     return true unless request.post?
     token = request.headers['X-CSRF-Token']
     if token.blank? || !valid_authenticity_token?(token, nil)
       render json: { error: 'Invalid CSRF token' }, status: :unprocessable_entity
     end
  end
end

I also have another controller which i use to get my token from my rails app

class CsrfTokenController < ApplicationController
  skip_before_action :verify_authenticity_token

  def index
    render json: { csrf_token: form_authenticity_token }
  end
end

which i send to my ApiController. the first issue i encounter is this error

HTTP Origin header (http://localhost:3020) didn't match request.base_url (http://localhost:3000)

secondly when i perform post requests the token i sent is invalid

error: "Invalid CSRF token"

i can confirm that the token request is the one being sent back when i print the variable.

Is there something i'm missing here, or am i understanding :null_session all wrong?

Edit:

Updated due to incorrect method mentioned for protecting route


Solution

  • What the CSRF protection does is give you a degree of certainty that the request originated from a page rendered by you own server. That's great for classical apps and APIs that are just meant to feed your own Single Page Application with JSON. But if you're creating an API that is designed to be used cross domain and also possibly by server based clients the Rails CSRF protection is irrelevant and actually completely opposed to those requirements.

    method uses form_authenticity_token to protect a route

    No not really. The way this works is that a unique token is rendered into the HTML of the page (forms and the meta tag) and the same token is stored in the session.

    Since the session is stored in an encrypted cookie we have a reasonable degree of certainty that it hasn't be tampered with.

    When the user then submits the form your controller checks for a token in the requests body or the X-CSRF-Token header and raises an exception unless it matches the value provided by the session cookie. This token should only be valid for a single request cycle.

    Since cookies are single domain by default you shouldn't actually expect this to work when you're sending a request from a different origin.

    See: