FHIR and OpenID Connect

If you’ve read the previous posts talking about OAuth2, then (hopefully) it’s clear how a user can authorize an application to retrieve their data from some server without needing to expose their login credentials. The Authorization Server is the only component that needs to have the persons username and password (or whatever authorization method is being used – there are plenty of other options).

But – beyond this login stuff – how does the application know who the user is? When they query the FHIR server to get the data, exactly whom are they getting it for? (Incidentally, this process of identifying the patient/user is often called passing the ‘context’ between applications and there are lots of ways of doing this, such HL7s CCOW standard that is designed for desktop usage and so won’t help us here).

To answer this we need more information about the users Identity – or put another way, one or more properties of the users identity that both the application and FHIR server recognize so that they know they are dealing with the same person. In FHIR, we think of this as an Identifier – and there are many entities/resources that can have them, including the patient.

Another way to look at this is that the application needs to be able to make a query like this:

GET [base]/fhir/Condition?subject.identifier=PRP1660

to get the conditions for the patient whose identifier is PRP1660. (Or any other FHIR mechanism for that matter).

So let’s think about Identifiers for a second. We’ve actually touched on this a number of times in this blog – often in the context of separating the concepts of FHIR resource ID from Identifier (confusingly, they are often both called ‘ID’ ) – but they are quite different.

An identifier is usually a string that uniquely identifies an entity within the context of a ‘system’ of some sort and is not confined to healthcare. Examples of identifiers include a Drivers License, a Social Security Number or – in New Zealand – a National Health Index (NHI number). So this implies that there is some ‘registry’ that holds and assigns these identifiers that everyone participating in the exchanges recognizes. (Of course, there may be more than one identifier for any one entity).

(as an aside, because it’s uncommon to have a single patient identifier that can be used for healthcare delivery, there’s a component – the Enterprise Master Patient Index (EMPI) that is often used in real-world implementations to provide this consistent identifier for a patient – and link to redundant or duplicate identifiers).

So, to be of use to us, an Identifier has a minimum of 2 parts: The actual identifier string, and the system within which the identifier string is unique. (and yes, I know the example above hasn’t got the system!)

Having decided that, the next question is where do we get the identifier from? In the exchanges we’ve discussed thus far the closest we’ve got to this are the logon credentials with the Authorization Server and that’s not the same (the Authorization Server could be Google, Facebook or Amazon for example). What we need is an ‘Identity Server’ , and some way to get the patients identity from it – and that’s where OpenID Connect comes in.

OpenID Connect is a protocol that sits ‘on top’ of OAuth2 to provide identity services – which means that it uses the same components and flows as OAuth2 does (and what we’ve looked at in the last couple of posts), but adds a few extra pieces that relate to identity – ie more attributes about the person than just the login that they use to the Authorization server, such as name, email address, birthday – and identifiers!

Now, there’s actually a wrinkle here.  OpenID Connect defines a number of ‘standard’ attributes about a user (which it calls ‘claims’) that are listed here, but identifier is not one of them! However, it does allow you to define extra ones – and this is what we’re going to use for the patients identifier. But because this is not in the standard, we need a separate agreement between all those who are going to use this approach about what code we are going to use to signify the identifier. This is sometimes called a ‘trading party agreement’ or a part of an Implementation guide. Just ignore the details of this for now – we’ll come back to it later.

Another way of looking at OpenID Connect is that it takes the existing OAuth components, uses the existing flows and adds specific extra functionality to them. (If you read the spec you’ll see that it does give different names to some of the OAuth2 components – eg ‘Client’ becomes ‘Relying Party’  and ‘Authorization Server becomes OpenID Connect Provider. We’ll continue to use the names we’re used to here, but don’t get confused if you see them in the spec.)

The actual flow is very similar to the standard OAuth2 flow that we’re already discussed (unsurprisingly). The following description gives an overview of this flow – calling out the differences (or additions) to OAuth2.

openid

  1. The browser interacts with the app, and is redirected to the Authorization Server. The parameters in the call include a scope parameter which lists the identity claims that the app wants plus the value of ‘openid’ to indicate to the Authorization Server that this is an OpenID Connect request.
  2. The redirected browser calls the Authorization Server and a login page is rendered. The page will usually include a list of the claims requested by the app, so the user can authorize it. (This facility is part of standard OAuth2).
  3. The user enters their login details, and authorizes the release of information.
  4. The details are sent to the Authorization Server
  5. which validates the user…
  6.  and issues a redirect back to the application containing an authorization code.
  7. The application receives the redirected request with the authorization code.  It then makes a request to the Token endpoint of the Authorization Server with the authorization code, the Authorization Server validates the code and returns all 3 tokens:
    1. Access Token
    2. Refresh Token
    3. ID token
  8. … and the flow continues as described before

(Just a reminder that a whole lot of detail is omitted from the description above in the interest of clarity – refer to the spec if you want the details. They’re helpful to understand the properties of whatever library you’re using to do this stuff).

It’s also worth mentioning that it’s quite possible to have a completely separate ‘Claims Provider’ – so that in step 7 above only the Access/Refresh Tokens are returned, and there’s a separate call to the Claims Provider. The spec refers to a UserInfo endpoint that would be exposed by the Claims Provider in this case.

We’re already familiar with the Access and Refresh Tokens, but the ID Token is worth looking at in a bit more detail, as it’s specific to OpenID Connect. It’s in the form of a JSON Web Token which is described as “a compact URL-safe means of representing claims to be transferred between two parties” (Remember that in this context a ‘claim’ is another word for an attribute of a user). We won’t go into the details of this format as your library will shield you from the details (go look at the spec if you’re interested), but the key part of interest to us is that it contains the claims that we’re interested in – including the identifier – in the form of a JSON string. This makes it easy to extract and use.

Here’s an example (I filched it from the spec) of what a successful response from the Authorization Server would look like in step 7 (but remember that you’re library will shield you from this).

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store   
Pragma: no-cache

{
"access_token": "SlAV32hkKG",
"token_type": "Bearer",
"refresh_token": "8xLOxBtZp8",
"expires_in": 3600,
"id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjFlOWdkazcifQ.ewogImlzcyI6ICJodHRwOi8vc2VydmVyLmV4YW1wbGUuY29tIiwKICJzdWIiOiAiMjQ4Mjg5NzYxMDAxIiwKICJhdWQiOiAiczZCaGRSa3F0MyIsCiAibm9uY2UiOiAibi0wUzZV3pBMk1qIiwKICJleHAiOiAxMzExMjgxOTcwLAogImlhdCI6IDEzMTEyODA5NzAKfQ.ggW8hZ1EuVLuxNuuIJKX_V8a_OMXzR0EHR9R6jgdqrOOF4daGU96Sr_P6qJp6IcmD3HP99Obi1PRs-3LOp146waJ8IhehcwL7F09JdijmBqkvPeB2T9CJNqeGpe-ccMg4vfKjkM8FcGvnzZUN4_KSP0aAp1tOJ1zZwgjxqGByKHiOtX7TpdQyHE5lcMiKPXfEIQILVq0pc_E2DzL7emopWoaoZTF_m0_N0YzFC6g6EJbOEoRoSK5hoDalrcvRYLSrQAZZKflyuVCyixEoV9GfNQC3_osjzw2PAithfubEEBLuVVk4XUVrWOLrLl0nx7RkKU8NXNHq-rvKMzqg"   
}

 

