Yogesh Dhimate

Notes to Myself

Jan 23, 2021 - 5 minute read - Comments - Programming

Using Epic's FHIR APIs in Mule 4

Introduction

FHIR (Fast Healthcare Interoperability Resources) is a new and emerging standard for interoperability in healthcare IT. Unlike the ancient HL7v2 standard, FHIR is open, extensible, and composable. It supports modern web standards like JSON, XML, HTTP(!), and Turtle. It also defines different data exchange methods like RESTful API, messaging, services, documents, and persistent storage. Best of all the FHIR documentation is outstanding: There is detail documentation for every type of resource, with lots of examples. Enough detail for anyone to create a compliant implementation. All of the data types like dates and times, terminologies, identifiers, references, numeric values, extensions are documented in detail, including how to read and write them. It’s probably one of the best documented and well thought out APIs I have worked with.

Epic made their FHIR APIs available on https://fhir.epic.com/. In this post, we will discuss using Epic’s FHIR APIs in Mule 4

Prerequisites

You will need an Epic developer account. You can get one for free by signing up here. I have used Anypoint Studio 7.7 for Mule app.

Configuration

Certificate setup for JWS

  1. We will use SMART Backend Services approach described here which uses signed JWT to get the access token.

  2. Generate a new public-private key pair, and get the certificate(s) in appropriate format

    1
    2
    3
    4
    5
    6
    7
    8
    
    # generate a new public-private key pair
    openssl genrsa -out Anypoint_Mulesoft.pem 2048  
    
    # export the public key to a base64 encoded X.509 certificate
    openssl req -new -x509 -key Anypoint_Mulesoft.pem -out Anypoint_PublicKey.pem -subj '/CN=Anypoint' -days 365
    
    # export the keystore in pkcs12
    openssl pkcs12 -export -in Anypoint_PublicKey.pem -inkey Anypoint_Mulesoft.pem -out Anypoint_Keystore.p12

Create an app in Epic

  1. Login to https://fhir.epic.com/ and click on ‘Build Apps’

    dwarf

  2. Click on ‘Create’ and enter your app details as follows. Make sure to select the Application Audience as ‘Backend Systems’. For Sandbox JWT signing public key, upload the public key store (Anypoint_PublicKey.pem) file generated in the previous step. Click on save.

    dwarf

  3. You should see the Client ID and Non-Production Client ID for your app. Select the SMART on FHIR version as R4, enter the summary, accept the terms of usage and click on ‘Save & Ready for Sandbox’.

    dwarf

In my experience it usually takes a couple of hours for your app to be usable in Epic. It took me ~2 hours for my app to be usable

Mule application details

  1. Create a new Mule application, and copy the pkcs12 formatted keystore (Anypoint_Keystore.p12) created earlier, in the src/main/resources folder.

  2. To generate a signed JWT (JWS), we will use Java JWT library. In the pom.xml of your Mule application, include the following dependencies. (I have included Apache log4j dependencies, these are optional).

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
        <version>2.14.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.14.0</version>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-api</artifactId>
        <version>0.11.2</version>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-impl</artifactId>
        <version>0.11.2</version>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-jackson</artifactId> 
        <version>0.11.2</version>
    </dependency>
    <dependency>
        <groupId>org.bouncycastle</groupId>
        <artifactId>bcprov-jdk15on</artifactId>
        <version>1.65</version>
        <type>jar</type>
    </dependency>

  3. In Mule application’s src/main/java folder, create a new Java package com.dhimate.demo, class JWTProvider and use following code snippet to generate signed JWT.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    
    package com.dhimate.demo;
    
    import io.jsonwebtoken.Jwts; 
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.net.URL;
    import java.security.KeyStore;
    import java.security.PrivateKey;
    import java.util.Date;
    import java.util.Enumeration;
    import java.util.UUID;
    import java.util.concurrent.TimeUnit;
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    
    
    public class JWTProvider {
    //	private static final String PRIVATE_KEY_FILE_RSA = "Anypoint_Keystore.p12";
    //	private static final String PRIVATE_KEY_PASSWORD = "CHANGEIT";
    //	private static final String EPIC_CLIENT_ID = "86121d55-247e-4a0b-b3c0-6fa28c995f72";
    //	private static final String EPIC_FHIR_URL = "https://fhir.epic.com/interconnect-fhir-oauth/oauth2/token";
    	
    	private static final Logger logger = LogManager.getLogger("JWTProvider");
    	public static String getToken(String PRIVATE_KEY_FILE_RSA, 
                                    String PRIVATE_KEY_PASSWORD, 
                                    String EPIC_CLIENT_ID, 
                                    String EPIC_FHIR_URL) throws Exception {
    		
    		logger.info("Generating JWS token");
    		
    		String jws = Jwts
    				.builder()
    				.setHeaderParam("alg", "RS384")
    				.setHeaderParam("typ", "JWT")
    				.setIssuer(EPIC_CLIENT_ID)
    				.setSubject(EPIC_CLIENT_ID)
    				.setAudience(EPIC_FHIR_URL)
    				.setId(UUID.randomUUID().toString())
    				.setExpiration(new Date(System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(5)))
    				.signWith(getKey(PRIVATE_KEY_FILE_RSA, PRIVATE_KEY_PASSWORD))
    				.compact();
    		
    		return (jws);
    	}
    
    	private static PrivateKey getKey(String PRIVATE_KEY_FILE_RSA, 
                                        String PRIVATE_KEY_PASSWORD) throws Exception {
    		
    		KeyStore keystore = KeyStore.getInstance("PKCS12");
    		URL resource = JWTProvider.class.getClassLoader().getResource(PRIVATE_KEY_FILE_RSA);
    		FileInputStream is = new FileInputStream(new File(resource.toURI()));
    		
    		keystore.load(is, PRIVATE_KEY_PASSWORD.toCharArray());
    		Enumeration<String> aliases = keystore.aliases();
    		String keyAlias = "";
    		
    		while (aliases.hasMoreElements()) {
    			keyAlias = (String) aliases.nextElement();
    		}
    
    		PrivateKey key = (PrivateKey) keystore.getKey(keyAlias, PRIVATE_KEY_PASSWORD.toCharArray());
    		return (key);
    	}
    }
     

