0
votes

I'm sure that this question (or questions very similar) has been asked many times, but I'm new to cross origin requests, and in searching through other people's answers, I haven't been able to send basic requests from a React front end to a rails-api only backend, while both are running locally on development servers.

Any help to resolve this issue/help me understand why it's not working would be really appreciated!

Front end: (as on onClick function handler, running on an npm dev server on port 3000)

function sendGetRequest() {
    Axios.request({
        url: 'http://localhost:3001/users/2',
        method: 'get',
        headers: {
            'Content-Type': 'application/json',
            'Accept': 'application/json'
        },
        withCredentials: true
    }).then(function (response) {
        console.log(response);
    })
    .catch(function (error) {
        console.log(error);
    });
}

Backend (rails rack-cors, running on a rails puma server, on port 3001):

config.middleware.insert_before 0, Rack::Cors do
      allow do
        origins 'localhost:3000'
        resource '*',
          :headers => :any,
          :expose  => ['access-token', 'expiry', 'token-type', 'uid', 'client'],
          :methods => [:get, :post, :put, :patch, :delete, :options, :head]
      end
    end

I have verified through postman and rspec that the various controller methods are all responding with JSON appropriately.

When I attempt to run this, I receive errors along the lines of:

"Failed to load http://localhost:3001/users/2: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. Origin 'http://localhost:3000' is therefore not allowed access. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute."

Thanks so much!

1
Your origins 'localhost:3000' line need to instead be origins 'http://localhost:3000' (the http:// protocol part must be included). But even once you make that change I don’t think it’s going to fix the problem that’s causing the error message in the question. The cause of that seems to be that some other part of your server code for the http://localhost:3001 server is already sending back an Access-Control-Allow-Origin: * response header. The server should instead be sending back an Access-Control-Allow-Origin: http://localhost:3001 response header. - sideshowbarker
@sideshowbarker Got it - and you're correct, adding the http protocol did not fix the issue. Do you know where a rails api (presumably in the rack cors gem somewhere) the Access-Control-Allow-Origin header is being sent? - Nathan Lauer
@sideshowbarker I'm not so sure the protocol prefix is required with localhost. I have a similar setup that functions correctly without 'http://'. - w_hile
@w_hile It’s definitely required by browsers. A Access-Control-Allow-Origin: localhost:3001 response header is not going to match the http://localhost:3001 origin the browser expects. That header value needs to be an origin, which must include the protocol. - sideshowbarker
Whilst the value of the Origin request header and the ACAO response header do indeed need to contain the scheme, the origins node in rack-cors is generically compared against the Origin header, to check whether the CORS response headers should be sent. So in this case, you can indeed specify origins localhost:3001 - this will allow requests from both http://localhost:3001 and https://localhost:3001 - roryhewitt

1 Answers

0
votes

Let me share you with some code from my app using rack-cors.

This is the code in config/initializers/cors.rb.

if Rails.application.config.x.cors_allowed_origins
  Rails.application.config.middleware.insert_before 0, Rack::Cors do
    allow do
      origins Rails.application.config.x.cors_allowed_origins

      resource '*',
        headers: :any,
        methods: [:get, :post, :put, :patch, :delete, :options, :head],
        credentials: true
    end
  end
end

Rails.application.config.x.cors_allowed_origins above is set in config/application.rb via an environment variable in order to set different allowed origins on development and on production.

config.x.cors_allowed_origins = ENV['CORS_ALLOWED_ORIGINS']

With these setting, this app is expected to be launched with an environment variable which accepts the running port of SPA like CORS_ALLOWED_ORIGINS=localhost:8080 bundle exec rails s. localhost:8080 here is the host where SPA is running on.

In SPA side, this is the option given to whatwg-fetch. Sorry that I don't use Axios here, but it would be of your help.

const options = {
  headers: {
    'Content-Type': 'application/json',
    'Accept': 'application/json'
  },
  mode: 'cors',
  credentials: 'include'
};