Skip to content

SMART on FHIR Client

A production-ready SMART on FHIR EHR launch client for Epic, built on Spring Boot 3, Java 21, and HAPI FHIR R4. Complete OAuth2 handshake, PKCE, dynamic discovery, token refresh, OIDC user profiles, and a clinical Thymeleaf UI โ€” all audited and tested.


๐ŸŽฏ Key Features

๐Ÿ”

SMART App Launch v2.2

Full EHR and standalone launch modes. Dynamic /.well-known/smart-configuration discovery, PKCE S256, Epic aud param, multi-tenant ISS caching.

๐Ÿฅ

Epic-Ready Out of the Box

Handles every Epic-specific requirement: launch token, aud=iss, token response extras (patient, encounter, need_patient_banner).

๐Ÿ”„

Proactive Token Refresh

120-second buffer refresh before expiry. Handles refresh token rotation. Force-refresh on Epic 401. Clinicians never see an unexpected session expiry.

๐Ÿ›ก๏ธ

Security Hardened

Spring Security 6 filter chain. Constant-time CSRF state comparison. Log injection sanitised. Session fixation protection. CSRF Double-Submit Cookie. denyAll() route fallback.

๐Ÿ‘ค

OIDC User Profiles

Validates Epic's id_token: RS256 algorithm, issuer, audience, expiry, nonce, JWKS key presence. Extracts UserProfile with subject, name, fhirUser.

โš•๏ธ

Clinical UI Included

Thymeleaf templates with patient banner, dashboard stat cards, conditions table, medications table, clinician profile. Matches Epic's UI contract for needPatientBanner.

๐Ÿงช

140+ Tests

WireMock for all network calls. @WebMvcTest for controllers. Pure unit tests for all domain classes. SMART Health IT and Epic sandbox integration test profiles.

๐Ÿ“ฆ

Production Architecture

Redis-ready session objects (Serializable, pinned serialVersionUID). HAPI FHIR R4 with timeout config. Actuator health probes. Structured for App Orchard submission.


๐Ÿ“Š At a Glance

30 Production classes
20 Test classes
140+ Unit tests
9 Packages
v2.2 SMART spec
R4 FHIR version

๐Ÿš€ Quick Start

Get the app running against the free SMART Health IT sandbox in under 5 minutes โ€” no Epic account required.

# Clone the repository
git clone https://github.com/AKHester-Technologies/akhester-smart-on-fhir.git
cd smart-fhir-client

# Run against the public SMART Health IT sandbox (no credentials needed)
mvn spring-boot:run -Dspring-boot.run.profiles=smart

# Open the app
open http://localhost:8080

Then go to launch.smarthealthit.org, pick a synthetic patient, and enter http://localhost:8080/launch as your launch URL.

Full Quick Start Guide โ†’


๐Ÿ—๏ธ Architecture Overview

The app is structured around the SMART on FHIR EHR launch sequence. Each step maps to one or more Spring beans.

sequenceDiagram
    participant E as Epic (EHR)
    participant L as SmartLaunchController
    participant D as SmartDiscoveryService
    participant A as SmartAuthRequestBuilder
    participant C as SmartCallbackController
    participant T as SmartTokenService
    participant X as SmartContextExtractor
    participant F as FhirClientFactory

    E->>L: GET /launch?iss=...&launch=...
    L->>D: discover(iss)
    D-->>L: SmartConfiguration (auth + token endpoints)
    L->>A: buildAuthorizeUrl(session)
    Note over A: Generates PKCE pair<br/>Stores verifier in session
    L-->>E: 302 โ†’ Epic authorize URL

    E->>C: GET /callback?code=...&state=...
    C->>C: Validate state (CSRF)
    C->>T: exchange(code, verifier)
    T-->>C: SmartTokenResponse
    C->>X: extract(tokenResponse, iss)
    X-->>C: SmartLaunchContext (stored in session)
    C-->>E: 302 โ†’ /

    E->>F: GET /api/patient
    F->>F: FhirClientFactory.create(ctx)
    F-->>E: Patient JSON

