CORS (Cross-Origin Resource Sharing) 19.1

Cross-Origin Resource Sharing is a browser technology specification that defines ways for a web server to allow its resources to be accessed by a browser application from a different origin domain. Such access would otherwise be forbidden by the same origin policy. CORS defines a way in which the browser and the server can interact to determine whether to allow the cross-origin request. It is a compromise that allows greater flexibility, but is more secure than simply allowing all such requests.

The CORS standard works by adding new HTTP headers that allow servers to serve resources to permitted origin domains. Browsers support these headers and enforce the restrictions they establish. Additionally, for HTTP request methods that can cause side-effects on user data―in particular, for HTTP methods other than GET, or for POST usage with certain MIME types―the specification mandates that browsers “pre-flight” the request, soliciting supported methods from the server with an HTTP OPTIONS request header, and then, upon “approval” from the server, sending the actual request with the actual HTTP request method. Servers can also notify clients whether "credentials" (including Cookies and HTTP Authentication data) should be sent with requests.

CORS can be used as a modern alternative to the JSONP pattern. While JSONP supports only the GET request method, CORS also supports other types of HTTP requests. Using CORS enables a web programmer to use XMLHttpRequest, which supports a better error handling than JSONP. CORS is supported by most modern web browsers. However, JSONP works on legacy browsers which preclude CORS support.

CORS and the Open Commerce API

The Open Commerce API supports the CORS specification.

If an API request contains an Origin header, the origins in the header are verified against a whitelist of allowed origins. The whitelist can be configured in Business Manager Open Commerce API Settings on a per site and client application basis. If all the origins in the header match the configured origins, the API confirms the origins by returning all allowed origins in response header Access-Control-Allow-Origin. Additionally, the API returns the response header Access-Control-Allow-Credentials with value "true", which notifies the client to send also cookies. If an origin is not defined in the allowed origins whitelist, the API does NOT add the Access-Control-Allow headers to the response. This is true for GET and HEAD requests. For PATCH, POST, PUT, and DELETE requests, the API returns a 401 fault of type UnauthorizedOriginException and ensures that there is no processing on the server side.

"Preflighted" requests first send an HTTP OPTIONS request header to the resource on the other domain; this determines whether the actual request is safe to send. Cross-site requests are preflighted because the requests may affect user data. In particular, a request is preflighted if it uses methods other than GET or POST. A request is also preflighted if the POST method is used to send request data with a Content-Type other than application/x-www-form-urlencoded, multipart/form-data, or text/plain―for example, if the POST request sends an XML payload to the server using application/json, application/xml or text/xml, the request is preflighted.

Configure Allowed Origin(s)

To configure allowed origins for the Open Commerce API, perform the following steps:

  1. In Business Manager, select Administration > Site Development > Open Commerce API Settings.
  2. Select a site for which you want to configure allowed origins.
  3. In the text field, you configure the property allowed_origins per client application in the JSON document, as described below.
{
  "_v":"19.1",
  "clients":
  [ 
    {
      "allowed_origins":["http://foo.com","https://secure.foo.com:8888"],
      "client_id":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
      "resources":
      [
        {
          "resource_id":"/customers/auth",
          "methods":["post"],
          "read_attributes":"(**)",
          "write_attributes":"(**)"
        },
        {
          "resource_id":"/baskets",
          "methods":["post"],
          "read_attributes":"(**)",
          "write_attributes":"(**)"
        },
        ...
      ]
    }
  ]
}  
Note:

The '*' wildcard is not supported.

Examples

Example 1: GET request with unknown origin - no Access-Control-Allow headers in response; browser should reject the response by forcing same origin policy.

REQUEST:
GET /dw/shop/v19_1/products/123/availability HTTP/1.1
Host: example.com
Origin: http://bar.com
 
RESPONSE:
HTTP/1.1 200 OK
Content-Length: 67
Content-Type: application/json; charset=UTF-8
Cache-Control: max-age=60,must-revalidate
 
{
  "id":"123",
  "name":"Shirt",
  "orderable":true
}

Example 2: GET request with known origin - Access-Control-Allow headers in response; modern browsers should make the response content available.

REQUEST:
GET /dw/shop/v19_1/products/123/availability HTTP/1.1
Host: example.com
Origin: http://foo.com
 
RESPONSE:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://foo.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: location,x-dw-version-status
Content-Length: 67
Content-Type: application/json; charset=UTF-8
Cache-Control: max-age=60,must-revalidate
 
{
  "id":"123",
  "name":"Shirt",
  "orderable":true
}

Example 3: POST request with unknown origin: Pre-flight OPTIONS request contains no confirming Access-Control-Allow headers; browsers skip the second request.

REQUEST:
OPTIONS /dw/shop/v19_1/baskets HTTP/1.1
Host: example.com
Origin: http://bar.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type

RESPONSE:
HTTP/1.1 204 No Content
Allow: POST

Example 4: POST request with known origin: Pre-flight OPTIONS request has confirming Access-Control-Allow headers in response; browsers execute the second request.

REQUEST 1:
OPTIONS /dw/shop/v19_1/baskets HTTP/1.1
Host: example.com
Origin: http://foo.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type

RESPONSE 1:
HTTP/1.1 204 No Content
Access-Control-Allow-Methods: POST
Access-Control-Max-Age: 86400
Access-Control-Allow-Origin: http://foo.com
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Content-Type
Allow: POST

REQUEST 2:
POST /dw/shop/v19_1/baskets HTTP/1.1
Host: example.com
Origin: http://foo.com
Authorization:Bearer eyJfdiI6IjXXXXXX.eyJfdiI6IjEiLCJleHAXXXXXXX.-d5wQW4c4O4wt-Zkl7_fiEiALW1XXXX 
Content-Type: application/json
Content-Length: 67

{
  "product_id" : "456",
  "quantity" : 1.00
}

RESPONSE 2:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://foo.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: location,x-dw-version-status
Content-Type: application/json;charset=UTF-8
Cache-Control: max-age=0,no-cache,no-store,must-revalidate
Content-Length: 158

{
  "_v" : "19.1",
  "_resource_state" : "ba4e84383e1790597e49eeee34b201633d80ed3f499992f5af11d639dd903a36"
  "currency" : "USD",
  "product_sub_total" : 40.00,
  "product_total" : 40.00,
  "shipping_total" : null,
  "tax_total" : null,
  "order_total" : null,
  "product_items" : 
  [
    {
      "product_id" : "456",
      "item_text" : "Product foo",
      "quantity" : 1.00,
      "product_name" : "foo",
      "base_price" : 40.00,
      "price" : 40.00
    }
  ]
}