keycloak-aplcache

Changes

Details

diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/js/app.js b/admin-ui/src/main/resources/META-INF/resources/admin/js/app.js
index bfb76f7..bce292e 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/js/app.js
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/js/app.js
@@ -176,6 +176,21 @@ module.config([ '$routeProvider', function($routeProvider) {
             },
             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', {
             templateUrl : 'partials/user-list.html',
             resolve : {
@@ -292,6 +307,21 @@ module.config([ '$routeProvider', function($routeProvider) {
             },
             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 : {
@@ -540,6 +570,18 @@ module.config([ '$routeProvider', function($routeProvider) {
             },
             controller : 'RealmRevocationCtrl'
         })
+        .when('/realms/:realm/sessions/realm', {
+            templateUrl : 'partials/session-realm.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                stats : function(RealmSessionStatsLoader) {
+                    return RealmSessionStatsLoader();
+                }
+            },
+            controller : 'RealmSessionStatsCtrl'
+        })
 
         .otherwise({
             templateUrl : 'partials/notfound.html'
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/applications.js b/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/applications.js
index 27607e9..d7416a0 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/applications.js
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/applications.js
@@ -43,6 +43,45 @@ module.controller('ApplicationCredentialsCtrl', function($scope, $location, real
     });
 });
 
+module.controller('ApplicationSessionsCtrl', function($scope, realm, stats, application,
+                                                      ApplicationLogoutUser,
+                                                      ApplicationLogoutAll,
+                                                      ApplicationSessionStats,
+                                                      ApplicationSessionStatsWithUsers,
+                                                    $location, Dialog, Notifications) {
+    $scope.realm = realm;
+    $scope.stats = stats;
+    $scope.users = {};
+    $scope.application = application;
+
+    $scope.toDate = function(val) {
+        return new Date(val);
+    };
+
+    $scope.loadUsers = function() {
+        ApplicationSessionStatsWithUsers.get({ realm : realm.realm, application: $scope.application.name }, function(updated) {
+            $scope.stats = updated;
+            $scope.users = updated.users;
+        })
+    };
+
+    $scope.logoutAll = function() {
+        ApplicationLogoutAll.save({realm : realm.realm, application: $scope.application.name}, function () {
+            Notifications.success('Logged out all users');
+            $scope.loadUsers();
+        });
+    };
+
+    $scope.logoutUser = function(user) {
+        console.log('Trying to logout user: ' + user);
+        ApplicationLogoutUser.save({realm : realm.realm, application: $scope.application.name, user: user}, function () {
+            Notifications.success('Logged out user' + user);
+            $scope.loadUsers();
+        });
+    };
+
+});
+
 module.controller('ApplicationClaimsCtrl', function($scope, realm, application, claims,
                                                         ApplicationClaims,
                                                         $http, $location, Dialog, Notifications) {
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js b/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js
index cb0fa24..d2e4e68 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js
@@ -690,6 +690,26 @@ module.controller('RealmKeysDetailCtrl', function($scope, Realm, realm, $http, $
     };
 });
 
+module.controller('RealmSessionStatsCtrl', function($scope, realm, stats, RealmSessionStats, RealmLogoutAll, Notifications) {
+    $scope.realm = realm;
+    $scope.stats = stats;
+
+    console.log(stats);
+
+    $scope.logoutAll = function() {
+        RealmLogoutAll.save({realm : realm.realm}, function () {
+            Notifications.success('Logged out all users');
+            RealmSessionStats.get({realm: realm.realm}, function(updated) {
+                Notifications.success('Logged out all users');
+                $scope.stats = updated;
+            })
+        });
+    };
+
+
+});
+
+
 module.controller('RealmRevocationCtrl', function($scope, Realm, RealmPushRevocation, realm, $http, $location, Dialog, Notifications) {
     $scope.realm = angular.copy(realm);
 
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/users.js b/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/users.js
index b0048a9..178e26b 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/users.js
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/users.js
@@ -118,6 +118,35 @@ module.controller('UserRoleMappingCtrl', function($scope, $http, realm, user, ro
 
 });
 
+module.controller('UserSessionsCtrl', function($scope, realm, user, stats, UserLogout, ApplicationLogoutUser, UserSessionStats, Notifications) {
+    $scope.realm = realm;
+    $scope.user = user;
+    $scope.stats = stats;
+
+    $scope.logoutAll = function() {
+        UserLogout.save({realm : realm.realm, user: user.username}, function () {
+            Notifications.success('Logged out user in all applications');
+            UserSessionStats.get({realm: realm.realm, user: user.username}, function(updated) {
+                $scope.stats = updated;
+            })
+        });
+    };
+
+    $scope.logoutApplication = function(app) {
+        console.log('log user out of app: ' + app);
+        ApplicationLogoutUser.save({realm : realm.realm, application: app, user: user.username}, function () {
+            Notifications.success('Logged out user from application');
+            UserSessionStats.get({realm: realm.realm, user: user.username}, function(updated) {
+                $scope.stats = updated;
+            })
+        });
+    };
+
+
+
+});
+
+
 module.controller('UserListCtrl', function($scope, realm, User) {
     $scope.realm = realm;
     $scope.searchQuery = function() {
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/js/loaders.js b/admin-ui/src/main/resources/META-INF/resources/admin/js/loaders.js
index d5b3917..84f8dbc 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/js/loaders.js
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/js/loaders.js
@@ -48,20 +48,37 @@ module.factory('RealmLoader', function(Loader, Realm, $route, $q) {
 });
 
 module.factory('UserListLoader', function(Loader, User, $route, $q) {
-	return Loader.query(User, function() {
-		return {
-			realm : $route.current.params.realm
-		}
-	});
+    return Loader.query(User, function() {
+        return {
+            realm : $route.current.params.realm
+        }
+    });
+});
+
+module.factory('RealmSessionStatsLoader', function(Loader, RealmSessionStats, $route, $q) {
+    return Loader.get(RealmSessionStats, function() {
+        return {
+            realm : $route.current.params.realm
+        }
+    });
 });
 
 module.factory('UserLoader', function(Loader, User, $route, $q) {
-	return Loader.get(User, function() {
-		return {
-			realm : $route.current.params.realm,
-			userId : $route.current.params.user
-		}
-	});
+    return Loader.get(User, function() {
+        return {
+            realm : $route.current.params.realm,
+            userId : $route.current.params.user
+        }
+    });
+});
+
+module.factory('UserSessionStatsLoader', function(Loader, UserSessionStats, $route, $q) {
+    return Loader.get(UserSessionStats, function() {
+        return {
+            realm : $route.current.params.realm,
+            user : $route.current.params.user
+        }
+    });
 });
 
 module.factory('RoleLoader', function(Loader, Role, $route, $q) {
@@ -91,6 +108,15 @@ module.factory('ApplicationRoleLoader', function(Loader, ApplicationRole, $route
     });
 });
 
+module.factory('ApplicationSessionStatsLoader', function(Loader, ApplicationSessionStats, $route, $q) {
+    return Loader.get(ApplicationSessionStats, function() {
+        return {
+            realm : $route.current.params.realm,
+            application : $route.current.params.application
+        }
+    });
+});
+
 module.factory('ApplicationClaimsLoader', function(Loader, ApplicationClaims, $route, $q) {
     return Loader.get(ApplicationClaims, function() {
         return {
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/js/services.js b/admin-ui/src/main/resources/META-INF/resources/admin/js/services.js
index 355091b..cb31b5e 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/js/services.js
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/js/services.js
@@ -147,14 +147,27 @@ module.factory('ServerInfo', function($resource) {
 });
 
 module.factory('User', function($resource) {
-	return $resource('/auth/rest/admin/realms/:realm/users/:userId', {
-		realm : '@realm',
-		userId : '@userId'
-	}, {
+    return $resource('/auth/rest/admin/realms/:realm/users/:userId', {
+        realm : '@realm',
+        userId : '@userId'
+    }, {
         update : {
             method : 'PUT'
         }
-	});
+    });
+});
+
+module.factory('UserSessionStats', function($resource) {
+    return $resource('/auth/rest/admin/realms/:realm/users/:user/session-stats', {
+        realm : '@realm',
+        user : '@user'
+    });
+});
+module.factory('UserLogout', function($resource) {
+    return $resource('/auth/rest/admin/realms/:realm/users/:user/logout', {
+        realm : '@realm',
+        user : '@user'
+    });
 });
 
 module.factory('UserCredentials', function($resource) {
@@ -241,6 +254,13 @@ module.factory('RealmPushRevocation', function($resource) {
     });
 });
 
+module.factory('RealmSessionStats', function($resource) {
+    return $resource('/auth/rest/admin/realms/:realm/session-stats', {
+        realm : '@realm'
+    });
+});
+
+
 module.factory('RoleApplicationComposites', function($resource) {
     return $resource('/auth/rest/admin/realms/:realm/roles-by-id/:role/composites/applications/:application', {
         realm : '@realm',
@@ -456,6 +476,7 @@ module.factory('ApplicationRole', function($resource) {
         }
     });
 });
+
 module.factory('ApplicationClaims', function($resource) {
     return $resource('/auth/rest/admin/realms/:realm/applications/:application/claims', {
         realm : '@realm',
@@ -467,6 +488,39 @@ module.factory('ApplicationClaims', function($resource) {
     });
 });
 
+module.factory('ApplicationSessionStats', function($resource) {
+    return $resource('/auth/rest/admin/realms/:realm/applications/:application/session-stats', {
+        realm : '@realm',
+        application : "@application"
+    });
+});
+
+module.factory('ApplicationSessionStatsWithUsers', function($resource) {
+    return $resource('/auth/rest/admin/realms/:realm/applications/:application/session-stats?users=true', {
+        realm : '@realm',
+        application : "@application"
+    });
+});
+
+module.factory('ApplicationLogoutAll', function($resource) {
+    return $resource('/auth/rest/admin/realms/:realm/applications/:application/logout-all', {
+        realm : '@realm',
+        application : "@application"
+    });
+});
+module.factory('ApplicationLogoutUser', function($resource) {
+    return $resource('/auth/rest/admin/realms/:realm/applications/:application/logout-user/:user', {
+        realm : '@realm',
+        application : "@application",
+        user : "@user"
+    });
+});
+module.factory('RealmLogoutAll', function($resource) {
+    return $resource('/auth/rest/admin/realms/:realm/logout-all', {
+        realm : '@realm'
+    });
+});
+
 module.factory('ApplicationPushRevocation', function($resource) {
     return $resource('/auth/rest/admin/realms/:realm/applications/:application/push-revocation', {
         realm : '@realm',
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-claims.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-claims.html
index 2b2e34b..f33e8cd 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-claims.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-claims.html
@@ -8,6 +8,7 @@
         <li class="active"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/claims">Claims</a></li>
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/scope-mappings">Scope</a></li>
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/revocation">Revocation</a></li>
+        <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
     </ul>
     <div id="content">
         <ol class="breadcrumb" data-ng-hide="create">
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-credentials.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-credentials.html
index e95bcc1..b3b9839 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-credentials.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-credentials.html
@@ -8,6 +8,7 @@
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/claims">Claims</a></li>
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/scope-mappings">Scope</a></li>
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/revocation">Revocation</a></li>
+        <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
     </ul>
     <div id="content">
         <ol class="breadcrumb" data-ng-hide="create">
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-detail.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-detail.html
index d8b1dc4..dc834c3 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-detail.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-detail.html
@@ -8,6 +8,7 @@
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/claims">Claims</a></li>
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/scope-mappings">Scope</a></li>
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/revocation">Revocation</a></li>
+        <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
     </ul>
     <div id="content">
         <ol class="breadcrumb" data-ng-show="create">
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-installation.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-installation.html
index 2caaa81..9c527d7 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-installation.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-installation.html
@@ -9,6 +9,7 @@
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/claims">Claims</a></li>
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/scope-mappings">Scope</a></li>
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/revocation">Revocation</a></li>
+        <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
     </ul>
 
     <div class="top-nav" data-ng-show="create">
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-revocation.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-revocation.html
index 8295782..6b7e951 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-revocation.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-revocation.html
@@ -8,6 +8,7 @@
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/claims">Claims</a></li>
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/scope-mappings">Scope</a></li>
         <li class="active"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/revocation">Revocation</a></li>
+        <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
     </ul>
     <div id="content">
         <ol class="breadcrumb">
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-role-detail.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-role-detail.html
index a7bae45..8e3d603 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-role-detail.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-role-detail.html
@@ -8,6 +8,7 @@
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/claims">Claims</a></li>
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/scope-mappings">Scope</a></li>
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/revocation">Revocation</a></li>
+        <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
     </ul>
 
     <div id="content">
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-role-list.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-role-list.html
index 49329fe..77b4065 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-role-list.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-role-list.html
@@ -9,6 +9,7 @@
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/claims">Claims</a></li>
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/scope-mappings">Scope</a></li>
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/revocation">Revocation</a></li>
+        <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
     </ul>
 
     <div id="content">
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-scope-mappings.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-scope-mappings.html
index 5be878c..ac6dd6f 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-scope-mappings.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-scope-mappings.html
@@ -9,6 +9,7 @@
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/claims">Claims</a></li>
         <li class="active"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/scope-mappings">Scope</a></li>
         <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/revocation">Revocation</a></li>
+        <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
     </ul>
 
     <div id="content">
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-sessions.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-sessions.html
new file mode 100755
index 0000000..129647c
--- /dev/null
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-sessions.html
@@ -0,0 +1,60 @@
+<div class="bs-sidebar col-md-3 clearfix" data-ng-include data-src="'partials/realm-menu.html'"></div>
+<div id="content-area" class="col-md-9" role="main">
+    <ul class="nav nav-tabs nav-tabs-pf">
+        <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}">Settings</a></li>
+        <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/credentials">Credentials</a></li>
+        <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/installation">Installation</a></li>
+        <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/roles">Roles</a></li>
+        <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/claims">Claims</a></li>
+        <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/scope-mappings">Scope</a></li>
+        <li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/revocation">Revocation</a></li>
+        <li class="active"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
+    </ul>
+    <div id="content">
+        <ol class="breadcrumb">
+            <li><a href="#/realms/{{realm.realm}}">{{application.name}}</a></li>
+            <li class="active">Application Sessions</li>
+        </ol>
+        <h2><span>{{application.name}}</span> Sessions</h2>
+        <form class="form-horizontal" name="sessionStats">
+            <fieldset class="border-top">
+                <div class="form-group">
+                    <label class="col-sm-2 control-label" for="activeSessions">Active Sessions</label>
+                    <div class="col-sm-4">
+                        <input class="form-control" type="text" id="activeSessions" name="activeSessions" data-ng-model="stats.activeSessions" ng-disabled="true">
+                    </div>
+                </div>
+                <div class="form-group">
+                    <label class="col-sm-2 control-label" for="activeUsers">Active Users</label>
+                    <div class="col-sm-4">
+                        <input class="form-control" type="text" id="activeUsers" name="activeUsers" data-ng-model="stats.activeUsers" ng-disabled="true">
+                    </div>
+                </div>
+            </fieldset>
+        </form>
+        <table class="table" data-ng-show="stats.activeSessions > 0">
+            <thead>
+            <tr>
+                <th class="kc-table-actions" colspan="3">
+                    <div class="pull-right">
+                        <a class="btn btn-primary" ng-click="logoutAll()">Logout All</a>
+                        <a class="btn btn-primary" ng-click="loadUsers()">Show Users</a>
+                    </div>
+                </th>
+            </tr>
+            <tr>
+                <th>User</th>
+                <th>Login Time</th>
+                <th></th>
+            </tr>
+            </thead>
+            <tbody>
+            <tr data-ng-repeat="(user, data) in users">
+                <td><a href="#/realms/{{realm.realm}}/users/{{user}}">{{user}}</a></td>
+                <td>{{data.whenLoggedIn | date:'medium'}}</td>
+                <td><a ng-click="logoutUser(user)">logout</a> </td>
+            </tr>
+            </tbody>
+        </table>
+    </div>
+</div>
\ No newline at end of file
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/role-mappings.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/role-mappings.html
index c290d7a..63e34da 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/role-mappings.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/role-mappings.html
@@ -4,6 +4,7 @@
         <li><a href="#/realms/{{realm.realm}}/users/{{user.username}}">Attributes</a></li>
         <li data-ng-show="access.manageUsers"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/user-credentials">Credentials</a></li>
         <li class="active"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/role-mappings">Role Mappings</a></li>
+        <li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/sessions">Sessions</a></li>
     </ul>
     <div id="content">
         <ol class="breadcrumb">
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/session-realm.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/session-realm.html
new file mode 100755
index 0000000..27a4e4e
--- /dev/null
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/session-realm.html
@@ -0,0 +1,37 @@
+<div class="bs-sidebar col-md-3 clearfix" data-ng-include data-src="'partials/realm-menu.html'"></div>
+<div id="content-area" class="col-md-9" role="main">
+    <ul class="nav nav-tabs nav-tabs-pf">
+        <li><a href="#/realms/{{realm.realm}}/sessions/revocation">Revocation</a></li>
+        <li class="active"><a href="#/realms/{{realm.realm}}/sessions/realm">Realm Sessions</a></li>
+    </ul>
+    <div id="content">
+        <ol class="breadcrumb">
+            <li><a href="#/realms/{{realm.realm}}">{{realm.realm}}</a></li>
+            <li class="active">Realm Sessions</li>
+        </ol>
+        <h2><span>{{realm.realm}}</span> Sessions</h2>
+        <table class="table">
+            <thead>
+            <tr>
+                <th class="kc-table-actions" colspan="3">
+                    <div class="pull-right">
+                        <a class="btn btn-primary" ng-click="logoutAll()">Logout All</a>
+                    </div>
+                </th>
+            </tr>
+            <tr>
+                <th>Application</th>
+                <th>Active Sessions</th>
+                <th>Active Users</th>
+            </tr>
+            </thead>
+            <tbody>
+            <tr data-ng-repeat="(application, data) in stats">
+                <td><a href="#/realms/{{realm.realm}}/applications/{{application}}/sessions">{{application}}</a></td>
+                <td>{{data.activeSessions}}</td>
+                <td>{{data.activeUsers}}</td>
+            </tr>
+            </tbody>
+        </table>
+    </div>
+</div>
\ No newline at end of file
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/session-revocation.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/session-revocation.html
index e8cf513..3ca24d0 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/session-revocation.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/session-revocation.html
@@ -2,6 +2,7 @@
 <div id="content-area" class="col-md-9" role="main">
     <ul class="nav nav-tabs nav-tabs-pf"  data-ng-show="!create">
         <li class="active"><a href="#/realms/{{realm.realm}}/sessions/revocation">Revocation</a></li>
+        <li><a href="#/realms/{{realm.realm}}/sessions/realm">Realm Sessions</a></li>
     </ul>
     <div id="content">
         <ol class="breadcrumb">
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/user-credentials.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/user-credentials.html
index 6d0fd70..38ca069 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/user-credentials.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/user-credentials.html
@@ -5,6 +5,7 @@
         <li><a href="#/realms/{{realm.realm}}/users/{{user.username}}">Attributes</a></li>
         <li class="active"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/user-credentials">Credentials</a></li>
         <li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/role-mappings">Role Mappings</a></li>
+        <li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/sessions">Sessions</a></li>
     </ul>
 
     <div id="content">
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/user-detail.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/user-detail.html
index 4ce093a..0894d18 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/user-detail.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/user-detail.html
@@ -5,6 +5,7 @@
         <li class="active"><a href="#/realms/{{realm.realm}}/users/{{user.username}}">Attributes</a></li>
         <li data-ng-show="access.manageUsers"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/user-credentials">Credentials</a></li>
         <li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/role-mappings">Role Mappings</a></li>
+        <li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/sessions">Sessions</a></li>
     </ul>
 
     <div id="content">
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/user-sessions.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/user-sessions.html
new file mode 100755
index 0000000..0011ad3
--- /dev/null
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/user-sessions.html
@@ -0,0 +1,41 @@
+<div class="bs-sidebar col-md-3 clearfix" data-ng-include data-src="'partials/realm-menu.html'"></div>
+<div id="content-area" class="col-md-9" role="main">
+    <ul class="nav nav-tabs nav-tabs-pf">
+        <li><a href="#/realms/{{realm.realm}}/users/{{user.username}}">Attributes</a></li>
+        <li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/user-credentials">Credentials</a></li>
+        <li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/role-mappings">Role Mappings</a></li>
+        <li class="active"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/sessions">Sessions</a></li>
+    </ul>
+    <div id="content">
+        <ol class="breadcrumb">
+            <li><a href="#/realms/{{realm.realm}}">{{realm.realm}}</a></li>
+            <li><a href="#/realms/{{realm.realm}}/users">Users</a></li>
+            <li><a href="#/realms/{{realm.realm}}/users/{{user.username}}">{{user.username}}</a></li>
+            <li class="active">User Sessions</li>
+        </ol>
+        <h2><span>{{user.username}}</span> Sessions</h2>
+        <table class="table">
+            <thead>
+            <tr>
+                <th class="kc-table-actions" colspan="3">
+                    <div class="pull-right">
+                        <a class="btn btn-primary" ng-click="logoutAll()">Logout</a>
+                    </div>
+                </th>
+            </tr>
+            <tr>
+                <th>Application</th>
+                <th>Login Time</th>
+                <th></th>
+            </tr>
+            </thead>
+            <tbody>
+            <tr data-ng-repeat="(application, data) in stats">
+                <td><a href="#/realms/{{realm.realm}}/applications/{{application}}/sessions">{{application}}</a></td>
+                <td>{{data.whenLoggedIn | date:'medium'}}</td>
+                <td><a ng-click="logoutApplication(application)">logout</a> </td>
+            </tr>
+            </tbody>
+        </table>
+    </div>
+</div>
\ No newline at end of file
diff --git a/core/src/main/java/org/keycloak/adapters/AdapterConstants.java b/core/src/main/java/org/keycloak/adapters/AdapterConstants.java
index 93ddfa7..b03f09e 100755
--- a/core/src/main/java/org/keycloak/adapters/AdapterConstants.java
+++ b/core/src/main/java/org/keycloak/adapters/AdapterConstants.java
@@ -9,6 +9,8 @@ public interface AdapterConstants {
     // URL endpoints
     public static final String K_LOGOUT = "k_logout";
     public static final String K_PUSH_NOT_BEFORE = "k_push_not_before";
+    public static final String K_GET_USER_STATS = "k_get_user_stats";
+    public static final String K_GET_SESSION_STATS = "k_get_session_stats";
     public static final String K_QUERY_BEARER_TOKEN = "k_query_bearer_token";
 
     // This param name is defined again in Keycloak Subsystem class
diff --git a/core/src/main/java/org/keycloak/representations/adapters/action/AdminAction.java b/core/src/main/java/org/keycloak/representations/adapters/action/AdminAction.java
index c372fde..7fcc3a0 100755
--- a/core/src/main/java/org/keycloak/representations/adapters/action/AdminAction.java
+++ b/core/src/main/java/org/keycloak/representations/adapters/action/AdminAction.java
@@ -8,18 +8,20 @@ import org.codehaus.jackson.annotate.JsonIgnore;
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
-public class AdminAction {
+public abstract class AdminAction {
     protected String id;
     protected int expiration;
     protected String resource;
+    protected String action;
 
     public AdminAction() {
     }
 
-    public AdminAction(String id, int expiration, String resource) {
+    public AdminAction(String id, int expiration, String resource, String action) {
         this.id = id;
         this.expiration = expiration;
         this.resource = resource;
+        this.action = action;
     }
 
     public String getId() {
@@ -56,4 +58,14 @@ public class AdminAction {
     public void setResource(String resource) {
         this.resource = resource;
     }
+
+    public String getAction() {
+        return action;
+    }
+
+    public void setAction(String action) {
+        this.action = action;
+    }
+
+    public abstract boolean validate();
 }
diff --git a/core/src/main/java/org/keycloak/representations/adapters/action/LogoutAction.java b/core/src/main/java/org/keycloak/representations/adapters/action/LogoutAction.java
index 69f5872..ef94ebe 100755
--- a/core/src/main/java/org/keycloak/representations/adapters/action/LogoutAction.java
+++ b/core/src/main/java/org/keycloak/representations/adapters/action/LogoutAction.java
@@ -5,13 +5,14 @@ package org.keycloak.representations.adapters.action;
  * @version $Revision: 1 $
  */
 public class LogoutAction extends AdminAction {
+    public static final String LOGOUT = "LOGOUT";
     protected String user;
 
     public LogoutAction() {
     }
 
     public LogoutAction(String id, int expiration, String resource, String user) {
-        super(id, expiration, resource);
+        super(id, expiration, resource, LOGOUT);
         this.user = user;
     }
 
@@ -22,4 +23,9 @@ public class LogoutAction extends AdminAction {
     public void setUser(String user) {
         this.user = user;
     }
+
+    @Override
+    public boolean validate() {
+        return LOGOUT.equals(action);
+    }
 }
diff --git a/core/src/main/java/org/keycloak/representations/adapters/action/PushNotBeforeAction.java b/core/src/main/java/org/keycloak/representations/adapters/action/PushNotBeforeAction.java
index b3ff1c4..d11a624 100755
--- a/core/src/main/java/org/keycloak/representations/adapters/action/PushNotBeforeAction.java
+++ b/core/src/main/java/org/keycloak/representations/adapters/action/PushNotBeforeAction.java
@@ -6,13 +6,14 @@ package org.keycloak.representations.adapters.action;
  */
 public class PushNotBeforeAction extends AdminAction {
 
+    public static final String PUSH_NOT_BEFORE = "PUSH_NOT_BEFORE";
     protected int notBefore;
 
     public PushNotBeforeAction() {
     }
 
     public PushNotBeforeAction(String id, int expiration, String resource, int notBefore) {
-        super(id, expiration, resource);
+        super(id, expiration, resource, PUSH_NOT_BEFORE);
         this.notBefore = notBefore;
     }
 
@@ -23,4 +24,10 @@ public class PushNotBeforeAction extends AdminAction {
     public void setNotBefore(int notBefore) {
         this.notBefore = notBefore;
     }
+
+    @Override
+    public boolean validate() {
+        return PUSH_NOT_BEFORE.equals(action);
+    }
+
 }
diff --git a/core/src/main/java/org/keycloak/representations/adapters/action/SessionStats.java b/core/src/main/java/org/keycloak/representations/adapters/action/SessionStats.java
new file mode 100755
index 0000000..84d08c1
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/adapters/action/SessionStats.java
@@ -0,0 +1,39 @@
+package org.keycloak.representations.adapters.action;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class SessionStats {
+    protected int activeSessions;
+    protected int activeUsers;
+    protected Map<String, UserStats> users;
+
+    public int getActiveSessions() {
+        return activeSessions;
+    }
+
+    public void setActiveSessions(int activeSessions) {
+        this.activeSessions = activeSessions;
+    }
+
+    public int getActiveUsers() {
+        return activeUsers;
+    }
+
+    public void setActiveUsers(int activeUsers) {
+        this.activeUsers = activeUsers;
+    }
+
+    public Map<String, UserStats> getUsers() {
+        return users;
+    }
+
+    public void setUsers(Map<String, UserStats> users) {
+        this.users = users;
+    }
+}
diff --git a/core/src/main/java/org/keycloak/representations/adapters/action/SessionStatsAction.java b/core/src/main/java/org/keycloak/representations/adapters/action/SessionStatsAction.java
new file mode 100755
index 0000000..1bcb1fa
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/adapters/action/SessionStatsAction.java
@@ -0,0 +1,35 @@
+package org.keycloak.representations.adapters.action;
+
+/**
+ * Query session stats.
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class SessionStatsAction extends AdminAction {
+
+    public static final String SESSION_STATS = "SESSION_STATS";
+
+    protected boolean listUsers;
+
+    public SessionStatsAction() {
+    }
+
+    public SessionStatsAction(String id, int expiration, String resource) {
+        super(id, expiration, resource, SESSION_STATS);
+    }
+
+    public boolean isListUsers() {
+        return listUsers;
+    }
+
+    public void setListUsers(boolean listUsers) {
+        this.listUsers = listUsers;
+    }
+
+    @Override
+    public boolean validate() {
+        return SESSION_STATS.equals(action);
+    }
+
+}
diff --git a/core/src/main/java/org/keycloak/representations/adapters/action/UserStats.java b/core/src/main/java/org/keycloak/representations/adapters/action/UserStats.java
new file mode 100755
index 0000000..2eb9778
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/adapters/action/UserStats.java
@@ -0,0 +1,26 @@
+package org.keycloak.representations.adapters.action;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class UserStats {
+    protected boolean loggedIn;
+    protected long whenLoggedIn;
+
+    public boolean isLoggedIn() {
+        return loggedIn;
+    }
+
+    public void setLoggedIn(boolean loggedIn) {
+        this.loggedIn = loggedIn;
+    }
+
+    public long getWhenLoggedIn() {
+        return whenLoggedIn;
+    }
+
+    public void setWhenLoggedIn(long whenLoggedIn) {
+        this.whenLoggedIn = whenLoggedIn;
+    }
+}
diff --git a/core/src/main/java/org/keycloak/representations/adapters/action/UserStatsAction.java b/core/src/main/java/org/keycloak/representations/adapters/action/UserStatsAction.java
new file mode 100755
index 0000000..c7e5b63
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/adapters/action/UserStatsAction.java
@@ -0,0 +1,31 @@
+package org.keycloak.representations.adapters.action;
+
+/**
+ * Query session stats.
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class UserStatsAction extends AdminAction {
+
+    public static final String USER_STATS = "USER_STATS";
+    protected String user;
+
+    public UserStatsAction() {
+    }
+
+    public UserStatsAction(String id, int expiration, String resource, String user) {
+        super(id, expiration, resource, USER_STATS);
+        this.user = user;
+    }
+
+    public String getUser() {
+        return user;
+    }
+
+    @Override
+    public boolean validate() {
+        return USER_STATS.equals(action);
+    }
+
+}
diff --git a/core/src/main/java/org/keycloak/util/JsonSerialization.java b/core/src/main/java/org/keycloak/util/JsonSerialization.java
index f69c971..9e1633b 100755
--- a/core/src/main/java/org/keycloak/util/JsonSerialization.java
+++ b/core/src/main/java/org/keycloak/util/JsonSerialization.java
@@ -6,6 +6,7 @@ import org.codehaus.jackson.map.annotate.JsonSerialize;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStream;
 
 /**
  * Utility class to handle simple JSON serializable for Keycloak.
@@ -25,6 +26,11 @@ public class JsonSerialization {
         prettyMapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
     }
 
+    public static void writeValueToStream(OutputStream os, Object obj) throws IOException {
+        mapper.writeValue(os, obj);
+
+    }
+
     public static String writeValueAsString(Object obj) throws IOException {
         return mapper.writeValueAsString(obj);
     }
diff --git a/core-jaxrs/src/main/java/org/keycloak/SkeletonKeyContextResolver.java b/core-jaxrs/src/main/java/org/keycloak/SkeletonKeyContextResolver.java
index 805a843..01aafd6 100755
--- a/core-jaxrs/src/main/java/org/keycloak/SkeletonKeyContextResolver.java
+++ b/core-jaxrs/src/main/java/org/keycloak/SkeletonKeyContextResolver.java
@@ -18,11 +18,11 @@ public class SkeletonKeyContextResolver implements ContextResolver<ObjectMapper>
     protected ObjectMapper mapper = new ObjectMapper();
 
     public SkeletonKeyContextResolver() {
-        mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_DEFAULT);
+        mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
     }
 
     public SkeletonKeyContextResolver(boolean indent) {
-        mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_DEFAULT);
+        mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
         if (indent) {
             mapper.enable(SerializationConfig.Feature.INDENT_OUTPUT);
         }
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSession.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSession.java
index 32d4917..77c6fef 100755
--- a/integration/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSession.java
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSession.java
@@ -52,6 +52,7 @@ public class RefreshableKeycloakSession extends KeycloakSecurityContext {
     }
 
     public void refreshExpiredToken() {
+        log.info("checking whether to refresh.");
         if (isActive()) return;
         if (this.realmConfiguration == null || refreshToken == null) return; // Might be serialized in HttpSession?
 
@@ -75,6 +76,10 @@ public class RefreshableKeycloakSession extends KeycloakSecurityContext {
         } catch (VerificationException e) {
             log.error("failed verification of token");
         }
+        if (response.getNotBeforePolicy() > realmConfiguration.getNotBefore()) {
+            realmConfiguration.setNotBefore(response.getNotBeforePolicy());
+        }
+
         this.token = token;
         this.refreshToken = response.getRefreshToken();
         this.tokenString = tokenString;
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java
index 4787a11..c9470db 100755
--- a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java
@@ -156,7 +156,7 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
 
     protected void pushNotBefore(JWSInput token, HttpServletResponse response) throws IOException {
         try {
-            log.debug("->> pushNotBefore: ");
+            log.info("->> pushNotBefore: ");
             PushNotBeforeAction action = JsonSerialization.readValue(token.getContent(), PushNotBeforeAction.class);
             if (action.isExpired()) {
                 log.warn("admin request failed, expired token");
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletAdminActionsHandler.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletAdminActionsHandler.java
index 1b7db55..092b99b 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletAdminActionsHandler.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletAdminActionsHandler.java
@@ -12,12 +12,22 @@ import org.keycloak.adapters.ResourceMetadata;
 import org.keycloak.adapters.config.RealmConfiguration;
 import org.keycloak.jose.jws.JWSInput;
 import org.keycloak.jose.jws.crypto.RSAProvider;
+import org.keycloak.representations.adapters.action.AdminAction;
 import org.keycloak.representations.adapters.action.PushNotBeforeAction;
+import org.keycloak.representations.adapters.action.SessionStats;
+import org.keycloak.representations.adapters.action.SessionStatsAction;
+import org.keycloak.representations.adapters.action.UserStats;
+import org.keycloak.representations.adapters.action.UserStatsAction;
 import org.keycloak.util.JsonSerialization;
 import org.keycloak.util.StreamUtil;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -101,6 +111,12 @@ public class ServletAdminActionsHandler implements HttpHandler {
         } else if (requestUri.endsWith(AdapterConstants.K_PUSH_NOT_BEFORE)) {
             handlePushNotBefore(request, response);
             return;
+        } else if (requestUri.endsWith(AdapterConstants.K_GET_SESSION_STATS)) {
+            handleGetSessionStats(request, response);
+            return;
+        }else if (requestUri.endsWith(AdapterConstants.K_GET_USER_STATS)) {
+            handleGetUserStats(request, response);
+            return;
         } else {
             next.handleRequest(exchange);
             return;
@@ -110,20 +126,79 @@ public class ServletAdminActionsHandler implements HttpHandler {
     protected void handlePushNotBefore(HttpServletRequest request, HttpServletResponse response) throws Exception {
         log.info("K_PUSH_NOT_BEFORE sent");
         JWSInput token = verifyAdminRequest(request, response);
-        if (token == null) return;
+        if (token == null) {
+            return;
+        }
         PushNotBeforeAction action = JsonSerialization.readValue(token.getContent(), PushNotBeforeAction.class);
+        if (!validateAction(response, action)) return;
+        realmConfig.setNotBefore(action.getNotBefore());
+        return;
+    }
+
+    protected boolean validateAction(HttpServletResponse response, AdminAction action) throws IOException {
+        if (!action.validate()) {
+            log.warn("admin request failed, not validated" + action.getAction());
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Not validated");
+            return false;
+        }
         if (action.isExpired()) {
             log.warn("admin request failed, expired token");
             response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Expired token");
-            return;
+            return false;
         }
         if (!resourceMetadata.getResourceName().equals(action.getResource())) {
             log.warn("Resource name does not match");
             response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Resource name does not match");
-            return;
+            return false;
 
         }
-        realmConfig.setNotBefore(action.getNotBefore());
+        return true;
+    }
+
+    protected void handleGetSessionStats(HttpServletRequest request, HttpServletResponse response) throws Exception {
+        log.info("K_GET_SESSION_STATS sent");
+        JWSInput token = verifyAdminRequest(request, response);
+        if (token == null) return;
+        SessionStatsAction action = JsonSerialization.readValue(token.getContent(), SessionStatsAction.class);
+        if (!validateAction(response, action)) return;
+        SessionStats stats = new SessionStats();
+        stats.setActiveSessions(userSessionManagement.getActiveSessions());
+        stats.setActiveUsers(userSessionManagement.getActiveUsers().size());
+        if (action.isListUsers() && userSessionManagement.getActiveSessions() > 0) {
+            Map<String, UserStats> list = new HashMap<String, UserStats>();
+            for (String user : userSessionManagement.getActiveUsers()) {
+                list.put(user, getUserStats(user));
+            }
+            stats.setUsers(list);
+        }
+        response.setStatus(200);
+        response.setContentType("application/json");
+        JsonSerialization.writeValueToStream(response.getOutputStream(), stats);
         return;
     }
+    protected void handleGetUserStats(HttpServletRequest request, HttpServletResponse response) throws Exception {
+        log.info("K_GET_USER_STATS sent");
+        JWSInput token = verifyAdminRequest(request, response);
+        if (token == null) return;
+        UserStatsAction action = JsonSerialization.readValue(token.getContent(), UserStatsAction.class);
+        if (!validateAction(response, action)) return;
+        String user = action.getUser();
+        UserStats stats = getUserStats(user);
+        response.setStatus(200);
+        response.setContentType("application/json");
+        JsonSerialization.writeValueToStream(response.getOutputStream(), stats);
+        return;
+    }
+
+    protected UserStats getUserStats(String user) {
+        UserStats stats = new UserStats();
+        Long loginTime = userSessionManagement.getUserLoginTime(user);
+        if (loginTime != null) {
+            stats.setLoggedIn(true);
+            stats.setWhenLoggedIn(loginTime);
+        } else {
+            stats.setLoggedIn(false);
+        }
+        return stats;
+    }
 }
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UserSessionManagement.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UserSessionManagement.java
index 4cbd1d1..fcf502b 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UserSessionManagement.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UserSessionManagement.java
@@ -31,7 +31,7 @@ import java.util.concurrent.ConcurrentHashMap;
 public class UserSessionManagement implements SessionListener {
     private static final Logger log = Logger.getLogger(UserSessionManagement.class);
     private static final String AUTH_SESSION_NAME = CachedAuthenticatedSessionHandler.class.getName() + ".AuthenticatedSession";
-    protected ConcurrentHashMap<String, Set<String>> userSessionMap = new ConcurrentHashMap<String, Set<String>>();
+    protected ConcurrentHashMap<String, UserSessions> userSessionMap = new ConcurrentHashMap<String, UserSessions>();
 
     protected RealmConfiguration realmInfo;
 
@@ -39,6 +39,51 @@ public class UserSessionManagement implements SessionListener {
         this.realmInfo = realmInfo;
     }
 
+    public static class UserSessions {
+        protected Set<String> sessionIds = new HashSet<String>();
+        protected long loggedIn = System.currentTimeMillis();
+
+        public Set<String> getSessionIds() {
+            return sessionIds;
+        }
+
+        public long getLoggedIn() {
+            return loggedIn;
+        }
+    }
+
+    public int getNumUserLogins() {
+        return userSessionMap.size();
+    }
+
+    public int getActiveSessions() {
+        int active = 0;
+        synchronized (userSessionMap) {
+            for (UserSessions sessions : userSessionMap.values()) {
+                active += sessions.getSessionIds().size();
+            }
+
+        }
+        return active;
+    }
+
+    /**
+     *
+     * @param username
+     * @return null if user not logged in
+     */
+    public Long getUserLoginTime(String username) {
+        UserSessions sessions = userSessionMap.get(username);
+        if (sessions == null) return null;
+        return sessions.getLoggedIn();
+    }
+
+    public Set<String> getActiveUsers() {
+        HashSet<String> set = new HashSet<String>();
+        set.addAll(userSessionMap.keySet());
+        return set;
+    }
+
     public void remoteLogout(JWSInput token, SessionManager manager, HttpServletResponse response) throws IOException {
         try {
             log.info("->> remoteLogout: ");
@@ -77,28 +122,22 @@ public class UserSessionManagement implements SessionListener {
 
     protected void addAuthenticatedSession(String username, String sessionId) {
         synchronized (userSessionMap) {
-            Set<String> map = userSessionMap.get(username);
-            if (map == null) {
-                final Set<String> value = new HashSet<String>();
-                map = userSessionMap.putIfAbsent(username, value);
-                if (map == null) {
-                    map = value;
-                }
+            UserSessions sessions = userSessionMap.get(username);
+            if (sessions == null) {
+                sessions = new UserSessions();
+                userSessionMap.put(username, sessions);
             }
-            synchronized (map) {
-                map.add(sessionId);
-            }
-
+            sessions.getSessionIds().add(sessionId);
         }
     }
 
     protected void removeAuthenticatedSession(String sessionId, String username) {
         synchronized (userSessionMap) {
-            Set<String> map = userSessionMap.get(username);
-            if (map == null) return;
-            synchronized (map) {
-                map.remove(sessionId);
-                if (map.isEmpty()) userSessionMap.remove(username);
+            UserSessions sessions = userSessionMap.get(username);
+            if (sessions == null) return;
+            sessions.getSessionIds().remove(sessionId);
+            if (sessions.getSessionIds().isEmpty()) {
+                userSessionMap.remove(username);
             }
         }
     }
@@ -119,24 +158,24 @@ public class UserSessionManagement implements SessionListener {
 
     public void logout(SessionManager manager, String user) {
         log.info("logoutUser: " + user);
-        Set<String> map = userSessionMap.remove(user);
-        if (map == null) {
+        UserSessions sessions = null;
+        synchronized (userSessionMap) {
+            sessions = userSessionMap.remove(user);
+        }
+        if (sessions == null) {
             log.info("no session for user: " + user);
             return;
         }
         log.info("found session for user");
-        synchronized (map) {
-            for (String id : map) {
-                log.debug("invalidating session for user: " + user);
-                Session session = manager.getSession(id);
-                try {
-                    session.invalidate(null);
-                } catch (Exception e) {
-                    log.warn("Session already invalidated.");
-                }
+        for (String id : sessions.getSessionIds()) {
+            log.debug("invalidating session for user: " + user);
+            Session session = manager.getSession(id);
+            try {
+                session.invalidate(null);
+            } catch (Exception e) {
+                log.warn("Session already invalidated.");
             }
         }
-
     }
 
     @Override
diff --git a/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java b/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java
index d084758..96a49f0 100755
--- a/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java
@@ -7,12 +7,19 @@ import org.keycloak.TokenIdGenerator;
 import org.keycloak.adapters.AdapterConstants;
 import org.keycloak.models.ApplicationModel;
 import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
 import org.keycloak.representations.adapters.action.LogoutAction;
 import org.keycloak.representations.adapters.action.PushNotBeforeAction;
+import org.keycloak.representations.adapters.action.SessionStats;
+import org.keycloak.representations.adapters.action.SessionStatsAction;
+import org.keycloak.representations.adapters.action.UserStats;
+import org.keycloak.representations.adapters.action.UserStatsAction;
 
 import javax.ws.rs.client.Entity;
 import javax.ws.rs.core.Response;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -21,7 +28,87 @@ import java.util.List;
 public class ResourceAdminManager {
     protected static Logger logger = Logger.getLogger(ResourceAdminManager.class);
 
-    public void singleLogOut(RealmModel realm, String user) {
+    public SessionStats getSessionStats(RealmModel realm, ApplicationModel application, boolean users) {
+        ResteasyClient client = new ResteasyClientBuilder()
+                .disableTrustManager() // todo fix this, should have a trust manager or a good default
+                .build();
+
+        try {
+            return getSessionStats(realm, application, users, client);
+        } finally {
+            client.close();
+        }
+
+    }
+
+    public SessionStats getSessionStats(RealmModel realm, ApplicationModel application, boolean users, ResteasyClient client) {
+        String managementUrl = application.getManagementUrl();
+        if (managementUrl != null) {
+            SessionStatsAction adminAction = new SessionStatsAction(TokenIdGenerator.generateId(), (int)(System.currentTimeMillis() / 1000) + 30, application.getName());
+            adminAction.setListUsers(users);
+            String token = new TokenManager().encodeToken(realm, adminAction);
+            logger.info("session stats for application: {0} url: {1}", application.getName(), managementUrl);
+            Response response = client.target(managementUrl).path(AdapterConstants.K_GET_SESSION_STATS).request().post(Entity.text(token));
+            if (response.getStatus() != 200) {
+                logger.warn("Failed to get stats: " + response.getStatus());
+                return null;
+            }
+            SessionStats stats = response.readEntity(SessionStats.class);
+
+            // replace with username
+            if (users && stats.getUsers() != null) {
+                Map<String, UserStats> newUsers = new HashMap<String, UserStats>();
+                for (Map.Entry<String, UserStats> entry : stats.getUsers().entrySet()) {
+                    UserModel user = realm.getUserById(entry.getKey());
+                    if (user == null) continue;
+                    newUsers.put(user.getLoginName(), entry.getValue());
+
+                }
+                stats.setUsers(newUsers);
+            }
+            return stats;
+        } else {
+            logger.info("no management url.");
+            return null;
+        }
+
+    }
+
+    public UserStats getUserStats(RealmModel realm, ApplicationModel application, UserModel user) {
+        ResteasyClient client = new ResteasyClientBuilder()
+                .disableTrustManager() // todo fix this, should have a trust manager or a good default
+                .build();
+
+        try {
+            return getUserStats(realm, application, user, client);
+        } finally {
+            client.close();
+        }
+
+    }
+
+
+    public UserStats getUserStats(RealmModel realm, ApplicationModel application, UserModel user, ResteasyClient client) {
+        String managementUrl = application.getManagementUrl();
+        if (managementUrl != null) {
+            UserStatsAction adminAction = new UserStatsAction(TokenIdGenerator.generateId(), (int)(System.currentTimeMillis() / 1000) + 30, application.getName(), user.getId());
+            String token = new TokenManager().encodeToken(realm, adminAction);
+            logger.info("session stats for application: {0} url: {1}", application.getName(), managementUrl);
+            Response response = client.target(managementUrl).path(AdapterConstants.K_GET_USER_STATS).request().post(Entity.text(token));
+            if (response.getStatus() != 200) {
+                logger.warn("Failed to get stats: " + response.getStatus());
+                return null;
+            }
+            UserStats stats = response.readEntity(UserStats.class);
+            return stats;
+        } else {
+            logger.info("no management url.");
+            return null;
+        }
+
+    }
+
+    public void logoutUser(RealmModel realm, String user) {
         ResteasyClient client = new ResteasyClientBuilder()
                 .disableTrustManager() // todo fix this, should have a trust manager or a good default
                 .build();
@@ -30,14 +117,43 @@ public class ResourceAdminManager {
             List<ApplicationModel> resources = realm.getApplications();
             logger.debug("logging out {0} resources ", resources.size());
             for (ApplicationModel resource : resources) {
-                logoutResource(realm, resource, user, client);
+                logoutApplication(realm, resource, user, client);
             }
         } finally {
             client.close();
         }
     }
+    public void logoutAll(RealmModel realm) {
+        ResteasyClient client = new ResteasyClientBuilder()
+                .disableTrustManager() // todo fix this, should have a trust manager or a good default
+                .build();
+
+        try {
+            List<ApplicationModel> resources = realm.getApplications();
+            logger.debug("logging out {0} resources ", resources.size());
+            for (ApplicationModel resource : resources) {
+                logoutApplication(realm, resource, null, client);
+            }
+        } finally {
+            client.close();
+        }
+    }
+
+    public void logoutApplication(RealmModel realm, ApplicationModel resource, String user) {
+        ResteasyClient client = new ResteasyClientBuilder()
+                .disableTrustManager() // todo fix this, should have a trust manager or a good default
+                .build();
+
+        try {
+            logoutApplication(realm, resource, user, client);
+        } finally {
+            client.close();
+        }
+
+    }
+
 
-    protected boolean logoutResource(RealmModel realm, ApplicationModel resource, String user, ResteasyClient client) {
+    protected boolean logoutApplication(RealmModel realm, ApplicationModel resource, String user, ResteasyClient client) {
         String managementUrl = resource.getManagementUrl();
         if (managementUrl != null) {
             LogoutAction adminAction = new LogoutAction(TokenIdGenerator.generateId(), (int)(System.currentTimeMillis() / 1000) + 30, resource.getName(), user);
@@ -49,7 +165,7 @@ public class ResourceAdminManager {
             logger.info("logout success.");
             return success;
         } else {
-            logger.info("logout failure.");
+            logger.info("Can't logout" + resource.getName() + " no mgmt url.");
             return false;
         }
     }
diff --git a/services/src/main/java/org/keycloak/services/managers/TokenManager.java b/services/src/main/java/org/keycloak/services/managers/TokenManager.java
index 44689c2..b617bb9 100755
--- a/services/src/main/java/org/keycloak/services/managers/TokenManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/TokenManager.java
@@ -366,18 +366,20 @@ public class TokenManager {
         return encodedToken;
     }
 
-    public AccessTokenResponseBuilder responseBuilder(RealmModel realm) {
-        return new AccessTokenResponseBuilder(realm);
+    public AccessTokenResponseBuilder responseBuilder(RealmModel realm, ClientModel client) {
+        return new AccessTokenResponseBuilder(realm, client);
     }
 
     public class AccessTokenResponseBuilder {
         RealmModel realm;
+        ClientModel client;
         AccessToken accessToken;
         RefreshToken refreshToken;
         IDToken idToken;
 
-        public AccessTokenResponseBuilder(RealmModel realm) {
+        public AccessTokenResponseBuilder(RealmModel realm, ClientModel client) {
             this.realm = realm;
+            this.client = client;
         }
 
         public AccessTokenResponseBuilder accessToken(AccessToken accessToken) {
@@ -465,7 +467,9 @@ public class TokenManager {
                 String encodedToken = new JWSBuilder().jsonContent(refreshToken).rsa256(realm.getPrivateKey());
                 res.setRefreshToken(encodedToken);
             }
-            res.setNotBeforePolicy(realm.getNotBefore());
+            int notBefore = realm.getNotBefore();
+            if (client.getNotBefore() > notBefore) notBefore = client.getNotBefore();
+            res.setNotBeforePolicy(notBefore);
             return res;
         }
     }
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java
index db4dd12..085bbec 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java
@@ -6,6 +6,9 @@ import org.keycloak.models.ApplicationModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.representations.adapters.action.SessionStats;
+import org.keycloak.representations.adapters.action.UserStats;
 import org.keycloak.representations.idm.ApplicationRepresentation;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.services.managers.ApplicationManager;
@@ -17,17 +20,22 @@ import org.keycloak.util.JsonSerialization;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
 import javax.ws.rs.GET;
 import javax.ws.rs.NotFoundException;
 import javax.ws.rs.POST;
 import javax.ws.rs.PUT;
 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.Application;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.UriInfo;
 import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -35,7 +43,7 @@ import java.util.Set;
  * @version $Revision: 1 $
  */
 public class ApplicationResource {
-    protected static final Logger logger = Logger.getLogger(RealmAdminResource.class);
+    protected static final Logger logger = Logger.getLogger(ApplicationResource.class);
     protected RealmModel realm;
     private RealmAuth auth;
     protected ApplicationModel application;
@@ -193,6 +201,50 @@ public class ApplicationResource {
         new ResourceAdminManager().pushApplicationRevocationPolicy(realm, application);
     }
 
+    @Path("session-stats")
+    @GET
+    @NoCache
+    @Produces(MediaType.APPLICATION_JSON)
+    public SessionStats getSessionStats(@QueryParam("users") @DefaultValue("false") boolean users) {
+        logger.info("session-stats");
+        auth.requireView();
+        if (application.getManagementUrl() == null || application.getManagementUrl().trim().equals("")) {
+            logger.info("sending empty stats");
+            SessionStats stats = new SessionStats();
+            if (users) stats.setUsers(new HashMap<String, UserStats>());
+            return stats;
+        }
+        SessionStats stats = new ResourceAdminManager().getSessionStats(realm, application, users);
+        if (stats == null) {
+            logger.info("app returned null stats");
+        } else {
+            logger.info("activeUsers: " + stats.getActiveUsers());
+            logger.info("activeSessions: " + stats.getActiveSessions());
+        }
+        return stats;
+     }
+
+    @Path("logout-all")
+    @POST
+    public void logoutAll() {
+        auth.requireManage();
+        new ResourceAdminManager().logoutApplication(realm, application, null);
+    }
+
+    @Path("logout-user/{username}")
+    @POST
+    public void logout(final @PathParam("username") String username) {
+        auth.requireManage();
+        UserModel user = realm.getUser(username);
+        if (user == null) {
+            throw new NotFoundException();
+        }
+        new ResourceAdminManager().logoutApplication(realm, application, user.getId());
+    }
+
+
+
+
 
 
 
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
index 0a89969..c9bf4f4 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
@@ -2,8 +2,10 @@ package org.keycloak.services.resources.admin;
 
 import org.jboss.resteasy.annotations.cache.NoCache;
 import org.jboss.resteasy.logging.Logger;
+import org.keycloak.models.ApplicationModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
+import org.keycloak.representations.adapters.action.SessionStats;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.services.managers.ModelToRepresentation;
 import org.keycloak.services.managers.RealmManager;
@@ -13,6 +15,10 @@ import org.keycloak.services.managers.TokenManager;
 import javax.ws.rs.*;
 import javax.ws.rs.container.ResourceContext;
 import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -112,4 +118,27 @@ public class RealmAdminResource {
         new ResourceAdminManager().pushRealmRevocationPolicy(realm);
     }
 
+    @Path("logout-all")
+    @POST
+    public void logoutAll() {
+        auth.requireManage();
+        new ResourceAdminManager().logoutAll(realm);
+    }
+
+    @Path("session-stats")
+    @GET
+    @NoCache
+    @Produces(MediaType.APPLICATION_JSON)
+    public Map<String,SessionStats> getSessionStats() {
+        logger.info("session-stats");
+        auth.requireView();
+        Map<String, SessionStats> stats = new HashMap<String, SessionStats>();
+        for (ApplicationModel applicationModel : realm.getApplications()) {
+            if (applicationModel.getManagementUrl() == null) continue;
+            SessionStats appStats = new ResourceAdminManager().getSessionStats(realm, applicationModel, false);
+            stats.put(applicationModel.getName(), appStats);
+        }
+        return stats;
+    }
+
 }
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
index 29b7374..e3f9bbe 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
@@ -10,6 +10,8 @@ import org.keycloak.models.RealmModel;
 import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserModel;
+import org.keycloak.representations.adapters.action.SessionStats;
+import org.keycloak.representations.adapters.action.UserStats;
 import org.keycloak.representations.idm.*;
 import org.keycloak.services.email.EmailException;
 import org.keycloak.services.email.EmailSender;
@@ -17,6 +19,7 @@ import org.keycloak.services.managers.AccessCodeEntry;
 import org.keycloak.services.managers.Auth;
 import org.keycloak.services.managers.ModelToRepresentation;
 import org.keycloak.services.managers.RealmManager;
+import org.keycloak.services.managers.ResourceAdminManager;
 import org.keycloak.services.managers.TokenManager;
 import org.keycloak.services.resources.flows.Flows;
 import org.keycloak.services.resources.flows.Urls;
@@ -34,6 +37,7 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.container.ResourceContext;
 import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 import java.util.ArrayList;
@@ -138,12 +142,45 @@ public class UsersResource {
         auth.requireView();
 
         UserModel user = realm.getUser(username);
-        if (user == null || !isUser(user)) {
+        if (user == null) {
             throw new NotFoundException();
         }
         return ModelToRepresentation.toRepresentation(user);
     }
 
+    @Path("{username}/session-stats")
+    @GET
+    @NoCache
+    @Produces(MediaType.APPLICATION_JSON)
+    public Map<String,UserStats> getSessionStats(final @PathParam("username") String username) {
+        logger.info("session-stats");
+        auth.requireView();
+        UserModel user = realm.getUser(username);
+        if (user == null) {
+            throw new NotFoundException();
+        }
+        Map<String, UserStats> stats = new HashMap<String, UserStats>();
+        for (ApplicationModel applicationModel : realm.getApplications()) {
+            if (applicationModel.getManagementUrl() == null) continue;
+            UserStats appStats = new ResourceAdminManager().getUserStats(realm, applicationModel, user);
+            if (appStats.isLoggedIn()) stats.put(applicationModel.getName(), appStats);
+        }
+        return stats;
+    }
+
+    @Path("{username}/logout")
+    @POST
+    public void logout(final @PathParam("username") String username) {
+        auth.requireManage();
+        UserModel user = realm.getUser(username);
+        if (user == null) {
+            throw new NotFoundException();
+        }
+        new ResourceAdminManager().logoutUser(realm, user.getId());
+    }
+
+
+
     @Path("{username}")
     @DELETE
     @NoCache
@@ -191,17 +228,11 @@ public class UsersResource {
         }
 
         for (UserModel user : userModels) {
-            if (isUser(user)) {
-                results.add(ModelToRepresentation.toRepresentation(user));
-            }
+            results.add(ModelToRepresentation.toRepresentation(user));
         }
         return results;
     }
 
-    private boolean isUser(UserModel user) {
-        return true;
-    }
-
     @Path("{username}/role-mappings")
     @GET
     @Produces("application/json")
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 6784de8..0cae7c2 100755
--- a/services/src/main/java/org/keycloak/services/resources/TokenService.java
+++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java
@@ -5,17 +5,13 @@ import org.jboss.resteasy.logging.Logger;
 import org.jboss.resteasy.spi.HttpRequest;
 import org.jboss.resteasy.spi.HttpResponse;
 import org.keycloak.OAuthErrorException;
-import org.keycloak.jose.jws.JWSBuilder;
 import org.keycloak.jose.jws.JWSInput;
 import org.keycloak.jose.jws.crypto.RSAProvider;
-import org.keycloak.models.ApplicationModel;
 import org.keycloak.models.ClientModel;
-import org.keycloak.models.Constants;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakTransaction;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RequiredCredentialModel;
-import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.representations.AccessToken;
@@ -54,7 +50,6 @@ import javax.ws.rs.core.SecurityContext;
 import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
 import javax.ws.rs.ext.Providers;
-import java.security.PrivateKey;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
@@ -161,7 +156,7 @@ public class TokenService {
             throw new NotAuthorizedException("Auth failed");
         }
         String scope = form.getFirst("scope");
-        AccessTokenResponse res = tokenManager.responseBuilder(realm)
+        AccessTokenResponse res = tokenManager.responseBuilder(realm, client)
                 .generateAccessToken(scope, client, user)
                 .generateIDToken()
                 .build();
@@ -191,7 +186,7 @@ public class TokenService {
             throw new BadRequestException(Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build(), e);
         }
 
-        AccessTokenResponse res = tokenManager.responseBuilder(realm)
+        AccessTokenResponse res = tokenManager.responseBuilder(realm, client)
                                               .accessToken(accessToken)
                                               .generateIDToken()
                                               .generateRefreshToken().build();
@@ -421,7 +416,7 @@ public class TokenService {
                     .build();
         }
         logger.debug("accessRequest SUCCESS");
-        AccessTokenResponse res = tokenManager.responseBuilder(realm)
+        AccessTokenResponse res = tokenManager.responseBuilder(realm, client)
                                               .accessToken(accessCode.getToken())
                                               .generateIDToken()
                                               .generateRefreshToken().build();
@@ -563,7 +558,7 @@ public class TokenService {
             logger.info("Logging out: {0}", user.getLoginName());
             authManager.expireIdentityCookie(realm, uriInfo);
             authManager.expireRememberMeCookie(realm, uriInfo);
-            resourceAdminManager.singleLogOut(realm, user.getId());
+            resourceAdminManager.logoutUser(realm, user.getId());
         } else {
             logger.info("No user logged in for logout");
         }