-
Notifications
You must be signed in to change notification settings - Fork 60
Description
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'