I'm implementing a FHIR integration in Python to pull data from Epic's sandbox environment. I wanted to see if anyone else has attempted this, or has had any issues with token authentication when doing the kick-off request.
Some notable information on my build/progress:
I'm using https://fhir.epic.com/interconnect-fhir-oauth/oauth2/token to get the token
The JWT passed back is valid and signed
Per Epic's bulk export specification, I'm using https://apporchard.epic.com/interconnect-aocurprd-oauth/api/FHIR/R4/Group/eIscQb2HmqkT.aPxBKDR1mIj3721CpVk1suC7rlu3yX83/$export as the endpoint to request data from.
To the above url, in my get request, I pass the following headers:
'Accept':'application/fhir+json',
'Content-type' : 'application/fhir+json', <- I've also tried removing this as it isn't explicitly stated to be passed
'Prefer':'respond-async',
'Authorization' : 'Bearer ' + token
I use a session to handle requests, and preserve the Authorization header by overriding NoRebuildAuthSession in requests.Session
The response from the get request comes back with a 401 error and the following information under www-authenticate: Bearer error="invalid_token", error_description="The access token provided is not valid"
Any guidance would be helpful! I think I might be using the wrong endpoint but I'm totally stumped right now.
class NoRebuildAuthSession(Session):
def rebuild_auth(self, prepared_request, response):
"""
No code here means requests will always preserve the
Authorization
header when redirected.
"""
session = NoRebuildAuthSession()
logging.basicConfig(level=logging.DEBUG)
instance = jwt.JWT()
message = {
# Client ID for non-production
'iss': '<client-id>',
'sub': '<client-id>',
'aud': 'https://fhir.epic.com/interconnect-fhir-oauth/oauth2/token',
'jti': '<string of characters>',
'iat': get_int_from_datetime(datetime.now(timezone.utc)),
'exp': get_int_from_datetime(datetime.now(timezone.utc) + timedelta(minutes=1)),
}
# Load a RSA key from a PEM file.
with open('<private-key-path>', 'rb') as fh:
signing_key = jwt.jwk_from_pem(fh.read())
compact_jws = instance.encode(message, signing_key, alg='RS384')
headers = CaseInsensitiveDict()
headers['Content-Type'] = 'application/x-www-form-urlencoded'
data = {
'grant_type': 'client_credentials',
'client_assertion_type': 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
'client_assertion': compact_jws
}
x = session.post('https://fhir.epic.com/interconnect-fhir-oauth/oauth2/token', headers=headers, data=data)
responseDict = json.loads(x.text)
token = x.json()['access_token']
print(responseDict['access_token'])
headers = {
'Accept':'application/fhir+json',
'Content-type' : 'application/fhir+json',
'Prefer':'respond-async',
'Authorization' : 'Bearer ' + token
}
session.headers = headers
base = "https://apporchard.epic.com/interconnect-aocurprd-oauth/api/FHIR/R4/Group/eIscQb2HmqkT.aPxBKDR1mIj3721CpVk1suC7rlu3yX83/$export"
y = session.get(base, headers = headers)
print(yaml.dump(y.headers, default_flow_style=False))
print(y.text)