Your Mule application should look like this.

dwarf
  1. In Mule application’s src/main/java folder, create a new Java package com.dhimate.demo, class JWTProvider and use the following code snippet to generate signed JWT.

  2. Calling Epic’s FHIR APIs is a two-step process.

  • In the first step we need to get the access token using the signed JWT token
  • In the second step we will use the access token received in the step above to access FHIR resource
    dwarf
  1. Let’s look at the configuration of important components
  • Generate signed JWS token using Invoke Static component
    dwarf
  • Prepare request payload to get access token
    dwarf
  • Get the access token
    dwarf
  • Call FHIR API to get the data
    dwarf
  1. Once this is done, we can add a simple HTTP listener and connect our flow to it to get the data from Epic’s FHIR API. Epic has provided test data in the sandbox. Let’s use it to search for a patient record.
    dwarf

Jan 16, 2021 - 4 minute read - Comments - Programming

How to Use Different Authentication Options for Salesforce Connector in Mule 4

Introduction

MuleSoft offers multiple connectors for Salesforce integration. This post is about Salesforce Connector for Mule 4. This connector helps in integrating with Sales Cloud, Service Cloud, Salesforce Platform, and Force.com. We will specifically look into different options for authentication with Salesforce.

Software Prerequisites

You need a Salesforce developer account. You can get one for free by signing up here. I have used the following versions for MuleSoft software.

  1. Anypoint Studio 7.7
  2. Salesforce Connector 10.8.0

Configuration

Security Token in Salesforce

Salesforce security token is an alphanumeric code associated with your password. You do not need a security token if you are trying to authenticate from an IP address that is inside your Salesforce org’s trusted IP range, or your Salesforce profile’s login IP range. When you sign up for a new developer account, your Salesforce org does not have trusted IP ranges. You can either set up trusted or login IP ranges in your org, or use the security token for authentication. Generating security token is simple.

  1. Go to settings

    dwarf

  2. Reset security token by using the ‘Reset My Security Token’ menu and clicking on the ‘Reset Security Token’ button.

    dwarf

  3. You should receive your new security token in your email account that you used to sign up for the Salesforce developer account.

Certificate setup in Salesforce

For certificate-based security, you need to set up a certificate in Salesforce.

  1. Go to setup

    dwarf

  2. Search for certificate and key management and select the option

    dwarf

  3. Click on ‘Create Self-Signed Certificate’ button to create a new certificate and enter the information and save it.

    dwarf

  4. Once certificate is created, download the certificate (.crt) file. We need it for setting up the connected app.

    dwarf

  5. Export the certificate to keystore. We need it to configure the connection information in Mule app.

    dwarf

  6. Enter the password when exporting to keystore

    dwarf

  7. Make sure you save both the .crt file (downloaded in step 4 above) and the .jks file safely on your disk.

Connected App setup in Salesforce

  1. Go to setup

    dwarf

  2. Search for app manager and select the ‘App Manager’ option

    dwarf

  3. Use the ‘New Connected App’ button to create a new connected app.

    dwarf

  4. Fill in name and email. Select ‘Enable OAuth Settings’ Enter two Callback URLs

