Search code examples
gohl7-fhir

EPIC FHIR SMART Backend Services: { "error": "invalid_client", "error_description": null }


I'm trying to implement the EPIC FHIR SMART Backend Services (Backend OAuth 2.0) on go programming language.

I've created my dev account, uploaded the public key there, and selecting the backend system as the application audience.

I'm pretty sure my jwt token is correct. I've inspected it on jwt.io, the signature is correct. However, I always get this error:

{ "error": "invalid_client", "error_description": null }

I've tried other possible solutions as well such as:

  • ensuring the expiration date within the jet claim is below 5 minutes
  • placing the payload in the body with the correct content type, which is application/x-www-form-urlencoded
  • ensuring to use the sandbox client_id
  • using the correct jwt sign in method (RS384)

What should I do to resolve this issue?

Btw, I also saw several discussions on the google groups saying that it's worth to wait for one or two days after the dev account is created.

Below is my code. Appreciate the help!

var (
    oauth2TokenUrl  = "https://fhir.epic.com/interconnect-fhir-oauth/oauth2/token"
    sandboxClientID = "..."
    privateKey      = "..."
)

// load private key
signKey, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(privateKey))
So(err, ShouldBeNil)

// construct jwt claims
now := time.Now()
claims := jwt.MapClaims{
    "iss": sandboxClientID,
    "sub": sandboxClientID,
    "aud": oauth2TokenUrl,
    "jti": uuid.New().String(),             // fill with reference id
    "exp": now.Add(1 * time.Minute).Unix(), // cannot be more than 5 minutes!
}
log.Info("  => claims:", utility.ToJsonString(claims))

// generate signed token using private key with RS384 algorithm
alg := jwt.SigningMethodRS384
signedToken, err := jwt.NewWithClaims(alg, claims).SignedString(signKey)
So(err, ShouldBeNil)
log.Info("  => signed token", signedToken)

// prepare api call payload
payload := map[string]string{
    "grant_type":            "client_credentials",
    "client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
    "client_assertion":      signedToken,
}

// dispatch the api call
req := resty.New().
    R().
    EnableTrace().
    SetFormData(payload)
res, err := req.Post(oauth2TokenUrl)
So(err, ShouldBeNil)
log.Info("  => response status:", res.StatusCode())
log.Info("  => response header:", res.Header())
log.Info("  => response body:", string(res.Body()))

// parse response
resBody := make(map[string]interface{})
err = json.Unmarshal(res.Body(), &resBody)
So(err, ShouldBeNil)

Solution

  • Fantastic, I got it working now.

    The solution is simply waiting! it was confusing because I can't find any explanation about this on the doc, and also the error message was not quite friendly.

    In summary, after creating dev app and the public key is uploaded there, we have to wait for a few hours/days, and then the credentials will eventually be usable.

    The waiting part is applied to both open epic and app orchard dev accounts.

    UPDATE #1

    Just in case anyone still experiencing the same error even after few hours passed, might be worth to check the following guidance from EPIC.

    https://fhir.epic.com/Resources/jwt_auth_troubleshoot_eof