CORS in FHIR

The following is a copy of an email sent to the FHIR list by Peter Bernhardt (and copied with permission). I’ve not read through it in detail, but it’s something that I’m grappling with myself as I prepare for the SMART track in the up-coming Connectathon.

I suspect that it’s something that a lot of folk are going to have ‘fun’ with, so I suggested to Peter that we copy it here so it can be found later!

(btw – all the servers that Peter refers to are described in the spec)

 

Hey Folks,

I’m following up conversations I’ve had with a few people on the implementers chat about Cross-Origin Resource Sharing (CORS). I’ve been writing a FHIR client application using JavaScript, and I’ve had limited success interacting with various servers.

I’ve tested against the following:

  • SPARK
  • Health Intersections
  • HAPI
  • Blaze
  • SMRT

While I’ve had no problem issuing GET requests to all of these servers, only SPARK is accepting other methods. I’ve summarized my findings below.

Per CORS, here’s the “pre-flight” request sent to the remote servers:

OPTIONS /fhir/Organization/b27ed191-f62d-4128-d99d-40b5e84f2bf2 HTTP/1.1

Host: spark.furore.com

Connection: keep-alive

Cache-Control: no-cache

Pragma: no-cache

Access-Control-Request-Method: POST

Origin: http://www.fhir-starter.com

User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.125 Safari/537.36

Access-Control-Request-Headers: accept, x-fhir-starter, content-type

Accept: */*

DNT: 1

Referer: http://www.fhir-starter.com/organization/edit/0B7

Accept-Encoding: gzip,deflate,sdch

Accept-Language: en-US,en;q=0.8

Aside from the resource URL and Host, the request is always the same, so I won’t repeat it. From a CORS perspective, the most relevant information in that request are these headers:

Origin: http://www.fhir-starter.com

Access-Control-Request-Method: POST

Access-Control-Request-Headers: accept, x-fhir-starter, content-type

Essentially, this is the browser or user agent letting the server know who it is that is calling (Origin), what they intend to do (POST), and what headers they will include.

For the request headers, a particular browser injects different headers (I used Chrome, so you see the accept and content-type headers). I’ve written the client to also include the X-FHIR-Starter header.

The SPARK server responds as follows.

HTTP/1.1 200 OK

Server: nginx

Date: Thu, 07 Aug 2014 16:15:20 GMT

Content-Length: 0

Cache-Control: no-cache

Pragma: no-cache

Expires: -1

Access-Control-Allow-Origin: *

Access-Control-Allow-Headers: x-fhir-starter,content-type

Connection: Keep-Alive

Again, focusing on CORS-related headers, we have these:

Access-Control-Allow-Origin: *

Access-Control-Allow-Headers: x-fhir-starter,content-type

The Origin value tells me there is no whitelist restriction. And the Allow-Headers value lets me know the server is expecting that I include X-FHIR-Starter and Accept headers in my POST request.

The transaction continues with the POST (or PUT or DELETE, as the case may be), with the following headers.

POST /fhir/Organization/b27ed191-f62d-4128-d99d-40b5e84f2bf2 HTTP/1.1

Host: spark.furore.com

Connection: keep-alive

Content-Length: 1177

Cache-Control: no-cache

Pragma: no-cache

X-FHIR-Starter: urn:fhir.starter

Origin: http://www.fhir-starter.com

User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.125 Safari/537.36

Content-Type: application/json+fhir

Accept: application/json+fhir, application/json, text/plain, */*

DNT: 1

Referer: http://www.fhir-starter.com/organization/edit/0B7

Accept-Encoding: gzip,deflate,sdch

Accept-Language: en-US,en;q=0.8

Note that this includes the X-FHIR-Starter header the browser promised to include.

My client can successfully execute PUT, POST and DELETE against Spark. For the other FHIR servers I tested, the transaction fails after the server’s response to the OPTIONS request. Here’s a sampling.

