I have a proxy set up for a third party service that at the moment looks like this:
app.use('/service', (req, res) => {
let url = `http://www.service.com/endpoint/${config.POSTCODER_KEY}${req.url}`
req.headers['Referer'] = 'my.domain.com'
console.log(req.headers['Referer'])
req.pipe(request(url)).pipe(res)
})
As you can see I am trying to add Referer header to the request and it seems to be working as console.log prints out 'my.domain.com' however request fails and the error I get back from the service is 403 unauthorised referring to Referer header. When I inspect network in inspector tools my referer is displayed as localhost.
I am testing this in Postman api client (https://www.getpostman.com) by setting Referer to my white listed domain and it works. I'm not sure why it uses localhost with express.
Piping streams together only transfers the data in those streams. Headers are not a part of that. When you req.pipe(request(url)) you're only writing the request body to the proxied request. If you want to set the headers used for the proxied request, you have to pass them to request, like:
req.pipe(request({ url: url, headers: req.headers })).pipe(res);
However, as noted in my answer to your previous question, you will also need to properly set the headers on res when the proxied response arrives.
Related
We've created one EXE file using the CPP language and create one API like http://localhost:5800/get-id/. when I open in browser return me the perfect output.
When I used fetch in HTML > script page, then getting No "Access-Control-Allow-Origin" header is present on the requested resource.
Code1:
fetch("http://localhost:5800/get-id/", {method: 'GET').then(function(response) {
console.log(response.text());
}).catch(function(error) {
console.log('Request failed', error)
});
After research, I've added the mode: no-cors error lost but getting an empty response.
Code2:
fetch("http://localhost:5800/get-id/", {method: 'GET', mode: 'no-cors'}).then(function(response) {
console.log(response.text());
}).catch(function(error) {
console.log('Request failed', error)
});
If I use code2 in any inspect console then getting an empty body but when I open http://localhost:5800/get-id/ in the browser and try to hit code2 in the console then getting the perfect parameter.
It means, localhost domain it's working fine but when it's fetched from any domain through my error.
What is the proper solution for it? In C/CPP language how can we allow cors?
Strange:
when I hit from console, it's show me empty
For same request I checked network tab, show 200 OK with proper response / preview data
CORS is a complex topic, I usually use CORS middleware to handle it in Node.JS in Express server (maybe the code will be useful to solve this).
It's goal is to allow API on domain api-domain to list web applications that can use it, for example your application is on webapp-domain domain.
When application calls fetch('http://api-domain/get-id/') to another domain it is referred to as cross-origin call.
All browser do CORS preflight call like this to check for allowance:
OPTIONS /get-id/
Access-Control-Request-Method: GET
Access-Control-Request-Headers: origin, x-requested-with
Origin: http://webapp-domain
(please note it's an OPTION request to the API, not GET)
And response should list webapp-domain as allowed (and specify which HTTP methods are allowed)
HTTP/1.1 204 No Content
Connection: keep-alive
Access-Control-Allow-Origin: http://webapp-domain
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
Access-Control-Max-Age: 86400
After the successful preflight call like this the browser will continue with fetch, for example, it will send GET request to http://api-domain/get-id/
P.S.
One of the ways to skip CORS is to set HTTP proxy in webapp-domain which will call api-domain on server-side and is not limited by CORS. See this answer for details.
Basically, I'm trying to get a username by id from Sequelize. The problem is that I am either stuck with a CORS problem or 500 Internal Server error depending on the response(status)
cors and 500
controller code
async getUserFromUserId (req, res) {
try {
// const user = await User.findByPk(req.body.id)
const id = req.body.id
const user = await User.findByPk(id)
res.send(user.username)
} catch (err) {
// or res.status(some random number).send() for CORS problem to appear
res.status(500).send({
error: 'an error has occured trying to fetch the users id'
})
}
},
client code
this.notifiedUser = (await UserService.getUserFromUserId({id: UserId})).data
I get a Status: 200 OK from postman though.
Postman Solution
Edit:
I have seen how the other Solution for the cors thingy, but the solutions does not specify as to why I get "undefined" results after resolving the cors problem.
So, CORS is actually really obnoxious in this regard, but there's a fairly straightforward way to fix this. It's a super useful security feature, though it is frustrating at best sometimes.
Your browser does what is called a Preflight Request, which is of the http verb OPTIONS. Your browser calls whatever route you want, but instead of what you asked it to do, it calls using OPTIONS first. Your server should accept all routes that the client can ask for with the OPTIONS method, and your server should respond with the following headers to be an externally available, cross-origin API.
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, ...
(note, you should not put the ... in, but you can put any HTTP verb in this list)
If you require your own headers (for auth purposes), you want to add this header for Client -> Server.
Access-Control-Allow-Headers: YourHeader, YourHeader2, YourHeader3
You want to add this one for Server -> Client
Access-Control-Expose-Headers: YourHeader,YourHeader3
Note that the OPTIONS call is an entirely separate call that you should handle as well as the GET method.
You've now told the browser what it is allowed to ask for, and what it can expect to get back from your API. If you don't respond to the OPTIONS request, the browser terminates the request, resulting in a CORS error.
I'm going to take a gander at a guess and assume you're likely using Express, which this answer describes how to set the headers on.
What do the headers mean, in English?
Access-Control-Allow-Origin
From where are clients allowed to access this resource (endpoint)? This can match partial domains with wildcards, or just a * to allow anywhere.
Access-Control-Allow-Methods
What HTTP methods are permissible on this route?
Access-Control-Expose-Headers
When I get a response from the server, what should I (the browser) expose to the client-side?
Access-Control-Allow-Headers
What am I as the client side allowed to send as headers?
Okay, so I figured out the problem.
In a way, I did not have to deal with any of the cors stuff because I believe that was not the main source of the problem.
So, instead of accessing my database data through "GET" and getting the data by doing this:
this.data = (Service.function(bodyValue)).data
I did "POST" to get the data, and accessed the data by simply doing this
const response = Service.function({
id: bodyValue
})
this.data = response.data
This accesses the data without having to get "secured" information from the database, but by accessing the data from the database by getting Observer object info from the database.
The Observer object looks as follows, which treats the user data as an object instead of pure data.
Compared to a data object, where each data {...} has user information.
I am not sure if I am using the correct words, but these are to the extent of my current understanding.
If your origin is from localhost, Chrome usually blocks any CORS request originating from this origin.
You can install this extension:
https://chrome.google.com/webstore/detail/allow-cors-access-control/lhobafahddgcelffkeicbaginigeejlf?hl=en
Or you can disable the security when running chrome (add the flag):
--disable-web-security
I'm having trouble understanding Apollo Client library as it does not work as intended. Instead of sending the GET HTTP method, it sends the OPTIONS HTTP method even though I've put to use GET only when retrieving data from GraphQL server.
const client = new ApolloClient({
link: ApolloLink.from([
new MeteorAccountsLink(),
new HttpLink({
uri: 'https://selo-comments.herokuapp.com/graphql',
useGETForQueries: true
})
]),
cache: new InMemoryCache()
});
Console log from the browser:
OPTIONS https://selo-comments.herokuapp.com/graphql?query=%7B%0A%20%20comments(id%3A%20%22TFpQmhrDxQqHk2ryy%22)%20%7B%0A%20%20%20%20articleID%0A%20%20%20%20content%0A%20%20%20%20userId%0A%20%20%20%20createdAt%0A%20%20%20%20commentID%0A%20%20%20%20votes%0A%20%20%20%20blockedUsers%0A%20%20%20%20__typename%0A%20%20%7D%0A%7D%0A&variables=%7B%7D 405 (Method Not Allowed)
Which obviously means that the HTTP method is incorrect even if it has the query parameter in the url. If you query that url using Postman or simply navigating to the url using browser's address bar, you will get GraphQL data. I have to use https://cors-anywhere.herokuapp.com/ in order to execute the query successfully.
What am I doing wrong?
The options request is probably a preflight request for CORS.
A CORS preflight request is a CORS request that checks to see if the CORS protocol is understood.
It is an OPTIONS request, using three HTTP request headers: Access-Control-Request-Method, Access-Control-Request-Headers, and the Origin header.
You probably need to configure your server to allow cross origin calls.
Maybe you can find some inspiration here to get u started. Allow CORS REST request to a Express/Node.js application on Heroku
I can't execute the 'GET' request with the getTasks() function.
$(document).ready(function(){
getTasks();
});
const apiKey = 'xxxxxxx';
function getTasks(){
$.ajax({
type: 'GET',
url: 'https://api.mlab.com/api/1/databases/taskmanager/collections/tasks?apiKey='+apiKey,
contentType: 'application/json',
xhrFields: {
withCredentials: true
},
success: function(data){
console.log(data);
},
error: function(){
console.log('FAIL')
}
})
}
The error that I get is:
api.mlab.com/api/1/databases/taskmanager/collections/tasks?apiKey=xxxxxxx
Failed to load resource: the server responded with a status of 400
(Bad Request)
Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'null' is therefore not allowed access. The response
had HTTP status code 400.
I understand that Google-Chrome on Windows is CORS enabled, and will not (by default) allow communication with a different domain. I'm not sure what a preflight request is. Regardless, I tried to implement what I saw from Using CORS - HTML5 Rocks (from the CORS from jQuery section), but to no avail.
At a guess, the remote API simply does not respond to pre-flight requests for GET calls (because it shouldn't have to).
Your code is triggering a pre-flight request because it is non-simple. This is due to your adding a Content-type: application/json header. A request Content-type header is used to indicate the request payload format. As it is a GET, there is no payload.
Try this instead...
$.getJSON('https://api.mlab.com/api/1/databases/taskmanager/collections/tasks', {
apiKey: apiKey
}).done(function(data) {
console.log(data)
}).fail(function() {
console.log('FAIL')
})
CORS is there to protect you. If you want some more info on it, wikipedia has a good entry on it.
It appears the issue here is that you're trying to access your mongodb hosted by mlab directly from your web app. As you can see in your code, you're providing credentials/api keys to make that request.
My guess is that mlab's intent of not allowing CORS is to prevent you from doing this. You should never put your private API keys in html to be hosted on a web page, as it's easily accessible by reading source code. Then someone would have direct access to your mongodb.
Instead, you should create a server-side application (node, or... ** Whatever **) that exposes an api you control on the same domain (or a domain you give permission to via CORS).
As far as the "preflight" request, if you look in your chrome debugging tools, you should see an additional request go out with the "OPTIONS" method. This is the request that chrome (and most other http clients) send out first to a server hosted on a different domain. it's looking for the Access-Control-Allow-Origin header to find out whether it's allowed to make the request. Pretty interesting stuff if you ever have some time to dig into it.
I am trying to get data from API that has oAuth authentication.
What I am doing is sending the auth request from server1 (where my Angular app is) to server2 and I get the access token. Then I put the token in the JS variable and I am trying to access an API endpoint.
My js code looks like this:
let headers = new Headers({ 'Authorization': "Bearer " + oAuthAccessToken });
let options = new RequestOptions({ headers: headers });
let url = "http://example.com/api/getSomething"
this.http.post(url, body, options)
.subscribe( res => {
console.log(res.json())
});
The problem is that I am always getting "401 Unathorized". When I inspect the request in the Network tab of Chrome Dev Tools I see two strange things - first the request method is OPTIONS not POST and the header Authorization is missing.
Any idea what I might be doing wrong ? Why is the header not set ?
Edit:
The problem was that Angular sends OPTIONS request before the POST and my app firewall was expecting Authorization header to be always present. This header is not present in the OPTIONS request so I was getting Unauthorized. I changed my server app to send proper headers on OPTIONS request and now everything is fine.
Thanks for the help.
I think the browser try to discover which http methods are allowed, so the first request is an request with the OPTIONS method. Usually the backend service answers with Access-Control-Allow-Methods inside the header. Afterwards the browser sends the real request.
I think that you need to allow CORS, then it should work as expected
As you are dealing with cross-domain requests, Chrome is preflighting the request to look for CORS headers. If the request is acceptable, it will then send the real request. so the option request is just to check is the server support CORS.
From : https://stackoverflow.com/a/21783145/3279156
Content-Type should be like below:
let header= new Headers({'Content-type':'application/x-www-form-urlencode'});
header.append('Authorization',"Bearer " + token);
let opt= new RequestOptions({headers:header});