diff --git a/packages/auth/src/auth-keycloak.js b/packages/auth/src/auth-keycloak.js
index 9717dbf84dc3e34b4e55420bccc64b3ecffa14b6..45b395c09b38e23607f31cbaa2a6df0b8ed68c03 100644
--- a/packages/auth/src/auth-keycloak.js
+++ b/packages/auth/src/auth-keycloak.js
@@ -229,6 +229,7 @@ export class AuthKeycloak extends LitElement {
     }
 
     disconnectedCallback() {
+        this._kcwrapper.close();
         this._kcwrapper.removeEventListener('changed', this._onKCChanged);
         this._bus.close();
 
diff --git a/packages/auth/src/keycloak.js b/packages/auth/src/keycloak.js
index f0e2a2e35db79ec99d25c3deb2d92725976e1974..42db6858c48cede6a0169b3bb52137f39d0e90b3 100644
--- a/packages/auth/src/keycloak.js
+++ b/packages/auth/src/keycloak.js
@@ -55,6 +55,15 @@ const ensureURL = function(urlOrPath) {
  */
 export class KeycloakWrapper extends EventTarget {
 
+    /* Minimum validity of the token in seconds */
+    MIN_VALIDITY = 20;
+
+    /* Interval at which the token validity is checked, in seconds */
+    CHECK_INTERVAL = 10;
+
+    /* Enables extra debug logging */
+    DEBUG = false;
+
     constructor(baseURL, realm, clientId, silentCheckSsoUri, idpHint) {
         super();
 
@@ -65,6 +74,24 @@ export class KeycloakWrapper extends EventTarget {
         this._initDone = false;
         this._silentCheckSsoUri = silentCheckSsoUri;
         this._idpHint = idpHint;
+        this._checkId = null;
+
+        this._onVisibilityChanged = this._onVisibilityChanged.bind(this);
+        document.addEventListener("visibilitychange", this._onVisibilityChanged);
+    }
+
+    /**
+     * This needs to be called or the instance will leak;
+     */
+    close() {
+        document.removeEventListener("visibilitychange", this._onVisibilityChanged);
+    }
+
+    _onVisibilityChanged() {
+        let isVisible = (document.visibilityState === 'visible');
+        if (isVisible && this._keycloak.authenticated) {
+            this._checkTokeHasExpired();
+        }
     }
 
     _onChanged() {
@@ -97,6 +124,44 @@ export class KeycloakWrapper extends EventTarget {
         console.assert(refreshed, "token should have been refreshed");
     }
 
+    async _checkTokeHasExpired() {
+        let refreshed;
+
+        let minValidity = this.MIN_VALIDITY + this.CHECK_INTERVAL;
+        if (this.DEBUG) {
+            console.log(`Updating token if not valid for at least ${minValidity}s`);
+        }
+        try {
+            refreshed = await this._keycloak.updateToken(minValidity);
+        } catch (error) {
+            console.log('Failed to refresh the token', error);
+        }
+
+        if (this.DEBUG && refreshed)
+            console.log("token has been refreshed");
+    }
+
+    async _onAuthSuccess() {
+        // We check every once in a while if the token is still valid and
+        // and refresh it if needed.
+        if (this._checkId !== null) {
+            clearInterval(this._checkId);
+            this._checkId = null;
+        }
+        this._checkId = setInterval(this._checkTokeHasExpired.bind(this), this.CHECK_INTERVAL * 1000);
+
+        this._onChanged();
+    }
+
+    async _onAuthLogout() {
+        if (this._checkId !== null) {
+            clearInterval(this._checkId);
+            this._checkId = null;
+        }
+
+        this._onChanged();
+    }
+
     async _ensureInstance() {
         if (this._keycloak !== null)
             return;
@@ -112,8 +177,8 @@ export class KeycloakWrapper extends EventTarget {
         this._keycloak.onTokenExpired = this._onTokenExpired.bind(this);
         this._keycloak.onAuthRefreshSuccess = this._onChanged.bind(this);
         this._keycloak.onAuthRefreshError = this._onChanged.bind(this);
-        this._keycloak.onAuthLogout = this._onChanged.bind(this);
-        this._keycloak.onAuthSuccess = this._onChanged.bind(this);
+        this._keycloak.onAuthLogout = this._onAuthLogout.bind(this);
+        this._keycloak.onAuthSuccess = this._onAuthSuccess.bind(this);
         this._keycloak.onAuthError = this._onChanged.bind(this);
         this._keycloak.onReady = this._onReady.bind(this);
     }
@@ -139,6 +204,9 @@ export class KeycloakWrapper extends EventTarget {
             pkceMethod: 'S256',
         };
 
+        if (this.DEBUG) {
+            options['enableLogging'] = true;
+        }
 
         if (this._silentCheckSsoUri) {