keycloak-memoizeit

Merge pull request #388 from patriot1burke/master session

5/16/2014 12:18:43 AM

Changes

Details

diff --git a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
index 66a0147..9c43eb6 100755
--- a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
@@ -10,13 +10,12 @@ import java.util.Set;
  * @version $Revision: 1 $
  */
 public class RealmRepresentation {
-    protected String self; // link
     protected String id;
     protected String realm;
     protected Integer notBefore;
     protected Integer accessTokenLifespan;
-    protected Integer refreshTokenLifespan;
-    protected Integer centralLoginLifespan;
+    protected Integer ssoSessionIdleTimeout;
+    protected Integer ssoSessionMaxLifespan;
     protected Integer accessCodeLifespan;
     protected Integer accessCodeLifespanUserAction;
     protected Boolean enabled;
@@ -62,14 +61,6 @@ public class RealmRepresentation {
     protected long auditExpiration;
     protected List<String> auditListeners;
 
-    public String getSelf() {
-        return self;
-    }
-
-    public void setSelf(String self) {
-        this.self = self;
-    }
-
     public String getId() {
         return id;
     }
@@ -142,20 +133,20 @@ public class RealmRepresentation {
         this.accessTokenLifespan = accessTokenLifespan;
     }
 
-    public Integer getRefreshTokenLifespan() {
-        return refreshTokenLifespan;
+    public Integer getSsoSessionIdleTimeout() {
+        return ssoSessionIdleTimeout;
     }
 
-    public Integer getCentralLoginLifespan() {
-        return centralLoginLifespan;
+    public void setSsoSessionIdleTimeout(Integer ssoSessionIdleTimeout) {
+        this.ssoSessionIdleTimeout = ssoSessionIdleTimeout;
     }
 
-    public void setCentralLoginLifespan(Integer centralLoginLifespan) {
-        this.centralLoginLifespan = centralLoginLifespan;
+    public Integer getSsoSessionMaxLifespan() {
+        return ssoSessionMaxLifespan;
     }
 
-    public void setRefreshTokenLifespan(Integer refreshTokenLifespan) {
-        this.refreshTokenLifespan = refreshTokenLifespan;
+    public void setSsoSessionMaxLifespan(Integer ssoSessionMaxLifespan) {
+        this.ssoSessionMaxLifespan = ssoSessionMaxLifespan;
     }
 
     public List<UserRoleMappingRepresentation> getRoleMappings() {
diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccount.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccount.java
index a2e7979..7313b5c 100755
--- a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccount.java
+++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccount.java
@@ -121,7 +121,7 @@ public class FreeMarkerAccount implements Account {
                 attributes.put("log", new LogBean(events));
                 break;
             case SESSIONS:
-                attributes.put("sessions", new SessionsBean(sessions));
+                attributes.put("sessions", new SessionsBean(realm, sessions));
                 break;
         }
 
diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/SessionsBean.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/SessionsBean.java
old mode 100644
new mode 100755
index 4db66dd..173414e
--- a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/SessionsBean.java
+++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/SessionsBean.java
@@ -1,5 +1,6 @@
 package org.keycloak.account.freemarker.model;
 
+import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.util.Time;
 
@@ -13,11 +14,12 @@ import java.util.List;
 public class SessionsBean {
 
     private List<UserSessionBean> events;
+    private RealmModel realm;
 
-    public SessionsBean(List<UserSessionModel> sessions) {
+    public SessionsBean(RealmModel realm, List<UserSessionModel> sessions) {
         this.events = new LinkedList<UserSessionBean>();
         for (UserSessionModel session : sessions) {
-            this.events.add(new UserSessionBean(session));
+            this.events.add(new UserSessionBean(realm, session));
         }
     }
 
@@ -28,8 +30,10 @@ public class SessionsBean {
     public static class UserSessionBean {
 
         private UserSessionModel session;
+        private RealmModel realm;
 
-        public UserSessionBean(UserSessionModel session) {
+        public UserSessionBean(RealmModel realm, UserSessionModel session) {
+            this.realm = realm;
             this.session = session;
         }
 
@@ -42,7 +46,8 @@ public class SessionsBean {
         }
 
         public Date getExpires() {
-            return Time.toDate(session.getExpires());
+            int max = session.getStarted() + realm.getSsoSessionMaxLifespan();
+            return Time.toDate(max);
         }
 
     }
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js
index dd02ff2..5908977 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js
@@ -623,16 +623,16 @@ module.controller('RealmTokenDetailCtrl', function($scope, Realm, realm, $http, 
         $scope.realm.accessTokenLifespan = TimeUnit.convert($scope.realm.accessTokenLifespan, from, to);
     });
 
-    $scope.realm.centralLoginLifespanUnit = TimeUnit.autoUnit(realm.centralLoginLifespan);
-    $scope.realm.centralLoginLifespan = TimeUnit.toUnit(realm.centralLoginLifespan, $scope.realm.centralLoginLifespanUnit);
-    $scope.$watch('realm.centralLoginLifespanUnit', function(to, from) {
-        $scope.realm.centralLoginLifespan = TimeUnit.convert($scope.realm.centralLoginLifespan, from, to);
+    $scope.realm.ssoSessionIdleTimeoutUnit = TimeUnit.autoUnit(realm.ssoSessionIdleTimeout);
+    $scope.realm.ssoSessionIdleTimeout = TimeUnit.toUnit(realm.ssoSessionIdleTimeout, $scope.realm.ssoSessionIdleTimeoutUnit);
+    $scope.$watch('realm.ssoSessionIdleTimeoutUnit', function(to, from) {
+        $scope.realm.ssoSessionIdleTimeout = TimeUnit.convert($scope.realm.ssoSessionIdleTimeout, from, to);
     });
 
-    $scope.realm.refreshTokenLifespanUnit = TimeUnit.autoUnit(realm.refreshTokenLifespan);
-    $scope.realm.refreshTokenLifespan = TimeUnit.toUnit(realm.refreshTokenLifespan, $scope.realm.refreshTokenLifespanUnit);
-    $scope.$watch('realm.refreshTokenLifespanUnit', function(to, from) {
-        $scope.realm.refreshTokenLifespan = TimeUnit.convert($scope.realm.refreshTokenLifespan, from, to);
+    $scope.realm.ssoSessionMaxLifespanUnit = TimeUnit.autoUnit(realm.ssoSessionMaxLifespan);
+    $scope.realm.ssoSessionMaxLifespan = TimeUnit.toUnit(realm.ssoSessionMaxLifespan, $scope.realm.ssoSessionMaxLifespanUnit);
+    $scope.$watch('realm.ssoSessionMaxLifespanUnit', function(to, from) {
+        $scope.realm.ssoSessionMaxLifespan = TimeUnit.convert($scope.realm.ssoSessionMaxLifespan, from, to);
     });
 
     $scope.realm.accessCodeLifespanUnit = TimeUnit.autoUnit(realm.accessCodeLifespan);
@@ -660,14 +660,14 @@ module.controller('RealmTokenDetailCtrl', function($scope, Realm, realm, $http, 
     $scope.save = function() {
         var realmCopy = angular.copy($scope.realm);
         delete realmCopy["accessTokenLifespanUnit"];
-        delete realmCopy["refreshTokenLifespanUnit"];
+        delete realmCopy["ssoSessionMaxLifespanUnit"];
         delete realmCopy["accessCodeLifespanUnit"];
-        delete realmCopy["centralLoginLifespanUnit"];
+        delete realmCopy["ssoSessionIdleTimeoutUnit"];
         delete realmCopy["accessCodeLifespanUserActionUnit"];
 
         realmCopy.accessTokenLifespan = TimeUnit.toSeconds($scope.realm.accessTokenLifespan, $scope.realm.accessTokenLifespanUnit)
-        realmCopy.centralLoginLifespan = TimeUnit.toSeconds($scope.realm.centralLoginLifespan, $scope.realm.centralLoginLifespanUnit)
-        realmCopy.refreshTokenLifespan = TimeUnit.toSeconds($scope.realm.refreshTokenLifespan, $scope.realm.refreshTokenLifespanUnit)
+        realmCopy.ssoSessionIdleTimeout = TimeUnit.toSeconds($scope.realm.ssoSessionIdleTimeout, $scope.realm.ssoSessionIdleTimeoutUnit)
+        realmCopy.ssoSessionMaxLifespan = TimeUnit.toSeconds($scope.realm.ssoSessionMaxLifespan, $scope.realm.ssoSessionMaxLifespanUnit)
         realmCopy.accessCodeLifespan = TimeUnit.toSeconds($scope.realm.accessCodeLifespan, $scope.realm.accessCodeLifespanUnit)
         realmCopy.accessCodeLifespanUserAction = TimeUnit.toSeconds($scope.realm.accessCodeLifespanUserAction, $scope.realm.accessCodeLifespanUserActionUnit)
 
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-tokens.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-tokens.html
index ba455e4..4dc0ffc 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-tokens.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-tokens.html
@@ -2,8 +2,8 @@
 <div id="content-area" class="col-sm-9" role="main">
     <ul class="nav nav-tabs nav-tabs-pf"  data-ng-show="!create">
         <li><a href="#/realms/{{realm.realm}}/sessions/realm">Realm Sessions</a></li>
-        <li><a href="#/realms/{{realm.realm}}/token-settings">Token Settings</a></li>
-        <li class="active"><a href="#/realms/{{realm.realm}}/sessions/revocation">Revocation</a></li>
+        <li class="active"><a href="#/realms/{{realm.realm}}/token-settings">Token Settings</a></li>
+        <li><a href="#/realms/{{realm.realm}}/sessions/revocation">Revocation</a></li>
         <li><a href="#/realms/{{realm.realm}}/sessions/brute-force">Brute Force</a></li>
     </ul>
     <div id="content">
@@ -15,17 +15,37 @@
         <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
             <fieldset class="border-top">
                 <div class="form-group input-select">
-                    <label class="col-sm-2 control-label" for="centralLoginLifespan">Central Login Lifespan</label>
+                    <label class="col-sm-2 control-label" for="ssoSessionIdleTimeout">SSO Session Idle Timeout</label>
                     <div class="col-sm-10">
                         <div class="row">
                             <div class="col-sm-2">
                                 <input class="form-control" type="number" required min="1"
-                                       max="31536000" data-ng-model="realm.centralLoginLifespan"
-                                       id="centralLoginLifespan" name="centralLoginLifespan"/>
+                                       max="31536000" data-ng-model="realm.ssoSessionIdleTimeout"
+                                       id="ssoSessionIdleTimeout" name="ssoSessionIdleTimeout"/>
                             </div>
                             <div class="col-sm-2 select-kc">
-                                <select name="centralLoginLifespanUnit" data-ng-model="realm.centralLoginLifespanUnit" >
-                                    <option data-ng-selected="!realm.centralLoginLifespanUnit">Seconds</option>
+                                <select name="ssoSessionIdleTimeoutUnit" data-ng-model="realm.ssoSessionIdleTimeoutUnit" >
+                                    <option data-ng-selected="!realm.ssoSessionIdleTimeoutUnit">Seconds</option>
+                                    <option>Minutes</option>
+                                    <option>Hours</option>
+                                    <option>Days</option>
+                                </select>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <div class="form-group input-select">
+                    <label class="col-sm-2 control-label" for="ssoSessionMaxLifespan">SSO Session Max Lifespan</label>
+                    <div class="col-sm-10">
+                        <div class="row">
+                            <div class="col-sm-2">
+                                <input class="form-control" type="number" required min="1"
+                                       max="31536000" data-ng-model="realm.ssoSessionMaxLifespan"
+                                       id="ssoSessionMaxLifespan" name="ssoSessionMaxLifespan"/>
+                            </div>
+                            <div class="col-sm-2 select-kc">
+                                <select name="ssoSessionMaxLifespanUnit" data-ng-model="realm.ssoSessionMaxLifespanUnit" >
+                                    <option data-ng-selected="!realm.ssoSessionMaxLifespanUnit">Seconds</option>
                                     <option>Minutes</option>
                                     <option>Hours</option>
                                     <option>Days</option>
@@ -90,26 +110,6 @@
                         </div>
                     </div>
                 </div>
-                <div class="form-group input-select">
-                    <label class="col-sm-2 control-label" for="refreshTokenLifespan">Refresh token lifespan</label>
-                    <div class="col-sm-10">
-                        <div class="row">
-                            <div class="col-sm-2">
-                                <input class="form-control" type="number" required min="1"
-                                       max="31536000" data-ng-model="realm.refreshTokenLifespan"
-                                       id="refreshTokenLifespan" name="refreshTokenLifespan"/>
-                            </div>
-                            <div class="col-sm-2 select-kc">
-                                <select name="refreshTokenLifespanUnit" data-ng-model="realm.refreshTokenLifespanUnit" >
-                                    <option data-ng-selected="!realm.refreshTokenLifespanUnit">Seconds</option>
-                                    <option>Minutes</option>
-                                    <option>Hours</option>
-                                    <option>Days</option>
-                                </select>
-                            </div>
-                        </div>
-                    </div>
-                </div>
             </fieldset>
             <div class="pull-right form-actions" data-ng-show="access.manageRealm">
                 <button kc-reset data-ng-show="changed">Clear changes</button>
diff --git a/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java b/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java
index d7fde9c..d19ca09 100755
--- a/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java
+++ b/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java
@@ -30,11 +30,11 @@ public class RealmEntity extends AbstractIdentifiableEntity {
     private int failureFactor;
     //--- end brute force settings
 
-    private int centralLoginLifespan;
+    private int ssoSessionIdleTimeout;
+    private int ssoSessionMaxLifespan;
     private int accessTokenLifespan;
     private int accessCodeLifespan;
     private int accessCodeLifespanUserAction;
-    private int refreshTokenLifespan;
     private int notBefore;
 
     private String publicKeyPem;
@@ -196,12 +196,20 @@ public class RealmEntity extends AbstractIdentifiableEntity {
         this.failureFactor = failureFactor;
     }
 
-    public int getCentralLoginLifespan() {
-        return centralLoginLifespan;
+    public int getSsoSessionIdleTimeout() {
+        return ssoSessionIdleTimeout;
     }
 
-    public void setCentralLoginLifespan(int centralLoginLifespan) {
-        this.centralLoginLifespan = centralLoginLifespan;
+    public void setSsoSessionIdleTimeout(int ssoSessionIdleTimeout) {
+        this.ssoSessionIdleTimeout = ssoSessionIdleTimeout;
+    }
+
+    public int getSsoSessionMaxLifespan() {
+        return ssoSessionMaxLifespan;
+    }
+
+    public void setSsoSessionMaxLifespan(int ssoSessionMaxLifespan) {
+        this.ssoSessionMaxLifespan = ssoSessionMaxLifespan;
     }
 
     public int getAccessTokenLifespan() {
@@ -228,14 +236,6 @@ public class RealmEntity extends AbstractIdentifiableEntity {
         this.accessCodeLifespanUserAction = accessCodeLifespanUserAction;
     }
 
-    public int getRefreshTokenLifespan() {
-        return refreshTokenLifespan;
-    }
-
-    public void setRefreshTokenLifespan(int refreshTokenLifespan) {
-        this.refreshTokenLifespan = refreshTokenLifespan;
-    }
-
     public int getNotBefore() {
         return notBefore;
     }
diff --git a/model/api/src/main/java/org/keycloak/models/RealmModel.java b/model/api/src/main/java/org/keycloak/models/RealmModel.java
index ac91fc7..c48613b 100755
--- a/model/api/src/main/java/org/keycloak/models/RealmModel.java
+++ b/model/api/src/main/java/org/keycloak/models/RealmModel.java
@@ -59,25 +59,23 @@ public interface RealmModel extends RoleContainerModel, RoleMapperModel, ScopeMa
 
     void setResetPasswordAllowed(boolean resetPasswordAllowed);
 
-    int getCentralLoginLifespan();
+    int getSsoSessionIdleTimeout();
+    void setSsoSessionIdleTimeout(int seconds);
 
-    void setCentralLoginLifespan(int lifespan);
+    int getSsoSessionMaxLifespan();
+    void setSsoSessionMaxLifespan(int seconds);
 
     int getAccessTokenLifespan();
 
-    void setAccessTokenLifespan(int tokenLifespan);
-
-    int getRefreshTokenLifespan();
-
-    void setRefreshTokenLifespan(int tokenLifespan);
+    void setAccessTokenLifespan(int seconds);
 
     int getAccessCodeLifespan();
 
-    void setAccessCodeLifespan(int accessCodeLifespan);
+    void setAccessCodeLifespan(int seconds);
 
     int getAccessCodeLifespanUserAction();
 
-    void setAccessCodeLifespanUserAction(int accessCodeLifespanUserAction);
+    void setAccessCodeLifespanUserAction(int seconds);
 
     String getPublicKeyPem();
 
diff --git a/model/api/src/main/java/org/keycloak/models/UserSessionModel.java b/model/api/src/main/java/org/keycloak/models/UserSessionModel.java
old mode 100644
new mode 100755
index 33c33fc..e2e16a7
--- a/model/api/src/main/java/org/keycloak/models/UserSessionModel.java
+++ b/model/api/src/main/java/org/keycloak/models/UserSessionModel.java
@@ -21,8 +21,8 @@ public interface UserSessionModel {
 
     void setStarted(int started);
 
-    int getExpires();
+    int getLastSessionRefresh();
 
-    void setExpires(int expires);
+    void setLastSessionRefresh(int seconds);
 
 }
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
index 2051109..c5bf40c 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
@@ -60,11 +60,11 @@ public class RealmEntity {
     protected boolean updateProfileOnInitialSocialLogin;
     protected String passwordPolicy;
 
-    protected int centralLoginLifespan;
+    private int ssoSessionIdleTimeout;
+    private int ssoSessionMaxLifespan;
     protected int accessTokenLifespan;
     protected int accessCodeLifespan;
     protected int accessCodeLifespanUserAction;
-    protected int refreshTokenLifespan;
     protected int notBefore;
 
     @Column(length = 2048)
@@ -201,20 +201,20 @@ public class RealmEntity {
         this.updateProfileOnInitialSocialLogin = updateProfileOnInitialSocialLogin;
     }
 
-    public int getCentralLoginLifespan() {
-        return centralLoginLifespan;
+    public int getSsoSessionIdleTimeout() {
+        return ssoSessionIdleTimeout;
     }
 
-    public void setCentralLoginLifespan(int centralLoginLifespan) {
-        this.centralLoginLifespan = centralLoginLifespan;
+    public void setSsoSessionIdleTimeout(int ssoSessionIdleTimeout) {
+        this.ssoSessionIdleTimeout = ssoSessionIdleTimeout;
     }
 
-    public int getRefreshTokenLifespan() {
-        return refreshTokenLifespan;
+    public int getSsoSessionMaxLifespan() {
+        return ssoSessionMaxLifespan;
     }
 
-    public void setRefreshTokenLifespan(int refreshTokenLifespan) {
-        this.refreshTokenLifespan = refreshTokenLifespan;
+    public void setSsoSessionMaxLifespan(int ssoSessionMaxLifespan) {
+        this.ssoSessionMaxLifespan = ssoSessionMaxLifespan;
     }
 
     public int getAccessTokenLifespan() {
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserSessionEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserSessionEntity.java
old mode 100644
new mode 100755
index 83db701..62769ea
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserSessionEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserSessionEntity.java
@@ -17,7 +17,7 @@ import javax.persistence.NamedQuery;
 @NamedQueries({
         @NamedQuery(name = "getUserSessionByUser", query = "select s from UserSessionEntity s where s.user = :user"),
         @NamedQuery(name = "removeUserSessionByUser", query = "delete from UserSessionEntity s where s.user = :user"),
-        @NamedQuery(name = "removeUserSessionExpired", query = "delete from UserSessionEntity s where s.expires < :currentTime")
+        @NamedQuery(name = "removeUserSessionExpired", query = "delete from UserSessionEntity s where s.started < :maxTime or s.lastSessionRefresh < :idleTime")
 })
 public class UserSessionEntity {
 
@@ -33,7 +33,7 @@ public class UserSessionEntity {
 
     int started;
 
-    int expires;
+    int lastSessionRefresh;
 
     public String getId() {
         return id;
@@ -67,12 +67,11 @@ public class UserSessionEntity {
         this.started = started;
     }
 
-    public int getExpires() {
-        return expires;
+    public int getLastSessionRefresh() {
+        return lastSessionRefresh;
     }
 
-    public void setExpires(int expires) {
-        this.expires = expires;
+    public void setLastSessionRefresh(int lastSessionRefresh) {
+        this.lastSessionRefresh = lastSessionRefresh;
     }
-
 }
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
index d462e65..3f420c8 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
@@ -246,25 +246,23 @@ public class RealmAdapter implements RealmModel {
     }
 
     @Override
-    public int getCentralLoginLifespan() {
-        return realm.getCentralLoginLifespan();
+    public int getSsoSessionIdleTimeout() {
+        return realm.getSsoSessionIdleTimeout();
     }
 
     @Override
-    public void setCentralLoginLifespan(int lifespan) {
-        realm.setCentralLoginLifespan(lifespan);
-        em.flush();
+    public void setSsoSessionIdleTimeout(int seconds) {
+        realm.setSsoSessionIdleTimeout(seconds);
     }
 
     @Override
-    public int getRefreshTokenLifespan() {
-        return realm.getRefreshTokenLifespan();
+    public int getSsoSessionMaxLifespan() {
+        return realm.getSsoSessionMaxLifespan();
     }
 
     @Override
-    public void setRefreshTokenLifespan(int tokenLifespan) {
-        realm.setRefreshTokenLifespan(tokenLifespan);
-        em.flush();
+    public void setSsoSessionMaxLifespan(int seconds) {
+        realm.setSsoSessionMaxLifespan(seconds);
     }
 
     @Override
@@ -1391,10 +1389,9 @@ public class RealmAdapter implements RealmModel {
         entity.setIpAddress(ipAddress);
 
         int currentTime = Time.currentTime();
-        int expires = currentTime + realm.getCentralLoginLifespan();
 
         entity.setStarted(currentTime);
-        entity.setExpires(expires);
+        entity.setLastSessionRefresh(currentTime);
 
         em.persist(entity);
         return new UserSessionAdapter(entity);
@@ -1431,7 +1428,10 @@ public class RealmAdapter implements RealmModel {
 
     @Override
     public void removeExpiredUserSessions() {
-        em.createNamedQuery("removeUserSessionExpired").setParameter("currentTime", Time.currentTime()).executeUpdate();
+        em.createNamedQuery("removeUserSessionExpired")
+                .setParameter("maxTime", Time.currentTime() - getSsoSessionMaxLifespan())
+                .setParameter("idleTime", Time.currentTime() - getSsoSessionIdleTimeout())
+                .executeUpdate();
     }
 
 }
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/UserSessionAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/UserSessionAdapter.java
old mode 100644
new mode 100755
index 1f4ba28..0de48e5
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/UserSessionAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/UserSessionAdapter.java
@@ -60,13 +60,12 @@ public class UserSessionAdapter implements UserSessionModel {
     }
 
     @Override
-    public int getExpires() {
-        return entity.getExpires();
+    public int getLastSessionRefresh() {
+        return entity.getLastSessionRefresh();
     }
 
     @Override
-    public void setExpires(int expires) {
-        entity.setExpires(expires);
+    public void setLastSessionRefresh(int seconds) {
+        entity.setLastSessionRefresh(seconds);
     }
-
 }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
index 9791740..eb61b5b 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
@@ -281,39 +281,38 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
 
 
     @Override
-    public int getAccessTokenLifespan() {
-        return realm.getAccessTokenLifespan();
+    public int getSsoSessionIdleTimeout() {
+        return realm.getSsoSessionIdleTimeout();
     }
 
     @Override
-    public void setAccessTokenLifespan(int tokenLifespan) {
-        realm.setAccessTokenLifespan(tokenLifespan);
-        updateRealm();
+    public void setSsoSessionIdleTimeout(int seconds) {
+        realm.setSsoSessionIdleTimeout(seconds);
     }
 
     @Override
-    public int getCentralLoginLifespan() {
-        return realm.getCentralLoginLifespan();
+    public int getSsoSessionMaxLifespan() {
+        return realm.getSsoSessionMaxLifespan();
     }
 
     @Override
-    public void setCentralLoginLifespan(int lifespan) {
-        realm.setCentralLoginLifespan(lifespan);
-        updateRealm();
+    public void setSsoSessionMaxLifespan(int seconds) {
+        realm.setSsoSessionMaxLifespan(seconds);
     }
 
-
     @Override
-    public int getRefreshTokenLifespan() {
-        return realm.getRefreshTokenLifespan();
+    public int getAccessTokenLifespan() {
+        return realm.getAccessTokenLifespan();
     }
 
     @Override
-    public void setRefreshTokenLifespan(int tokenLifespan) {
-        realm.setRefreshTokenLifespan(tokenLifespan);
+    public void setAccessTokenLifespan(int tokenLifespan) {
+        realm.setAccessTokenLifespan(tokenLifespan);
         updateRealm();
     }
 
+
+
     @Override
     public int getAccessCodeLifespan() {
         return realm.getAccessCodeLifespan();
@@ -1357,10 +1356,9 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
         entity.setIpAddress(ipAddress);
 
         int currentTime = Time.currentTime();
-        int expires = currentTime + realm.getCentralLoginLifespan();
 
         entity.setStarted(currentTime);
-        entity.setExpires(expires);
+        entity.setLastSessionRefresh(currentTime);
 
         getMongoStore().insertEntity(entity, invocationContext);
         return new UserSessionAdapter(entity, this, invocationContext);
@@ -1399,8 +1397,14 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
 
     @Override
     public void removeExpiredUserSessions() {
+        int currentTime = Time.currentTime();
         DBObject query = new QueryBuilder()
-                .and("expires").lessThan(Time.currentTime())
+                .and("started").lessThan(currentTime - realm.getSsoSessionMaxLifespan())
+                .get();
+
+        getMongoStore().removeEntities(MongoUserSessionEntity.class, query, invocationContext);
+        query = new QueryBuilder()
+                .and("lastSessionRefresh").lessThan(currentTime - realm.getSsoSessionIdleTimeout())
                 .get();
 
         getMongoStore().removeEntities(MongoUserSessionEntity.class, query, invocationContext);
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserSessionAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserSessionAdapter.java
old mode 100644
new mode 100755
index ab02e1c..675e4ba
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserSessionAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserSessionAdapter.java
@@ -65,13 +65,13 @@ public class UserSessionAdapter implements UserSessionModel {
     }
 
     @Override
-    public int getExpires() {
-        return entity.getExpires();
+    public int getLastSessionRefresh() {
+        return entity.getLastSessionRefresh();
     }
 
     @Override
-    public void setExpires(int expires) {
-        entity.setExpires(expires);
+    public void setLastSessionRefresh(int seconds) {
+        entity.setLastSessionRefresh(seconds);
     }
 
 }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoUserSessionEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoUserSessionEntity.java
old mode 100644
new mode 100755
index de2a6d0..2d98d19
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoUserSessionEntity.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoUserSessionEntity.java
@@ -17,7 +17,7 @@ public class MongoUserSessionEntity extends AbstractIdentifiableEntity implement
 
     private int started;
 
-    private int expires;
+    private int lastSessionRefresh;
 
     public String getUser() {
         return user;
@@ -43,12 +43,12 @@ public class MongoUserSessionEntity extends AbstractIdentifiableEntity implement
         this.started = started;
     }
 
-    public int getExpires() {
-        return expires;
+    public int getLastSessionRefresh() {
+        return lastSessionRefresh;
     }
 
-    public void setExpires(int expires) {
-        this.expires = expires;
+    public void setLastSessionRefresh(int lastSessionRefresh) {
+        this.lastSessionRefresh = lastSessionRefresh;
     }
 
     @Override
diff --git a/model/tests/src/test/java/org/keycloak/model/test/AdapterTest.java b/model/tests/src/test/java/org/keycloak/model/test/AdapterTest.java
index b15de5d..be26f44 100755
--- a/model/tests/src/test/java/org/keycloak/model/test/AdapterTest.java
+++ b/model/tests/src/test/java/org/keycloak/model/test/AdapterTest.java
@@ -726,7 +726,7 @@ public class AdapterTest extends AbstractModelTest {
     @Test
     public void userSessions() throws InterruptedException {
         realmManager.createRealm("userSessions");
-        realmManager.getRealmByName("userSessions").setCentralLoginLifespan(5);
+        realmManager.getRealmByName("userSessions").setSsoSessionIdleTimeout(5);
 
         UserModel user = realmManager.getRealmByName("userSessions").addUser("userSessions1");
 
@@ -749,7 +749,7 @@ public class AdapterTest extends AbstractModelTest {
 
         assertNull(realmManager.getRealmByName("userSessions").getUserSession(userSession.getId()));
 
-        realmManager.getRealmByName("userSessions").setCentralLoginLifespan(1);
+        realmManager.getRealmByName("userSessions").setSsoSessionIdleTimeout(1);
 
         userSession = realmManager.getRealmByName("userSessions").createUserSession(user, "127.0.0.1");
         commit();
diff --git a/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java b/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java
index 41586d6..f67357e 100755
--- a/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java
@@ -2,22 +2,13 @@ package org.keycloak.services.managers;
 
 import org.jboss.logging.Logger;
 import org.jboss.resteasy.spi.UnauthorizedException;
-import org.keycloak.RSATokenVerifier;
-import org.keycloak.VerificationException;
-import org.keycloak.jose.jws.JWSInput;
-import org.keycloak.jose.jws.crypto.RSAProvider;
-import org.keycloak.models.ClientModel;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.provider.ProviderSession;
-import org.keycloak.representations.AccessToken;
 
-import org.jboss.resteasy.spi.BadRequestException;
 import javax.ws.rs.core.Cookie;
 import javax.ws.rs.core.HttpHeaders;
-import javax.ws.rs.core.NewCookie;
 import javax.ws.rs.core.UriInfo;
-import java.net.URI;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -33,6 +24,11 @@ public class AppAuthManager extends AuthenticationManager {
     public UserModel authenticateRequest(RealmModel realm, UriInfo uriInfo, HttpHeaders headers) {
         AuthResult authResult = authenticateIdentityCookie(realm, uriInfo, headers);
         if (authResult != null) {
+            Cookie remember = headers.getCookies().get(AuthenticationManager.KEYCLOAK_REMEMBER_ME);
+            boolean rememberMe = remember != null;
+            // refresh the cookies!
+            createLoginCookie(realm, authResult.getUser(), authResult.getSession(), uriInfo, rememberMe);
+            if (rememberMe) createRememberMeCookie(realm, uriInfo);
             return authResult.getUser();
         } else {
             return authenticateBearerToken(realm, uriInfo, headers);
diff --git a/services/src/main/java/org/keycloak/services/managers/ApplianceBootstrap.java b/services/src/main/java/org/keycloak/services/managers/ApplianceBootstrap.java
index aff58a7..1ef7d7c 100755
--- a/services/src/main/java/org/keycloak/services/managers/ApplianceBootstrap.java
+++ b/services/src/main/java/org/keycloak/services/managers/ApplianceBootstrap.java
@@ -54,9 +54,9 @@ public class ApplianceBootstrap {
         realm.setName(adminRealmName);
         realm.setEnabled(true);
         realm.addRequiredCredential(CredentialRepresentation.PASSWORD);
-        realm.setCentralLoginLifespan(3000);
+        realm.setSsoSessionIdleTimeout(300);
         realm.setAccessTokenLifespan(60);
-        realm.setRefreshTokenLifespan(3600);
+        realm.setSsoSessionMaxLifespan(36000);
         realm.setAccessCodeLifespan(60);
         realm.setAccessCodeLifespanUserAction(300);
         realm.setSslNotRequired(true);
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 c3b9eec..453f550 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -5,6 +5,7 @@ import org.jboss.resteasy.spi.HttpResponse;
 import org.jboss.resteasy.spi.ResteasyProviderFactory;
 import org.keycloak.RSATokenVerifier;
 import org.keycloak.VerificationException;
+import org.keycloak.audit.Audit;
 import org.keycloak.jose.jws.JWSBuilder;
 import org.keycloak.models.AuthenticationLinkModel;
 import org.keycloak.models.ClientModel;
@@ -61,6 +62,29 @@ public class AuthenticationManager {
         this.protector = protector;
     }
 
+    public static boolean isSessionValid(RealmModel realm, UserSessionModel session) {
+        if (session == null) return false;
+        int currentTime = Time.currentTime();
+        int max = session.getStarted() + realm.getSsoSessionMaxLifespan();
+        boolean valid = session != null && session.getLastSessionRefresh() + realm.getSsoSessionIdleTimeout() > currentTime && max > currentTime;
+        return valid;
+    }
+
+    public static void logout(RealmModel realm, UserSessionModel session, UriInfo uriInfo) {
+        if (session == null) return;
+        UserModel user = session.getUser();
+
+        logger.infov("Logging out: {0} ({1})", user.getLoginName(), session.getId());
+
+        realm.removeUserSession(session);
+        expireIdentityCookie(realm, uriInfo);
+        expireRememberMeCookie(realm, uriInfo);
+
+        new ResourceAdminManager().logoutUser(uriInfo.getRequestUri(), realm, user.getId(), session.getId());
+
+    }
+
+
     public AccessToken createIdentityToken(RealmModel realm, UserModel user, UserSessionModel session) {
         logger.info("createIdentityToken");
         AccessToken token = new AccessToken();
@@ -71,26 +95,25 @@ public class AuthenticationManager {
         if (session != null) {
             token.setSessionState(session.getId());
         }
-        if (realm.getCentralLoginLifespan() > 0) {
-            token.expiration(Time.currentTime() + realm.getCentralLoginLifespan());
+        if (realm.getSsoSessionIdleTimeout() > 0) {
+            token.expiration(Time.currentTime() + realm.getSsoSessionIdleTimeout());
         }
         return token;
     }
 
-    public void createLoginCookie(Response.ResponseBuilder builder, RealmModel realm, UserModel user, UserSessionModel session, UriInfo uriInfo, boolean rememberMe) {
+    public void createLoginCookie(RealmModel realm, UserModel user, UserSessionModel session, UriInfo uriInfo, boolean rememberMe) {
         logger.info("createLoginCookie");
-        String cookieName = KEYCLOAK_IDENTITY_COOKIE;
         String cookiePath = getIdentityCookiePath(realm, uriInfo);
         AccessToken identityToken = createIdentityToken(realm, user, session);
         String encoded = encodeToken(realm, identityToken);
         boolean secureOnly = !realm.isSslNotRequired();
-        logger.debugv("creatingLoginCookie - name: {0} path: {1}", cookieName, cookiePath);
+        logger.debugv("creatingLoginCookie - name: {0} path: {1}", KEYCLOAK_IDENTITY_COOKIE, cookiePath);
         int maxAge = NewCookie.DEFAULT_MAX_AGE;
         if (rememberMe) {
-            maxAge = realm.getCentralLoginLifespan();
+            maxAge = realm.getSsoSessionIdleTimeout();
             logger.info("createLoginCookie maxAge: " + maxAge);
         }
-        CookieHelper.addCookie(cookieName, encoded, cookiePath, null, null, maxAge, secureOnly, true);
+        CookieHelper.addCookie(KEYCLOAK_IDENTITY_COOKIE, encoded, cookiePath, null, null, maxAge, secureOnly, true);
         //builder.cookie(new NewCookie(cookieName, encoded, cookiePath, null, null, maxAge, secureOnly));// todo httponly , true);
 
         String sessionCookieValue = realm.getName() + "-" + user.getId();
@@ -98,16 +121,16 @@ public class AuthenticationManager {
             sessionCookieValue += "-" + session.getId();
         }
         // THIS SHOULD NOT BE A HTTPONLY COOKIE!  It is used for OpenID Connect Iframe Session support!
-        builder.cookie(new NewCookie(KEYCLOAK_SESSION_COOKIE, sessionCookieValue, cookiePath, null, null, maxAge, secureOnly));
+        CookieHelper.addCookie(KEYCLOAK_SESSION_COOKIE, sessionCookieValue, cookiePath, null, null, maxAge, secureOnly, false);
 
     }
 
-    public void createRememberMeCookie(HttpResponse response, RealmModel realm, UriInfo uriInfo) {
+    public void createRememberMeCookie(RealmModel realm, UriInfo uriInfo) {
         String path = getIdentityCookiePath(realm, uriInfo);
         boolean secureOnly = !realm.isSslNotRequired();
         // remember me cookie should be persistent
         //NewCookie cookie = new NewCookie(KEYCLOAK_REMEMBER_ME, "true", path, null, null, realm.getCentralLoginLifespan(), secureOnly);// todo httponly , true);
-        CookieHelper.addCookie(KEYCLOAK_REMEMBER_ME, "true", path, null, null, realm.getCentralLoginLifespan(), secureOnly, true);
+        CookieHelper.addCookie(KEYCLOAK_REMEMBER_ME, "true", path, null, null, realm.getSsoSessionIdleTimeout(), secureOnly, true);
     }
 
     protected String encodeToken(RealmModel realm, Object token) {
@@ -117,25 +140,26 @@ public class AuthenticationManager {
         return encodedToken;
     }
 
-    public void expireIdentityCookie(RealmModel realm, UriInfo uriInfo) {
+    public static void expireIdentityCookie(RealmModel realm, UriInfo uriInfo) {
         logger.debug("Expiring identity cookie");
         String path = getIdentityCookiePath(realm, uriInfo);
         expireCookie(realm, KEYCLOAK_IDENTITY_COOKIE, path, true);
         expireCookie(realm, KEYCLOAK_SESSION_COOKIE, path, false);
+        expireRememberMeCookie(realm, uriInfo);
     }
-    public void expireRememberMeCookie(RealmModel realm, UriInfo uriInfo) {
+    public static void expireRememberMeCookie(RealmModel realm, UriInfo uriInfo) {
         logger.debug("Expiring remember me cookie");
         String path = getIdentityCookiePath(realm, uriInfo);
         String cookieName = KEYCLOAK_REMEMBER_ME;
         expireCookie(realm, cookieName, path, true);
     }
 
-    protected String getIdentityCookiePath(RealmModel realm, UriInfo uriInfo) {
+    protected static String getIdentityCookiePath(RealmModel realm, UriInfo uriInfo) {
         URI uri = RealmsResource.realmBaseUrl(uriInfo).build(realm.getName());
         return uri.getRawPath();
     }
 
-    public void expireCookie(RealmModel realm, String cookieName, String path, boolean httpOnly) {
+    public static void expireCookie(RealmModel realm, String cookieName, String path, boolean httpOnly) {
         logger.debugv("Expiring cookie: {0} path: {1}", cookieName, path);
         boolean secureOnly = !realm.isSslNotRequired();
         CookieHelper.addCookie(cookieName, "", path, null, "Expiring cookie", 0, secureOnly, httpOnly);
@@ -147,15 +171,9 @@ public class AuthenticationManager {
 
     public AuthResult authenticateIdentityCookie(RealmModel realm, UriInfo uriInfo, HttpHeaders headers, boolean checkActive) {
         logger.info("authenticateIdentityCookie");
-        String cookieName = KEYCLOAK_IDENTITY_COOKIE;
-        return authenticateIdentityCookie(realm, uriInfo, headers, cookieName, checkActive);
-    }
-
-    private AuthResult authenticateIdentityCookie(RealmModel realm, UriInfo uriInfo, HttpHeaders headers, String cookieName, boolean checkActive) {
-        logger.info("authenticateIdentityCookie");
-        Cookie cookie = headers.getCookies().get(cookieName);
+        Cookie cookie = headers.getCookies().get(KEYCLOAK_IDENTITY_COOKIE);
         if (cookie == null) {
-            logger.infov("authenticateCookie could not find cookie: {0}", cookieName);
+            logger.infov("authenticateCookie could not find cookie: {0}", KEYCLOAK_IDENTITY_COOKIE);
             return null;
         }
 
@@ -163,7 +181,9 @@ public class AuthenticationManager {
         AuthResult authResult = verifyIdentityToken(realm, uriInfo, checkActive, tokenString);
         if (authResult == null) {
             expireIdentityCookie(realm, uriInfo);
+            return null;
         }
+        authResult.getSession().setLastSessionRefresh(Time.currentTime());
         return authResult;
     }
 
@@ -175,7 +195,6 @@ public class AuthenticationManager {
                 logger.info("Checking if identity token is active");
                 if (!token.isActive() || token.getIssuedAt() < realm.getNotBefore()) {
                     logger.info("identity cookie expired");
-                    expireIdentityCookie(realm, uriInfo);
                     return null;
                 } else {
                     logger.info("token.isActive() : " + token.isActive());
@@ -196,9 +215,9 @@ public class AuthenticationManager {
             }
 
             UserSessionModel session = realm.getUserSession(token.getSessionState());
-            if (session == null || session.getExpires() < Time.currentTime()) {
+            if (!isSessionValid(realm, session)) {
+                if (session != null) logout(realm, session, uriInfo);
                 logger.info("User session not active");
-                expireIdentityCookie(realm, uriInfo);
                 return null;
             }
 
diff --git a/services/src/main/java/org/keycloak/services/managers/ModelToRepresentation.java b/services/src/main/java/org/keycloak/services/managers/ModelToRepresentation.java
index f2d34b8..91c7686 100755
--- a/services/src/main/java/org/keycloak/services/managers/ModelToRepresentation.java
+++ b/services/src/main/java/org/keycloak/services/managers/ModelToRepresentation.java
@@ -90,8 +90,8 @@ public class ModelToRepresentation {
         rep.setVerifyEmail(realm.isVerifyEmail());
         rep.setResetPasswordAllowed(realm.isResetPasswordAllowed());
         rep.setAccessTokenLifespan(realm.getAccessTokenLifespan());
-        rep.setCentralLoginLifespan(realm.getCentralLoginLifespan());
-        rep.setRefreshTokenLifespan(realm.getRefreshTokenLifespan());
+        rep.setSsoSessionIdleTimeout(realm.getSsoSessionIdleTimeout());
+        rep.setSsoSessionMaxLifespan(realm.getSsoSessionMaxLifespan());
         rep.setAccessCodeLifespan(realm.getAccessCodeLifespan());
         rep.setAccessCodeLifespanUserAction(realm.getAccessCodeLifespanUserAction());
         rep.setSmtpServer(realm.getSmtpConfig());
diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
index 8410776..43e9630 100755
--- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
@@ -186,8 +186,8 @@ public class RealmManager {
             realm.setAccessCodeLifespanUserAction(rep.getAccessCodeLifespanUserAction());
         if (rep.getNotBefore() != null) realm.setNotBefore(rep.getNotBefore());
         if (rep.getAccessTokenLifespan() != null) realm.setAccessTokenLifespan(rep.getAccessTokenLifespan());
-        if (rep.getRefreshTokenLifespan() != null) realm.setRefreshTokenLifespan(rep.getRefreshTokenLifespan());
-        if (rep.getCentralLoginLifespan() != null) realm.setCentralLoginLifespan(rep.getCentralLoginLifespan());
+        if (rep.getSsoSessionIdleTimeout() != null) realm.setSsoSessionIdleTimeout(rep.getSsoSessionIdleTimeout());
+        if (rep.getSsoSessionMaxLifespan() != null) realm.setSsoSessionMaxLifespan(rep.getSsoSessionMaxLifespan());
         if (rep.getRequiredCredentials() != null) {
             realm.updateRequiredCredentials(rep.getRequiredCredentials());
         }
@@ -313,10 +313,10 @@ public class RealmManager {
         if (rep.getAccessTokenLifespan() != null) newRealm.setAccessTokenLifespan(rep.getAccessTokenLifespan());
         else newRealm.setAccessTokenLifespan(300);
 
-        if (rep.getRefreshTokenLifespan() != null) newRealm.setRefreshTokenLifespan(rep.getRefreshTokenLifespan());
-        else newRealm.setRefreshTokenLifespan(36000);
-        if (rep.getCentralLoginLifespan() != null) newRealm.setCentralLoginLifespan(rep.getCentralLoginLifespan());
-        else newRealm.setCentralLoginLifespan(300);
+        if (rep.getSsoSessionIdleTimeout() != null) newRealm.setSsoSessionIdleTimeout(rep.getSsoSessionIdleTimeout());
+        else newRealm.setSsoSessionIdleTimeout(600);
+        if (rep.getSsoSessionMaxLifespan() != null) newRealm.setSsoSessionMaxLifespan(rep.getSsoSessionMaxLifespan());
+        else newRealm.setSsoSessionMaxLifespan(36000);
 
         if (rep.getAccessCodeLifespan() != null) newRealm.setAccessCodeLifespan(rep.getAccessCodeLifespan());
         else newRealm.setAccessCodeLifespan(60);
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 4995e81..89adfaf 100755
--- a/services/src/main/java/org/keycloak/services/managers/TokenManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/TokenManager.java
@@ -23,6 +23,7 @@ import org.keycloak.representations.RefreshToken;
 import org.keycloak.util.Time;
 
 import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.UriInfo;
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.util.HashSet;
@@ -106,7 +107,7 @@ public class TokenManager {
         return code;
     }
 
-    public AccessToken refreshAccessToken(RealmModel realm, ClientModel client, String encodedRefreshToken, Audit audit) throws OAuthErrorException {
+    public AccessToken refreshAccessToken(UriInfo uriInfo, RealmModel realm, ClientModel client, String encodedRefreshToken, Audit audit) throws OAuthErrorException {
         JWSInput jws = new JWSInput(encodedRefreshToken);
         RefreshToken refreshToken = null;
         try {
@@ -137,7 +138,9 @@ public class TokenManager {
         }
 
         UserSessionModel session = realm.getUserSession(refreshToken.getSessionState());
-        if (session == null || session.getExpires() < Time.currentTime()) {
+        int currentTime = Time.currentTime();
+        if (!AuthenticationManager.isSessionValid(realm, session)) {
+            AuthenticationManager.logout(realm, session, uriInfo);
             throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Session not active", "Session not active");
         }
 
@@ -191,6 +194,12 @@ public class TokenManager {
         AccessToken accessToken = initToken(realm, client, user, session);
         accessToken.setRealmAccess(refreshToken.getRealmAccess());
         accessToken.setResourceAccess(refreshToken.getResourceAccess());
+
+        // only refresh session if next token refresh will be after idle timeout
+        if (currentTime + realm.getAccessTokenLifespan() > session.getLastSessionRefresh() + realm.getSsoSessionIdleTimeout()) {
+            session.setLastSessionRefresh(currentTime);
+        }
+
         return accessToken;
     }
 
@@ -375,7 +384,7 @@ public class TokenManager {
             refreshToken = new RefreshToken(accessToken);
             refreshToken.id(KeycloakModelUtils.generateId());
             refreshToken.issuedNow();
-            refreshToken.expiration(Time.currentTime() + realm.getRefreshTokenLifespan());
+            refreshToken.expiration(Time.currentTime() + realm.getSsoSessionIdleTimeout());
             return this;
         }
 
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 d436318..d31aab5 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,9 @@ 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;
-            authManager.createLoginCookie(location, realm, accessCode.getUser(), session, uriInfo, rememberMe);
+            // refresh the cookies!
+            authManager.createLoginCookie(realm, accessCode.getUser(), session, uriInfo, rememberMe);
+            if (rememberMe) authManager.createRememberMeCookie(realm, uriInfo);
             return location.build();
         }
     }
diff --git a/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java b/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java
index e760263..4c6139c 100755
--- a/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java
@@ -408,7 +408,8 @@ public class RequiredActionsService {
             AuthenticationManager authManager = new AuthenticationManager(providerSession);
 
             UserSessionModel session = realm.getUserSession(accessCode.getSessionState());
-            if (session == null || session.getExpires() < Time.currentTime()) {
+            if (!AuthenticationManager.isSessionValid(realm, session)) {
+                AuthenticationManager.logout(realm, session, uriInfo);
                 return Flows.oauth(realm, request, uriInfo, authManager, tokenManager).redirectError(accessCode.getClient(), "access_denied", accessCode.getState(), accessCode.getRedirectUri());
             }
             audit.session(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 a262c83..e9c2c80 100755
--- a/services/src/main/java/org/keycloak/services/resources/TokenService.java
+++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java
@@ -58,7 +58,6 @@ import javax.ws.rs.core.Context;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
-import javax.ws.rs.core.NewCookie;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.SecurityContext;
 import javax.ws.rs.core.UriBuilder;
@@ -297,13 +296,14 @@ public class TokenService {
         String refreshToken = form.getFirst(OAuth2Constants.REFRESH_TOKEN);
         AccessToken accessToken = null;
         try {
-            accessToken = tokenManager.refreshAccessToken(realm, client, refreshToken, audit);
+            accessToken = tokenManager.refreshAccessToken(uriInfo, realm, client, refreshToken, audit);
         } catch (OAuthErrorException e) {
             Map<String, String> error = new HashMap<String, String>();
             error.put(OAuth2Constants.ERROR, e.getError());
             if (e.getDescription() != null) error.put(OAuth2Constants.ERROR_DESCRIPTION, e.getDescription());
             audit.error(Errors.INVALID_TOKEN);
-            throw new BadRequestException("OAuth Error", e, Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build());
+            logger.error("OAuth Error", e);
+            return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build();
         }
 
         AccessTokenResponse res = tokenManager.responseBuilder(realm, client, audit)
@@ -374,7 +374,7 @@ public class TokenService {
         AuthenticationStatus status = authManager.authenticateForm(clientConnection, realm, formData);
 
         if (remember) {
-            authManager.createRememberMeCookie(response, realm, uriInfo);
+            authManager.createRememberMeCookie(realm, uriInfo);
         } else {
             authManager.expireRememberMeCookie(realm, uriInfo);
         }
@@ -636,7 +636,8 @@ public class TokenService {
         }
 
         UserSessionModel session = realm.getUserSession(accessCode.getSessionState());
-        if (session == null || session.getExpires() < Time.currentTime()) {
+        if (!AuthenticationManager.isSessionValid(realm, session)) {
+            AuthenticationManager.logout(realm, session, uriInfo);
             Map<String, String> res = new HashMap<String, String>();
             res.put(OAuth2Constants.ERROR, "invalid_grant");
             res.put(OAuth2Constants.ERROR_DESCRIPTION, "Session not active");
@@ -855,15 +856,7 @@ public class TokenService {
 
     private void logout(UserSessionModel session) {
         UserModel user = session.getUser();
-
-        logger.infov("Logging out: {0} ({1})", user.getLoginName(), session.getId());
-
-        realm.removeUserSession(session);
-        authManager.expireIdentityCookie(realm, uriInfo);
-        authManager.expireRememberMeCookie(realm, uriInfo);
-
-        resourceAdminManager.logoutUser(uriInfo.getRequestUri(), realm, user.getId(), session.getId());
-
+        authManager.logout(realm, session, uriInfo);
         audit.user(user).session(session).success();
     }
 
@@ -915,7 +908,8 @@ public class TokenService {
         }
 
         UserSessionModel session = realm.getUserSession(accessCodeEntry.getSessionState());
-        if (session == null || session.getExpires() < Time.currentTime()) {
+        if (!AuthenticationManager.isSessionValid(realm, session)) {
+            AuthenticationManager.logout(realm, session, uriInfo);
             audit.error(Errors.INVALID_CODE);
             return oauth.forwardToSecurityFailure("Session not active");
         }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java
index 46c7bb2..ba5aaab 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java
@@ -28,6 +28,7 @@ import org.junit.Test;
 import org.keycloak.OAuth2Constants;
 import org.keycloak.models.ApplicationModel;
 import org.keycloak.models.Constants;
+import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
@@ -156,4 +157,67 @@ public class AdapterTest {
 
 
     }
+
+    @Test
+    public void testLoginSSOIdle() throws Exception {
+        // test login to customer-portal which does a bearer request to customer-db
+        driver.navigate().to("http://localhost:8081/customer-portal");
+        System.out.println("Current url: " + driver.getCurrentUrl());
+        Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL));
+        loginPage.login("bburke@redhat.com", "password");
+        System.out.println("Current url: " + driver.getCurrentUrl());
+        Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/customer-portal");
+        String pageSource = driver.getPageSource();
+        System.out.println(pageSource);
+        Assert.assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen"));
+
+        KeycloakSession session = keycloakRule.startSession();
+        RealmModel realm = session.getRealmByName("demo");
+        int originalIdle = realm.getSsoSessionIdleTimeout();
+        realm.setSsoSessionIdleTimeout(1);
+        keycloakRule.stopSession(session, true);
+
+        Thread.sleep(2000);
+
+
+        // test SSO
+        driver.navigate().to("http://localhost:8081/product-portal");
+        Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL));
+
+        session = keycloakRule.startSession();
+        realm = session.getRealmByName("demo");
+        realm.setSsoSessionIdleTimeout(originalIdle);
+        keycloakRule.stopSession(session, true);
+    }
+    @Test
+    public void testLoginSSOMax() throws Exception {
+        // test login to customer-portal which does a bearer request to customer-db
+        driver.navigate().to("http://localhost:8081/customer-portal");
+        System.out.println("Current url: " + driver.getCurrentUrl());
+        Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL));
+        loginPage.login("bburke@redhat.com", "password");
+        System.out.println("Current url: " + driver.getCurrentUrl());
+        Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/customer-portal");
+        String pageSource = driver.getPageSource();
+        System.out.println(pageSource);
+        Assert.assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen"));
+
+        KeycloakSession session = keycloakRule.startSession();
+        RealmModel realm = session.getRealmByName("demo");
+        int original = realm.getSsoSessionMaxLifespan();
+        realm.setSsoSessionMaxLifespan(1);
+        keycloakRule.stopSession(session, true);
+
+        Thread.sleep(2000);
+
+
+        // test SSO
+        driver.navigate().to("http://localhost:8081/product-portal");
+        Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL));
+
+        session = keycloakRule.startSession();
+        realm = session.getRealmByName("demo");
+        realm.setSsoSessionMaxLifespan(original);
+        keycloakRule.stopSession(session, true);
+    }
 }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeRoleTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeRoleTest.java
index 5954d10..aa5004b 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeRoleTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeRoleTest.java
@@ -60,9 +60,9 @@ public class CompositeRoleTest {
             RealmModel realm = manager.createRealm("Test");
             manager.generateRealmKeys(realm);
             realmPublicKey = realm.getPublicKey();
-            realm.setCentralLoginLifespan(3000);
+            realm.setSsoSessionIdleTimeout(3000);
             realm.setAccessTokenLifespan(10000);
-            realm.setRefreshTokenLifespan(10000);
+            realm.setSsoSessionMaxLifespan(10000);
             realm.setAccessCodeLifespanUserAction(1000);
             realm.setAccessCodeLifespan(1000);
             realm.setSslNotRequired(true);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java
index 030bec7..cead905 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java
@@ -29,8 +29,14 @@ import org.keycloak.OAuth2Constants;
 import org.keycloak.audit.Details;
 import org.keycloak.audit.Errors;
 import org.keycloak.audit.Event;
+import org.keycloak.models.Config;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.provider.ProviderSession;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.RefreshToken;
+import org.keycloak.services.managers.RealmManager;
 import org.keycloak.testsuite.AssertEvents;
 import org.keycloak.testsuite.OAuthClient;
 import org.keycloak.testsuite.OAuthClient.AccessTokenResponse;
@@ -41,9 +47,7 @@ import org.keycloak.testsuite.rule.WebRule;
 import org.keycloak.util.Time;
 import org.openqa.selenium.WebDriver;
 
-import static org.hamcrest.Matchers.allOf;
-import static org.hamcrest.Matchers.greaterThanOrEqualTo;
-import static org.hamcrest.Matchers.lessThanOrEqualTo;
+import static org.hamcrest.Matchers.*;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 
@@ -93,7 +97,8 @@ public class RefreshTokenTest {
         Assert.assertEquals("bearer", tokenResponse.getTokenType());
 
         Assert.assertThat(token.getExpiration() - Time.currentTime(), allOf(greaterThanOrEqualTo(250), lessThanOrEqualTo(300)));
-        Assert.assertThat(refreshToken.getExpiration() - Time.currentTime(), allOf(greaterThanOrEqualTo(35950), lessThanOrEqualTo(36000)));
+        int actual = refreshToken.getExpiration() - Time.currentTime();
+        Assert.assertThat(actual, allOf(greaterThanOrEqualTo(559), lessThanOrEqualTo(600)));
 
         Assert.assertEquals(sessionId, refreshToken.getSessionState());
 
@@ -161,4 +166,131 @@ public class RefreshTokenTest {
         events.clear();
     }
 
+    @Test
+    public void testUserSessionRefreshAndIdle() throws Exception {
+        oauth.doLogin("test-user@localhost", "password");
+
+        Event loginEvent = events.expectLogin().assertEvent();
+
+        String sessionId = loginEvent.getSessionId();
+
+        String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
+        OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(code, "password");
+
+        events.poll();
+
+        String refreshId = oauth.verifyRefreshToken(tokenResponse.getRefreshToken()).getId();
+
+        KeycloakSession session = keycloakRule.startSession();
+        RealmModel realm = session.getRealmByName("test");
+        UserSessionModel userSession = realm.getUserSession(sessionId);
+        int last = userSession.getLastSessionRefresh();
+        keycloakRule.stopSession(session, false);
+
+        Thread.sleep(2000);
+
+        tokenResponse = oauth.doRefreshTokenRequest(tokenResponse.getRefreshToken(), "password");
+
+        AccessToken refreshedToken = oauth.verifyToken(tokenResponse.getAccessToken());
+        RefreshToken refreshedRefreshToken = oauth.verifyRefreshToken(tokenResponse.getRefreshToken());
+
+        Assert.assertEquals(200, tokenResponse.getStatusCode());
+
+        session = keycloakRule.startSession();
+        realm = session.getRealmByName("test");
+        userSession = realm.getUserSession(sessionId);
+        int next = userSession.getLastSessionRefresh();
+        keycloakRule.stopSession(session, false);
+
+        // should not update last refresh because the access token interval is way less than idle timeout
+        Assert.assertEquals(last, next);
+
+
+
+        session = keycloakRule.startSession();
+        realm = session.getRealmByName("test");
+        int lastAccessTokenLifespan = realm.getAccessTokenLifespan();
+        realm.setAccessTokenLifespan(100000);
+        keycloakRule.stopSession(session, true);
+
+        Thread.sleep(2000);
+        tokenResponse = oauth.doRefreshTokenRequest(tokenResponse.getRefreshToken(), "password");
+
+        session = keycloakRule.startSession();
+        realm = session.getRealmByName("test");
+        userSession = realm.getUserSession(sessionId);
+        next = userSession.getLastSessionRefresh();
+        keycloakRule.stopSession(session, false);
+
+        // lastSEssionRefresh should be updated because access code lifespan is higher than sso idle timeout
+        Assert.assertThat(next, allOf(greaterThan(last), lessThan(last + 6)));
+
+        session = keycloakRule.startSession();
+        realm = session.getRealmByName("test");
+        int originalIdle = realm.getSsoSessionIdleTimeout();
+        realm.setSsoSessionIdleTimeout(1);
+        keycloakRule.stopSession(session, true);
+
+        events.clear();
+        Thread.sleep(2000);
+        tokenResponse = oauth.doRefreshTokenRequest(tokenResponse.getRefreshToken(), "password");
+
+        // test idle timeout
+        assertEquals(400, tokenResponse.getStatusCode());
+        assertNull(tokenResponse.getAccessToken());
+        assertNull(tokenResponse.getRefreshToken());
+
+        events.expectRefresh(refreshId, sessionId).error(Errors.INVALID_TOKEN);
+
+        session = keycloakRule.startSession();
+        realm = session.getRealmByName("test");
+        realm.setSsoSessionIdleTimeout(originalIdle);
+        realm.setAccessTokenLifespan(lastAccessTokenLifespan);
+        keycloakRule.stopSession(session, true);
+
+        events.clear();
+    }
+
+    @Test
+    public void refreshTokenUserSessionMaxLifespan() throws Exception {
+        oauth.doLogin("test-user@localhost", "password");
+
+        Event loginEvent = events.expectLogin().assertEvent();
+
+        String sessionId = loginEvent.getSessionId();
+
+        String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
+        OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(code, "password");
+
+        events.poll();
+
+        String refreshId = oauth.verifyRefreshToken(tokenResponse.getRefreshToken()).getId();
+
+        KeycloakSession session = keycloakRule.startSession();
+        RealmModel realm = session.getRealmByName("test");
+        int maxLifespan = realm.getSsoSessionMaxLifespan();
+        realm.setSsoSessionMaxLifespan(1);
+        keycloakRule.stopSession(session, true);
+
+        Thread.sleep(1000);
+
+        tokenResponse = oauth.doRefreshTokenRequest(tokenResponse.getRefreshToken(), "password");
+
+        assertEquals(400, tokenResponse.getStatusCode());
+        assertNull(tokenResponse.getAccessToken());
+        assertNull(tokenResponse.getRefreshToken());
+
+        session = keycloakRule.startSession();
+        realm = session.getRealmByName("test");
+        realm.setSsoSessionMaxLifespan(maxLifespan);
+        keycloakRule.stopSession(session, true);
+
+
+        events.expectRefresh(refreshId, sessionId).error(Errors.INVALID_TOKEN);
+
+        events.clear();
+    }
+
+
+
 }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java
index f0e37cc..9312eee 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java
@@ -133,4 +133,17 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
         byte[] bytes = os.toByteArray();
         return JsonSerialization.readValue(bytes, RealmRepresentation.class);
     }
+
+    public KeycloakSession startSession() {
+        KeycloakSession session = server.getKeycloakSessionFactory().createSession();
+        session.getTransaction().begin();
+        return session;
+    }
+
+    public void stopSession(KeycloakSession session, boolean commit) {
+        if (commit) {
+            session.getTransaction().commit();
+        }
+        session.close();
+    }
 }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/KeycloakRule.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/KeycloakRule.java
index d04cc21..4096658 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/KeycloakRule.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/KeycloakRule.java
@@ -81,19 +81,6 @@ public class KeycloakRule extends AbstractKeycloakRule {
         }
     }
 
-    public KeycloakSession startSession() {
-        KeycloakSession session = server.getKeycloakSessionFactory().createSession();
-        session.getTransaction().begin();
-        return session;
-    }
-
-    public void stopSession(KeycloakSession session, boolean commit) {
-        if (commit) {
-            session.getTransaction().commit();
-        }
-        session.close();
-    }
-
     public void removeUserSession(String sessionId) {
         KeycloakSession keycloakSession = startSession();
         RealmModel realm = keycloakSession.getRealm("test");