Cross origin for POST - post

I have a Jetty http server with some Jersey rest services. Those services are called from a React website that runs on a Node server.
Due to the cross origin nature of this setup, I had to add some HTTP headers. Basically, all my webservices return a createOkResult() which is created as follows.
#POST
#Path("orders/quickfilter")
#Consumes(MediaType.APPLICATION_JSON)
public Response getQuickFilterProductionOrders(String data)
{
...
return createOkResult(json.toString());
}
protected Response createOkResult(Object result)
{
return buildCrossOrigin(Response.ok().entity(result));
}
protected static Response buildCrossOrigin(Response.ResponseBuilder responseBuilder)
{
return responseBuilder.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT")
.allow("OPTIONS")
.build();
}
For the #GET webservices that works fine. But when I create an #POST service, I just can't get it working.
Webbrowsers (chrome and firefox) return these kind of errors:
Access to XMLHttpRequest at 'http://localhost:59187/rs/production/orders/quickfilter' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
So, at first sight I would be tempted to think that the headers are still missing. The thing is, when I test this service with a tool like Postman, then all headers turn out to be in place, and the service even returns the requested data.
This is a screenshot of a POST request.
From my front-end (which runs on the node server), I use the axios API, which uses promises, and my request looks like this:
const url = "http://localhost:59187/rs/production/orders/quickfilter";
const data = JSON.stringify(request);
const headers = { headers: { "Content-Type": "application/json" } };
const promise = axios.post(url, data, headers);
Right now I have a HTTP error 500, If I remove the content type header, I get an unsupported media exception. So, I have reasons to believe that the content type is ok.

Paul Samsotha pointed me in the right direction.
I ended up adding a filter to the ServletContextHandler. Unlike the linked article, I didn't really have to create that filter from scratch. There was an existing filter class that I could use: i.e. org.eclipse.jetty.servlets.CrossOriginFilter.
FilterHolder filterHolder = context.addFilter(CrossOriginFilter.class, "/*", EnumSet.allOf(DispatcherType.class));
filterHolder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET,PUT,POST,DELETE,OPTIONS");
filterHolder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
filterHolder.setInitParameter(CrossOriginFilter.ALLOWED_HEADERS_PARAM, "Content-Type,Authorization,X-Requested-With,Content-Length,Accept,Origin");
filterHolder.setInitParameter(CrossOriginFilter.ALLOW_CREDENTIALS_PARAM, "true");
filterHolder.setInitParameter(CrossOriginFilter.CHAIN_PREFLIGHT_PARAM, "false");
Some of the above parameters can probably be left out, as they are default values. But what appeared to be crucial for me, was to set the CHAIN_PREFLIGHT_PARAM to false.
One nice side-effect, is that I can simplify the code of the actual services. They do not longer need to add special headers, by contrast they can now just return Response.ok().entity(result).build();.

Related

How to allow to make my REST API end point be called via ajax as well as http libraries?

