keycloak-uncached

Details

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 445876f..ae7dcc5 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,11 +73,32 @@ User <b id="subject"></b> made this request.
     };
 
     var reloadData = function () {
-        keycloak.updateToken(10).success(loadData).error(loadFailure);
+        keycloak.checkLoginIframe(
+                function() {
+                    keycloak.updateToken(10).success(loadData).error(loadFailure);
+                },
+                function() {
+                    document.getElementById('customers').innerHTML = '<b>Failed to load data.  User is logged out.</b>';
+                    //window.location.reload();
+                }
+        );
     }
 
-    keycloak.onAuthSuccess = loadData;
-    keycloak.init('login-required');
+    //
+    // 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);
+    });
 
 </script>
 
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/index.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/index.html
index 742fd5e..2384e1b 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/index.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/index.html
@@ -1,100 +1,100 @@
-<!doctype html>
-<html lang="en">
-
-<head>
-    <meta charset="utf-8">
-    <title>Keycloak Admin Console</title>
-
-    <link rel="icon" href="img/favicon.ico">
-
-    <link rel="stylesheet" href="css/styles.css">
-
-    <script src="lib/jquery/jquery-1.10.2.js" type="text/javascript"></script>
-    <script src="lib/select2-3.4.1/select2.js" type="text/javascript"></script>
-
-    <script src="lib/angular/angular.js"></script>
-    <script src="lib/angular/angular-resource.js"></script>
-    <script src="lib/angular/angular-route.js"></script>
-    <script src="lib/angular/ui-bootstrap-tpls-0.4.0.js"></script>
-
-    <script src="lib/jquery/jquery.idletimer.js" type="text/javascript"></script>
-    <script src="lib/jquery/jquery.idletimeout.js" type="text/javascript"></script>
-    <script src="lib/angular/select2.js" type="text/javascript"></script>
-    <script src="lib/fileupload/angular-file-upload.min.js"></script>
-
-    <script src="js/keycloak.js" type="text/javascript"></script>
-    <script src="js/app.js" type="text/javascript"></script>
-    <script src="js/controllers/realm.js" type="text/javascript"></script>
-    <script src="js/controllers/applications.js" type="text/javascript"></script>
-    <script src="js/controllers/oauth-clients.js" type="text/javascript"></script>
-    <script src="js/controllers/users.js" type="text/javascript"></script>
-    <script src="js/loaders.js" type="text/javascript"></script>
-    <script src="js/services.js" type="text/javascript"></script>
-
-    <style>
-        [ng\:cloak], [ng-cloak], .ng-cloak {
-            display: none !important;
-        }
-    </style>
-</head>
-
-<body class="admin-console" data-ng-controller="GlobalCtrl" data-ng-cloak>
-<div id="idletimeout">
-    You will be logged off in <strong><span></span> seconds</strong> due to inactivity.
-    <a id="idletimeout-resume" href="#">Click here to continue using this web page</a>.
-</div>
-
-<div class="feedback-aligner" data-ng-show="notification" data-ng-click="notification = null">
-    <div class="alert alert-{{notification.type}}">
-        <span class="pficon pficon-ok" ng-show="notification.type == 'success'"></span>
-        <span class="pficon pficon-info" ng-show="notification.type == 'info'"></span>
-        <span class="pficon-layered" ng-show="notification.type == 'danger'">
-            <span class="pficon pficon-error-octagon"></span>
-            <span class="pficon pficon-error-exclamation"></span>
-        </span>
-        <span class="pficon-layered" ng-show="notification.type == 'warning'">
-            <span class="pficon pficon-warning-triangle"></span>
-            <span class="pficon pficon-warning-exclamation"></span>
-        </span>
-        <strong>{{notification.header}}</strong> {{notification.message}}
-    </div>
-</div>
-
-<header class="navbar navbar-default navbar-pf navbar-main header">
-    <div data-ng-include data-src="'partials/menu.html'"></div>
-</header>
-
-<div class="container">
-    <div data-ng-view id="view"></div>
-    <div id="loading" class="loading-backdrop">
-        <div class="loading">
-            <span>Loading...</span>
-        </div>
-    </div>
-</div>
-
-<script type="text/javascript">
-    $.idleTimeout('#idletimeout', '#idletimeout a', {
-        idleAfter: 300,
-        pollingInterval: 60,
-//        keepAliveURL: authUrl + '/admin/keepalive', would need to change this path
-        serverResponseEquals: '',
-        failedRequests: 1,
-        onTimeout: function(){
-            $(this).slideUp();
-            logout();
-        },
-        onIdle: function(){
-            $(this).slideDown(); // show the warning bar
-        },
-        onCountdown: function( counter ){
-            $(this).find("span").html( counter ); // update the counter
-        },
-        onResume: function(){
-            $(this).slideUp(); // hide the warning bar
-        }
-    });
-</script>
-
-</body>
-</html>
+<!doctype html>
+<html lang="en">
+
+<head>
+    <meta charset="utf-8">
+    <title>Keycloak Admin Console</title>
+
+    <link rel="icon" href="img/favicon.ico">
+
+    <link rel="stylesheet" href="css/styles.css">
+
+    <script src="lib/jquery/jquery-1.10.2.js" type="text/javascript"></script>
+    <script src="lib/select2-3.4.1/select2.js" type="text/javascript"></script>
+
+    <script src="lib/angular/angular.js"></script>
+    <script src="lib/angular/angular-resource.js"></script>
+    <script src="lib/angular/angular-route.js"></script>
+    <script src="lib/angular/ui-bootstrap-tpls-0.4.0.js"></script>
+
+    <script src="lib/jquery/jquery.idletimer.js" type="text/javascript"></script>
+    <script src="lib/jquery/jquery.idletimeout.js" type="text/javascript"></script>
+    <script src="lib/angular/select2.js" type="text/javascript"></script>
+    <script src="lib/fileupload/angular-file-upload.min.js"></script>
+
+    <script src="js/keycloak.js" type="text/javascript"></script>
+    <script src="js/app.js" type="text/javascript"></script>
+    <script src="js/controllers/realm.js" type="text/javascript"></script>
+    <script src="js/controllers/applications.js" type="text/javascript"></script>
+    <script src="js/controllers/oauth-clients.js" type="text/javascript"></script>
+    <script src="js/controllers/users.js" type="text/javascript"></script>
+    <script src="js/loaders.js" type="text/javascript"></script>
+    <script src="js/services.js" type="text/javascript"></script>
+
+    <style>
+        [ng\:cloak], [ng-cloak], .ng-cloak {
+            display: none !important;
+        }
+    </style>
+</head>
+
+<body class="admin-console" data-ng-controller="GlobalCtrl" data-ng-cloak>
+<div id="idletimeout">
+    You will be logged off in <strong><span></span> seconds</strong> due to inactivity.
+    <a id="idletimeout-resume" href="#">Click here to continue using this web page</a>.
+</div>
+
+<div class="feedback-aligner" data-ng-show="notification" data-ng-click="notification = null">
+    <div class="alert alert-{{notification.type}}">
+        <span class="pficon pficon-ok" ng-show="notification.type == 'success'"></span>
+        <span class="pficon pficon-info" ng-show="notification.type == 'info'"></span>
+        <span class="pficon-layered" ng-show="notification.type == 'danger'">
+            <span class="pficon pficon-error-octagon"></span>
+            <span class="pficon pficon-error-exclamation"></span>
+        </span>
+        <span class="pficon-layered" ng-show="notification.type == 'warning'">
+            <span class="pficon pficon-warning-triangle"></span>
+            <span class="pficon pficon-warning-exclamation"></span>
+        </span>
+        <strong>{{notification.header}}</strong> {{notification.message}}
+    </div>
+</div>
+
+<header class="navbar navbar-default navbar-pf navbar-main header">
+    <div data-ng-include data-src="'partials/menu.html'"></div>
+</header>
+
+<div class="container">
+    <div data-ng-view id="view"></div>
+    <div id="loading" class="loading-backdrop">
+        <div class="loading">
+            <span>Loading...</span>
+        </div>
+    </div>
+</div>
+
+<script type="text/javascript">
+    $.idleTimeout('#idletimeout', '#idletimeout a', {
+        idleAfter: 300,
+        pollingInterval: 60,
+//        keepAliveURL: authUrl + '/admin/keepalive', would need to change this path
+        serverResponseEquals: '',
+        failedRequests: 1,
+        onTimeout: function(){
+            $(this).slideUp();
+            logout();
+        },
+        onIdle: function(){
+            $(this).slideDown(); // show the warning bar
+        },
+        onCountdown: function( counter ){
+            $(this).find("span").html( counter ); // update the counter
+        },
+        onResume: function(){
+            $(this).slideUp(); // hide the warning bar
+        }
+    });
+</script>
+
+</body>
+</html>
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 65db064..b63ba29 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
@@ -1,1153 +1,1153 @@
-'use strict';
-
-var indexUrl = window.location.href;
-var consoleBaseUrl = window.location.href;
-consoleBaseUrl = consoleBaseUrl.substring(0, consoleBaseUrl.indexOf("/console"));
-consoleBaseUrl = consoleBaseUrl + "/console";
-var configUrl = consoleBaseUrl + "/config";
-var logoutUrl = consoleBaseUrl + "/logout";
-var auth = {};
-var logout = function(){
-    console.log('*** LOGOUT');
-    auth.loggedIn = false;
-    auth.authz = null;
-    auth.user = null;
-    window.location = logoutUrl;
-};
-
-
-var authUrl = window.location.href;
-authUrl = authUrl.substring(0, authUrl.indexOf('/admin/'));
-
-
-var module = angular.module('keycloak', [ 'keycloak.services', 'keycloak.loaders', 'ui.bootstrap', 'ui.select2', 'angularFileUpload' ]);
-var resourceRequests = 0;
-var loadingTimer = -1;
-
-angular.element(document).ready(function ($http) {
-    var keycloakAuth = new Keycloak(configUrl);
-    var auth = {};
-    auth.loggedIn = false;
-
-    keycloakAuth.init('login-required').success(function () {
-        auth.loggedIn = true;
-        auth.authz = keycloakAuth;
-        module.factory('Auth', function() {
-            return auth;
-        });
-        angular.bootstrap(document, ["keycloak"]);
-    }).error(function () {
-            window.location.reload();
-        });
-
-});
-
-module.factory('authInterceptor', function($q, Auth) {
-    return {
-        request: function (config) {
-            var deferred = $q.defer();
-            if (Auth.authz.token) {
-                Auth.authz.updateToken(5).success(function() {
-                    config.headers = config.headers || {};
-                    config.headers.Authorization = 'Bearer ' + Auth.authz.token;
-
-                    deferred.resolve(config);
-                }).error(function() {
-                        deferred.reject('Failed to refresh token');
-                    });
-            }
-            return deferred.promise;
-        }
-    };
-});
-
-
-
-
-module.config([ '$routeProvider', function($routeProvider) {
-
-    $routeProvider
-        /*
-        .when('/create/realm', {
-            templateUrl : 'partials/realm-detail.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return {};
-                }
-            },
-            controller : 'RealmDetailCtrl'
-        })
-        */
-
-        .when('/create/realm', {
-            templateUrl : 'partials/realm-create.html',
-            resolve : {
-
-            },
-            controller : 'RealmCreateCtrl'
-        })
-        .when('/realms/:realm', {
-            templateUrl : 'partials/realm-detail.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                serverInfo : function(ServerInfoLoader) {
-                    return ServerInfoLoader();
-                }
-            },
-            controller : 'RealmDetailCtrl'
-        })
-        .when('/realms', {
-            templateUrl : 'partials/realm-list.html',
-            controller : 'RealmListCtrl'
-        })
-        .when('/realms/:realm/token-settings', {
-            templateUrl : 'partials/realm-tokens.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                }
-            },
-            controller : 'RealmTokenDetailCtrl'
-        })
-        .when('/realms/:realm/keys-settings', {
-            templateUrl : 'partials/realm-keys.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                }
-            },
-            controller : 'RealmKeysDetailCtrl'
-        })
-        .when('/realms/:realm/social-settings', {
-            templateUrl : 'partials/realm-social.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                serverInfo : function(ServerInfoLoader) {
-                    return ServerInfoLoader();
-                }
-            },
-            controller : 'RealmSocialCtrl'
-        })
-        .when('/realms/:realm/default-roles', {
-            templateUrl : 'partials/realm-default-roles.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                applications : function(ApplicationListLoader) {
-                    return ApplicationListLoader();
-                },
-                roles : function(RoleListLoader) {
-                    return RoleListLoader();
-                }
-            },
-            controller : 'RealmDefaultRolesCtrl'
-        })
-        .when('/realms/:realm/required-credentials', {
-            templateUrl : 'partials/realm-credentials.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                }
-            },
-            controller : 'RealmRequiredCredentialsCtrl'
-        })
-        .when('/realms/:realm/smtp-settings', {
-            templateUrl : 'partials/realm-smtp.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                }
-            },
-            controller : 'RealmSMTPSettingsCtrl'
-        })
-        .when('/realms/:realm/ldap-settings', {
-            templateUrl : 'partials/realm-ldap.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                }
-            },
-            controller : 'RealmLdapSettingsCtrl'
-        })
-        .when('/realms/:realm/audit', {
-            templateUrl : 'partials/realm-audit.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                }
-            },
-            controller : 'RealmAuditEventsCtrl'
-        })
-        .when('/realms/:realm/audit-settings', {
-            templateUrl : 'partials/realm-audit-config.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                serverInfo : function(ServerInfoLoader) {
-                    return ServerInfoLoader();
-                },
-                auditConfig : function(RealmAuditLoader) {
-                    return RealmAuditLoader();
-                }
-            },
-            controller : 'RealmAuditCtrl'
-        })
-        .when('/realms/:realm/auth-settings', {
-            templateUrl : 'partials/realm-auth-list.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                }
-            },
-            controller : 'RealmAuthSettingsCtrl'
-        })
-        .when('/realms/:realm/auth-settings/create', {
-            templateUrl : 'partials/realm-auth-detail.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                serverInfo : function(ServerInfoLoader) {
-                    return ServerInfoLoader();
-                }
-            },
-            controller : 'RealmAuthSettingsDetailCtrl'
-        })
-        .when('/realms/:realm/auth-settings/:index', {
-            templateUrl : 'partials/realm-auth-detail.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                serverInfo : function(ServerInfoLoader) {
-                    return ServerInfoLoader();
-                }
-            },
-            controller : 'RealmAuthSettingsDetailCtrl'
-        })
-        .when('/create/user/:realm', {
-            templateUrl : 'partials/user-detail.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                user : function() {
-                    return {};
-                }
-            },
-            controller : 'UserDetailCtrl'
-        })
-        .when('/realms/:realm/users/:user', {
-            templateUrl : 'partials/user-detail.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                user : function(UserLoader) {
-                    return UserLoader();
-                }
-            },
-            controller : 'UserDetailCtrl'
-        })
-        .when('/realms/:realm/users/:user/user-credentials', {
-            templateUrl : 'partials/user-credentials.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                user : function(UserLoader) {
-                    return UserLoader();
-                }
-            },
-            controller : 'UserCredentialsCtrl'
-        })
-        .when('/realms/:realm/users/:user/role-mappings', {
-            templateUrl : 'partials/role-mappings.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                user : function(UserLoader) {
-                    return UserLoader();
-                },
-                applications : function(ApplicationListLoader) {
-                    return ApplicationListLoader();
-                },
-                roles : function(RoleListLoader) {
-                    return RoleListLoader();
-                }
-            },
-            controller : 'UserRoleMappingCtrl'
-        })
-        .when('/realms/:realm/users/:user/sessions', {
-            templateUrl : 'partials/user-sessions.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                user : function(UserLoader) {
-                    return UserLoader();
-                },
-                stats : function(UserSessionStatsLoader) {
-                    return UserSessionStatsLoader();
-                }
-            },
-            controller : 'UserSessionsCtrl'
-        })
-        .when('/realms/:realm/users/:user/social-links', {
-            templateUrl : 'partials/user-social-links.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                user : function(UserLoader) {
-                    return UserLoader();
-                },
-                socialLinks : function(UserSocialLinksLoader) {
-                    return UserSocialLinksLoader();
-                }
-            },
-            controller : 'UserSocialCtrl'
-        })
-        .when('/realms/:realm/users', {
-            templateUrl : 'partials/user-list.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                }
-            },
-            controller : 'UserListCtrl'
-        })
-
-        .when('/create/role/:realm', {
-            templateUrl : 'partials/role-detail.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                role : function() {
-                    return {};
-                },
-                roles : function(RoleListLoader) {
-                    return RoleListLoader();
-                },
-                applications : function(ApplicationListLoader) {
-                    return ApplicationListLoader();
-                }
-            },
-            controller : 'RoleDetailCtrl'
-        })
-        .when('/realms/:realm/roles/:role', {
-            templateUrl : 'partials/role-detail.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                role : function(RoleLoader) {
-                    return RoleLoader();
-                },
-                roles : function(RoleListLoader) {
-                    return RoleListLoader();
-                },
-                applications : function(ApplicationListLoader) {
-                    return ApplicationListLoader();
-                }
-            },
-            controller : 'RoleDetailCtrl'
-        })
-        .when('/realms/:realm/roles', {
-            templateUrl : 'partials/role-list.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                roles : function(RoleListLoader) {
-                    return RoleListLoader();
-                }
-            },
-            controller : 'RoleListCtrl'
-        })
-
-        .when('/create/role/:realm/applications/:application', {
-            templateUrl : 'partials/application-role-detail.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                application : function(ApplicationLoader) {
-                    return ApplicationLoader();
-                },
-                role : function() {
-                    return {};
-                },
-                roles : function(RoleListLoader) {
-                    return RoleListLoader();
-                },
-                applications : function(ApplicationListLoader) {
-                    return ApplicationListLoader();
-                }
-            },
-            controller : 'ApplicationRoleDetailCtrl'
-        })
-        .when('/realms/:realm/applications/:application/roles/:role', {
-            templateUrl : 'partials/application-role-detail.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                application : function(ApplicationLoader) {
-                    return ApplicationLoader();
-                },
-                role : function(ApplicationRoleLoader) {
-                    return ApplicationRoleLoader();
-                },
-                roles : function(RoleListLoader) {
-                    return RoleListLoader();
-                },
-                applications : function(ApplicationListLoader) {
-                    return ApplicationListLoader();
-                }
-            },
-            controller : 'ApplicationRoleDetailCtrl'
-        })
-        .when('/realms/:realm/applications/:application/claims', {
-            templateUrl : 'partials/application-claims.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                application : function(ApplicationLoader) {
-                    return ApplicationLoader();
-                },
-                claims : function(ApplicationClaimsLoader) {
-                    return ApplicationClaimsLoader();
-                }
-            },
-            controller : 'ApplicationClaimsCtrl'
-        })
-        .when('/realms/:realm/applications/:application/sessions', {
-            templateUrl : 'partials/application-sessions.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                application : function(ApplicationLoader) {
-                    return ApplicationLoader();
-                },
-                stats : function(ApplicationSessionStatsLoader) {
-                    return ApplicationSessionStatsLoader();
-                }
-            },
-            controller : 'ApplicationSessionsCtrl'
-        })
-        .when('/realms/:realm/applications/:application/credentials', {
-            templateUrl : 'partials/application-credentials.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                application : function(ApplicationLoader) {
-                    return ApplicationLoader();
-                }
-            },
-            controller : 'ApplicationCredentialsCtrl'
-        })
-        .when('/realms/:realm/applications/:application/roles', {
-            templateUrl : 'partials/application-role-list.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                application : function(ApplicationLoader) {
-                    return ApplicationLoader();
-                },
-                roles : function(ApplicationRoleListLoader) {
-                    return ApplicationRoleListLoader();
-                }
-            },
-            controller : 'ApplicationRoleListCtrl'
-        })
-        .when('/realms/:realm/applications/:application/revocation', {
-            templateUrl : 'partials/application-revocation.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                application : function(ApplicationLoader) {
-                    return ApplicationLoader();
-                }
-            },
-            controller : 'ApplicationRevocationCtrl'
-        })
-        .when('/realms/:realm/applications/:application/scope-mappings', {
-            templateUrl : 'partials/application-scope-mappings.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                application : function(ApplicationLoader) {
-                    return ApplicationLoader();
-                },
-                applications : function(ApplicationListLoader) {
-                    return ApplicationListLoader();
-                },
-                roles : function(RoleListLoader) {
-                    return RoleListLoader();
-                }
-            },
-            controller : 'ApplicationScopeMappingCtrl'
-        })
-        .when('/realms/:realm/applications/:application/installation', {
-            templateUrl : 'partials/application-installation.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                application : function(ApplicationLoader) {
-                    return ApplicationLoader();
-                }
-            },
-            controller : 'ApplicationInstallationCtrl'
-        })
-        .when('/create/application/:realm', {
-            templateUrl : 'partials/application-detail.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                applications : function(ApplicationListLoader) {
-                    return ApplicationListLoader();
-                },
-                application : function() {
-                    return {};
-                }
-            },
-            controller : 'ApplicationDetailCtrl'
-        })
-        .when('/realms/:realm/applications/:application', {
-            templateUrl : 'partials/application-detail.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                applications : function(ApplicationListLoader) {
-                    return ApplicationListLoader();
-                },
-                application : function(ApplicationLoader) {
-                    return ApplicationLoader();
-                }
-            },
-            controller : 'ApplicationDetailCtrl'
-        })
-        .when('/realms/:realm/applications', {
-            templateUrl : 'partials/application-list.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                applications : function(ApplicationListLoader) {
-                    return ApplicationListLoader();
-                }
-            },
-            controller : 'ApplicationListCtrl'
-        })
-
-        // OAUTH Client
-
-        .when('/realms/:realm/oauth-clients/:oauth/claims', {
-            templateUrl : 'partials/oauth-client-claims.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                oauth : function(OAuthClientLoader) {
-                    return OAuthClientLoader();
-                },
-                claims : function(OAuthClientClaimsLoader) {
-                    return OAuthClientClaimsLoader();
-                }
-            },
-            controller : 'OAuthClientClaimsCtrl'
-        })
-        .when('/realms/:realm/oauth-clients/:oauth/revocation', {
-            templateUrl : 'partials/oauth-client-revocation.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                oauth : function(OAuthClientLoader) {
-                    return OAuthClientLoader();
-                }
-            },
-            controller : 'OAuthClientRevocationCtrl'
-        })
-        .when('/realms/:realm/oauth-clients/:oauth/credentials', {
-            templateUrl : 'partials/oauth-client-credentials.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                oauth : function(OAuthClientLoader) {
-                    return OAuthClientLoader();
-                }
-            },
-            controller : 'OAuthClientCredentialsCtrl'
-        })
-        .when('/realms/:realm/oauth-clients/:oauth/scope-mappings', {
-            templateUrl : 'partials/oauth-client-scope-mappings.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                oauth : function(OAuthClientLoader) {
-                    return OAuthClientLoader();
-                },
-                applications : function(ApplicationListLoader) {
-                    return ApplicationListLoader();
-                },
-                roles : function(RoleListLoader) {
-                    return RoleListLoader();
-                }
-            },
-            controller : 'OAuthClientScopeMappingCtrl'
-        })
-        .when('/realms/:realm/oauth-clients/:oauth/installation', {
-            templateUrl : 'partials/oauth-client-installation.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                oauth : function(OAuthClientLoader) {
-                    return OAuthClientLoader();
-                },
-                installation : function(OAuthClientInstallationLoader) {
-                    return OAuthClientInstallationLoader();
-                }
-            },
-            controller : 'OAuthClientInstallationCtrl'
-        })
-        .when('/create/oauth-client/:realm', {
-            templateUrl : 'partials/oauth-client-detail.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                oauth : function() {
-                    return {};
-                }
-            },
-            controller : 'OAuthClientDetailCtrl'
-        })
-        .when('/realms/:realm/oauth-clients/:oauth', {
-            templateUrl : 'partials/oauth-client-detail.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                oauth : function(OAuthClientLoader) {
-                    return OAuthClientLoader();
-                }
-            },
-            controller : 'OAuthClientDetailCtrl'
-        })
-        .when('/realms/:realm/oauth-clients', {
-            templateUrl : 'partials/oauth-client-list.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                oauthClients : function(OAuthClientListLoader) {
-                    return OAuthClientListLoader();
-                }
-            },
-            controller : 'OAuthClientListCtrl'
-        })
-
-        .when('/', {
-            templateUrl : 'partials/home.html',
-            controller : 'HomeCtrl'
-        })
-        .when('/mocks/:realm', {
-            templateUrl : 'partials/realm-detail_mock.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                serverInfo : function(ServerInfoLoader) {
-                    return ServerInfoLoader();
-                }
-            },
-            controller : 'RealmDetailCtrl'
-        })
-        .when('/realms/:realm/sessions/revocation', {
-            templateUrl : 'partials/session-revocation.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                }
-            },
-            controller : 'RealmRevocationCtrl'
-        })
-        .when('/realms/:realm/sessions/brute-force', {
-            templateUrl : 'partials/session-brute-force.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                }
-            },
-            controller : 'RealmBruteForceCtrl'
-        })
-        .when('/realms/:realm/sessions/realm', {
-            templateUrl : 'partials/session-realm.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                stats : function(RealmSessionStatsLoader) {
-                    return RealmSessionStatsLoader();
-                }
-            },
-            controller : 'RealmSessionStatsCtrl'
-        })
-        .when('/logout', {
-            templateUrl : 'partials/home.html',
-            controller : 'LogoutCtrl'
-        })
-        .otherwise({
-            templateUrl : 'partials/notfound.html'
-        });
-} ]);
-
-module.config(function($httpProvider) {
-    $httpProvider.responseInterceptors.push('errorInterceptor');
-
-    var spinnerFunction = function(data, headersGetter) {
-        if (resourceRequests == 0) {
-            loadingTimer = window.setTimeout(function() {
-                $('#loading').show();
-                loadingTimer = -1;
-            }, 500);
-        }
-        resourceRequests++;
-        return data;
-    };
-    $httpProvider.defaults.transformRequest.push(spinnerFunction);
-
-    $httpProvider.responseInterceptors.push('spinnerInterceptor');
-    $httpProvider.interceptors.push('authInterceptor');
-
-});
-
-module.factory('errorInterceptor', function($q, $window, $rootScope, $location,Notifications) {
-    return function(promise) {
-        return promise.then(function(response) {
-            return response;
-        }, function(response) {
-            if (response.status == 401) {
-                console.log('session timeout?');
-                logout();
-             } else if (response.status == 403) {
-                Notifications.error("Forbidden");
-            } else if (response.status == 404) {
-                Notifications.error("Not found");
-            } else if (response.status) {
-                if (response.data && response.data.errorMessage) {
-                    Notifications.error(response.data.errorMessage);
-                } else {
-                    Notifications.error("An unexpected server error has occurred");
-                }
-            }
-            return $q.reject(response);
-        });
-    };
-});
-
-module.factory('spinnerInterceptor', function($q, $window, $rootScope, $location) {
-    return function(promise) {
-        return promise.then(function(response) {
-            resourceRequests--;
-            if (resourceRequests == 0) {
-                if(loadingTimer != -1) {
-                    window.clearTimeout(loadingTimer);
-                    loadingTimer = -1;
-                }
-                $('#loading').hide();
-            }
-            return response;
-        }, function(response) {
-            resourceRequests--;
-            if (resourceRequests == 0) {
-                if(loadingTimer != -1) {
-                    window.clearTimeout(loadingTimer);
-                    loadingTimer = -1;
-                }
-                $('#loading').hide();
-            }
-
-            return $q.reject(response);
-        });
-    };
-});
-
-// collapsable form fieldsets
-module.directive('collapsable', function() {
-    return function(scope, element, attrs) {
-        element.click(function() {
-            $(this).toggleClass('collapsed');
-            $(this).find('.toggle-icons').toggleClass('kc-icon-collapse').toggleClass('kc-icon-expand');
-            $(this).find('.toggle-icons').text($(this).text() == "Icon: expand" ? "Icon: collapse" : "Icon: expand");
-            $(this).parent().find('.form-group').toggleClass('hidden');
-        });
-    }
-});
-
-// collapsable form fieldsets
-module.directive('uncollapsed', function() {
-    return function(scope, element, attrs) {
-        element.prepend('<span class="kc-icon-collapse toggle-icons">Icon: collapse</span>');
-        element.click(function() {
-            $(this).toggleClass('collapsed');
-            $(this).find('.toggle-icons').toggleClass('kc-icon-collapse').toggleClass('kc-icon-expand');
-            $(this).find('.toggle-icons').text($(this).text() == "Icon: expand" ? "Icon: collapse" : "Icon: expand");
-            $(this).parent().find('.form-group').toggleClass('hidden');
-        });
-    }
-});
-
-// collapsable form fieldsets
-module.directive('collapsed', function() {
-    return function(scope, element, attrs) {
-        element.prepend('<span class="kc-icon-expand toggle-icons">Icon: expand</span>');
-        element.parent().find('.form-group').toggleClass('hidden');
-        element.click(function() {
-            $(this).toggleClass('collapsed');
-            $(this).find('.toggle-icons').toggleClass('kc-icon-collapse').toggleClass('kc-icon-expand');
-            $(this).find('.toggle-icons').text($(this).text() == "Icon: expand" ? "Icon: collapse" : "Icon: expand");
-            $(this).parent().find('.form-group').toggleClass('hidden');
-        });
-    }
-});
-
-/**
- * Directive for presenting an ON-OFF switch for checkbox.
- * Usage: <input ng-model="mmm" name="nnn" id="iii" onoffswitch [on-text="ooo" off-text="fff"] />
- */
-module.directive('onoffswitch', function() {
-    return {
-        restrict: "EA",
-        replace: true,
-        scope: {
-            name: '@',
-            id: '@',
-            ngModel: '=',
-            ngDisabled: '=',
-            kcOnText: '@onText',
-            kcOffText: '@offText'
-        },
-        // TODO - The same code acts differently when put into the templateURL. Find why and move the code there.
-        //templateUrl: "templates/kc-switch.html",
-        template: "<span><div class='onoffswitch' tabindex='0'><input type='checkbox' ng-model='ngModel' ng-disabled='ngDisabled' class='onoffswitch-checkbox' name='{{name}}' id='{{id}}'><label for='{{id}}' class='onoffswitch-label'><span class='onoffswitch-inner'><span class='onoffswitch-active'>{{kcOnText}}</span><span class='onoffswitch-inactive'>{{kcOffText}}</span></span><span class='onoffswitch-switch'></span></label></div></span>",
-        compile: function(element, attrs) {
-            /*
-            We don't want to propagate basic attributes to the root element of directive. Id should be passed to the
-            input element only to achieve proper label binding (and validity).
-            */
-            element.removeAttr('name');
-            element.removeAttr('id');
-
-            if (!attrs.onText) { attrs.onText = "ON"; }
-            if (!attrs.offText) { attrs.offText = "OFF"; }
-
-            element.bind('keydown', function(e){
-                var code = e.keyCode || e.which;
-                if (code === 32 || code === 13) {
-                    e.stopImmediatePropagation();
-                    e.preventDefault();
-                    $(e.target).find('input').click();
-                }
-            });
-        }
-    }
-});
-
-module.directive('kcInput', function() {
-    var d = {
-        scope : true,
-        replace : false,
-        link : function(scope, element, attrs) {
-            var form = element.children('form');
-            var label = element.children('label');
-            var input = element.children('input');
-
-            var id = form.attr('name') + '.' + input.attr('name');
-
-            element.attr('class', 'control-group');
-
-            label.attr('class', 'control-label');
-            label.attr('for', id);
-
-            input.wrap('<div class="controls"/>');
-            input.attr('id', id);
-
-            if (!input.attr('placeHolder')) {
-                input.attr('placeHolder', label.text());
-            }
-
-            if (input.attr('required')) {
-                label.append(' <span class="required">*</span>');
-            }
-        }
-    };
-    return d;
-});
-
-module.directive('kcEnter', function() {
-    return function(scope, element, attrs) {
-        element.bind("keydown keypress", function(event) {
-            if (event.which === 13) {
-                scope.$apply(function() {
-                    scope.$eval(attrs.kcEnter);
-                });
-
-                event.preventDefault();
-            }
-        });
-    };
-});
-
-module.directive('kcSave', function ($compile, Notifications) {
-    return {
-        restrict: 'A',
-        link: function ($scope, elem, attr, ctrl) {
-            elem.addClass("btn btn-primary btn-lg");
-            elem.attr("type","submit");
-            elem.bind('click', function() {
-                $scope.$apply(function() {
-                    var form = elem.closest('form');
-                    if (form && form.attr('name')) {
-                        var ngValid = form.find('.ng-valid');
-                        if ($scope[form.attr('name')].$valid) {
-                            //ngValid.removeClass('error');
-                            ngValid.parent().removeClass('has-error');
-                            $scope['save']();
-                        } else {
-                            Notifications.error("Missing or invalid field(s). Please verify the fields in red.")
-                            //ngValid.removeClass('error');
-                            ngValid.parent().removeClass('has-error');
-
-                            var ngInvalid = form.find('.ng-invalid');
-                            //ngInvalid.addClass('error');
-                            ngInvalid.parent().addClass('has-error');
-                        }
-                    }
-                });
-            })
-        }
-    }
-});
-
-module.directive('kcReset', function ($compile, Notifications) {
-    return {
-        restrict: 'A',
-        link: function ($scope, elem, attr, ctrl) {
-            elem.addClass("btn btn-default btn-lg");
-            elem.attr("type","submit");
-            elem.bind('click', function() {
-                $scope.$apply(function() {
-                    var form = elem.closest('form');
-                    if (form && form.attr('name')) {
-                        form.find('.ng-valid').removeClass('error');
-                        form.find('.ng-invalid').removeClass('error');
-                        $scope['reset']();
-                    }
-                })
-            })
-        }
-    }
-});
-
-module.directive('kcCancel', function ($compile, Notifications) {
-    return {
-        restrict: 'A',
-        link: function ($scope, elem, attr, ctrl) {
-            elem.addClass("btn btn-default btn-lg");
-            elem.attr("type","submit");
-        }
-    }
-});
-
-module.directive('kcDelete', function ($compile, Notifications) {
-    return {
-        restrict: 'A',
-        link: function ($scope, elem, attr, ctrl) {
-            elem.addClass("btn btn-danger btn-lg");
-            elem.attr("type","submit");
-        }
-    }
-});
-
-
-module.directive('kcDropdown', function ($compile, Notifications) {
-    return {
-        scope: {
-            kcOptions: '=',
-            kcModel: '=',
-            id: "=",
-            kcPlaceholder: '@'
-        },
-        restrict: 'EA',
-        replace: true,
-        templateUrl: 'templates/kc-select.html',
-        link: function(scope, element, attr) {
-            scope.updateModel = function(item) {
-                scope.kcModel = item;
-            };
-        }
-    }
-});
-
-module.directive('kcReadOnly', function() {
-    var disabled = {};
-
-    var d = {
-        replace : false,
-        link : function(scope, element, attrs) {
-            var disable = function(i, e) {
-                if (!e.disabled) {
-                    disabled[e.tagName + i] = true;
-                    e.disabled = true;
-                }
-            }
-
-            var enable = function(i, e) {
-                if (disabled[e.tagName + i]) {
-                    e.disabled = false;
-                    delete disabled[i];
-                }
-            }
-
-            scope.$watch(attrs.kcReadOnly, function(readOnly) {
-                if (readOnly) {
-                    console.debug('readonly');
-                    element.find('input').each(disable);
-                    element.find('button').each(disable);
-                    element.find('select').each(disable);
-                    element.find('textarea').each(disable);
-                } else {
-                    element.find('input').each(enable);
-                    element.find('input').each(enable);
-                    element.find('button').each(enable);
-                    element.find('select').each(enable);
-                    element.find('textarea').each(enable);
-                }
-            });
-        }
-    };
-    return d;
-});
-
-module.directive('kcNavigation', function ($compile, Notifications) {
-    return {
-        scope: true,
-        restrict: 'E',
-        replace: true,
-        templateUrl: 'templates/kc-navigation.html',
-
-        compile: function(element, attrs){
-            if (!attrs.kcSocial) {
-                attrs.kcSocial = false;
-            }
-        }
-    }
-});
-
-/*
-*  Used to select the element (invoke $(elem).select()) on specified action list.
-*  Usages kc-select-action="click mouseover"
-*  When used in the textarea element, this will select/highlight the textarea content on specified action (i.e. click).
-*/
-module.directive('kcSelectAction', function ($compile, Notifications) {
-    return {
-        restrict: 'A',
-        compile: function (elem, attrs) {
-
-            var events = attrs.kcSelectAction.split(" ");
-
-            for(var i=0; i < events.length; i++){
-
-                elem.bind(events[i], function(){
-                    elem.select();
-                });
-            }
-        }
-    }
-});
-
-module.filter('remove', function() {
-    return function(input, remove, attribute) {
-        if (!input || !remove) {
-            return input;
-        }
-
-        var out = [];
-        for ( var i = 0; i < input.length; i++) {
-            var e = input[i];
-
-            if (Array.isArray(remove)) {
-                for (var j = 0; j < remove.length; j++) {
-                    if (attribute) {
-                        if (remove[j][attribute] == e[attribute]) {
-                            e = null;
-                            break;
-                        }
-                    } else {
-                        if (remove[j] == e) {
-                            e = null;
-                            break;
-                        }
-                    }
-                }
-            } else {
-                if (attribute) {
-                    if (remove[attribute] == e[attribute]) {
-                        e = null;
-                    }
-                } else {
-                    if (remove == e) {
-                        e = null;
-                    }
-                }
-            }
-
-            if (e != null) {
-                out.push(e);
-            }
-        }
-
-        return out;
-    };
-});
-
-module.filter('capitalize', function() {
-    return function(input) {
-        if (!input) {
-            return;
-        }
-        var result = input.substring(0, 1).toUpperCase();
-        var s = input.substring(1);
-        for (var i=0; i<s.length ; i++) {
-            var c = s[i];
-            if (c.match(/[A-Z]/)) {
-                result = result.concat(" ")
-            };
-            result = result.concat(c);
-        };
-        return result;
-    };
+'use strict';
+
+var indexUrl = window.location.href;
+var consoleBaseUrl = window.location.href;
+consoleBaseUrl = consoleBaseUrl.substring(0, consoleBaseUrl.indexOf("/console"));
+consoleBaseUrl = consoleBaseUrl + "/console";
+var configUrl = consoleBaseUrl + "/config";
+var logoutUrl = consoleBaseUrl + "/logout";
+var auth = {};
+var logout = function(){
+    console.log('*** LOGOUT');
+    auth.loggedIn = false;
+    auth.authz = null;
+    auth.user = null;
+    window.location = logoutUrl;
+};
+
+
+var authUrl = window.location.href;
+authUrl = authUrl.substring(0, authUrl.indexOf('/admin/'));
+
+
+var module = angular.module('keycloak', [ 'keycloak.services', 'keycloak.loaders', 'ui.bootstrap', 'ui.select2', 'angularFileUpload' ]);
+var resourceRequests = 0;
+var loadingTimer = -1;
+
+angular.element(document).ready(function ($http) {
+    var keycloakAuth = new Keycloak(configUrl);
+    var auth = {};
+    auth.loggedIn = false;
+
+    keycloakAuth.init('login-required').success(function () {
+        auth.loggedIn = true;
+        auth.authz = keycloakAuth;
+        module.factory('Auth', function() {
+            return auth;
+        });
+        angular.bootstrap(document, ["keycloak"]);
+    }).error(function () {
+            window.location.reload();
+        });
+
+});
+
+module.factory('authInterceptor', function($q, Auth) {
+    return {
+        request: function (config) {
+            var deferred = $q.defer();
+            if (Auth.authz.token) {
+                Auth.authz.updateToken(5).success(function() {
+                    config.headers = config.headers || {};
+                    config.headers.Authorization = 'Bearer ' + Auth.authz.token;
+
+                    deferred.resolve(config);
+                }).error(function() {
+                        deferred.reject('Failed to refresh token');
+                    });
+            }
+            return deferred.promise;
+        }
+    };
+});
+
+
+
+
+module.config([ '$routeProvider', function($routeProvider) {
+
+    $routeProvider
+        /*
+        .when('/create/realm', {
+            templateUrl : 'partials/realm-detail.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return {};
+                }
+            },
+            controller : 'RealmDetailCtrl'
+        })
+        */
+
+        .when('/create/realm', {
+            templateUrl : 'partials/realm-create.html',
+            resolve : {
+
+            },
+            controller : 'RealmCreateCtrl'
+        })
+        .when('/realms/:realm', {
+            templateUrl : 'partials/realm-detail.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                serverInfo : function(ServerInfoLoader) {
+                    return ServerInfoLoader();
+                }
+            },
+            controller : 'RealmDetailCtrl'
+        })
+        .when('/realms', {
+            templateUrl : 'partials/realm-list.html',
+            controller : 'RealmListCtrl'
+        })
+        .when('/realms/:realm/token-settings', {
+            templateUrl : 'partials/realm-tokens.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                }
+            },
+            controller : 'RealmTokenDetailCtrl'
+        })
+        .when('/realms/:realm/keys-settings', {
+            templateUrl : 'partials/realm-keys.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                }
+            },
+            controller : 'RealmKeysDetailCtrl'
+        })
+        .when('/realms/:realm/social-settings', {
+            templateUrl : 'partials/realm-social.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                serverInfo : function(ServerInfoLoader) {
+                    return ServerInfoLoader();
+                }
+            },
+            controller : 'RealmSocialCtrl'
+        })
+        .when('/realms/:realm/default-roles', {
+            templateUrl : 'partials/realm-default-roles.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                applications : function(ApplicationListLoader) {
+                    return ApplicationListLoader();
+                },
+                roles : function(RoleListLoader) {
+                    return RoleListLoader();
+                }
+            },
+            controller : 'RealmDefaultRolesCtrl'
+        })
+        .when('/realms/:realm/required-credentials', {
+            templateUrl : 'partials/realm-credentials.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                }
+            },
+            controller : 'RealmRequiredCredentialsCtrl'
+        })
+        .when('/realms/:realm/smtp-settings', {
+            templateUrl : 'partials/realm-smtp.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                }
+            },
+            controller : 'RealmSMTPSettingsCtrl'
+        })
+        .when('/realms/:realm/ldap-settings', {
+            templateUrl : 'partials/realm-ldap.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                }
+            },
+            controller : 'RealmLdapSettingsCtrl'
+        })
+        .when('/realms/:realm/audit', {
+            templateUrl : 'partials/realm-audit.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                }
+            },
+            controller : 'RealmAuditEventsCtrl'
+        })
+        .when('/realms/:realm/audit-settings', {
+            templateUrl : 'partials/realm-audit-config.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                serverInfo : function(ServerInfoLoader) {
+                    return ServerInfoLoader();
+                },
+                auditConfig : function(RealmAuditLoader) {
+                    return RealmAuditLoader();
+                }
+            },
+            controller : 'RealmAuditCtrl'
+        })
+        .when('/realms/:realm/auth-settings', {
+            templateUrl : 'partials/realm-auth-list.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                }
+            },
+            controller : 'RealmAuthSettingsCtrl'
+        })
+        .when('/realms/:realm/auth-settings/create', {
+            templateUrl : 'partials/realm-auth-detail.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                serverInfo : function(ServerInfoLoader) {
+                    return ServerInfoLoader();
+                }
+            },
+            controller : 'RealmAuthSettingsDetailCtrl'
+        })
+        .when('/realms/:realm/auth-settings/:index', {
+            templateUrl : 'partials/realm-auth-detail.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                serverInfo : function(ServerInfoLoader) {
+                    return ServerInfoLoader();
+                }
+            },
+            controller : 'RealmAuthSettingsDetailCtrl'
+        })
+        .when('/create/user/:realm', {
+            templateUrl : 'partials/user-detail.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                user : function() {
+                    return {};
+                }
+            },
+            controller : 'UserDetailCtrl'
+        })
+        .when('/realms/:realm/users/:user', {
+            templateUrl : 'partials/user-detail.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                user : function(UserLoader) {
+                    return UserLoader();
+                }
+            },
+            controller : 'UserDetailCtrl'
+        })
+        .when('/realms/:realm/users/:user/user-credentials', {
+            templateUrl : 'partials/user-credentials.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                user : function(UserLoader) {
+                    return UserLoader();
+                }
+            },
+            controller : 'UserCredentialsCtrl'
+        })
+        .when('/realms/:realm/users/:user/role-mappings', {
+            templateUrl : 'partials/role-mappings.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                user : function(UserLoader) {
+                    return UserLoader();
+                },
+                applications : function(ApplicationListLoader) {
+                    return ApplicationListLoader();
+                },
+                roles : function(RoleListLoader) {
+                    return RoleListLoader();
+                }
+            },
+            controller : 'UserRoleMappingCtrl'
+        })
+        .when('/realms/:realm/users/:user/sessions', {
+            templateUrl : 'partials/user-sessions.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                user : function(UserLoader) {
+                    return UserLoader();
+                },
+                stats : function(UserSessionStatsLoader) {
+                    return UserSessionStatsLoader();
+                }
+            },
+            controller : 'UserSessionsCtrl'
+        })
+        .when('/realms/:realm/users/:user/social-links', {
+            templateUrl : 'partials/user-social-links.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                user : function(UserLoader) {
+                    return UserLoader();
+                },
+                socialLinks : function(UserSocialLinksLoader) {
+                    return UserSocialLinksLoader();
+                }
+            },
+            controller : 'UserSocialCtrl'
+        })
+        .when('/realms/:realm/users', {
+            templateUrl : 'partials/user-list.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                }
+            },
+            controller : 'UserListCtrl'
+        })
+
+        .when('/create/role/:realm', {
+            templateUrl : 'partials/role-detail.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                role : function() {
+                    return {};
+                },
+                roles : function(RoleListLoader) {
+                    return RoleListLoader();
+                },
+                applications : function(ApplicationListLoader) {
+                    return ApplicationListLoader();
+                }
+            },
+            controller : 'RoleDetailCtrl'
+        })
+        .when('/realms/:realm/roles/:role', {
+            templateUrl : 'partials/role-detail.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                role : function(RoleLoader) {
+                    return RoleLoader();
+                },
+                roles : function(RoleListLoader) {
+                    return RoleListLoader();
+                },
+                applications : function(ApplicationListLoader) {
+                    return ApplicationListLoader();
+                }
+            },
+            controller : 'RoleDetailCtrl'
+        })
+        .when('/realms/:realm/roles', {
+            templateUrl : 'partials/role-list.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                roles : function(RoleListLoader) {
+                    return RoleListLoader();
+                }
+            },
+            controller : 'RoleListCtrl'
+        })
+
+        .when('/create/role/:realm/applications/:application', {
+            templateUrl : 'partials/application-role-detail.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                application : function(ApplicationLoader) {
+                    return ApplicationLoader();
+                },
+                role : function() {
+                    return {};
+                },
+                roles : function(RoleListLoader) {
+                    return RoleListLoader();
+                },
+                applications : function(ApplicationListLoader) {
+                    return ApplicationListLoader();
+                }
+            },
+            controller : 'ApplicationRoleDetailCtrl'
+        })
+        .when('/realms/:realm/applications/:application/roles/:role', {
+            templateUrl : 'partials/application-role-detail.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                application : function(ApplicationLoader) {
+                    return ApplicationLoader();
+                },
+                role : function(ApplicationRoleLoader) {
+                    return ApplicationRoleLoader();
+                },
+                roles : function(RoleListLoader) {
+                    return RoleListLoader();
+                },
+                applications : function(ApplicationListLoader) {
+                    return ApplicationListLoader();
+                }
+            },
+            controller : 'ApplicationRoleDetailCtrl'
+        })
+        .when('/realms/:realm/applications/:application/claims', {
+            templateUrl : 'partials/application-claims.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                application : function(ApplicationLoader) {
+                    return ApplicationLoader();
+                },
+                claims : function(ApplicationClaimsLoader) {
+                    return ApplicationClaimsLoader();
+                }
+            },
+            controller : 'ApplicationClaimsCtrl'
+        })
+        .when('/realms/:realm/applications/:application/sessions', {
+            templateUrl : 'partials/application-sessions.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                application : function(ApplicationLoader) {
+                    return ApplicationLoader();
+                },
+                stats : function(ApplicationSessionStatsLoader) {
+                    return ApplicationSessionStatsLoader();
+                }
+            },
+            controller : 'ApplicationSessionsCtrl'
+        })
+        .when('/realms/:realm/applications/:application/credentials', {
+            templateUrl : 'partials/application-credentials.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                application : function(ApplicationLoader) {
+                    return ApplicationLoader();
+                }
+            },
+            controller : 'ApplicationCredentialsCtrl'
+        })
+        .when('/realms/:realm/applications/:application/roles', {
+            templateUrl : 'partials/application-role-list.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                application : function(ApplicationLoader) {
+                    return ApplicationLoader();
+                },
+                roles : function(ApplicationRoleListLoader) {
+                    return ApplicationRoleListLoader();
+                }
+            },
+            controller : 'ApplicationRoleListCtrl'
+        })
+        .when('/realms/:realm/applications/:application/revocation', {
+            templateUrl : 'partials/application-revocation.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                application : function(ApplicationLoader) {
+                    return ApplicationLoader();
+                }
+            },
+            controller : 'ApplicationRevocationCtrl'
+        })
+        .when('/realms/:realm/applications/:application/scope-mappings', {
+            templateUrl : 'partials/application-scope-mappings.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                application : function(ApplicationLoader) {
+                    return ApplicationLoader();
+                },
+                applications : function(ApplicationListLoader) {
+                    return ApplicationListLoader();
+                },
+                roles : function(RoleListLoader) {
+                    return RoleListLoader();
+                }
+            },
+            controller : 'ApplicationScopeMappingCtrl'
+        })
+        .when('/realms/:realm/applications/:application/installation', {
+            templateUrl : 'partials/application-installation.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                application : function(ApplicationLoader) {
+                    return ApplicationLoader();
+                }
+            },
+            controller : 'ApplicationInstallationCtrl'
+        })
+        .when('/create/application/:realm', {
+            templateUrl : 'partials/application-detail.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                applications : function(ApplicationListLoader) {
+                    return ApplicationListLoader();
+                },
+                application : function() {
+                    return {};
+                }
+            },
+            controller : 'ApplicationDetailCtrl'
+        })
+        .when('/realms/:realm/applications/:application', {
+            templateUrl : 'partials/application-detail.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                applications : function(ApplicationListLoader) {
+                    return ApplicationListLoader();
+                },
+                application : function(ApplicationLoader) {
+                    return ApplicationLoader();
+                }
+            },
+            controller : 'ApplicationDetailCtrl'
+        })
+        .when('/realms/:realm/applications', {
+            templateUrl : 'partials/application-list.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                applications : function(ApplicationListLoader) {
+                    return ApplicationListLoader();
+                }
+            },
+            controller : 'ApplicationListCtrl'
+        })
+
+        // OAUTH Client
+
+        .when('/realms/:realm/oauth-clients/:oauth/claims', {
+            templateUrl : 'partials/oauth-client-claims.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                oauth : function(OAuthClientLoader) {
+                    return OAuthClientLoader();
+                },
+                claims : function(OAuthClientClaimsLoader) {
+                    return OAuthClientClaimsLoader();
+                }
+            },
+            controller : 'OAuthClientClaimsCtrl'
+        })
+        .when('/realms/:realm/oauth-clients/:oauth/revocation', {
+            templateUrl : 'partials/oauth-client-revocation.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                oauth : function(OAuthClientLoader) {
+                    return OAuthClientLoader();
+                }
+            },
+            controller : 'OAuthClientRevocationCtrl'
+        })
+        .when('/realms/:realm/oauth-clients/:oauth/credentials', {
+            templateUrl : 'partials/oauth-client-credentials.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                oauth : function(OAuthClientLoader) {
+                    return OAuthClientLoader();
+                }
+            },
+            controller : 'OAuthClientCredentialsCtrl'
+        })
+        .when('/realms/:realm/oauth-clients/:oauth/scope-mappings', {
+            templateUrl : 'partials/oauth-client-scope-mappings.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                oauth : function(OAuthClientLoader) {
+                    return OAuthClientLoader();
+                },
+                applications : function(ApplicationListLoader) {
+                    return ApplicationListLoader();
+                },
+                roles : function(RoleListLoader) {
+                    return RoleListLoader();
+                }
+            },
+            controller : 'OAuthClientScopeMappingCtrl'
+        })
+        .when('/realms/:realm/oauth-clients/:oauth/installation', {
+            templateUrl : 'partials/oauth-client-installation.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                oauth : function(OAuthClientLoader) {
+                    return OAuthClientLoader();
+                },
+                installation : function(OAuthClientInstallationLoader) {
+                    return OAuthClientInstallationLoader();
+                }
+            },
+            controller : 'OAuthClientInstallationCtrl'
+        })
+        .when('/create/oauth-client/:realm', {
+            templateUrl : 'partials/oauth-client-detail.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                oauth : function() {
+                    return {};
+                }
+            },
+            controller : 'OAuthClientDetailCtrl'
+        })
+        .when('/realms/:realm/oauth-clients/:oauth', {
+            templateUrl : 'partials/oauth-client-detail.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                oauth : function(OAuthClientLoader) {
+                    return OAuthClientLoader();
+                }
+            },
+            controller : 'OAuthClientDetailCtrl'
+        })
+        .when('/realms/:realm/oauth-clients', {
+            templateUrl : 'partials/oauth-client-list.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                oauthClients : function(OAuthClientListLoader) {
+                    return OAuthClientListLoader();
+                }
+            },
+            controller : 'OAuthClientListCtrl'
+        })
+
+        .when('/', {
+            templateUrl : 'partials/home.html',
+            controller : 'HomeCtrl'
+        })
+        .when('/mocks/:realm', {
+            templateUrl : 'partials/realm-detail_mock.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                serverInfo : function(ServerInfoLoader) {
+                    return ServerInfoLoader();
+                }
+            },
+            controller : 'RealmDetailCtrl'
+        })
+        .when('/realms/:realm/sessions/revocation', {
+            templateUrl : 'partials/session-revocation.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                }
+            },
+            controller : 'RealmRevocationCtrl'
+        })
+        .when('/realms/:realm/sessions/brute-force', {
+            templateUrl : 'partials/session-brute-force.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                }
+            },
+            controller : 'RealmBruteForceCtrl'
+        })
+        .when('/realms/:realm/sessions/realm', {
+            templateUrl : 'partials/session-realm.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                stats : function(RealmSessionStatsLoader) {
+                    return RealmSessionStatsLoader();
+                }
+            },
+            controller : 'RealmSessionStatsCtrl'
+        })
+        .when('/logout', {
+            templateUrl : 'partials/home.html',
+            controller : 'LogoutCtrl'
+        })
+        .otherwise({
+            templateUrl : 'partials/notfound.html'
+        });
+} ]);
+
+module.config(function($httpProvider) {
+    $httpProvider.responseInterceptors.push('errorInterceptor');
+
+    var spinnerFunction = function(data, headersGetter) {
+        if (resourceRequests == 0) {
+            loadingTimer = window.setTimeout(function() {
+                $('#loading').show();
+                loadingTimer = -1;
+            }, 500);
+        }
+        resourceRequests++;
+        return data;
+    };
+    $httpProvider.defaults.transformRequest.push(spinnerFunction);
+
+    $httpProvider.responseInterceptors.push('spinnerInterceptor');
+    $httpProvider.interceptors.push('authInterceptor');
+
+});
+
+module.factory('errorInterceptor', function($q, $window, $rootScope, $location,Notifications) {
+    return function(promise) {
+        return promise.then(function(response) {
+            return response;
+        }, function(response) {
+            if (response.status == 401) {
+                console.log('session timeout?');
+                logout();
+             } else if (response.status == 403) {
+                Notifications.error("Forbidden");
+            } else if (response.status == 404) {
+                Notifications.error("Not found");
+            } else if (response.status) {
+                if (response.data && response.data.errorMessage) {
+                    Notifications.error(response.data.errorMessage);
+                } else {
+                    Notifications.error("An unexpected server error has occurred");
+                }
+            }
+            return $q.reject(response);
+        });
+    };
+});
+
+module.factory('spinnerInterceptor', function($q, $window, $rootScope, $location) {
+    return function(promise) {
+        return promise.then(function(response) {
+            resourceRequests--;
+            if (resourceRequests == 0) {
+                if(loadingTimer != -1) {
+                    window.clearTimeout(loadingTimer);
+                    loadingTimer = -1;
+                }
+                $('#loading').hide();
+            }
+            return response;
+        }, function(response) {
+            resourceRequests--;
+            if (resourceRequests == 0) {
+                if(loadingTimer != -1) {
+                    window.clearTimeout(loadingTimer);
+                    loadingTimer = -1;
+                }
+                $('#loading').hide();
+            }
+
+            return $q.reject(response);
+        });
+    };
+});
+
+// collapsable form fieldsets
+module.directive('collapsable', function() {
+    return function(scope, element, attrs) {
+        element.click(function() {
+            $(this).toggleClass('collapsed');
+            $(this).find('.toggle-icons').toggleClass('kc-icon-collapse').toggleClass('kc-icon-expand');
+            $(this).find('.toggle-icons').text($(this).text() == "Icon: expand" ? "Icon: collapse" : "Icon: expand");
+            $(this).parent().find('.form-group').toggleClass('hidden');
+        });
+    }
+});
+
+// collapsable form fieldsets
+module.directive('uncollapsed', function() {
+    return function(scope, element, attrs) {
+        element.prepend('<span class="kc-icon-collapse toggle-icons">Icon: collapse</span>');
+        element.click(function() {
+            $(this).toggleClass('collapsed');
+            $(this).find('.toggle-icons').toggleClass('kc-icon-collapse').toggleClass('kc-icon-expand');
+            $(this).find('.toggle-icons').text($(this).text() == "Icon: expand" ? "Icon: collapse" : "Icon: expand");
+            $(this).parent().find('.form-group').toggleClass('hidden');
+        });
+    }
+});
+
+// collapsable form fieldsets
+module.directive('collapsed', function() {
+    return function(scope, element, attrs) {
+        element.prepend('<span class="kc-icon-expand toggle-icons">Icon: expand</span>');
+        element.parent().find('.form-group').toggleClass('hidden');
+        element.click(function() {
+            $(this).toggleClass('collapsed');
+            $(this).find('.toggle-icons').toggleClass('kc-icon-collapse').toggleClass('kc-icon-expand');
+            $(this).find('.toggle-icons').text($(this).text() == "Icon: expand" ? "Icon: collapse" : "Icon: expand");
+            $(this).parent().find('.form-group').toggleClass('hidden');
+        });
+    }
+});
+
+/**
+ * Directive for presenting an ON-OFF switch for checkbox.
+ * Usage: <input ng-model="mmm" name="nnn" id="iii" onoffswitch [on-text="ooo" off-text="fff"] />
+ */
+module.directive('onoffswitch', function() {
+    return {
+        restrict: "EA",
+        replace: true,
+        scope: {
+            name: '@',
+            id: '@',
+            ngModel: '=',
+            ngDisabled: '=',
+            kcOnText: '@onText',
+            kcOffText: '@offText'
+        },
+        // TODO - The same code acts differently when put into the templateURL. Find why and move the code there.
+        //templateUrl: "templates/kc-switch.html",
+        template: "<span><div class='onoffswitch' tabindex='0'><input type='checkbox' ng-model='ngModel' ng-disabled='ngDisabled' class='onoffswitch-checkbox' name='{{name}}' id='{{id}}'><label for='{{id}}' class='onoffswitch-label'><span class='onoffswitch-inner'><span class='onoffswitch-active'>{{kcOnText}}</span><span class='onoffswitch-inactive'>{{kcOffText}}</span></span><span class='onoffswitch-switch'></span></label></div></span>",
+        compile: function(element, attrs) {
+            /*
+            We don't want to propagate basic attributes to the root element of directive. Id should be passed to the
+            input element only to achieve proper label binding (and validity).
+            */
+            element.removeAttr('name');
+            element.removeAttr('id');
+
+            if (!attrs.onText) { attrs.onText = "ON"; }
+            if (!attrs.offText) { attrs.offText = "OFF"; }
+
+            element.bind('keydown', function(e){
+                var code = e.keyCode || e.which;
+                if (code === 32 || code === 13) {
+                    e.stopImmediatePropagation();
+                    e.preventDefault();
+                    $(e.target).find('input').click();
+                }
+            });
+        }
+    }
+});
+
+module.directive('kcInput', function() {
+    var d = {
+        scope : true,
+        replace : false,
+        link : function(scope, element, attrs) {
+            var form = element.children('form');
+            var label = element.children('label');
+            var input = element.children('input');
+
+            var id = form.attr('name') + '.' + input.attr('name');
+
+            element.attr('class', 'control-group');
+
+            label.attr('class', 'control-label');
+            label.attr('for', id);
+
+            input.wrap('<div class="controls"/>');
+            input.attr('id', id);
+
+            if (!input.attr('placeHolder')) {
+                input.attr('placeHolder', label.text());
+            }
+
+            if (input.attr('required')) {
+                label.append(' <span class="required">*</span>');
+            }
+        }
+    };
+    return d;
+});
+
+module.directive('kcEnter', function() {
+    return function(scope, element, attrs) {
+        element.bind("keydown keypress", function(event) {
+            if (event.which === 13) {
+                scope.$apply(function() {
+                    scope.$eval(attrs.kcEnter);
+                });
+
+                event.preventDefault();
+            }
+        });
+    };
+});
+
+module.directive('kcSave', function ($compile, Notifications) {
+    return {
+        restrict: 'A',
+        link: function ($scope, elem, attr, ctrl) {
+            elem.addClass("btn btn-primary btn-lg");
+            elem.attr("type","submit");
+            elem.bind('click', function() {
+                $scope.$apply(function() {
+                    var form = elem.closest('form');
+                    if (form && form.attr('name')) {
+                        var ngValid = form.find('.ng-valid');
+                        if ($scope[form.attr('name')].$valid) {
+                            //ngValid.removeClass('error');
+                            ngValid.parent().removeClass('has-error');
+                            $scope['save']();
+                        } else {
+                            Notifications.error("Missing or invalid field(s). Please verify the fields in red.")
+                            //ngValid.removeClass('error');
+                            ngValid.parent().removeClass('has-error');
+
+                            var ngInvalid = form.find('.ng-invalid');
+                            //ngInvalid.addClass('error');
+                            ngInvalid.parent().addClass('has-error');
+                        }
+                    }
+                });
+            })
+        }
+    }
+});
+
+module.directive('kcReset', function ($compile, Notifications) {
+    return {
+        restrict: 'A',
+        link: function ($scope, elem, attr, ctrl) {
+            elem.addClass("btn btn-default btn-lg");
+            elem.attr("type","submit");
+            elem.bind('click', function() {
+                $scope.$apply(function() {
+                    var form = elem.closest('form');
+                    if (form && form.attr('name')) {
+                        form.find('.ng-valid').removeClass('error');
+                        form.find('.ng-invalid').removeClass('error');
+                        $scope['reset']();
+                    }
+                })
+            })
+        }
+    }
+});
+
+module.directive('kcCancel', function ($compile, Notifications) {
+    return {
+        restrict: 'A',
+        link: function ($scope, elem, attr, ctrl) {
+            elem.addClass("btn btn-default btn-lg");
+            elem.attr("type","submit");
+        }
+    }
+});
+
+module.directive('kcDelete', function ($compile, Notifications) {
+    return {
+        restrict: 'A',
+        link: function ($scope, elem, attr, ctrl) {
+            elem.addClass("btn btn-danger btn-lg");
+            elem.attr("type","submit");
+        }
+    }
+});
+
+
+module.directive('kcDropdown', function ($compile, Notifications) {
+    return {
+        scope: {
+            kcOptions: '=',
+            kcModel: '=',
+            id: "=",
+            kcPlaceholder: '@'
+        },
+        restrict: 'EA',
+        replace: true,
+        templateUrl: 'templates/kc-select.html',
+        link: function(scope, element, attr) {
+            scope.updateModel = function(item) {
+                scope.kcModel = item;
+            };
+        }
+    }
+});
+
+module.directive('kcReadOnly', function() {
+    var disabled = {};
+
+    var d = {
+        replace : false,
+        link : function(scope, element, attrs) {
+            var disable = function(i, e) {
+                if (!e.disabled) {
+                    disabled[e.tagName + i] = true;
+                    e.disabled = true;
+                }
+            }
+
+            var enable = function(i, e) {
+                if (disabled[e.tagName + i]) {
+                    e.disabled = false;
+                    delete disabled[i];
+                }
+            }
+
+            scope.$watch(attrs.kcReadOnly, function(readOnly) {
+                if (readOnly) {
+                    console.debug('readonly');
+                    element.find('input').each(disable);
+                    element.find('button').each(disable);
+                    element.find('select').each(disable);
+                    element.find('textarea').each(disable);
+                } else {
+                    element.find('input').each(enable);
+                    element.find('input').each(enable);
+                    element.find('button').each(enable);
+                    element.find('select').each(enable);
+                    element.find('textarea').each(enable);
+                }
+            });
+        }
+    };
+    return d;
+});
+
+module.directive('kcNavigation', function ($compile, Notifications) {
+    return {
+        scope: true,
+        restrict: 'E',
+        replace: true,
+        templateUrl: 'templates/kc-navigation.html',
+
+        compile: function(element, attrs){
+            if (!attrs.kcSocial) {
+                attrs.kcSocial = false;
+            }
+        }
+    }
+});
+
+/*
+*  Used to select the element (invoke $(elem).select()) on specified action list.
+*  Usages kc-select-action="click mouseover"
+*  When used in the textarea element, this will select/highlight the textarea content on specified action (i.e. click).
+*/
+module.directive('kcSelectAction', function ($compile, Notifications) {
+    return {
+        restrict: 'A',
+        compile: function (elem, attrs) {
+
+            var events = attrs.kcSelectAction.split(" ");
+
+            for(var i=0; i < events.length; i++){
+
+                elem.bind(events[i], function(){
+                    elem.select();
+                });
+            }
+        }
+    }
+});
+
+module.filter('remove', function() {
+    return function(input, remove, attribute) {
+        if (!input || !remove) {
+            return input;
+        }
+
+        var out = [];
+        for ( var i = 0; i < input.length; i++) {
+            var e = input[i];
+
+            if (Array.isArray(remove)) {
+                for (var j = 0; j < remove.length; j++) {
+                    if (attribute) {
+                        if (remove[j][attribute] == e[attribute]) {
+                            e = null;
+                            break;
+                        }
+                    } else {
+                        if (remove[j] == e) {
+                            e = null;
+                            break;
+                        }
+                    }
+                }
+            } else {
+                if (attribute) {
+                    if (remove[attribute] == e[attribute]) {
+                        e = null;
+                    }
+                } else {
+                    if (remove == e) {
+                        e = null;
+                    }
+                }
+            }
+
+            if (e != null) {
+                out.push(e);
+            }
+        }
+
+        return out;
+    };
+});
+
+module.filter('capitalize', function() {
+    return function(input) {
+        if (!input) {
+            return;
+        }
+        var result = input.substring(0, 1).toUpperCase();
+        var s = input.substring(1);
+        for (var i=0; i<s.length ; i++) {
+            var c = s[i];
+            if (c.match(/[A-Z]/)) {
+                result = result.concat(" ")
+            };
+            result = result.concat(c);
+        };
+        return result;
+    };
 });
