IMPORTANT: SMART-on-FHIR access controls are enabled on Google Cloud
Healthcare FHIR API in private preview (v1alpha2
) and public preview
(v1beta1
) with FHIR stores created on or after January 1, 2021 UTC. You should
restrict the ALLOWED_PATH_PREFIX
to only match paths for this version.
- There are plans to support older FHIR stores in the near future.
- If using this proxy with a different API version or on an older store other than what is indicated above, SMART-on-FHIR access controls will be ignored and access may be granted to all users of the proxy regardless of SMART patient context or SMART permission scopes.
- Note that other tools not using SMART access may use other API versions to
populate FHIR content, however the proxy must use
v1alpha2
orv1beta1
to enable SMART access for proxy users.
SMARTproxy delivers a means to have the Google Healthcare FHIR API accept and validate SMARTonFHIR access tokens as a separate processing layer, allowing the core FHIR API extend to include SMART access tokens as part of its identity management and permission model.
See the SMARTonFHIR specification for more details on SMART-compliant access tokens.
SMARTproxy offers the following configuration parameters:
FHIR_ISSUER
: the "iss" claim expected in the SMARTonFHIR token.PROXY_TO
: the FHIR service root URL.SERVICE_ACCOUNT_NAME
: the service account name for the proxy to use when calling the FHIR (PROXY_TO) endpoints. Default is "fhirproxy".ALLOWED_PATH_PREFIX
: a comma-delimited set of Healthcare API paths to proxy. This provides the ability to limit which projects, datasets etc. with which the proxy may be used. Each entry must start with "/".AUDIENCE
: a string that represents the expected audience for the access token. It may be set to the same string as the Token Issuer's client ID depending on how the Token Issuer populates the claim.FHIR_ISSUER_CLIENT_ID
: the Relying Party client ID for the proxy as allocated by the token issuer IDP. This may be used when the proxy calls OIDC endpoints.FHIR_ISSUER_CLIENT_SECRET
: the Relying Party client secret for the proxy as allocated by the token issuer IDP. This may be used when the proxy calls OIDC endpoints.CLIENTS_OF_PROXY
: a set of key/value pairs of client applications that are able to access the proxy. When non-empty, all clients of this proxy must pass inX-Client-ID
andX-Client-Secret
headers with their requests and match one of the entries on this list, or else the request will be unauthorized.REMOVE_SCOPES
: a comma-delimited set of scopes to remove from theX-Authorization-Scope
andX-Consent-Scope
headers that the proxy includes in its requests to downstream servers.WELL_KNOWN_AUTHORIZATION_ENDPOINT
: url to authorize for access token. Example: https://example.com/authoriize, it can be found on https://${FHIR_ISSUER}/.well-known/oidc-configurationWELL_KNOWN_TOKEN_ENDPOINT
: url to exchange token using auth code or refresh token for access token. Example: https://example.com/token, it can be found on https://${FHIR_ISSUER}/.well-known/oidc-configurationWELL_KNOWN_CAPABILITIES
: a comma-delimited set of capabilities, see https://hl7.org/fhir/smart-app-launch/conformance/index.html#capability-sets
If you want to reduce audit logs from Federated Access auth library, set the following environment variable before the service start:
export DISABLE_AUDIT_LOG=true
Note: default request / processing logs may still be available even when audit logs are disabled, however the supplemental SMART context and identity audit information will not be added to the default logs.
For the deployment setup, you will need to create a GCP project then run the following to enable needed APIs, create service accounts and grant permission to service accounts:
scripts/prepare_project_gae.bash -p ${PROJECT?}
This script with change enable the following required APIs:
iam
: for use to create short-live access token to acess FHIR APIcontainerregistry
: to store smart on FHIR proxy docker imageappengineflex
: GAE Flex will host the SMART-on-FHIR proxy
It will also set the following permissions:
- the GAE Service Account (SA) needs
serviceAccountTokenCreator
permissions to create short-live access tokens - the
fhiruser
SA, which will be used by the proxy to make calls into the FHIR store, needshealthcare.datasetViewer
,healthcare.datasetAdmin
,healthcare.fhirStoreAdmin
andhealthcare.fhirResourceEditor
to access FHIR API
Once the prepare_project
script completes, you will need to deploy the proxy
service in your project. It will need to make use of an OIDC server that mints
an access token that contains a specific aud
claim to indicate that this proxy
is the relying party. Once you have determined your OIDC_ISSUER
URL (i.e. the
iss
claim value) and aud
it will use for the proxy, run the following:
proxy/deploy/gae-flex/deploy.bash -p ${PROJECT?} -o ${OIDC_ISSUER?} -a ${AUD?}
Similarly to GAE deployment, we have prepare_project
and deploy
scripts for
GKE.
scripts/prepare_project_gke.bash -p ${PROJECT?}
proxy/deploy/gke/deploy.bash -p ${PROJECT?} -o ${OIDC_ISSUER?} -a ${AUD?}
Prepare your test FHIR store with some simple data:
-
Create a FHIR store named
smart-on-fhir
under a dataset namedtest
. Set environment variables for use in future steps:PROJECT=<your-project-id> LOCATION=us-central1 DATASET=test FHIR_STORE_ID=smart-on-fhir
-
Fetch basic dataset information via the proxy to see if project or service settings in general cause any errors:
curl "https://${PROJECT?}.uc.r.appspot.com/v1/projects/${PROJECT?}/locations/${LOCATION?}/datasets/${DATASET?}" -H "Authorization: Bearer ${TOKEN?}
Resolve any errors before moving on to the next steps.
-
Create a FHIR resource using Curl. Take note of the
id
returned in the response. SetPATIENT_ID
to thisid
for use in future steps. -
Allocate a test account SMART-on-FHIR token from your OIDC issuer. Make sure it has the
patient/*.read
scope orpatient/${PATIENT_ID?}.read
scope. -
Fetch the patient record like this:
curl "https://${PROJECT?}.uc.r.appspot.com/v1/projects/${PROJECT?}/locations/${LOCATION?}/datasets/${DATASET?}/fhirStores/${FHIR_STORE_ID?}/fhir/Patient/${PATIENT_ID?}" -H "Authorization: Bearer ${TOKEN?}
Validating opaque access token is heavy because it need to call a remote endpoint. We may use cache to improve it by storing the sha256 hash of access token as key and the result of the remote validation endpoint with min(token expiry, 10 minute) TTL.
To enable this feature:
- Grant iam role
roles/redis.editor
to Service accountfhiruser
:
gcloud projects add-iam-policy-binding --member serviceAccount:fhiruser@${PROJECT?}.iam.gserviceaccount.com --role roles/redis.editor
- Enable by setting env var:
export CACHE_ADDR=${REDIS_IP?}:${REDIS_PORT?}
- Also enable verify access token via userinfo endpoint by setting env var:
export USE_USERINFO_TO_VERIFY_ACCESSTOKEN=true
If you are using terraform script to deploy, change use_userinfo_to_verify_accesstoken
and enable_cache
to true in the head of deployment.hcl
.
Smart on FHIR proxy requires secrets pass to env var:
FHIR_ISSUER_CLIENT_SECRET
: client secret used to call OIDC issuerCLIENTS_OF_PROXY
: client secret app used tp call proxy
The proxy supports using GCP secretmanager to store these secrets. To enable this feature:
- Grant iam role
roles/secretmanager.viewer
to Service accountfhiruser
:
gcloud projects add-iam-policy-binding --member serviceAccount:fhiruser@${PROJECT?}.iam.gserviceaccount.com --role roles/secretmanager.viewer
- Enable by setting env var:
export USE_SECRET_MANAGER=true
- Use the key of secret instead of the secret in
FHIR_ISSUER_CLIENT_SECRET
andCLIENTS_OF_PROXY
- Store secret in secretmanager before start the service, example:
gcloud secrets create ${MY_SECRET_KEY?} --replication-policy=automatic
gcloud secrets versions add ${MY_SECRET_KEY?} --data-file="/path/to/file.txt"
If you are using terraform script to deploy, change use_secret_manager
to true in the head of deployment.hcl
.
./run_tests.bash
To run e2e tests, you will need to:
- Deploy the proxy, see the deployment section above.
- Deploy an OIDC server, and use the persona token as the RSA key. You may use the persona server for testing.
- The proxy is able to receive requests from the test runner.
- Test runner requires roles:
gcloud projects add-iam-policy-binding -q ${PROJECT?} \
--member serviceAccount:testuser@${PROJECT?}.iam.gserviceaccount.com --role roles/healthcare.datasetAdmin
gcloud projects add-iam-policy-binding -q ${PROJECT?} \
--member serviceAccount:testuser@${PROJECT?}.iam.gserviceaccount.com --role roles/healthcare.fhirStoreAdmin
gcloud projects add-iam-policy-binding -q ${PROJECT?} \
--member serviceAccount:testuser@${PROJECT?}.iam.gserviceaccount.com --role roles/healthcare.fhirResourceEditor
If you are using terraform script to deploy, change setup_e2e_test_project
to true in the head of deployment.hcl
.
Then you can run the tests:
go test ./proxy/integration_test -e2etest=true -iss=${YOUR_TEST_OIDC_ISSUER_URL?} -proxy=${YOUR_PROXY_URL?} -project=${YOUR_TEST_PROJECT?} -aud=${AUD_ACCEPTED_IN_PROXY?}