SMART on FHIR: Secure Healthcare App Integration

~15 min read
SMART on FHIR - Secure healthcare app integration

The Reader's Dilemma

Dear Marilyn,I want to build a healthcare app that works with Epic, Cerner, and other EHR systems. But the security requirements are terrifying—HIPAA, OAuth, patient consent. How do I build something that's both useful AND secure? Is there a standard way to do this?

Marilyn's Reply

You've discovered the golden key to healthcare app development: SMART on FHIR. It stands for "Substitutable Medical Applications, Reusable Technologies" and it's the standard that lets apps plug into EHR systems like apps plug into your smartphone.

The Spark: Understanding SMART on FHIR

What is SMART on FHIR?

SMART on FHIR is a set of open standards that enable third-party applications to integrate with EHR systems securely. Think of it as the "App Store" model for healthcare—apps can be launched from within the EHR, access patient data with proper authorization, and provide specialized functionality.

Security

OAuth 2.0 for authorization, OpenID Connect for authentication

Scopes

Fine-grained access control to specific resource types

Launch Context

Apps know which patient/encounter is in context

Substitutable

Apps work across different EHR platforms

The SMART Launch Sequence

When a clinician launches your app from within the EHR, a carefully choreographed dance begins:

1

EHR Launch

The EHR opens your app's launch URL with a launch parameter and iss (issuer) URL.

2

Discovery

Your app fetches the FHIR server's capability statement and OAuth endpoints from /.well-known/smart-configuration.

3

Authorization Request

Your app redirects to the authorization endpoint, requesting specific scopes (permissions).

4

User Consent

The EHR presents a consent screen; the user approves or denies access.

5

Token Exchange

Your app exchanges the authorization code for access and refresh tokens.

6

API Access

Your app uses the access token to make FHIR API calls for the patient in context.

Understanding Scopes

SMART scopes define exactly what data your app can access. They follow a pattern: context/resourceType.permission

ScopeMeaning
patient/Patient.readRead the current patient's demographics
patient/Observation.readRead the current patient's observations
patient/MedicationRequest.writeCreate/update medication orders for the patient
user/Patient.readRead any patient the user has access to
launch/patientReceive the patient ID in launch context
openid fhirUserGet the logged-in user's identity

Principle of Least Privilege: Only request the scopes your app actually needs. Requesting patient/*.read when you only need vitals will raise red flags during app review.

Code Example: SMART Launch

// Step 1: Handle the launch request

app.get('/launch', async (req, res) => {
  const { launch, iss } = req.query;
  
  // Store launch token for later
  req.session.launch = launch;
  req.session.iss = iss;
  
  // Fetch SMART configuration
  const config = await fetch(`${iss}/.well-known/smart-configuration`)
    .then(r => r.json());
  
  // Build authorization URL
  const authUrl = new URL(config.authorization_endpoint);
  authUrl.searchParams.set('response_type', 'code');
  authUrl.searchParams.set('client_id', CLIENT_ID);
  authUrl.searchParams.set('redirect_uri', REDIRECT_URI);
  authUrl.searchParams.set('scope', 'launch patient/Patient.read patient/Observation.read');
  authUrl.searchParams.set('state', generateState());
  authUrl.searchParams.set('aud', iss);
  authUrl.searchParams.set('launch', launch);
  
  res.redirect(authUrl.toString());
});

// Step 2: Handle the callback and exchange code for token

app.get('/callback', async (req, res) => {
  const { code, state } = req.query;
  
  // Verify state to prevent CSRF
  if (state !== req.session.state) {
    return res.status(400).send('Invalid state');
  }
  
  // Exchange code for tokens
  const tokenResponse = await fetch(tokenEndpoint, {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      grant_type: 'authorization_code',
      code,
      redirect_uri: REDIRECT_URI,
      client_id: CLIENT_ID,
    })
  }).then(r => r.json());
  
  // Store tokens and patient context
  req.session.accessToken = tokenResponse.access_token;
  req.session.patientId = tokenResponse.patient;
  
  res.redirect('/app');
});

Major EHR Platforms Supporting SMART

Epic

App Orchard marketplace, extensive FHIR R4 support

Cerner (Oracle Health)

Code Console for app development, FHIR R4

Allscripts

Open API program, SMART on FHIR certified

athenahealth

Marketplace apps, FHIR API access

The Gauntlet: Test Your Knowledge

Quick Check

What does SMART stand for in SMART on FHIR?

Quick Check

What authentication protocol does SMART on FHIR use?

Quick Check

What is the purpose of SMART scopes?

Quick Check

What is the 'launch context' in SMART on FHIR?

Quick Check

Where does an app discover the EHR's OAuth endpoints?