HAPI (http://fhirtest.uhn.ca/base):

HTTP/1.1 200 OK

Date: Thu, 07 Aug 2014 16:34:50 GMT

Server: GlassFish Server Open Source Edition  4.0

X-Powered-By: HAPI FHIR 0.6-SNAPSHOT RESTful Server

Access-Control-Allow-Origin: *

Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS

Access-Control-Expose-Headers: Content-Location

Content-Type: application/xml;charset=UTF-8

Transfer-Encoding: chunked

Connection: Keep-Alive

Content-Encoding: gzip

Health Intersections (http://fhir.healthintersections.com.au/open:

HTTP/1.1 200 OK

Content-Type: text/html; charset=UTF-8

Content-Length: 2

Date: Thu, 07 Aug 2014 16:25:13 GMT

Expires: Wed, 06 Aug 2014 16:25:13 GMT

Access-Control-Allow-Origin: *

Access-Control-Allow-Methods: GET, POST, PUT, DELETE

Access-Control-Expose-Headers: Content-Location

Access-Control-Request-Method: GET, POST, PUT, DELETE

Server: Health Intersections FHIR Server

Connection: Keep-Alive

SMART (https://fhir-open-api.smartplatforms.org):

HTTP/1.1 200 OK

Server: nginx/1.4.6 (Ubuntu)

Date: Thu, 07 Aug 2014 16:57:13 GMT

Content-Length: 0

Connection: keep-alive

Access-Control-Allow-Origin: http://www.fhir-starter.com

Access-Control-Allow-Credentials: true

Access-Control-Allow-Headers: origin, authorization, accept, content-type, x-requested-with

Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS

Access-Control-Max-Age: 3600

Same results for HAPI, Health Intersections and SMART: While the server returns a 200 response, the XMLHttpRequest object throws a fatal error: “Request header field X-FHIR-Starter is not allowed by Access-Control-Allow-Headers”. According to the W3C CORS spec (Section 6.2, step 4 [http://www.w3.org/TR/cors/#resource-preflight-requests]), I should get back my X-FHIR-Starter header in the list of Allow-Headers.

I get a different response from Blaze.

Blaze (https://fhir.orionhealth.com/blaze/fhir):

HTTP/1.1 405 Method Not Allowed

Cache-Control: no-cache

Pragma: no-cache

Allow: DELETE

Content-Type: application/xml+fhir; charset=utf-8

Content-Encoding: gzip

Expires: -1

Vary: Accept,Accept-Encoding

Server: Microsoft-IIS/7.5

X-AspNet-Version: 4.0.30319

X-Powered-By: ASP.NET

Access-Control-Allow-Origin: *

Access-Control-Expose-Headers: *

Date: Thu, 07 Aug 2014 16:30:59 GMT

Content-Length: 208

The same Invalid HTTP status code 405 is returned for all but the GET method (which is successful).

Some references I found helpful:

http://www.w3.org/TR/cors/

https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

Regards,

Peter Bernhardt

About David Hay
I'm an independent contractor working with a number of Organizations in the health IT space. I'm an HL7 Fellow, Chair Emeritus of HL7 New Zealand and a co-chair of the FHIR Management Group. I have a keen interest in health IT, especially health interoperability with HL7 and the FHIR standard. I'm the author of a FHIR training and design tool - clinFHIR - which is sponsored by InterSystems Ltd.

4 Responses to CORS in FHIR

  1. John Moehrke says:

    I am noticing renewed interest in “good security guidance”, that does not use CORS… This seems like something the general http REST community would have already addressed, so doesn’t feel to me like something Healthcare, especially HL7, should need to address… Comment?

    • David Hay says:

      You mean the people are thinking that CORS is not good practice? Certainly clinFHIR (or any other browser based app) would have issues without CORS (though I guess I could set up a server based proxy…)

  2. Sumit says:

    “X-FHIR-Starter” why this header used for?

Leave a Reply

%d bloggers like this: