I use a Rails backend and React frontend. I recently deployed my app to Heroku using the following naming convention: app-name-frontend.herokuapp.com and app-name-backend.herokuapp.com, so the domain is the same. However, when I send a cookie from the api to frontend, I can see the cookie being sent but its never saved. My cors is setup as follows:
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins 'app-name-frontend.herokuapp.com'
resource '*',
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head],
credentials: true
end
end
And I set my Cookie as follow:
{
value: JsonWebToken.encode(user_id: user.id),
expires: 1.day,
httponly: true,
secure: true,
same_site: :lax,
domain: :all
}
Since I have domain as :all this means that it should work on all domains and subdomains, and since both are on the domain herokuapp.com shouldn't Safari set the cookie? What am I doing wrong?
May be the HttpOnly attribute is added in your cookie settings. If the HttpOnly flag is included in the HTTP response header, the cookie cannot be accessed through the client-side script. You can find a detailed explanation here
Related
I'm trying to pass an HttpOnly cookie with a response from an API I'm writing. The purpose is for the cookie act like a refresh token for the purpose of silent refresh for a SPA in React.
In my controller method, I've got the following code:
response.set_cookie(
:foo,
{
value: 'testing',
expires: 7.days.from_now,
path: '/api/v1/auth',
secure: true,
httponly: true
}
)
I'm making a post request to this action with a fetch command like so:
fetch("http://localhost:3001/api/v1/auth", {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: 'aaron#example.com',
password: '123456',
})
})
Not sure if that matters but I'm wondering if passing cookies in a XHR response doesn't work? However, this seems to be working as you can see in my response I'm getting this:
Set-Cookie: foo=testing; path=/api/v1/auth; expires=Sun, 26 Jan 2020 05:15:30 GMT; secure; HttpOnly
Also in the Network tab under Cookies I'm getting this:
However, I'm NOT getting the cookie set under Application -> Cookies:
To clarify, the React app is sitting on localhost:3000 and the rails backend is listening on localhost:3001.
Any ideas?
Ok, so it looks like I needed to configure my CORS (in Rails this is your Rack::CORS middleware.
I setup my config/initializers/cors.rb file like so:
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins 'http://localhost:3000'
resource '*',
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head],
credentials: true
end
end
and my fetch command should look something like this with credentials: 'include' as a parameter:
return fetch(`${endPoint}`, {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
email,
password,
password_confirmation: passwordConfirmation
})
})
Adding credentials: true allows cookies to be set by the browser. Apparently, even if you send them, you need Access-Control-Allow-Credentials: true in the headers for the browser to do anything with them.
EDIT: Recreating this application for learning experience I came across this issue again even after including the credentials option. I wasn't seeing the HttpOnly cookie being stored in the browser. Turns out however, that it WAS and does get sent. You can probably test for this in a controller action. Keep this in mind if this solution doesn't 'seem' to work for you!
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!
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'
};
I am out of ideas. I made a small app for a friend who is using it in a school. As long as it is on non-school internet, it works fine. At school, though, there are two buttons that don't work. They both are making an ajax request to a different controller. They give a no access-control-allow-origin header error. I have tried every fix that I can find, and nothing will work. I am out of ideas. The current iteration involves the rack-cors gem, which I have below. I have also tried the fixes at
CORS issue: Getting error "No 'Access-Control-Allow-Origin' header is present" when it actually is, XMLHttpRequest No 'Access-Control-Allow-Origin' header is present on the requested resource, http://www.yihangho.com/rails-cross-origin-resource-sharing/, and http://leopard.in.ua/2012/07/08/using-cors-with-rails/. Is there anything else I can do?
config.ru
require 'rack/cors'
use Rack::Cors do
allow do
origins '*'
resource '*',
:headers => :any,
:methods => [:get, :post, :delete, :put, :options, :patch]
end
end
application.rb
config.middleware.insert_before 0, "Rack::Cors" do
allow do
origins '*'
resource '*', headers: :any, methods: [:get, :post, :patch, :options, :put]
end
end
CORS is designed to prevent cross domain XML requests.
I'm not sure as to the specific reasons for this, but the short of it is that if you try and hit an external domain with Ajax (without CORS permissions granted), it will be denied.
As long as it is on non-school internet, it works fine
Yep, because the "domain" will be considered "local" Sorry I misread that as "Intranet".
This will depend on the server you're accessing - you need to make sure the server has CORS enabled.
I've dealt with a similar issue to you before.
You've done the right thing by using rack-cors:
#config/application.rb
config.middleware.insert_before 0, "Rack::Cors" do
allow do
origins '*' #-> has to be "*" or specific
resource '*', headers: :any, methods: [:get, :post, :options]
end
end
If you're using this (remember, you have to restart your server to get it working), it should work.
The main considerations for CORS are the origin and resource - they either have to be "*" (all), or explicitly defined (IE google.com or something).
In your case, allowing them "all" should be okay to test - you'll then need to ensure you have the specific domain defined.
I have a rails server witch expose APIs and i want to allow only some domains to do requests on my api.
I've added the gem rack-cors to my Gemfile and the following code to application.rb
config.middleware.use Rack::Cors do
allow do
origins '*'
resource '*', headers: :any, methods: [:get, :post, :put, :delete, :options]
end
end
This code allows every domain to send requests to my server, is there a way to dynamically updates the allowed domains list? Is this a good approach to my problem?
I've built a Ruby On Rails 3.0.x app that returns JSON. So far, I've got only four methods in my Advertisements controller: index, show, update, destroy.
When I try to call a method from an app within the same domain (through AJAX & jQuery), I succeed. But when I try to do the same from another app within another domain, I get the following error message, only when I try to use the methods PUT and DELETE (it works fine for GET and POST):
XMLHttpRequest cannot load URL_HERE Origin URL_HERE is not allowed by
Access-Control-Allow-Origin.
My RESTful service is called via HTTP, not HTTPS.
Below is the AJAX code that I'm using (16 is the advertisement's id):
$.ajax({
type: "DELETE",
url: "http://SERVICE_URL/advertisements/16.json",
crossDomain: true,
success: function(response){
alert("test");
}
});
Any ideas?
Thanks.
You might try rack-cors. We enable CORS for our web service with:
# config/application.rb
config.middleware.use Rack::Cors do |requests|
requests.allow do |allow|
allow.origins '*'
# perhaps you would stick :put and :delete here?
# then you should follow the rack-cors documentation to only enable it where necessary
allow.resource '*', :headers => :any, :methods => [:get, :post]
end
end