Epic Connector
Overview
A Mendix Marketplace module that enables Mendix applications to consume Epic FHIR R4 APIs using backend OAuth 2.0 with JWT client assertions (SMART Backend Services / RFC 7523).
This module handles authentication, key management, JWKS publication, and generic FHIR resource access.
Documentation
# EpicFHIRConnector
A Mendix Marketplace module that enables Mendix applications to consume Epic FHIR R4 APIs using backend OAuth 2.0 with JWT client assertions (SMART Backend Services / RFC 7523).
This module handles authentication, key management, JWKS publication, and generic FHIR resource access. Typed entity models for Patient, Observation, Condition, and Encounter are provided separately in **[EpicFHIRResources](../EpicFHIRResources)**, which depends on this module.
---
## Requirements
- Mendix **10.24.19** or higher
- [JWT module](https://marketplace.mendix.com/link/component/1) from the Mendix Marketplace
- A registered app on [fhir.epic.com](https://fhir.epic.com) with Backend Systems audience and a JWKS URL configured
---
## Installation
1. Download **EpicFHIRConnector** from the Mendix Marketplace and add it to your project.
2. Download the **JWT module** from the Mendix Marketplace if not already present.
3. Add the provided `USE_ME/` pages to your navigation or embed the snippets in an admin page.
4. Configure your environment — see [Configuration](#configuration).
---
## Configuration
All configuration is stored in database entities, not constants. No redeployment is required to change environments.
### EpicBackendConfig
Create one instance per environment via the admin UI or directly in your database. This entity is the root configuration for all connector behaviour.
| Attribute | Description |
|---|---|
| `ClientId` | The non-production (sandbox) or production client ID from your Epic app registration |
| `TokenEndpoint` | Epic's OAuth 2.0 token endpoint (e.g. `https://fhir.epic.com/interconnect-fhir-oauth/oauth2/token`) |
| `FHIRBaseUrl` | Epic's FHIR R4 base URL (e.g. `https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/R4/`) |
| `Scope` | Space-separated FHIR scopes matching your app registration (e.g. `system/Patient.read system/Observation.read`) |
| `ActiveKeyKid` | The `kid` of the currently active `KeyMaterial` entry |
### Key management
Use the admin pages in `USE_ME/` to:
1. **Generate a key pair** — creates a `JWTRSAKeyPair` via the JWT module and a linked `KeyMaterial` entity with a computed `kid` (RFC 7638 JWK thumbprint).
2. **Copy the JWKS URL** — register this URL on fhir.epic.com under your app's public key settings so Epic can fetch your public key automatically.
3. **Export the public key as PEM** — for manual copy-paste into Epic's app registration if you prefer not to use a JWKS URL.
4. **Rotate keys** — activate a new `KeyMaterial`, mark the old one inactive. The old key remains in the JWKS for a 24-hour grace period while Epic refreshes its cache.
The JWKS endpoint is published at `/rest/epic-connector/v1/jwks` by default (configurable). It must be publicly accessible over HTTPS for Epic to fetch it.
---
## Usage
### Obtain an access token
Call the provided microflow:
```
SUB_OAuthToken_Get_Epic
```
This microflow signs a JWT client assertion using the active `KeyMaterial`, posts to Epic's token endpoint, and returns the access token string. It either succeeds or throws — no partial state.
The signed JWT includes:
- `iss` / `sub` = `ClientId`
- `aud` = `TokenEndpoint`
- `exp` = `iat + 5 minutes` (Epic's maximum)
- `jti` = fresh UUID per call
- `kid` header = `KeyMaterial.Kid`
### Retrieve a FHIR resource
```
FHIRResource_RetrieveByReference(ResourceType, ResourceId, AccessToken)
→ FHIRResource
```
Returns a `FHIRResource` non-persistent entity with the full FHIR JSON in `ResourceJson` plus extracted `ResourceType`, `ResourceId`, `VersionId`, and `LastUpdated`.
### Search for FHIR resources
```
FHIRResource_Search(ResourceType, QueryString, AccessToken)
→ raw search response
```
Accepts any FHIR resource type and a query string (e.g. `patient=erXuFYUfucBZaryVksYEcMg3&_count=10`). Returns the raw FHIR search response for the caller to process.
### Using typed entities
For typed entities (Patient, Observation, Condition, Encounter) install **[EpicFHIRResources](../EpicFHIRResources)** alongside this module.
---
## Security considerations
- **Private keys never leave the application.** Keys are generated server-side, stored as encrypted FileDocument content via the JWT module, and never exposed through any connector endpoint or log output.
- **JWKS publishes only public keys.** The `/jwks` endpoint returns only the public half of active key pairs.
- **Token logging is disabled by default.** Access tokens are not written to Mendix log output.
- **Key rotation is non-breaking.** Keys marked inactive remain in the JWKS for 24 hours so Epic's cache can refresh before the key is removed.
- **Access rights.** `KeyMaterial` and `EpicBackendConfig` entities should be restricted to Administrator-level roles. Do not expose these to end-user roles.
---
## Epic sandbox test patients
For development against Epic's public sandbox at `fhir.epic.com`:
| Patient | FHIR ID |
|---|---|
| Camila Lopez | `erXuFYUfucBZaryVksYEcMg3` |
| Derrick Lin | `eq081-VQEgP8drUUqCWzHfw3` |
Camila Lopez has the richest Observation data (vitals, labs, social history) and is recommended for initial testing.
---
## Roadmap
- **v1.x** — token caching, retry on transient failures, OperationOutcome parsing, scheduled key rotation.
- **v2** — SMART on FHIR (user-context authorization code flow with PKCE for EHR launch scenarios).
Typed resource models are out of scope for this module — see **FHIRMapper**.
---
## Related modules
| Module | Purpose |
|---|---|
| [FHIRMapper](../FHIRMapper) | Dutch FHIR resource mapping (nl-core / zibs 2020) |
---
## License
Apache 2.0
Releases
Initial release