I have a main REST web app where I have an endpoint: POST /api/v1/my_endpoint
I want to allow it to be called:
1) via a browser via ajax from my other web apps on different domains
2) from a server side via HTTP client library.
In the case of ajax call - #1 - I'll have to include "Allow-Control-Allow-Origin" and the similar headers to my response to let a browser receive a response.
In the case #2 - I won't need to include those headers.
However, there's no reliable way to distinguish between #1 and #2.
How can I solve that?
On the server, you check for the presence of an Origin header. This header is sent by the browser as part of the CORS protocol. To explain how it works, below is a filter (in other frameworks, this is also known as middleware) used by a Jersey application. Jersey is a Java REST framework. Sorry I don't know Rails. But you should still be able to follow along with this explanation,.
How this filter works is that the ContiainerRequestFilter is called before the backend controller method is called, then the controller method is called, then the ContainerResponseFilter is called. See the commented notes above the methods to see which method are for which.
public class CorsFilter implements ContainerRequestFilter, ContainerResponseFilter {
#Override
// ******************************************
// implementation for ContainerRequestFilter
// ******************************************
public void filter(ContainerRequestContext request) throws IOException {
if (isPreflightRequest(request)) {
request.abortWith(Response.ok().build());
return;
}
}
private static boolean isPreflightRequest(ContainerRequestContext request) {
return request.getHeaderString("Origin") != null
&& request.getMethod().equalsIgnoreCase("OPTIONS");
}
#Override
// ******************************************
// implementation for ContainerResponseFilter
// ******************************************
public void filter(ContainerRequestContext request, ContainerResponseContext response)
throws IOException {
if (request.getHeaderString("Origin") == null) {
return;
}
if (isPreflightRequest(request)) {
response.getHeaders().add("Access-Control-Allow-Credentials", "true");
response.getHeaders().add("Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS, HEAD");
response.getHeaders().add("Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept, Authorization, X-CSRF-Token, " +
"Accept-Version, Content-Length, Content-MD5, Date, X-Api-Version, X-File-Name");
}
response.getHeaders().add("Access-Control-Allow-Origin", "*");
}
}
So when the request comes in, the filter checks to see if it is Preflight request. There are two types of cross origin request: a normal one, and preflight. The preflight request occurs before the actual request is made to the server. This is an OPTIONS request where the browser sends some CORS request headers to check with the server to see if the request is allowed. The server should respond back with CORS response headers telling the server that the request is allowed.
So with this filter, is it a preflight request, we abort the request (the controller method will not be called) and then the code execution goes to the ContainerResponseFilter where we set the CORS response headers. You can see in the code, we check the same isPreflightRequest() method.
If the request is not an OPTIONS request, and the Origin header is present, then it is a "normal" cross origin request, in which case, all that is required in in the response headers is Access-Control-Allow-Origin.
Notice that if there is no Origin header, then no CORS headers are added. This is basically how you will differentiate from an AJAX client and other HTTP clients. Only AJAX requests will add the Origin header, and this is done automatically by the browser when it detects a cross origin request.
I hope you understand everything I am talking about, even though the code is Java. It think it's pretty straight forward though, even if you have never used Java. The naming or methods and variables should make it easy to follow. If you understand the flow of the code, then you should pretty much understand the flow of the CORS protocol.
Aside
As an aside, it doesn't hurt if you add the Access-Control-Allow-Origin header on all responses, whether it be an AJAX request or an HTTP client request. If you want to be lazy and implement it this way, it won't hurt. It is required for the AJAX client, but not for the HTTP client. But if you include it in the HTTP client, nobody will die. The world will go on business as usual.

Grails CORS not enabled because no origin

