I'm creating a backend systems application on Epic's App Marketplace. I checked the box "Use Oauth 2.0," am using R4 FHIR version, and I uploaded a public key to Epic's app marketplace. I'm using 'https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/R4' as the BASE_URL when instantiating a FHIR::Client instance. Per Epic's documentation for a Backend Systems app, a POST request needs to be made to the token endpoint to obtain an access token. This POST request needs to look like this example shown in Epic's documentation
POST <TOKEN_URL> HTTP/1.1
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&client_assertion=eyJhbGciOiJSUzM4NCIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJkNDUwNDljMy0zNDQxLTQwZWYtYWI0ZC1iOWNkODZhMTcyMjUiLCJzdWIiOiJkNDUwNDljMy0zNDQxLTQwZWYtYWI0ZC1iOWNkODZhMTcyMjUiLCJhdWQiOiJodHRwczovL2FwcG1hcmtldC5lcGljLmNvbS9pbnRlcmNvbm5lY3QtYW1jdXJwcmQtb2F1dGgvb2F1dGgyL3Rva2VuIiwianRpIjoiZjllYWFmYmEtMmU0OS0xMWVhLTg4ODAtNWNlMGM1YWVlNjc5IiwiZXhwIjoxNTgzNTI0NDAyLCJuYmYiOjE1ODM1MjQxMDIsImlhdCI6MTU4MzUyNDEwMn0.cM3o-9LzamiNP0luN1slylY3W0mo8YwYx4Bqj2uLPf_IhEUvt8Y3BiYBGlfnU_RgWaMkij9TCuhCRk7A8N6kVVxpBknd0JscKyTKeIrFqPbYk0gkWbpOjsxtkWnuKS0bdkjlRPXsaLMJ4uNfSAcViuuZ-wbLxnWX4AEZzzGXj8lt3uNKfOx7i3f-3wXZ-Mwe0qQq3pUjex7LpQA7qPBoQ2dJFXn491DBTW43QXsAebeKzWpKY94q5p2CU8qAJRBVrigbXc_C9Bsy_rknI-SGjH8X7BsIzMLLNFL5B1wIHhrkVFaZ83iLnJQQxaiFtsJUgtyaR02eSKhWdH3t40liMQ
This POST request needs to have a body that includes grant_type, client_credentials, client_assertion_type, and client_assertion. Here, client_assertion needs to be an encoded JWT token that looks like
def payload
{
'iss': NON_PROD_CLIENT_ID,
'sub': NON_PROD_CLIENT_ID,
'aud': AUD,
'iat': Time.zone.now.to_i,
'exp': Time.zone.now.to_i + 4 * 60,
'jti': SecureRandom.hex(6),
'kid': PUBLIC_KEY_FINGERPRINT
}
end
def assertion
@token ||= JWT.encode payload, rsa_private, 'RS384', {typ: 'JWT', alg: 'RS384', kid: PUBLIC_KEY_FINGERPRINT}
end
In FHIR::Client#set_oauth2_auth, OAuth2::Client#get_token is called with no arguments.
@client = client.client_credentials.get_token
OAuth2::Client#get_token accepts params as the first argument. In the method, it uses params to make the body of the POST request. If no params are passed, this request to get the access token will not be successful.
def get_token(params, access_token_opts = {}, access_token_class = AccessToken) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
params = Authenticator.new(id, secret, options[:auth_scheme]).apply(params)
opts = {:raise_errors => options[:raise_errors], :parse => params.delete(:parse)}
headers = params.delete(:headers) || {}
if options[:token_method] == :post
opts[:body] = params
opts[:headers] = {'Content-Type' => 'application/x-www-form-urlencoded'}
else
...
Can FHIR::Client#set_oauth2_auth be re-worked to accept a params argument so that I can pass the necessary parameters to the make the initial POST request to get the needed access token? Please let me know if you could use more information from me or if I'm misunderstanding the implementation.
In case it's useful, this is the terminal output when I try to run the example oauth2 code from GH
I, [2022-11-07T15:25:21.450261 #45018] INFO -- : Initializing client with https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/R4
I, [2022-11-07T15:25:21.450680 #45018] INFO -- : Configuring the client to use no authentication.
I, [2022-11-07T15:25:21.451568 #45018] INFO -- : GETTING: https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/R4/metadata
D, [2022-11-07T15:25:21.864158 #45018] DEBUG -- : GET - Request: {"method":"get","headers":{"User-Agent":"Ruby FHIR Client","Accept-Charset":"utf-8","Accept":"application/fhir+json"},"url":"https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/R4/metadata","password":null,"user":null,"uri":"https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/R4/metadata","cookie_jar":[],"payload":null,"block_response":null,"raw_response":false,"stream_log_percent":10,"ssl_opts":{"verify_ssl":1,"cert_store":{"verify_callback":null,"error":null,"error_string":null,"chain":null,"time":null}},"log":null,"max_redirects":10,"processed_headers":{"Accept":"application/fhir+json","User-Agent":"Ruby FHIR Client","Accept-Charset":"utf-8"},"processed_headers_lowercase":{"accept":"application/fhir+json","user-agent":"Ruby FHIR Client","accept-charset":"utf-8"},"args":{"method":"get","url":"https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/R4/metadata","headers":{"User-Agent":"Ruby FHIR Client","Accept-Charset":"utf-8","Accept":"application/fhir+json"}},"before_execution_proc":null}, Response: [too large]
D, [2022-11-07T15:25:21.865255 #45018] DEBUG -- : Parsing response with {klass: FHIR::CapabilityStatement, format: application/fhir+json, code: 200}.
I, [2022-11-07T15:25:21.890849 #45018] INFO -- : Configuring the client to use OpenID Connect OAuth2 authentication.
OAuth2::Error: invalid_client:
{
"error": "invalid_client",
"error_description": null
}
from /Users/austinmiller/.rbenv/versions/3.0.4/lib/ruby/gems/3.0.0/gems/oauth2-1.4.4/lib/oauth2/client.rb:120:in `request'
I'm creating a backend systems application on Epic's App Marketplace. I checked the box "Use Oauth 2.0," am using
R4FHIR version, and I uploaded a public key to Epic's app marketplace. I'm using'https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/R4'as theBASE_URLwhen instantiating aFHIR::Clientinstance. Per Epic's documentation for a Backend Systems app, aPOSTrequest needs to be made to the token endpoint to obtain an access token. ThisPOSTrequest needs to look like this example shown in Epic's documentationThis
POSTrequest needs to have a body that includesgrant_type,client_credentials,client_assertion_type, andclient_assertion. Here,client_assertionneeds to be an encoded JWT token that looks likeIn
FHIR::Client#set_oauth2_auth,OAuth2::Client#get_tokenis called with no arguments.OAuth2::Client#get_tokenacceptsparamsas the first argument. In the method, it usesparamsto make the body of thePOSTrequest. If no params are passed, this request to get the access token will not be successful.Can
FHIR::Client#set_oauth2_authbe re-worked to accept aparamsargument so that I can pass the necessary parameters to the make the initialPOSTrequest to get the needed access token? Please let me know if you could use more information from me or if I'm misunderstanding the implementation.In case it's useful, this is the terminal output when I try to run the example oauth2 code from GH