Note how there are the 3 tokens in the response as properties of the JSON object. The value of the ‘id_token’ property is the ID token in JWT format. It actually has 3 parts separated by a ‘.’, each of which is base64 encoded. The middle part contains the claims.

So in summary, the OpenID Connect protocol adds functionality to OAuth2 that allows a server (in this case the Authorization Server) to store and deliver identity data that the applications can use when manipulating patient information. Exactly how that server got that identity in the first place – and how it links it to the login credentials – is up to each implementation.

From the clients perspective it’s really simple:

  1. Take OAuth2
  2. Add the claims you want + a string with the value ‘openid’ when calling the Authorization Server (step 1)
  3. Get back the required data when you get the Access token (assuming everything was valid of course).

Everything else works exactly as OAuth2 does.

Easy! (well, sort of <s>). And again – at risk of being boring – use a Library!

So that’s about all we’re going to say about OpenID Connect for the moment. But there are a couple of details in this overall solution  that we haven’t really thought about yet.

  • When the user authorizes access to their data for an application, how can they do so in a granular fashion? eg that they are happy for the app to read their lab data, but not any other data, and not able to write anything?
  • Given that OpenID Connect doesn’t have a standard ‘claim’ for identifier, how do we indicate that we want it (or any other claims for that matter)?

And – how could all these bits fit together to create a secure ‘ecosystem’ of data sources and services? And, even better,  an ‘appstore’ of applications that a user can download and be confident that they will play properly in that ecosystem?

We’ll think about that next…

 

FHIR, Oauth2 and the Mobile client

In the previous discussion on OAuth2 in FHIR we discussed the ‘usual flow’ that would be used by a web based client (where the application is executed in a browser with pages and script served up by a web server). But what if the application is either a Desktop or a Mobile application? There are a couple of issues to think about:

  • For the application to ‘remember’ that who the user was, it needs to store the Refresh Token, as this will be used by the Authorization Server to issue a new Access Token (actually, so does the web client of course).
  • The process flow in OAuth2 is dependant on the use of HTTPS. It describes login screens and browser redirects for example.

Lets think about storing that Refresh Token.

In theory, (at least in OAuth2 speak), these applications are not capable of holding the Refresh Token securely. That is because the token will be stored on an end-user device (the Mobile or Desktop) and so could be stolen by a determined attacker. So, again in theory, the application can’t store the Refresh Token – it must authenticate with the Authorization Server each time the user wants to use it, and the session can only be as long as the lifetime of the Access Token (typically an hour). OAuth2 actually describes a specific workflow for this – the Implicit workflow, where the Authorization Server directly returns an Access Token after the user authenticates, but does not return a Refresh Token. It is specifically intended for ‘public’ scenarios where the user doesn’t want their credentials remembered long term (Like using the ‘Incognito’ browser window if you’re in an Internet Cafe).

That’s all well and good, but not very user friendly when it’s your own mobile device that you’re using – and unlikely to be acceptable to users. In this scenario, you have no alternative other than to store the Refresh Token locally but:

  • You must do that as securely as possible (maybe you encrypt in some way).
  • You must deal with the possibility that the app could be used by multiple users – for example to associate it with the currently logged in user.
  • You must provide an easy mechanism for the user to be able to revoke the access token. The spec doesn’t detail how this is to be done, but one possibility would be for the Authorization Server to have a ‘reset’ or ‘lost my mobile’ function. This could be a ‘normal’ web page where the user logs in (using the standard OAuth2 flow), and the Authorization Server then revokes all Refresh Tokens associated with that user. When the user uses their new device they will simply re-authenticate, and the process will continue as usual.

With that out of the way, we can now think about how the desktop/mobile application can use OAuth2. One way is for the application to launch an embedded browser window to interact with the Authorization Server, as described in the following diagram.

 

Oauth Mobile

  1. The User starts the app (presumably logging in in some way). The app launches a browser component which is directed to the ‘request login’ endpoint of the Authorization Server. The Authorization Server generates and returns a login page.
  2. The user enters their details and submits the page back to the Authorization Server. The Authorization Server authenticates the user, generates an authentication code and sends a redirects back to the embedded browser, instructing it to redirect to the application callback endpoint (which, of course, doesn’t actually exist!)
  3. The application intercepts the browser redirect (remember it is in an embedded browser component)
  4. The application extracts the authentication code and terminates the browser component.
  5. The application makes an HTTPS call to the Authorization Server, passing across the authentication code and gets back an Access Token and a Refresh Token.
  6. The Refresh Token is saved locally (in the most secure way possible)
  7. The application makes an HTTPS call to the Resource (FHIR) Server, passing across the Access Token in the ‘Authorization’ header. The Resource server validates the token and returns the data
  8. The application performs any other required business logic.

One thing we haven’t talked about is dealing with an expired Access Token (and this is the same for the web client we discussed last time). Access Tokens have a limited life span (commonly an hour – but that’s up to the Authorization Server) so what happens if the application is in the middle of doing something when it expires? Well, the flow is something like this:

  1. The application makes a request (or an update) on the Resource Server.
  2. The Resource Server recognizes that the token has expired and rejects the request. (The spec doesn’t specify the status code to use – but I presume its 401  – Unauthorized).
  3. The application retrieves the Refresh Token from its local store and sends that to the Authorization Server, which issues a new Access Token (assuming that the Refresh Token is valid of course).
  4. The application re-tries the request to the Resource Server with the new Access Token.

So, we’ve talked about how a Mobile or Desktop Application can implement standard OAuth2 functionality to authenticate themselves and grant access to their data. This is by no means the only way, but one that should be straightforward to implement.

And a reminder – don’t to this at home folks – use a library!

In the next post we’ll turn our attention to the users Identity – how does the Resource Server know who the patient is? (Hint: their login details aren’t enough…)

 

FHIR and OAuth2

I’ve mentioned before that one of the reasons I started this blog was to document for myself things I find out about FHIR so that when I forget them, I know where to go and look to find them…

Thus far I’ve focussed on the FHIR standard itself, but one thing that has become increasingly significant are some of the aspects of security – notably Identity, Authentication, Authorization and Audit. Here at Orion Health, we’re building a FHIR interface to support the uploading of glucose results from a patient device and so these are issues we need to address right now. A perfect opportunity for a post!

First let’s agree on some definitions.

  • Identity (in the IT sense) are specific attributes of an entity that identify that entity – such as name, gender, date of birth etc. Note that a single person may have multiple identities – for example I might keep separate identities for LinkedIn and my Bank even though there’s only one of me.
  • Authentication is when we ‘prove’ to some system that the Identity accessing it is who they claim to be. Generally the system will have some piece (or pieces) of information that can be used to make this assertion. For example a username and password is a common way of doing this (though increasingly insecure).
  • Authorization is about specifying and controlling access to certain types of resource. For example, that to access clinical data a user needs to be in the role of a clinician.
  • Audit is where you keep a track of what has been done in the system, about and by whom. In FHIR this is represented by the SecurityEvent resource, which is modelled on the IHE ATNA profile.

And these are all inter-dependant. For example in order to perform authorization you need to have first authenticated the identity of the person wanting to access the particular data and create an audit record when they actually do something which will link to the identity and possibly the authenticator. (I’m well aware that these are very incomplete definitions – but enough for us to move forwards).

