4
votes

I'm building a Chrome extension in React that's connected to a Rails 6 app for the backend. I want the Rails app to act as an API and store data from the extension, but I can't get it to accept requests from my extension.

Here's my fetch code in my extension:

    fetch('https://www.myapp.com/api/post_comment/test', {
     method: 'get',
     headers: {'Content-Type':'application/json'}
    });

and here's the errors I keep getting in my js console:

Access to fetch at 'https://www.myapp.com/api/test' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

The FetchEvent for "https://www.myapp.com/api/test" resulted in a network error response: an object that was not a Response was passed to respondWith().

I tried adding the rack-cors gem to my Rails app:

#Gemfile:

gem 'rack-cors'

#config/initializers/cors.rb:

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins '*'
    resource '/api/*', headers: :any, methods: [:get, :post, :patch, :put]
  end
end

and I also tried adding the header manually in my Rails app controller:

before_action :cors_preflight_check
after_action :cors_set_access_control_headers


def cors_set_access_control_headers
    headers['Access-Control-Allow-Origin'] = '*'
    headers['Access-Control-Allow-Methods'] = 'POST, PUT, DELETE, GET, OPTIONS'
    headers['Access-Control-Request-Method'] = '*'
    headers['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept, Authorization'
end

# If this is a preflight OPTIONS request, then short-circuit the
# request, return only the necessary headers and return an empty
# text/plain.

def cors_preflight_check
    headers['Access-Control-Allow-Origin'] = '*'
    headers['Access-Control-Allow-Methods'] = 'POST, PUT, DELETE, GET, OPTIONS'
    headers['Access-Control-Request-Method'] = '*'
    headers['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept, Authorization'
    render :text => '', :content_type => 'text/plain'
end

But I'm still getting the error. Anyone know how I can fix this?

If I add mode: 'no-cors', to my fetch code, I'm able to post data, but I can't retrieve data.

2

2 Answers

4
votes

move your config from config/cors.rb to config/initializers/cors.rb

all files in config/initializers/ will be loaded on rails startup.

also it could be possible to blocked by rails 6 new feature to block unwanted hosts.

add following snippet to config/application.rb or env specific file in config/environments/

  config.hosts.clear

check Upgraded Rails to 6, getting Blocked host Error

1
votes

In an extension you have two origins - origin of the page the content script is injected to and background script origin.

For background script to be able to connect to your api - you need to add "https://www.myapp.com/" to manifest's permissions, it is not subject to regular CORS checking. Content script can talk to background via message passing, and thus may avoid CORS:

// from content script:
chrome.runtime.sendMessage({action: "doSomeAction"}, function(response) {
  console.log(response);
});
//in background script:
chrome.runtime.onMessage.addListener(
  function(request, sender, sendResponse) {
    console.log(sender.tab ? "from a content script:" + sender.tab.url : "from the extension");
    if (request.action == "doSomeAction") {
      fetch('https://www.myapp.com/api/post_comment/test', {
        method: 'get',
        headers: {'Content-Type':'application/json'}
      }).then(function(response){
        // do something with response and pass pack to content script:
        sendResponse({foo: "bar"});
      });
      return true; // keep message channel so sendResponse is available in 
    }
  });

As for rails CORS - I'd start with ensuring the header is present:

curl -IX OPTIONS https://www.myapp.com/api/post_comment/test -H 'Access-Control-Request-Method: GET' -H 'Origin: http://someorigin.example.com'

this should return something like:

HTTP/2 200
server: nginx
date: Thu, 22 Oct 2020 19:50:00 GMT
access-control-allow-origin: http://someorigin.example.com
access-control-allow-methods: GET, POST, OPTIONS
access-control-expose-headers:
access-control-max-age: 7200
cache-control: no-cache
x-request-id: 5846b7400a15b19398c96bec7faac570
x-runtime: 0.001532