SMART on FHIR – adding OAuth2

You may recall that a week back we had a look at one of the connectathon scenarios – the SMART scenario.

In this post we’re going to take the work that we had done in the last post, and make it secure using the SMART version of the OAuth2 standard. As always, a primary reason I’m writing it down is so that when I forget what I did to make it work – I’ll have this as a reference to remind me <s>. And a reminder – I’m using the Java based HAPI FHIR client, in a web based application running in a Tomcat servlet engine, with IntelliJ IDEA as my IDE.
Read more of this post

CORS in FHIR

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

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

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

  Read more of this post

A simple OAuth client

We’ve talked about OAuth in a few posts now, so I thought it might be a good idea to try some of this out for real. (My other motivation is that we’re adding OAuth to Blaze, so I promised Richard I’d have a client for when he’s ready for testing).

I decided to start with a simple web based client and see how far I got.

From a technology perspective, Javascript and Node.js is my preferred language, so I created a simple node server that is going to act as my OAuth ‘client’ which I can then use to call different OAuth servers. The overall architecture looks a bit like this:

OAuth arch

 

A pretty basic web application, which serves up HTML pages to the browser, and communicates separately via HTTPS (of course!) to the OAuth servers. Because it’s a ‘secure’ application (the code runs on the server and not in the users browser)  it can use the ‘authorization code’ flow (or grant).

For my library I chose to use the node module ‘simple-outh2’ which exposes simple methods to make the required OAuth calls.

The first experiment is against Google. Google uses OAuth2 to authorize access to its services – in effect Google hosts both Authorization and Resource servers in the OAuth2 model, so should be quite straight forward.

First step is to establish credentials that the client can use to Authenticate against the Authorization Server (ie the client ID and client secret), which I got from the Developer website.

Then the app is quite simple:

var request = require('request');   //https://github.com/mikeal/request
var path = require('path');
var express = require('express'),
    app = express();

app.use(express.cookieParser());
app.use(express.session({secret: '1234567890QWERTY'}));
app.use(express.static(path.join(__dirname, 'public')));

var OAuth2;

var credentialsGoogle = {
    clientID: &quot;&lt;myclientid&gt;&quot;,
    clientSecret: &quot;&lt;mysecret&gt;&quot;,
    'site': 'https://accounts.google.com/o/oauth2/',
    'authorizationPath' : 'auth',
    'tokenPath' : 'token'
};

// Initial call redirecting to the Auth Server
app.get('/auth', function (req, res) {
    OAuth2 = require('simple-oauth2')(credentialsGoogle);
    authorization_uri = OAuth2.AuthCode.authorizeURL({
        redirect_uri: 'http://localhost:3001/callback',
        scope: 'openid email https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/tasks',
        state: '3(#0/!~',
        access_type: &quot;offline&quot;      //causes google to return a refresh token
    });

    res.redirect(authorization_uri);
});

// Callback endpoint parsing the authorization token and asking for the access token
app.get('/callback', function (req, res) {
    var code = req.query.code;

    OAuth2.AuthCode.getToken({
        code: code,
        redirect_uri: 'http://localhost:3001/callback'
    }, saveToken);

    function saveToken(error, result) {
        if (error) {
            console.log('Access Token Error', error.message, error);
            res.json({'Access Token Error': error.message});
        } else {
            //see what we've got...
            console.log(result);
            //this adds the expiry time to the token by adding the validity time to the token
            token = OAuth2.AccessToken.create(result);
            //save the response back from the token endpoint in a session
            req.session.token = result;

            //perform the res of the processing now that we have an Access Token
            gotToken(req,res);
        }
    }

    //Now the token has been received and saved in the session, return the next page for processing
    function gotToken(req, res) {
        res.sendfile(__dirname +&quot;/public/main.html&quot;);
    };

});