Select ‘Use digital signatures’ and upload the .crt file received when setting up the certificate above. Select all the OAuth Scopes if possible

dwarf
Note the client_id and client_Secret

  1. In your connected app’s page, click on Manage button to review and update the app policies. Click on Manage.

    dwarf

  2. And then on ‘Edit Policies’

    dwarf

  3. In the Permitted Users option you can see two options

    • Admin approved users are pre-authorized.

      We will not go into the details of this option, but if you want to use it you can use the following steps

      • Go to Settings > Manage Users > Profiles
      • Edit the profile associated with the user
      • For ‘Connected App Access’ under the profile, select the checkbox against the connected app. Save.
    • All users may self-authorize.

      We will use this option. Ensure that you select this option and save the policy.

      dwarf

  4. Open your browser and go to https://oauthdebugger.com/. Enter following details

    • Authorization url = ‘https://login.salesforce.com/services/oauth2/authorize’
    • Redirect URI = ‘https://oauthdebugger.com/debug’
    • Client ID = obtained from the connected app
    • Scope = ‘api refresh_token offline_access’
    • Response type = ‘code’
    • Response mode = ‘query’
    • Click on “send request”
dwarf
  1. It should redirect you to enter your credentials on Salesforce login screen and you should receive an authorization code.

    dwarf

  2. This will complete the connected app setup.

Basic Authentication

Basic authentication is the easiest way to authenticate with Salesforce. For basic authentication you need username and password, and optionally a security token.

dwarf

OAuth JWT

For OAuth JWT connection copy and paste the key store file (.jks) that you created in the certificate setup section into the src/main/resources folder.

  • Consumer Key – Consumer key of the Connected App that was created in Salesforce
  • Key Store – Keystore file (.jks) that you downloaded from the certificate setup
  • Store Password – Keystore password that was created when you generated the key store file
  • Principal – Salesforce username of the user that was approved against the authorization URL
dwarf

OAuth SAML

For OAuth SAML connection copy and paste the key store file (.jks) that you created in the certificate setup section into the src/main/resources folder.

  • Consumer Key – Consumer key of the Connected App that was created in Salesforce
  • Key Store – Keystore file (.jks) that you downloaded from the certificate setup
  • Store Password – Keystore password that was created when you generated the key store file
  • Principal – Salesforce username of the user that was approved against the authorization URL
dwarf

OAuth Username Password

For OAuth Username Password configure the following properties

  • Consumer Key – Consumer key of the Connected App that was created in Salesforce
  • Consumer Secret – Consumer secret of the Connected App that was created in Salesforce
  • Username – Salesforce login id
  • Password – Salesforce password
  • Security Token - Security token associated with the username
dwarf

Jan 13, 2021 - 2 minute read - Comments - Books

Reflections on Range by David Epstein

I recently finished reading Range - Why Generalists Triumph in a Specialized World by David Epstein. It wasn’t exactly a new topic. It was recommended in Bill Gates' blog so I was curious about it and picked it up. The book talks about (hyper)specialization vs broad thinking in career as well as life. I had read one perspective about this in other books like ‘Talent is Overrated’ by Geoffrey Colvin, and Cal Newport’s ‘So Good They Can’t Ignore You’.

This book offers a different perspective. It’s quite a myth-buster about the 10000 hours theory or deliberate practice. It explains how the hyperspecialization of narrow skills can be harmful to your long term growth. David kept me engaged throughout the book, even though sometimes the content kept on dragging the same message. It also felt relevant to my experience in life and career. I have been a sort of generalist, trying out different things like software engineering, solution architecture, or pre-sales over the last 20 years in my career. I intentionally avoided sticking to a single tech stack or framework or career track. And sometimes felt bad (or incompetent) being a jack of all trades type of a person. Turns out it isn’t bad to be that guy. In a way, I felt vindicated of everything I have done so far.

The key messages that stuck with me were about the experimentation and match quality - the term used to describe the fit between work someone does and their abilities. Everyone progresses at a different rate, so feeling behind doesn’t help much. Most likely you don’t know where exactly you are going. In this situation, the proactive pursuit of match quality, experimentation with different things, learning and adjusting as you go is the best approach. Comparing yourself to (cherry-picked) younger people doesn’t help either. Instead, compare yourself to your yesterday’s self and see how you are progressing. Epstein provides a good framework and reason to encourage range (rather than depth) over different areas of interest and stay curious.

As noted above, some of the repetitive content could’ve been edited to make it more concise. But other than that it was a fun and enjoyable read.