Now, FHIR quite explicitly is not a security protocol, and for very good reason as this is really hard to do properly – and there are other groups who do this stuff and produce the required standards. It does provide ‘hooks’ where security-related considerations can be applied (e.g. tags for privacy) and some resources that are useful (such as SecurityEvent and Provenance), and when used over HTTP/S can take advantage of standards like TLS (Transport Layer Security), OAuth2 and OpenID Connect – and it is these latter two that we’re going to focus on in this series of posts.

OAuth2 is a framework that allows an application to access a users data on another server, without that application needing to have a copy of the users credentials on that system.

For example, suppose you write a simple web based application to allow a patient to access their data from a FHIR server. Here’s how your architecture might look:

oauth-1

The user is interacting with pages in their browser which have been served up by the application over the Internet. The application is server side code that is retrieving (and possibly updating) information from the FHIR server – let’s say that’s the users weight observations over time.

Now, the user will have logged into the application (maybe using a username and password), but how does the FHIR server know that it’s OK to deliver up the results for that patient?

One way would be for the user to have an account on the FHIR server, and for the Application to store those credentials and pass them across to the FHIR server when it makes the request. There are a number of problems with this approach.

  • The application now has a copy of the users credentials in it’s store thus increasing the possibility of loss to an attacker. (Weakest links in a chain and all that stuff)
  • What happens when another FHIR server needs to be accessed? Either the user has the same account details on all servers (unlikely), or the application now needs to store multiple sets of credentials for the user.
  • What happens if the user no longer wants the application to be able to access their data? The application has their account details – the user will need to change those account details on all of the FHIR servers holding data about them.
  • Every application and every server will need to store copies of every users credentials – this is going to become a real mess as applications are enabled and disabled, credential details change, and as new FHIR servers are brought into the mix. And the risk of exposure rises significantly…

OAuth2 approaches this problem by separating the role of Authentication & Authorizing, from the role of supplying information (or other services).

oauth-2 (1)

As you can see in the diagram above, there is now a separate component – the Authorization Server. It’s this component alone that holds the users credentials – the other components (the application and the FHIR server) trust the Authorization Server to identify the user and indicate if (and what) data about them can be released to the calling application. (Oh, and note that the components have specific names on OAuth2).

Commonly, the Authorization Server will be some trusted system where the user already has an account – such as Google or Amazon, but can be a separate component – or even functionality of the FHIR server.

At a high level it works something like this.

OAuth2sequencediagram

  1. The user (via their browser) makes a request to the Application (client) and the application needs to get some information from the FHIR server (Resource Server). The Application sends a ‘browser redirect’ back to the users browser, which causes the users browser to make a login request to the Authorization Server.
  2. The Authorization Server delivers a login page to the user.
  3. The user completes the login information, and agrees that the application can access their data (and can also give permission at quite a granular level for the type of information they are prepared to allow access to).
  4. They submit the page back to the Authorization Server.
  5. The Authorization Server validates the login details
  6. The Authorization Server sends another browser redirect back to the users browser, which causes it to navigate to a special endpoint on the application. The redirect contains an ‘authentication code’ that gives the users permissions to access their data.
  7. The users browser is re-directed to a callback end-point hosted by the application. The application:
    1. Calls the Authorization Server with the authentication code it was supplied (plus some other data), and the Authorization Server returns an Access Token (and a Refresh token – we’ll talk about that in a minute).
    2. The Application makes the FHIR query to the Resource Server (the FHIR Server), including the access token in the request (usually the Authorization header).
    3. The Resource Server confirms that the Access token is legitimate, fulfils the request and returns the FHIR data to the application.
    4. The application constructs the page and returns it to the users browser.

 

Some notes:

  • This is a high level summary of the process only, and there are many details omitted. Refer to the spec for details but – as we’ll talk about in a moment – you’ll almost always use a library to implement OAuth2.
  • The Resource Server that requires OAuth2 authorization is commonly referred to as ‘OAuth2 protected’.
  • The mechanism by which the Resource Server (the FHIR server) validates the Access Token is up to the implementer, and there are a number of options:
    • If the Authorization Server component is actually part of the FHIR server, then it could do it directly.
    • The FHIR server could make a separate call to the Authorization Server to confirm it
    • The Authorization Server could sign the Access Token, proving to the FHIR server that it is legitimate.
  • When the Authorization Server returns the Access Token, it also returns a separate token – the Refresh Token, which the application saves locally. The Access Token generally has limited life span – often an hour, and when it expires, the application can use the Refresh Token (which has a much longer lifetime) to get another Access Token. The reason for this complexity is that it allows the Authorization Server to revoke the Refresh Token, which will stop the application from being able to get more Access Tokens and prevent it from accessing the FHIR server after it’s current Access Token expires. Without this mechanism, the FHIR server would have to check with the Authorization Server every time to see if the Access Token had been revoked.
  • This description is a flow that is called the ‘Code’ request type, and is only applicable when the application is capable of keeping the Refresh Token secret. In this scenario it is able to do so as it is hosted on a server (this is a web application remember). If the application (client) is, say, a desktop application or a mobile application, then it cannot do this (someone could hack the device/computer and get it). In this case there is another flow – the ‘implicit’ flow that is used. We’ll talk about that in the next post.
  • The application needs to be registered with the Authorization Server separately to this flow. During this process (which is one-off) the application receives a code by which it can identify itself to the Authorization Server, and also supplies the applications callback address where the browser will be redirected after the user logs in.
  • The security of OAuth2 absolutely depends on using secure transport mechanisms – which is why Transport Level Security (TLS) such as SSL is absolutely required.
  • In addition, the details of the flow is quite complex (the description above is just a summary – there are lots of other details) and there is a strong recommendation to use one of the open source libraries that are available. There’s a list on the spec page.

Well that’s about enough for now – but there’s much more to talk about. Next time we’ll cover:

  • The Implicit flow which is used by Desktops and Mobiles
  • The OpenID Connect standard – which sits on top of Oauth2 and provides some identity functionality
  • Some thoughts on Architecture – specifically in the New Zealand realm!

And finally – I’m not a security expert – if I’ve made mistakes or if something is unclear then do let me know and I’ll make whatever corrections are required.

Some references:

The OAuth2 Specification

A post on Grahames Blog. This points out some issues with delegating authorization..

A tutorial by Jakob Jenkov

Blue Button

SMART on FHIR

FHIR transactions: the Search functionality

FHIR supports transactions by POSTing a bundle of resources to the root of a server. The server processes them separately as if they had been sent as individual requests – except that the entire group of updates either succeeds or fails as a group (which is what a transaction is, after all…)

It’s one of the more complex processing that a server has to do – particularly as it needs to manage references between resources for new and existing resources within the bundle.

But there’s one particular aspect to this, and that is what if you don’t know if the resource already exists on the server?

For example, we’re working on a project to import glucose data from a personal device. Our current thinking is to create a bundle containing a Patient resource, a Device resource and Observation resources for each of the glucose results we want to save. (We’re probably not using the DeviceObservationReport as it doesn’t add anything extra for us in this scenario).

So when the client creates the bundle, what does it use for the Patient ID – and the Device ID for that matter? (The Observations will always be new, so cid: IDs are appropriate for them). We do have an identifier for both, but that’s not the same as the ID – and having to look up the resources before putting them in the bundle kind of defeats the purpose. And if we include the resource anyway with a cid: ID , then the server will always create a new one – not what we want.

