403 forbidden Rest API (node js) in Angular js1 - angularjs

I am calling nodeJS REST API running on different localhost into an angular app,
in postman I get the expected result. but in angular application it is falling due to 403 error, It is happening because of preflight( Response for preflight has invalid HTTP status code 403). I tried to remove some default headers using $httpprovider and also tried to use proper server name instead of wildcard(*) in access-control-allow-region nothing worked.Looking for help!
There is no Authentication for preflight requests.
Posting my req-respo snap

By handling OPTIONS request at server side i am able to solve the problem,
This post solved my problem
By adding following line of code
app.use(function (req,res,next){
if (req.method === 'OPTIONS') {
console.log('!OPTIONS');
var headers = {};
// IE8 does not allow domains to be specified, just the *
// headers["Access-Control-Allow-Origin"] = req.headers.origin;
headers["Access-Control-Allow-Origin"] = "*";
headers["Access-Control-Allow-Methods"] = "POST, GET, PUT, DELETE, OPTIONS";
headers["Access-Control-Allow-Credentials"] = false;
headers["Access-Control-Max-Age"] = '86400'; // 24 hours
headers["Access-Control-Allow-Headers"] = "X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept";
res.writeHead(200, headers);
res.end();
} else {
//...
other requests
}
});

Related

Where should I write cors filter enabler code in angularjs

This is my js file, the following code is inside .controller
xhr.onreadystatechange = function () {
if(xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200){
$http.post(smsHorizon,'Access-Control-Allow-Origin', '*').then(function(res){
res.addheader('Access-Control-Allow-Origin', '*');
res.addheader('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
res.addheader('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With');
if(res){
alert("OTP has been Send");
}
})
}
else{
}
}
Is this the way to code cors? Please help!!!
CORS request enabling always-on server side. You need to set CORS on response header.
CORS- How Internally Work
A resource makes a cross-origin HTTP request when it requests a resource from a domain or port which is different from the one which the first resource itself serves. For security reasons, browsers restrict cross-origin HTTP requests initiated from within scripts.
For example:
your app running on www.my-domain.com and its request for the resource on www.your-domain.com than browser do not allow to make the request within a script.
CORS- How To Resolve
suppose we made a request through angular script under domain www.my-domain.com is www.your-domain.com\id. This request hit on the server at endpoint /id on www.your-domain.com. So, at a time of rendering/sending a response by the server, it set 'Access-Control-Allow-Origin', '*' header in the response.
//java-jersey example
Response.status(200).entity(data)
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "GET")
.build();
//Nodejs Example
var express = require('express')
var cors = require('cors')
var app = express()
app.use(cors())
app.get('/products/:id', function (req, res, next) {
res.json({msg: 'This is CORS-enabled for all origins!'})
})
For more details : https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

Angular js don't send custom header while preflight request is made

I have following code
angular.module('myApp.Utils',[])
.factory('AuthInterceptor', function($q, $location) {
return {
request: function(config) {
config.headers = config.headers || {};
if (window.localStorage.token) {
config.headers.Authorization = 'Bearer ' + window.localStorage.token;
}
config.headers["Content-Type"] = 'application/x-www-form-urlencoded';
return config || $q.when(config);
},
response: function(response) {
if (response.status === 401) {
//redirect user to login page
$location.url('/login');
}
return response || $q.when(response);
}
};
})
.config(function($httpProvider) {
$httpProvider.interceptors.push('AuthInterceptor');
});
If we have value for token key in localStorage, it adds Authorization Header to every ajax calls made.
Problem
It works well if there is no token (No header sent). But when token exist in localstorage and I make an API call, I get following error
Response for preflight has invalid HTTP status code 404
After research i found that, the browser makes preflight request (OPTIONS request), before making a real request.
I also found that we cannot send custom Headers in preflight request. So is there anyway to fix this, to send custom Headers only when making a real request.?
I have tried adding following in .htaccess of the server that serves API calls
<IfModule mod_headers.c>
Header set Access-Control-Allow-Origin "*"
Header add Access-Control-Allow-Headers "Content-Type, Authorization, Origin, X-Requested-With"
Header always set Access-Control-Allow-Methods "GET,POST,HEAD,DELETE,PUT,OPTIONS"
</IfModule>
I have found answer myself.
Its not related to angular or .htaccess thing. Its more related to the server side.
When CORS request is made, the browser makes a test(preflight) request to see if everything is ok, then only it makes actual request.
Following code in the API call solved the problem.
<?php
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Headers: X-Requested-With, Authorization');
exit;
}

AngularJS POST request with JSON array data to Express server

