Epic Connector

Content Type: Module
Categories: Authentication,Connectors,Data

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

Version: 1.0.0
Framework Version: 10.24.19
Release Notes:

Initial release