Skip to main content

SMART on FHIR Client

v1.0.0 · Spring Boot 3.3 · HAPI FHIR 7.4 · Java 21 · Apache 2.0

A production-ready SMART on FHIR EHR launch client for Epic, built on Spring Boot 3 and Java 21. It implements the complete SMART App Launch v2.2 handshake — PKCE S256, dynamic discovery, token exchange, proactive token refresh, OIDC id_token validation — and exposes retrieved FHIR data through a clinical Thymeleaf UI and a REST API.

What it provides out of the box

Epic EHR ──launch──► SMART Client :8080 ──FHIR API──► Epic FHIR R4
SMART v2.2 handshake HAPI R4 + bearer token

Once you drop this into a Spring Boot project, you get:

ComponentWhat it does
SmartLaunchControllerHandles GET /launch, runs SMART discovery, builds PKCE, redirects to Epic
SmartCallbackControllerHandles GET /callback, exchanges code for tokens, validates id_token
SmartLaunchContextTyped session object: access token, patient ID, encounter ID, needPatientBanner, scopes, expiry
FhirClientFactoryHAPI R4 client per session, pre-configured with bearer token
TokenRefreshFilterProactive refresh 120 s before expiry — clinicians never see a 401
IdTokenValidatorRS256, iss, aud, exp, nonce, JWKS signature
Clinical UIPatient banner, dashboard, conditions, medications, clinician profile

Epic-specific handling

Epic requirementHow it is handled
aud parameter in authorization requestSet to iss automatically
launch token in authorization requestCarried from /launch request
patient extra in token responseExtracted by SmartContextExtractor
need_patient_banner extraDrives patient banner rendering
RS256 id_tokenValidated with JWKS from jwks_uri
Refresh token rotationDetected and stored in session

140+ tests

  • @WebMvcTest for all controllers
  • WireMock for all network calls (discovery, token exchange, JWKS, FHIR)
  • Pure unit tests for PKCE, OIDC validation, context extraction
  • Integration profiles: smart (SMART Health IT sandbox), epic (Epic non-production)

Quick install

pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
application.yml — minimum required
smart:
epic:
client-id: ${SMART_CLIENT_ID}
redirect-uri: ${SMART_REDIRECT_URI:http://localhost:8080/callback}
scopes: "launch openid fhirUser patient/Patient.rs patient/Condition.rs"
./mvnw spring-boot:run

Then open http://localhost:9000/portal, select a patient, and click Launch.


Next: Introduction →