//get the lists for the current user...
app.get(&quot;/tasks&quot;, function (req, res) {
    //Need a token to access the google services
    if (req.session.token) {
        var AT = req.session.token[&quot;access_token&quot;];
        var url = &quot;https://www.googleapis.com/tasks/v1/users/@me/lists&quot;;

        var options = {
            method: &quot;GET&quot;,
            headers: {
                &quot;content-type&quot;: &quot;application/json+fhir&quot;,
                &quot;authorization&quot;: &quot;Bearer &quot; + AT
            },
            rejectUnauthorized: false,      //to allow self-signed cetificates
            uri: url
        };

        request(options, function (error, response, body) {
            res.json(body);
        });
    } else {
        res.json({err:&quot;Not logged in&quot;});
    }

});

app.listen(3001);

console.log(&quot;OAuth Client started on port 3001&quot;);
  1. The flow starts by calling the /auth endpoint – line 21 above – (I had a simple <a> tag in the front page). This initialises the OAuth2 object with the required credentials – and the location of the Authorization and Token endpoints, and then re-directs to Authorization Server with the required parameters (like scope, state and others). You can find the details on the Google site.
  2. Google then serves up a login page showing what scope you are after (eg accessing google drive and tasks) and the user logs in and authenticates the request. (If you’re logged in to google services separately it remembers who you are – which can be a bit disconcerting at first).
  3. Assuming all goes well, then the browser will eventually be redirected back to the local callback endpoint (http://localhost:3001/callback – line 34 in the example above) which extracts the Authorization code from the reply and requests an Authorization Token from Google.
  4. Once that’s done we save the token (actually the whole response from the Token request) and serve up another HTML page.
  5. Now, the user can access the Google resources – an example is the /task endpoint (line 67) which will get the task lists for the current user.

Interestingly, the access_type value of ‘offline’ in line 27 above is supposed to cause Google to include a Refresh Token in its response – which doesn’t seem to be happening for me, so I need to look into that. It means that when the Access Token expires, then the user should have to re-authenticate – though that didn’t seem to be happening. It may be that Google is doing something clever on the backend…

Oh, and if you look at the response that comes back from the server when you get a Authorization Token, you’ll see an id_token as well. Because we put ‘openid’ in the scope property, Google will also implement OpenId Connect and return Identity data as well.

So, as you can see, the library abstracts much of the complexity of OAuth away – and I presume that  the libraries in other languages act in a similar fashion.

But – this is a rather simple flow where both Authorization and Resource servers are served up by the same app – which can then do all sorts of clever things on the back end. What happens when they are on different servers?

We’ll look in to that soon…

 

 

FHIR: Securing an ecosystem

In our previous discussions on OAuth2 and OpenID Connect, we’ve talked about how the Authorization Server can authenticate a user, and provide an ‘Access Token’ that a Resource Server (e.g. a FHIR server) can use as a basis of deciding what information it can supply and what services it will allow. (As an aside, a reminder that the FHIR spec does talk about how this could be implemented – and also points out the IHE IUA profile (formal spec) as work in this area that is worth following).

Things are – relatively – straightforward when each server supports its own authentication, but gets a lot more complex when you try to support an ‘ecosystem’ of different servers. Let’s have a look at a couple of the issues:

  • How does a Resource Server know that a particular Access Token is valid (both that it’s a valid token and hasn’t expired)
  • How can the Authorization server pass the ‘scope’ of access to the Resource Server (where the scope is what the patient – also called the Resource Owner – has agreed to)

To provide a backdrop for our discussion lets provide a picture of a simple ecosystem – i.e. where there are multiple separate servers performing the various roles.

To keep things (relatively) simple, we’re going to assume that we want a single Authorization Server for our ecosystem. In fact, this is kind of like the IHE XDS Affinity domain concept, where you have:

 “a group of healthcare enterprises that have agreed to work together using a common set of policies and share a common infrastructure”

The difference being that the affinity domain has a single Registry – in our case we have a single Authorization Server. It isn’t always going to work out this way, but it’s a good starting point. (btw this picture comes from a presentation I’m giving to our National Standards Organization this week to show how these standards could work together in New Zealand).

Here’s our ecosystem:

sources

So we have:

  • An Authorization Server backed up by some databases for identifying patients and providers (This is so that the Authorization Server can also serve up Identity (Patient and Provider) – as we talked about last time. It’s not significant in the context of this discussion).
  • A FHIR Profile and ValueSet registry. This is a FHIR based ecosystem, and we’re bound to want to define profiles, ValueSets and extensions. Of course, we will very likely use other registries as well, especially for the HL7 defined extensions, but one of our own is likely to be required.
  • A Record Locator Service. This is serving the role of an XDS Registry and holds DocumentReference resources that can be queried as we’ve previously discussed.
  • A couple of Resource Servers, one directly holding resources  and the other ‘proxying’ to an existing Medical Records system.

Trusting the Access Token.

The question is simple. When one of the Resource Servers gets an Access Token, how does it know it’s valid and how does it know what scope of disclosure the User (Resource Owner) has agreed to?

When the Authorization Server and the Resource Server are the same application, this is straight forward – in the process of issuing the Access Token, the Authorization Server can save the details in a local cache which it can reference when the Resource Server receives it in a request. But when the Token needs to be used by multiple different servers it becomes a lot harder.

A couple of options come to mind (and I’m sure there are others of course).

  • The Authorization Server could make its cache of valid Tokens available to Resource Servers via a secure ‘back channel’ of some sort. (e.g. a Redis server holding valid tokens in memory keyed by the Token with the expiry set to the token expiry). When the Resource Server receives a request it queries the cache to confirm validity and retrieve scope information)
  • The Access Token could actually be a more complex structure than a simple Bearer Token – for example it could be a JWT (JSON Web Token) that contains the expiry time and the scope and signed by the Authorization Server. This would require that the Resource Servers have the Authorization Server’s public key – but as there are unlikely to be that many Resource Servers, this probably isn’t a big problem.

