smart-on-fhir / client-py

Python SMART on FHIR client
http://docs.smarthealthit.org
Other
596 stars 213 forks source link

Add an option to use custom auth request parameters #178

Open sky-code opened 2 months ago

sky-code commented 2 months ago

This API is not working out of the box with current auth implementaiton https://docs.athenahealth.com/api/guides/token-endpoint#3legged_Token_Generation_2

fhirclient.server.FHIRServer.post_as_form sends client_id and client_secret in basic auth header, but for API above those parameters must be sent in request body and there is no way to change that except of monkey patching.

I do monkey patching right before handle_callback call to make it work

def post_as_form(self_, url, formdata, auth=None):
    # monkey-patch fhirclient.server.FHIRServer.post_as_form
    formdata["client_id"] = auth[0]
    formdata["client_secret"] = auth[1]
    res = self_.session.post(url, data=formdata)
    self_.raise_for_status(res)
    patch_response_json(res)
    return res

self.fhir_client.server.post_as_form = post_as_form.__get__(self.fhir_client.server)

It would be nice to have some way to do that with options instead.

mikix commented 2 months ago

Do you know if Athena complains if you also send it basic auth? Curious if we could just send both unconditionally - would need to test on other servers too

sky-code commented 2 months ago

I did some testing, when sending auth in header: res = self_.session.post(url, data=formdata, auth=auth) returns 401 Unauthorized

'{
    "error": "Invalid Request",
    "detailedmessage": "Cannot supply multiple client credentials. Use one of the following: credentials in the Authorization header, credentials in the post body, or a client_assertion in the post body."
}
'

both header and body:

formdata["client_id"] = auth[0]
formdata["client_secret"] = auth[1]
res = self_.session.post(url, data=formdata, auth=auth)

returns 401 Unauthorized

'{
    "error": "Invalid Request",
    "detailedmessage": "Cannot supply multiple client credentials. Use one of the following: credentials in the Authorization header, credentials in the post body, or a client_assertion in the post body."
}
'

only this option works when credentials sent in body without header

formdata["client_id"] = auth[0]
formdata["client_secret"] = auth[1]
res = self_.session.post(url, data=formdata)

returns 200 OK

mikix commented 2 months ago

OK thank you - so we need to either somehow detect / handle this case or allow more customization of the auth flow.

sky-code commented 2 months ago

Not sure that detecting all such cases can be done reliable, more customization probably better option here.