Using this kind of setup brings us several advantages:
- We can update the console independently from the rest of the AS code base
- We can deploy the console to various (web)app stores like Firefox Marketplace or Chrome Web Store
- We can have different console versions with different feature sets (nightly, beta, stable)
- Using one console you can connect to different server instances running different setups (development, staging, production)
The console talks to the domain controller (DC) using the HTTP management API. Now when the console and the DC live on different hosts, the same origin policy (SOP) throws a monkey wrench in our efforts. Luckily we’re not the first ones confronted with this problem. There are several ways to solve SOP restrictions:
According to caniuse.com CORS is implemented in most of the browsers. CORS knows two different types of requests.
A request that only uses
POSTis used to send data to the server, the content type of the data sent to the server with the HTTP
POSTrequest is one of
text/plain. The request must not contain custom headers (such as
A request that uses methods other than
POST. Also, if
POSTis used to send request data with a content type other than
text/plain, e.g. if the
POSTrequest sends an XML payload to the server using
text/xml, then the request is a preflighted request. Furthermore if the request contains custom headers, it’s preflighted.
Unlike simple requests, preflighted requests first send an HTTP
OPTIONS request header to the resource on the other
domain, in order to determine whether the actual request is safe to send. Since the console uses
POST with a
content type of
application/dmr-encoded all requests are preflighted requests. This will be important when looking
at the server side.
Common to all requests is that the browser adds the request header
Origin. The value of this header is the site
that served the page. For example, suppose a page on http://www.example-social-network.com attempts to access a
user’s data in online-personal-calendar.com. If the browser implements CORS, the following request header would be sent:
It is up to the server to decide which origins are allowed. When the server accepts cross-origin requests, it sends an
Access-Control-Allow-Origin header in its response. The value of the header indicates what origin sites are allowed.
For example, a response to the previous request would contain the following:
For the JBoss AS this implies changes to the class
org.jboss.as.domain.http.server.DomainApiHandler. This class
handles HTTP requests to the management API. The modified version accepts preflighted
OPTIONS requests and sets the
relevant response headers.
The console uses digest authentication. This must continue to work when doing cross-origin requests. By default,
XMLHttpRequest invocations, browsers will not send credentials. A specific flag called
withCredentials has to be set on the
XMLHttpRequest object when it is invoked. Unfortunately GWTs implementation
XMLHttpRequest object does
not yet implement this flag. For the time being
we need to include
withCredentials by ourselves.
The next challenge is to trigger the login dialog when accessing protected resources. Unfortunately when it comes to authentication browsers handle CORS quite differently. There are some workarounds depending on which browser is used:
- Chrome prevents basic authentication from a different origin as a phishing attack period.
The only way to get around that is to start Chrome using the command line option
- Firefox makes no problems when it comes to display the login dialog (well done guys!)
- Safari won’t show the login dialog. You can get around that by inserting a hidden
<iframe>element linking to the protected url. This will trigger the authentication popup and once the user has authenticated, you can execute direct
- Internet Explorer somewhat support CORS in IE8 and IE9 using the
XDomainRequestobject (but has limitations).
Finally there’s an issue related to preflighted
OPTIONS requests on the server side. According to the specification
those requests must exclude user credentials:
Otherwise, make a preflight request […] with the following additional constraints:
- Exclude user credentials.
Hence these requests are blocked by the server with “401 Unauthorized”. As a workaround the authenticators in the AS
code base must let pass preflighted
At the moment a first preview of an independent console is available at OpenShift: https://console-hpehl.rhcloud.com. Please note that you must have a CORS enabled server instance running in order to connect from the console. You can build one by yourself using the “cors” branch of the AS code base: https://github.com/hpehl/wildfly/tree/cors. Follow the steps in the README to build the server.
Due to the limitations regarding authentication and CORS, the solution described here will only work in Firefox and
Safari. If you want to use Chrome make sure to use the
--allow-cross-origin-auth-prompt command line option.
I stumbled upon an interesting project on GitHub: https://github.com/pazguille/CORS. Seems to have support for IE8+, offers an easy to use API and hides the awkward implementation details. I will evaluate this and try to make a GWT port.