The answer to this is a couple of paragraphs in the spec that talks about how a server can manage this. Here it is (it’s in the transaction description):

The application constructing a bundle may not be sure whether a particular resource will already exist at the time that the transaction is executed; this is typically the case with reference resources such as patient and provider. In this case, the bundle should contain a candidate resource with a cid: identifier, and an additional search parameter using an Atom link:

<link href="http://localhost/Patient?[parameters]" rel="search"/>

A search link with a root of http://localhost means to search the local resource store for a match as specified in the parameters (which must conform to the servers capability for searching as specified in its conformance statement). If the search returns no matches, the server process the resource normally. If the search returns one match, the server uses this matching resource instead, and ignores the submitted resource. If more than one resource is found, the transaction SHALL be rejected.

 This means that we can include the identifier of the Patient and the Device in the bundle, and the server will locate the resource if it exists – and create it if it does not.

So here’s a sample of a patient resource in a bundle:

&lt;entry&gt;
    &lt;title&gt;Patient details&lt;/title&gt;
    &lt;id&gt;cid:patient@bundle&lt;/id&gt;
    &lt;updated&gt;2014-05-28T22:12:21Z&lt;/updated&gt;
    &lt;link href=&quot;http://localhost/Patient?identifier=PRP1660&quot; rel=&quot;search&quot;/&gt;
    &lt;content type=&quot;text/xml&quot;&gt;
        &lt;Patient xmlns=&quot;http://hl7.org/fhir&quot;&gt;
            &lt;text&gt;
                &lt;status value=&quot;generated&quot;/&gt;
                &lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;Joe Bloggs&lt;/div&gt;
            &lt;/text&gt;
            &lt;identifier&gt;
                &lt;value value=&quot;PRP1660&quot;/&gt;
            &lt;/identifier&gt;
            &lt;name&gt;
                &lt;text value=&quot;Joe Bloggs&quot;/&gt;
            &lt;/name&gt;
        &lt;/Patient&gt;
    &lt;/content&gt;
&lt;/entry&gt;

Note the <link> element where we specify that the search parameter is an identifier with the value of PRP1660. (and, of course, we have this identifier in the candidate resource – otherwise it will never work!)

This is incredibly useful! In fact it does raise the issue of whether a server claiming to be able to process transactions in its conformance statement SHALL be able to implement this functionality. I don’t think the spec is clear on this – and probably needs to be.

 

Profiling a FHIR Careplan for Immunizations

After the previous discussion on how to represent an Immunization plan, I had intended to talk about how to represent the Immunization protocol (or schedule), and how to apply that to a patients’ plan, but (as so often happens) I got a bit side-tracked, and thought we could first think about how we might create a Profile against the CarePlan resource to represent an immunization plan.

Now, we’ve talked about FHIR profiles before so I’m not going to repeat that here, other than to say that it is the profile that takes a standard resource (or resources) and makes them fit more precisely to a particular Use Case of some sort, so profiling CarePlan to support an Immunization plan seems a perfect fit!

There’s actually quite a lot to do to get a good profile (and I’m sure I haven’t got it completely correct), but at a high level, once you have your use cases you go through the resource and:

  • Alter multiplicity of elements (e.g. to make an optional element required or not used. Remember you can’t make a required element optional though)
  • Specify a particular terminology or codeset for a binding (and often you’ll define a ValueSet to hold the list of allowable codes)
  • Add a new element (as an extension) where it’s needed by the Use Case but not defined in the base resource

(There’s other things you can do as well, but that’s enough for our current needs).

Overall design

Here’s the structure of the Care Plan resource (Sorry about the image quality – its a screen scraping from my computer – here’s the original)

Screen Shot 2014-05-27 at 1.48.31 pm

How we’ll structure our profile overall is to use the goal element to list the diseases that this plan is intended to protect against (using an extension), then optionally link the activities back to the goals/diseases that are covered by that vaccine. That way we can say which activities contribute to protection against which disease. We’ll make this all optional, as not everyone will want to go to this level of detail (and, in truth, is probably a bit overkill). (btw – Another way of doing the same thing would be to add extensions to the activity directly to indicate the diseases being covered)

The activity will generally be the administration of a vaccine, however we’ll keep it a bit flexible to allow for, say, educational sessions as part of the plan. We’ll know which activity is an vaccination because the activity.simple.category will have the value ‘drug’.

We only need to profile the CarePlan resource for this Use Case.

For simplicity, we’ll put all the extensions in the same profile – in real life we might try to re-use existing extensions or to make some of ours a bit more general. This will really need common registries of profiles, which the FHIR team are working on at the moment…

Layout

So the following section lists the changes (in a textual format) that we’ve going to make to the CarePlan resource to allow it to hold the information required to represent an immunization plan. Each entry has the path to the element, followed by the multiplicity (which may not be the same as that in the base resource). If the entry is an extension (and remember that extensions can be at any level in a resource) then the path will end in a #{extensionDef code} and the data type will be displayed. Thus:

Careplan.goal#disease (0..1) CodeableConcept

means that this extension is against the goal element of the CarePlan resource, that it’s optional and there can only be one, and that the datatype is CodeableConcept.

If the values of the element (extension or base element) come from a ValueSet, then we’ll place the name of the ValueSet in square brackets at the end. Eg

Careplan.Activity.simple.code (1..1) [immunizationVaccines]

means that the value of the code (which is required in this profile) comes from the valueset whose name is immunizationVaccines. (Actually, this should really be a complete URI, but we’ll keep it simple to avoid cluttering the page). Note that this layout is just the one we’re using for this post – the formal representation will be the profile resource and maybe other artifacts that the core team is working on.  (Actually, I think the next connectathon is going to focus on profiles and conformance)

So, here we go. To minimize space, we’ll only include entries that are different to the base resource, or where there’s a particular comment to make.

We’ll also do this in 2 posts. This one will be more of a ‘design’ session – what we’re going to do and why. The next post will build the actual profile.

Items in the Profile

Careplan#protocolDescription (0..1) string

An extension where we can describe the protocol that this plan was drawn from.

Careplan#protocolUri (0..1) uri

A direct link to the protocol (if known).

CarePlan.patient (1..1)

You have to have a patient in our plan…

Careplan.patient#dob (0..1) date

The patients Date of Birth. This is a bit naughty as you should really retrieve the Patient resource and look up the Date of Birth from there. The reason to include it here as an extension is to allow us to be able to determine if an immunization is overdue just on the information in the plan. In reality, you probably woudn’t do this but, hey, its my blog and I’ll de-normalise if I want to

Careplan.period (0..1)

This would be the age range over which the immunization plan was active – eg from birth to 18 years

Careplan.modified (1..1)

This should be updated when the plan itself is changed – ie a new activity is added, to the status of an existing activity changed. The multiplicity is changed to make it required.

Careplan.concern (0..0)

As this plan is specifically about immunizations, the concern is not needed – i.e. the whole point is to avoid the patient getting those conditions. Change the multiplicity to 0..0 to remove it.

Careplan.goal#disease (0..1) codeableconcept

We’ll use the goal to list the diseases that this plan is intended to protect against, then optionally link the activities back to the goal. That way we can say which activities (immunizations) are protecting against which diseases. Note that there’s only a single disease per goal, but multiple goals per plan.

Careplan.goal.status (0..0)

