Setting Up the MDM Web UI for Data Stewards

 

This tutorial walks a system administrator through every step needed before a data steward can open the MDM Web UI dashboard and start reviewing patient identity decisions. The MDM Web UI supports the Probabilistic MDM strategy, in which borderline matches are resolved by a human reviewer. For a shorter reference-style overview of the same setup, see the MDM UI Tutorial; this tutorial is a detailed step-by-step walkthrough. By the end, the following will be in place:

  • An MDM module with match rules that produce both automatic links and Possible Match / Possible Duplicate tasks for human review
  • A SMART Outbound Security module, a JSON Admin API endpoint, and a dedicated FHIR REST endpoint — the three backend services the MDM UI requires
  • An OIDC client that authorizes the MDM UI application to authenticate users
  • An MDM UI module accessible in a browser
  • Data steward user accounts with the correct permissions
This tutorial configures MDM UI and MDM's MATCH_AND_LINK mode for link management, which requires a separately purchased MDM license. Contact sales@smiledigitalhealth.com if you need to enable it. An MDM module explicitly configured with MATCH_ONLY mode does not require a license — see MDM for details.

Prerequisites

 
  • A running Smile CDR installation at version 2024.02 or later.
  • Access to the Web Admin Console at http://localhost:9100 (or your configured admin port) using an account with the ROLE_SUPERUSER permission.
  • The FHIR Storage (R4 RDBMS) module is already created and running. This tutorial refers to it as persistence.
  • The Subscription Matching module is already created and running with a dependency on persistence. On a fresh install both modules exist by default. If either is absent, create it before continuing.

Step 1: Configure the FHIR Storage Module

 

The MDM engine runs asynchronously on top of Smile CDR's subscription infrastructure. Two settings on the FHIR Storage module must be enabled before the MDM module can operate.

Open the FHIR Storage module

In the Web Admin Console, go to Configuration → Module Config and open the persistence module.

Enable MDM and message subscriptions

Use the On this page sidebar to navigate to each section below, then set the values shown:

Section in sidebarSettingValue
FHIR MDM ServerMDM Mode EnabledEnabled
FHIR Subscription PersistenceMessage Subscription EnabledEnabled
FHIR ConfigurationSeed Base Validation ResourcesEnabled

The Seed Base Validation Resources setting loads StructureDefinition and related FHIR conformance resources on startup. The MDM UI frontend requires these to be present.

Save and restart the persistence module.


Step 2: Configure the MDM Module with Match Rules

 

The MDM module processes incoming resources and produces the links that data stewards review. If the match rules produce only MATCH and no POSSIBLE_MATCH outcomes, the data steward queue will always be empty. This step configures rules that route borderline cases to the dashboard.

Create the MDM module (if it does not already exist)

Go to Configuration → Module Config → Add Module and select MDM.

Set the consumer thread count

Set Consumer Thread Count to 2 (or 1 for a low-volume environment).

Set the Subscription dependency

Under Module Dependencies, select the Subscription Matching module.

Enter the MDM Rule Definition Script

Paste the following JSON into the MDM Rule Definition Script field. It matches automatically when both name and birth date agree, and routes cases where only the family name and birth date agree to the data steward queue as Possible Matches.

{
  "version": "v1",
  "mdmTypes": ["Patient"],
  "candidateSearchParams": [
    {
      "resourceType": "Patient",
      "searchParams": ["birthdate"]
    },
    {
      "resourceType": "Patient",
      "searchParams": ["identifier"]
    }
  ],
  "candidateFilterSearchParams": [],
  "matchFields": [
    {
      "name": "family-name",
      "resourceType": "Patient",
      "resourcePath": "name.family",
      "similarity": {
        "algorithm": "JARO_WINKLER",
        "matchThreshold": 0.88
      }
    },
    {
      "name": "given-name",
      "resourceType": "Patient",
      "resourcePath": "name.given",
      "matcher": {
        "algorithm": "DOUBLE_METAPHONE"
      }
    },
    {
      "name": "birthdate",
      "resourceType": "Patient",
      "resourcePath": "birthDate",
      "matcher": {
        "algorithm": "DATE"
      }
    }
  ],
  "matchResultMap": {
    "family-name,given-name,birthdate": "MATCH",
    "family-name,birthdate": "POSSIBLE_MATCH"
  }
}

