Sunday, April 21, 2013

CORS: Doing the preflight dance with Jetty and Angularjs

Foreword

After giving my best with CORS I'm finally giving up out of following reasons:

  • Browsers behave differently with CORS. This is not usable. One can assume what happens when the next browser upgrade is coming. Debugging, debugging, debugging which just isn't worth the time.
  • Setting Authorization: Basic headers will simply not work. In the W3C specs they have a section for credentials, but they don't loose a word about setting the Authorization header. The only thing that is for sure is that it will be a pre-flight request which other developers warn from using.
  • I have spent about 3 hours every day within 2 weeks to find a way to use CORS with Authorization headers... no luck
  • After that I needed a big mug of beer :) With big mug I mean the equivalent to two bottles of white wine...

Even after these warning signs I believed in my own stupidity until I fell over following sentences:

Funnily enough, when making a CORS request using jQuery, the JavaScript library specifically avoids setting the custom header, along with a word of warning to developers:

// For cross-domain requests, seeing as conditions for a preflight are akin to a jigsaw puzzle, we simply never set it to be sure.

So possibly the best advice, if possible, is to avoid setting the custom header if you don’t want to do the preflight dance.

Otherwise good luck my friend, I hope this has helped!

This boosted my self confidence again seeing that also other people went to the asylum :) But I also found it a petty that I can't use Basic Authentication for cross domain requests. So my next plan is to try solving the issue with tokens or simply not using cross origin requests which has the limitation that I can't separate the client from the backend which makes it less scalable then I wanted my software to be.

Please note that currently development is my hobby. I write these sentences primarily for myself so that I don't repeat mistakes in the next project. I'm happy if my writing helps some people and I'm also happy to get feedback and be corrected in case something is wrong. I work harder if I feel like an idiot.

If you are confident that you want to do the preflight dance... below I describe as far as I got and.... failed with OPTIONS request being blocked.

Adding Maven Dependency

First of all you must check which version of Jetty you are using. According to that you can search for the corresponding Maven jetty-servlets Version. Once determined you can add the dependency to your pom.xml.

        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-servlets</artifactId>
            <version>8.1.9.v20130131</version>
        </dependency>

Configuring web.xml

As described above declarations such as allowedMethods and allowedHeaders won't work by setting a "*". You have to tell explicitly what you want to allow.

Additionally I noticed (after some weeks of frustration....) that the order you add filters in web.xml seems to matter. The CORS filter has to be added before the springSecurityFilterChain filter. Otherwise authorization doesn't seem to work. I'm not sure here but I guess that the headers first have to be allowed by CORS.

    <!-- CORS related filter -->
    <filter>
        <filter-name>CORS</filter-name>
        <filter-class>
          org.eclipse.jetty.servlets.CrossOriginFilter
        </filter-class>

        <init-param>
            <param-name>allowedOrigins</param-name>
            <param-value>*</param-value>
        </init-param>
       <init-param>
            <param-name>allowedMethods</param-name>
            <param-value>GET,POST,DELETE,PUT,HEAD</param-value>
        </init-param>
        <init-param>
            <param-name>allowedHeaders</param-name>
            <param-value>
               origin, content-type, accept, authorization, 
                  x-requested-with
            </param-value>
        </init-param>
        <init-param>
            <param-name>supportsCredentials</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>CORS</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- Spring Security Filters -->
    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>
            org.springframework.web.filter.DelegatingFilterProxy
        </filter-class>
    </filter>

    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

spring-security.xml

Finally create-session has to be set to stateless in the spring security configuration. Without enabling stateless authentication it can also lead to errors when executing a request from within Angularjs $http method.

<http use-expressions="true" create-session="stateless">
...
<http>

Test with Curl

You can test the headers with the following curl command below.

curl -v -X GET \
    -H "Origin: http://www.example.com" \
    -H "Authorization: Basic am9obkBqb2huLmNvbTpibGE=" \
    -H "Accept: application/json" \ 
    -H "Access-Control-Request-Headers: X-Requested-With" \  
    http://localhost:8080/api/user

The output should look similar to this.