Full Architecture Docs โ†’


๐Ÿ”„ The SMART Launch Flow

EHR sends the launch request

Epic calls GET /launch?iss=...&launch=.... The ISS identifies the FHIR server; the launch token binds the EHR context (patient, encounter).

Dynamic discovery

SmartDiscoveryService fetches {iss}/.well-known/smart-configuration to resolve the authorization and token endpoints. Cached per ISS.

PKCE + authorize redirect

PkceHelper generates a 96-byte code_verifier and S256 code_challenge. SmartAuthRequestBuilder builds the authorize URL with Epic-specific aud, launch, and PKCE params.

Callback and token exchange

SmartCallbackController validates the state nonce (CSRF), retrieves the PKCE verifier, and calls SmartTokenService to POST the code exchange.

Context extraction + OIDC

SmartContextExtractor parses Epic's token response extras (patient, encounter, need_patient_banner), validates the id_token via IdTokenValidator, and stores SmartLaunchContext in the session.

FHIR data access

FhirClientFactory creates a HAPI R4 IGenericClient with a BearerTokenInterceptor. The UI fetches Patient, Condition, and MedicationRequest resources โ€” with scope gating before every call.


๐Ÿ“ฆ Package Structure

auth/
PkceHelper
PkceParameters
SmartAuthRequestBuilder
context/
SmartLaunchContext
SmartContextExtractor
discovery/
SmartDiscoveryService
SmartConfiguration
SmartDiscoveryException
fhir/
FhirClientFactory
BearerTokenInterceptor
PatientDataController
launch/
SmartLaunchController
SmartLaunchSession
oidc/
IdTokenValidator
UserProfile
IdTokenException
refresh/
TokenRefreshService
TokenRefreshFilter
TokenRefreshException
security/
SecurityConfig
SmartSecurityFilter
SmartAuthenticationToken
token/
SmartCallbackController
SmartTokenService
SmartTokenResponse

Full Package Guide โ†’


๐Ÿ“š Documentation

๐Ÿš€

Getting Started

Introduction, prerequisites, quick start, configuration, and Epic sandbox registration.

๐Ÿ“

Architecture

System design, package structure, security model, and session lifecycle.

๐Ÿ”ง

Developer Guide

Deep dives into each layer: launch flow, FHIR client, OIDC, token refresh, and UI.

๐Ÿ“ก

API Reference

All 8 REST endpoints with request/response schemas, scope requirements, and error codes.

๐Ÿงช

Testing Guide

Running unit tests, SMART Health IT sandbox ITs, and the Epic sandbox manual checklist.

๐Ÿšข

Operations

HTTPS, Redis sessions, Docker, CI/CD, monitoring, and App Orchard submission.


akhester-smart-on-fhir vs HealthLX/smart-on-fhir

Feature HealthLX (2019) This Client
SMART spec v1 (static YAML) v2.2 (dynamic discovery)
PKCE โŒ Missing โœ… RFC 7636 S256
Spring Boot 2.x (Boot 3 incompatible) 3.3.5
Spring Security 5 (WebSecurityConfigurerAdapter โ€” removed) 6 (SecurityFilterChain)
Token refresh โŒ Not implemented โœ… Proactive 120s buffer
OIDC validation โŒ Not implemented โœ… RS256, JWKS, nonce
Standalone launch โŒ Not implemented โœ… Full support
FHIR client โŒ Not included โœ… HAPI R4 + bearer token
Epic aud param โŒ Missing โœ… Required by Epic
Test coverage Minimal 140+ unit tests
Last commit December 2019 2025

๐Ÿค Contributing

Contributions are welcome. Whether it's:

  • Bug reports and issue reproduction cases
  • Feature requests (Backend Services, Cerner support, R5)
  • Documentation improvements
  • Code contributions (tests especially welcome)

Please open an issue first to discuss significant changes.


๐Ÿ“„ License

This project is open source under the MIT License. Free for personal, educational, and commercial use.

View License โ†’