The overall status of the plan is at the careplan level, not the goal.

Careplan.goal.concern (0..0)

Same notes as for careplan.concern.

Careplan.Activity#immunization (0..1) ResourceReference

An extension that references the Immunization reference. Should be populated when the vaccine is either given or offered and declined.

Careplan.Activity.goal (0..*)

To allow us to link this activity back to the goals (the diseases this vaccination is intended to protect against) if we want to.

Careplan.Activity.status (0..1)

Really the only codes that makes sense here are ‘completed’ and ‘cancelled’. However, this is a fixed code set in FHIR so we can’t change it. If due then we can either leave it blank, or we could use the value ‘not started’ or ‘scheduled’

Careplan.Activity.actionResulting (0..0)

Don’t want to record this in the plan

Careplan.Activity.detail (0..0)

The careplan.activity.notes can capture the level of detail that may be required, so this is not required.

Careplan.Activity.simple.category (1..1)

Fix the value as ‘drug’. We might want to suggest that ‘immunization’ be added as an option to the base resource.

Careplan.Activity.simple.code (1..1) [immunizationVaccines]

This will be the vaccine that is to be given so is mandatory. The value is drawn from the immunizationVaccines ValueSet

Careplan.Activity.simple.code#numberOfDoses (0..1) integer

The number of doses of this vaccine that are going to be given altogether

Careplan.Activity.simple.code#doseSequence (0..1) integer

The sequence number for this dose

Careplan.Activity.simple.timing (1..1) Period

Fix to use a period and make required. The ‘start’ property is required. ‘end’ is optional. This will be the period over which the vaccine must be given to be effective.

Careplan.Activity.simple.location (0..0)

No need to specify location in the plan

Careplan.Activity.Simple.product (0..0)

This information is in the code

Careplan.Activity.simple.dailyAmount (0..0)

 Not needed

Careplan.Activity.simple.quantity (0..0)

Not needed

So now that we have an idea about what our profile is going to look like, we can build the profile itself. That will be the topic of the next post!

 

FHIR Questionnaires and exchanging information

I had an interesting conversation on the skype ‘Implementers chat’ last week.

Like many others who are attending the FHIR connectathon in a couple of weeks, I’ve been working on scenarios involving  the Questionnaire resource (I’m writing a ‘template designer’ as well as a ‘form renderer’), and it occurred to me that it would be good to be able to share completed Questionnaires with others. I saw a similarity with sharing documents, and so asked the question whether the DocumentReference resource should be extended to permit the discovery and sharing on questionnaires as well.

Lloyd commented that his view was that the Questionnaire should not be a primary means of data exchange – that other resources are intended for that purpose, and that the Questionnaire doesn’t have ‘primacy’ over other resources. Grahame also commented that if you did wish to share a completed Questionnaire, then do so as you would any other resource.

That got me thinking.

A few years ago in New Zealand we developed a standard called the ‘Forms Server’ standard. The basic idea was that we would have a standardized way of representing a form template that could be shared between those needing to complete a form, and those needing the structured data within the completed form. A good example of this (and the one we were working with at the time) was a referral: in most cases when referring a patient to a specialist, there is data that is specific to that specialty that needs to be collected – a referral to a cardiologist will have different elements than one to a dermatologist.

We envisaged a central server that held these templates, and a standard that described their structure, plus the completion and transmission of the completed form. Potential referral recipients would upload templates containing the information they needed, and people making referrals would select the appropriate template to make the referral with. (We weren’t thinking of service discovery – just the templates and forms, and the manner that they could be shared). There was an associated standard way of getting data from referring systems (in our case primary care system), and another standard way of representing the actual referral, so in summary 3 major components:

  • Template structure and discovery (local standard)
  • Template pre-population (local standard)
  • Referral form and workflow (based on HL7 v2).

In the end, although the standards were implemented, it didn’t go the way we hoped. It was more of an implementation architecture than a standard, and because they were local to New Zealand, they were only ever implemented by one vendor (though the primary care vendors who were creating the referrals hosted the components and made a local pre-population service available), and so the only way of creating and updating templates was through that vendors system – as was the dissemination of the referrals.

We had achieved the goals of a particular project, but not in a way that encouraged other vendors to join the market, nor established any kind of re-use for other purposes.

So, fast forward to FHIR.

With the Questionnaire resource (and the Referral resource that is being worked on at the moment), there are now the pieces need to fulfil our vision in a truly standardized way. (In the following discussion, do remember that the Questionnaire resource fills the purpose of both template and completed form, using the status property to distinguish between them).

Template Structure and discovery is, of course, the Questionnaire resource and the standard FHIR query mechanisms. I think that there need to be a couple of extra fields to support discoverability – so you can categorize Questionnaire templates and search for specific ‘types’ of template. There are some proposals that we’ll discuss in Phoenix, but we can always use extensions if we need to.

Pre-population needs a couple of things. First the template needs a coded way of describing what information is needed, and the ‘filling’ system needs to be able to locate that data within it’s own data stores.

Well, each question in the Questionnaire has a ‘name’ property of type CodeableConcept, so all that’s needed for that is a standardized terminology like SNOMED, and possibly agreement between users about which codes to use – SNOMED is vast, and not that easy to navigate after all. So, a ValueSet containing the common terms (like usual medications, conditions, allergies etc.) to use might make sense.

In fact, the Questionnaire also allows us to use ValueSets to directly define the options for a question.

We might also want to describe the relationship between entries in a Questionnaire, and other FHIR resources or CDA documents to make it easier for implementers to convert between the two.

And the Referral resource is the final piece for this particular use case.

So we can imagine a workflow like this:

  1. The user locates the Questionnaire Template they wish to use
  2. The system downloads the Questionnaire and pre-populates it from the local data store based on the individual question.name entries.
  3. The user enters the remainder of the data described in the Questionnaire.
  4. When complete, the system constructs a Referral resource, and likely a number of other resources (like MedicationStatement, Observation, Condition) which are placed in a bundle and sent to the recipient using any of the described paradigms of exchange.

This workflow (and the implementing systems) is completely generic, and can be re-used in any other scenario where there is some standardized form of data collection and exchange – like routine assessments, progress notes, discharge summaries etc.

It’s important to appreciate that we’re using the Questionnaire as the interface between the user and the local system – not as the unit of exchange between systems, which would use resources specific to each purpose. In this case a Referral resource, but another example could be a Discharge Summary, which is a document. (I’ve glossed over some of the details of this exchange, which actually uses other resources like Order and OrderResponse, and of course there are security /privacy considerations as well.)

In fact, if we were half way clever we’d also re-use any appropriate FHIR server. For example, we’re currently looking at creating a ‘Record Locator Service’ (which could be a FHIR server hosting DocumentReference resources) – why not use the same infrastructure to store the Questionnaire templates?

So I’m starting to think that the Questionnaire could replace the List as my favourite FHIR resource.

Let’s see what happens in Phoenix…

 

 

 

 

Tamper resistant auditing in FHIR

Had an interesting chat with a colleague here at Orion (Richard) about audit events and signing them, and how to be sure that they haven’t been tampered with (which is apparently a Meaningful Use requirement) so have made a note here for when I forget, as this is an area that I’m not that familiar with. I should also say that there are doubtless other ways of doing this and real life implementations really need to be done by those who are experts – it’s been said that poor security is worse than no security at all…

