Skip to content
Snippets Groups Projects
Commit 86d93a15 authored by Reiter, Christoph's avatar Reiter, Christoph :snake:
Browse files

Support redirect-less login in case the user is already logged in.

The newest keycloak library supporst a mode for doing the redirect in an iframe
with a separate page that needs to be whitelisted in keycloak.

The newer version also finally fixes the native promise bugs so we might as well
use the one from npm instead of fetching it from the keycloak server at runtime.
parent 83a803c4
No related branches found
No related tags found
No related merge requests found
<html>
<body>
<script>
parent.postMessage(location.href, location.origin)
</script>
</body>
</html>
\ No newline at end of file
......@@ -28,6 +28,7 @@
"vpu-common": "file:./vendor/common"
},
"dependencies": {
"keycloak-js": "^8.0.0",
"lit-element": "^2.1.0"
},
"scripts": {
......
......@@ -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'},
]
......
/**
* 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,
}));
});
}
}
......
......@@ -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>
`;
......
......@@ -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);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment