How to make GWT work with CORS and cookies - gwt

I separated GWT - static code is on CDN and dynamic is on a different server.
I configured this CORS filter on Tomcat7 and it works fine:
http://software.dzhuvinov.com/cors-filter-installation.html
However I'm still struggling with getting Cookies to work with GWT and CORS.
I mainly need it for XSRF protection (and few other things):
https://developers.google.com/web-toolkit/doc/latest/DevGuideSecurityRpcXsrf
It looks like I have to set withcredentials=true in XMLHttpRequest.
Does anyone know how to do it in GWT?

By default Cookies are not sent with CORS. If you want them, you have to set the withCredentials attribute to the low-level XMLHttpRequest used by GWT. Unfortunately there is no way to set it from your java code.
A simple hack is that you maintain your own version of XMLHttpRequest.java in your source tree (use the same namespace), and modify the create() method in this way:
private static native XMLHttpRequest create(String responseType) /*-{
[...]
xhr.withCredentials = true;
return xhr;
}-*/;
Note: from server side you have to sent this header as well:
Access-Control-Allow-Credentials: true
Otherwise, the normal way to deal with authentication in GWT is that you use special headers with the auth info and read it in the server side.
builder.setHeader("X-My-Authenticated-Header", "whatever");
EDIT: as t.broyer points in his comment below, a RequestBuilder.setIncludeCredentials method is in 2.5.1-rc1. So if you are using this release, call this method in when creating your builder.

Related

HTTP Options method is not working as expected

I have a Jersey 2.x application running in tomcat. All the method implementations are working as expected, and even I am able to get WADL by navigating to http://{host}:{port}/JerseyRESTWebapp/ws/rest/application.wadl.
Everything is great so far.
Now, Out of curiosity I tried navigating to http://{host}:{port}/JerseyRESTWebapp/ws/rest/employees URL using using HTTP OPTIONS method expecting i will get 405 Method not allowed but i got the 200 OK and response body contains the WADL. Can someone let me know why is this happening? I am using POSTMAN chrome extension as REST client.
Also in the Response Allow Header, i am getting POST,GET,DELETE,OPTIONS,HEAD. I am missing PUT method here. why?
This is how the resource discovery works by default. It's implemented to follow the spec in regards to OPTIONS resource discovery
This method allows the client to determine the options and/or requirements associated with a resource, or the capabilities of a server, without implying a resource action or initiating a resource retrieval.
If you want to disable the WADL, you can by setting the property ServerProperties.WADL_FEATURE_DISABLE to true.
If you're curious about how this is implemented, check out the source for the WadlModelProcessor. It goes through all the resource models and adds an extra OPTIONS resource method. You can read more about the ModelProcessor in the Jersey docs Programmatic API for Building Resources

GWT same origin policy workaround?

Assume that I have a website whose static assets are hosted in CDN (say, AWS CloudFront), however, all the GWT-RPC calls will be handled in the domain hosts. How can I achieve this in GWT?
As #robert mentioned, CORS is another option, but it doesn't require any changes in your client-side code - if the browser is new enough to support CORS at all, then all you do is make the call to the remote server, and make sure that the remote server supports it.
Depending on which server you are using, the support will be slightly different. https://www.w3.org/wiki/CORS_Enabled has a list of different servers and how you might enable CORS, depending on what you are using, and whether you enable it across your entire server, or just in a single part of the application.
For example, in Jetty:
<filter>
<filter-name>cross-origin</filter-name>
<filter-class>org.eclipse.jetty.servlets.CrossOriginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>cross-origin</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
While in Tomcat:
CorsFilter
org.apache.catalina.filters.CorsFilter
CorsFilter
/*
It is also possible to modify your RemoteServiceServlet class to handle this, but it comes with the disadvantage of needing to more fully understand the spec and make sure you handle it all correctly.
As with JSONP, there are important security implications of allowing cross-domain requests. Unlike JSONP, the CORS spec includes features to mitigate this, and ensure that the browser doesn't attempt to make calls to your remote server from the wrong host page - you can restrict calls so that they only come from specific domains. The specific header is Access-Control-Allow-Origin, and while it can be assigned to *, meaning "all servers", you likely want to restrict it to avoid potential XSRF attacks against your application - for the above examples this is managed via init-parameters, check your container's documentation for specific details. Additionally, changing the url-pattern can restrict the urls that these filters apply to can limit what can be requested remotely.
You could try to use CORS (https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS)
Something like this:
public class CrossSiteRpcRequestBuilder extends RpcRequestBuilder {
#Override
protected RequestBuilder doCreate(String serviceEntryPoint) {
RequestBuilder requestBuilder=super.doCreate(serviceEntryPoint);
requestBuilder.setIncludeCredentials(true);
return requestBuilder;
}
}
public class CrossSiteRpcRequestBuilderFactory {
public RpcRequestBuilder get() {
return new CrossSiteRpcRequestBuilder();
}
}
RpcRequestBuilder rpcRequestBuilder=((CrossSiteRpcRequestBuilderFactory) GWT.create(CrossSiteRpcRequestBuilderFactory.class)).get();
rpcRequestBuilder.create("{YOUR_HOST}/cors_handshake");
rpcRequestBuilder.setContentType("text/x-gwt-rpc; charset=utf-8");
rpcRequestBuilder.setRequestData("cors handshake");
rpcRequestBuilder.setCallback(new RequestCallback() {
...
});
RequestBuilder r=rpcRequestBuilder.finish();
r.send();
An alternative would be to use your server as a proxy to the remote server (bandwidth).
In conclusion:
I would use JsonpRequestBuilder instead of RPC, its less convenient, but guaranteed to work (unlike CORS).
See http://www.gwtproject.org/doc/latest/tutorial/Xsite.html for details.

restler 3 cross domain not working

My restler 3 api works fine on local test server and works fine on production server if calls from that same server, but if I make the call remotely then it fails.
Using the same rest client with the luracast online examples it works fine with remote call so must be something in my configuration (either my api or my production server).
I found mention of need to send headers and so tried adding these headers to index.php file:
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, PATCH, DELETE');
header('Access-Control-Max-Age: 1000');
header('Access-Control-Allow-Headers: *');
But that didn't help. Using RESTClient addon in firefox, I can see that those headers are sent, and the browser will show the data both locally and remotely, whether I use those header commands or not.
Here's a sample call:
https://api.masterpiecesolutions.org/v1/artists/?key=A4oxMOYEUSF9lwyeFuleug==
My index.php for that call uses this, with 2nd param to map to root level
$r->addAPIClass('Artists', '');
Don't know if that is relevant.
Also, the production server is Amazon EC2, so perhaps has something to do with security policy?
Or, maybe it's some other header issue? In google chrome, using Advanced Rest Client extension, it gives status of 403 Forbidden and Content-Type is text/plain (whether using local or remote server) so it won't work at all, unlike the firefox addon.
I also see use of $_SERVER['HTTP_ORIGIN'] in Restler.php, and this doesn't appear to be supported everywhere yet?
* is not a valid value for the Access-Control-Allow-Headers response header. You need to list out every non-simple request header. For example:
header('Access-Control-Allow-Headers: Content-Type');
Also consider putting a single origin value or just * for the Access-Control-Allow-Origin header. I just visited your sample url and there are multiple values in that header. Although this should work according to the CORS spec, it is not very widely adopted yet.
Lastly I noticed that the server was setting Access-Control-Allow-Credentials: true. If you set this to true, then you also need to do two other things:
The value of the Access-Control-Allow-Origin header must be the value of the Origin (e.g. http://localhost, it can not be *).
You will need to set xhr.withCredentials = true; in your JavaScript client code.
If you are just testing, you should try to get things working without setting the Access-Control-Allow-Credentials header. It will make things easier to debug.
The problem, for me at least, was using SSL and the restclient class didn't accommodate that.
So I added (to my RestClient.class.php from phpclasses.org)
curl_setopt($this->curl, CURLOPT_SSL_VERIFYPEER, false); // for SSL
and now it works.
Also required was setting
public static $crossOriginResourceSharing = true;
in Defaults.php for Restler 3.

Defaulthttpclient scheme

The request is using the wrong scheme (http instead of https). I can see this when i debug my client and inspect the scheme inside the HttpHost object. I'm using JerseyClient to submit the request, it does so by creating a web resource with a URI. I simply pass a string https://myserver.com:443/some/path. However inside the DefaultHttpRoutePlanner class it decides to use the default settings for HttpRoute and uses http. Can anyone tell me how i can override the default settings of the HttpRoute or RoutePlanner classes?
found the answer -
return new HttpHost(request.getURI().getHost(), request.getURI().getPort(), request.getURI().getScheme());

GWT JSONP with Post not Get

I have a web service in the form `http://....../methodName
It returns a jsonp result such as:
methodName(["a":"a", "b":"b"])
GWT provides JsonpRequestBuilder class to parse jsonp.
JsonpRequestBuilder rb = new JsonpRequestBuilder();
rb.setCallbackParam("callback");
rb.requestObject("http://...../methodName", new AsyncCallback<TestJS>(){
...
});
This structure makes a request to url :
"http://...../methodName/?callback=__gwt_jsonp_P0.onSuccess".
My web service returns a callback with methodName not with __gwt_json.....
So gwt could not create a JavaScriptObject from that response.
Also JsonpRequestBuilder works with GET not POST.
How can I achieve those: Sending requests with POST and modifying GWT's default callback name.
JSONP will NOT work with POST. Its not a GWT limitation btw.
JSONP is essentially including a javascript file from your server. So, when you make a JSONP call, a temporary tag is added to the DOM.
Now, a <script> tag can always makes a GET request. That's a browser thing, and GWT cannot do much about it.
If you want to make a cross-domain POST call, you have to chose from one of the following options (and they have nothing to do with GWT)
Use Flash plus a crossdomain.xml that allows cross domain posts
Use Cross Origin Resource Sharing, or CORS. NOTE that this is only supported in modern browsers
Use a proxy server on your domain
Unfortunatly, this isn't how JsonP works. The requests are made by adding a tag to the page, and the results are passed into a function wrapped around the data – in your case, __gwt_jsonp_P0.onSuccess.
The callback name can't be affected, at least while using JsonpRequestBuilder – the system needs to account for the fact that you could send multiple requests out at once, possibly even to different endpoints. A JsonP endpoint that doesn't allow the caller to customize the callback function name is very unusual, and even more odd is an endpoint expecting JsonP calls that expects an impossible POST.
You can implement your own JsonP client side code by using the ScriptElement type, and registering your own global callback to call into your GWT java code.
Look into the API docs for the web service, and see if there is perhaps a better way to communicate with it, perhaps by using a proxy on your own server, avoiding the cross domain issue altogether.