diff --git a/packages/auth/assets/silent-check-sso.html b/packages/auth/assets/silent-check-sso.html new file mode 100644 index 0000000000000000000000000000000000000000..94fe2268cbd7ff3f752f2093bb3cddd6d10b6388 --- /dev/null +++ b/packages/auth/assets/silent-check-sso.html @@ -0,0 +1,7 @@ +<html> +<body> + <script> + parent.postMessage(location.href, location.origin) + </script> +</body> +</html> \ No newline at end of file diff --git a/packages/auth/package.json b/packages/auth/package.json index b0714cceb589e36e44c6980fbb522740062ce0e2..7ce234678f0abf5645071cdd354259b4f2212b6a 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -28,6 +28,7 @@ "vpu-common": "file:./vendor/common" }, "dependencies": { + "keycloak-js": "^8.0.0", "lit-element": "^2.1.0" }, "scripts": { diff --git a/packages/auth/rollup.config.js b/packages/auth/rollup.config.js index 37a3abb0c6fe23f9eb1493e790bbaa6285549ef6..549369f1059c07c88259ff3ac437e006d8724ed3 100644 --- a/packages/auth/rollup.config.js +++ b/packages/auth/rollup.config.js @@ -22,6 +22,13 @@ export default { format: 'esm', sourcemap: true }, + onwarn: function (warning, warn) { + // keycloak bundled code uses eval + if (warning.code === 'EVAL') { + return; + } + warn(warning); + }, plugins: [ del({ targets: 'dist/*' @@ -43,6 +50,7 @@ export default { copy({ targets: [ {src: 'assets/index.html', dest:'dist'}, + {src: 'assets/silent-check-sso.html', dest:'dist'}, {src: 'assets/favicon.ico', dest:'dist'}, {src: 'node_modules/vpu-common/assets/icons/*.svg', dest: 'dist/local/vpu-common/icons'}, ] diff --git a/packages/auth/src/keycloak.js b/packages/auth/src/keycloak.js index 616c4ed8dcd3320677cea47fe6c456a635649ab9..98bc0cf999c3436175443b1b39b525115932f94c 100644 --- a/packages/auth/src/keycloak.js +++ b/packages/auth/src/keycloak.js @@ -1,29 +1,3 @@ -/** - * Imports the keycloak JS API as if it was a module. - * - * @param baseUrl {string} - */ -async function importKeycloak(baseUrl) { - const keycloakSrc = baseUrl + '/js/keycloak.js'; - // Importing will write it to window so we take it from there - await import(keycloakSrc); - if (importKeycloak._keycloakMod !== undefined) - return importKeycloak._keycloakMod; - importKeycloak._keycloakMod = {Keycloak: window.Keycloak}; - delete window.Keycloak; - return importKeycloak._keycloakMod; -} - - -async function kcMakeAsync(promise) { - // the native keycloak promise implementation is broken, wrap it instead - // https://stackoverflow.com/questions/58436689/react-keycloak-typeerror-kc-updatetoken-success-is-not-a-function - return new Promise(function(resolve, reject) { - promise.success((...args) => { resolve(...args); }).error((...args) => { reject(...args); }); - }); -} - - /** * Wraps the keycloak API to support async/await, adds auto token refreshing and consolidates all * events into one native "changed" event @@ -32,7 +6,7 @@ async function kcMakeAsync(promise) { */ export class KeycloakWrapper extends EventTarget { - constructor(baseURL, realm, clientId) { + constructor(baseURL, realm, clientId, silentCheckSsoUri) { super(); this._baseURL = baseURL; @@ -40,6 +14,7 @@ export class KeycloakWrapper extends EventTarget { this._clientId = clientId; this._keycloak = null; this._initDone = false; + this._silentCheckSsoUri = silentCheckSsoUri; } _onChanged() { @@ -62,7 +37,7 @@ export class KeycloakWrapper extends EventTarget { let refreshed = false; try { - refreshed = await kcMakeAsync(this._keycloak.updateToken(5)); + refreshed = await this._keycloak.updateToken(5); } catch (error) { console.log('Failed to refresh the token', error); return; @@ -79,9 +54,9 @@ export class KeycloakWrapper extends EventTarget { if (this._keycloak !== null) return; - const module = await importKeycloak(this._baseURL); + const Keycloak = await import('keycloak-js').then((mod) => { return mod.default; }); - this._keycloak = module.Keycloak({ + this._keycloak = Keycloak({ url: this._baseURL, realm: this._realm, clientId: this._clientId, @@ -101,7 +76,14 @@ export class KeycloakWrapper extends EventTarget { if (this._initDone) return; this._initDone = true; - await kcMakeAsync(this._keycloak.init()); + + const options = {promiseType: 'native'}; + if (this._silentCheckSsoUri) { + options['onLoad'] = 'check-sso'; + options['silentCheckSsoRedirectUri'] = this._silentCheckSsoUri; + } + + await this._keycloak.init(options); } /** @@ -119,9 +101,9 @@ export class KeycloakWrapper extends EventTarget { const language = options['lang'] || 'en'; if (!this._keycloak.authenticated) { - await kcMakeAsync(this._keycloak.login({ + await this._keycloak.login({ kcLocale: language, - })); + }); } } diff --git a/packages/auth/src/vpu-auth-demo.js b/packages/auth/src/vpu-auth-demo.js index a4ed4482e436590f2f0a9551f99e30f617e2197a..d6e59e6b8ffe4994d1dd35f15efab926120796ae 100644 --- a/packages/auth/src/vpu-auth-demo.js +++ b/packages/auth/src/vpu-auth-demo.js @@ -27,6 +27,7 @@ class AuthDemo extends LitElement { render() { commonUtils.initAssetBaseURL('vpu-auth-src'); + const silentCheckSsoUri = commonUtils.getAssetURL('silent-check-sso.html'); return html` <style> /* from BULMA.CSS */ @@ -49,7 +50,7 @@ class AuthDemo extends LitElement { <h1 class="title">Auth-Demo</h1> </div> <div class="container"> - <vpu-auth lang="${this.lang}" client-id="${commonUtils.setting('keyCloakClientId')}" load-person remember-login></vpu-auth> + <vpu-auth lang="${this.lang}" client-id="${commonUtils.setting('keyCloakClientId')}" silent-check-sso-uri="${silentCheckSsoUri}" load-person remember-login></vpu-auth> </div> </section> `; diff --git a/packages/auth/src/vpu-auth.js b/packages/auth/src/vpu-auth.js index e0e099f9f3664b1445b7a4010080e0ee416f5150..6479ab574a3458a380729f299c5e7b6ecd2b6b04 100644 --- a/packages/auth/src/vpu-auth.js +++ b/packages/auth/src/vpu-auth.js @@ -146,6 +146,7 @@ class VPUAuth extends VPULitElement { rememberLogin: { type: Boolean, attribute: 'remember-login' }, loadPerson: { type: Boolean, attribute: 'load-person' }, clientId: { type: String, attribute: 'client-id' }, + silentCheckSsoUri: { type: String, attribute: 'silent-check-sso-uri' }, name: { type: String, attribute: false }, token: { type: String, attribute: false }, subject: { type: String, attribute: false }, @@ -160,7 +161,7 @@ class VPUAuth extends VPULitElement { const baseURL = commonUtils.setting('keyCloakBaseURL'); const realm = commonUtils.setting('keyCloakRealm'); - this._kcwrapper = new KeycloakWrapper(baseURL, realm, this.clientId); + this._kcwrapper = new KeycloakWrapper(baseURL, realm, this.clientId, this.silentCheckSsoUri); this._kcwrapper.addEventListener('changed', this._onKCChanged);