On this page:

7.9SMART on FHIR: Introduction

 

The SMART on FHIR specification is a popular choice for providing a consistent approach to security and data requirements for health applications. SMART on FHIR defines a workflow that an application can use to securely request access to data, and then receive and use that data.

The SMART App Gallery contains many examples of SMART on FHIR apps, and more are being created every day.

In a nutshell, SMART on FHIR can be thought of as three things:

  1. Identity and Access Management: SMART uses the OpenID Connect identity management protocol, which allows applications to request access to clinical data. This access might be simple read-only access to a few records, broad read/write access to an entire EHR, or anything in between. The SMART specifications describe a specific flavour of OpenID Connect that is customized for use in the health context.

  2. Access to Data: SMART uses the FHIR standard for actually reading and/up updating that data. This means that in a SMART on FHIR architecture, a set of FHIR services are available for use by SMART applications. These services are secured using the Identity and Access Management layer described above.

  3. Launch: For web-based applications, SMART describes a consistent URL scheme for portals, EHRs, etc., to use for launching web-based applications with a set of context being passed to the application. This context can include information about the currently selected patient, clinical encounter, styling information, etc.

Platforms

Applications can take many forms:

  • Web Apps: Web-based applications are presently the most common type of SMART application found in the gallery. Part of the appeal of the SMART on FHIR specification is that in many cases it is possible to create useful applications without needing to create a backend layer. In other words, a meaningful application can be written in purely HTML + Javascript + CSS using a SMART on FHIR server as the application backend.

  • Mobile Apps: The technologies used in the SMART on FHIR specification are equally useful for creating rich mobile/mHealth applications, too.

  • Server / Back Office Apps: Interactive user-facing applications are not the only purpose for the SMART on FHIR specification. By providing a consistent way of authorizing access to data, backend processing applications can be created too.

7.9.1The SMART Launch Sequence

 

This section explains how the launch sequence for a SMART on FHIR application actually works.

Underlying Technology

First, let's clarify a few technologies we are using.

  • OAuth2 (sometimes called OA2): This is an authorization protocol that is widely used on the Internet. Consumers are often familiar with this protocol, even if they do not know its name, as it is the protocol that powers processes such as "Login using Google" and "Login using Facebook". OAuth2 allows a user to authenticate to Application A using their account credentials in Application B without Application A ever needing to know (or being able to see) those credentials. As a part of the protocol, the application can also request "Scopes", which are permissions to perform specific functions. For example, an application using "Login using Facebook" might request permission to "see my friend list" or "post on my behalf".

  • OpenID Connect (sometimes called OIC): This protocol builds on top of OAuth2 by adding identity management functions such as the ability for the application to learn the user's demographic details and other facts. In other words, think of OpenID Connect as "The OAuth2 Protocol with identity management features added on top".

Participants

In the SMART on FHIR Authorization process, there are four participants:

  • The User: This is the person who is actually logging into the application to perform some function. Not all SMART on FHIR flows actually require a user to be present but most do.

  • The SMART on FHIR Application (referred to as the "SMART App" or "Client" below): This is an application that the user wishes to access to perform some function.

  • The Authorization Server: This is an OpenID Connect compliant web server that is able to authenticate users and issue Access Tokens. Smile CDR is able to perform this role but other Authorization Servers may also be used.

  • The Resource Server: This is a FHIR compliant web server that is able to respond to FHIR REST requests using Access Tokens issued by the Authorization Server. Smile CDR is able to perform this role but other Resource Servers may also be used instead (or in combination with Smile CDR).

7.9.2The Interactive Launch Process

 

The following diagram illustrates the sequence of events when a SMART on FHIR application is started.

SMART on FHIR Architecture

Step 1: Application Launch

For web-based applications, the user will generally follow a link to the application. This link includes parameters specifying the "launch context".

For example, suppose a SMART on FHIR application has been created for visualizing the trend of a specific lab test over time. The workflow for this application would involve users working in another system (such as an EMR or a clinical portal) and following a link to the application from within a view that is specific to a particular patient. The link to launch the SMART on FHIR application would include details about which patient was being viewed.

Note that this step is optional, and it is only used when the application is being launched by another application. In other architectures (e.g. mobile applications and standalone web applications), the authorization process might not work this way.