I have a static AngularJS website and an Express server running on different domains. The site sends GET requests to the server with a file name as the parameter. The server then requests the file from an S3 bucket, that sends it with "binary/octet-stream" as the "Content-Type". It decrypts the data and sends it back to the site with the same "Content-Type", which then downloads the file. This all works well for single file requests, however I would like to be able to send the server an array of file names all at once, and then be able to download multiple files. I tried sending the file names as a JSON array but I get the error:
XMLHttpRequest cannot load http://localhost:3000/decrypt. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'file://' is therefore not allowed access.
In my server console, the request shows up as OPTIONS instead of POST. I've also made sure to include Access-Control-Allow-Origin: * in my response headers. How should I go about resolving this?
UPDATE:
I was able to resolve the CORS error by adding the following middleware on my router:
function allowCrossDomain(req, res, next) {
if ('OPTIONS' == req.method) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,PATCH,OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With');
res.status(200).end();
}
else {
next();
}
}
However, I'm still not sure how to send multiple (for each file) "binary/octet-stream" in the response, and download them as files on the static site. Currently I am using angular-file-saver to save files from single requests.
You need to do a couple things in your server. First off are you using multer with bodyParser? Multer will allow you to add in post calls and will handle passing the data for you.
First AngularJS Post:
$http.post(baseUrl + "mypostmethod", o) //o is your object
.then(function successCallback(resp) {
console.log(resp)
}, function errorCallback(resp) {
console.log(resp)
});
Now as for your nodejs express setup, you want to ensure you are using all the proper modules. I will provide the basic list that I use in most of my projects. Also if you use req.headers.origin instead of * you sould no longer get the Access-Control-Allow-Origin error.
NodeJS:
var express = require('express'),
fs = require('fs'),
spdy = require('spdy'),
bodyParser = require('body-parser'),
multer = require('multer'),
helmet = require('helmet'),
upload = multer(), // this will allow you to pass your object into your post call with express
path = require('path'),
cookieParser = require('cookie-parser'),
request = require('request'),
app = express(),
http = require('http'),
formidable = require('formidable'); //Good for handling file uploads
app.use(helmet());
app.use(cookieParser());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,PATCH');
res.header('Access-Control-Allow-Headers', 'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version');
next();
});
These are the basic ones I use in almost all my programs. The most important with expressis bodyParser and multer since these will allow you to use your gets and post properly.
Here is an example of a post NodeJS:
app.post('/mypostmethod', upload.array(), function(req, res) {
var body = req.body,
sess = req.session;
res.send({
Status: "Success"
});
});
In this post when you use upload.array() that is utilizing multer and now req.body is the object you passed in with your angular post call.
Let me know if you have any question, I hope this helps.

CORS error with AngularJS post only in FireFox

I am having issues only with FireFox when making a cross origin request in an Ionic app (so it's using AngularJS). The js app is doing a $http.post() to a Laravel 5.1 API, and I get the following error only in FireFox (39.0.3):
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://blah.dev/endpoint. (Reason: missing token 'content-type' in CORS header 'Access-Control-Allow-Headers' from CORS preflight channel).
This is on my localhost (OSX Mavericks). and works fine in Chrome and Safari, only FireFox is given the error. I have cleared the cache, and tried in a "private window", with the same results.
Here is the Laravel route defined to handle the OPTIONS request:
Route::options('endpoint', function() {
return response('OK')
->header('Access-Control-Allow-Headers ', 'Origin, Authorization, X-Requested-With, Content-Type, Accept')
->header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
->header('Access-Control-Allow-Origin', '*')
->header('Cache-Control', 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0')
;
});
Route::post('endpoint', array('uses' => '\Path\To\Controller#endpoint'));
I did find this question that seems to have a similar issue only in FireFox, but the solution was to comma separate the Allow-Methods, which I already am.
The message " missing token 'content-type' " is about the client. For a http request to be a valid post one, it must have a header
'Content-Type': 'application/x-www-form-urlencoded'
or
'Content-Type': 'multipart/form-data'
The first content-type is the most common one.
In angularjs one way to do a CORS post request is
$http.post(
'http://external-domain.ext/the/rest/url',
'param_name=param_value',
{headers:{'Content-Type': 'application/x-www-form-urlencoded'}}
).
then(function successCallback(response) {
$scope.something = response;
}, function errorCallback(response) {
// not good
});
If the server is configured with
header('Access-Control-Allow-Origin', '*'), the CORS post must succeed without authentication and credentials.
on the back end try putting
->header('Access-Control-Allow-Credentials', 'true')
and on the front end try putting
.config(function($locationProvider, $httpProvider) {
// CORS requests with credentials
$httpProvider.defaults.withCredentials = true;
})

AngularJS $http GET method to backend server: Request Method:OPTIONS 405

$http GET request to a clojure backend, to get a list of services.
I get is an OPTIONS request (???), which gets a 405 response...
<code>
var config = {headers: {
'Authorization': 'Bearer d2VudHdvYW5nZV9tZQ',
"X-Testing" : "testing"
}
};
$http.get(SERVER.MESSAGE_SERVICES, config)
.success(function(successCallback) {
$scope.services = successCallback;
})
.error(function(errorCallback) {
console.log(errorCallback.toString);
}).
finally(function() {
console.log("Message services rest call");
});
</code>
**clojure backend**:
<code>
headers {"Access-Control-Allow-Origin" "*"
"Access-Control-Allow-Headers" "X-Requested-With, Origin,Content-Type, Accept"
"Access-Control-Allow-Methods" "GET, POST, OPTIONS"}
</code>
There is no problem that AngularJS sends an OPTIONS request, that is because CORS standards force to do so. Be sure that the server is configured to allow a GET method.
Yes as raso suggested this problem is because of Cross Origin Resource Sharing(CORS). The same old privacy policy prevents JS/ angularJS from making requests across domain boundaries.
Configure server to allow cross domain requests.
Or
If you are using Chrome than you can use this extension to surpass this problem.

Resources