FHIR provides a specific resource – the SecurityEvent resource – which records some event of importance. This could be anything from creating, reading, deleting or updating other resources, and can serve as the basis for an audit log to answer questions like ‘who has accessed my record’. The resource is based on the IHE ATNA profile, and indicates ‘who‘ did ‘what’ and ‘when’.

The spec states that a server that stores SecurityEvents should not allow them to be updated or deleted – which makes sense for something related to audit – but for the truly paranoid, how can you know this? How can you know that a SecurityEvent resource has not been deleted or modified?

To answer this question, we need a bit of background on some security terminology.

  • A cryptographic hash is a string (sometimes called a digest) that is generated from a source (like a FHIR resource) using some hash function. SHA-256 is an example. The digest that is generated by the function will always be the same when the input is the same, but it is not possible (or more accurately is unfeasible) to regenerate the original from the digest. So, if you have a digest and the original, then you can be sure that the digest ‘matches’ the original by creating a digest yourself and checking that they are the same – kind of like a fingerprint identifies an individual person.
  • Public Key cryptography is a system where there are 2 ‘keys’ that are linked mathematically. It’s possible to encrypt something (producing a signature) with one of the keys, and then to re-generate the original using the other key (So this is ‘two-way’ – unlike the hash which is ‘one-way’). One of the keys is kept secret (the private key) and the other one is shared (a public key). Assuming that you have my public key, then I can encrypt a message with my private key and send it to you. You can use my public key to decode it – and you know that it came from me. The public key is often called a certificate.
  • Of course, for the above to work, you need to be able to trust that you have got my public key – and not that of someone else. This is where a certificate authority (CA) comes in. This is an organization that we both trust which asserts that a particular key pair (public and private) actually belongs to a given person (or organization / thing). You prove to the CA that you are who you say you are, and the CA signs your public certificate with their own one. So – if you get a message encrypted with my private key, and you have my public key (certificate), then you can check that the CA has validated that certificate, and then use it to decode the message. Provide you trust the Certificate Authority, then all is well. The infrastructure that supports all this is referred to as Public Key Infrastructure – or PKI.

Just a couple more things before we move on:

  • The computations required to sign and decode messages using public & private keys can take a long time to run with large input files. What people commonly do is to take a message, produce a hash from that and then sign the hash.
  • This discussion is all about proving who (or what) created a resource – the integrity of that resource. The actual encryption of the contents is a separate discussion.
  • It can get a lot more complicated than this! This is not a simple area…

So with the basics behind us, how can we be sure that our SecurityEvent resources have not been tampered with? One way is to use a signed Provenance resource that refers to the SecurityEvent. This would be created by the same application that created the SecurityEvent.

A key assumption to make is where do we get the signers public key (certificate) from? There are a number of options for this, and the details will vary for each particular implementation (as far as I am aware there is no intention for FHIR to move into this space).

One possibility is to actually store the certificate in the signature itself! This sounds silly, but the idea is that the certificate is signed by a Certificate Authority. So, if we trust that Authority, then we trust that they assigned it to the right person. We’ll use that for the example below.

This process would go something like this.

  1. The application creates and saves the SecurityEvent resource.
  2. Next, it creates a Provenance resource. The provenance resource will refer back to the SecurityEvent resource (Provenance.target).
  3. Next generate a Hash of the SecurityEvent resource.
  4. Create a signature by encrypting the Hash using the systems private key, and include the public key (certificate) in the signature, which is then saved in the Provenance resource. (Provenance.integritySignature).
  5. Save the Provenance resource.

All of the above steps would need to be executed on the server in a single transaction.

So, with that in place how can I check that a particular resource hasn’t been altered? Well:

  1. Retrieve the SecurityEvent resource I want to check
  2. Generate a Hash of the resource using the same hash function as before.
  3. Retrieve the Provenance resource that refers to that SecurityEvent. (This would be a query against Provenance – /Provenance?target={ SecurityEvent.ID} ). The element Provenance.integritySignature has the signature generated by the system that signed it.
  4. Extract the signers Certificate from the signature, and make sure that the issuing CA (in the Certificate) is one that I trust. (Actually, there can be a whole chain of CA’s – but let’s not go there…)
  5. Using that Certificate, decode the rest of the signature. This will produce the original Hash, which should be the same as the one generated in step 2. If they match, then you can be sure that the SecurityEvent is the same as when it was signed. If not, you have a problem.

So what stops the bad guy from altering the signature in the Provenance? Well, they don’t have the private key of the signing system, so can’t encode the hash in a way that can be decoded by the public key (certificate) of the signing system. If they do put their own certificate in, then the CA to which it refers (and that I trust) tells me it was someone else.

The whole thing depends on being able to keep the private key of the signing system secret, and having certificates – maybe from a CA – that we both trust.

Couple of questions that come to mind.

Why not just sign the SecurityEvent resource and add the signature to it directly? Well, the problem with that is that the signature is ‘signing’ the whole SecurityEvent resource. If the digest is a part of that resource, then you have to generate the digest and then add it to the resource and then… oh wait … we’re just modified the resource and therefore the digest is invalid…

How do you know if a SecurityEvent has been deleted? After all, the bad guy could delete both SecurityEvent and Provenance? One way around this is for each SecurityEvent to refer to the previously created SecurityEvent in a chain (You’d need an extension for this in FHIR). That way you can ‘walk the chain’ to make sure there are none missing – and provided you check each SecurityEvent as you go, you know that the bad guy hasn’t changed any of the links.

Security is a complex topic, and this merely scratches the surface of what is required to implement correctly – it’s a high level overview and not an implementation guide.  To understand more – John Moehrke is one of the acknowledged experts in this space in healthcare.

Fixed code sets in FHIR

One of the common use cases for FHIR is going to be to create a ‘proxy’ or ‘façade’ FHIR server that provides a FHIR interface against an existing data source – and this is what we are doing at Orion Health in front of our Patient Administration System. (We’re actually building a rather cool mobile medication administration app – hope I’m allowed to say that!).

One of the issues we’ve come across is where there is a property with datatype of code and a fixed set of code values (a codeset) – such as  MedicationPrescription.status.

The problem we are facing is that in the ‘back end’ system, this field is a configurable set of values – and it often won’t match the values defined in FHIR. This is bad enough for optional properties, but even worse when the property is a required one – such as MedicationAdministration.status.

Now there are very good reasons why the workgroup that designed these resources have made the codes a fixed set – they are important properties that a recipient must understand to safely use the resource – and we are about interoperability after all. But it’s still a problem we need to solve.

I can think of a few possibilities for us.

 Option 1 is to change the codeset in the backend system to that defined in the FHIR resource – perhaps a bit unlikely…

 Option 2. Ideally, every value in the back end system can be mapped to one of the values from the fixed code set – with an acceptable loss of fidelity. Then, you can create an extension to hold the original value. In this way a recipient who doesn’t understand your custom codeset can still safely process the resource (using the defined property) but internal clients can apply whatever extra logic is supported by the enhanced set in the extension.

For example, the MedicationPrescription resource has a status property of which one of the values is ‘on-hold’. If the backend system has a number of values that could mean this (on-hold due to an operation, on-hold due to other therapy, on-hold because the pharmacy has run out) then you can set the status value to on-hold, and the extension can contain the more precise value. Incidentally, this might be one of the few places where a Coding datatype is appropriate.

 Option 3. If you have values that simply cannot be mapped in this way then you can use a modifierExtension on the property itself to carry the extra value. This severely reduces the interoperability of your resource of course, as a recipient must be able to understand and process your custom value – and should reject the resource if it cannot.