The following SMART on FHIR compliant launch URL shows a launch of the example "MyLabViewer" application: https://mylabviewer.com/launch.html?iss=https://try.smilecdr.com:8000&launch=wd9833&patientId=Patient/123

Note the URL parameters in the example:

  • iss is the base URL for a FHIR server that the application should use to fetch data. In the example above, the issuer is https://try.smilecdr.com:8000, and this is assumed to be the base URL of a FHIR server, meaning that the server's CapabilityStatement can be loaded at the URL: https://try.smilecdr.com:8000/metadata.
  • launch is a one-time "nonce" (a string of repeating characters) that is used to prevent security issues such as replay attacks.
  • patientId is the FHIR ID of the currently selected patient.

As a part of the launch sequence, the SMART Application should fetch the FHIR server's CapabilityStatement from [iss]/metadata. Within this CapabilityStatement will be a pair of extensions containing URLs that can be used to authorize the user.

For example, fetching the CapabilityStatement for the example above returns the following result (note that most of the content has been omitted for brevity):

{
  "resourceType": "CapabilityStatement",
  "software": {
    "name": "Smile CDR",
    "version": "2017.11.R01"
  },
  "fhirVersion": "3.0.1",
  "rest": [
    {
      "mode": "server",
      "security": {
        "extension": [
          {
            "url": "http://fhir-registry.smarthealthit.org/StructureDefinition/oauth-uris",
            "extension": [
              {
                "url": "token",
                "valueUri": "https://try.smilecdr.com:9200/oauth/token"
              },
              {
                "url": "authorize",
                "valueUri": "https://try.smilecdr.com:9200/oauth/authorize"
              }
            ]
          }
        ]
      }
    }
  ]
}

The CapabilityStatement contains two important URLs:

  • The Authorization Endpoint is the URL that the user will be directed to in order to actually log in.
  • The Token Endpoint is a service endpoint that the SMART on FHIR Application may communicate with in order to exchange data with the Authorization Server.

Step 2: Request Access

The application directs the user to an OpenID Connect Authorization Server. This process is the same one used in the "Login using Facebook" example above but in this case instead of using Google or Facebook we are using a SMART on FHIR compliant authorization server. The general principle is that the user is passed to a third party website where they are asked to enter credentials (if they aren't already logged in) without the SMART on FHIR application ever knowing what those credentials are.

In the case of web applications, this step generally works by having the user follow a link that temporarily takes the user to the third party authorization server. In the case of mobile applications, a web browser window is temporarily opened on the user's device in order to allow the user to access the authorization server.

When the SMART on FHIR Application directs the user to the authorization server, it also requests a set of "OAuth2 Scopes". These scopes are defined in the SMART on FHIR specification and include the following examples (note that these are only examples, not an exhaustive list). See SMART on FHIR Scopes for a complete list of scopes defined in the specification, and see Smile CDR Supported Scopes for a list of scopes supported by Smile CDR.

  • patient/*.read - The application is requesting access to read any data about any Patient in the system.
  • profile - The application is requesting access to the user's profile (demographic data such as name and email address).

Step 3: Receive Token

Once the user has successfully authenticated with the Authorization Server, they are directed back to the SMART on FHIR application. As a part of this step, the SMART on FHIR application receives an "Access Token", which is a long string of characters that will serve as credentials in the steps below.

At a glance, this token looks like a long string of random letters and numbers. In reality it is not random at all. OpenID Connect specifies that the token is a JSON Web Token (JWT). It is essentially a small JSON document that has been digitally signed. The document contains a few small pieces of information about where the token was issued and what application requested it.

Step 4: Client Request to Access or Modify Clinical Data

The application may now invoke FHIR services by communicating with the Resource Server. A SMART on FHIR Resource Server is a FHIR server that supports a set of FHIR operations, and requires an Access Token as a part of this request.

The request might be a FHIR search operation for discovering data, or a FHIR create operation for uploading newly created data. The scopes that were requested and granted by the Authorization Server will be used to determine whether the user has appropriate permission to perform the specific function they are invoking.

A sample HTTP request follows. Note the presence of an Authorization header whose value is the string Bearer followed by the access token. For readability, the token has been shortened (real tokens are typically much longer).

GET /Observation?subject=Patient/123
Accept: application/fhir+json
Authorization: Bearer eyJraWQiOiJ1b.o_GAe_P5E-y4GrCl.gTYd4is8zAQ

Step 5: Validate Token

The Resource Server should now validate that the token is acceptable. The token has been digitally signed by the Authorization Server, and by verifying this signature the Resource Server can know that it is legitimate.

The verification uses a public key that can either be provided out-of-band or fetched as required by the Resource Server. This public key matches a private key held by the Authorization Server.

As shown above, the token will take a form similar to the following: eyJraWQiOiJ1b.o_GAe_P5E-y4GrCl.gTYd4is8zAQ (although it will generally be longer than this example). This format is called JWT (JSON Web Token), and libraries are available for most languages that can be used to decode this token. Decoding the token results in a JSON document similar to the following:

{
   "azp":"my-client-id",
   "profile":"http:\/\/example.org:8884\/fhir\/RelatedPerson\/999",
   "iss":"http:\/\/example.org:8884",
   "exp":1516069149,
   "iat":1516069089,
   "jti":"95d12878-dd09-4d61-9930-d4b3cf077ec2"
}

Each key in this payload is called a "claim", and the claims are defined by the JWT, OAuth2, and OpenID Connect specifications. Note the following:

  • azp: This is the ID of the client (SMART Application) on whose behalf this token was issued.
  • profile: This is a FHIR resource URL (i.e. a resource ID) that can be used to fetch a resource corresponding to the user who authenticated and requested this token.
  • iss: This is the issuer, meaning the base URL of the Authorization Server. According to the OpenID Connect specification, a client can add /.well-known/openid-configuration to this URL and fetch a metadata statement describing the endpoints and functionality of the Authorization Server.
  • exp: This is the expiry date of the token, specified as the number of seconds since the unix epoch (not to be confused with the Java Date time format that is the number of milliseconds since the unix epoch). This property is important since the Resource Server can use it to determine whether a token is still valid without needing to talk to the Authorization Server, and because the token is digitally signed the SMART Application is not able to alter this time.

Step 6 (optional): Introspect Token and Request

If the Authorization Server and the Resource Server are different servers (as they often are in real world architectures), the Resource Server often needs to know more about the user, including their name and demographics, and which scopes have been approved for the user by the Authorization Server.

The following example shows a request being made by a Resource Server to an Authorization Server.

POST /oauth/token/introspect
Accept: application/json
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer 23410913-abewfq.123483

token=eyJraWQiOiJ1b.o_GAe_P5E-y4GrCl.gTYd4is8zAQ

The following payload shows an example response. Note that the Authorization Server is indicating that the user has been approved for a SMART Patient read scope and several others.

{
  "active": true,
  "scope": "patient/*.read online_access openid profile",
  "client_id": "growth-chart",
  "token_type": "Bearer",
  "exp": 1516069149,
  "iat": 1516069089,
  "iss": "http://localhost:8884"
}

Step 7: Server Response to Access or Modify Clinical Data

Assuming that the access token has been verified correctly and the token introspection shows that the user has appropriate permissions to perform the desired functions, the server will now respond to the given FHIR query.

Step 8 (optional): Refresh Token

As discussed in Step 5 (Validate Token) above, access tokens can be verified by the Resource Server simply by verifying the digital signature on the token using a key.

This is a useful property of the process as it means that the two different logical servers have less (or no) need to communicate directly with each other in order to validate individual client requests. This is good for scalability (indeed this is a common design principle in the popular Microservice Architecture application design).

However, his property does have one important drawback: it means that the Authorization Server is not able to notify the Resource Server if it wishes to revoke an access token. Because of this, the Authorization Server will often specify an access token with a short expiry time (often as short as 60 seconds or less), and will issue a second token called a "Refresh Token" while it is issuing an access token to the SMART Application.

This Refresh Token may be used by the SMART Application to request a new access token when the current one has expired (or perhaps beforehand). By issuing Access Tokens with a short expiry time and regularly requiring the SMART Application to request a new Access Token, the Authorization Server is able to ensure that the SMART Application only has a valid Access Token for an appropriate amount of time (in other words, the Authorization Server can effectively revoke tokens within a short amount of time).

Refresh tokens may also be used by the application to perform background tasks, such as periodic fetches of data on behalf of the user, even when the user is not actively using the client application.

The following example shows a SMART Application making a request to the Authorization Server to grant a new Access Token using a Refresh Token (for readability each parameter is shown on a new line but in a real request these would be joined together in one long line):

POST /oauth/token
Accept: application/json
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token
&refresh_token=37d53025-964c-4ab4-afcb-54ea5d892ebb
&client_id=growth-chart
&client_secret=someclientsecret

The server will then respond with a response similar to the following, which includes a new Access Token and a Refresh Token that may be used to request the next Access Token (note that the Authorization Server may or may not reuse the same Refresh Token at its own discretion).

{
   "access_token":"eyJraWQiOiJ1bml0dG.eyJhenAiOiJteS1j.THbNnfuX7Ly4g",
   "token_type":"bearer",
   "refresh_token":"c5bbdcab-2e02-4c80-bb2c-3022c66b2a7e",
   "expires_in":59,
   "scope":"patient/*.read online_access openid"
}

7.9.3Authorization Flows / Grant Types

 

Smile CDR supports several authorization flows, which are specific workflows that a SMART Application can go through in order to request an Access Token. The correct flow to select depends on the type of application being authorized.

The "Authorization Code" Flow

This flow should be used by applications that are able to "keep a secret". This typically means applications that have a backend server, and this backend server is fully secured.

The secret referred to here is a password that is specific to the application itself (not the user). Examples of applications that should not use the Authorization Code flow include:

  • Native Mobile Apps where the app is installed on a user's device and the app does not have a backend server. Authorization Code is inappropriate because the secret would need to be stored in the code, and the user could theoretically decompile the application and retrieve it.
  • Single Page WebApps where the entire application consists of HTML+JS code. Authorization Code is inappropriate because the User has access to the source code of these applications.

The Authorization Code flow works as follows:

Step 1: The SMART Application redirects the user to the Authorization Server (or in the case of a mobile application, opens a web browser) at a URL similar to the following: http://myserver:9200/oauth/authorize?response_type=code&scope=openid+profile+patient/*.read&client_id=my-client-id&state=af0ifjsldkj&redirect_uri=https://myapp.example.com/cb

Step 2: The Authorization Server presents the user with a login screen, and if necessary asks the user to approve the selected scopes.

Step 3: The Authorization Server redirects the user back to the Callback URL, adding several parameters to the URL. For example: https://myapp.example.com/cb?state=af0ifjsldkj&code=cdcd883gr3g8dggakH

The client should verify that the state code matches the code it originally provided. The code parameter is a temporary token (an "Authorization Code") provided by the Authorization Server that the SMART Application can now use to exchange for an Access Token.

Step 4: The SMART Application invokes a REST method on the Authorization Server to exchange the Authorization Code for an Access Token. This is done using a request such as the following: http://myserver:9200/oauth/token?client_id=my-client-id&client_secret=MY_CLIENT_SECRET&grant_type=authorization_code&code=cdcd883gr3g8dggakH&redirect_uri=https://myapp.example.com/cb&scope=openid&state=af0ifjsldkj

The Authorization Server will respond with a response similar to the following, which includes the Access Token:

{
   "access_token":"eyJraWQiOiJ1.eyJhenAiOiJteS1jbGllb.Bv42OB0p",
   "id_token":"eyJra33ed8dofh.doh3ohfeisgdOiJteS1jbGllb.u44hdhB0p",
   "token_type":"bearer",
   "expires_in":59,
   "scope":"openid profile patient/*.read"
}

The "Implicit Grant" Flow

This flow is used for applications that are unable to "keep a secret". It should only be used in these cases.

Step 1: The SMART Application redirects the user to the Authorization Server (or in the case of a native application, opens a web browser) at a URL similar to the following: http://myserver:9200/oauth/authorize?response_type=id_token%20token&scope=openid+profile+patient/*.read&client_id=my-client-id&state=af0ifjsldkj&redirect_uri=https://myapp.example.com/cb

Step 2: The Authorization Server presents the user with a login screen, and if necessary asks the user to approve the selected scopes.

Step 3: The Authorization Server redirects the user back to the Callback URL, adding several parameters to the URL. For example: https://myapp.example.com/cb?state=af0ifjsldkj&access_token=cdcd883gr3g8d.ggakHeuedhd&token_type=bearer&expires_in=60

In this response the access_token parameter contains the Access Token that has been granted.

The "Resource Owner Password Credentials" Flow

This flow is dangerous to use with an app that isn't completely trusted. This is because this flow allows the Application to see the raw password that the user enters. It should generally only be used for applications that were developed by the owner of the Resource Server.

This flow relies on credentials being collected directly in the source application and then transferred to the Authorization Server for validation.

To perform the Resource Owner Password Credentials flow, the Application collects the user's username and password directly in the Application UI. It then uses an HTTP POST to the token endpoint in order to request an access token.

The following example shows a SMART Application making a request to the Authorization Server to grant a new Access Token using the Resource Owner Password Credentials flow (for readability each parameter is shown on a new line but in a real request these would be joined together in one long line):

POST /oauth/token
Accept: application/json
Content-Type: application/x-www-form-urlencoded

grant_type=password
&client_id=growth-chart
&client_secret=someclientsecret
&username=someuser
&password=someuserspassword
&scope=openid+profile+patient.%2A%2Fread

The Authorization Server will respond with a response similar to the following, which includes the Access Token:

{
   "access_token":"eyJraWQiOiJ1.eyJhenAiOiJteS1jbGllb.Bv42OB0p",
   "id_token":"eyJra33ed8dofh.doh3ohfeisgdOiJteS1jbGllb.u44hdhB0p",
   "token_type":"bearer",
   "expires_in":59,
   "scope":"openid profile patient/*.read"
}
# Launch Context Scopes

Background: Mobile App and Patient Portal

In many cases an app wants to be authorized without knowing which patient it is expected to display.

For example, consider a 3rd party Mobile App that is authorizing itself against a hospital Patient Portal that acts as both a SMART on FHIR compliant Authorization Server and Resource Server. Users already have an account (with username and password) in the Patient Portal application, and by authorizing against those credentials the Mobile App can download data from the Patient Portal.

In this case the user is associated with a Patient resource ID on the Resource Server, but neither the Mobile App nor the user are aware of what resource ID that would be.

Background: Micro-Application Portal

Another use-case for launch scopes would be a portal which functions as a "desktop" of micro-applications (small web applications that do one thing, and do it well).

In this case, it could be assumed that the portal itself has a concept of which Patient (and/or which practitioner, which location, etc) is currently selected. The user's expectation would be that if a Patient is selected in the portal, any micro-apps would launch with the same Patient selected.

7.9.4Launch Scopes

 

SMART on FHIR specifies a set of scopes which request that the Authorization Server return the launch context to the Client.

From the Client's perspective, a flow using a launch scope works as follows:

  • The client performs an OAuth2 grant request (typically using the implicit or authorization code flows). In the initial authorize request, the client requests the launch/patient scope. This is a request for the Authorization Server to include a patient launch context.

http://myserver:9200/oauth/authorize?response_type=code&scope=openid+profile+launch/patient&client_id=my-client-id&state=af0ifjsldkj&redirect_uri=https://myapp.example.com/cb

  • At the end of the flow (several steps have been skipped here) when the access_token and id_token are received, the response will contain an additional claim called patient.
{
   "access_token":"eyJraWQiOiJ1bml0dGV (..snipped..)",
   "token_type":"bearer",
   "scope":"launch/patient openid profile",
   "id_token":"eyJraWQiOiJ1bml0dGVzdC1 (..snipped..)",
   "patient":"123"
}

7.9.5The OpenID and Profile Scopes

 

The openid and profile scopes are used by the Client to request user identity information from the Authorization Server as a part of the auth flow.

When these scopes are requested, an additional claim is returned by the authentication server called id_token. This claim is called the ID Token, and you can see it being returned in many of the examples above.

The ID Token is a Signed JWT. It may be decoded, revealing a number of attributes.

{
   "jti":"d34af0de-e65a-4c19-8e38-365916948356"
   "at_hash":"xDKTbwLJPrb6hr_loWD8Qw",
   "sub":"user123",
   "profile":"http:\/\/example.com\/issuer\/fhir\/RelatedPerson\/123",
   "iss":"http:\/\/example.com\/issuer",
   "nonce":"NONCETEXT-123",
   "aud":"my-client-id",
   "auth_time":1532416414,
   "exp":1532416474,
   "iat":1532416414,
   "name":"John Smith",
   "given_name":"John",
   "family_name":"Smith",
}