The matchResultMap is what creates work for data stewards. The MATCH entry handles the confident cases automatically. The POSSIBLE_MATCH entry catches cases where the family name and birth date agree but the given name comparison fails — for example, when the given name is absent on one record or differs significantly. Each POSSIBLE_MATCH link becomes a task in the dashboard.

Enter a survivorship script

Survivorship rules control which field values are promoted to the Golden Record when multiple source records are linked, and which values are used when two Golden Records are merged. Without a survivorship script, the Golden Record is left empty after a merge.

Paste the following into the MDM Survivorship Script field:

function mdmApplySurvivorshipRules(targetRec, goldenRec, transactionContext) {
    var helper = new MdmHelper(Fhir.getContext(), targetRec, goldenRec, transactionContext);
    helper.replaceAll();
}

function mdmApplySurvivorshipRulesOnMergeGoldenResources(fromGoldenRec, toGoldenRec, transactionContext) {
    var helper = new MdmHelper(Fhir.getContext(), fromGoldenRec, toGoldenRec, transactionContext);
    helper.mergeAll();
}

The first function runs whenever a source record is linked to a Golden Record: it copies all demographic fields from the source record to the Golden Record. The second function runs when two Golden Records are merged by a data steward: it combines all fields from both records into the surviving one.

Save the MDM module and start it.

Verify that the module status icon turns green. If it stays red or yellow, check Logs → Module Logs for errors. The most common cause is a missing Subscription Matching dependency.


Step 3: Configure the SMART Outbound Security Module

 

The MDM UI authenticates users through SMART/OIDC. A SMART Outbound Security module issues tokens, and a callback script within it assigns each user the appropriate MDM UI role at login time.

Create the module (if it does not already exist)

Go to Configuration → Module Config → Add Module and select SMART Outbound Security. Use the module ID smart_auth.

Set the listener and issuer

SettingValue
OpenID Connect (OIDC) → Issuer URLhttp://localhost:9200/smartauth
HTTP Listener → Listener Port9200
HTTP Listener → Context Path/smartauth
HTTP Listener → Respect Forward HeadersDisabled
HTTP Listener → HTTPS Forwarding AssumedDisabled
Reverse proxy deployments only: If the MDM UI is exposed through an HTTPS-terminating reverse proxy (nginx, Apache, a load balancer, etc.), enable both Respect Forward Headers and HTTPS Forwarding Assumed on this module. With those flags off, the server generates http:// URLs for all OIDC endpoints, which is correct for a direct connection. With them on, the server treats every connection as HTTPS and generates https:// URLs regardless of the actual listener protocol — connecting directly to the server without a proxy in front will then produce an ERR_SSL_PROTOCOL_ERROR in the browser.

Disable enforced scope restrictions

Under SMART Authorization, disable Enforce Approved Scopes. This lets the MDM UI receive all the permissions it needs without requiring each scope to be pre-approved on the client.

Enable CORS