Which to choose (or some other)? Well, that will be up to the implementation (or the agreements in the “Affinity Domain”). In either case, the bulk of the complexity is with the server rather than the client – which is where we want it to be. (In the diagram above, the dotted lines represent this relationship – whether a direct query or indirect via the key).

Defining the scope.

We’ve glossed over this a bit in previous discussions.

When the user initially authenticates to the Authorization Server, they can supply the details of what information they are allowing the application to retrieve from the Resource Server/s on their behalf. This is called the ‘scope’ of the request and is defined in the OAuth2 spec as follows:

The value of the scope parameter is expressed as a list of space-delimited, case-sensitive strings.  The strings are defined by the authorization server.  If the value contains multiple space-delimited strings, their order does not matter, and each string adds an additional access range to the requested scope.

So, we need to decide what is the level of access granularity that we want to support, how we’re going to represent that and then make sure that all participants in our ecosystem support them. No small task as there is no agreed standard for this.

Some possibilities from Grahame:

  • Read any data
  • Read any data not labelled confidential
  • Read/write
    • Medications
    • Allergies
    • Conditions
    • Other clinical

But you can see that this is going to provide some thought. (Incidentally, there are a number of people looking into this – if you’re interested then drop me a line).

It should also be apparent why having a single Authorization Server does simplify things. In principle there is no reason why you can’t have more than one – but preferably they will all need to support the specific implementation requirements – such as the type of Access Token and the codes used for scope. There are doubtless ways of accommodating ones that can’t, but it does add complexity – and therefore risk – to the overall solution.

I have to say I kind of like the analogy with these questions and the IHE Affinity Domain concept. Although there will likely be some standardization in the future, there are always going to be differences in specific implementations, so this concept is a nice way of wrapping all the specific details up for a specific implementation (or, in the case of New Zealand – maybe the country!).

 

 

 

 

 

 

 

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