Implementing SMART on FHIR in an EHR

We’ve talked about SMART and OAuth2 before, but it was a little while ago and it was in the context of what SMART is about and how it worked (with the odd bit of sample code thrown in). This post takes a slightly different perspective by looking at SMART from the perspective of an EHR (Electronic Health Record) developer tasked with implementing a SMART interface to an EHR– what are they things that they need to consider adding SMART Interfaces ?

We’ll take a slightly roundabout way of doing that by starting with a brief overview and some key points about SMART, then diving into the details of the steps involved in a sample implementation. That’ll be enough for this post, then in a follow up we’ll take a closer look at the issues that our EHR developer will need to resolve. And later on, we’ll switch to the clients perspective.

So what is SMART?

Basically, SMART fulfills the use cases around supporting small, independent applications that want to provide specialized functionality in the context of an EHR (Electronic Health Record). The example often given is that of an application that displays pediatric growth charts. Launched from within the context of an EHR, it can pull data from the EHR to display charts of a childs development progress and potentially interact with other services to provide customized services such as decision support to the end user. In effect, it adds this functionality to that of the EHR to give a richer experience to the clinician – something that Wes Richell has called ‘sidecar’ applications.

To do so, it takes a number of well accepted existing standards – both within healthcare and more generally in the Internet community, and creates a profile of those standards – a pattern of use – to meet that need.

These standards are:

  • FHIR. Describes the content of data being exchanged, and the query API to be used. FHIR is Health specific.
  • OAuth2. An Authorization framework widely used outside of health – particularly allowing users to permit client applications (like mobile and web) to access their data. It permits the authentication (are you who say you are) and the authorization (what are you allowed to to) processes to be delegated to separate  ‘Authorization Servers’ so that the application supplying the data (the ‘Resource Server’) doesn’t have to do this – it can delegate that to the Authorization server.
  • OpenID Built on top of OAuth2, this supports user identity – personal information about the user to be given to the client application.

The huge value of this  standards based approach is that the specialized application can then work against any EHR that supports the SMART interface, in effect helping to establish the ecosystem of health care applications that will support innovation across the healthcare sector by creating a market place for application developers to exploit specific niches.

To understand how SMART works, we need to describe the main components – the building blocks – that we will use.

The Client application is the SMART enabled application that wishes to access data from the EHR – or to update it – in order to perform its specialized function (displaying growth charts in this example). While it uses (and potentially updates) EHR based data – there is nothing that prohibits it getting data from other sources, or calling on other sources (like immunization protocols) as it performs its functions.

The EHR, or Electronic Health Record is the system which holds the information that the client application wishes to access, and Is responsible for managing access to it as well as it’s overall integrity. It can be considered as the Custodian of the data. In many (but not all) of the Use Cases, the client application will be invoked from within the EHR. Part of the work in developing a SMART EHR interface is creating a way to actually invoke the client application – a process that we’ll talk about a bit later on

In SMART speak, the EHR has a number of sub-components that are separately defined in the underlying OAuth2 spec, but are managed by (and are really a part of) the EHR application. These are:

  • The Resource Server. This is the component that holds the desired data and is responsible for delivering / updating it. In SMART, this is an implementation of a FHIR server. The more fine-grained privacy will likely be implemented by this component (Again, we’ll talk about this a bit later on).
  • The Authorization Server is the component responsible for confirming that the user of the client application is who they say they are (a process called Authentication for the geeks out there), and for determining the precise access that a given user can have to a given patients records (Authorization).
  • The Conformance Endpoint is part of FHIR, and allows a client to discover the capabilities of the FHIR Server.

Here’s a picture of how these components interact.

arch

Note that making these components part of the EHR simplifies the security model in this version of SMART. Note that the terms ‘Authorization Server’ and ‘Resource Server’ are logical terms – physically they may be the same application.

It is also entirely possible that future versions will support a more distributed model, where – for example – the Authorization Server sits outside of the EHR, and manages the access rights to multiple EHRs. This is something that the base OAuth2 specification supports, but it is complex (and therefore expensive) to implement.

