diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000..16d875f
Binary files /dev/null and b/.DS_Store differ
diff --git a/README-Health-Data-Access-Pattern_Integration.md b/README-Health-Data-Access-Pattern_Integration.md
new file mode 100644
index 0000000..b08b368
--- /dev/null
+++ b/README-Health-Data-Access-Pattern_Integration.md
@@ -0,0 +1,47 @@
+# How to Securely Deploy Patient Browser Application by integrating with a Health Data Access Pattern FHIR server
+
+## Introduction
+
+Instructions are provided below for a configuration to securely deploy the Patient Browser Application by integrating with a FHIR server protected by a SMART App Launch authorization server that is built on Keycloak, as per the [Health Data Access Reference Implementation](https://github.com/Alvearie/health-patterns/tree/main/data-access). Note that this is a local deployment end to end - with Patient Browser, IBM FHIR server and Keycloak service each running in separate docker containers.
+
+
+## Deployment
+
+### Health Data Access Pattern Deployment
+
+Deploy the Health Data Access Pattern as per [here](https://github.com/Alvearie/health-patterns/tree/main/data-access) but with the following additional steps carried out within the Keycloak Admin Console;
+
+1. Select 'Client' from LHS menu.
+2. Click on 'inferno' client from Client pane.
+3. If you don't want additional Consent screen after login screen the first time you try to log into Patient Browser, then set 'Consent Required' to 'OFF' for 'inferno' client.
+4. Add 'http://127.0.0.1:8081/*' to list of 'Valid Redirect URLs'
+5. Add 'http://127.0.0.1:8081' to 'Web Origins' list.
+
+
+### Patient Browser Deployment
+
+Git clone this repository and cd into this directory;
+
+```bash
+git clone https://github.com/Alvearie/patient-browser
+```
+
+Run the following to refresh the build;
+
+```bash
+npm i
+```
+
+Modify build/config/default.json5 as follows;
+1. Set server.url parameter to ‘https://localhost:9443/fhir-server/api/v4'
+2. Set authEnabled to ‘true’
+
+Start Patient Browser running on node http server locally by running;
+
+```bash
+npm start
+```
+
+URL to access Patient Browser deployment is -> http://127.0.0.1:8081
+
+When prompted for username/password enter fhiruser/change-password
diff --git a/build/.DS_Store b/build/.DS_Store
index 978b171..355d267 100644
Binary files a/build/.DS_Store and b/build/.DS_Store differ
diff --git a/build/config/default.json5 b/build/config/default.json5
index 21fc0bf..bd433c0 100644
--- a/build/config/default.json5
+++ b/build/config/default.json5
@@ -2480,6 +2480,7 @@
},
},
patientsPerPage: 25,
+ authEnabled: false,
timeout: 20000,
renderSelectedOnly: false,
fhirViewer: {
diff --git a/build/config/keycloak.json b/build/config/keycloak.json
new file mode 100644
index 0000000..0919ae0
--- /dev/null
+++ b/build/config/keycloak.json
@@ -0,0 +1,8 @@
+{
+ "realm": "test",
+ "auth-server-url": "https://localhost:8443/auth",
+ "ssl-required": "external",
+ "resource": "inferno",
+ "public-client": true,
+ "confidential-port": 0
+}
diff --git a/package.json b/package.json
index d6f72e7..5f3a27e 100644
--- a/package.json
+++ b/package.json
@@ -54,6 +54,7 @@
"if-env": "^1.0.0",
"jquery": "^3.2.1",
"json5": "^0.5.1",
+ "keycloak-js": "^17.0.0",
"less": "^2.7.2",
"less-loader": "^4.0.3",
"mixin-deep": "^1.2.0",
diff --git a/src/index.js b/src/index.js
index 4d410b4..e9f049f 100644
--- a/src/index.js
+++ b/src/index.js
@@ -8,27 +8,96 @@ import PatientList from "./components/PatientList";
import { Router, Route, Switch } from "react-router";
import createHistory from "history/createHashHistory";
import jQuery from "jquery";
+import Keycloak from "keycloak-js";
+import JSON5 from "json5";
+import { parseQueryString } from "./lib";
window.$ = window.jQuery = jQuery;
-const history = createHistory();
-
-ReactDOM.render(
-
-
-
-
-
-
-
-
-
- ,
- document.getElementById("main")
-);
-
-$(function () {
- $("body").tooltip({
- selector: ".patient-detail-page [title]",
- });
+let authEnabled = false;
+const DEFAULT_CONFIG = "default";
+let { config, ...params } = parseQueryString(window.location.search);
+
+jQuery.ajax({
+ url: `./config/${config || DEFAULT_CONFIG}.json5`,
+ dataType: "text",
+ cache: false,
+ async: false,
+ success: (json) => {
+ json = JSON5.parse(json);
+ authEnabled = json.authEnabled;
+ },
});
+
+if (authEnabled) {
+ let keycloak = new Keycloak("config/keycloak.json");
+
+ keycloak.onTokenExpired = function () {
+ keycloak.updateToken().then((refreshed) => {
+ if (refreshed) {
+ sessionStorage.setItem("access-token", keycloak.token);
+ }
+ });
+ };
+
+ keycloak
+ .init({
+ onLoad: "login-required",
+
+ scope: "patient/*.read",
+ })
+ .then(function (authenticated) {
+ if (authenticated) {
+ sessionStorage.setItem("access-token", keycloak.token);
+
+ const history = createHistory();
+
+ ReactDOM.render(
+
+
+
+
+
+
+
+
+
+ ,
+ document.getElementById("main")
+ );
+
+ $(function () {
+ $("body").tooltip({
+ selector: ".patient-detail-page [title]",
+ });
+ });
+ }
+ })
+ .catch(function () {
+ alert(
+ "Failed to initialize KeyCloak adapter. Check KeyCloak config file."
+ );
+ });
+} else {
+ const history = createHistory();
+
+ ReactDOM.render(
+
+
+
+
+
+
+
+
+
+ ,
+ document.getElementById("main")
+ );
+
+ $(function () {
+ $("body").tooltip({
+ selector: ".patient-detail-page [title]",
+ });
+ });
+}
diff --git a/src/lib/PatientSearch.js b/src/lib/PatientSearch.js
index 958ea28..054a10f 100644
--- a/src/lib/PatientSearch.js
+++ b/src/lib/PatientSearch.js
@@ -836,18 +836,33 @@ export default class PatientSearch {
if (server.type == "DSTU-2") {
data = data.replace(/\bdeceased=(true|false)\b/gi, "");
}
-
- // prepare the base options for the patient ajax request
- let options = {
- url: `${server.url}/Patient/_search`,
- method: "POST",
- processData: false,
- data,
- headers: {
- accept: "application/fhir+json",
- "content-type": "application/x-www-form-urlencoded",
- },
- };
+ // Take Access token from session storage if its available ie. if KeyCloak enabled authentication enabled.
+ // And only include authorization header if access token is available
+ let keycloakToken = sessionStorage.getItem("access-token");
+
+ let options = keycloakToken
+ ? {
+ url: `${server.url}/Patient/_search`,
+ method: "POST",
+ processData: false,
+ data,
+ headers: {
+ accept: "application/fhir+json",
+ "content-type": "application/x-www-form-urlencoded",
+
+ authorization: "Bearer " + keycloakToken,
+ },
+ }
+ : {
+ url: `${server.url}/Patient/_search`,
+ method: "POST",
+ processData: false,
+ data,
+ headers: {
+ accept: "application/fhir+json",
+ "content-type": "application/x-www-form-urlencoded",
+ },
+ };
return this.getPatientIDs(server)
.then((ids) => {