I have a grails 2.2.4 application. I wanted to enable CORS
So I installed cors plugin by having the following line in build config.
plugins {
runtime ':cors:1.1.8'
}
Then in the config.groovy
cors.headers = ['Access-Control-Allow-Origin': '*']
But after this when I run the application, CORS in not enabled. So I debugged the CORS plugin. The issue seems to be in CorsFilter class in the following method
private boolean checkOrigin(HttpServletRequest req, HttpServletResponse resp) {
String origin = req.getHeader("Origin");
if (origin == null) {
//no origin; per W3C spec, terminate further processing for both preflight and actual requests
return false;
}
The origin parameter in the above line is always null as the request does not have the parameter 'Origin'. Is there something i'm doing wrong? I'm not looking for the answer which says add a manual header with the name "Origin" since that is not exactly a proper fix
I'm quite new to CORS so appriciate the help.
In addition to Access-Control-Allow-Origin, and in addition to setting the Origin header on request, you probably need to specify these response headers as well:
Access-Control-Allow-Headers: accept
Access-Control-Allow-Headers: origin
Access-Control-Allow-Headers: content-type
Access-Control-Allow-Method: GET
Access-Control-Allow-Method: POST
Also make sure you respond to HTTP OPTIONS requests with these headers and a blank 200 OK response.
For now, let's assume that RestClient is sending the Origin header properly. It may still be getting stripped by your application. You can prevent this using the Access-Control-Allow-Headers: Origin header.
Most of the problems I have had with my web services is that the right headers are being sent, but they are stripped from the message by my web server. So I tend to adopt a shotgun approach of "allow everything" and then one by one remove what I don't need. My allow-headers header usually is pretty long and I end up having to include stuff like Content-Type, X-Requested-With and other junk before my requests will finally go through.
I further recommend that you test using something besides RestClient, if only as a sanity check. I use Postman, a free Chrome app, for all my messaging tests. It looks to me like the problem is with RestClient not sending the proper Origin header.

Request option "withCredentials" - Angular2

I am getting a CORS issue using request to connect to Neo4j in an Angular2 component:
Response to preflight request doesn't pass access control check. A wildcard '*' cannot be used in the 'Access-Control-Allow-Origin' header when the credentials flag is true. Origin 'http://localhost:8080' is therefore not allowed access. The credentials mode of an XMLHttpRequest is controlled by the withCredentials attribute
How do I set withCredentials: false using request (Typescript)? I'm assuming this will fix the problem. But the request ts file does not list withCredentials in its CoreOptions object. Neither does the Neo4j-Typescript package include this in its Typescript definition.
You can do this by extending the BrowserXhr class:
#Injectable()
export class CustomBrowserXhr extends BrowserXhr {
constructor() {}
build(): any {
let xhr = super.build();
xhr.withCredentials = false;
return <any>(xhr);
}
}
and override the BrowserXhr provider with the extended:
bootstrap(AppComponent, [
HTTP_PROVIDERS,
provide(BrowserXhr, { useClass: CustomBrowserXhr })
]);
I had the same issue in my Angular2 application.
The problem is that before every request made by the client a preflight request is sent to the server.
This kind of request have a type OPTIONS, and it's duty of the server to send back a preflight response with status 200 and headers set for accepting requests from that client.
This is my solution (with express):
// Domain you wish to allow
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
// Request methods you wish to allow
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
// Request headers you wish to allow
res.setHeader('Access-Control-Allow-Headers', 'YOUR-CUSTOM-HEADERS-HERE');
// Set to true if you need the website to include cookies in requests
res.setHeader('Access-Control-Allow-Credentials', true);
// Check if Preflight Request
if (req.method === 'OPTIONS') {
res.status(200);
res.end();
}
else {
// Pass to next layer of middleware
next();
}
As you can see, i set the headers and then fetch if the request type is OPTIONS. In that case i send back a status 200 and end the response.
In this way, the client will be authorized and you will be also able to set your custom headers in all the requests without worrying about CORS anymore.
If you really want to change withCredentials than you have to provide customized BrowserXhr, see this answer.

$httpProvider interceptor how to read all headers

I have an interceptor using $httpProvider as follwos (taken from Angular doc)
$httpProvider.interceptors.push(function ($q) {
return {
'response': function (response) {
// look for the antiforgerytoken and update dataService with the same.
return response || $q.when(response);
}
};
});
using the debugger I see that this method is called on each response and when I look at the 'response' object pased I see only one header, namely the Accept-Header as shown below
When I look at the response packet in the chrome I see many more headers
My question is why am I not able to see all the headers in the response?
Specifically I want to read the cookie for AntiforgeryToken and store it, how can I do that?
I believe the cookie AntiforgeryToken is a HttpOnly cookie and cannot be accessed from javascript. Can you check
See some details here http://blog.codinghorror.com/protecting-your-cookies-httponly/
Such cookies are mean for server exclusively.

XMLHttpRequest Post Method - Headers are stopping function

I'm trying to send an XMLHttpRequest object to my Rails server but the headers are causing my function to stop. Here are my observations:
When I comment out the 3 lines of code that set the headers, then xhr.readyState will eventually equal 4 (alert boxes within the anonymous function fire off).
If any one of the 3 header lines are uncommented, then the xhr object never changes state (none of the alert boxes ever fire off).
function saveUserProfile(){
var user_email = $('#userEmail_box').val();
var xhr = new XMLHttpRequest();
xhr.onreadystatechange=function(){
if (xhr.readyState==4 && xhr.status==200)
{
alert("Yes: " + xhr.readyState);
}
alert("No: " + xhr.readyState);
}
var method = 'POST';
var params = 'userEmail=user_email';
var url = 'http://localhost:3000/xhr_requests.json';
var async = true;
//Need to send proper header information with POST request
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.setRequestHeader('Content-length', params.length);
xhr.setRequestHeader('Connection', 'close');
xhr.open(method, url, async);
xhr.send(params);
}
My three questions are:
What do I need to change in the code above in order to send data through the POST method?
I'm under the impression that the POST method requires some headers to be sent but am not clear about which ones though "xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');" seems to be one that is often mentioned in references. Can somebody help me understand a) why headers need to be sent and b) which ones need to be sent?
I'm using a rails server and am developing locally. Ultimately, this code will be executed on the client side of a mobile device which will go to a hosted rails server for passing and receiving data. Are there limitations with using the POST method with a rails server? Keep in mind that I plan to use JSON when sending information to the client from the server.
Thanks!
UPDATE: The headers should come AFTER the opening the xhr request but BEFORE sending it:
xhr.open(method, url, async);
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.setRequestHeader('Content-length', params.length);
xhr.setRequestHeader('Connection', 'close');
xhr.send(params);
Hope this post saves somebody else 4 hours.
Does your web page with the JavaScript code also live on localhost:3000? If not, this is considered a cross-domain request, and your server will need to return special headers. So you have two options:
1) Host the web page on the same domain as the server, which will make this a same-domain request.
2) Have your server return the appropriate CORS headers. You can learn more about CORS here: http://www.html5rocks.com/en/tutorials/cors/

Resources