Unable to send http post with credentials to iis node server - node.js

I'm trying to send a http post request from my angular app to my node.js server with user credentials in order to log it.
Both are hosted in IIS (the node server with nodeiis) and both are configured to be authenticated by windows authentication.
My angular code:
var url = "http://myurl:15001/addItem";
this.http.post(url, {
"itemName": "SomeName",
"itemColor": "SomeColor"
}, {withCredentials: true}).subscribe(res =>{
console.log("Great, item was added");
})
My Node.js Code:
var app = express();
var allowCrossDomain = function (req, res, next){
res.header('Access-Control-Allow-Origin', "http://myurl") //Cannot be a wildcard because of the credentials.
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
if(res.method == 'OPTIONS')
res.send(200);
else
next();
};
app.use(allowCrossDomain);
app.post('/addItem', function(req, res){
//Saves the item...
res.setHeader('Access-Control-Allow-Origin', 'http://myurl')
res.status(200);
res.send(true);
});
When I do the request I get the following error to the console:
OPTIONS http://myurl:15001/addItem 401(Unauthorized)
XMLHttpRequest cannot load http://myurl:15001/addItem. Response to
preflight doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://myurl' is therefore bot allowed access. The
response had HTTP status code 401.
When I try to do the same thing with my http get request everything works properly and I get the result.
I don't know why my OPTIONS request is unauthorized when I send 200 code for every OPTIONS request.
I tried to use cors node.js package but it didn't help, maybe I didn't used that right.
Could someone explain me how can I solve this and make my http post pass the preflight? Thanks a lot!

If you are using Angular 6, it supports proxy.conf which will proxy the Backend API URL. For more details, here is the link https://github.com/angular/angular-cli/blob/master/docs/documentation/stories/proxy.md

Related

The same node.js backend service, behaves different for the same request from different clients

There is a backend service which is resides on azure portal and developed with node.js by using Express.js
Requests are sending from an azure devops extension.
Most of the user's requests are successful but just a few users stuck on cors errors.
The cors error is
Access to fetch at ..... from origin .... has been blocked by CORS
policy: No 'Access-Control-Allow-Origin' header is present on the
request resource. If an opaque response serves your needs, set the
request's mode to 'no-cors' to fetch the resource wiht CORS disabled.
A part of node.js server code (probably there are unnecessary parts in it)
var cors = require('cors')
app.use(cors())
app.use(function (req, res, next) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS');
res.setHeader('Access-Control-Allow-Headers', '*');
res.setHeader("Content-Type", "application/json");
res.setHeader("Content-Type", "text/html");
next();
});
Successful request headers
Failed request headers
A get method example from client code
export function getTheValue() {
return fetch('https://....../.....')
.then(response => {
return response.text();
})
.catch(error => {
return reject(error);
})
}

Why is the postman post request sent to my Sails.js server hosted on Heroku returning a cors preflight error when the headers have been set?

