Hire me
Do you have a project you need help with? Would you like to improve your processes to work faster and more efficiently? Contact me for a free consult to see if I can help, let's get you and your team being productive and shipping products.
After I upgraded my Open EMR instance without reading the changelog (never do this) and breaking my site (this is why) due to massive changes to the way their API works I had to then spend 4 days wrapping my brain around how the new API oauth2 flows work with Open EMR v6. Luckily my app is a POC and nothing to be concerned about it being broken so I had time to work on it, the following is what I learned in my 4 days trying to get Open EMR v6 API working with my React app using the Password Grant Flow.
The first thing we need to do is enable the API, to do this we go to Administration > Globals on the main menu. We then proceed to click on the Connectors link on the sidebar menu. On this screen, we need to enable a few things and add our site address in order to get the API up and running.
In order to use the API we need to register an API Client for our app that will be accessing Open EMRs data, theres a few ways to accomplish this, you can use the curl registration example they have on the API Readme if thats your thing, me I do all my API testing with Postman and will be using that for this tutorial.
So fire up postman and we are going to use the
curl registration example code in postman to register our client. Based on the registration example we see in order to register an API Client we need to make a POST to the API endpoint: http://localhost/oauth2/default/registration
and send it some JSON data.
In Postman make sure you set your Body to raw and paste the following JSON object:
{
"application_type": "private",
"redirect_uris": [
"http://localhost:3000"
],
"post_logout_redirect_uris": [
"http://localhost:3000"
],
"initiate_login_uri": "https://localhost:3000",
"client_name": "external-web-app",
"token_endpoint_auth_method": "client_secret_post",
"username": "admin",
"password": "pass",
"scope": "openid api:oemr api:fhir api:port api:pofh user/allergy.read user/allergy.write user/appointment.read user/appointment.write user/dental_issue.read user/dental_issue.write user/document.read user/document.write user/drug.read user/encounter.read user/encounter.write user/facility.read user/facility.write user/immunization.read user/insurance.read user/insurance.write user/insurance_company.read user/insurance_company.write user/insurance_type.read user/list.read user/medical_problem.read user/medical_problem.write user/medication.read user/medication.write user/message.write user/patient.read user/patient.write user/practitioner.read user/practitioner.write user/prescription.read user/procedure.read user/soap_note.read user/soap_note.write user/surgery.read user/surgery.write user/vital.read user/vital.write user/AllergyIntolerance.read user/CareTeam.read user/Condition.read user/Encounter.read user/Immunization.read user/Location.read user/Medication.read user/MedicationRequest.read user/Observation.read user/Organization.read user/Organization.write user/Patient.read user/Patient.write user/Practitioner.read user/Practitioner.write user/PractitionerRole.read user/Procedure.read patient/encounter.read patient/patient.read patient/Encounter.read patient/Patient.read"
}
In this example I added all the API scopes since I wanted to test and make sure everything works but in the real world you want to only assign the required scopes to your API Client for security purposes. Make sure you specify a client_name
for your API Client, for easier identification I use the name of the app I am connecting to Open EMR. Also of note make sure you use the proper username
, password
for the respective account you want to register.
Click send and wait for the API response.
You should get a response similar to this:
{
"client_id":"Wy4c-6ZXlUE1YB7yoW1YcL1IZ1MQWRwFH3Q49itRtnw",
"client_secret":"BPdPWQZbLMj_qyjmpLHUY6mWPvj-YsgG_wklT9bO1m2xP7VbkMa5edUzuwZrmDmBrmDtG93bvjFtsZKTtBeLRQ",
"registration_access_token":"pu6OnFowFkDAdw9oNyFyDW2_ACsHmJlbHldRetpeUcQ",
"registration_client_uri":"http:\/\/localhost\/oauth2\/default\/client\/FxEp5ADJTFYMHC9lsfG4UQ",
"client_id_issued_at":1611675409,
"client_secret_expires_at":0,
"client_role":"user",
"application_type":"private",
"client_name":"external-web-app",
"redirect_uris":["https:\/\/localhost:3000"],
"post_logout_redirect_uris":["https:\/\/localhost:3000"],
"token_endpoint_auth_method":"client_secret_post",
"initiate_login_uri":"https:\/\/localhost:3000",
"scope":"openid api:oemr api:fhir api:port api:pofh user\/allergy.read user\/allergy.write user\/appointment.read
user\/appointment.write user\/dental_issue.read user\/dental_issue.write user\/document.read user\/document.write
user\/drug.read user\/encounter.read user\/encounter.write user\/facility.read user\/facility.write
user\/immunization.read user\/insurance.read user\/insurance.write user\/insurance_company.read
user\/insurance_company.write user\/insurance_type.read user\/list.read user\/medical_problem.read
user\/medical_problem.write user\/medication.read user\/medication.write user\/message.write user\/patient.read
user\/patient.write user\/practitioner.read user\/practitioner.write user\/prescription.read user\/procedure.read
user\/soap_note.read user\/soap_note.write user\/surgery.read user\/surgery.write user\/vital.read user\/vital.write
user\/AllergyIntolerance.read user\/CareTeam.read user\/Condition.read user\/Encounter.read user\/Immunization.read
user\/Location.read user\/Medication.read user\/MedicationRequest.read user\/Observation.read user\/Organization.read
user\/Organization.write user\/Patient.read user\/Patient.write user\/Practitioner.read user\/Practitioner.write
user\/PractitionerRole.read user\/Procedure.read patient\/encounter.read patient\/patient.read patient\/Encounter.read
patient\/Patient.read"
}
Head back to your Open EMR instance and click on Administrations > System > API Clients you will see the API Client you registered which should be in a disabled state, every API Client is disabled by default so we need to enable it.
In order to do so click on Edit and click on Enable Client
Your API Client is now ready to access Open EMRs API.
Since my React app does not use a database I use Open EMR as it’s database/backend server I require the use of Password Grant flow which is similar to how we accessed the API prior to v6. Now that we enabled our API Client we can proceed to authenticate with it.
In postman open a new tab we are going to do a POST to the API endpoint: http://localhost/oauth2/default/token
in the Body tab select x-www-form-urlencoded and we are going to need to add a few key/value pairs with the data the endpoint is expecting.
client_id
insert the Client ID you got from your registration (it is also accessible via the Client Registrations screen in Open EMR)grant_type
= “password”user_role
= “users”scope
= Open EMR scope list you needusername
= username of registered Open EMR userpassword
= password of the registered Open EMR userClick send and wait for the API response.
You should get a response similar to this:
{
"id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiJHTXlkQ1ZGNzZMUC0zT1JXYUk1ekthSUUwQzFzZW51YTAxTHVOc0tZWlZFIiwiaXNzIjoiaHR0cDpcL1wvbG9jYWxob3N0XC9vYXV0aDJcL2RlZmF1bHQiLCJpYXQiOjE2MTE2NzU1MDksImV4cCI6MTYxMTY3OTEwOSwic3ViIjoiOTI4NGI4NDMtM2RhZi00Nzg0LTk2NGEtNmZiYjA0MzUzZjk3IiwiYXBpOm9lbXIiOnRydWUsImFwaTpmaGlyIjp0cnVlLCJhcGk6cG9ydCI6dHJ1ZSwiYXBpOnBvZmgiOnRydWV9.kie0UsDzkUsqFlWYyf-U9-nVA32uLY96xxDBXyIGN6iuuR4ndGy1lqfgOuwhcaVZcoTeLxEK9_lm1xksz5ZdpMegTzNdXZcaV2F7ymETWC1z0_KE-ogPetxSBNiSIOiJ-QTzDlKDJmGO8Y9j6A16bkSfR80r2qwupTP8z5oR7fzRQ1lVrntFqZNkPlJOSM8XwrlifEUcj41-IpBIxBzwd6XX7ygdtS32LnQH0Kj5Lsav7Y4VNcXSon1Gyy61xGgG024TIs3qkrIoRe0PoomZWdI7UAxwbzLngKcP81U0g9u0-7oPzKhYPpYCR6Wxauct70AVKdAXKv3yVqKsUlf4lA",
"scope": "openid user/allergy.read user/allergy.write user/appointment.read user/appointment.write user/dental_issue.read user/dental_issue.write user/document.read user/document.write user/drug.read user/encounter.read user/encounter.write user/facility.read user/facility.write user/immunization.read user/insurance.read user/insurance.write user/insurance_company.read user/insurance_company.write user/insurance_type.read user/list.read user/medical_problem.read user/medical_problem.write user/medication.read user/medication.write user/message.write user/patient.read user/patient.write user/practitioner.read user/practitioner.write user/prescription.read user/procedure.read user/soap_note.read user/soap_note.write user/surgery.read user/surgery.write user/vital.read user/vital.write user/AllergyIntolerance.read user/CareTeam.read user/Condition.read user/Encounter.read user/Immunization.read user/Location.read user/Medication.read user/MedicationRequest.read user/Observation.read user/Organization.read user/Organization.write user/Patient.read user/Patient.write user/Practitioner.read user/Practitioner.write user/PractitionerRole.read user/Procedure.read patient/encounter.read patient/patient.read patient/Encounter.read patient/Patient.read",
"token_type": "Bearer",
"expires_in": 3600,
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiJHTXlkQ1ZGNzZMUC0zT1JXYUk1ekthSUUwQzFzZW51YTAxTHVOc0tZWlZFIiwianRpIjoiZDY3NzQ0YzM0NDRiODYyOThiZDNhYWUyY2E5YzJlYTAyMjkwODBlMGRhMWMwNzczMzVkNzRhNmY4NDhjY2JmOGQ3YjBiNzliNTgxZjhhODciLCJpYXQiOjE2MTE2NzU1MDksIm5iZiI6MTYxMTY3NTUwOSwiZXhwIjoxNjExNjc5MTA5LCJzdWIiOiI5Mjg0Yjg0My0zZGFmLTQ3ODQtOTY0YS02ZmJiMDQzNTNmOTciLCJzY29wZXMiOlsib3BlbmlkIiwiYXBpOm9lbXIiLCJhcGk6ZmhpciIsImFwaTpwb3J0IiwiYXBpOnBvZmgiLCJ1c2VyXC9hbGxlcmd5LnJlYWQiLCJ1c2VyXC9hbGxlcmd5LndyaXRlIiwidXNlclwvYXBwb2ludG1lbnQucmVhZCIsInVzZXJcL2FwcG9pbnRtZW50LndyaXRlIiwidXNlclwvZGVudGFsX2lzc3VlLnJlYWQiLCJ1c2VyXC9kZW50YWxfaXNzdWUud3JpdGUiLCJ1c2VyXC9kb2N1bWVudC5yZWFkIiwidXNlclwvZG9jdW1lbnQud3JpdGUiLCJ1c2VyXC9kcnVnLnJlYWQiLCJ1c2VyXC9lbmNvdW50ZXIucmVhZCIsInVzZXJcL2VuY291bnRlci53cml0ZSIsInVzZXJcL2ZhY2lsaXR5LnJlYWQiLCJ1c2VyXC9mYWNpbGl0eS53cml0ZSIsInVzZXJcL2ltbXVuaXphdGlvbi5yZWFkIiwidXNlclwvaW5zdXJhbmNlLnJlYWQiLCJ1c2VyXC9pbnN1cmFuY2Uud3JpdGUiLCJ1c2VyXC9pbnN1cmFuY2VfY29tcGFueS5yZWFkIiwidXNlclwvaW5zdXJhbmNlX2NvbXBhbnkud3JpdGUiLCJ1c2VyXC9pbnN1cmFuY2VfdHlwZS5yZWFkIiwidXNlclwvbGlzdC5yZWFkIiwidXNlclwvbWVkaWNhbF9wcm9ibGVtLnJlYWQiLCJ1c2VyXC9tZWRpY2FsX3Byb2JsZW0ud3JpdGUiLCJ1c2VyXC9tZWRpY2F0aW9uLnJlYWQiLCJ1c2VyXC9tZWRpY2F0aW9uLndyaXRlIiwidXNlclwvbWVzc2FnZS53cml0ZSIsInVzZXJcL3BhdGllbnQucmVhZCIsInVzZXJcL3BhdGllbnQud3JpdGUiLCJ1c2VyXC9wcmFjdGl0aW9uZXIucmVhZCIsInVzZXJcL3ByYWN0aXRpb25lci53cml0ZSIsInVzZXJcL3ByZXNjcmlwdGlvbi5yZWFkIiwidXNlclwvcHJvY2VkdXJlLnJlYWQiLCJ1c2VyXC9zb2FwX25vdGUucmVhZCIsInVzZXJcL3NvYXBfbm90ZS53cml0ZSIsInVzZXJcL3N1cmdlcnkucmVhZCIsInVzZXJcL3N1cmdlcnkud3JpdGUiLCJ1c2VyXC92aXRhbC5yZWFkIiwidXNlclwvdml0YWwud3JpdGUiLCJ1c2VyXC9BbGxlcmd5SW50b2xlcmFuY2UucmVhZCIsInVzZXJcL0NhcmVUZWFtLnJlYWQiLCJ1c2VyXC9Db25kaXRpb24ucmVhZCIsInVzZXJcL0VuY291bnRlci5yZWFkIiwidXNlclwvSW1tdW5pemF0aW9uLnJlYWQiLCJ1c2VyXC9Mb2NhdGlvbi5yZWFkIiwidXNlclwvTWVkaWNhdGlvbi5yZWFkIiwidXNlclwvTWVkaWNhdGlvblJlcXVlc3QucmVhZCIsInVzZXJcL09ic2VydmF0aW9uLnJlYWQiLCJ1c2VyXC9Pcmdhbml6YXRpb24ucmVhZCIsInVzZXJcL09yZ2FuaXphdGlvbi53cml0ZSIsInVzZXJcL1BhdGllbnQucmVhZCIsInVzZXJcL1BhdGllbnQud3JpdGUiLCJ1c2VyXC9QcmFjdGl0aW9uZXIucmVhZCIsInVzZXJcL1ByYWN0aXRpb25lci53cml0ZSIsInVzZXJcL1ByYWN0aXRpb25lclJvbGUucmVhZCIsInVzZXJcL1Byb2NlZHVyZS5yZWFkIiwicGF0aWVudFwvZW5jb3VudGVyLnJlYWQiLCJwYXRpZW50XC9wYXRpZW50LnJlYWQiLCJwYXRpZW50XC9FbmNvdW50ZXIucmVhZCIsInBhdGllbnRcL1BhdGllbnQucmVhZCIsInNpdGU6ZGVmYXVsdCJdfQ.DD-ddIubs6CLzFU6n3hJ37tEvZ4YkaMLP8dof97xSyo-NKI3Ar3MbhlxsLbKL1eWCLh9AJxx2Bdj69Nf1WHut48dQd-471wVJR5hs0BIp7JD4fz2LgqmNfIQVKmvk4uUmeozkKfzeBGgLEleGV_Ezgs79ABRN0yAfq5qYISjrmOgWI3XW_StrexrEfutkvCg9LarliHnsWUN5uQdhGzlJCIoi_FoR3rpFCl3r64iSc2MxCOTz3sgRDj1Aq00DfWSjP7EwR3m9Ky1kXN-lJpYvjprfy9lHpmnIMldG2unyWSSe9qf9P_NgFrcIFkp-rgK2BRTlj-WlmVw9Lq9Ea0M_A",
"refresh_token": "def50200862c8846430bf966f95595cb9af0e22582c14527da4f2e62ca966c4337f6625e3f900bdc37bd4a5dd30ae27602a05910d73b025b74ce4cb2355e45d27dae36d606892fd7a237c2b134357563d05e97cef77a0379ec5cfe9872e10df2c8673a6962cd2bba0feb02795d686b774d77b1634a70e0b44a945df1313f46b4dbb23ae08db2b88f10816dc1ca978dae18cbe497bb92f66ab179b88850a4c20b3242b3ec25fe674869a2d341ce851f0eb89aeba26fc4d69c5f0dd657a95aaac25c22fef0527bcb908b150710490f95e0081543dd5ad036f5065bcc39546595ea79018b2a77d084466bd955e9536f8e02ed47a06e006dd5c8d831eedbcc18773f8ee190abf1d226842c755dd3b1e276d14f01f74b2e46998c8b6b764b6ab19f9b30e77d5806574346582177e6bb5d1a71aa7ef5d9e939ea7f8426b3218d92df39bcac764e9259063b340123635947678a2d9af5cd4ab91ecc5b76644574717c0cf8b540a9df0d6092292cc4294a6df9725e50049cd202fe3f4de88b0fd4c1119b22234c691cb3bbc4d3793e88ee29c00744b2e22e38f53e034a20ff53bc982d7a5c1b836f1108e846fcda2b088b40f652993ff2da23eafccfb3973e312419a9622a20138a27963689f42450aa6260ebe84a892cf31fb15841873914af57fd1e2557926450e4483335c31891c706f6fcf91668acc981ab68fa213d812e8bb1601851c04a222c083b133a2fcaae1bae26c6f49f20c074a6142e2ea04cf83fd6ade52a43273f51c7a6a5adde6c7144becb46b6ed3b0030b085d105dae19d94033f1f938dbee5c90b604793f1a91ae6c499da2d1904858741a7664588d6ecb86d67364694e6f90ac34511b9a326f8db672622882e7c07e45826a6eeb7be1d54a064bb03455d554205667295304bed6fdf76b8430cd1ba77a6b26bbacdebdba922075270771ec34183d388cbfee8a9af72f2e7937ecfc93f7cdfb20a02e087ca673e1ee515eaef27db82b9addfbbd98384ee8ad5f485cf10773adbb893db51cde17ade2f38531cfa1d809a988a6682eb2f56ab9c00351c0628dd61784ee9ce3556a6d4ffe4e0dfe53841f3bf8b9d0208d6471a671e661dea12459caecd8ccd30962fc4da2e9d96e69433f019943b78d769ad9fb829931413a17db2eccccbd5df8175776def80181d2f5e89a22a7186267a3359eeafc9f41988da02a39c057f15e9d25a1b1f1af2b2e9a8feca48d3c0ffe7de1e9aeabb27d8f7565dcaa7bfbce482f15d926438183b6661feaca344856e149aaa3962a3a2f683cd7fc03ac165a0b5f7ca07ac90dbfb463a7026d1cfdb1f4abe9874196a45c27ee3bc74b743e423a3c143518d00be34633e6816c76fc3cdb888048f6e25ce3324c6fbd80870a884ad1de365a3895347a2aedb509ab77c28fbccce91861ddeaa48b460545ed74565a94ceefe6ea3768231b801be53c4bd609afddd988b955fe806bf036baed718f3f31bd6966e66883896c409adfd61fd075dc1ff085bda3463e6fdd865878e82cd3ba5677460d43b6f3cf933dc142db4739e8e3b4426f206daa7d244c4f686dd2e533b1a54d5d028e321e922b274da70afe1e34faf3e95806eff2ff0a5dc990d0b9d4f6895f8d8d5296fe91fe527a5af375f8e9b777e4639962eefe8178a986177fdfffd2440e4952a030f6676ccd1df98c404bf4eed2bdc8dc194cf144437d0845c03fd4d67f0de96f2b359eed6715a72c85224230555a32a7a0d07f78f82612a5384e0052a090c604c5f108e664b2d44f01d3ecb38a84e591670bef9afe3e4ba54bf2166201cc75fb9521b6354730d7eb7cff0727c3b14d89cf0a2894a4b312f3d2f7c47f77aefedfdc9950f4e311b486cfdb2acd383a039c7d11b5c8216f0a08bc937c0c2fa68cf9c0e4fb3c1c8e388ff7999b9fad1f6ceb27f46cfe0c77af410a3e2f38c97aa440d8f625a8638ae100d2fca76c2355d385ef2568c138c3bde5340053f261bb9835a8b7f513d7e3371ce63c32db86d2889c7b4325ff31566ff4d68b20c2da1719734a9d684c34b67048870b770335dbd1d8396ca73ec70b3d65f30d6dedf3fd23af78cae46af54924cb2ad01acdec99a22442c9c625fd0bea93d5c85ff7afd231fe1fe0391c95ee684fd8c0bf86cd56f1040ea01600b1826ebfa3a9d7c846528027e0227665e78c897017ecd60c1d053d575bae1f782edac69269eb1f65aaa38c5afdcfb8e858d4e6792890bacc9d0da5fbf406e2b224641122824b6dc00703f987d0ccf062b06a9b5bb895a7ec77995a543a32cd7a6255b1536c29b6664c2d3b2f0bdd6ad85c4fac02d451b64fbcca18d3a03098a2d2359d096a6487032b5bf477e5d09f44ab964d1a2422acf6ae76719c00361a3dfdf9ad392b1dbe699736bde1a06104aadbd5f73eca386d7883cd0b1d37a267da0f490dfcee78401cecb59e84574f56a54dd57911b9d52f9301b37646284339533a8549ccc8ab10988f37055e3e0b035815a11b6dfac76f9b97d73660909c0c141db30279a04be4d12542f93c7ceb63b0e3977120f5431bfb93f37455183f6f568f6527ccf424a302468687cbc9b676854cfa469d6d54bb27eb28ff87eb04e369c7d2f3928600ce504a2c451d8277457e5e6e57e2dcbef07aad87c92d1a098b60a75b347b3de1bfdf2b68551b24fad5712d407ce3780b4d0426ebad20acaedbcd3303ed8"
}
You know have the access_token
to be able to make API requests to Open EMR.
In order to make requests to Open EMRs API we need to provide the endpoints the authorization bearer token (access_token
we got in the previous step). Back in postman let’s open a new tab to make our first API request.
We are going to make an API GET request to the patient endpoint: http://localhost/apis/default/api/patient
to retrieve a list of all patients in our Open EMR database, before we do this we need to provide psotman the access_token
as a Bearer Token. Click on the Authorization tab in postman under type make sure to select Bearer Token then paste your access_token
and click Send.
You will get the list of patients in your Open EMR database 🙌 🎉