(It may not have been obvious that resource properties can have extensions as well as the resource themselves – check out the spec for more details. In fact, it’s possible to extend a datatype if you need to!)

If you do find yourself in this position, it would pay to ask a question through one of the various FHIR support channels – you might get advice about a value you can use, or if your use case is compelling enough the codeset can be extended in FHIR itself.

 So there you go. A price of being interoperable is that you have to conform to a set of agreed behavior and content – and sometimes that means compromise. Hopefully there will always be some way to achieve the business requirements, and especially in this DSTU phase of FHIR changes to the spec are quite possible – if there is a good enough reason.

Modelling Encounters with FHIR

One of the tasks I’ve been given here at Orion is to look at our internal data stores, and see how we could create a FHIR based service layer that would allow external (and internal) consumers to interact with that store.

I started with the Encounter service, and things went pretty well – but I realized that if this was to make sense to other BA’s (Business Analysts) and Developers then I needed to draw the bigger picture – how the resources would relate to each other when representing an encounter – it’s not quite as simple as it might seem!

The biggest change in thinking is going to be thinking in terms of multiple resources. If we want to retrieve the details of a single encounter, then we’re not going to just call the service and get back a single object that represents the encounter and all the properties of that encounter – rather we’re going to get back an ‘object graph’ – a number of resources related to that encounter contained within an atom bundle.

The client could request that the server return the referenced resources by using the _include parameter to specify the resources that it would like to be included in the returned bundle. (It could also get back just the Encounter and then make separate calls for all the referenced resources – but that’s a bit klutzy, and there are other problems that we’ll talk about in a minute).

I’ve used the New Zealand situation for this discussion to demonstrate the process I used: the actual relationship of entities will be different in different realms of course (HL7 uses the term ‘realm’ to refer to different countries).

OrgLoc

So, from the diagram we can see that:

  • The hospital organization is part of a DHB (District Health Board)
  • A primary care centre is in the catchment area of a hospital (this needed an extension).
  • The hospital location has a number of ward locations
  • An encounter at a hospital refers to the hospital Organization as the service provider, and has relationships within a number of wards within that hospital. It also has relationships with the patient and one or more participants
  • A primary care encounter has exactly the same sort of relationships  – albeit with different resource instances. (Just for fun, I associated the Primary Care Clinician with the hospital encounter to show that the GP was also involved in the patients care while in hospital. This is not uncommon in New Zealand – or at least it was when I practiced medicine a few decades ago…)

So what does this look like on the wire?

Well the snippet below shows a hospital encounter, with the resources within a bundle and appropriate  references between resources for the (It’s not complete of course – in fact there’s just enough in there to allow it to validate).

&lt;feed xmlns=&quot;http://www.w3.org/2005/Atom&quot;&gt;
    &lt;title&gt;Encounter Resource with all related Resources as separate resources&lt;/title&gt;
    &lt;id&gt;urn:uuid:500bee81-d973-4afe-b592-d39fe71e38&lt;/id&gt;
    &lt;updated&gt;2013-05-28T22:12:21Z&lt;/updated&gt;     &lt;!-- Time the bundle was built --&gt;
    &lt;!-- Author is required by the Atom spec. FHIR doesn't use it--&gt;
    &lt;author&gt;
        &lt;name&gt;Harold House Surgeon&lt;/name&gt;
    &lt;/author&gt;

    &lt;entry&gt;
        &lt;title&gt;Encounter Resource&lt;/title&gt;
        &lt;id&gt;cid:encounter1&lt;/id&gt;
        &lt;updated&gt;2013-05-28T22:12:21Z&lt;/updated&gt;
        &lt;content type=&quot;text/xml&quot;&gt;
            &lt;Encounter xmlns=&quot;http://hl7.org/fhir&quot;&gt;
                &lt;text&gt;
                    &lt;status value=&quot;additional&quot;/&gt;
                    &lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;An encounter at Ward 10 Middlemore hospital,
                    for John Cardinal from 1st to 3rd January 2014. Dr Marcus Welby attending.&lt;/div&gt;
                &lt;/text&gt;
                &lt;status value=&quot;finished&quot;/&gt;
                &lt;class value=&quot;inpatient&quot;/&gt;
                &lt;subject&gt;
                    &lt;reference value=&quot;cid:patient&quot;/&gt;
                    &lt;display value=&quot;John Cardinal&quot;/&gt;
                &lt;/subject&gt;
                &lt;participant&gt;
                    &lt;individual&gt;
                        &lt;reference value=&quot;cid:doctor&quot;/&gt;
                        &lt;display value=&quot;Dr Marcus Welby&quot;/&gt;
                    &lt;/individual&gt;
                &lt;/participant&gt;
                &lt;period&gt;
                    &lt;start value=&quot;2014-01-01&quot;/&gt;
                    &lt;end value=&quot;2014-01-03&quot;/&gt;
                &lt;/period&gt;
                &lt;location&gt;
                    &lt;location&gt;
                        &lt;reference value=&quot;cid:ward&quot;/&gt;
                        &lt;display value=&quot;Ward 10&quot;/&gt;
                    &lt;/location&gt;
                    &lt;period&gt;
                        &lt;start value=&quot;2014-01-01&quot;/&gt;
                        &lt;end value=&quot;2014-01-03&quot;/&gt;
                    &lt;/period&gt;
                &lt;/location&gt;
                &lt;serviceProvider&gt;
                    &lt;reference value=&quot;cid:hospital&quot;/&gt;
                &lt;/serviceProvider&gt;
            &lt;/Encounter&gt;
        &lt;/content&gt;
    &lt;/entry&gt;

    &lt;entry&gt;
        &lt;title&gt;Patient details&lt;/title&gt;
        &lt;id&gt;cid:patient&lt;/id&gt;
        &lt;updated&gt;2013-05-28T22:12:21Z&lt;/updated&gt;

        &lt;content type=&quot;text/xml&quot;&gt;
            &lt;Patient xmlns=&quot;http://hl7.org/fhir&quot;&gt;
                &lt;text&gt;
                    &lt;status value=&quot;additional&quot;/&gt;
                    &lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;John Cardinal&lt;/div&gt;
                &lt;/text&gt;
            &lt;/Patient&gt;
        &lt;/content&gt;
    &lt;/entry&gt;

    &lt;entry&gt;
        &lt;title&gt;Hospital&lt;/title&gt;
        &lt;id&gt;cid:hospital&lt;/id&gt;
        &lt;updated&gt;2013-05-28T22:12:21Z&lt;/updated&gt;

        &lt;content type=&quot;text/xml&quot;&gt;
            &lt;Organization xmlns=&quot;http://hl7.org/fhir&quot;&gt;
                &lt;text&gt;
                    &lt;status value=&quot;additional&quot;/&gt;
                    &lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;Middlemore Hospital&lt;/div&gt;
                &lt;/text&gt;
            &lt;/Organization&gt;
        &lt;/content&gt;
    &lt;/entry&gt;

    &lt;entry&gt;
        &lt;title&gt;Location (ward)&lt;/title&gt;
        &lt;id&gt;cid:ward&lt;/id&gt;
        &lt;updated&gt;2013-05-28T22:12:21Z&lt;/updated&gt;

        &lt;content type=&quot;text/xml&quot;&gt;
            &lt;Location xmlns=&quot;http://hl7.org/fhir&quot;&gt;
                &lt;text&gt;
                    &lt;status value=&quot;additional&quot;/&gt;
                    &lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;Ward 10&lt;/div&gt;
                &lt;/text&gt;
            &lt;/Location&gt;
        &lt;/content&gt;
    &lt;/entry&gt;

    &lt;entry&gt;
        &lt;title&gt;Practitioner&lt;/title&gt;
        &lt;id&gt;cid:doctor&lt;/id&gt;
        &lt;updated&gt;2013-05-28T22:12:21Z&lt;/updated&gt;

        &lt;content type=&quot;text/xml&quot;&gt;
            &lt;Practitioner xmlns=&quot;http://hl7.org/fhir&quot;&gt;
                &lt;text&gt;
                    &lt;status value=&quot;additional&quot;/&gt;
                    &lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;Dr Marcus Welby&lt;/div&gt;
                &lt;/text&gt;
            &lt;/Practitioner&gt;
        &lt;/content&gt;
    &lt;/entry&gt;

