Skip to main content

Introduction

What is SMART on FHIR?โ€‹

SMART on FHIR (Substitutable Medical Applications, Reusable Technologies) is an open standard that defines how healthcare apps connect to Electronic Health Record systems. It layers OAuth 2.0 and OpenID Connect on top of HL7 FHIR R4 to answer three questions:

  1. Which EHR is the app talking to? โ€” resolved via the iss parameter and dynamic discovery of /.well-known/smart-configuration
  2. Who is the patient and clinician in context? โ€” carried in the launch token and returned as extras in the OAuth2 token response
  3. What data can the app access? โ€” governed by SMART scopes (patient/Patient.rs, patient/Condition.rs, etc.) granted during the OAuth2 flow

The result is a single app that can launch inside any SMART-compliant EHR โ€” Epic, Cerner, and others โ€” without being hard-coded to any one vendor's proprietary API.


What this client providesโ€‹

This project is a production-ready SMART on FHIR EHR launch client targeting Epic, built on Spring Boot 3 and Java 21. It implements the complete launch handshake and exposes the retrieved FHIR data through a Thymeleaf UI and a REST API.

Epic EHR  โ”€โ”€launchโ”€โ”€โ–ถ  Your App  โ”€โ”€FHIR APIโ”€โ”€โ–ถ  Epic FHIR R4
SMART HAPI client
handshake + bearer token

What is handled automaticallyโ€‹

Once the SMART handshake completes, the app gives you:

  • SmartLaunchContext โ€” a typed session object holding the access token, patient ID, encounter ID, needPatientBanner flag, granted scopes, token expiry, and OIDC user profile
  • FhirClientFactory โ€” a HAPI R4 IGenericClient pre-configured with a bearer token interceptor, pointed at the correct FHIR server for this hospital
  • TokenRefreshFilter โ€” transparent token refresh 120 seconds before expiry on every /api/** request โ€” clinicians never see an unexpected 401
  • SecurityConfig โ€” Spring Security 6 filter chain with session fixation protection, CSRF (Double-Submit Cookie), and denyAll() fallback for unknown routes
  • A working UI โ€” patient banner, dashboard, conditions table, medications table, clinician profile from OIDC

What you build on topโ€‹

The app is a functional starting point. You extend it by:

  • Adding new FHIR resource endpoints in PatientDataController
  • Adding new UI pages in UiController and templates/
  • Requesting additional scopes in application.yml
  • Connecting to your own backend services or databases

SMART App Launch v2.2โ€‹

This client implements SMART App Launch 2.2, the current specification. Key differences from v1 (which most older libraries including HealthLX implement):

CapabilitySMART v1SMART v2 (this client)
Server discoveryStatic YAML configDynamic /.well-known/smart-configuration
PKCEOptionalRequired (S256 enforced)
Scope syntaxpatient/*.readpatient/Patient.rs
aud parameterNot requiredRequired by Epic
Standalone launchPartialFull launch/patient support

EHR launch vs standalone launchโ€‹

The app supports both SMART launch modes:

EHR launchโ€‹

A clinician clicks your app's button inside Epic Hyperspace. Epic sends:

GET /launch?iss=https://hospital.epic.com/.../FHIR/R4&launch=abc123opaque

The launch token pre-selects the patient and encounter. After the OAuth2 handshake, SmartLaunchContext.patientId() is always populated.

Standalone launchโ€‹

A user navigates directly to your app's URL (bookmarked, from a patient portal, etc.). Only iss is provided โ€” no launch token. The OAuth2 flow uses launch/patient scope, which may trigger a patient picker in the auth server.

SmartLaunchController detects the mode automatically by checking whether the launch query parameter is present.


Technology stackโ€‹

LayerTechnologyVersion
RuntimeJava21
FrameworkSpring Boot3.3.5
SecuritySpring Security6.x
FHIR clientHAPI FHIR7.4.5
FHIR versionHL7 FHIRR4
TemplatingThymeleaf3.x
BuildMaven3.9+
TestingJUnit 5 + WireMock5.x / 3.6.0

Project structureโ€‹

smart-fhir-client/
โ”œโ”€โ”€ src/main/java/com/smartfhir/client/
โ”‚ โ”œโ”€โ”€ auth/ PKCE generation + authorize URL builder
โ”‚ โ”œโ”€โ”€ context/ SmartLaunchContext + extraction from token response
โ”‚ โ”œโ”€โ”€ discovery/ /.well-known/smart-configuration fetching + caching
โ”‚ โ”œโ”€โ”€ fhir/ HAPI FHIR client factory + data controller
โ”‚ โ”œโ”€โ”€ launch/ /launch endpoint โ€” EHR and standalone modes
โ”‚ โ”œโ”€โ”€ oidc/ id_token validation + UserProfile extraction
โ”‚ โ”œโ”€โ”€ refresh/ Proactive token refresh filter + service
โ”‚ โ”œโ”€โ”€ security/ Spring Security 6 filter chain + SMART session bridge
โ”‚ โ”œโ”€โ”€ token/ /callback endpoint + token exchange service
โ”‚ โ””โ”€โ”€ ui/ Thymeleaf page controller
โ”œโ”€โ”€ src/main/resources/
โ”‚ โ”œโ”€โ”€ templates/ Thymeleaf HTML pages
โ”‚ โ”œโ”€โ”€ static/ CSS + JS
โ”‚ โ”œโ”€โ”€ application.yml
โ”‚ โ”œโ”€โ”€ application-epic.yml
โ”‚ โ””โ”€โ”€ application-smart.yml
โ””โ”€โ”€ src/test/java/
โ””โ”€โ”€ com/smartfhir/client/
โ””โ”€โ”€ it/ Epic + SMART Health IT integration tests

Next stepsโ€‹

Check prerequisites

Ensure Java 21, Maven 3.9+, and a compatible IDE are installed.

Run the quick start

Get the app running against the free SMART Health IT sandbox โ€” no Epic account needed.

Register with Epic

Create a free developer account and get a non-production client ID for Epic sandbox testing.

Read the architecture docs

Understand the launch flow and package structure before extending the app.

Prerequisites โ†’ Quick Start โ†’