SMART – Scopes and Profiles
March 23, 2016 2 Comments
In a previous post we looked at some of the ‘security related’ aspects of SMART. In this one we’re going to take a closer look at what the ‘scope’ is, and make a couple of comments on the use of Profiles.
Scope is an AOuth2 term that represents the range of functionality requested by (and potentially granted to) a client application by the Authorization Server. For example an app that displays a person’s vitals might want to be able to read and create Observations.
The sequence when requesting a scope goes something like this:
- After passing authentication (so we know that the user has a valid account in the EHR), the client app requests to be able to perform a particular set of functionality, and represents this in the scope string (which contains any number of individual scopes separated by spaces). For example, either of the following scopes would represent the Observations example above (read and write on a patients observations):
Patient/Observation.read Patient/Observation.write Patient/Observation.*
Note that the scope doesn’t specify the specific patient – just that it intends to read and write observations. It is up to the Resource Server to decide if the authenticated user can do this against any specific patient.
- The Authorization Server determines if the client can do this. This is completely up to the particular implementation, but generally it will make this decision based on the EHR capability, the User and possibly the selected Patient if this was an EHR launch. Part of the workflow may involve asking the user if this is OK – like when you load a new app on to your phone.
- The access granted (if any) is then returned to the client (in the access token) as well as being stored in a way that the Resource server can access it when deciding whether to respond to a request from that client. This could be by storing a copy somewhere else where the Resource Server can access it, or the Authorization Server could sign the Access token – or some other mechanism. So the app knows in advance what the Resource Server will likely allow it to do and can adjust its UI accordingly.
OAuth2 does not define the contents of the scope – it simply states that that is the mechanism by which access can be negotiated between client and server. So what SMART does is to define a pattern for the use of scopes, which is one of the neat things about SMART, as otherwise every implementation would be doing something different – hardly helping interoperability!
As an aside, it’s interesting to note that the functions of scope overlap with the conformance resource that is usually used to describe the capabilities of a server (which resources it supports, which operations on those resources and other stuff), but it can equally be used by a client to state what its expectations of a server are.
It could be argued that it would have been more FHIR-like to use a conformance resource rather than scope. However, there are a few reasons why scope is the better choice in this scenario:
- There are some scopes that don’t have an analogue in conformance (e.g. a request that the EHR display a patient selection screen as described below)
- If SMART didn’t support scopes, then it would be diverging from OAuth2, which is not such a great idea.
- The scope is what can be presented to the app user during authentication so they know what data the app is going to collect (and what actions it can potentially take against the EHR) which is an important part of the process
Refer to the spec for a detailed view of how scope works in SMART, but briefly there are a number of categories:
- Clinical Data
- Contextual Data
- Identity Data
Clinical data is further divided into:
Patient specific scopes (about a single patient) e.g. patient/Observation.read would be read access to the selected patients observations, patient/Condition.write to be allowed to create Conditions and so forth. The pattern for patient specific scopes is: patient/:resourceType.(read|write|*)
User level scopes – information that the user has access to. Note that this not information about the user – it’s what they are allowed to see (This may be the same, but not necessarily.
For example:
- user/Appointment.read – read access to all the appointments that the user is allowed to view.
- user/Patient.read – if the client wants to select a patient directly.
Because there’s a whole page in the spec on scopes, we won’t repeat that here, but we will call out some of the specific ones of interest.
Selecting the Patient
If a client wants to have the ability to select a patient, then they do so by setting a particular scope. There are a couple of options:
- launch/patient means that the application is requesting that the EHR display a patient selecting form, and allow the user to select the patient. The EHR may limit the patients that a user can access, based on the user role.
- user/Patient.read means that the client application wishes to be able to select a patient from within the app (again, the EHR may limit who the current user can select)
Notes:
- The scopes above simply deal with the selection of the patient – other scopes (such as patient/*.read) will be required to be able to actually do anything with the selected patient
- The EHR server has complete control over what it will allow. It may limit the patients allowed to be selected by the user or – for an EHR launched app – may only allow that patient to be selected
- There is no concept of placing the patient in context in the EHR – the client makes the appropriate queries which the EHR may execute.
The standalone app will definitely need one of these scopes if it is to be able to operate against specific patients. The EHR launched app may also need it, for example it may be launched without a patient selected.
Requesting a refresh token
As described in the previous post, an Access Token has a limited lifespan – commonly an hour. After this time, it is no longer valid, and the app would need to re-authenticate if it needs to continue to access the EHR data. The app can request the Authorization Server issue a ‘Refresh Token’ during the initial authentication process – which will allow it to get a new Access Token simply by sending the Refresh Token to the ‘token’ end point of the Authorization Server. It does so by including one of a couple of scopes:
- online_access – The Refresh Token is valid only if the app remains on-line (however that may be determined)
- offline_access – The Refresh Token is valid even if the user goes off line. (generally the more useful)
Note that the Refresh Token also has a limited time within which it is valid – no longer than 24 hours is the recommendation. After that, the client will need to re-authenticate.
Find out about the current user
It’s important to note that in all of our discussions so far, we haven’t considered the identity of the user of that app. As long as they have a valid account in the EHR they have been allowed to access data (within the bounds of their access rights of course). For clinical users, that’s probably all that’s needed as they are accessing the details of other people and the whole EHR infrastructure is likely built around doing that in a secure fashion (respecting the patients privacy) but for Patient access it’s a bit different.
In general, a patient will only be able to access their own data. (In some cases of course this is not quite correct – there may be the ability to look up the data of other people like children or relatives, but in that case from a security model perspective they are acting just like clinicians – albeit with more limited functionality).
So what we need is a way for the user of the app to request access to themselves. This is where openID Connect comes into the picture.
The app puts a couple of scopes – openid & profile – into the authorization request, and if the Authorization Server accepts the request, then it will include an ID token along with the Authorization Token when it responds to the request.
There’s a slightly convoluted process for starting with the ID token and getting the information you need – which ends up as a url to a FHIR resource (Generally a Practitioner or a Patient). This is described at the bottom of the scopes page so go take a look there for details.
So that’s a brief look at scopes. As always, the definitive description is in the spec. The other way in which a SMART client ‘negotiates’ with the Resource server is in the use of profiles – so let’s take a quick look at that.
Most people familiar with FHIR are familiar with the concept of Profiling. It’s the way that we take the defined resource (like an Observation), and make them suit a specific use case. They can:
- take elements out
- add new elements
- change how many of a given element can be present
- specify what terminologies and/or ValueSets coded element are bound to
and a bunch of other stuff.
Profiles are necessary because FHIR – by design – is a ‘platform’ specification. It is designed to be used anywhere in the world where healthcare interoperability is needed, and so there is a balance between being restrictive (‘this is the way to do it’) and permissive (‘just do what you need to do’).
But the amount of work the client has to do, is directly related to the ‘permissiveness’ of the profile – the more permissive it is, the more work they need to do.
To make this easier for implementers, SMART defines a specific profile to use. Actually, it’s a wee bit more complicated than that, as details of the profile are being reviewed as part of the Argonaut program – and likely to settle on the DAF (Data Access Framework), itself a work in progress.
Now, there’s a slight problem with that, as DAF is a ‘US realm’ specification, meaning that it is tailored towards US requirements. Other ‘realms’ (read Countries) will likely have slightly different requirements (though hopefully aligned with the US ones where that is possible). For example, one of the tracks in the next connectathon will be to use a Canadian profile in SMART (and note that it specifically refers to using DAF as a base) .
My guess is that profiles are one of the areas where we are going to see the most change in SMART as more people start to use it. The security and the scope aspects will likely be common internationally, but the profiles will vary between countries. We will need a way for a SMART application to indicate what profiles it supports/needs which could well be using the conformance resource which can indicate supported profiles. Another possibility might be to use Implementation Guides, which are a ‘layer above’ profiles.
We’ll also see ‘SMART security’ being else in other areas – like the rather exciting ‘CDS-hooks’ that is just starting up.
So that completes our ‘mini-series’ on the technical aspects of SMART. Hope you found it helpful! There’s a lot more to think about – I would recommend taking a look at Argonaut if you are seriously interested in this stuff, it’s still in operation…
David, I think you have missed a critical aspect of scopes, or at least didn’t communicate it. A scope is a request to restrict the authority that would otherwise be given. So, if you don’t ask for a scope, then you get all the access authority of that user. If you ask for a scope, and that scope is within the access authority of that user, than that limitation on the whole access authority is granted. The use of scope is best viewed as a way someone can design for least-privilege. In this way an application that asks for least-privilege will expose the resource server to less ‘risk’ if something goes wrong (such as the token getting copied and re-used maliciously).
Also the delegation of rights concept really needs far more robust mechanism. This is specifically what UMA is for.
John
Hi John – thanks for that! I must admit that I was looking at it from the other direction – don’t ask, don’t get. So if an app doesn’t ask for any Patient/? scopes then the app can access whatever the user is allowed to, but if they specify any Patient/? scope, then they will be limited to those scopes only – right?
cheers…