google / fhir-gateway

A generic proxy server for applying access-control policies for a FHIR-store.
Other
70 stars 28 forks source link

FHIR Information Gateway

Build Status codecov

FHIR Information Gateway is a simple access-control proxy that sits in front of a FHIR store (e.g., a HAPI FHIR server, GCP FHIR store, etc.) and controls access to FHIR resources.

Note: "gateway" and "proxy" are used interchangably here, as the gateway is implemented as a proxy server.

The authorization and access-control have three components; one of them is this access proxy. The other two are an Identity Provider (IDP) and an Authorization server (AuthZ). The responsibility of this pair is to authenticate the user and issue access tokens (in JWT format and using authorization flow of OAuth 2.0). The requests to the access proxy should have the access token as a Bearer Authorization header. Based on that, the proxy decides whether to grant access for a FHIR query.

Modules involved in FHIR authorization/access-control

For more information on the technical design, see the design doc.

Modules

The proxy consists of a core, which is in the server module, and a set of access-checker plugins, which can be implemented by third parties and added to the proxy server. Two sample plugins are implemented in the plugins module. There is also a sample exec module which shows how all pieces can be woven together into a single Spring Boot app. To build all modules, from the root run:

mvn package -Dspotless.apply.skip=true

The server and the plugins can be run together through this executable jar ( --server.port is just one of the many default Spring Boot flags):

java -jar exec/target/exec-0.1.0.jar --server.port=8081

Note that extra access-checker plugins can be added through the loader.path property (although it is probably easier to build them into your server):

java -Dloader.path="PATH-TO-ADDITIONAL-PLUGINGS/custom-plugins.jar" \
  -jar exec/target/exec-0.1.0.jar --server.port=8081

The plugin library can be swapped with any third party access-checker as described in the plugins directory and the wiki.

Note: Spring Boot is not a requirement for using FHIR Information Gateway; we just use it to simplify the MainApp. The only Spring-related requirement is to do a @ComponentScan to find all access-checker plugins in the classpath.

Configuration parameters

The configuration parameters are provided through environment variables:

Gateway to server access

The proxy must be able to send FHIR queries to the FHIR server. The FHIR server must be configured to accept connections from the proxy while rejecting most other requests.

If you use a GCP FHIR store you have the following options:

Once you have set all the above, you can run the proxy server. The sample exec module uses Apache Tomcat through Spring Boot and the usual configuration parameters apply, e.g., to run on port 8081:

java -jar exec/target/exec-0.1.0.jar --server.port=8081

Docker

The proxy is also available as a docker image:

$ docker run -p 8081:8080 -e TOKEN_ISSUER=[token_issuer_url] \
  -e PROXY_TO=[fhir_server_url] -e ACCESS_CHECKER=list \
  us-docker.pkg.dev/fhir-proxy-build/stable/fhir-gateway:latest

Note if the TOKEN_ISSUER is on the localhost you may need to bypass proxy's token issuer check by setting RUN_MODE=DEV environment variable if you are accessing the proxy from an Android emulator, which runs on a separate network.

Try the proxy with test servers in Docker.

GCP note: if this is not on a VM with proper service account (e.g., on a local host), you need to pass GCP credentials to it, for example by mapping the .config/gcloud volume (i.e., add -v ~/.config/gcloud:/root/.config/gcloud to the above command).

How to use this proxy

Once the proxy is running, we first need to fetch an access token from the TOKEN_ISSUER; you need the test username and password plus the client_id:

$ curl -X POST -d 'client_id=CLIENT_ID' -d 'username=testuser' \
  -d 'password=testpass' -d 'grant_type=password' \
"http://localhost:9080/auth/realms/test/protocol/openid-connect/token"

We need the access_token of the returned JSON to be able to convince the proxy to authorize our FHIR requests (there is also a refresh_token in the above response). Assuming this is stored in the ACCESS_TOKEN environment variable, we can access the FHIR store:

$ curl -X GET -H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json; charset=utf-8" \
'http://localhost:8081/Patient/f16b5191-af47-4c5a-b9ca-71e0a4365824'
$ curl -X PUT -H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json; charset=utf-8" \
'http://localhost:8081/Patient/f16b5191-af47-4c5a-b9ca-71e0a4365824' \
-d @Patient_f16b5191-af47-4c5a-b9ca-71e0a4365824_modified.json

Of course, whether a query is accepted or denied, depends on the access-checker used and the ACCESS_TOKEN claims. For example, for ACCESS_CHECKER=list there should be a patient_list claim which is the ID of a List FHIR resource with all the patients that this user has access to. For ACCESS_CHECKER=patient, there should be a patient_id claim with a valid Patient resource ID.

Acknowledgement

This proxy is implemented as a HAPI FHIR Plain Server, starting from this hapi-fhirstarters-simple-server example.