In my last post, I spoke to our need to handle sophisticated consent cases ONC Cures Act API compliance for 1/1/21, including patient representatives of various kinds.
The mandate has de-scoped data segmentation and redaction for sensitive conditions, as too big a lift for the industry, leaving APIs to deliver all scoped data or none of it.
From a consumer perspective that simply won’t fly, so I expect there to be major consumer pushback followed by a rapid cycle update of the regulation to bring data segmentation and redaction back into scope, perhaps as soon as 2022 (which would be warp speed as these things go.)
I also believe you can’t comply with HIPAA Individual Right of Access and related privacy legislation without such segmentation, leaving us all between a legal rock and a regulatory hard place. More about that in my next post.
For today let’s talk about a simpler, more addressable concern.
If I am a patient representative and have legal rights to ‘see’ three members’ data (I work for a payer so think of them as members – substitute ‘patient’ or ‘consumer’ as useful, I may go back and forth), how does the plumbing work to let me access their data from MyLilHealthApp?
The answer to that is not at all clear from the mandated FHIR Smart App Launch framework.
Let’s look at a simple case first, a member granting the app access to their own claims data.
Per that IG, if a standalone app needs to know the patient ID it adds the scope ‘launch/patient’ to the request it passes to the OAuth AS. The ‘launch context parameters’ come back alongside the access token. Let’s say the app has been granted Claim read access.
After the OAuth dance the app gets back a JSON structure that looks like this:
{
“access_token”: “09a86sf87asdf78y087a-9v-98uvblahblahblah”,
“token_type”: “bearer”,
“expires-in”:3600,
“scope”: “patient/Claim.read”,
“patient”: “456”,
…
}
Per the framework doc, the “456” is intended to be the FHIR resource identifier, not the Patient.identifier which might hold your unique member identifier. See the discussion at 2.26.3.4 here: https://www.hl7.org/fhir/resource.html#identifiers. The patient ID is not going to be something like “456”, but probably a UUID like “f0e270d7-ed56-4041-9dac-a45a73461c99”. A FHIR resource ID is an opaque ID up to 64 characters long, alphanumeric but can include dashes and periods.
The third-party application calling our FHIR API needs to know what patient ID to put into its query.
With that access token we are only letting the app query about that specific patient. So their query needs to look something like this:
https://yourfhirserver.yourdomain.com:8000/Claim?patient=Patient/456
The user is querying a permitted resource – they got the patient/Claim.read scope – about the consenting member’s data – Patient/456.
In your API (more likely in your reverse proxy) you call the OAuth AS to introspect the access token and validate the claims for the scope and patient id. (You should always call the AS instead of getting the JWKs and doing it yourself in case the token has been revoked.)
I suppose this could also work with query rewriting, where the API, after introspecting the token, tacks “patient=Patient/456” onto the query. That is awkward. If that was how it should work why pass the patient ID to the app in plaintext in JSON?.
<quick rant>Query rewriting – and redaction for that matter – violate a fundamental API precept, that an app should ask for exactly what it wants and get back either exactly what it asked for or an error. Any other API behavior is squirrely. I was reminded of this by Nikolai Ryzhikov in a web presentation he did on Health Samurai’s FHIR Server aidbox (around the 54:10 mark of a group presentation on ‘Fine Grained Security Policies Beyond OAuth2’).</rant>
Now let’s look at our patient representative who can access three members’ data case.
The application behavior we want to support is obviously the user being able to see the members whose data they can access and being able to access that data individually or collectively.
And the consent behavior we want is for the user to be able to choose which of those members data the app will be able to access, and, for each of those members, what resource scopes.
For what it’s worth, accessing all of their data in a single FHIR call looks like a non-starter, given this note in the FHIR US Core general guidance, section 2.1.1.13, “Searching multiple patients”:
Currently, most EHRs permit queries that provide a single patient id, but do not support the comma separated query or a query where the patient parameter is omitted as described in the standard FHIR REST API. Instead, a user facing app can perform multiple “parallel” queries on a list of patient ids. Alternatively, the FHIR Bulk Data Access (Flat FHIR) specification can be used to perform a “back end” system level query to access a large volumes[sic] of information on a group of individuals or when trying to identify and query against an unknown population such as when looking for population based research data.
However, neither specification defines how a user facing provider app is able to seek realtime “operational” data on multiple patients (such as all patients with recent lab results). Opportunities to add this capability to this guide are discussed in Future of US Core
An in that discussion in Future of US Core, 2.6.1.1.2, “Future Candidate Requirements Under Considerations[sic]”, we find this:
Searching for Multiple Patients – Searching for multiple patients has been called out in the ONC Health IT Certification Program. Defining capabilities for multiple patient access would focus on querying real time data for a user facing provider app across patients. Examples of the type of queries that would be addressed include searching for all of a provider’s patients: with recent lab results; currently in the Emergency Department; with an Allergy to X
Third-party apps accessing a broad range of endpoints will have to assume the least common denominator of functionality, which sounds like one patient’s data at a time.
So let’s say for now our third-party apps will have to query each member’s data discretely, possibly in parallel if it is all tricked out.
So how do we scope the app’s access to our set of members, the ones our patient representative has the right to access under 45 CFR 164.502(g)?
You will recall there are two categories of clinical scopes in our FHIR scopes, patient-level and user-level.

