No Cookies with Rails API, CORS and Next getInitialProps - ruby-on-rails

I have a Rails API only backend with rack-cors installed at port 3000.
My NextJS is running on port 5000.
My goal is to consume a cookie set by the server within my NextJS app. It contains a JWT token which holds an email and roles, which will be decoded and used for Authorization.
With Postman I can see the Cookie.
I have set my Cookie in Rails with httpOnly, but I can't see it my Chrome-dev-tools.
When I set a httpOnly cookie with NextJS, it's present.
I can see it in the DevTools "response" when POST to Rails's /login but not in the Application -> Storage -> Cookies(see images attached).
It seems Chrome does not set the cookie in DevTools "storage", it's always empty. Equally if I use "httpOnly" or not.
Can someone help me to consume the Cookie on NextJS (client side)?
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins 'localhost:5000', '127.0.0.1:5000'
resource '*',
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head],
expose: ['Per-Page', 'Total', 'Link', 'ETag', 'Set-Cookie']
end
Here I set my cookie in the Rails controller:
response.set_cookie(
"withIP",
{
value: auth_token[:token],
expires: 30.minutes.from_now,
path: "/",
domain: "localhost:5000", # tried to remove domain completly also tried "127.0.0.1" as domain, as well as '.localhost'
secure: Rails.env.production?,
httponly: true # Rails.env.production?
}
)
This is my request in the NextJS app:
return fetch(`${PublicApiUrl}/auth/login.json?${qs.stringify(options)}`,
{ method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
credentials: 'same-origin', // also tried 'credentials' and with 'inline', as well completely removed at all.
})
.then((response) => {
console.log("RESPONSE", response); // 1* see image below
console.log("RESPONSE HEADERS", response.headers); // *2 ALWAYS undefined
console.log("RESPONSE forEeach", response.headers.forEach(console.log)); // ALWAYS undefined
console.log("response.headers.get('set-cookie')", response.headers.get('set-cookie')); // NULL
console.log("document.cookie", document.cookie); // nope
return response.json()
} )
.then((data)=>{
console.log("DATA", data); // returns my authToken
return data;
})
*1 console.log from above
RESPONSE
Response {type: 'cors', url: 'http://localhost:3000/auth/login.json?email=xxxxxxx&password=xxxxxx', redirected: false, status: 200, ok: true, …}
body: (...)
bodyUsed: true
headers: Headers
[[Prototype]]: Headers
ok: true
redirected: false
status: 200
statusText: "OK"
type: "cors"
url: "http://localhost:3000/auth/login.json?email=XXXXXX&password=XXXXXX"
*2 RESPONSE HEADERS (console.log from above)
RESPONSE HEADERS Headers {}[[Prototype]]: Headers

Related

When I call any API is getting a response CORS error for rails API

I am trying to configure the back-end with the front-end while they are running two different ports. I send a request to it from an app (on a different subdomain than the API) I get the following response:
Access to XMLHttpRequest at 'http://localhost:3000/api/products?desc=true&tab=Competition&trending=false&page=1' from origin 'http://localhost:3001' has been blocked by CORS policy: 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'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
I don't understand why this is happening, since I have already set up Rack CORS Middleware.
CROS configuration as follows:
Gemfile:
gem 'rack-cors'
config/initializers/cors.rb:
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
# TODO: add only authorized url address
origins '*'
resource '*', headers: :any, methods: [:get, :post, :patch, :put]
end
end
For reference, I have attached a browser error screenshot.
Preflight request:
Preflight response body
Ajax request :
export const fetchProducts = () => (dispatch) => {
// dispatch(requestProducts())
const data = { desc: true, tab: 'My Products', trending: false }
$.ajax({
method: 'get',
url: `http://localhost:3000/api/products?desc=true&tab=Competition&trending=false&page=1`,
// csrfToken,
xhrFields: {
withCredentials: true
},
success(response) {
console.log(response)
// dispatch(receiveProducts(response));
},
error(xhr) {
console.log(xhr)
// dispatch(receiveServerErrors(xhr));
}
})
}
Thank You
You should try to set the response header key "access-control-allow-credentials" to be true with a specific origin. Like the code below:
# config/initializers/cors.rb
require 'rack/cors'
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins 'http://localhost:3000'
resource '*', headers: :any, methods: [:get, :post, :patch, :put], credentials: true
end
end

Fetches failing on site deployed on netlify- status 500, cors error, uncaught (in promise) syntax error

Have my backend of my site deployed to heroku and my frontend deployed to netlify. I can login, signup and logout on my site when running on netlify, but other functionality and fetches are not working. Also should note that everything on my site was working perfectly before I had started deploying.
I am seeing these errors in the console: Failed to load resource: the server responded with a status of 500 (Internal Server Error)
photo-sharer-kkirby16.netlify.app/:1 Uncaught (in promise) SyntaxError: Unexpected end of JSON input
at allPosts.js:19
I console logged what the response is in my get request fetch that should get all the posts (instagram-esque app) and it showed as this in the console:
This picture shows there is a status 500 cors error and more info
Here is how I have my getAllPosts action creator set up, which is not working to fetch to get all the posts for some reason.
export const getAllPosts = () => {
return (dispatch) => {
return fetch("https://photo-sharer-backend.herokuapp.com/api/v1/posts", {
credentials: "include",
//say "credentials: include" for when you need to send an authenticated or authorized request of some sort.
method: "GET",
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Credentials": true,
},
})
.then((r) => r.json())
.then((response) => {
dispatch(setAllPosts(response));
});
};
};
Here is my cors.rb file:
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins "https://photo-sharer-kkirby16.netlify.app"
resource "*",
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head],
credentials: true
end
end
First time trying to deploy a site and would really appreciate any help here.

Sending cookie in XHR response but it's not getting stored?

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!

POST request works in Postman but is blocked from React front end. No CORS error, not making it to the server

I have a database setup on Dokku that is currently functioning fine with Postman. My full stack React front end and Rails back end are functioning locally, but when I attempt the same fetch calls (only difference in code is the url), the requests don't have any parameters and are blocked before ever reaching the server.
Here's an example of a fetch call I'm trying (using Redux):
export const createPurchase = purchase => {
return dispatch => {
return fetch(baseURL + 'purchases', {
method: "POST",
headers: {
"Content-Type": "application/json",
"Accepts": "application/json"
},
body: JSON.stringify({ amount: purchase.amount, bundle_name: purchase.bundleName, user_id: purchase.userId })
})
.then(resp => resp.json())
.then(userObj => {
console.log('purchase created!')
})
.catch(error => console.log(error))
}
}
Reading my Rails server logs on Dokku shows that it isn't aware that requests are being made. My CORS file looks like this:
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins '*'
resource '*',
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head]
end
end
I'm not sure why the fetch requests are automatically getting blocked when I am allowing everything with CORS, do I need to add a header to the fetch requests?

rails api, react front end, axios, CORS in development not working

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'
};

Resources