\ No newline at end of file
diff --git a/integration/js/src/main/resources/keycloak.js b/integration/js/src/main/resources/keycloak.js
index c142663..c204f83 100755
--- a/integration/js/src/main/resources/keycloak.js
+++ b/integration/js/src/main/resources/keycloak.js
@@ -1,626 +1,689 @@
-var Keycloak = function (config) {
-    if (!(this instanceof Keycloak)) {
-        return new Keycloak(config);
-    }
-
-    var kc = this;
-    var adapter;
-
-    kc.init = function (init) {
-        kc.authenticated = false;
-
-        if (window.Cordova) {
-            adapter = loadAdapter('cordova');
-        } else {
-            adapter = loadAdapter();
-        }
-
-        var promise = createPromise();
-
-        var initPromise = createPromise();
-        initPromise.promise.success(function() {
-            kc.onReady && kc.onReady(kc.authenticated);
-            promise.setSuccess(kc.authenticated);
-        }).error(function() {
-            promise.setError();
-        });
-
-        var configPromise = loadConfig(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 {
-                initPromise.setSuccess();
-            }
-        }
-
-        configPromise.success(processInit);
-        configPromise.error(function() {
-            promise.setError();
-        });
-
-        return promise.promise;
-    }
-
-    kc.login = function (options) {
-        return adapter.login(options);
-    }
-
-    kc.createLoginUrl = function(options) {
-        var state = createUUID();
-
-        var redirectUri = adapter.redirectUri(options);
-        if (options && options.prompt) {
-            if (redirectUri.indexOf('?') == -1) {
-                redirectUri += '?prompt=' + options.prompt;
-            } else {
-                redirectUri += '&prompt=' + options.prompt;
-            }
-        }
-
-        sessionStorage.oauthState = state;
-
-        var url = getRealmUrl()
-            + '/tokens/login'
-            + '?client_id=' + encodeURIComponent(kc.clientId)
-            + '&redirect_uri=' + encodeURIComponent(redirectUri)
-            + '&state=' + encodeURIComponent(state)
-            + '&response_type=code';
-
-        if (options && options.prompt) {
-            url += '&prompt=' + options.prompt;
-        }
-
-        return url;
-    }
-
-    kc.logout = function(options) {
-        return adapter.logout(options);
-    }
-
-    kc.createLogoutUrl = function(options) {
-        var url = getRealmUrl()
-            + '/tokens/logout'
-            + '?redirect_uri=' + encodeURIComponent(adapter.redirectUri(options));
-
-        return url;
-    }
-
-    kc.createAccountUrl = function(options) {
-        var url = getRealmUrl()
-            + '/account'
-            + '?referrer=' + kc.clientId
-            + '&referrer_uri=' + encodeURIComponent(adapter.redirectUri(options));
-
-        return url;
-    }
-
-    kc.accountManagement = function() {
-        return adapter.accountManagement();
-    }
-
-    kc.hasRealmRole = function (role) {
-        var access = kc.realmAccess;
-        return access && access.roles.indexOf(role) >= 0 || false;
-    }
-
-    kc.hasResourceRole = function(role, resource) {
-        if (!kc.resourceAccess) {
-            return false;
-        }
-
-        var access = kc.resourceAccess[resource || kc.clientId];
-        return access && access.roles.indexOf(role) >= 0 || false;
-    }
-
-    kc.loadUserProfile = function() {
-        var url = getRealmUrl() + '/account';
-        var req = new XMLHttpRequest();
-        req.open('GET', url, true);
-        req.setRequestHeader('Accept', 'application/json');
-        req.setRequestHeader('Authorization', 'bearer ' + kc.token);
-
-        var promise = createPromise();
-
-        req.onreadystatechange = function () {
-            if (req.readyState == 4) {
-                if (req.status == 200) {
-                    kc.profile = JSON.parse(req.responseText);
-                    promise.setSuccess(kc.profile);
-                } else {
-                    promise.setError();
-                }
-            }
-        }
-
-        req.send();
-
-        return promise.promise;
-    }
-
-    kc.isTokenExpired = function(minValidity) {
-        if (!kc.tokenParsed || !kc.refreshToken) {
-            throw 'Not authenticated';
-        }
-
-        var expiresIn = kc.tokenParsed['exp'] - (new Date().getTime() / 1000);
-        if (minValidity) {
-            expiresIn -= minValidity;
-        }
-
-        return expiresIn < 0;
-    }
-
-    kc.updateToken = function(minValidity) {
-        if (!kc.tokenParsed || !kc.refreshToken) {
-            throw 'Not authenticated';
-        }
-
-        var promise = createPromise();
-
-        if (minValidity) {
-            if (!kc.isTokenExpired(minValidity)) {
-                promise.setSuccess(false);
-                return promise.promise;
-            }
-        }
-
-        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');
-
-        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);
-
-        return promise.promise;
-    }
-
-    function getRealmUrl() {
-        return kc.authServerUrl + '/realms/' + encodeURIComponent(kc.realm);
-    }
-
-    function processCallback(oauth, promise) {
-        var code = oauth.code;
-        var error = oauth.error;
-        var prompt = oauth.prompt;
-
-        if (code) {
-            var params = 'code=' + code;
-            var url = getRealmUrl() + '/tokens/access/codes';
-
-            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);
-            }
-
-            req.withCredentials = true;
-
-            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.onAuthSuccess && kc.onAuthSuccess();
-                        promise && promise.setSuccess();
-                    } else {
-                        kc.onAuthError && kc.onAuthError();
-                        promise && promise.setError();
-                    }
-                }
-            };
-
-            req.send(params);
-        } else if (error) {
-            if (prompt != 'none') {
-                kc.onAuthError && kc.onAuthError();
-                promise && promise.setError();
-            } else {
-                promise && promise.setSuccess();
-            }
-        }
-    }
-
-    function loadConfig(url) {
-        var promise = createPromise();
-        var configUrl;
-
-        if (!config) {
-            configUrl = 'keycloak.json';
-        } else if (typeof config === 'string') {
-            configUrl = config;
-        }
-
-        if (configUrl) {
-            var req = new XMLHttpRequest();
-            req.open('GET', configUrl, true);
-            req.setRequestHeader('Accept', 'application/json');
-
-            req.onreadystatechange = function () {
-                if (req.readyState == 4) {
-                    if (req.status == 200) {
-                        var config = JSON.parse(req.responseText);
-
-                        kc.authServerUrl = config['auth-server-url'];
-                        kc.realm = config['realm'];
-                        kc.clientId = config['resource'];
-
-                        promise.setSuccess();
-                    } else {
-                        promise.setError();
-                    }
-                }
-            };
-
-            req.send();
-        } else {
-            if (!config['url']) {
-                var scripts = document.getElementsByTagName('script');
-                for (var i = 0; i < scripts.length; i++) {
-                    if (scripts[i].src.match(/.*keycloak\.js/)) {
-                        config.url = scripts[i].src.substr(0, scripts[i].src.indexOf('/js/keycloak.js'));
-                        break;
-                    }
-                }
-            }
-
-            if (!config.realm) {
-                throw 'realm missing';
-            }
-
-            if (!config.clientId) {
-                throw 'clientId missing';
-            }
-
-            kc.authServerUrl = config.url;
-            kc.realm = config.realm;
-            kc.clientId = config.clientId;
-
-            promise.setSuccess();
-        }
-
-        return promise.promise;
-    }
-
-    function clearToken() {
-        setToken(null, null);
-        kc.onAuthLogout && kc.onAuthLogout();
-    }
-
-    function setToken(token, refreshToken) {
-        if (token) {
-            kc.token = token;
-            kc.tokenParsed = JSON.parse(decodeURIComponent(escape(window.atob( token.split('.')[1] ))));
-            kc.authenticated = true;
-            kc.subject = kc.tokenParsed.sub;
-            kc.realmAccess = kc.tokenParsed.realm_access;
-            kc.resourceAccess = kc.tokenParsed.resource_access;
-
-            for (var i = 0; i < idTokenProperties.length; i++) {
-                var n = idTokenProperties[i];
-                if (kc.tokenParsed[n]) {
-                    if (!kc.idToken) {
-                        kc.idToken = {};
-                    }
-                    kc.idToken[n] = kc.tokenParsed[n];
-                }
-            }
-        } else {
-            delete kc.token;
-            delete kc.tokenParsed;
-            delete kc.subject;
-            delete kc.realmAccess;
-            delete kc.resourceAccess;
-            delete kc.idToken;
-
-            kc.authenticated = false;
-        }
-
-        if (refreshToken) {
-            kc.refreshToken = refreshToken;
-            kc.refreshTokenParsed = JSON.parse(atob(refreshToken.split('.')[1]));
-        } else {
-            delete kc.refreshToken;
-            delete kc.refreshTokenParsed;
-        }
-    }
-
-    function createUUID() {
-        var s = [];
-        var hexDigits = '0123456789abcdef';
-        for (var i = 0; i < 36; i++) {
-            s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
-        }
-        s[14] = '4';
-        s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
-        s[8] = s[13] = s[18] = s[23] = '-';
-        var uuid = s.join('');
-        return uuid;
-    }
-
-    function parseCallback(url) {
-        if (url.indexOf('?') != -1) {
-            var oauth = {};
-
-            var params = url.split('?')[1].split('&');
-            for (var i = 0; i < params.length; i++) {
-                var p = params[i].split('=');
-                switch (decodeURIComponent(p[0])) {
-                    case 'code':
-                        oauth.code = p[1];
-                        break;
-                    case 'error':
-                        oauth.error = p[1];
-                        break;
-                    case 'state':
-                        oauth.state = decodeURIComponent(p[1]);
-                        break;
-                    case 'redirect_fragment':
-                        oauth.fragment = decodeURIComponent(p[1]);
-                        break;
-                    case 'prompt':
-                        oauth.prompt = p[1];
-                        break;
-                }
-            }
-
-            if ((oauth.code || oauth.error) && oauth.state && oauth.state == sessionStorage.oauthState) {
-                delete sessionStorage.oauthState;
-                return oauth;
-            }
-        }
-    }
-
-    function createPromise() {
-        var p = {
-            setSuccess: function(result) {
-                p.success = true;
-                p.result = result;
-                if (p.successCallback) {
-                    p.successCallback(result);
-                }
-            },
-
-            setError: function(result) {
-                p.error = true;
-                p.result = result;
-                if (p.errorCallback) {
-                    p.errorCallback(result);
-                }
-            },
-
-            promise: {
-                success: function(callback) {
-                    if (p.success) {
-                        callback(p.result);
-                    } else if (!p.error) {
-                        p.successCallback = callback;
-                    }
-                    return p.promise;
-                },
-                error: function(callback) {
-                    if (p.error) {
-                        callback(p.result);
-                    } else if (!p.success) {
-                        p.errorCallback = callback;
-                    }
-                    return p.promise;
-                }
-            }
-        }
-        return p;
-    }
-
-    function loadAdapter(type) {
-        if (!type || type == 'default') {
-            return {
-                login: function(options) {
-                    window.location.href = kc.createLoginUrl(options);
-                },
-
-                logout: function(options) {
-                    window.location.href = kc.createLogoutUrl(options);
-                },
-
-                accountManagement : function() {
-                    window.location.href = kc.createAccountUrl();
-                },
-
-                redirectUri: function(options) {
-                    if (options && options.redirectUri) {
-                        return options.redirectUri;
-                    } else if (kc.redirectUri) {
-                        return kc.redirectUri;
-                    } else {
-                        var url = (location.protocol + '//' + location.hostname + (location.port && (':' + location.port)) + location.pathname);
-                        if (location.hash) {
-                            url += '?redirect_fragment=' + encodeURIComponent(location.hash.substring(1));
-                        }
-                        return url;
-                    }
-                }
-            };
-        }
-
-        if (type == 'cordova') {
-            console.debug('Enabling Cordova support');
-
-            return {
-                login: function(options) {
-                    var promise = createPromise();
-
-                    var o = 'location=no';
-                    if (options && options.prompt == 'none') {
-                        o += ',hidden=yes';
-                    }
-
-                    var loginUrl = kc.createLoginUrl(options);
-                    var ref = window.open(loginUrl, '_blank', o);
-
-                    var callback;
-                    var error;
-
-                    ref.addEventListener('loadstart', function(event) {
-                        if (event.url.indexOf('http://localhost') == 0) {
-                            callback = parseCallback(event.url);
-                            ref.close();
-                        }
-                    });
-
-                    ref.addEventListener('loaderror', function(event) {
-                        if (event.url.indexOf('http://localhost') != 0) {
-                            error = true;
-                            ref.close();
-                        }
-                    });
-
-                    ref.addEventListener('exit', function(event) {
-                        if (error || !callback) {
-                            promise.setError();
-                        } else {
-                            processCallback(callback, promise);
-                        }
-                    });
-
-                    return promise.promise;
-                },
-
-                logout: function(options) {
-                    var promise = createPromise();
-
-                    var logoutUrl = kc.createLogoutUrl(options);
-                    var ref = window.open(logoutUrl, '_blank', 'location=no,hidden=yes');
-
-                    var error;
-
-                    ref.addEventListener('loadstart', function(event) {
-                        if (event.url.indexOf('http://localhost') == 0) {
-                            ref.close();
-                        }
-                    });
-
-                    ref.addEventListener('loaderror', function(event) {
-                        if (event.url.indexOf('http://localhost') != 0) {
-                            error = true;
-                            ref.close();
-                        }
-                    });
-
-                    ref.addEventListener('exit', function(event) {
-                        if (error) {
-                            promise.setError();
-                        } else {
-                            clearToken();
-                            promise.setSuccess();
-                        }
-                    });
-
-                    return promise.promise;
-                },
-
-                accountManagement : function() {
-                    var accountUrl = kc.createAccountUrl();
-                    var ref = window.open(accountUrl, '_blank', 'location=no');
-                    ref.addEventListener('loadstart', function(event) {
-                        if (event.url.indexOf('http://localhost') == 0) {
-                            ref.close();
-                        }
-                    });
-                },
-
-                redirectUri: function(options) {
-                    return 'http://localhost';
-                }
-            }
-        }
-
-        throw 'invalid adapter type: ' + type;
-    }
-
-    var idTokenProperties = [
-        "name", 
-        "given_name", 
-        "family_name", 
-        "middle_name", 
-        "nickname", 
-        "preferred_username", 
-        "profile", 
-        "picture", 
-        "website", 
-        "email", 
-        "email_verified", 
-        "gender", 
-        "birthdate", 
-        "zoneinfo", 
-        "locale", 
-        "phone_number", 
-        "phone_number_verified", 
-        "address", 
-        "updated_at", 
-        "formatted", 
-        "street_address", 
-        "locality", 
-        "region", 
-        "postal_code", 
-        "country", 
-        "claims_locales"
-    ]
-}
+var Keycloak = function (config) {
+    if (!(this instanceof Keycloak)) {
+        return new Keycloak(config);
+    }
+
+    var kc = this;
+    var adapter;
+
+    kc.callbackMap = new Object();
+
+
+
+    kc.init = function (init) {
+        kc.authenticated = false;
+
+        if (window.Cordova) {
+            adapter = loadAdapter('cordova');
+        } else {
+            adapter = loadAdapter();
+        }
+
+        var promise = createPromise();
+
+        var initPromise = createPromise();
+        initPromise.promise.success(function() {
+            kc.onReady && kc.onReady(kc.authenticated);
+            promise.setSuccess(kc.authenticated);
+        }).error(function() {
+            promise.setError();
+        });
+
+        var configPromise = loadConfig(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 {
+                initPromise.setSuccess();
+            }
+        }
+
+        configPromise.success(processInit);
+        configPromise.error(function() {
+            promise.setError();
+        });
+
+        return promise.promise;
+    }
+
+    kc.login = function (options) {
+        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);
+
+        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;
+        kc.iframe.contentWindow.postMessage(msg, kc.iframe.contentWindow.location.origin);
+    }
+
+    kc.createLoginUrl = function(options) {
+        var state = createUUID();
+
+        var redirectUri = adapter.redirectUri(options);
+        if (options && options.prompt) {
+            if (redirectUri.indexOf('?') == -1) {
+                redirectUri += '?prompt=' + options.prompt;
+            } else {
+                redirectUri += '&prompt=' + options.prompt;
+            }
+        }
+
+        sessionStorage.oauthState = state;
+
+        var url = getRealmUrl()
+            + '/tokens/login'
+            + '?client_id=' + encodeURIComponent(kc.clientId)
+            + '&redirect_uri=' + encodeURIComponent(redirectUri)
+            + '&state=' + encodeURIComponent(state)
+            + '&response_type=code';
+
+        if (options && options.prompt) {
+            url += '&prompt=' + options.prompt;
+        }
+
+        return url;
+    }
+
+    kc.logout = function(options) {
+        return adapter.logout(options);
+    }
+
+    kc.createLogoutUrl = function(options) {
+        var url = getRealmUrl()
+            + '/tokens/logout'
+            + '?redirect_uri=' + encodeURIComponent(adapter.redirectUri(options));
+
+        return url;
+    }
+
+    kc.createAccountUrl = function(options) {
+        var url = getRealmUrl()
+            + '/account'
+            + '?referrer=' + kc.clientId
+            + '&referrer_uri=' + encodeURIComponent(adapter.redirectUri(options));
+
+        return url;
+    }
+
+    kc.accountManagement = function() {
+        return adapter.accountManagement();
+    }
+
+    kc.hasRealmRole = function (role) {
+        var access = kc.realmAccess;
+        return access && access.roles.indexOf(role) >= 0 || false;
+    }
+
+    kc.hasResourceRole = function(role, resource) {
+        if (!kc.resourceAccess) {
+            return false;
+        }
+
+        var access = kc.resourceAccess[resource || kc.clientId];
+        return access && access.roles.indexOf(role) >= 0 || false;
+    }
+
+    kc.loadUserProfile = function() {
+        var url = getRealmUrl() + '/account';
+        var req = new XMLHttpRequest();
+        req.open('GET', url, true);
+        req.setRequestHeader('Accept', 'application/json');
+        req.setRequestHeader('Authorization', 'bearer ' + kc.token);
+
+        var promise = createPromise();
+
+        req.onreadystatechange = function () {
+            if (req.readyState == 4) {
+                if (req.status == 200) {
+                    kc.profile = JSON.parse(req.responseText);
+                    promise.setSuccess(kc.profile);
+                } else {
+                    promise.setError();
+                }
+            }
+        }
+
+        req.send();
+
+        return promise.promise;
+    }
+
+    kc.isTokenExpired = function(minValidity) {
+        if (!kc.tokenParsed || !kc.refreshToken) {
+            throw 'Not authenticated';
+        }
+
+        var expiresIn = kc.tokenParsed['exp'] - (new Date().getTime() / 1000);
+        if (minValidity) {
+            expiresIn -= minValidity;
+        }
+
+        return expiresIn < 0;
+    }
+
+    kc.updateToken = function(minValidity) {
+        if (!kc.tokenParsed || !kc.refreshToken) {
+            throw 'Not authenticated';
+        }
+
+        var promise = createPromise();
+
+        if (minValidity) {
+            if (!kc.isTokenExpired(minValidity)) {
+                promise.setSuccess(false);
+                return promise.promise;
+            }
+        }
+
+        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');
+
+        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);
+
+        return promise.promise;
+    }
+
+    function getRealmUrl() {
+        return kc.authServerUrl + '/realms/' + encodeURIComponent(kc.realm);
+    }
+
+    function processCallback(oauth, promise) {
+        var code = oauth.code;
+        var error = oauth.error;
+        var prompt = oauth.prompt;
+
+        if (code) {
+            var params = 'code=' + code;
+            var url = getRealmUrl() + '/tokens/access/codes';
+
+            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);
+            }
+
+            req.withCredentials = true;
+
+            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.onAuthSuccess && kc.onAuthSuccess();
+                        promise && promise.setSuccess();
+                    } else {
+                        kc.onAuthError && kc.onAuthError();
+                        promise && promise.setError();
+                    }
+                }
+            };
+
+            req.send(params);
+        } else if (error) {
+            if (prompt != 'none') {
+                kc.onAuthError && kc.onAuthError();
+                promise && promise.setError();
+            } else {
+                promise && promise.setSuccess();
+            }
+        }
+    }
+
+    function loadConfig(url) {
+        var promise = createPromise();
+        var configUrl;
+
+        if (!config) {
+            configUrl = 'keycloak.json';
+        } else if (typeof config === 'string') {
+            configUrl = config;
+        }
+
+        if (configUrl) {
+            var req = new XMLHttpRequest();
+            req.open('GET', configUrl, true);
+            req.setRequestHeader('Accept', 'application/json');
+
+            req.onreadystatechange = function () {
+                if (req.readyState == 4) {
+                    if (req.status == 200) {
+                        var config = JSON.parse(req.responseText);
+
+                        kc.authServerUrl = config['auth-server-url'];
+                        kc.realm = config['realm'];
+                        kc.clientId = config['resource'];
+
+                        promise.setSuccess();
+                    } else {
+                        promise.setError();
+                    }
+                }
+            };
+
+            req.send();
+        } else {
+            if (!config['url']) {
+                var scripts = document.getElementsByTagName('script');
+                for (var i = 0; i < scripts.length; i++) {
+                    if (scripts[i].src.match(/.*keycloak\.js/)) {
+                        config.url = scripts[i].src.substr(0, scripts[i].src.indexOf('/js/keycloak.js'));
+                        break;
+                    }
+                }
+            }
+
+            if (!config.realm) {
+                throw 'realm missing';
+            }
+
+            if (!config.clientId) {
+                throw 'clientId missing';
+            }
+
+            kc.authServerUrl = config.url;
+            kc.realm = config.realm;
+            kc.clientId = config.clientId;
+
+            promise.setSuccess();
+        }
+
+        return promise.promise;
+    }
+
+    function clearToken() {
+        setToken(null, null);
+        kc.onAuthLogout && kc.onAuthLogout();
+    }
+
+    function setToken(token, refreshToken) {
+        if (token) {
+            kc.token = token;
+            kc.tokenParsed = JSON.parse(decodeURIComponent(escape(window.atob( token.split('.')[1] ))));
+            var sessionId = kc.realm + '-' + kc.tokenParsed.sub;
+            if (kc.tokenParsed.session_state) {
+                sessionId = sessionId + '-' + kc.tokenParsed.session_state;
+            }
+            kc.sessionId = sessionId;
+            kc.authenticated = true;
+            kc.subject = kc.tokenParsed.sub;
+            kc.realmAccess = kc.tokenParsed.realm_access;
+            kc.resourceAccess = kc.tokenParsed.resource_access;
+
+            for (var i = 0; i < idTokenProperties.length; i++) {
+                var n = idTokenProperties[i];
+                if (kc.tokenParsed[n]) {
+                    if (!kc.idToken) {
+                        kc.idToken = {};
+                    }
+                    kc.idToken[n] = kc.tokenParsed[n];
+                }
+            }
+        } else {
+            delete kc.token;
+            delete kc.tokenParsed;
+            delete kc.subject;
+            delete kc.realmAccess;
+            delete kc.resourceAccess;
+            delete kc.idToken;
+
+            kc.authenticated = false;
+        }
+
+        if (refreshToken) {
+            kc.refreshToken = refreshToken;
+            kc.refreshTokenParsed = JSON.parse(atob(refreshToken.split('.')[1]));
+        } else {
+            delete kc.refreshToken;
+            delete kc.refreshTokenParsed;
+        }
+    }
+
+    function createUUID() {
+        var s = [];
+        var hexDigits = '0123456789abcdef';
+        for (var i = 0; i < 36; i++) {
+            s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
+        }
+        s[14] = '4';
+        s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
+        s[8] = s[13] = s[18] = s[23] = '-';
+        var uuid = s.join('');
+        return uuid;
+    }
+
+    kc.callback_id = 0;
+
+    function createCallbackId() {
+        var id = '<id: ' + (kc.callback_id++) + (Math.random()) + '>';
+        return id;
+
+    }
+
+    function parseCallback(url) {
+        if (url.indexOf('?') != -1) {
+            var oauth = {};
+
+            var params = url.split('?')[1].split('&');
+            for (var i = 0; i < params.length; i++) {
+                var p = params[i].split('=');
+                switch (decodeURIComponent(p[0])) {
+                    case 'code':
+                        oauth.code = p[1];
+                        break;
+                    case 'error':
+                        oauth.error = p[1];
+                        break;
+                    case 'state':
+                        oauth.state = decodeURIComponent(p[1]);
+                        break;
+                    case 'redirect_fragment':
+                        oauth.fragment = decodeURIComponent(p[1]);
+                        break;
+                    case 'prompt':
+                        oauth.prompt = p[1];
+                        break;
+                }
+            }
+
+            if ((oauth.code || oauth.error) && oauth.state && oauth.state == sessionStorage.oauthState) {
+                delete sessionStorage.oauthState;
+                return oauth;
+            }
+        }
+    }
+
+    function createPromise() {
+        var p = {
+            setSuccess: function(result) {
+                p.success = true;
+                p.result = result;
+                if (p.successCallback) {
+                    p.successCallback(result);
+                }
+            },
+
+            setError: function(result) {
+                p.error = true;
+                p.result = result;
+                if (p.errorCallback) {
+                    p.errorCallback(result);
+                }
+            },
+
+            promise: {
+                success: function(callback) {
+                    if (p.success) {
+                        callback(p.result);
+                    } else if (!p.error) {
+                        p.successCallback = callback;
+                    }
+                    return p.promise;
+                },
+                error: function(callback) {
+                    if (p.error) {
+                        callback(p.result);
+                    } else if (!p.success) {
+                        p.errorCallback = callback;
+                    }
+                    return p.promise;
+                }
+            }
+        }
+        return p;
+    }
+
+    function loadAdapter(type) {
+        if (!type || type == 'default') {
+            return {
+                login: function(options) {
+                    window.location.href = kc.createLoginUrl(options);
+                },
+
+                logout: function(options) {
+                    window.location.href = kc.createLogoutUrl(options);
+                },
+
+                accountManagement : function() {
+                    window.location.href = kc.createAccountUrl();
+                },
+
+                redirectUri: function(options) {
+                    if (options && options.redirectUri) {
+                        return options.redirectUri;
+                    } else if (kc.redirectUri) {
+                        return kc.redirectUri;
+                    } else {
+                        var url = (location.protocol + '//' + location.hostname + (location.port && (':' + location.port)) + location.pathname);
+                        if (location.hash) {
+                            url += '?redirect_fragment=' + encodeURIComponent(location.hash.substring(1));
+                        }
+                        return url;
+                    }
+                }
+            };
+        }
+
+        if (type == 'cordova') {
+            console.debug('Enabling Cordova support');
+
+            return {
+                login: function(options) {
+                    var promise = createPromise();
+
+                    var o = 'location=no';
+                    if (options && options.prompt == 'none') {
+                        o += ',hidden=yes';
+                    }
+
+                    var loginUrl = kc.createLoginUrl(options);
+                    var ref = window.open(loginUrl, '_blank', o);
+
+                    var callback;
+                    var error;
+
+                    ref.addEventListener('loadstart', function(event) {
+                        if (event.url.indexOf('http://localhost') == 0) {
+                            callback = parseCallback(event.url);
+                            ref.close();
+                        }
+                    });
+
+                    ref.addEventListener('loaderror', function(event) {
+                        if (event.url.indexOf('http://localhost') != 0) {
+                            error = true;
+                            ref.close();
+                        }
+                    });
+
+                    ref.addEventListener('exit', function(event) {
+                        if (error || !callback) {
+                            promise.setError();
+                        } else {
+                            processCallback(callback, promise);
+                        }
+                    });
+
+                    return promise.promise;
+                },
+
+                logout: function(options) {
+                    var promise = createPromise();
+
+                    var logoutUrl = kc.createLogoutUrl(options);
+                    var ref = window.open(logoutUrl, '_blank', 'location=no,hidden=yes');
+
+                    var error;
+
+                    ref.addEventListener('loadstart', function(event) {
+                        if (event.url.indexOf('http://localhost') == 0) {
+                            ref.close();
+                        }
+                    });
+
+                    ref.addEventListener('loaderror', function(event) {
+                        if (event.url.indexOf('http://localhost') != 0) {
+                            error = true;
+                            ref.close();
+                        }
+                    });
+
+                    ref.addEventListener('exit', function(event) {
+                        if (error) {
+                            promise.setError();
+                        } else {
+                            clearToken();
+                            promise.setSuccess();
+                        }
+                    });
+
+                    return promise.promise;
+                },
+
+                accountManagement : function() {
+                    var accountUrl = kc.createAccountUrl();
+                    var ref = window.open(accountUrl, '_blank', 'location=no');
+                    ref.addEventListener('loadstart', function(event) {
+                        if (event.url.indexOf('http://localhost') == 0) {
+                            ref.close();
+                        }
+                    });
+                },
+
+                redirectUri: function(options) {
+                    return 'http://localhost';
+                }
+            }
+        }
+
+        throw 'invalid adapter type: ' + type;
+    }
+
+    var idTokenProperties = [
+        "name", 
+        "given_name", 
+        "family_name", 
+        "middle_name", 
+        "nickname", 
+        "preferred_username", 
+        "profile", 
+        "picture", 
+        "website", 
+        "email", 
+        "email_verified", 
+        "gender", 
+        "birthdate", 
+        "zoneinfo", 
+        "locale", 
+        "phone_number", 
+        "phone_number_verified", 
+        "address", 
+        "updated_at", 
+        "formatted", 
+        "street_address", 
+        "locality", 
+        "region", 
+        "postal_code", 
+        "country", 
+        "claims_locales"
+    ]
+}
diff --git a/integration/js/src/main/resources/login-status-iframe.html b/integration/js/src/main/resources/login-status-iframe.html
new file mode 100755
index 0000000..a8c6409
--- /dev/null
+++ b/integration/js/src/main/resources/login-status-iframe.html
@@ -0,0 +1,31 @@
+<script>
+    function getCookie(cname)
+    {
+        var name = cname + "=";
+        var ca = document.cookie.split(';');
+        for(var i=0; i<ca.length; i++)
+        {
+            var c = ca[i].trim();
+            if (c.indexOf(name)==0) return c.substring(name.length,c.length);
+        }
+        return null;
+    }
+    function receiveMessage(event)
+    {
+        if (event.origin !== "ORIGIN") {
+            console.log(event.origin + " does not match built origin");
+            return;
+
+        }
+        event.data.loggedIn = false;
+        var cookie = getCookie('KEYCLOAK_SESSION');
+        if (cookie) {
+            event.data.loggedIn = true;
+            event.data.session = cookie;
+        }
+
+        event.source.postMessage(event.data,
+                event.origin);
+    }
+    window.addEventListener("message", receiveMessage, false);
+</script>
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
index c8df10c..ce5afb7 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -27,6 +27,7 @@ import javax.ws.rs.core.Cookie;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.NewCookie;
+import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 import java.net.URI;
 import java.util.HashSet;
