Saturday, September 17, 2016

Enable CORS in WSO2 ESB

Introduction

This post explains concept of Same Origin Policy (SOP) and how Cross-Origin Resource Sharing(CORS) can be used to handle SOP in details. Finally this describe how to implements CORS in WOS2 ESB.

Same origin policy

The same origin policy restricts the browser how a document or script loaded from one origin can interact with resource from another origin. For example if http://example.com tries to retrieve data from (by sending GET request) https://example.com , the browser will disallow access since origins are different.  The origin is everything in the URL before the path . It follows schema/host/port pattern.(for example, http://www.example.com:8080). This policy basically prevents a malicious script on one page from obtaining access to sensitive data on another web page through that page's DOM.

What if your web application want to access third party API or you want to provide API for third-party access?

Cross-Origin Resource Sharing (CORS)

CORS is a mechanism that allows access resources from one origin to another origin. This seems contradiction. How can CORS allow cross-origin requests if the same-origin policy explicitly prohibited them.? The key is that CORS put servers to decide who can make requests, what type of requests allowed and what are the custom headers allowed.

How CORS work?

The browser and the server use HTT headers to communicate how cross-origin requests behave.Using the response headers, the server can indicate which clients can access the API , which HTTP methods or HTTP
headers are allowed, and whether cookies are allowed in the request.

CORS is basically build around following headers.
  1. The Origin request header (Normally added by the browser)
  2. The Access-Control-Allow-Origin response header
These headers must be present on every successful CORS request. Without one or the other, the CORS request will fail.

1.Simple cross-site request 

A simple cross-site request is one that meets all the following conditions:

1. Allowed http methods
  •  GET
  •  HEAD
  •  POST
2. Allowed values for the Content-Type header of POST request
  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain
for eg: Suppose you have a website that running on http://localhost:8080 needs to get some data from CORS-enabled REST API hosted in http://example.com. Then if you observe the request in browser(in this example chrome) you would see following.

General
Request URL: http://example.com/get/posts
Request Method: GET
Status Code: 200 OK

Request Headers
Origin: http:/localhost:8080

Response Headers
Access-Control-Allow-Origin: http:/localhost:8080

Note: Origin Header will be added by browser automatically and 
Access-Control-Allow-Origin should be added by server.

2. Preflight requests

There are some cases where the Access-Control-Allow-Origin header alone is not enough. Certain types of requests , such as DELETE and PUT, need to go a step further and ask for the sever's permission before making the actual request. This is called "preflight" request.
The preflight request is a small request(OPTIONS)  that sent by browser before the actual request. It contains information like which HTTP method is used, any HTTP headers are available.It gives the server a chance to examine what the actual request look like.Then server can decide whether the browser should send the actual request or return an error without sending the actual response.

In particular, a request is preflighted if

1.It uses methods other than GET, HEAD or POST.  Also, if POST is used to send request data with a Content-Type other than application/x-www-form-urlencoded, multipart/form-data, or text/plain, e.g. if the POST request sends an XML payload to the server using application/xml or text/xml, then the request is preflighted.
2.It sets custom headers in the request (e.g. the request uses a header such as X-PINGOTHER

for eg: Suppose you need to send POST request with Content-Type application/xml to CORS-enabled http://example.com/add/post from http://localhost:8080. Then you would see two request as follows.

1. Preflight request

General
Request URL: http://example.com/add/post
Request Method: OPTIONS
Status Code: 204 OK

Request Headers
Origin: http:/localhost:8080
Access-Control-Request-Method: POST
Content-Type: application/xml

Response Headers
Access-Control-Allow-Origin: http:/localhost:8080
Access-Control-Allow-Methods: POST

2. Actual request

If preflight request success this would be normal POST request.


HTTP OPTIONS method

The HTTP spec ( RFC2616 ) defines an OPTIONS request as “a request for information about the communication options available on the request/response chain.” This means that even before CORS , clients could use the OPTIONS method to learn more about an endpoint. When used out-side of CORS , the OPTIONS method traditionally conveys which HTTP methods are
supported on a particular URL .

Handle Preflight request in WSO2 ESB

Add a filter to handle "OPTIONS" request and add required headers as in following configurations.

<filter source="get-property('axis2', 'HTTP_METHOD')" regex="OPTIONS">
            <then>
               <property name="Access-Control-Request-Headers" value="authorization,content-type" scope="transport"/>
               <property name="Access-Control-Allow-Headers" value="authorization,Access-Control-Allow-Origin,Content-Type,X-Requested-With,Accept" scope="transport"/>
               <property name="Access-Control-Allow-Methods" value="GET,POST,PUT,DELETE,OPTIONS" scope="transport"/>
               <property name="Access-Control-Allow-Origin" value="*" scope="transport"/>
               <property name="RESPONSE" value="true" scope="default" type="STRING"/>
               <respond/>
            </then>
         </filter>