Under Cross-Origin Resource Sharing, enable CORS and set the allowed origin to the URL where the MDM UI will be served (for example, http://localhost:8080). Do not use * here because the OIDC logout flow requires an exact origin match.

Add the Post-Authorization Script

In the SMART Callback Script section, paste the following into the Post Authorization Script Text field. This script assigns the Data Steward or Admin role to a user based on their username at the time a token is issued.

function onTokenGenerating(theUserSession, theAuthorizationRequestDetails) {
    var userName = theUserSession.username;

    if (userName === 'DATA_STEWARD' || userName === 'ADMIN') {
        theUserSession.addAuthority('FHIR_WRITE_ALL_OF_TYPE', 'AuditEvent');
        theUserSession.addAuthority('FHIR_WRITE_ALL_OF_TYPE', 'Task');
        theUserSession.addAuthority('ACCESS_ADMIN_JSON');
        theUserSession.addAuthority('UPDATE_USER');
        theUserSession.addAuthority('VIEW_USERS');
    }

    if (userName === 'DATA_STEWARD') {
        theUserSession.addAuthority('ROLE_MDMUI_DATASTEWARD_FHIR');
        theUserSession.addAuthority('VIEW_MODULE_CONFIG_FOR_MODULE', 'Master/mdm');
        theAuthorizationRequestDetails.addAccessTokenClaim('user_role', 'data_steward');

    } else if (userName === 'ADMIN') {
        theUserSession.addAuthority('FHIR_EXTENDED_OPERATION_ON_SERVER', '$mdm-evaluate');
        theUserSession.addAuthority('ROLE_MDMUI_ADMIN_FHIR');
        theUserSession.addAuthority('MODULE_ADMIN_FOR_MODULE', 'Master/mdm');
        theUserSession.addAuthority('VIEW_BATCH_JOBS');
        theAuthorizationRequestDetails.addAccessTokenClaim('user_role', 'admin');
    }
}
This script uses hard-coded usernames for simplicity. In a production environment, replace the username checks with lookups against your organization's directory service (LDAP or an external OIDC provider) so that role assignment is managed centrally rather than in this script. See the Roles & Permissions reference for the full list of MDM UI authorities.

The ROLE_MDMUI_DATASTEWARD_FHIR permission bundle includes the individual MDM operation authorities needed to query links, update links, merge Golden Records, and mark pairs as not-duplicate. The ROLE_MDMUI_ADMIN_FHIR bundle includes all of those plus the ability to run $mdm-clear and $mdm-submit.

Save and restart the smart_auth module.


Step 4: Configure the JSON Admin API Endpoint

 

The MDM UI uses the JSON Admin API to fetch module configuration and metadata.

Create the module (if it does not already exist)

Go to Configuration → Module Config → Add Module and select JSON Admin API.

Configure it

SettingValue
HTTP Listener → Listener Port9000
HTTP Listener → Context Path/json-admin
Auth: OpenID Connect → OpenIdConnect SecurityEnabled
Cross-Origin Resource Sharing → CORS EnabledEnabled
Cross-Origin Resource Sharing → CORS Allowed Originshttp://localhost:8080 (or * for testing only)

Set module dependencies

In the Module Dependencies section, set the OpenID Connect Authentication dropdown to smart_auth.

Save and restart the JSON Admin API module.


Step 5: Configure the FHIR REST Endpoint

 

The MDM UI reads and writes FHIR resources — Patient records, AuditEvents, Tasks — through a dedicated FHIR REST endpoint that is protected by OIDC.

Create the module

Go to Configuration → Module Config → Add Module and select FHIR REST Endpoint (All FHIR Versions). Use the module ID fhir_endpoint.

Configure it

SettingValue
HTTP Listener → Listener Port8000
HTTP Listener → Context Path/fhir-request
FHIR REST Endpoint → Fixed Value for Endpoint Base URLhttp://localhost:8000/fhir-request
Auth: OpenID Connect → OpenID Connect SecurityYes
Cross-Origin Resource Sharing → CORS EnabledEnabled
CORS Allowed Originshttp://localhost:8080 (or * for testing only)

Set module dependencies

Under Module Dependencies, add:

Dependency typeSelect
FHIR Storage Modulepersistence (FHIR Storage R4 RDBMS)
OpenID Connect Authenticationsmart_auth (SMART Outbound Security)

Save and start the fhir_endpoint module.


Step 6: Create the OIDC Client

 

The MDM UI is a browser application. An OIDC client record authorizes it to request tokens from the SMART auth module.

Open the OIDC client manager

In the Web Admin Console, go to Users & Authorization → OpenID Connect Clients and click Add Client.

Fill in the client details

FieldValue
Client IDMDM_UI
Client NameMDM UI
Authorized Grant TypesAuthorization Code, Refresh Token, JWT Bearer Token
Authorized Redirect URLshttp://localhost:8080/mdm-ui/
Scopescdr_all_user_authorities launch/patient launch/practitioner offline_access openid profile

The Authorized Redirect URL must match the URL of the MDM UI module exactly, including the trailing slash and any context path. If you change the MDM UI's port or context path in the next step, update this URL to match.

Click Create Client.


Step 7: Add the MDM UI Module

 

Create the module

Go to Configuration → Module Config → Add Module and select MDM UI.

Configure it

SettingValue
HTTP Listener → Listener Port8080
HTTP Listener → Context Path/mdm-ui/
Organization IdentifierA system and value identifying your organization (for example, http://example.org/org-id\|my-org). A new Organization resource is created automatically if one does not exist with this identifier.
JSON Admin URLhttp://localhost:9000/json-admin
OIDC Client IDMDM_UI
Issuer URLhttp://localhost:9200/smartauth
Redirect URLhttp://localhost:8080/mdm-ui/
Logout URLhttp://localhost:9200/smartauth/logout?cb=none&revoke=token&revoke=token_refresh

Set module dependencies

Under Module Dependencies, select all available dependencies. This typically includes the FHIR REST endpoint, the JSON Admin API, and the SMART Outbound Security module.

Save and start the MDM UI module.

The MDM UI is now accessible at http://localhost:8080/mdm-ui/.


Step 8: Create Data Steward User Accounts

 

Users logging into the MDM UI must have accounts in the local user store. The post-authorization script from Step 3 grants the correct MDM UI permissions at login time based on the username — so the account names must match the values checked in that script.

Open the User Manager

Go to Users & Authorization → User Management and click Add User.

Create the Data Steward account

FieldValue
UsernameDATA_STEWARD
PasswordChoose a strong password and share it securely with the user.
PermissionsNo explicit permissions are required here — the post-authorization script adds them when the user logs into the MDM UI.

Click Save.

Create the Admin account (optional)

Repeat the above with username ADMIN if you also want an MDM UI administrator account that can run match evaluations and batch jobs from within the UI.

The hard-coded usernames DATA_STEWARD and ADMIN match the values in the post-authorization script written in Step 3. If you use different usernames, update the script accordingly. For production deployments with many stewards, replace the per-username checks with group membership lookups so you do not need to modify the script each time you add or remove a user.

Step 9: Verify the Setup

 

Check module status

In the Web Admin Console, go to Configuration → Module Config and confirm that all seven modules show a green status icon:

  • persistence (FHIR Storage R4 RDBMS)
  • subscription (Subscription Matching)
  • MDM
  • smart_auth (SMART Outbound Security)
  • admin_json (JSON Admin API)
  • fhir_endpoint_mdm (FHIR REST Endpoint)
  • MDM UI

Log in as a data steward

Open http://localhost:8080/mdm-ui/ in a browser. You should be redirected to the SMART auth login page. Log in with the DATA_STEWARD credentials created in Step 8.

After login you should see the MDM dashboard with Possible Matches and Possible Duplicates sections. Both counts will be zero on a fresh installation because no patient data has been loaded yet.

Confirm the data steward's view

Verify that the user can see the review queues but does not see the configuration or administration panels — those are visible only to users with the ADMIN role.

Send a test patient pair

To verify the full pipeline end to end, create two Patient records whose names and birth dates trigger a POSSIBLE_MATCH under the rules in Step 2.

Patient 1:

{
  "resourceType": "Patient",
  "name": [{ "family": "Smith", "given": ["Jonathan"] }],
  "birthDate": "1985-03-15"
}

Patient 2 (given name absent — should produce a Possible Match, not an automatic Match):

{
  "resourceType": "Patient",
  "name": [{ "family": "Smith" }],
  "birthDate": "1985-03-15"
}

Wait a few seconds for asynchronous MDM processing, then reload the MDM UI dashboard. The Possible Matches count should increment to 1. The data steward can now open that task and review it.


What You've Accomplished

 

In this tutorial you:

  1. Enabled MDM mode and message subscriptions on the FHIR Storage module.
  2. Configured the MDM module with match rules that produce both automatic MATCH links and POSSIBLE_MATCH tasks for data steward review, and a survivorship script for Golden Record merges.
  3. Set up a SMART Outbound Security module with a post-authorization script that assigns the ROLE_MDMUI_DATASTEWARD_FHIR and ROLE_MDMUI_ADMIN_FHIR permission bundles at login.
  4. Configured a JSON Admin API endpoint and a FHIR REST endpoint as backend services for the MDM UI.
  5. Created an OIDC client that authorizes the MDM UI application.
  6. Launched the MDM UI module and confirmed it is reachable in a browser.
  7. Created local user accounts for data stewards.

Next Steps

 
  • Tuning match rules: The rules in Step 2 are a starting point. Adjust the JARO_WINKLER threshold and the candidateSearchParams to fit your patient population's data quality. See Advanced Rule Authoring for a systematic guide.
  • Integrating an external identity provider: Replace the hard-coded username checks in the post-authorization script with LDAP or federated OIDC lookups. See the optional external IdP step in MDM UI Setup Guide.
  • Production message broker: The default embedded message broker is suitable for testing. For production, configure an external Apache Kafka or ActiveMQ broker to ensure MDM processing is durable and scalable. See Message Broker.
  • Reverse proxy configuration: If the MDM UI is exposed through a web proxy, enable Respect Forward Headers and HTTPS Forwarding Assume on the smart_auth module, and ensure all four services (MDM UI, FHIR endpoint, SMART auth, JSON Admin API) are reachable at their configured external URLs.
  • Data steward onboarding: Point your data stewards at the MDM UI Tutorial, which describes the review workflow they will follow once the queue has items in it.