Small steps!

So, as promised, let’s jump right into the detail of how all this works in a specific Use Case. The example that we’ll use is where there is a vendor who has created the worlds best paediatric growth chart application. Their application is web based, and so can be started simply by calling a specific Url from within our EHR.

Our user (a clinician) is using the EHR. They will select a patient, and then click a button labeled ‘Growth Chart’. After a short (hopefully) delay, a window will open with the Growth Chart application User Interface populated with data from the EHR system. (Of course, the application could also open inside of the EHR so the user has no idea that the functionality is being provided by an external application, which would be even cooler…)

They interact with the application – viewing data, entering new information, making orders – and when finished close the window. All of the changes they have made are reflected in their EHR.

So what happened under to hood to make this happen? Well…

Detailed steps

We’ll now look at the sequence of events that made this happen. If you’re come here to find out what SMART can do, then you probably want to stop reading now. If you like the details, them read on…

There are a few prerequisites for all this to work properly. Essentially the client app needs to be initially registered with the EHR, providing:

  • A client_id that uniquely identifies the client application to the EHR.
  • The launch_url for the client application has been supplied- so the EHR knows where to call the app.
  • The redirect_url for the application has been registered – so that the EHR can redirect after authentication has successfully completed.

(We’ll think about how all this might happen in a later post).

Also – for those looking at this who are already aware of some of the SMART concepts, the app is assumed to be a ‘public’ application – ie it is not capable of keeping a ‘secret’ token. This has implications on the workflow, which is why we need to be aware of this. We’ll talk about this a bit more in the next post, so you can safely ignore that for now.

Architecturally, this will be a Single Page Application – running in the browser, perhaps using a framework like Angular.

Now for the details. First a sequence diagram:

SMART

 

And the description of the steps – with the numbers corresponding to those in the diagram.

s

1,2 The EHR user invokes the launch Url to start the application (eg create an iframe with the Url pointing to the client application launch Url). As part of this process (prior to creating the iframe) the EHR saves the current context (User, Patient etc.) in some common place where the Authorization Server can access it, with a ‘launch’ parameter (see below) below being the key to it. (There are other ways that the EHR can talk to the Authorization Server, but this will likely be a common one)

The HTTPS call to the client application includes the following required parameters:

  • iss – (short for issuer) the url of the base FHIR endpoint of the EHR. This allows the client to query for FHIR data – including the conformance resource, which holds the location of the authorization and token end points (more to follow).
  • launch – an opaque token that is the key into the common store recording the current EHR context (patient, user). (btw – there are a number of different ways of doing this – for example it’s also feasible that the launch token actually contains the state directly if strongly encrypted and signed)