* connected
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /api/user HTTP/1.1
> User-Agent: curl/7.27.0
> Host: localhost:8080
> Origin: http://www.example.com
> Authorization: Basic am9obkBqb2huLmNvbTpibGE=
> Accept: application/json
> Access-Control-Request-Headers: X-Requested-With
> 
< HTTP/1.1 200 OK
< Access-Control-Allow-Origin: http://www.example.com
< Access-Control-Allow-Credentials: true
< Access-Control-Expose-Headers: 
< Content-Type: application/json
< Transfer-Encoding: chunked
< Server: Jetty(8.1.9.v20130131)
< 
* Connection #0 to host localhost left intact
Here you should see the JSON DATA that is parsed back.
* Closing connection #0

Further resources

Saturday, April 13, 2013

Node.js Express JS and CORS (Cross Origin Resource Sharing)

Foreword

It seems that by default web browsers don't allow Cross Origin Resource Sharing which means that if a request is done from for example www.domain1.com to a resource on www.domain2.com it is treated as security issue. This is also the case if different ports are used with the same domain. So for example if your Angularjs application is running on localhost port 8000 and you are connecting on to an Express JS server that is running on localhost port 3000 it won't work. It's actually quite hard to debug as in Firefox the request will be displayed as red but there is no error message that is showing up explaining why the request is not working. Also the http status is 0 which is quite confusing. When however debugging the request with the Chrome browser there will be a message that says that XMLHttpRequests are forbidden.

One additional thing there is to say is that Chrome also does not accept such requests if the html file is opened directly in the browser. A web server will be required to execute the request (file:// is not working with all browsers, http:// is working).

More information on the issue can be found here.

Configuring Express JS

To solve this issue it is necessary to configure the Express JS server to explicitly allow such connections. To achieve this I have added following to the configuration of Express JS.
var express = require('express');

var app = express();

app.all('/*', function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "X-Requested-With");
  next();
});

...
  • The first header setting is allowing access from different origins. "*" means that all origins will be accepted but it is also possible to set explicit origins.
  • The second header setting is necessary because Angular is adding a "X-Requested-With" XMLHttpRequest header when opening a connection to a resource.

Tuesday, April 9, 2013

Creating a simple webserver with node.js and Expressjs

Foreword

I played with angularjs for a while and struggled a little as I didn't find an easy way to simulate json responses. I want to decouple the client completely from the server so that it is possible to work in my nifty little javascript world without having to take care about the server part that is running on Spring/Java. I'm mainly doing that because I want to decouple as much code as possible to outsource parts to other developers that are experts in their segment. I don't want that a javascript developer needs knowledge about Java or any other development language. I also want to split projects in to chunks, spread them among different developers and set them together to the full picture once complete.
At last I found my solution with node.js and the Expressjs server. This is an easy example to setup a "one-script" solution to simulate json responses. This article is not intended to teach others but to share knowledge and learn.

Installation

Install node.js by downloading the corresponding binary package. I have downloaded the Linux binary package. I like to keep binaries like this in my home directory. So I did the following:
mkdir $HOME/Programs
cd $HOME/Downloads
tar -xvzf node-v0.10.3-linux-x64.tar.gz
mv node-v0.10.3-linux-x64 $HOME/Programs
Then I added the bin directory to the path so that the commands can be executed.
vim $HOME/.bashrc
export PATH=$PATH:$HOME/Programs/node-v0.10.3-linux-x64/bin
After opening a new terminal window I tested if node.js has been added to the path correctly:
node -v
v0.10.3

Creating the webserver

I have created a folder to install the webserver.
mkdir $HOME/Documents/node-server-example
Then I changed in to the directory and created a file called package.js.
cd $HOME/Documents/node-server-example/
vim package.json
{
  "name": "Express Test Server",
  "description": "Testing the Express Server",
  "version": "0.0.1",
  "private": true,
  "dependencies": {
    "express": "3.x"
  }
}
After that I have executed following command to install the Express Webserver.
npm install
This created a directory called node_modules with the express server dependencies in it. Then I have created a file called server.js that contains the server dependent code.
vim server.js
var express = require('express');

var app = express();

app.get('/api/user/email/testemail1', function(req, res) {
  res.status(201);
  res.json({ message: "this sends status header 201 (Created)" });
});
app.get('/api/user/email/testemail2', function(req, res) {
  res.status(200);
  res.json({ message: "this sends status header 200 (OK)" });
});

app.listen(3000);
console.log('Listening on port 3000...');
Then I started the webserver and browsed to the site with the Firefox RESTClient.
node server
Listening on port 3000...
And voila..... that was it. Pretty amazing.

Additional Sources