The design intent of the categorization is to distinguish between single patient’s data access and many patients’ data access, with ‘patient’ scopes the former and ‘user’ scopes the latter.
Patient-level scopes are what were used in our simple example above.
User scopes are ones such as this:
user/Appointment.read
which, per the standard means ‘read all the appointments to which the authorizing user has access’ (my bold italics).
Also per the IG this scope
User/Patient.read
means “allows the client app to select a patient”. Implied, just as it was for the Appointment scope above, is ‘Patients to which the authorizing user has access.’
FHIR guru David Hay pointed toward this use years ago, writing this:
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).
If the app requested User/Patient.read we could let them query Patients and get a list back of the Patients the user had access to. But we would still need to constrain that query to the Patients the user had access to. The access token and/or ID token need to somehow channel that constraint.
The most obvious way is to do it like we do the ‘single patient who is the user’ case.
After authentication in the OAuth dance, the user is presented the list of scopes requested by the app, and given the opportunity to select all, some, or none of them (none in case they changed their mind at the altar?).
In the simple case, the AS has to obtain the Patient ID to put into the access token and return to the app alongside the token.
If the scope selection also included a patient selection, the user could choose all, some, or none of the patient’s whose data they had rights to ‘see’. Those IDs could be returned in the ‘patient’ claim just as we return multiple scopes.
{
…,
“patient”: “456 846 239”,
…
}
(Again, the actual IDs would be long strings, probably 36-character UUIDs.)
Then the app knows what IDs it can query, and the API knows what IDs have been authorized.
Instead of returning all the patient IDs, we could make the user choose only one, and return the single ID as explicitly illustrated in the IG.
But if we did that, then the app and user would have to use them serially, re-authenticating and getting new tokens each time – our AS is (probably – need to vet this) not going to let a given app with a given user have multiple live access tokens. Even if we did, then the user experience would be the app going back to the well for each patient whose data the user wanted to grant access to. We would have to track who the patient was for each issued token so the AS would not re-present that patient at scope selection time – we don’t want two tokens for the same person for the same app out there. And the app would have to have some kind of token wallet, whipping the right one out as needed to access a particular patient’s data.
All of that sounds non-standard and unworkable versus the alternative of simply returning multiple patient IDs in a single patient claim. Would that be too much data in an access token? The spec leaves the max size undefined. In practice some ASes document a limit so their clients can manage them – Google’s access token size limit is 2048 bytes. FHIR resource IDs can be up to 64 characters long, but in practice a 36-character UUID is the longest you might expect. Hmmm. In the US the largest family – the Bates – famously had 19 children. So Mom, Dad, and the brood would be 21, times 36 characters per ID is 756, plus 20 spaces in between IDs would be 776. ASes should check and not exceed their documented max token size, but it sounds like a size issue from many patient IDs would be rare if ever. Can you think of any scenarios larger than the Bates brood?
So then what clinical scopes to use with our multi-patient claim?
Either patient or user scopes would work. But the design intent is clearly patient scope == single patient, user scope == many patients.
On the other hand, for the app to know to request user scopes instead of patient scopes, it would have to know ahead of time that the user could ‘see’ more than one patients’ data. Which the user may or may not know. And given the practical constraint outlined above in the US Core discussion, it seems like the only safe thing for an app to do is to query each patient’s data discretely – which sounds like the patient clinical scopes case. (Per that US Core discussion, if you can’t query many patients’ data at once, and for now have to do Flat FHIR for that kind of thing, what use are user scopes anyway? Perhaps I am not tracking – thar be monsters here.) On balance, since with respect to the actual resource scope grant given the patient claim constraint user scopes and patient scopes are semantically equivalent, I’d say go with user scopes as the most elegant, where the number of patients the user can access is 1 to many.
All of that assumes that the AS is able to map from the authenticated user’s identity to the list of patient IDs they can access. So the resource holder’s consent evaluation and mapping mechanism must be accessible to the AS. That interaction would be a one-off – there is no standard there.
The OIDC ID token returned by the AS contains a fhirUser, which is an URL pointing to a Person, Related Person, Practitioner, or Patient. In the case of a personal representative, that would be a Person. In the scopes and launch context IG we find this about fhir User:
Note that
Personis only used if the other resource type do not apply to the current user, for example, the “authorized representative” for >1 patients.
Is there some reasonable way to make it so if the App called that URL they could ‘see’ all the permitted member/patients they can access?
Even if the AS somehow automagically updated the Person and related resources at that URL to reflect the allowed Patients, I frankly don’t see how to map from a Person to many Patients anyway. And it would be a stretch to call that ‘standard’, unless every API endpoint did it.
You can go from a Patient to many Related Persons via the Patient.link property, which can point to a Patient or Related Person.
Although none of the link types make sense for a related person mapping – this is from https://www.hl7.org/fhir/valueset-link-type.html:
All codes from system http://hl7.org/fhir/link-type
| Code | Display | Definition |
| replaced-by | Replaced-by | The patient resource containing this link must no longer be used. The link points forward to another patient resource that must be used in lieu of the patient resource that contains this link. |
| replaces | Replaces | The patient resource containing this link is the current active patient record. The link points back to an inactive patient resource that has been merged into this resource, and should be consulted to retrieve additional referenced information. |
| refer | Refer | The patient resource containing this link is in use and valid but not considered the main source of information about a patient. The link points forward to another patient resource that should be consulted to retrieve additional patient information. |
| seealso | See also | The patient resource containing this link is in use and valid, but points to another patient resource that is known to contain data about the same person. Data in this resource might overlap or contradict information found in the other patient resource. This link does not indicate any relative importance of the resources concerned, and both should be regarded as equally valid. |
‘Refer’ almost makes sense – but it says the current Patient record, the one containing the link, is not considered the main source of information about a patient. Which makes no sense for a link to a Related Person.
All that aside, you can’t map the other way anyway. A Person is 1 to 1 with a Related Person (same human with an identity match confidence level). But a Related Person is 1 to 1 with a Patient – they point to each other uniquely. So you can’t have one Related Person related to many Patients. (That is how I am reading the spec – if true that is an awkward design constraint on what should clearly be a lattice of related persons.)
Based on the relationship types, RelatedPerson is not primarily intended to support the kind of legal relationships we are talking about here anyway, although in the 117 or so types we do find foster parents.
In sum, there is no reasonable way to use fhirPerson to directly map to all the Patients the Person might currently be in legal relationships with.
No joy using fhirUser somehow for this.
Ok. To recap, whether we are using user or patient scopes, we need the App and the API to have the list of members/patients the App has been granted access to by the patient representative.
We can return those as multiple patient IDs in the person claim. (We could always create a new claim type, but that would defeat the purpose of having this be standard.)
We can’t do some kind of mapping from fhirUser in the ID token to the members/patients no matter how much bubble gum and bailing wire we apply.
We could take a different tack and introduce User Managed Access, a standard extension of OAuth designed to give an individual independent, asynchronous control over who can access their data.
UMA is the plumbing that supports letting the patient – the ‘Resource Owner’ – grant some set of scopes to a ‘Requesting Party’ who is using a ‘Client’ app. For our example here, Patient Joe Bob would grant his Personal Representative Hiram some set of scopes, then Hiram in turn could grant the Client some subset of that set of scopes.
UMA doesn’t manage the consents or the identities – the resource holder, e.g. you, are still on the hook for that. It essentially takes the part of the OAuth dance involving identifying the requesting party and determining their data access rights and moves it out of a proprietary solution inside the AS into a different proprietary solution inside the AS, except with standardized dialogs among components to share the knowledge – as opposed to the scope games we are playing in OAuth 2.
In UMA “[i]t is only possible to request permissions for access to the resources of a single resource owner, protected by a single authorization server, at a time.”
Which on the face of it makes our one user to many resource owners’ data access look problematic.
UMA absolutely might work for this – it is notoriously complex – but it is not yet ubiquitous in OAuth AS implementations, although its use is growing – it is in Gluu, Keycloak, and ForgeRock among other authz servers.
As a possible alternative there is a nascent standard from Luis and Julie Maas called UDAP Client Authorization Grants, which looks simpler than UMA, that might be part of a solution.
Bottom line today is that neither UMA nor UDAP are in the ONC Cures Act mandated technology stack – we have to solve this with just old-school OAuth2 and OIDC, leaving UMA, UDAP, XYZ, to some other day. (Legislating technology is a tricky business – we – the country we – are getting better at it, but it is still essentially a sea-anchor providing stability while slowing progress.)
Within the OAuth and OIDC solution space, to support our cases here means having the AS returning the “patient” claim to the app alongside and inside the access token, where “patient” may be a string of one or more Patient IDs separated by spaces.
There seems no reasonable way of defining per-patient clinical scopes, so the requested and granted clinical scopes will apply to all consented patients. If the relationship between the personal representative and the patient changes, we will have to revoke the access and refresh tokens and make the user and app go back to the well and reestablish access for the remaining members/patients. Ditto if there are more members/patients whose data becomes ‘visible’ to the personal rep – we will have to nuke the tokens and have them reestablish access.
With respect to user versus patient clinical scopes, user scopes accessing one to many patient’s resources, where the set of patients is constrained by the patient claim, makes the most sense.
Not pretty, but – assuming we get the back side figured out in time which lets us map from a given person to the patient/member data they can legally access – it gets us compliant with 45 CFR 164.502(g) and 164.510(b).
To make this work, all apps accessing ONC mandated APIs will have to expect it to work the same way, and all APIs and ASes will have to comply. That demands explicit clarification of the approach from the ONC and HL7 – and soon.
The alternative is for us to not only be out of compliance with 45 CFR 164.502(g) and 164.510(b), but arguably guilty of information blocking. We obviously don’t want to be in either of those places.
I believe we still may have open issues about necessary, non-optional data segmentation and redaction to work through. Next time.
Stay tuned.
One thought on “How to Comply with the HIPAA Individual Right of Access in your ONC Cures Act-mandated FHIR APIs”