Stream: smart
Topic: security questions
Vlad Ganshin (Sep 08 2021 at 21:06):
Hi all! Could you help me to understand some use cases of Smart on FHIR (v1)?
I have a FHIR Server on [base-url]
and an Authorization Server on [auth-server]
. And a user has an access to couple of Patients resources and their clinical data (pt-1 & pt-2).
1) Let's suppose that the user provided a grant to 3rd party app within next scope 'patient/Patient.read patient/Observation.read patient/Observation.write' and patient=pt-1 in context. As a 3rd party, what responses should I expect?
GET [base-url]/Patient/pt-1
GET [base-url]/Observation?subject=pt-1
GET [base-url]/Patient/pt-1/Observation
GET [base-url]/Patient
GET [base-url]/Patient/pt-2
GET [base-url]/Observation?subject=pt-2
GET [base-url]/Observation?subject=pt-3
GET [base-url]/Observation
POST [base-url]/Observation
subject:
reference: Patient/pt-2
I believe that the first three requests should return 200 OK + data, and the rest of them 403 Unauthorized. Right? Or it depends on something?
2) Let's say we have not Observation, but CommunicationRequest resource. Which are search parameters available for 3rd party app within patient/CommunicationRequest.read scope? subject, sender, recipient, requester?
3) I'm thinking of building a tenant-based FHIR API which uses user-id as url prefix for every request.
[base-url]/user/<user-id>/Patient/pt-1 => 200 OK
[base-url]/user/<user-id>/Patient/pt-2 => 200 OK
[base-url]/user/<user-id>/Patient/pt-3 => 404 Not Found
I would say I have a lot of logical FHIR Servers on top of physical one. In case of EHR launch I could provide different iss parameters depending on the user who launched the app, so it could help me simplify access control module.
But I can't find an answer if it's possible to make something similar in case of Standalone launch. It seems like 3rd party app should know the only base-url and use it for every user? Is it possible to subsistute [base-url] somehow while launching?
Michele Mottini (Sep 08 2021 at 22:56):
For (1): in our server they are all 200 OK, they just return only the data of pt-2, so GET [base-url]/Observation?subject=pt-2 returns an empty bundle
Michele Mottini (Sep 08 2021 at 22:57):
(2) Which search parameter are available is unrelated to the scopes
Michele Mottini (Sep 08 2021 at 22:58):
(3) Don't think that would work, clients expect a fixed URL to connect to
Vlad Ganshin (Sep 09 2021 at 07:18):
(1.1) We have the same solution for now, but I'm not sure if it's correct using Authorization header as an implicit parameter for business logic. Will my server be conformant with FHIR Specification (which is RESTful) if it provides different responses on the same url depending on Authorization header? I believe, that according to REST, the role of Access Conrol is only accept/reject requests, but not influence on the result. So, I would say that in this case our FHIR Servers are not conformant with FHIR Specification. Is it correct, or I miss something?
(1.2) And what about saving resources (POST/PUT/PATCH)? Should my FHIR Server set reference to the patient implicitly or validate and require correct reference value in the body?
(2) From the smart app developer point of view, how should I understand which endpoints (or/and search parameters) are available within a specific scope I got from user? Does specification have this answer, or I have to adopt my application to every specific FHIR Server?
(3) I feel that the specification tries to connect Smart App Developer and FHIR Server Developer and it provides a concrete authentication flow. But as a Smart App dev, once I have a token, I don't see my capability within my scopes. And as FHIR Server dev I don't see a standard way to provide such capabilities.
So, once FHIR Server provided 'patient/Patient.read patient/Observation.*' scope, does it obligated to accept requests to [base-url]/Patient/<pt-id>, [base-url]/Observation
, etc?
Will my server be conformant with FHIR if I just say in documentation, that within patient/...
scopes smart app should use another base-url. e.g. [base-url]/patient/<pt-id>
([base-url]/patient/«pt-id>/Patient/<pt-id>, [base-url]/patient/<pt-id>/Observation
).
John Moehrke (Sep 09 2021 at 12:17):
http://hl7.org/fhir/security.html#AccessDenied
John Moehrke (Sep 09 2021 at 12:18):
policies are important. principles of REST do not override policies.
Josh Mandel (Sep 09 2021 at 12:55):
In general with smart, it is okay for a server to reject a query prospectively/statically because of scopes. It is also okay for a server too simply redact results because of scopes. We don't have prescriptive behavior on this. The only thing it's not okay to do is return data beyond what the scopes allow.
Josh Mandel (Sep 09 2021 at 12:56):
The question of what endpoints are available is somewhat orthogonal to your scopes. For example, a server might advertise in its documentation or in its capability statement that it supports a "batch" endpoint; if it does, clients can call this end point with requests that are consistent with the available scopes. If it does not, clients can't.
nicola (RIO/SS) (Sep 09 2021 at 13:48):
I wonder SMART on FHIR based on baseUrl convention instead of more flexible HATEOAS. Why server after auth could not just return explicit baseUrl for the client with access_token ?
{
access_token: ....
fhirBaseUrl: ....
}
nicola (RIO/SS) (Sep 09 2021 at 13:52):
Compartments like /Patient/<id>/{metadata,Observation,Encounter} look like good fit for SMART Clients and Servers.
Vlad Ganshin (Sep 09 2021 at 13:52):
Thank you! I do missed that part in Access Denied Response Handling chapter.
Just to clarify, if I have a FHIR Server which is rich on capability, it is ok to restrict some access depending on client type, not only scopes provided to that client, isn't it?
Josh Mandel (Sep 09 2021 at 13:56):
That would work for areas like the patient compartment where fhir explicitly defines some context, but the mapping breaks down pretty quickly too. For example even with "patient" compartments, a smart app authorized to access data about a given patient might be able to see certain data outside of a compartment, such as data directly linked from or needed to interpret the data in the compartment. So passing the patient id (or fhirContext[] as array of references as proposed in SMARTv2) has been a flexible choice.
nicola (RIO/SS) (Sep 09 2021 at 13:56):
Michele Mottini said:
For (1): in our server they are all 200 OK, they just return only the data of pt-2, so GET [base-url]/Observation?subject=pt-2 returns an empty bundle
Should not server respond with 403 for (1)?
nicola (RIO/SS) (Sep 09 2021 at 13:58):
Josh Mandel said:
That would work for areas like the patient compartment where fhir explicitly defines some context, but the mapping breaks down pretty quickly too. For example even with "patient" compartments, a smart app authorized to access data about a given patient might be able to see certain data outside of a compartment, such as data directly linked from or needed to interpret the data in the compartment. So passing the patient id (or fhirContext[] as array of references as proposed in SMARTv2) has been a flexible choice.
What is fhirContext?
nicola (RIO/SS) (Sep 09 2021 at 14:00):
We are thinking about "poko yoko" for Smart on FHIR apps - i.e. implicitly inject patient id into all search and even CRUD operations.
nicola (RIO/SS) (Sep 09 2021 at 14:01):
@Josh Mandel what is your opinion about such an implicit server behavior?
Josh Mandel (Sep 09 2021 at 14:44):
nicola (RIO/SS): Michele Mottini said:
For (1): in our server they are all 200 OK, they just return only the data of pt-2, so GET [base-url]/Observation?subject=pt-2 returns an empty bundle
Should not server respond with 403 for (1)?
From the SMART spec perspective, both of these are OK behaviors.
Josh Mandel (Sep 09 2021 at 14:45):
What is fhirContext?
See FHIR-32253; this is being added to resolve SMARTv2 ballot feedback
Josh Mandel (Sep 09 2021 at 14:47):
nicola (RIO/SS): We are thinking about "poko yoko" for Smart on FHIR apps - i.e. implicitly inject patient id into all search and even CRUD operations.
I like this, but there are edge cases where it'll prevent access that a client should have (e.g., with patient/*.cruds
access, you might still be allowed to issue a query like GET /Observation/123
where the subject of the observation is something other than the patient (say, a patient's device or location).
nicola (RIO/SS) (Sep 10 2021 at 08:10):
Michele Mottini said:
For (1): in our server they are all 200 OK, they just return only the data of pt-2, so GET [base-url]/Observation?subject=pt-2 returns an empty bundle
I think for (1) - 403 is more honest - otherwise, the client may be confused and interpret the empty response as a lack of data
Michele Mottini (Sep 10 2021 at 13:43):
I think no error is better. Using 403 is actually _leaking_ the information that there is data that the client cannot see
David Pyke (Sep 10 2021 at 13:46):
Yep, if they're fishing for information, a 200 tells them nothing, a 403 says there's data which is a problem from a privacy point of view
John Moehrke (Sep 10 2021 at 13:51):
AND this is why the actual value returned should be driven by POLICY. http://hl7.org/fhir/security.html#AccessDenied
Last updated: Apr 12 2022 at 19:14 UTC