2.1 The client application (which is hosted on a web server, of course) retrieves the servers conformance resource (which it can locate using the value of the iss parameter – ie {iss}/metadata). The Conformance resource will contain the authorization and token endpoints (as well as lots of other goodies). These are represented in the Conformance resource as standard extensions.
3 The client application then redirects the browser to the authorization endpoint in the EHR passing in a number of parameters.

  • response_type – fixed to the value ‘code’
  • client_id – the id of the client. This is the one created when the client app was registered with the EHR
  • redirect_uri – the uri that the EHR will redirect to after successful authentication. This must be the same as one that the client app set at registration time
  • launch – the opaque token that it was given by the EHR when first invoked (and is the key to the context)
  • scope – a string that describes the access rights that the client application needs to operate properly. This is a string (containing a number of words) in a specific format that we’ll consider in the next post. But for example
    • patient/*.read asks for permission to to be able to read all the data for a single patient.
    • patient/Condition.write asks to be allowed to create conditions.
    • offline_access and online_access are included if the client wants to get a refresh token. This will allow them to get a new access token after their current one expires. The difference between the two scopes is that the online_access is only valid if the user remains on line (it’s not specified how to determine that).
    • There are 2 scopes from openID that are significant here. If the values ‘openid’ and ‘profile’ are present, then the client application is requesting that they receive details of the current user – we’ll also think about that in a future post
  • state – an opaque token set by the client. The server (the EHR) will include this token when redirecting after successful authentication
  • aud – the url of the resource server. This is the same value as the iss parameter that the EHR included when it invoked the client app.
Authz server: Authenticate the request

The authorization server then authenticates the request. If the value of the launch parameter matches the one that the EHR set when first invoking the client app, then it can assume that it is the registered user (assuming sufficient entropy in the launch token).

(If this is not acceptable from a policy perspective, then there will need to be a separate step where the user re-authenticates, eg by user name & password. This may not be an attractive user experience).

Authz server: Authorize the request

The Authorization Server then decides whether it will grant the application the requested access rights (as listed in the scope parameter). It knows the user and the patient (from the original context of the call keyed by the launch token from the common store) so can apply privacy policies.

It may grant more or less that what was requested. The scope granted will be included in the Authorization token that is eventually issued (see below) so the client knows what it can do, but the Authorization Server will keep a copy locally so it can be supplied to the Resource Server when the client eventually makes a request.

It is also possible that the EHR request further information from the user at this point. For example:

  • If the user client app is requesting access to sensitive data the EHR may require a ‘break glass reason’.
  • If the user is a patient, then the EHR may wish to be certain that the patient is comfortable with the app getting that data.

Remember that it is the app that is requesting the access – not the user of the app – and there’s nothing stopping the app storing that data it receives from the server elsewhere, so these steps may be necessary.

Note that if the client application has requested access to the user profile (by setting the openid and profile scopes), the Authorization Server will also check whether those are permitted.

Authz server: Respond to the request

Assuming that Authz server is satisfied that the user can have at least some access to the data, it will redirect to the clients redirect_uri passing 2 parameters:

  • Code – a short lived token (string) that is created by the Authz server that the client will use to exchange for a longer lived access token. Referred to as the authorization code, it’s a key to some internal store in much the same way as the launch token was.
  • State – the same token that the client supplied (so it can check that the return is valid)

Of course, the Authorization server may decline the request for a number of reasons. These aren’t yet detailed in SMART, I asked Josh and he pointed out that looking at the OAuth2 spec we can see that there are a number of possibilities depending on the nature of the error:

  • Missing token: HTTP Status code401
  • Expired/bad token: 401
  • Good token, but not appropriate for the requested data: 403

These messages should also come with a WWW-Authenticate Response Header response header, and an error code (see the link above)

4,5 The browser will retrieve the actual HTML page for the application, and once it has loaded will make an AJAX request to retrieve the Access Token from the Authorization Server. (This means that the Authorization server will need to implement CORS, to allow the cross-domain request).

(The Access Token is really the key to the kingdom, and has a number of properties that are described below, but the main one is the one that it must supply when subsequently making a call to the Resource Server to get patient information. This is how the Resource Server can check with the Authorization Server that the call is legitimate.

The authorization code is generally short lived (ie the Authorization server will only accept it as valid for a short time – maybe a minute) and it’s purpose is to inform the Authorization server that a longer lived access token (typically lives for an hour) can be issued to the caller.

It is the access token that the Resource Server will examine when responding to a request. It is possible for the Authorization server to ‘refresh’ an access token – ie issue a new one when the old one has expired without having to repeat the handshake above – this is described below.)

So: the client make a POST to the Authorization Server (using application/x-www-form-urlencoded) containing the following parameters:

  • grant_type – fixed to ‘authorization_code’
  • code – the authorization code that was given to the client in the previous step.
  • redirect_uri – the same uri used in the initial query – step 2
  • client_id – the id of the client (as used in the original query, and how the Authorization Server knows which client application is making the authorization request.

The Authorization server checks that the code is still valid, and if so responds with an access token that is a JSON structure with the following properties:

  • access_token – the actual token (string) that the client will include when it makes a request to the Resource server. Confusingly, it has the same name as the whole structure!
  • token_type – fixed to Bearer
  • expires_in – how long (in seconds) that the access token is valid for.
  • scope – the authorized scope (in the same format that the client requested. This doesn’t have to be the same as what the client requested – the server may allow more or less than the requested
  • state – the opaque token that the client asked for in the initial request (see step 2)
  • id_token – if the client application requested information about the user, and the Authorization Server allowed it then an id_token will be included in the access token.
  • refresh_token – the access token has a limited life span (an hour is recommended). The refresh token is used when the access token has expired to get a new access token. The refresh token also has a prescribed life span – 4 hours is recommended – which means that it must be used within 4 hours of issuing. This is only included if the client included either a offline_access or an online_access scope in their initial request (and the Authorization Server permitted it)
  • Patient – the id of the patient, if there was a patient in context when the app was launched. Other context parameters are possible.

 

6 Client makes FHIR requests on the resource server

Once the client has been authenticated, it can make standard FHIR queries against the data in the Resource Server. It passes the access_token property of the access token in the Authorization header (somewhat confusing terminology IMHO). The resource server will validate that the token is current, and that the query is within the allowed scope before responding. It may also apply other privacy rules, depending on the specific configuration and data requested.

Refresh Access token

Not shown in the diagram is the ‘refresh token’ stage. When the Access Token expires, if there is a refresh_token in the Access Token, the Client can get a new one by again calling the ‘token’ end point, passing in the refresh_token and they will be issued with a new Access Token (probably). See the spec for details.

So that’s the detailed flow for a single Use Case – an EHR user invoking a SMART application which can securely access data from the underlying EHR (the Resource Server). Note that this is just one implementation – there are many others that could be used. SMART really focuses on the interface between EHR and Client – not the internals of how the EHR (or client) do their work.

Which brings us nicely to the question of libraries. As we’ve seen, Outh2 is a complicated specification – and it’s very easy to have bugs when writing code to support it, which is not a good thing – especially in the security implementation! There are many libraries available that will perform these steps that have been thoroughly tested, and you are strongly advised to use one of them rather than trying to do it yourself.

And for clients, there’s also a javascript library provided by the SMART project team – plus others for python and iOS.

In the next post, we’ll take a step upward and consider some of the higher-level aspects of SMART – different Launch schemes, more about Scopes, Confidential vs Public applications and so forth. We’ll also think about the FHIR profiles that are used, and touch on User Details – the openID part of the specification.

And lastly, here are the official SMART specifications. The page on Authorization is well worth a read (most of the contents of this post came from reading that page – plagiarism at its best! – or the most sincere form of flattery…).

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.

7 Responses to Implementing SMART on FHIR in an EHR

  1. Pingback: SMART – Security | Hay on FHIR

  2. Pingback: Using SMART to talk between systems   | Hay on FHIR

  3. satyendra says:

    Dear Sir,
    We are trying to develop an FHIR based health application. Now from the Interoperability point of view. We have a doubt. how two different FHIR application will communicate between them.
    It will b really helpfull, if you can tell us.

    Regards
    satyendra

    • David Hay says:

      Hi Satyendra,

      Have you got specific questions? In general the point of FHIR is that if 2 applications have a common API (interface) and a common way of representing the information (Resources) then some of the physical barriers to interoperability are removed. Argonaut is a good example of where the client and teh server communicate using FHIR – that may be worth looking into…

      cheers

  4. David – Great article, being a newbie in FHIR/EHR areas, this article gives me a good overview on how an application would interface with Smart on FHIR. Reading and understanding the issues related to EHR update, I have been unable to find much on way of updating the Vitals in the EHR (assuming that the application is authorized to provide the vitals). With the update capability to vitals EHR becomes more meaningful.

  5. adminposhjewels says:

    Very Good article. Appreciate your effort.

  6. Pingback: Reducing your risk of a heart attack by looking at your data - OH Blog

Leave a Reply to satyendraCancel reply

Discover more from Hay on FHIR

Subscribe now to keep reading and get access to the full archive.

Continue reading