This is a Rust-based SMART-on-FHIR application that recreates Cerner's SMART-on-FHIR tutorial using Actix and Maud.
This repository is licensed Apache 2.0, with several exceptions:
- One CSS file is copied in verbatim from Cerner's tutorial repo,
this file is under
resources
The current application is a straightforward translation of Cerner's SMART-on-FHIR tutorial from static HTML and JavaScript into a Rust backend application that uses HTML macros defined using Maud and served using Actix. We then package the app into a Docker container, which is deployed via AWS Fargate.
Our application exposes five endpoints:
/callback, defined insrc/callback.rs/healthcheck.html, defined insrc/health.rs/index.html, defined insrc/index.rs/launch.html, defined insrc/launch.rs/standalone.html, defined insrc/standalone.rs
It also exposes endpoints to serve the contents of the /resources directory,
using the actix_files crate.
The /healthcheck.html endpoint provides a simple mechanism to check if the server is running.
The /launch.html endpoint is the endpoint that an EHR would call to launch your
SMART-on-FHIR application (for standalone launches, see /standalone.html).
This endpoint is responsible for starting the SMART authorization
sequence, and requesting the necessary Oauth scopes
for your application. This endpoint redirects to the FHIR authorization URL, starting the
authentication / authorization / credential exchange process.
Once a user authenticates against the EHR, the EHR will redirect to the /callback endpoint.
This endpoint completes the token exchange with with SMART-on-FHIR server, before redirecting
to the /index.html endpoint.
The /index.html endpoint is the endpoint that the /callback endpoint redirects to, after obtaining
valid credentials from the SMART-on-FHIR server. This endpoint uses the SMART-on-FHIR credentials to
request several observed values (including height, blood pressure, LDL/HDL) from the FHIR server,
and generates HTML displaying these values in a table.
The /standalone.html endpoint is used as part of a standalone app launch (most commonly
used for patient access to data from an app). In our example, we serve a page where a user
can input a SMART-on-FHIR server URL to start the launch workflow. This URL is passed to the
/launch.html endpoint.
For more on the EHR vs. standalone launch flows, see our documentation on how to launch the example app.
The app is packaged into a simple Docker container, using the Dockerfile in the root directory.
Once the container is built, it is pushed to AWS ECR;
example instructions can be found here.
We manage our deployment using Terraform,
which can be found in the iac directory. We make a few assumptions:
- You are deploying this application under a subdomain (e.g.,
fhir-example.translating.science, vstranslating.science) of a root domain - You have a multiple Account organization structure in AWS, where your DNS for your root domain
is managed in an
Infrastructureaccount that is separate from the account where you plan to deploy thefhir-exampleapplication (an example of this can be found here)
We have three deployment steps:
iac/ecr, which is used initially to set up an ECR repository that we will push our docker container to.iac/dns, which is used initially to set up DNS records and certificates. This allows us to deploy the app behind HTTPS in Fargate, with AWS managing the certificats via AWS ACM.iac/fargate, which sets up Fargate and its accompanying resources.
We use several Terraform variables:
aws_profile: Used byecr,dns, andfargate. This is the name of the AWS profile used to deploy into the AWS account for thefhir-exampleapplication.aws_profile_main: Used only bydns. This is the name of the AWS profile used to deploy into the AWSInfrastructureaccount.domain: Used only bydns. This is the name of the root domain.subdomain: Used by bothdnsandfargate. This is the name of the subdomain.image: Used only byfargate. This is the repository/tag of the Docker image to use.
There are additional optional variables defined in the IaC.
We have an example deployment running at https://fhir-example.translating.science. If you'd like to run your own deployment, you can follow the steps below.
As of June 12th, 2024, the cost of running a Fargate service in AWS' us-west-2
region are:
| Price | |
|---|---|
| per vCPU per hour | $0.04048 |
| per GB per hour | $0.004445 |
Our IaC scripts configure a service running 0.25 vCPU and 0.5 GB of memory. Thus, the computing costs of running the service are approximately:
| Timespan | Price |
|---|---|
| 1 hour | $0.01 |
| 1 day | $0.30 |
| 1 month (31 days) | $9.18 |
In addition to these costs, you will also need to pay for AWS' networking services.
Assuming you have Docker installed, you can
build a Docker container by running docker build . in the repository root.
Once you have built your Docker container, you need to create a private repository in the
AWS account you plan to deploy the application in. This step needs to happen exactly once.
To do this, move into the iac/ecr directory, and run terraform init to initialize the
terraform providers.
To create the terraform plan for this step, run:
terraform plan -var="aws_profile=<application_profile>" -out=plan
This is a good time to check the generated plan to make sure it looks correct. Assuming that your plan creates successfully, you can then create the resources by running:
terraform apply plan
This step is nearly instantaneous.
The terraform apply will output the URL of your ECR repository. With this, you can push your container to ECR by running
Step 4 and onwards in this AWS tutorial.
Move into the iac/dns directory, and run terraform init to initialize the terraform
providers.
To create the terraform plan for this step, run:
terraform plan -var="domain=<mydomain>" -var="subdomain=fhir-example.<mydomain>" -var="aws_profile=<application_profile>" -var="aws_profile_main=<infrastructure_profile>" -out=plan
Check your plan, and if it looks correct, deploy by running terraform apply plan.
This step should take approximately 5 minutes.
Move into the iac/fargate directory, and run terraform init. To generate a plan, you
will need to run:
terraform plan -var="subdomain=fhir-example.<mydomain>" -var="aws_profile=<application_profile>" -var="image=<application_account>.dkr.ecr.us-west-2.amazonaws.com/<repository_name>:<tag>" -out=plan
Check your plan, and if it looks correct, deploy by running terraform apply plan.
This step takes approximately 1-3 minutes to run. Once the resources are created, it takes
several minutes for Fargate to launch your instances and start your container.
You can launch your application by going to the SMART Sandbox Launcher. You will want to use the R4 FHIR version, and either the Provider EHR launch type or one of the Standalone launch types.
For the SMART Sandbox Launcher's validation settings (on the "Client Registration & Validation" tab of the interface), you will want to configure the app as a confidential symmetric app. For this, you will need to provide the following info:
- FHIR scopes: This is a whitespace delimited string that explains what FHIR scopes our app wants to access.
Our app uses the
patient/Patient.read patient/Observation.read launch launch/patient online_access openid profilescopes. - Client ID and secret: These are used to perform basic authentication as part of the
confidential symmetric flow. We set these values in our app using the environment variables
FHIR_EXAMPLE_CLIENT_IDandFHIR_EXAMPLE_CLIENT_SECRET. The default values areFHIR_EXAMPLE_CLIENT_ID=rust-smart-fhirandFHIR_EXAMPLE_CLIENT_SECRET=rust-smart-fhir-secret. - Allowed redirect URL: When we try to obtain an authentication code, we provide a
URL that the SMART-on-FHIR server will redirect the user to. The SMART-on-FHIR server will validate this URL against this list of allowed redirect URLs (which we have provided offline).
For our app, the URL should point to our
/callbackendpoint. If you are running the app locally, that URL will behttp://127.0.0.1:8080/callback. Otherwise, you should configure the URL by setting theFHIR_EXAMPLE_DOMAINenvironment variable.
When you launch the app through the SMART sandbox launcher, you will need to pick a patient. If you do not, the SMART sandbox launcher will display a patient picker as part of the launch flow. Note that not all of the patients in the SMART sandbox launcher will have observation data for the codes we request.
In addition to the EHR launch flow, we support standalone launches.
When you select a Standalone launch in the SMART Sandbox Launcher, it will provide you with a Server's FHIR Base URL that
we will launch our app against. Copy this URL from the SMART Sandbox Launcher site, and navigate to the /standalone.html endpoint of your server. You will provide
this URL as the URL of EHR endpoint: in the form on that page.