&lt;/feed&gt;

The query that would retrieve this bundle looks something like this:

GET [root]/Encounter?subject={patientID}&_include=Encounter.subject&_include=Encounter.Location...

Now there’s actually an issue with this representation, and it relates to our old friend the resource identity.

In this bundle all the resources are identified as ‘cid:’ references – meaning that they don’t refer to ‘real’ resources back on the server. (Because our back end is not fully FHIR enabled (yet), we’ve constructed the resources ‘on the fly’ from the data in the Encounter when the query was processed).

But what should the recipient do with the bundle if it wanted to save it locally? It would need to create a new resource locally for each one in the bundle. What would happen if the client made 2 separate queries for encounters, and each encounter referred to the same patient? The client would have no way of knowing this, and so would create another resource for the same patient.

So the more correct way to deal with this is to ‘contain’ the unidentified resources within the Encounter like this:

&lt;feed xmlns=&quot;http://www.w3.org/2005/Atom&quot;&gt;
    &lt;title&gt;Encounter Resource with all related Resources as contained resources&lt;/title&gt;
    &lt;id&gt;urn:uuid:500bee81-d973-4afe-b592-d39fe71e38&lt;/id&gt;
    &lt;updated&gt;2013-05-28T22:12:21Z&lt;/updated&gt;     &lt;!-- Time the bundle was built --&gt;
    &lt;entry&gt;
        &lt;title&gt;Encounter Resource&lt;/title&gt;
        &lt;id&gt;cid:enc1&lt;/id&gt;
        &lt;updated&gt;2013-05-28T22:12:21Z&lt;/updated&gt;
        &lt;content type=&quot;text/xml&quot;&gt;
            &lt;Encounter xmlns=&quot;http://hl7.org/fhir&quot;&gt;
                &lt;text&gt;
                    &lt;status value=&quot;generated&quot;/&gt;
                    &lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;An encounter at Ward 10 Middlemore hospital,
                        for John Cardinal from 1st to 3rd January 2014. Dr Marcus Welby attending.&lt;/div&gt;
                &lt;/text&gt;
                &lt;contained&gt;
                    &lt;Patient id=&quot;patient&quot;&gt;
                        &lt;name&gt;
                            &lt;text value=&quot;John Cardinal&quot;/&gt;
                        &lt;/name&gt;
                    &lt;/Patient&gt;
                &lt;/contained&gt;
                &lt;contained&gt;
                    &lt;Practitioner id=&quot;doctor&quot;&gt;
                        &lt;name&gt;
                            &lt;text value=&quot;Dr Marcus Welby&quot;/&gt;
                        &lt;/name&gt;
                    &lt;/Practitioner&gt;
                &lt;/contained&gt;
                &lt;contained&gt;
                    &lt;Location id=&quot;ward&quot;&gt;
                        &lt;name value=&quot;Ward 10&quot;/&gt;
                    &lt;/Location&gt;
                &lt;/contained&gt;
                &lt;contained&gt;
                    &lt;Organization id=&quot;hospital&quot;&gt;
                        &lt;name value=&quot;Middlemore Hospital&quot;/&gt;
                    &lt;/Organization&gt;
                &lt;/contained&gt;
                &lt;status value=&quot;finished&quot;/&gt;
                &lt;class value=&quot;inpatient&quot;/&gt;
                &lt;subject&gt;
                    &lt;reference value=&quot;#patient&quot;/&gt;
                    &lt;display value=&quot;John Cardinal&quot;/&gt;
                &lt;/subject&gt;
                &lt;participant&gt;
                    &lt;individual&gt;
                        &lt;reference value=&quot;#doctor&quot;/&gt;
                        &lt;display value=&quot;Dr Marcus Welby&quot;/&gt;
                    &lt;/individual&gt;
                &lt;/participant&gt;
                &lt;period&gt;
                    &lt;start value=&quot;2014-01-01&quot;/&gt;
                    &lt;end value=&quot;2014-01-03&quot;/&gt;
                &lt;/period&gt;
                &lt;location&gt;
                    &lt;location&gt;
                        &lt;reference value=&quot;#ward&quot;/&gt;
                        &lt;display value=&quot;Ward 10&quot;/&gt;
                    &lt;/location&gt;
                    &lt;period&gt;
                        &lt;start value=&quot;2014-01-01&quot;/&gt;
                        &lt;end value=&quot;2014-01-03&quot;/&gt;
                    &lt;/period&gt;
                &lt;/location&gt;
                &lt;serviceProvider&gt;
                    &lt;reference value=&quot;#hospital&quot;/&gt;
                    &lt;display value=&quot;Middlemore Hospital&quot;/&gt;
                &lt;/serviceProvider&gt;
            &lt;/Encounter&gt;
        &lt;/content&gt;
    &lt;/entry&gt;

&lt;/feed&gt;

This makes it explicit to the recipient that the contained resources only have meaning within the context of the containing Encounter. (And note that the contained resources no longer have a <text> element – the spec discourages this).

As we progress with FHIR, we’ll be able to start to expose ‘real’ resources (with proper identities). For example, once we have a Patient resource endpoint available, then we won’t have to contain the Patient resource in the Encounter – it will be an ‘identifiable’ resource with a ‘real’ URL that points to it – and we can choose to include the resource in the bundle or not. This does mean that the representation will change over time with resources moving from being contained to being ‘real’ resources in their own right, but that’s something that comes with being a FHIR client.

Of course this interface is actually more complex than the one it replaced, and it will take more work server-side to produce it that the previous custom version did. Where there was a single ‘object’ representing the encounter, we now have an object graph. But – and it is an important but – our interface can be used by any FHIR compliant system, which means that we can support – and promote – an international ecosystem of health information exchange.

Further, much of the complexity can be hidden behind libraries that are freely available (or we can write our own if we want to). It’s clean and easy to understand. And, finally, we have a design than has been created by some of the smartest minds in healthcare IT – and it didn’t cost us anything!

What’s not to like?

More comments on FHIR documents

With the emphasis on Documents in the coming connectathon, there have been a few questions on the skype chat about that. I suspect that most people who plan to attend connectathon will be monitoring that conversation (if not then you really should!) but just thought I’d post some of them here in case they got missed.

Read more of this post