I've been trying to send a 200 status from my Sails.js server in response to a demo POST webhook request from IAPhub, an in app purchase API, (which is necessary to start testing it with my Android app) but it wasn't working, so I tried to test it using postman.
This is the controller method:
getInAppPurchase: async function (req, res) {
req.header('Access-Control-Allow-Origin', '*')
req.header('Access-Control-Allow-Methods', 'OPTIONS,GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'OPTIONS,GET,PUT,POST,DELETE');
if (req.method == "OPTIONS") {
res.send("success")
} else {
// res.header('Access-Control-Allow-Headers', 'Content-Type');
res.send('Success 1')
res.end()
}
}
This is the route:
'POST /inapppurchases/getInAppPurchase' : 'InAppPurchaseController.getInAppPurchase'
This is my CORS security configuration in config/security.js:
cors: {
allRoutes: true,
allowOrigins: '*',
allowCredentials: false
}
This is my app url that the demo webhook is being sent to in postman (the number in the URL is different from the original for security purposes. (cors config in config/security.js is open to all routes so my server is not that secure)
http://mighty-app-46232519.herokuapp.com/inapppurchases/getInAppPurchase
I'm only a junior level developer so most of this stuff is new to me, but I've tried a lot of different things over the last few hours and nothing is working. I'm getting the error message as follows:
XHttpXMLRequest from origin 'https://meeting-appppp.postman.co' 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.
I've tried the following things:
Add
header('Access-Control-Allow-Origin', '*');
header('Access-Control-Allow-Methods', 'OPTIONS,GET,PUT,POST,DELETE');
to both req and res objects
Check if method is the pre-flight Options request
if (req.method == "OPTIONS") {
Sending the 200 status code back to the webhook origin which is the goal in the first place ("send" automatically sends the 200 status code as I understand it)
res.send("success")
Thanks so much for your help.
Try setting your route like this: POST /inapppurchases/getInAppPurchase': { action: 'InAppPurchaseController/getInAppPurchase', csrf: false }

No 'Access-Control-Allow-Origin' is present, Origin 'null' not allowed access - Passport-SteamStrategy, Node

I have a site that is using Passport Steam Strategy. My server (Node with Express) is currently running on localhost:3000 while my front end is running on localhost:8080. I keep running into a cross-origin issue, only when attempting to authorize through Steam. My requests are made through Axios and I am using CORS. I've spent hours Googling and trying various things but I can't seem to get it to work.
This is the error that I get:
XMLHttpRequest cannot load
https://steamcommunity.com/openid/login?openid.mode=checkid_setup&openid.ns…3000%2Fsteam%2Fauth%2Freturn&openid.realm=http%3A%2F%2Flocalhost%3A3000%2F.
No 'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'null' is therefore not allowed access.
Here is where the route is called from the front end:
linkSteam(){
api('http://localhost:3000/steam/auth')
.then(res => {
console.log(res);
state.user = res.data.user;
})
}
And here is my axios configuration on the front end.
let api = axios.create({
baseURL: 'http://localhost:3000/api/',
timeout: 3000,
withCredentials: true
})
My CORS is set up as follows
var whitelist = ['http://localhost:8080', 'https://steamcommunity.com', 'http://localhost:3000', 'null'];
// I added null here as someone said that it worked as their origin displayed null like mine does
var corsOptions = {
origin: function (origin, callback) {
var originIsWhitelisted = whitelist.indexOf(origin) !== -1;
callback(null, originIsWhitelisted);
},
credentials: true
};
I then import my CORS into my main file and use it as follows
app.use(cors(corsOptions))
app.use('*', cors(corsOptions))
I kept seeing that you can set your Access-Control-Allow-Credentials, etc and did so
app.use(function (req, res, next) {
res.setHeader('Access-Control-Allow-Credentials', true);
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8080');
res.setHeader('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept');
if ('OPTIONS' == req.method) {
res.send(200);
} else {
next();
}
});
This didn't help. I've tried breaking down my original function where /steam/auth is called and running a console.log(req.headers.origin) and spits back my correct URL, but then the error still reports that the Origin is null. If I set my Access-Control-Allow-Origin' to '*', I get a slightly different error that I must provide credentials, and that the Origin 'localhost:8080' is therefore not allowed.
Here's my /steam/auth route.
router.get('/steam/auth',
passport.authenticate('steam', {
failureRedirect: '/'
}),
function (req, res) {
res.redirect('/');
});
And this does work if I click on the URL that cannot be loaded and then login to Steam. It will then redirect me back to my site with my Steam profile information intact. The error is on attempting to get the initial redirect back to Steam in order to login. I have also tried doing this in a res.redirect to their direct URL and I get the same error.
Thanks in advance for any help! And let me know if I'm missing vital information. I've tried to include everything, but I wouldn't be surprise if I missed something.
If the error message in the question is the actual message the browser is reporting, it’s because your client-side code is trying to send a request to https://steamcommunity.com/openid/login.
So given that, no matter what config changes you make to your Node server on localhost:3000, that’s not going to change anything. Instead it seems like you you need to figure out where your client-side code is sending a request to https://steamcommunity.com/openid/login and why.
And the reason the request to https://steamcommunity.com/openid/login fails is that server doesn’t include the Access-Control-Allow-Origin response header in its response.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS has more details.
So because you can’t change the server’s behaviour, your browser will continue so block your code from getting at the response as long as it lacks the Access-Control-Allow-Origin header.
I don’t know how authorization is handled by Steam but maybe you instead need to either make the authorization request to Steam through your existing Node backend somehow, or else set up your own CORS proxy using, e.g., https://github.com/Rob--W/cors-anywhere/, and proxy your client-side authorization request to Steam through it.

Can't send POST from angular.js to express.js

I having problems to communicate between my angular.js APP and my express.js REST.
I'm using yeoman 1.0 with generator-angular 0.7.1.
I tried to use a middleware config for my grunt serve but i did not get it working.
Angular App (port: 9000):
angular.module('wboxApp')
.controller('AdminCtrl', function ($scope, $routeParams, $http, fbRef) {
var ref = fbRef();
var token = $routeParams.token;
$http.post('http://127.0.0.1:3000/box/token/get', {token: token}).success(function (data) {
console.log(data);
}
});
});
Express API (port: 3000):
app.post('/box/token/get', function (req, res) {
res.header('Access-Control-Allow-Origin', req.headers.origin || "*");
res.header('Access-Control-Allow-Methods', 'GET,POST,PUT,HEAD,DELETE,OPTIONS');
res.header('Access-Control-Allow-Headers', 'content-Type,x-requested-with');
var token = req.body.token;
var tokenRef = ref.child('tokens').child(token);
tokenRef.once('value', function (data) {
var fullToken = data.val();
fullToken = fullToken + '.' + token;
if (data.val()) {
res.json({fullToken: fullToken});
} else {
res.json({fullToken: null});
}
});
});
Browser Error:
OPTIONS http://127.0.0.1:3000/box/token/get No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:9000' is therefore not allowed access.
XMLHttpRequest cannot load http://127.0.0.1:3000/box/token/get. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:9000' is therefore not allowed access.
It seems the angular page was being served by server running on 127.0.0.1:9000. Cross origins policy disallows ajax requests from other domains. To get around it you can add express middleware which adds the headers for cross origin requests -
app.all('/*', function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Content-Type,X-Requested-With');
next();
});
Oh dear, do not perform cross-origin requests!
There's an better way: grunt-connect-proxy - Issue requests to 9000 port and have your frontend server forward them to actual backend.
here's how to configure it in less than 3 minutes: https://stackoverflow.com/a/21655532/1432478

AngularJS $resource makes HTTP OPTIONS request instead of HTTP POST for $save method

I'm in the process of writing a simple library application to get ready for a larger project with AngularJS. After reading a lot online about using $resource to interact with a RESTful API, I decided that it would probably offer some time-saving and scaling benefits to implement it instead of using $http for each request. The problem is that for some reason (I'm no expert on CORS and the request is being sent cross-domain) when using the $save method my Node.js console shows:
OPTIONS /books 200 1ms - 161b
Using the query() method works fine - the Node console shows:
GET /books 200 1ms - 228b
I've been stuck for several hours at this point, trying variations on the below but it always ends up being an OPTIONS request instead of POST (which is what it should be according to the Angular documentation) for the $save method.
AngularJS Web App
app.js
var libraryApp = angular.module('libraryApp', ['ngResource', 'ngRoute', 'libraryControllers']);
libraryApp.factory('$book', ['$resource', function ($resource) {
return $resource('http://mywebserver\\:1337/books/:bookId', { bookId: '#bookId' });
}]);
controllers.js
var libraryControllers = angular.module('libraryControllers', []);
libraryControllers.controller('BookCtrl', ['$scope', '$book', function($scope, $book) {
...
$scope.addBook = function () {
var b = new $book;
b.isbn = "TEST";
b.description = "TEST";
b.price = 9.99;
b.$save();
};
}]);
Node.js with Express REST API
app.js
var express = require('express'),
books = require('./routes/books'),
http = require('http'),
path = require('path');
var app = express();
...
// enable cross-domain scripting
app.all('*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", req.headers.origin);
res.header("Access-Control-Allow-Headers", "X-Requested-With");
next();
});
// routing
app.get('/books', books.getAll);
app.get('/books/:isbn', books.get);
// This is what I want to fire with the $save method
app.post('/books', books.add);
http.createServer(app).listen(app.get('port'), function(){
console.log('Express server listening on port ' + app.get('port'));
});
./routes/books.js
...
exports.add = function(req, res) {
console.log("POST request received...");
console.log(req.body.isbn);
};
Tried putting this line in my config function delete $httpProvider.defaults.headers.common["X-Requested-With"]; but no change.
I'm no Angular/Node pro but right now I'm thinking that it's something to do with it being cross domain and, like I said, I'm no expert on CORS.
Thanks in advance.
I know it may be in bad taste to answer my own question but I figured out the problem a few days after posting this.
It all comes down to how browsers manage CORS. When making a cross-domain request in JavaScript that is not "simple" (i.e. a GET request - which explains why the query() function worked), the browser will automatically make a HTTP OPTIONS request to the specified URL/URI, called a "pre-flight" request or "promise". As long as the remote source returns a HTTP status code of 200 and relevant details about what it will accept in the response headers, then the browser will go ahead with the original JavaScript call.
Here's a brief jQuery example:
function makeRequest() {
// browser makes HTTP OPTIONS request to www.myotherwebsite.com/api/test
// and if it receives a HTTP status code of 200 and relevant details about
// what it will accept in HTTP headers, then it will make this POST request...
$.post( "www.myotherwebsite.com/api/test", function(data) {
alert(data);
});
// ...if not then it won't - it's that simple.
}
All I had to do was add the details of what the server will accept in the response headers:
// apply this rule to all requests accessing any URL/URI
app.all('*', function(req, res, next) {
// add details of what is allowed in HTTP request headers to the response headers
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Methods', 'POST, GET, PUT, DELETE, OPTIONS');
res.header('Access-Control-Allow-Credentials', false);
res.header('Access-Control-Max-Age', '86400');
res.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept');
// the next() function continues execution and will move onto the requested URL/URI
next();
});
And then insert these few lines before the Express routing to simply return a HTTP 200 status code for every OPTIONS request:
// fulfils pre-flight/promise request
app.options('*', function(req, res) {
res.send(200);
});
Hopefully this will help anyone who stumbles on this page suffering from the same problem.
I didn´t actually try this, but wouldn´t it be enough to tell the Ressource how to handle the $save request?
$resource('http://mywebserver\\:1337/books/:bookId', { bookId: '#bookId' }, {save: {method: 'POST'});

Resources