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:
| Component | What it does |
|---|---|
SmartLaunchController | Handles GET /launch, runs SMART discovery, builds PKCE, redirects to Epic |
SmartCallbackController | Handles GET /callback, exchanges code for tokens, validates id_token |
SmartLaunchContext | Typed session object: access token, patient ID, encounter ID, needPatientBanner, scopes, expiry |
FhirClientFactory | HAPI R4 client per session, pre-configured with bearer token |
TokenRefreshFilter | Proactive refresh 120 s before expiry — clinicians never see a 401 |
IdTokenValidator | RS256, iss, aud, exp, nonce, JWKS signature |
| Clinical UI | Patient banner, dashboard, conditions, medications, clinician profile |
Epic-specific handling
| Epic requirement | How it is handled |
|---|---|
aud parameter in authorization request | Set to iss automatically |
launch token in authorization request | Carried from /launch request |
patient extra in token response | Extracted by SmartContextExtractor |
need_patient_banner extra | Drives patient banner rendering |
| RS256 id_token | Validated with JWKS from jwks_uri |
| Refresh token rotation | Detected and stored in session |
140+ tests
@WebMvcTestfor 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 →