keycloak-aplcache

Merge pull request #384 from stianst/master Updated session

5/14/2014 1:54:58 PM

Details

diff --git a/docbook/reference/en/en-US/modules/javascript-adapter.xml b/docbook/reference/en/en-US/modules/javascript-adapter.xml
index 8e0a5c7..fa01819 100755
--- a/docbook/reference/en/en-US/modules/javascript-adapter.xml
+++ b/docbook/reference/en/en-US/modules/javascript-adapter.xml
@@ -50,7 +50,7 @@ var keycloak = Keycloak({
         required will redirect to the login form on the server, while check-sso will redirect to the auth server to check
         if the user is already logged in to the realm. For example:
 <programlisting><![CDATA[
-keycloak.init('login-required')
+keycloak.init({ onLoad: 'login-required' })
 ]]></programlisting>
     </para>
 
@@ -126,6 +126,17 @@ keycloak.updateToken(30).success(function() {
     </para>
 
     <section>
+        <title>Session status iframe</title>
+
+        <para>
+            By default the JavaScript adapter creates a non-visible iframe that is used to detect if a single-sign out has occured.
+            This does not require any network traffic, instead the status is retrieved from a special status cookie. This feature can be disabled
+            by setting <literal>checkLoginIframe: false</literal> in the options passed to the <literal>init</literal>
+            method.
+        </para>
+    </section>
+
+    <section>
         <title>JavaScript Adapter reference</title>
 
         <section>
@@ -156,20 +167,127 @@ new Keycloak({ url: 'http://localhost/auth', realm: 'myrealm', clientId: 'myApp'
         <section>
             <title>Methods</title>
 
-            <itemizedlist>
-                <listitem>init - called to initialize the adapter. Returns promise to set functions to be invoked on success or error</listitem>
-                <listitem>login(options) - redirects to login form on (options is an optional object with redirectUri and/or prompt fields)</listitem>
-                <listitem>createLoginUrl(options) - returns the url to login form on (options is an optional object with redirectUri and/or prompt fields)</listitem>
-                <listitem>logout(options) - redirects to logout (options is an optional object with redirectUri)</listitem>
-                <listitem>createLogoutUrl(options) - returns the url to logout (options is an optional object with redirectUri)</listitem>
-                <listitem>accountManagement - redirects to account management</listitem>
-                <listitem>createAccountUrl - returns the url to account management</listitem>
-                <listitem>hasRealmRole(role) - returns true if the token has the given realm role</listitem>
-                <listitem>hasResourceRole(role, resource) - returns true if the token has the given role for the resource (resource is optional, if not specified clientId is used)</listitem>
-                <listitem>loadUserProfile() - loads the users profile. Returns promise to set functions to be invoked on success or error</listitem>
-                <listitem>isTokenExpired(minValidity) - returns true if the token has less than minValidity seconds left before it expires (minValidity is optional, if not specified 0 is used)</listitem>
-                <listitem>updateToken(minValidity) - refreshes the token if the token expires within minValidity seconds (minValidity is optional, if not specified 0 is used). Returns promise to set functions to be invoked on success or error</listitem>
-            </itemizedlist>
+            <simplesect>
+                <title>init(options)</title>
+
+                <para>Called to initialize the adapter.</para>
+                <para>Options is an Object, where:
+                    <itemizedlist>
+                        <listitem>onLoad - specifies an action to do on load, can be either 'login-required' or 'check-sso'</listitem>
+                        <listitem>token - set an initial value for the token</listitem>
+                        <listitem>refreshToken - set an initial value for the refresh token</listitem>
+                        <listitem>checkLoginIframe - set to enable/disable monitoring login state (default is true)</listitem>
+                        <listitem>checkLoginIframeInterval - set the interval to check login state (default is 5 seconds)</listitem>
+                    </itemizedlist>
+                </para>
+                <para>Returns promise to set functions to be invoked on success or error.</para>
+            </simplesect>
+
+            <simplesect>
+                <title>login(options)</title>
+
+                <para>Redirects to login form on (options is an optional object with redirectUri and/or prompt fields)</para>
+                <para>Options is an Object, where:
+                    <itemizedlist>
+                        <listitem>redirectUri - specifies the uri to redirect to after login</listitem>
+                        <listitem>prompt - can be set to 'none' to check if the user is logged in already (if not logged in a login form is not displayed)</listitem>
+                    </itemizedlist>
+                </para>
+            </simplesect>
+            <simplesect>
+                <title>createLoginUrl(options)</title>
+
+                <para>Returns the url to login form on (options is an optional object with redirectUri and/or prompt fields)</para>
+                <para>Options is an Object, where:
+                    <itemizedlist>
+                        <listitem>redirectUri - specifies the uri to redirect to after login</listitem>
+                        <listitem>prompt - can be set to 'none' to check if the user is logged in already (if not logged in a login form is not displayed)</listitem>
+                    </itemizedlist>
+                </para>
+            </simplesect>
+
+            <simplesect>
+                <title>logout(options)</title>
+
+                <para>Redirects to logout</para>
+                <para>Options is an Object, where:
+                    <itemizedlist>
+                        <listitem>redirectUri - specifies the uri to redirect to after logout</listitem>
+                    </itemizedlist>
+                </para>
+            </simplesect>
+
+            <simplesect>
+                <title>createLogoutUrl(options)</title>
+
+                <para>Returns logout out</para>
+                <para>Options is an Object, where:
+                    <itemizedlist>
+                        <listitem>redirectUri - specifies the uri to redirect to after logout</listitem>
+                    </itemizedlist>
+                </para>
+            </simplesect>
+
+            <simplesect>
+                <title>accountManagement()</title>
+
+                <para>Redirects to account management</para>
+            </simplesect>
+
+            <simplesect>
+                <title>createAccountUrl()</title>
+
+                <para>Returns the url to account management</para>
+            </simplesect>
+
+            <simplesect>
+                <title>hasRealmRole(role)</title>
+
+                <para>Returns true if the token has the given realm role</para>
+            </simplesect>
+
+            <simplesect>
+                <title>hasResourceRole(role, resource)</title>
+
+                <para>Returns true if the token has the given role for the resource (resource is optional, if not specified clientId is used)</para>
+            </simplesect>
+
+            <simplesect>
+                <title>loadUserProfile()</title>
+
+                <para>Loads the users profile</para>
+
+                <para>Returns promise to set functions to be invoked on success or error.</para>
+            </simplesect>
+
+            <simplesect>
+                <title>isTokenExpired(minValidity)</title>
+
+                <para>Returns true if the token has less than minValidity seconds left before it expires (minValidity is optional, if not specified 0 is used)</para>
+            </simplesect>
+
+            <simplesect>
+                <title>updateToken(minValidity)</title>
+
+                <para>If the token expires within minValidity seconds (minValidity is optional, if not specified 0 is used) the token is refreshed.
+                    If the session status iframe is enabled, the session status is also checked.
+                </para>
+
+                <para>Returns promise to set functions that can be invoked if the token is still valid, or if the token is no longer valid. For example:</para>
+
+                <programlisting><![CDATA[
+keycloak.updateToken(5).success(function(refreshed) {
+        if (refreshed) {
+            alert('token was successfully refreshed');
+        } else {
+            alert('token is still valid');
+        }
+    }).error(function() {
+        alert('failed to refresh the token, or the session has expired');
+    });
+]]></programlisting>
+
+            </simplesect>
         </section>
 
         <section>
@@ -187,7 +305,7 @@ keycloak.onAuthSuccess = function() { alert('authenticated'); }
                 <listitem>onAuthError - called if there was an error during authentication</listitem>
                 <listitem>onAuthRefreshSuccess - called when the token is refreshed</listitem>
                 <listitem>onAuthRefreshError - called if there was an error while trying to refresh the token</listitem>
-                <listitem>onAuthLogout - called when the user is logged out (only relevant to Cordova)</listitem>
+                <listitem>onAuthLogout - called if the user is logged out (will only be called if the session status iframe is enabled, or in Cordova mode)</listitem>
             </itemizedlist>
         </section>
     </section>
diff --git a/examples/demo-template/customer-app-js/src/main/webapp/customers/view.html b/examples/demo-template/customer-app-js/src/main/webapp/customers/view.html
index ae7dcc5..9c0dcb6 100755
--- a/examples/demo-template/customer-app-js/src/main/webapp/customers/view.html
+++ b/examples/demo-template/customer-app-js/src/main/webapp/customers/view.html
@@ -73,32 +73,14 @@ User <b id="subject"></b> made this request.
     };
 
     var reloadData = function () {
-        keycloak.checkLoginIframe(
-                function() {
-                    keycloak.updateToken(10).success(loadData).error(loadFailure);
-                },
-                function() {
+        keycloak.updateToken(10)
+                .success(loadData)
+                .error(function() {
                     document.getElementById('customers').innerHTML = '<b>Failed to load data.  User is logged out.</b>';
-                    //window.location.reload();
-                }
-        );
+                });
     }
 
-    //
-    // NOTE!!!: keycloak.setupCheckLoginIframe and checkLoginIframe are an optional way
-    // of discovering if you are logged out.  This approach may not always be feasible, but it is a lightweight
-    // approach that requires no network call to determine if the user is logged in.  The downside is that it
-    // requires a callback.
-    //
-    // Alternatively, on a single log out, the access token will eventually time out, then refresh token will fail
-    // as the session will be logged out on the auth server.
-    //
-    //
-
-    keycloak.init('login-required').success(function() {
-        // Use an iframe to detect if the user has done an SSO logout in a different window.
-        keycloak.setupCheckLoginIframe(document, window);
-    });
+    keycloak.init({ onLoad: 'login-required' }).success(reloadData);
 
 </script>
 
diff --git a/examples/js-console/example-realm.json b/examples/js-console/example-realm.json
index 537d120..7cb5485 100755
--- a/examples/js-console/example-realm.json
+++ b/examples/js-console/example-realm.json
@@ -48,9 +48,12 @@
             "name": "js-console",
             "enabled": true,
             "publicClient": true,
-            "baseUrl": "http://localhost:8080/js-console",
+            "baseUrl": "http://localhost/js-console",
             "redirectUris": [
-                "http://localhost:8080/js-console/*"
+                "http://localhost/js-console/*"
+            ],
+            "webOrigins": [
+                "http://localhost"
             ]
         }
     ],
diff --git a/examples/js-console/src/main/webapp/index.html b/examples/js-console/src/main/webapp/index.html
index 3b9e4a0..499c928 100644
--- a/examples/js-console/src/main/webapp/index.html
+++ b/examples/js-console/src/main/webapp/index.html
@@ -1,6 +1,6 @@
 <html>
 <head>
-    <script src="/auth/js/keycloak.js"></script>
+    <script src="//localhost:8081/auth/js/keycloak.js"></script>
 </head>
 <body>
 
@@ -86,6 +86,10 @@
         event('Auth Refresh Error');
     };
 
+    keycloak.onAuthLogout = function () {
+        event('Auth Logout');
+    };
+
     keycloak.init().success(function(authenticated) {
         output('Init Success (' + (authenticated ? 'Authenticated' : 'Not Authenticated') + ')');
     }).error(function() {
diff --git a/examples/js-console/src/main/webapp/keycloak.json b/examples/js-console/src/main/webapp/keycloak.json
index f166f87..66434b9 100644
--- a/examples/js-console/src/main/webapp/keycloak.json
+++ b/examples/js-console/src/main/webapp/keycloak.json
@@ -1,7 +1,7 @@
 {
   "realm" : "example",
   "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
-  "auth-server-url" : "http://localhost:8080/auth",
+  "auth-server-url" : "http://localhost:8081/auth",
   "ssl-not-required" : true,
   "resource" : "js-console",
   "public-client" : true
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js
index 33c2048..74d3a5e 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js
@@ -9,9 +9,6 @@ var logoutUrl = consoleBaseUrl + "/logout";
 var auth = {};
 var logout = function(){
     console.log('*** LOGOUT');
-    auth.loggedIn = false;
-    auth.authz = null;
-    auth.user = null;
     window.location = logoutUrl;
 };
 
@@ -28,7 +25,11 @@ angular.element(document).ready(function ($http) {
     var keycloakAuth = new Keycloak(configUrl);
     auth.loggedIn = false;
 
-    keycloakAuth.init('login-required').success(function () {
+    keycloakAuth.onAuthLogout = function() {
+        location.reload();
+    }
+
+    keycloakAuth.init({ onLoad: 'login-required' }).success(function () {
         auth.loggedIn = true;
         auth.authz = keycloakAuth;
         module.factory('Auth', function() {
@@ -36,9 +37,8 @@ angular.element(document).ready(function ($http) {
         });
         angular.bootstrap(document, ["keycloak"]);
     }).error(function () {
-            window.location.reload();
-        });
-
+        window.location.reload();
+    });
 });
 
 module.factory('authInterceptor', function($q, Auth) {
@@ -52,8 +52,8 @@ module.factory('authInterceptor', function($q, Auth) {
 
                     deferred.resolve(config);
                 }).error(function() {
-                        deferred.reject('Failed to refresh token');
-                    });
+                    location.reload();
+                });
             }
             return deferred.promise;
         }
diff --git a/integration/js/src/main/resources/keycloak.js b/integration/js/src/main/resources/keycloak.js
index 6c73ad5..046c9c1 100755
--- a/integration/js/src/main/resources/keycloak.js
+++ b/integration/js/src/main/resources/keycloak.js
@@ -6,11 +6,13 @@ var Keycloak = function (config) {
     var kc = this;
     var adapter;
 
-    kc.callbackMap = new Object();
+    var loginIframe = {
+        enable: true,
+        callbackMap: [],
+        interval: 5
+    };
 
-
-
-    kc.init = function (init) {
+    kc.init = function (initOptions) {
         kc.authenticated = false;
 
         if (window.Cordova) {
@@ -19,6 +21,16 @@ var Keycloak = function (config) {
             adapter = loadAdapter();
         }
 
+        if (initOptions) {
+            if (typeof initOptions.checkLoginIframe !== 'undefined') {
+                loginIframe.enable = initOptions.checkLoginIframe;
+            }
+
+            if (initOptions.checkLoginIframeInterval) {
+                loginIframe.interval = initOptions.checkLoginIframeInterval;
+            }
+        }
+
         var promise = createPromise();
 
         var initPromise = createPromise();
@@ -33,37 +45,31 @@ var Keycloak = function (config) {
 
         function processInit() {
             var callback = parseCallback(window.location.href);
+
             if (callback) {
                 window.history.replaceState({}, null, location.protocol + '//' + location.host + location.pathname + (callback.fragment ? '#' + callback.fragment : ''));
                 processCallback(callback, initPromise);
                 return;
-            } else if (init) {
-                if (init.code || init.error) {
-                    processCallback(init, initPromise);
-                    return;
-                } else if (init.token || init.refreshToken) {
-                    setToken(init.token, init.refreshToken);
-                    initPromise.setSuccess();
-                } else if (init == 'login-required') {
-                    var p = kc.login();
-                    if (p) {
-                        p.success(function() {
-                            initPromise.setSuccess();
-                        }).error(function() {
-                            initPromise.setError();
-                        });
-                    };
-                } else if (init == 'check-sso') {
-                    var p = kc.login({ prompt: 'none' });
-                    if (p) {
-                        p.success(function() {
-                            initPromise.setSuccess();
-                        }).error(function() {
-                            initPromise.setSuccess();
-                        });
-                    };
-                } else {
-                    throw 'invalid init: ' + init;
+            } else if (initOptions) {
+                if (initOptions.token || initOptions.refreshToken) {
+                    setToken(initOptions.token, initOptions.refreshToken);
+                } else if (initOptions.onLoad) {
+                    if (initOptions.onLoad == 'check-sso' || initOptions.onLoad == 'login-required') {
+                        var options = {};
+                        if (initOptions.onLoad == 'check-sso') {
+                            options.prompt = 'none';
+                        }
+                        var p = kc.login(options);
+                        if (p) {
+                            p.success(function() {
+                                initPromise.setSuccess();
+                            }).error(function() {
+                                initPromise.setError();
+                            });
+                        };
+                    } else {
+                        throw 'Invalid value for onLoad';
+                    }
                 }
             } else {
                 initPromise.setSuccess();
@@ -82,59 +88,6 @@ var Keycloak = function (config) {
         return adapter.login(options);
     }
 
-    kc.setupCheckLoginIframe = function(doc, win) {
-        kc.iframe = doc.createElement('iframe');
-        var src = getRealmUrl() + "/login-status-iframe.html?client_id=" + encodeURIComponent(kc.clientId);
-        console.log('iframe src='+ src);
-        kc.iframe.setAttribute('src', src );
-        kc.iframe.style.display = "none";
-        doc.body.appendChild(kc.iframe);
-        if (!kc.iframe.contentWindow.location.origin) {
-            kc.iframe.contentWindow.location.origin = kc.iframe.contentWindow.location.protocol + "//" + kc.iframe.contentWindow.location.host;
-        }
-
-        var messageCallback = function(event) {
-            if (event.origin !== kc.iframe.contentWindow.location.origin) {
-                return;
-            }
-            var data = event.data;
-            var success = kc.callbackMap[data.successId];
-            var failure = kc.callbackMap[data.failureId];
-            delete kc.callbackMap[data.successId];
-            delete kc.callbackMap[data.failureId];
-            if (kc.sessionId != data.session) {
-                console.log("session doesn't match received session: " + event.data.session);
-                console.log("forcing loggedIn to be false");
-                failure();
-            }
-            if (data.loggedIn) {
-                success();
-            } else {
-                failure();
-            }
-        };
-        win.addEventListener("message", messageCallback, false);
-    }
-
-    kc.checkLoginIframe = function(success, failure) {
-
-        var msg = {};
-        if (!success) {
-            throw "You must define a success method";
-        }
-        if (!failure) {
-            throw "You must define a failure method";
-        }
-        msg.successId = createCallbackId();
-        msg.failureId = createCallbackId();
-        kc.callbackMap[msg.successId] = success;
-        kc.callbackMap[msg.failureId] = failure;
-        var origin = kc.iframe.contentWindow.location.origin;
-        console.log('*** origin: ' + origin);
-        var iframe = kc.iframe;
-        iframe.contentWindow.postMessage(msg, origin);
-    }
-
     kc.createLoginUrl = function(options) {
         var state = createUUID();
 
@@ -241,47 +194,64 @@ var Keycloak = function (config) {
     }
 
     kc.updateToken = function(minValidity) {
+        var promise = createPromise();
+
         if (!kc.tokenParsed || !kc.refreshToken) {
-            throw 'Not authenticated';
+            promise.setError();
+            return promise.promise;
         }
 
-        var promise = createPromise();
+        minValidity = minValidity || 5;
 
-        if (minValidity) {
+        var exec = function() {
             if (!kc.isTokenExpired(minValidity)) {
                 promise.setSuccess(false);
-                return promise.promise;
-            }
-        }
+            } else {
+                var params = 'grant_type=refresh_token&' + 'refresh_token=' + kc.refreshToken;
+                var url = getRealmUrl() + '/tokens/refresh';
 
-        var params = 'grant_type=refresh_token&' + 'refresh_token=' + kc.refreshToken;
-        var url = getRealmUrl() + '/tokens/refresh';
+                var req = new XMLHttpRequest();
+                req.open('POST', url, true);
+                req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
 
-        var req = new XMLHttpRequest();
-        req.open('POST', url, true);
-        req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
+                if (kc.clientId && kc.clientSecret) {
+                    req.setRequestHeader('Authorization', 'Basic ' + btoa(kc.clientId + ':' + kc.clientSecret));
+                } else {
+                    params += '&client_id=' + encodeURIComponent(kc.clientId);
+                }
 
-        if (kc.clientId && kc.clientSecret) {
-            req.setRequestHeader('Authorization', 'Basic ' + btoa(kc.clientId + ':' + kc.clientSecret));
-        } else {
-            params += '&client_id=' + encodeURIComponent(kc.clientId);
+                req.onreadystatechange = function() {
+                    if (req.readyState == 4) {
+                        if (req.status == 200) {
+                            var tokenResponse = JSON.parse(req.responseText);
+                            setToken(tokenResponse['access_token'], tokenResponse['refresh_token']);
+                            kc.onAuthRefreshSuccess && kc.onAuthRefreshSuccess();
+                            promise.setSuccess(true);
+                        } else {
+                            kc.onAuthRefreshError && kc.onAuthRefreshError();
+                            promise.setError();
+                        }
+                    }
+                };
+
+                req.send(params);
+            }
         }
 
-        req.onreadystatechange = function() {
-            if (req.readyState == 4) {
-                if (req.status == 200) {
-                    var tokenResponse = JSON.parse(req.responseText);
-                    setToken(tokenResponse['access_token'], tokenResponse['refresh_token']);
-                    kc.onAuthRefreshSuccess && kc.onAuthRefreshSuccess();
-                    promise.setSuccess(true);
-                } else {
-                    kc.onAuthRefreshError && kc.onAuthRefreshError();
+        if (loginIframe.enable) {
+            if (loginIframe.enable) {
+                var iframePromise = checkLoginIframe();
+                iframePromise.success(function() {
+                    exec();
+                }).error(function() {
                     promise.setError();
-                }
+                })
+            } else {
+                promise.setSuccess(false);
             }
-        };
-
-        req.send(params);
+        } else {
+            exec();
+        }
 
         return promise.promise;
     }
@@ -398,11 +368,17 @@ var Keycloak = function (config) {
     }
 
     function clearToken() {
-        setToken(null, null);
-        kc.onAuthLogout && kc.onAuthLogout();
+        if (kc.token) {
+            setToken(null, null);
+            kc.onAuthLogout && kc.onAuthLogout();
+        }
     }
 
     function setToken(token, refreshToken) {
+        if (token || refreshToken) {
+            setupCheckLoginIframe();
+        }
+
         if (token) {
             kc.token = token;
             kc.tokenParsed = JSON.parse(decodeURIComponent(escape(window.atob( token.split('.')[1] ))));
@@ -539,6 +515,68 @@ var Keycloak = function (config) {
         return p;
     }
 
+    function setupCheckLoginIframe() {
+        if (!loginIframe.enable || loginIframe.iframe) {
+            return;
+        }
+
+        var iframe = document.createElement('iframe');
+
+        iframe.onload = function() {
+            var realmUrl = getRealmUrl();
+            loginIframe.iframeOrigin = realmUrl.substring(0, realmUrl.indexOf('/', 8));
+            loginIframe.iframe = iframe;
+        }
+
+        var src = getRealmUrl() + '/login-status-iframe.html?client_id=' + encodeURIComponent(kc.clientId);
+        console.log('iframe src='+ src);
+        iframe.setAttribute('src', src );
+        iframe.style.display = 'none';
+        document.body.appendChild(iframe);
+
+        var messageCallback = function(event) {
+            if (event.origin !== loginIframe.iframeOrigin) {
+                return;
+            }
+            var data = event.data;
+            var promise = loginIframe.callbackMap[data.callbackId];
+            delete loginIframe.callbackMap[data.callbackId];
+            if (kc.sessionId == data.session && data.loggedIn) {
+                promise.setSuccess();
+            } else {
+                clearToken();
+                promise.setError();
+            }
+        };
+        window.addEventListener('message', messageCallback, false);
+
+        var check = function() {
+            checkLoginIframe();
+            if (kc.token) {
+                setTimeout(check, loginIframe.interval * 1000);
+            }
+        };
+
+        setTimeout(check, loginIframe.interval * 1000);
+    }
+
+    function checkLoginIframe() {
+        var promise = createPromise();
+
+        if (loginIframe.iframe || loginIframe.iframeOrigin) {
+            var msg = {};
+            msg.callbackId = createCallbackId();
+            loginIframe.callbackMap[msg.callbackId] = promise;
+            var origin = loginIframe.iframeOrigin;
+            console.log('*** origin: ' + origin);
+            loginIframe.iframe.contentWindow.postMessage(msg, origin);
+        } else {
+            promise.setSuccess();
+        }
+
+        return promise.promise;
+    }
+
     function loadAdapter(type) {
         if (!type || type == 'default') {
             return {
diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java
index 35c70f6..980ce58 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -278,6 +278,10 @@ public class AccountService {
     @POST
     @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
     public Response processAccountUpdate(final MultivaluedMap<String, String> formData) {
+        if (auth == null) {
+            return login(null);
+        }
+
         require(AccountRoles.MANAGE_ACCOUNT);
 
         UserModel user = auth.getUser();
@@ -309,6 +313,10 @@ public class AccountService {
     @Path("totp-remove")
     @GET
     public Response processTotpRemove() {
+        if (auth == null) {
+            return login("totp");
+        }
+
         require(AccountRoles.MANAGE_ACCOUNT);
 
         UserModel user = auth.getUser();
@@ -323,6 +331,10 @@ public class AccountService {
     @Path("sessions-logout")
     @GET
     public Response processSessionsLogout() {
+        if (auth == null) {
+            return login("sessions");
+        }
+
         require(AccountRoles.MANAGE_ACCOUNT);
 
         UserModel user = auth.getUser();
@@ -335,6 +347,10 @@ public class AccountService {
     @POST
     @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
     public Response processTotpUpdate(final MultivaluedMap<String, String> formData) {
+        if (auth == null) {
+            return login("totp");
+        }
+
         require(AccountRoles.MANAGE_ACCOUNT);
 
         UserModel user = auth.getUser();
@@ -364,6 +380,10 @@ public class AccountService {
     @POST
     @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
     public Response processPasswordUpdate(final MultivaluedMap<String, String> formData) {
+        if (auth == null) {
+            return login("password");
+        }
+
         require(AccountRoles.MANAGE_ACCOUNT);
 
         UserModel user = auth.getUser();
@@ -403,6 +423,10 @@ public class AccountService {
     @GET
     public Response processSocialUpdate(@QueryParam("action") String action,
                                         @QueryParam("provider_id") String providerId) {
+        if (auth == null) {
+            return login("social");
+        }
+
         require(AccountRoles.MANAGE_ACCOUNT);
         UserModel user = auth.getUser();