@@ -41,7 +42,10 @@ import java.util.Set;
 public class AuthenticationManager {
     protected static Logger logger = Logger.getLogger(AuthenticationManager.class);
     public static final String FORM_USERNAME = "username";
+    // used for auth login
     public static final String KEYCLOAK_IDENTITY_COOKIE = "KEYCLOAK_IDENTITY";
+    // used solely to determine is user is logged in
+    public static final String KEYCLOAK_SESSION_COOKIE = "KEYCLOAK_SESSION";
     public static final String KEYCLOAK_REMEMBER_ME = "KEYCLOAK_REMEMBER_ME";
 
     protected ProviderSession providerSession;
@@ -72,18 +76,11 @@ public class AuthenticationManager {
         return token;
     }
 
-    public NewCookie createLoginCookie(RealmModel realm, UserModel user, UserSessionModel session, UriInfo uriInfo, boolean rememberMe) {
+    public void createLoginCookie(Response.ResponseBuilder builder, RealmModel realm, UserModel user, UserSessionModel session, UriInfo uriInfo, boolean rememberMe) {
         logger.info("createLoginCookie");
         String cookieName = KEYCLOAK_IDENTITY_COOKIE;
         String cookiePath = getIdentityCookiePath(realm, uriInfo);
-        return createLoginCookie(realm, user, session, null, cookieName, cookiePath, rememberMe);
-    }
-
-    protected NewCookie createLoginCookie(RealmModel realm, UserModel user, UserSessionModel session, ClientModel client, String cookieName, String cookiePath, boolean rememberMe) {
         AccessToken identityToken = createIdentityToken(realm, user, session);
-        if (client != null) {
-            identityToken.issuedFor(client.getClientId());
-        }
         String encoded = encodeToken(realm, identityToken);
         boolean secureOnly = !realm.isSslNotRequired();
         logger.debugv("creatingLoginCookie - name: {0} path: {1}", cookieName, cookiePath);
@@ -92,8 +89,14 @@ public class AuthenticationManager {
             maxAge = realm.getCentralLoginLifespan();
             logger.info("createLoginCookie maxAge: " + maxAge);
         }
-        NewCookie cookie = new NewCookie(cookieName, encoded, cookiePath, null, null, maxAge, secureOnly);// todo httponly , true);
-        return cookie;
+        builder.cookie(new NewCookie(cookieName, encoded, cookiePath, null, null, maxAge, secureOnly));// todo httponly , true);
+
+        String sessionCookieValue = realm.getName() + "-" + user.getId();
+        if (session != null) {
+            sessionCookieValue += "-" + session.getId();
+        }
+        builder.cookie(new NewCookie(KEYCLOAK_SESSION_COOKIE, sessionCookieValue, cookiePath, null, null, maxAge, secureOnly));// todo httponly , true);
+
     }
 
     public NewCookie createRememberMeCookie(RealmModel realm, UriInfo uriInfo) {
@@ -116,6 +119,7 @@ public class AuthenticationManager {
         String path = getIdentityCookiePath(realm, uriInfo);
         String cookieName = KEYCLOAK_IDENTITY_COOKIE;
         expireCookie(cookieName, path);
+        expireCookie(KEYCLOAK_SESSION_COOKIE, path);
     }
     public void expireRememberMeCookie(RealmModel realm, UriInfo uriInfo) {
         logger.debug("Expiring remember me cookie");
diff --git a/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java b/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java
index a5c0abe..d436318 100755
--- a/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java
+++ b/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java
@@ -93,7 +93,7 @@ public class OAuthFlows {
             Response.ResponseBuilder location = Response.status(302).location(redirectUri.build());
             Cookie remember = request.getHttpHeaders().getCookies().get(AuthenticationManager.KEYCLOAK_REMEMBER_ME);
             rememberMe = rememberMe || remember != null;
-            location.cookie(authManager.createLoginCookie(realm, accessCode.getUser(), session, uriInfo, rememberMe));
+            authManager.createLoginCookie(location, realm, accessCode.getUser(), session, uriInfo, rememberMe);
             return location.build();
         }
     }
diff --git a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
index 0c1c8ae..a5878d2 100755
--- a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
@@ -1,10 +1,13 @@
 package org.keycloak.services.resources;
 
 import org.jboss.logging.Logger;
+import org.jboss.resteasy.annotations.cache.NoCache;
 import org.jboss.resteasy.spi.NotFoundException;
 import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.jboss.resteasy.spi.UnauthorizedException;
 import org.keycloak.audit.Audit;
 import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.ClientModel;
 import org.keycloak.models.Constants;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
@@ -16,13 +19,21 @@ import org.keycloak.services.managers.BruteForceProtector;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.services.managers.SocialRequestManager;
 import org.keycloak.services.managers.TokenManager;
+import org.keycloak.util.StreamUtil;
 
+import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Set;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -74,6 +85,63 @@ public class RealmsResource {
     public static UriBuilder accountUrl(UriBuilder base) {
         return base.path(RealmsResource.class).path(RealmsResource.class, "getAccountService");
     }
+
+    /**
+     *
+     *
+     * @param name
+     * @param client_id
+     * @return
+     */
+    @Path("{realm}/login-status-iframe.html")
+    @GET
+    @Produces(MediaType.TEXT_HTML)
+    @NoCache
+    public String getLoginStatusIframe(final @PathParam("realm") String name,
+                                       @QueryParam("client_id") String client_id) {
+        AuthenticationManager auth = new AuthenticationManager(providers);
+
+        //logger.info("getting login-status-iframe.html for client_id: " + client_id);
+        RealmManager realmManager = new RealmManager(session);
+        RealmModel realm = locateRealm(name, realmManager);
+        ClientModel client = realm.findClient(client_id);
+        if (client == null) {
+            throw new NotFoundException("could not find client: " + client_id);
+        }
+        AuthenticationManager.AuthResult result = auth.authenticateIdentityCookie(realm, uriInfo, headers);
+        if (result == null) {
+            throw new UnauthorizedException("not logged in, can't get page");
+        }
+
+        InputStream is = getClass().getClassLoader().getResourceAsStream("login-status-iframe.html");
+        if (is == null) throw new NotFoundException("Could not find login-status-iframe.html ");
+        Set<String> redirectUris = TokenService.resolveValidRedirects(uriInfo, client.getRedirectUris());
+        String origin = null;
+        for (String redirect : redirectUris) {
+
+            int index = redirect.indexOf("://");
+            if (index == -1) continue;
+            index = redirect.indexOf('/', index + 3);
+            if (index == -1) {
+                origin = redirect;
+            } else {
+                origin = redirect.substring(0, index);
+            }
+            break;
+
+        }
+        String file = null;
+        try {
+            file = StreamUtil.readString(is);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+        file = file.replace("ORIGIN", origin);
+        //System.out.println(file);
+        return file;
+
+    }
+
     @Path("{realm}/tokens")
     public TokenService getTokenService(final @PathParam("realm") String name) {
         RealmManager realmManager = new RealmManager(session);
diff --git a/services/src/main/java/org/keycloak/services/resources/TokenService.java b/services/src/main/java/org/keycloak/services/resources/TokenService.java
index 4054953..aeda045 100755
--- a/services/src/main/java/org/keycloak/services/resources/TokenService.java
+++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java
@@ -968,21 +968,8 @@ public class TokenService {
             return redirectUri;
         } else {
             String r = redirectUri.indexOf('?') != -1 ? redirectUri.substring(0, redirectUri.indexOf('?')) : redirectUri;
+            Set<String> resolveValidRedirects = resolveValidRedirects(uriInfo, validRedirects);
 
-            // If the valid redirect URI is relative (no scheme, host, port) then use the request's scheme, host, and port
-            Set<String> resolveValidRedirects = new HashSet<String>();
-            for (String validRedirect : validRedirects) {
-                if (validRedirect.startsWith("/")) {
-                    URI baseUri = uriInfo.getBaseUri();
-                    String uri = baseUri.getScheme() + "://" + baseUri.getHost();
-                    if (baseUri.getPort() != -1) {
-                        uri += ":" + baseUri.getPort();
-                    }
-                    validRedirect = uri + validRedirect;
-                    logger.debugv("replacing relative valid redirect with: {0}", validRedirect);
-                }
-                resolveValidRedirects.add(validRedirect);
-            }
 
             boolean valid = matchesRedirects(resolveValidRedirects, r);
 
@@ -1005,6 +992,24 @@ public class TokenService {
         }
     }
 
+    public static Set<String> resolveValidRedirects(UriInfo uriInfo, Set<String> validRedirects) {
+        // If the valid redirect URI is relative (no scheme, host, port) then use the request's scheme, host, and port
+        Set<String> resolveValidRedirects = new HashSet<String>();
+        for (String validRedirect : validRedirects) {
+            if (validRedirect.startsWith("/")) {
+                URI baseUri = uriInfo.getBaseUri();
+                String uri = baseUri.getScheme() + "://" + baseUri.getHost();
+                if (baseUri.getPort() != -1) {
+                    uri += ":" + baseUri.getPort();
+                }
+                validRedirect = uri + validRedirect;
+                logger.debugv("replacing relative valid redirect with: {0}", validRedirect);
+            }
+            resolveValidRedirects.add(validRedirect);
+        }
+        return resolveValidRedirects;
+    }
+
     private boolean checkSsl() {
         if (realm.isSslNotRequired()) {
             return true;