From 9bda857a9e9da8739085c4d65eb2dd0f522891af Mon Sep 17 00:00:00 2001 From: "Fergus MacConnell WLRS:EX" Date: Fri, 20 Sep 2024 09:54:28 -0700 Subject: [PATCH 1/7] Debugging keycloack --- app/frontend/src/common/authenticate.js | 95 +++++++++++---------- app/frontend/src/common/components/Auth.vue | 44 +++++++--- 2 files changed, 82 insertions(+), 57 deletions(-) diff --git a/app/frontend/src/common/authenticate.js b/app/frontend/src/common/authenticate.js index eba2da481..dbbee07e2 100644 --- a/app/frontend/src/common/authenticate.js +++ b/app/frontend/src/common/authenticate.js @@ -86,12 +86,12 @@ export default { renewToken (instance, retries = 0) { const maxRetries = 2 - instance.updateToken(1800).success((refreshed) => { + instance.updateToken(1800).then((refreshed) => { if (refreshed) { this.setLocalToken(instance) } this.scheduleRenewal(instance) - }).error((e) => { + }).catch((e) => { console.log(e) // The refresh token is expired or was rejected // we will retry after 60 sec (up to the count defined by maxRetries) @@ -113,52 +113,61 @@ export default { return new Promise((resolve, reject) => { this.getInstance() .then((instance) => { + console.log("INSTANCE: ", instance) if (instance.authenticated && ApiService.hasAuthHeader() && !instance.isTokenExpired(0)) { // We've already authenticated, have a header, and we've not expired. resolve(instance) } else { + + this.removeLocalToken() + instance.clearToken() + // We update the store reference only after wiring up the API. (Someone might be waiting + // for login to complete before taking some action. ) + store.commit('SET_KEYCLOAK', instance) + resolve(instance) // Attempt to retrieve a stored token, this may avoid us having to refresh the page. - const token = localStorage.getItem('token') - const refreshToken = localStorage.getItem('refreshToken') - const idToken = localStorage.getItem('idToken') - instance.init({ - pkceMethod: 'S256', - onLoad: 'check-sso', - checkLoginIframe: true, - timeSkew: 10, // Allow for some deviation - token, - refreshToken, - idToken } - ).success((result) => { - if (instance.authenticated) { - // We may have been authenticated, but the token could be expired. - instance.updateToken(60).success(() => { - // Store the token to avoid future round trips, and wire up the API - this.setLocalToken(instance) - // We update the store reference only after wiring up the API. (Someone might be waiting - // for login to complete before taking some action. ) - // Assumes that store passed in includes a 'SET_KEYCLOAK' mutation! - store.commit('SET_KEYCLOAK', instance) - this.scheduleRenewal(instance) - resolve(instance) - }).error(() => { - // The refresh token is expired or was rejected - this.removeLocalToken() - instance.clearToken() - // We update the store reference only after wiring up the API. (Someone might be waiting - // for login to complete before taking some action. ) - store.commit('SET_KEYCLOAK', instance) - resolve(instance) - }) - } else { - // We may have failed to authenticate, for many reasons, e.g. - it may be we never logged in, - // or have an expired token. - store.commit('SET_KEYCLOAK', instance) - resolve(instance) - } - }).error((e) => { - reject(e) - }) + // const token = localStorage.getItem('token') + // const refreshToken = localStorage.getItem('refreshToken') + // const idToken = localStorage.getItem('idToken') + // instance.init({ + // pkceMethod: 'S256', + // onLoad: 'check-sso', + // checkLoginIframe: true, + // timeSkew: 10, // Allow for some deviation + // token, + // refreshToken, + // idToken, + // } + // ).then((result) => { + // if (instance.authenticated) { + // // We may have been authenticated, but the token could be expired. + // instance.updateToken(60).then(() => { + // // Store the token to avoid future round trips, and wire up the API + // this.setLocalToken(instance) + // // We update the store reference only after wiring up the API. (Someone might be waiting + // // for login to complete before taking some action. ) + // // Assumes that store passed in includes a 'SET_KEYCLOAK' mutation! + // store.commit('SET_KEYCLOAK', instance) + // this.scheduleRenewal(instance) + // resolve(instance) + // }).error(() => { + // // The refresh token is expired or was rejected + // this.removeLocalToken() + // instance.clearToken() + // // We update the store reference only after wiring up the API. (Someone might be waiting + // // for login to complete before taking some action. ) + // store.commit('SET_KEYCLOAK', instance) + // resolve(instance) + // }) + // } else { + // // We may have failed to authenticate, for many reasons, e.g. - it may be we never logged in, + // // or have an expired token. + // store.commit('SET_KEYCLOAK', instance) + // resolve(instance) + // } + // }).then((e) => { + // reject(e) + // }) } }) .catch((error) => { diff --git a/app/frontend/src/common/components/Auth.vue b/app/frontend/src/common/components/Auth.vue index 8a54a3c86..8e9d589fc 100644 --- a/app/frontend/src/common/components/Auth.vue +++ b/app/frontend/src/common/components/Auth.vue @@ -37,21 +37,37 @@ export default { this.keyCloakLogin() } }, - keyCloakLogin () { - this.keycloak.init().success(() => { - this.keycloak.login({ idpHint: this.config.sso_idp_hint }).success((authenticated) => { - if (authenticated) { - ApiService.authHeader('JWT', this.keycloak.token) - if (window.localStorage) { - localStorage.setItem('token', this.keycloak.token) - localStorage.setItem('refreshToken', this.keycloak.refreshToken) - localStorage.setItem('idToken', this.keycloak.idToken) - } - } - }).error((e) => { - this.$store.commit(SET_ERROR, { error: 'Cannot contact SSO provider' }) + async keyCloakLogin () { + + try { + const authenticated = await this.keycloak.init({ + flow: 'standard' }) - }) + console.log("Authenticated: ", authenticated) + } catch(error) { + console.error("Failed to initialize Keycloak: ", error); + } + + // console.log("before init") + // this.keycloak.init({ + // adapter: 'default', + // flow: 'implicit' + // }).then(() => { + // console.log("keyCloakLogin .then()") + // this.keycloak.login({ idpHint: this.config.sso_idp_hint }).then((authenticated) => { + // if (authenticated) { + // ApiService.authHeader('JWT', this.keycloak.token) + // if (window.localStorage) { + // localStorage.setItem('token', this.keycloak.token) + // localStorage.setItem('refreshToken', this.keycloak.refreshToken) + // localStorage.setItem('idToken', this.keycloak.idToken) + // } + // } + // }).catch((e) => { + // console.log("keyCloakLogin ERROR: ", e) + // this.$store.commit(SET_ERROR, { error: 'Cannot contact SSO provider' }) + // }) + // }) }, keyCloakLogout () { // This should log the user out, but unfortunately does not delete the cookie storing the user From 3fb7af95583e9020bb1847f358f3af9a504a2ef6 Mon Sep 17 00:00:00 2001 From: "Fergus MacConnell WLRS:EX" Date: Fri, 20 Sep 2024 10:15:30 -0700 Subject: [PATCH 2/7] Add keycloak npm library. --- app/frontend/package-lock.json | 21 +++++ app/frontend/package.json | 3 +- app/frontend/src/common/authenticate.js | 96 ++++++++++----------- app/frontend/src/common/components/Auth.vue | 42 +++------ 4 files changed, 80 insertions(+), 82 deletions(-) diff --git a/app/frontend/package-lock.json b/app/frontend/package-lock.json index 37069eba9..ea086633e 100644 --- a/app/frontend/package-lock.json +++ b/app/frontend/package-lock.json @@ -10413,6 +10413,11 @@ "easy-stack": "^1.0.1" } }, + "js-sha256": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", + "integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==" + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -10554,6 +10559,22 @@ "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-3.0.0.tgz", "integrity": "sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew==" }, + "keycloak-js": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/keycloak-js/-/keycloak-js-18.0.0.tgz", + "integrity": "sha512-kaD6nrzgYX3NhgwQfVEZiAzaZXua3PSBIBWGWf8fgqPgUdiO8uvYaKV1Ebf43IAB5M8kvubW+2J+eBS+UnWsuw==", + "requires": { + "base64-js": "^1.5.1", + "js-sha256": "^0.9.0" + }, + "dependencies": { + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + } + } + }, "keyv": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz", diff --git a/app/frontend/package.json b/app/frontend/package.json index bf6de39a5..904d769d2 100644 --- a/app/frontend/package.json +++ b/app/frontend/package.json @@ -37,7 +37,8 @@ "vue-router": "^3.0.1", "vue-select": "^3.1.0", "vuejs-noty": "^0.1.3", - "vuex": "^3.0.1" + "vuex": "^3.0.1", + "keycloak-js": "18.0.0" }, "devDependencies": { "@vue/cli-plugin-babel": "^3.7.0", diff --git a/app/frontend/src/common/authenticate.js b/app/frontend/src/common/authenticate.js index dbbee07e2..1ab2cba1b 100644 --- a/app/frontend/src/common/authenticate.js +++ b/app/frontend/src/common/authenticate.js @@ -10,6 +10,7 @@ limitations under the License. */ import ApiService from '@/common/services/ApiService.js' +import Keycloak from 'keycloak-js'; import Vue from 'vue' export default { @@ -33,7 +34,7 @@ export default { const keyCloakScript = document.createElement('script') keyCloakScript.onload = () => { // Construct the Keycloak object and resolve the promise. - Vue.prototype.$keycloak = new Keycloak(response.data) + Vue.prototype.$keycloak = new Keycloak(response.data) resolve(Vue.prototype.$keycloak) } keyCloakScript.onerror = (e) => { @@ -92,7 +93,6 @@ export default { } this.scheduleRenewal(instance) }).catch((e) => { - console.log(e) // The refresh token is expired or was rejected // we will retry after 60 sec (up to the count defined by maxRetries) if (retries > maxRetries) { @@ -113,61 +113,53 @@ export default { return new Promise((resolve, reject) => { this.getInstance() .then((instance) => { - console.log("INSTANCE: ", instance) if (instance.authenticated && ApiService.hasAuthHeader() && !instance.isTokenExpired(0)) { // We've already authenticated, have a header, and we've not expired. resolve(instance) } else { - - this.removeLocalToken() - instance.clearToken() - // We update the store reference only after wiring up the API. (Someone might be waiting - // for login to complete before taking some action. ) - store.commit('SET_KEYCLOAK', instance) - resolve(instance) // Attempt to retrieve a stored token, this may avoid us having to refresh the page. - // const token = localStorage.getItem('token') - // const refreshToken = localStorage.getItem('refreshToken') - // const idToken = localStorage.getItem('idToken') - // instance.init({ - // pkceMethod: 'S256', - // onLoad: 'check-sso', - // checkLoginIframe: true, - // timeSkew: 10, // Allow for some deviation - // token, - // refreshToken, - // idToken, - // } - // ).then((result) => { - // if (instance.authenticated) { - // // We may have been authenticated, but the token could be expired. - // instance.updateToken(60).then(() => { - // // Store the token to avoid future round trips, and wire up the API - // this.setLocalToken(instance) - // // We update the store reference only after wiring up the API. (Someone might be waiting - // // for login to complete before taking some action. ) - // // Assumes that store passed in includes a 'SET_KEYCLOAK' mutation! - // store.commit('SET_KEYCLOAK', instance) - // this.scheduleRenewal(instance) - // resolve(instance) - // }).error(() => { - // // The refresh token is expired or was rejected - // this.removeLocalToken() - // instance.clearToken() - // // We update the store reference only after wiring up the API. (Someone might be waiting - // // for login to complete before taking some action. ) - // store.commit('SET_KEYCLOAK', instance) - // resolve(instance) - // }) - // } else { - // // We may have failed to authenticate, for many reasons, e.g. - it may be we never logged in, - // // or have an expired token. - // store.commit('SET_KEYCLOAK', instance) - // resolve(instance) - // } - // }).then((e) => { - // reject(e) - // }) + const token = localStorage.getItem('token') + const refreshToken = localStorage.getItem('refreshToken') + const idToken = localStorage.getItem('idToken') + instance.init({ + pkceMethod: 'S256', + onLoad: 'check-sso', + checkLoginIframe: true, + timeSkew: 10, // Allow for some deviation + token, + refreshToken, + idToken, + } + ).then((result) => { + if (instance.authenticated) { + // We may have been authenticated, but the token could be expired. + instance.updateToken(60).then(() => { + // Store the token to avoid future round trips, and wire up the API + this.setLocalToken(instance) + // We update the store reference only after wiring up the API. (Someone might be waiting + // for login to complete before taking some action. ) + // Assumes that store passed in includes a 'SET_KEYCLOAK' mutation! + store.commit('SET_KEYCLOAK', instance) + this.scheduleRenewal(instance) + resolve(instance) + }).error(() => { + // The refresh token is expired or was rejected + this.removeLocalToken() + instance.clearToken() + // We update the store reference only after wiring up the API. (Someone might be waiting + // for login to complete before taking some action. ) + store.commit('SET_KEYCLOAK', instance) + resolve(instance) + }) + } else { + // We may have failed to authenticate, for many reasons, e.g. - it may be we never logged in, + // or have an expired token. + store.commit('SET_KEYCLOAK', instance) + resolve(instance) + } + }).then((e) => { + reject(e) + }) } }) .catch((error) => { diff --git a/app/frontend/src/common/components/Auth.vue b/app/frontend/src/common/components/Auth.vue index 8e9d589fc..27bd9e809 100644 --- a/app/frontend/src/common/components/Auth.vue +++ b/app/frontend/src/common/components/Auth.vue @@ -38,36 +38,20 @@ export default { } }, async keyCloakLogin () { - - try { - const authenticated = await this.keycloak.init({ - flow: 'standard' + this.keycloak.init().then(() => { + this.keycloak.login({ idpHint: this.config.sso_idp_hint }).then((authenticated) => { + if (authenticated) { + ApiService.authHeader('JWT', this.keycloak.token) + if (window.localStorage) { + localStorage.setItem('token', this.keycloak.token) + localStorage.setItem('refreshToken', this.keycloak.refreshToken) + localStorage.setItem('idToken', this.keycloak.idToken) + } + } + }).catch((e) => { + this.$store.commit(SET_ERROR, { error: 'Cannot contact SSO provider' }) }) - console.log("Authenticated: ", authenticated) - } catch(error) { - console.error("Failed to initialize Keycloak: ", error); - } - - // console.log("before init") - // this.keycloak.init({ - // adapter: 'default', - // flow: 'implicit' - // }).then(() => { - // console.log("keyCloakLogin .then()") - // this.keycloak.login({ idpHint: this.config.sso_idp_hint }).then((authenticated) => { - // if (authenticated) { - // ApiService.authHeader('JWT', this.keycloak.token) - // if (window.localStorage) { - // localStorage.setItem('token', this.keycloak.token) - // localStorage.setItem('refreshToken', this.keycloak.refreshToken) - // localStorage.setItem('idToken', this.keycloak.idToken) - // } - // } - // }).catch((e) => { - // console.log("keyCloakLogin ERROR: ", e) - // this.$store.commit(SET_ERROR, { error: 'Cannot contact SSO provider' }) - // }) - // }) + }) }, keyCloakLogout () { // This should log the user out, but unfortunately does not delete the cookie storing the user From b7318cd86575ee540464beb20050cb8ee67923cb Mon Sep 17 00:00:00 2001 From: "Fergus MacConnell WLRS:EX" Date: Fri, 20 Sep 2024 15:55:45 -0700 Subject: [PATCH 3/7] Current keycloak working state. --- app/frontend/jest.config.js | 3 +- app/frontend/package-lock.json | 6 +- app/frontend/package.json | 2 +- app/frontend/src/common/authenticate.js | 158 +++++++++++++++----- app/frontend/src/common/components/Auth.vue | 9 +- app/frontend/src/common/store/auth.js | 1 + app/frontend/vue.config.js | 10 ++ 7 files changed, 146 insertions(+), 43 deletions(-) diff --git a/app/frontend/jest.config.js b/app/frontend/jest.config.js index bf28a52f6..422069dce 100644 --- a/app/frontend/jest.config.js +++ b/app/frontend/jest.config.js @@ -5,7 +5,8 @@ module.exports = { 'js', 'jsx', 'json', - 'vue' + 'vue', + '.mjs' ], transform: { '^.+\\.vue$': 'vue-jest', diff --git a/app/frontend/package-lock.json b/app/frontend/package-lock.json index ea086633e..a8b42b7a7 100644 --- a/app/frontend/package-lock.json +++ b/app/frontend/package-lock.json @@ -10560,9 +10560,9 @@ "integrity": "sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew==" }, "keycloak-js": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/keycloak-js/-/keycloak-js-18.0.0.tgz", - "integrity": "sha512-kaD6nrzgYX3NhgwQfVEZiAzaZXua3PSBIBWGWf8fgqPgUdiO8uvYaKV1Ebf43IAB5M8kvubW+2J+eBS+UnWsuw==", + "version": "21.1.2", + "resolved": "https://registry.npmjs.org/keycloak-js/-/keycloak-js-21.1.2.tgz", + "integrity": "sha512-+6r1BvmutWGJBtibo7bcFbHWIlA7XoXRCwcA4vopeJh59Nv2Js0ju2u+t8AYth+C6Cg7/BNfO3eCTbsl/dTBHw==", "requires": { "base64-js": "^1.5.1", "js-sha256": "^0.9.0" diff --git a/app/frontend/package.json b/app/frontend/package.json index 904d769d2..0220241eb 100644 --- a/app/frontend/package.json +++ b/app/frontend/package.json @@ -38,7 +38,7 @@ "vue-select": "^3.1.0", "vuejs-noty": "^0.1.3", "vuex": "^3.0.1", - "keycloak-js": "18.0.0" + "keycloak-js": "21.1.2" }, "devDependencies": { "@vue/cli-plugin-babel": "^3.7.0", diff --git a/app/frontend/src/common/authenticate.js b/app/frontend/src/common/authenticate.js index 1ab2cba1b..4e2b980e1 100644 --- a/app/frontend/src/common/authenticate.js +++ b/app/frontend/src/common/authenticate.js @@ -20,37 +20,77 @@ export default { */ return new Promise((resolve, reject) => { if (!Vue.prototype.$keycloak) { + + // try { + // Vue.prototype.$keycloak = new Keycloak() + + // return Vue.prototype.$keycloak; + + // } catch (error) { + // console.error("Keycloak Instance ERROR: ", error); + // } // Keycloak has not yet been loaded, get Keycloak configuration from the server. ApiService.query('keycloak', {}) .then(response => { + + console.log("Keycloak Config Data: ", response.data) + + const { + url, + 'ssl-required': sslRequired , + resource, + realm, + 'public-client': publicClient, + 'confidential-port': confidentialPort, + clientId, + 'auth-server-url': authServerUrl + } = response.data; /* "A best practice is to load the JavaScript adapter directly from Keycloak Server as it will automatically be updated when you upgrade the server. If you copy the adapter to your web application instead, make sure you upgrade the adapter only after you have upgraded the server."; source : https://www.keycloak.org/docs/latest/securing_apps/index.html#_javascript_adapter: */ - const jsUrl = `${response.data['auth-server-url']}/js/keycloak.js` + // const jsUrl = `${response.data['auth-server-url']}/js/keycloak.js` + // const jsUrl = "https://unpkg.com/keycloak-js@24.0.0/dist/keycloak.min.js" + // Inject the Keycloak javascript into the DOM. - const keyCloakScript = document.createElement('script') - keyCloakScript.onload = () => { - // Construct the Keycloak object and resolve the promise. - Vue.prototype.$keycloak = new Keycloak(response.data) - resolve(Vue.prototype.$keycloak) - } - keyCloakScript.onerror = (e) => { - // This is pretty bad - keycloak didn't load - this should never ever happen. - // There's not much we can do, so we set keycloak to a random empty object and resolve. - console.error(e) - Vue.prototype.$keycloak = {} - resolve(Vue.prototype.$keycloak) - } - keyCloakScript.async = true - keyCloakScript.setAttribute('src', jsUrl) - document.head.appendChild(keyCloakScript) + // const keyCloakScript = document.createElement('script') + + // keyCloakScript.onload = () => { + // // Construct the Keycloak object and resolve the promise. + // Vue.prototype.$keycloak = new Keycloak(response.data) + // resolve(Vue.prototype.$keycloak) + // } + Vue.prototype.$keycloak = new Keycloak({ + url: authServerUrl, + realm, + clientId, + sslRequired, + resource, + publicClient, + confidentialPort, + }) + + resolve(Vue.prototype.$keycloak) + + // keyCloakScript.onerror = (e) => { + // // This is pretty bad - keycloak didn't load - this should never ever happen. + // // There's not much we can do, so we set keycloak to a random empty object and resolve. + // console.error(e) + // Vue.prototype.$keycloak = {} + // resolve(Vue.prototype.$keycloak) + // } + + // keyCloakScript.async = true + // keyCloakScript.setAttribute('src', jsUrl) + // document.head.appendChild(keyCloakScript) }) .catch(error => { + console.error(error) Vue.prototype.$keycloak = {} - reject(error) + resolve(Vue.prototype.$keycloak) + // reject(error) }) } else { // Keycloak has already been loaded, so just resolve the object. @@ -112,26 +152,36 @@ export default { */ return new Promise((resolve, reject) => { this.getInstance() - .then((instance) => { + .then(async (instance) => { + console.log("INSTANCE: ", instance.authenticated) if (instance.authenticated && ApiService.hasAuthHeader() && !instance.isTokenExpired(0)) { // We've already authenticated, have a header, and we've not expired. resolve(instance) } else { - // Attempt to retrieve a stored token, this may avoid us having to refresh the page. - const token = localStorage.getItem('token') - const refreshToken = localStorage.getItem('refreshToken') - const idToken = localStorage.getItem('idToken') - instance.init({ - pkceMethod: 'S256', - onLoad: 'check-sso', - checkLoginIframe: true, - timeSkew: 10, // Allow for some deviation - token, - refreshToken, - idToken, - } - ).then((result) => { + + // this.removeLocalToken() + // instance.clearToken() + // // We update the store reference only after wiring up the API. (Someone might be waiting + // // for login to complete before taking some action. ) + // store.commit('SET_KEYCLOAK', instance) + // resolve(instance) + + try { + console.log("before auth") + const authed = await instance.init({ + pkceMethod: 'S256', + onLoad: 'check-sso', + timeSkew: 10, + checkLoginIframe: false, + token, + refreshToken, + idToken, + }) + + console.log(`User is ${authed ? 'authenticated' : 'not authenticated'}`); + if (instance.authenticated) { + console.log("authenticated") // We may have been authenticated, but the token could be expired. instance.updateToken(60).then(() => { // Store the token to avoid future round trips, and wire up the API @@ -157,9 +207,45 @@ export default { store.commit('SET_KEYCLOAK', instance) resolve(instance) } - }).then((e) => { - reject(e) - }) + + } catch (error) { + console.error('Failed to initialize adapter:', error); + } + // Attempt to retrieve a stored token, this may avoid us having to refresh the page. + const token = localStorage.getItem('token') + const refreshToken = localStorage.getItem('refreshToken') + const idToken = localStorage.getItem('idToken') + + // ).then((result) => { + // if (instance.authenticated) { + // // We may have been authenticated, but the token could be expired. + // instance.updateToken(60).then(() => { + // // Store the token to avoid future round trips, and wire up the API + // this.setLocalToken(instance) + // // We update the store reference only after wiring up the API. (Someone might be waiting + // // for login to complete before taking some action. ) + // // Assumes that store passed in includes a 'SET_KEYCLOAK' mutation! + // store.commit('SET_KEYCLOAK', instance) + // this.scheduleRenewal(instance) + // resolve(instance) + // }).error(() => { + // // The refresh token is expired or was rejected + // this.removeLocalToken() + // instance.clearToken() + // // We update the store reference only after wiring up the API. (Someone might be waiting + // // for login to complete before taking some action. ) + // store.commit('SET_KEYCLOAK', instance) + // resolve(instance) + // }) + // } else { + // // We may have failed to authenticate, for many reasons, e.g. - it may be we never logged in, + // // or have an expired token. + // store.commit('SET_KEYCLOAK', instance) + // resolve(instance) + // } + // }).then((e) => { + // reject(e) + // }) } }) .catch((error) => { diff --git a/app/frontend/src/common/components/Auth.vue b/app/frontend/src/common/components/Auth.vue index 27bd9e809..507b7ddb9 100644 --- a/app/frontend/src/common/components/Auth.vue +++ b/app/frontend/src/common/components/Auth.vue @@ -37,9 +37,13 @@ export default { this.keyCloakLogin() } }, - async keyCloakLogin () { - this.keycloak.init().then(() => { + keyCloakLogin () { + this.keycloak.init({ + checkLoginIframe: false + }).then(() => { + console.log(".then after button click") this.keycloak.login({ idpHint: this.config.sso_idp_hint }).then((authenticated) => { + console.log("after login") if (authenticated) { ApiService.authHeader('JWT', this.keycloak.token) if (window.localStorage) { @@ -49,6 +53,7 @@ export default { } } }).catch((e) => { + console.error("keyCloakLogin: ", e) this.$store.commit(SET_ERROR, { error: 'Cannot contact SSO provider' }) }) }) diff --git a/app/frontend/src/common/store/auth.js b/app/frontend/src/common/store/auth.js index 0036055e4..78279cf7b 100644 --- a/app/frontend/src/common/store/auth.js +++ b/app/frontend/src/common/store/auth.js @@ -11,6 +11,7 @@ const auth = { }, getters: { keycloak (state) { + console.log("state keycloak", state.keycloak) return state.keycloak }, userRoles (state) { diff --git a/app/frontend/vue.config.js b/app/frontend/vue.config.js index a1c3b91cd..a47a1122a 100644 --- a/app/frontend/vue.config.js +++ b/app/frontend/vue.config.js @@ -8,11 +8,21 @@ module.exports = { publicPath: process.env.NODE_ENV === 'production' ? '/gwells/' : '/', configureWebpack: { resolve: { + extensions: ['.mjs', '.js', '.vue', '.json'], alias: { moment: 'moment/src/moment', lodash: 'lodash-es' } }, + module: { + rules: [ + { + test: /\.mjs$/, // Target .mjs files + include: /node_modules/, // Include node_modules (where Keycloak might reside) + type: 'javascript/auto' // Treat .mjs as a JavaScript module + } + ] + }, devServer: { watchOptions: { ignored: /node_modules/, From 7978c41fcff437fc9023e33658961f1215ce1690 Mon Sep 17 00:00:00 2001 From: "Fergus MacConnell WLRS:EX" Date: Fri, 20 Sep 2024 16:06:08 -0700 Subject: [PATCH 4/7] =?UTF-8?q?Clean=20up=20=F0=9F=99=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/frontend/src/common/authenticate.js | 95 ++------------------- app/frontend/src/common/components/Auth.vue | 2 - app/frontend/src/common/store/auth.js | 1 - app/frontend/vue.config.js | 10 --- 4 files changed, 7 insertions(+), 101 deletions(-) diff --git a/app/frontend/src/common/authenticate.js b/app/frontend/src/common/authenticate.js index 4e2b980e1..ae053cdca 100644 --- a/app/frontend/src/common/authenticate.js +++ b/app/frontend/src/common/authenticate.js @@ -21,22 +21,10 @@ export default { return new Promise((resolve, reject) => { if (!Vue.prototype.$keycloak) { - // try { - // Vue.prototype.$keycloak = new Keycloak() - - // return Vue.prototype.$keycloak; - - // } catch (error) { - // console.error("Keycloak Instance ERROR: ", error); - // } - // Keycloak has not yet been loaded, get Keycloak configuration from the server. ApiService.query('keycloak', {}) .then(response => { - console.log("Keycloak Config Data: ", response.data) - const { - url, 'ssl-required': sslRequired , resource, realm, @@ -45,23 +33,7 @@ export default { clientId, 'auth-server-url': authServerUrl } = response.data; - /* - "A best practice is to load the JavaScript adapter directly from Keycloak Server as it will - automatically be updated when you upgrade the server. If you copy the adapter to your web - application instead, make sure you upgrade the adapter only after you have upgraded the server."; - source : https://www.keycloak.org/docs/latest/securing_apps/index.html#_javascript_adapter: - */ - // const jsUrl = `${response.data['auth-server-url']}/js/keycloak.js` - // const jsUrl = "https://unpkg.com/keycloak-js@24.0.0/dist/keycloak.min.js" - // Inject the Keycloak javascript into the DOM. - // const keyCloakScript = document.createElement('script') - - // keyCloakScript.onload = () => { - // // Construct the Keycloak object and resolve the promise. - // Vue.prototype.$keycloak = new Keycloak(response.data) - // resolve(Vue.prototype.$keycloak) - // } Vue.prototype.$keycloak = new Keycloak({ url: authServerUrl, realm, @@ -74,23 +46,11 @@ export default { resolve(Vue.prototype.$keycloak) - // keyCloakScript.onerror = (e) => { - // // This is pretty bad - keycloak didn't load - this should never ever happen. - // // There's not much we can do, so we set keycloak to a random empty object and resolve. - // console.error(e) - // Vue.prototype.$keycloak = {} - // resolve(Vue.prototype.$keycloak) - // } - - // keyCloakScript.async = true - // keyCloakScript.setAttribute('src', jsUrl) - // document.head.appendChild(keyCloakScript) }) .catch(error => { console.error(error) Vue.prototype.$keycloak = {} resolve(Vue.prototype.$keycloak) - // reject(error) }) } else { // Keycloak has already been loaded, so just resolve the object. @@ -153,35 +113,28 @@ export default { return new Promise((resolve, reject) => { this.getInstance() .then(async (instance) => { - console.log("INSTANCE: ", instance.authenticated) if (instance.authenticated && ApiService.hasAuthHeader() && !instance.isTokenExpired(0)) { // We've already authenticated, have a header, and we've not expired. resolve(instance) } else { - // this.removeLocalToken() - // instance.clearToken() - // // We update the store reference only after wiring up the API. (Someone might be waiting - // // for login to complete before taking some action. ) - // store.commit('SET_KEYCLOAK', instance) - // resolve(instance) - try { - console.log("before auth") + // Attempt to retrieve a stored token, this may avoid us having to refresh the page. + const token = localStorage.getItem('token') + const refreshToken = localStorage.getItem('refreshToken') + const idToken = localStorage.getItem('idToken') + const authed = await instance.init({ pkceMethod: 'S256', onLoad: 'check-sso', timeSkew: 10, - checkLoginIframe: false, + checkLoginIframe: true, token, refreshToken, idToken, }) - console.log(`User is ${authed ? 'authenticated' : 'not authenticated'}`); - if (instance.authenticated) { - console.log("authenticated") // We may have been authenticated, but the token could be expired. instance.updateToken(60).then(() => { // Store the token to avoid future round trips, and wire up the API @@ -192,7 +145,7 @@ export default { store.commit('SET_KEYCLOAK', instance) this.scheduleRenewal(instance) resolve(instance) - }).error(() => { + }).catch(() => { // The refresh token is expired or was rejected this.removeLocalToken() instance.clearToken() @@ -211,41 +164,7 @@ export default { } catch (error) { console.error('Failed to initialize adapter:', error); } - // Attempt to retrieve a stored token, this may avoid us having to refresh the page. - const token = localStorage.getItem('token') - const refreshToken = localStorage.getItem('refreshToken') - const idToken = localStorage.getItem('idToken') - // ).then((result) => { - // if (instance.authenticated) { - // // We may have been authenticated, but the token could be expired. - // instance.updateToken(60).then(() => { - // // Store the token to avoid future round trips, and wire up the API - // this.setLocalToken(instance) - // // We update the store reference only after wiring up the API. (Someone might be waiting - // // for login to complete before taking some action. ) - // // Assumes that store passed in includes a 'SET_KEYCLOAK' mutation! - // store.commit('SET_KEYCLOAK', instance) - // this.scheduleRenewal(instance) - // resolve(instance) - // }).error(() => { - // // The refresh token is expired or was rejected - // this.removeLocalToken() - // instance.clearToken() - // // We update the store reference only after wiring up the API. (Someone might be waiting - // // for login to complete before taking some action. ) - // store.commit('SET_KEYCLOAK', instance) - // resolve(instance) - // }) - // } else { - // // We may have failed to authenticate, for many reasons, e.g. - it may be we never logged in, - // // or have an expired token. - // store.commit('SET_KEYCLOAK', instance) - // resolve(instance) - // } - // }).then((e) => { - // reject(e) - // }) } }) .catch((error) => { diff --git a/app/frontend/src/common/components/Auth.vue b/app/frontend/src/common/components/Auth.vue index 507b7ddb9..12b2a7c20 100644 --- a/app/frontend/src/common/components/Auth.vue +++ b/app/frontend/src/common/components/Auth.vue @@ -41,9 +41,7 @@ export default { this.keycloak.init({ checkLoginIframe: false }).then(() => { - console.log(".then after button click") this.keycloak.login({ idpHint: this.config.sso_idp_hint }).then((authenticated) => { - console.log("after login") if (authenticated) { ApiService.authHeader('JWT', this.keycloak.token) if (window.localStorage) { diff --git a/app/frontend/src/common/store/auth.js b/app/frontend/src/common/store/auth.js index 78279cf7b..0036055e4 100644 --- a/app/frontend/src/common/store/auth.js +++ b/app/frontend/src/common/store/auth.js @@ -11,7 +11,6 @@ const auth = { }, getters: { keycloak (state) { - console.log("state keycloak", state.keycloak) return state.keycloak }, userRoles (state) { diff --git a/app/frontend/vue.config.js b/app/frontend/vue.config.js index a47a1122a..a1c3b91cd 100644 --- a/app/frontend/vue.config.js +++ b/app/frontend/vue.config.js @@ -8,21 +8,11 @@ module.exports = { publicPath: process.env.NODE_ENV === 'production' ? '/gwells/' : '/', configureWebpack: { resolve: { - extensions: ['.mjs', '.js', '.vue', '.json'], alias: { moment: 'moment/src/moment', lodash: 'lodash-es' } }, - module: { - rules: [ - { - test: /\.mjs$/, // Target .mjs files - include: /node_modules/, // Include node_modules (where Keycloak might reside) - type: 'javascript/auto' // Treat .mjs as a JavaScript module - } - ] - }, devServer: { watchOptions: { ignored: /node_modules/, From d60d9cfaeb86eb4f8ddbd29b2bdc0ed5d2a4af03 Mon Sep 17 00:00:00 2001 From: "Fergus MacConnell WLRS:EX" Date: Fri, 20 Sep 2024 16:17:02 -0700 Subject: [PATCH 5/7] =?UTF-8?q?=F0=9F=99=83=20clean=20up.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/frontend/jest.config.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/frontend/jest.config.js b/app/frontend/jest.config.js index 422069dce..bf28a52f6 100644 --- a/app/frontend/jest.config.js +++ b/app/frontend/jest.config.js @@ -5,8 +5,7 @@ module.exports = { 'js', 'jsx', 'json', - 'vue', - '.mjs' + 'vue' ], transform: { '^.+\\.vue$': 'vue-jest', From 4c179a61e23cc8ede24bd0023a9ffe5aa1b15c95 Mon Sep 17 00:00:00 2001 From: "Fergus MacConnell WLRS:EX" Date: Fri, 20 Sep 2024 16:20:15 -0700 Subject: [PATCH 6/7] Remove whitespace. --- app/frontend/src/common/authenticate.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/frontend/src/common/authenticate.js b/app/frontend/src/common/authenticate.js index ae053cdca..e64eb4ef0 100644 --- a/app/frontend/src/common/authenticate.js +++ b/app/frontend/src/common/authenticate.js @@ -25,9 +25,9 @@ export default { .then(response => { const { - 'ssl-required': sslRequired , - resource, - realm, + 'ssl-required': sslRequired, + resource, + realm, 'public-client': publicClient, 'confidential-port': confidentialPort, clientId, From 5656cba6f30b0d9e9dc2ccb08a23049e40893d22 Mon Sep 17 00:00:00 2001 From: "Fergus MacConnell WLRS:EX" Date: Fri, 20 Sep 2024 16:52:45 -0700 Subject: [PATCH 7/7] =?UTF-8?q?Trigger=20PR=20=F0=9F=A4=B7.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/frontend/src/common/authenticate.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/frontend/src/common/authenticate.js b/app/frontend/src/common/authenticate.js index e64eb4ef0..954de609a 100644 --- a/app/frontend/src/common/authenticate.js +++ b/app/frontend/src/common/authenticate.js @@ -18,6 +18,7 @@ export default { /** * Returns a promise that resolves to an instance of Keycloak. */ + return new Promise((resolve, reject) => { if (!Vue.prototype.$keycloak) {