title: CORS Issue with Rails 4, omniauth-facebook, rack-cors, Heroku
body: <p>Summary:</p>
  
  <p>I have a CORS problem. I'm trying to authenticate with Facebook from a React app with a Rails API.</p>
  
  <p>My app runs on localhost:8080 and my api runs on Heroku.</p>
  
  <p>I'm able to login to facebook and create a session on callback, but the cookie set by facebook isn't picked up by omniauth. I use omniauth-facebook client-side authentication.</p>
  
  <p>I think that the problem is somewhere in my configuration of rack-cors. It works when I run my api on localhost:3000.</p>
  
  <p>Earlier, I received the error <code>csrf-detected</code>, but this was resolved by adding
  <code>provider_ignores_state: true</code> to omniauth configuration. Later, I found that <code>xhrFields: { withCredentials: true }</code> was required for cors calls to pass cookies.</p>
  
  <p>Finale solution: At the end, I resolve this issue by deploying my app to AWS 53 with domain <code>example.com</code> and create CNAMES 'api.example.com` to my api on Heroku.</p>
  
  <p>It takes a day to get CNAMES resolved. You can test your setup in terminal with commands host <code>example.com</code> and host <code>api.example.com</code>.</p>
  
  <p>Alternative solution is to use nginx as proxy.</p>
  
  <p>Versions:</p>
  
  <pre><code>Rails 4.2.2
  Omniauth-facebook 3.0.0
  Omniauth-oauth2 1.4.0
  Rack-cors 0.4.0
  </code></pre>
  
  <p>Issue:</p>
  
  <pre><code>Getting error: no_authorization_code.
  </code></pre>
  
  <p>Routes:</p>
  
  <pre><code>  namespace :api do
      match "/auth/:provider/callback", to: "sessions#create", via: [:get, :post]
      get "/auth/failure", to: "sessions#failure"
      get "/logout", to: "sessions#destroy"
  </code></pre>
  
  <p>Application.rb:</p>
  
  <pre><code>config.middleware.insert_before 0, "Rack::Cors" do
    allow do
      origins "localhost:8000", "example.com"
      resource "*",
        :headers =&gt; :any,
        :methods =&gt; [:get, :post, :delete, :put, :options],
        :max_age =&gt; 1728000
    end
  end
  </code></pre>
  
  <p>Omniauth.rb:</p>
  
  <pre><code>OmniAuth.config.path_prefix = "/api/auth"
  
  OmniAuth.config.logger = Rails.logger
  
  OmniAuth.config.on_failure = Proc.new { |env|
    OmniAuth::FailureEndpoint.new(env).redirect_to_failure
  }
  
  Rails.application.config.middleware.use OmniAuth::Builder do
    provider :facebook,
      Rails.application.secrets.facebook_key,
      Rails.application.secrets.facebook_secret, {
        provider_ignores_state: true,
        client_options: {
          :ssl =&gt; { :ca_file =&gt; "/usr/lib/ssl/certs/ca-certificates.crt" }
        }
      }
    provider :identity,
      fields: [:email],
      on_failed_registration: lambda { |env|
        Api::IdentitiesController.action(:new).call(env)
      }
  end
  </code></pre>
  
  <p>Secret.yml:</p>
  
  <pre><code>development:
  facebook_key: &lt;%= ENV["FACEBOOK_KEY"] %&gt;
  facebook_secret: &lt;%= ENV["FACEBOOK_SECRET"] %&gt;
  </code></pre>
  
  <p>Client:</p>
  
  <pre><code>$.ajax({
    url: http://localhost:3000/api/auth/facebook,
    dataType: "json",
    type: "GET",
    xhrFields: { withCredentials: true },
    success: function(data) {
    }.bind(this),
    error: function(xhr, status, err) {
    }.bind(this)
  })
  </code></pre>
  
  <p>SessionsController:</p>
  
  <pre><code>module Api
    class SessionsController &lt; ApplicationController
      skip_before_action :verify_authenticity_token
      skip_before_action :restrict_access
      skip_after_action :verify_authorized
  
  def create
    user = User.from_omniauth(auth_params)
    session[:user_id] = user.id
    render json: user, status: :ok
  end
  
  def destroy
    session[:user_id] = nil
    render json: {}, status: :ok
  end
  
  def failure
    render json: { errors: params[:message] }, status: :unauthorized
  end
  
  private
  
  def auth_params
    request.env.fetch("omniauth.auth")
  end end end
  </code></pre>
  
  <p>ApplicationsController:</p>
  
  <pre><code>class ApplicationController &lt; ActionController::Base
    include Pundit
    before_action :restrict_access
    after_action :verify_authorized, :except =&gt; :index
    after_action :verify_policy_scoped, :only =&gt; :index
  
      respond_to :json
  
      protected
  
      attr_reader :current_user
  
      # Allows access to current_user in serializators.
      serialization_scope :current_user
  
      def restrict_access
        authenticate_or_request_with_http_token do |token, options|
        @current_user = User.find_by(token: token)
      end
    end
  end
  </code></pre>