I was writing a blog application which has front end in react + typescript and backend in go iris. I'm doing a get request to fetch blog content. Backend runs at localhost:5000 and node at localhost:3000.But it fails with the error
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:5000/getposts. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).
I have already configured CORS in the backend
Cors := cors.New(cors.Options{
AllowedOrigins: []string{"http://localhost:3000"},
AllowCredentials: true,
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS"},
AllowedHeaders: []string{"Cache-Control", "X-File-Name", "X-Requested-With", "X-File-Name", "Content-Type", "Authorization", "Set-Cookie", "Cookie"},
Debug: true,
})
authConfig := basicauth.Config{
Users: map[string]string{USER_NAME: PASSWORD},
Realm: "Authorization Required", // defaults to "Authorization Required"
Expires: time.Duration(30) * time.Minute,
}
authentication := basicauth.New(authConfig)
app := iris.New()
app.Use(Cors)
app.Get("/getposts", authentication, GetPostsHandler)
This is how I send request
fetch("http://localhost:5000/getposts", {
method: "get",
credentials: "include",
mode: "cors",
headers: [
["Content-Type", "application/json"],
["Authorization", "Basic " + btoa("Sreyas:password")]
]
})
.then(response => {
if (response.ok) {
response.json().then(rawdata => {
this.setState({ blogdata: rawdata });
});
} else {
console.log("No posts");
this.setState({ blogdata: null });
}
})
.catch(error => {
console.log("Server Error");
this.setState({ blogdata: null });
});
I searched and tried for hours to fix this problem but no luck.
Thanks to Slotheroo for his suggestion to use nginx and that was the only possible way i could over come this problem.I used nginx to proxy the requests and route both front end and back end to 8000 port. I will leave a sample of my nginx server configuration and changes made to code here so that it helps anyone in future :)
(Please note that using loop back ip like "localhost" can affect performance on loading and sending request therefore use exact ip of the machine to overcome such performance issue)
nginx.conf
server {
listen 8000;
server_name localhost;
location / {
proxy_pass http://localhost:3000;
}
location /getposts {
proxy_pass http://localhost:5000/getposts;
}
}
Added localhost:8000 to Allowed Orgins in backend
AllowedOrigins: []string{"http://localhost:8000"},
request is now send to 8000 port
fetch('http://localhost:8000/getposts',{
method: 'get',
credentials: "include",
mode: "cors",
headers: [
["Content-Type", "application/json"],
["Authorization","Basic "+btoa('Sreyas:password')],
]
}).then((response) => {
if(response.ok){
response.json().then(rawdata =>{
this.setState({blogdata:rawdata})
})
}else{
console.log("No posts")
this.setState({blogdata:null})
}
}).catch(error => {
console.log("Server Error")
this.setState({blogdata:null})
})
}
Related
TL;DR: How to actually change a request header in http-proxy-middleware?
To get around some CORS errors I set up a local proxy using the http-proxy-middleware module. In addition to setting the mode of my request to "no-cors" I need to change an additional header: "Content-Type". However, this seems to be not working. In fact, I cannot even change the response headers on a redirected (through my proxy) request. For local requests (fetching pages etc) I am able to change the response headers but even then I am unable to change the request headers.
This is my setupProxy.js:
const { createProxyMiddleware } = require("http-proxy-middleware");
module.exports = function (app) {
app.use((req, res, next) => {
req.header("Content-Type", "application/json");
res.header("Access-Control-Allow-Origin", "*");
next();
});
function onProxyReq(proxyReq, req, res) {
console.log("test 1");
proxyReq.setHeader("Content-Type", "application/json");
req.header("Content-Type", "application/json");
}
app.use(
"/api",
createProxyMiddleware({
target: "https://my-domain.com/",
changeOrigin: true,
onProxyReg: { onProxyReq },
// secure: true,
// on: {
// proxyReq: requestInterceptor(async (buffer, proxyReq, req, res) => {
// console.log("test 2");
// }),
// },
logger: console,
})
);
};
And this is the code that sends the request:
try {
let requestOptions: RequestInit = {
method: "POST",
mode: "no-cors",
headers: {
accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({
email: { username },
password: { password },
}),
};
fetch("https://localhost:3000/api/path/to/login/api", requestOptions)
.then(async function (response) {
console.log(response);
if (!response.ok) {
setError("Error code: " + response.status.toString());
}
return response.json();
})
.then(function (response) {
console.log(response);
});
} catch (e) {
console.log(e);
}
I'm getting an error back from the API itself (the CORS avoidance is working):
Content type 'text/plain;charset=UTF-8' not supported
And indeed, when I use the Chrome inspector to look at the request, the request header is set to "text/plain;charset=UTF-8". I tried setting the response header content type to "text/plain" but even that remains untouched. But how can this be after routing the request through my proxy?
EDIT:
Ok so I found out part of the problem. Setting the mode to "no-cors" in my fetch request alters the headers. But this still doesn't explain why my proxy can't edit the request headers. When I remove the "no-cors" mode but copy the headers it produced, the server is giving me error 400 (bad request). This means it is not receiving the same request as before, but this baffles me since I copied all the headers manually.
EDIT2:
Actually, I found out that when I remove mode: "no-cors" and set the "Sec-Fetch-Mode" header to "no-cors" manually, it is still set to "cors" in the actual request!
EDIT3:
I tried sending my request through ReqBin and it works there :)! So at least we know my request is correct.
I found out that changing the "content-type" header in cors mode is simply not allowed. The solution is to first send a preflight request with the options. When this has been accepted, you can send the actual request.
You can send the request through ReqBin, it will take the necessary steps to complete the request succesfully. It will even generate code to reproduce the request for you.
var url = "https://thedomain.com/path/to/api";
var xhr = new XMLHttpRequest();
xhr.open("POST", url);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
console.log(xhr.status);
console.log(xhr.responseText);
}
};
var data_ = '{"email": "*************", "password": "******"}';
xhr.send(data_);
And this works! :)
I have a Rails 7 API and React 18 front end, both deployed to Heroku on separate subdomains. Locally, the Rails app runs on localhost:3000 and the React app on localhost:3001. I need to set a CSRF cookie on the front end from the back end. I'm using the rack-cors gem on the Rails side to handle cross-origin requests. The CSRF cookie is set on the server side when the server gets a request from the front end to create an authenticated session. When the server verifies the authentication token (received by the front end from Google, it is supposed to set a CSRF-TOKEN cookie that will then be set in the browser and included by the front end in subsequent authenticated requests. I can see that the Set-Cookie header is being sent with the following value, but the cookie is not being set in Chrome in my dev environment (the site won't load in Firefox or Safari for other reasons, so I'm unable to test cookie settings in other browsers right now):
Set-Cookie: CSRF-TOKEN=c2Cn8OMs4IhgI5A4g1GC1XjG5hEc6RRW7dSPynxNbgsb0vsoWCr07yulWVzUwFYNP7dD8ARMps3pz5MMngKdog; path=/; secure; SameSite=None
My CORS initializer (/config/initializers/cors.rb) - configatron.client_origin is set to http://localhost:3001 in dev environments or https://sim.danascheider.com in prod):
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins configatron.client_origin
resource '*',
headers: :any,
methods: %i[get post put patch delete options head],
credentials: true
end
end
Rails.application.config.action_controller.forgery_protection_origin_check = false
The code that sets the cookie (this works based on inspecting the response in the dev tools):
cookies['CSRF-TOKEN'] = {
value: form_authenticity_token,
domain: :all,
same_site: :none,
secure: true
}
And the code that makes the request from the front end:
export const logInUser = token => {
const uri = `${backendBaseUri}/sessions`
const body = JSON.stringify({ token })
return(
fetch(uri, { method: 'POST', crossDomain: true, body, headers: { 'Content-Type': 'application/json' } })
.then(resp => {
if (resp.status === 401) throw new AuthorizationError()
return resp
})
)
}
I've looked in various other sources and all say to do what I'm already doing.
The cookie ended up being set when I added the credentials: 'include' option to my fetch request. I don't fully understand why this worked or was required on this request, but it did:
export const logInUser = token => {
const uri = `${backendBaseUri}/sessions`
const body = JSON.stringify({ token })
return(
// Replace crossDomain: true (not a real option for 'fetch') with credentials: 'include'
fetch(uri, { method: 'POST', credentials: 'include', body, headers: { 'Content-Type': 'application/json' } })
.then(resp => {
if (resp.status === 401) throw new AuthorizationError()
return resp
})
)
}
I am creating a React app with a Go server. I set the cookie on the login request's response with http.cookie, which sends it back as seen in the network tab. But the browser doesn't save it. Tried with Chrome and Firefox. What am I doing wrong?
// Cors handler
r.Use(cors.Handler(cors.Options{
AllowOriginFunc: AllowOriginFunc,
AllowedMethods: []string{"GET", "POST", "DELETE"},
AllowedHeaders: []string{"*"},
AllowCredentials: true,
}))
func AllowOriginFunc(r *http.Request, origin string) bool {
if origin == "http://localhost:3000" || origin == "http://127.0.0.1:3000" {
return true
}
return false
}
// End of Login route sending back the token
userDetails := types.User{Name: user.Name, Email: user.Email, Profile_Pic: user.Profile_Pic}
cookie := &http.Cookie{Name: "accessToken", Value: token, MaxAge: int(maxAge), Path: "/api", HttpOnly: true, SameSite: http.SameSiteLaxMode}
http.SetCookie(w, cookie)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(userDetails)
Edit: Screenshots of the network tab.
Response headers
Request headers
Anybody else who comes across a similar problem, and is using the Fetch API, try setting 'credentials: "include"' in your fetch request that is EXPECTING A COOKIE IN THE RESPONSE. The browser then set the cookie it got in the response.
I had the wrong assumption that the 'credentials' flag must be set for requests that occur after the cookie is received. Finally working. Can't believe I spent 12 hours on setting a cookie smh.
fetch(`${url}/login`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
credentials: "include", // This here
body: JSON.stringify({
email: userDetails.email,
password: userDetails.password,
}),
}).then((response) => { ...
please try to put you cookie in header filed:"Set-Cookie".
e.g:
w.Header().Set("Set-Cookie","cookieName=cookieValue")
Make sure the response header has this field, and check it in you browser devtools.
I'm building a SPA with Reactjs and laravel as API. I use axios to make API calls, some requests work without any issue, however on some pages when I make a request i'll receive a error that the the request is blocked by the CORS policy.
In laravel i'm using the spatie/laravel-cors package to add CORS headers so the preflight requests won't get blocked, by default it will allow any type of request from any origin.
Request:
componentDidMount() {
const url = BACKEND_URL+API+'/XXX/';
let headers = {
"Authorization": "Bearer " + token,
"Content-Type": "application/json",
"Accept": "application/json",
}
axios({
url: url,
method: "GET",
headers: headers,
credentials: 'same-origin',
})
.then(response => {
const data = response.data.data;
this.setState({
data: data,
loading: false
})
})
.catch(error => {
console.log(error);
});
}
Expected response ( from localhost )
{
campaign_id: XXX
category: []
form_entry: {id: XXX, form_id: XXX, lead_id: XXX, fields: Array(1)}
form_entry_id: XXX
id: XXX
landing__page_id: XXX
last_contacted: "XXX"
name: "XXX"
notes: XXX
status: [{…}]
tag: []
}
Error message:
Access to XMLHttpRequest at 'XXX.XXX' from origin 'XXX.XXX' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.
from localhost everything works fine with the same setup, the strange thing is that i'm only getting the errors once I put both front and backend online.
Anyone has a clue why this is happening?
After preflight (cors) request server change origin to * and chrome not display request (but i look response body).
Request headers
Chrome's error:
Access to fetch at 'http://localhost:6529/graphql' from origin 'http://localhost:3000' 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'.
I use express, cors, graphql, apollo on backend and react on frontend.
Cors configuration (backend):
app.use(cors({
origin: 'http://localhost:3000',
credentials: true,
maxAge: 86400,
optionsSuccessStatus: 200,
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'].join(','),
}));
Headers configuration (frontend)
const credentials = "include";
let client: ApolloClient<NormalizedCacheObject> | null = null;
export function createClient(cookie: any, ctx: any, store: any): ApolloClient<NormalizedCacheObject> {
storage.setItem("ctx", ctx);
client = new ApolloClient({
cache,
link: ApolloLink.from([
onError(({graphQLErrors, networkError}) => {
if (graphQLErrors) {
if (!SERVER) {
const redirectUrl = getRedirect(graphQLErrors);
if (redirectUrl) {
location.assign(redirectUrl);
}
}
graphQLErrors.map(({message, locations, path}) => {
console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
);
});
}
if (networkError) {
console.log(`[Network error]: ${networkError}`);
}
}),
new ReduxLink(store),
new BatchHttpLink({
credentials,
uri: GRAPHQL,
headers: cookie,
fetchOptions: {
withCredentials: true,
credentials,
},
}),
],
),
ssrMode: SERVER,
connectToDevTools: true,
});
return client;
}
How to solve a problem?
I just went through the problem myself and it was a nightmare. The reason it is not working is that the CORS header must have a . in it. http://localhost:3000 doesn't meet that qualification.
I solved this issue by going into my host's file (on a Mac: /etc/hosts) and redirecting a dummy domain such as api.myapp.local to 127.0.0.1 (localhost). Then I redirected my frontend to app.myapp.local. So now when the CORS request is made it is from http://app.myapp.local:3000 to http://api.myapp.local:3001 and it meets that requirement. You can call the domain whatever you would like.