keycloak-aplcache

remember me

2/23/2014 1:30:32 PM

Details

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 9c5e974..760d501 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
@@ -549,7 +549,7 @@ module.controller('RealmTokenDetailCtrl', function($scope, Realm, realm, $http, 
         $scope.realm.accessTokenLifespan = TimeUnit.convert($scope.realm.accessTokenLifespan, from, to);
     });
 
-    $scope.realm.centralLoginLifespanUnit = TimeUnit.autoUnit(realm.accessTokenLifespan);
+    $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);
diff --git a/forms/common-themes/src/main/resources/theme/login/base/login.ftl b/forms/common-themes/src/main/resources/theme/login/base/login.ftl
index 4fd6f63..b609784 100755
--- a/forms/common-themes/src/main/resources/theme/login/base/login.ftl
+++ b/forms/common-themes/src/main/resources/theme/login/base/login.ftl
@@ -28,6 +28,13 @@
 
             <div class="${properties.kcFormGroupClass!}">
                 <div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
+                    <#if realm.rememberMe>
+                        <div class="checkbox">
+                            <label>
+                                <input id="rememberMe" name="rememberMe" type="checkbox" tabindex="3"> Remember Me
+                            </label>
+                        </div>
+                    </#if>
                     <div class="${properties.kcFormOptionsWrapperClass!}">
                         <#if realm.registrationAllowed>
                             <span>${rb.noAccount} <a href="${url.registrationUrl}">${rb.register}</a></span>
@@ -43,7 +50,7 @@
                         <input class="btn btn-primary btn-lg" name="login" id="kc-login" type="submit" value="${rb.logIn}"/>
                         <input class="btn btn-default btn-lg" name="cancel" id="kc-cancel" type="submit" value="${rb.cancel}"/>
                     </div>
-                </div>
+                 </div>
             </div>
         </form>
     <#elseif section = "info" >
diff --git a/forms/common-themes/src/main/resources/theme/login/base/messages/messages.properties b/forms/common-themes/src/main/resources/theme/login/base/messages/messages.properties
old mode 100644
new mode 100755
index 79c8e7b..9aa907e
--- a/forms/common-themes/src/main/resources/theme/login/base/messages/messages.properties
+++ b/forms/common-themes/src/main/resources/theme/login/base/messages/messages.properties
@@ -16,6 +16,7 @@ firstName=First name
 lastName=Last name
 email=Email
 password=Password
+rememberMe=Remember me
 passwordConfirm=Confirm password
 passwordNew=New Password
 passwordNewConfirm=New Password confirmation
diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/RealmBean.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/RealmBean.java
index 8a71244..a6b52f0 100755
--- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/RealmBean.java
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/RealmBean.java
@@ -49,5 +49,9 @@ public class RealmBean {
     public boolean isResetPasswordAllowed() {
         return realm.isResetPasswordAllowed();
     }
+
+    public boolean isRememberMe() {
+        return realm.isRememberMe();
+    }
     
 }
diff --git a/services/src/main/java/org/keycloak/services/managers/AccessCodeEntry.java b/services/src/main/java/org/keycloak/services/managers/AccessCodeEntry.java
index e3268cf..a94cb1b 100755
--- a/services/src/main/java/org/keycloak/services/managers/AccessCodeEntry.java
+++ b/services/src/main/java/org/keycloak/services/managers/AccessCodeEntry.java
@@ -22,6 +22,7 @@ public class AccessCodeEntry {
     protected String code;
     protected String state;
     protected String redirectUri;
+    protected boolean rememberMe;
 
     protected long expiration;
     protected RealmModel realm;
@@ -119,4 +120,12 @@ public class AccessCodeEntry {
     public void setRedirectUri(String redirectUri) {
         this.redirectUri = redirectUri;
     }
+
+    public boolean isRememberMe() {
+        return rememberMe;
+    }
+
+    public void setRememberMe(boolean rememberMe) {
+        this.rememberMe = rememberMe;
+    }
 }
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 237b7dc..b82f585 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -39,6 +39,7 @@ public class AuthenticationManager {
     protected static Logger logger = Logger.getLogger(AuthenticationManager.class);
     public static final String FORM_USERNAME = "username";
     public static final String KEYCLOAK_IDENTITY_COOKIE = "KEYCLOAK_IDENTITY";
+    public static final String KEYCLOAK_REMEMBER_ME = "KEYCLOAK_REMEMBER_ME";
 
     public AccessToken createIdentityToken(RealmModel realm, UserModel user) {
         AccessToken token = new AccessToken();
@@ -52,26 +53,26 @@ public class AuthenticationManager {
         return token;
     }
 
-    public NewCookie createLoginCookie(RealmModel realm, UserModel user, UriInfo uriInfo) {
+    public NewCookie createLoginCookie(RealmModel realm, UserModel user, UriInfo uriInfo, boolean rememberMe) {
         String cookieName = KEYCLOAK_IDENTITY_COOKIE;
         String cookiePath = getIdentityCookiePath(realm, uriInfo);
-        return createLoginCookie(realm, user, null, cookieName, cookiePath);
+        return createLoginCookie(realm, user, null, cookieName, cookiePath, rememberMe);
     }
 
     public NewCookie createSaasIdentityCookie(RealmModel realm, UserModel user, UriInfo uriInfo) {
         String cookieName = AdminService.SAAS_IDENTITY_COOKIE;
         URI uri = AdminService.saasCookiePath(uriInfo).build();
         String cookiePath = uri.getRawPath();
-        return createLoginCookie(realm, user, null, cookieName, cookiePath);
+        return createLoginCookie(realm, user, null, cookieName, cookiePath, false);
     }
 
     public NewCookie createAccountIdentityCookie(RealmModel realm, UserModel user, UserModel client, URI uri) {
         String cookieName = AccountService.ACCOUNT_IDENTITY_COOKIE;
         String cookiePath = uri.getRawPath();
-        return createLoginCookie(realm, user, client, cookieName, cookiePath);
+        return createLoginCookie(realm, user, client, cookieName, cookiePath, false);
     }
 
-    protected NewCookie createLoginCookie(RealmModel realm, UserModel user, UserModel client, String cookieName, String cookiePath) {
+    protected NewCookie createLoginCookie(RealmModel realm, UserModel user, UserModel client, String cookieName, String cookiePath, boolean rememberMe) {
         AccessToken identityToken = createIdentityToken(realm, user);
         if (client != null) {
             identityToken.issuedFor(client.getLoginName());
@@ -80,15 +81,22 @@ public class AuthenticationManager {
         boolean secureOnly = !realm.isSslNotRequired();
         logger.debug("creatingLoginCookie - name: {0} path: {1}", cookieName, cookiePath);
         int maxAge = NewCookie.DEFAULT_MAX_AGE;
-        /*
-        if (realm.isRememberMe()) {
+        if (rememberMe) {
             maxAge = realm.getCentralLoginLifespan();
+            logger.info("createLoginCookie maxAge: " + maxAge);
         }
-        */
         NewCookie cookie = new NewCookie(cookieName, encoded, cookiePath, null, null, maxAge, secureOnly, true);
         return cookie;
     }
 
+    public NewCookie 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, true);
+        return cookie;
+    }
+
     protected String encodeToken(RealmModel realm, Object token) {
         String encodedToken = new JWSBuilder()
                 .jsonContent(token)
@@ -103,6 +111,12 @@ public class AuthenticationManager {
         String cookieName = KEYCLOAK_IDENTITY_COOKIE;
         expireCookie(cookieName, path);
     }
+    public void expireRememberMeCookie(RealmModel realm, UriInfo uriInfo) {
+        logger.debug("Expiring remember me cookie");
+        String path = getIdentityCookiePath(realm, uriInfo);
+        String cookieName = KEYCLOAK_REMEMBER_ME;
+        expireCookie(cookieName, path);
+    }
 
     protected String getIdentityCookiePath(RealmModel realm, UriInfo uriInfo) {
         URI uri = RealmsResource.realmBaseUrl(uriInfo).build(realm.getName());
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 626aab8..8524807 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
@@ -35,6 +35,7 @@ import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.managers.TokenManager;
 import org.keycloak.services.resources.TokenService;
 
+import javax.ws.rs.core.Cookie;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
@@ -69,13 +70,20 @@ public class OAuthFlows {
     }
 
     public Response redirectAccessCode(AccessCodeEntry accessCode, String state, String redirect) {
+        return redirectAccessCode(accessCode, state, redirect, false);
+    }
+
+
+    public Response redirectAccessCode(AccessCodeEntry accessCode, String state, String redirect, boolean rememberMe) {
         String code = accessCode.getCode();
         UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam("code", code);
         log.debug("redirectAccessCode: state: {0}", state);
         if (state != null)
             redirectUri.queryParam("state", state);
         Response.ResponseBuilder location = Response.status(302).location(redirectUri.build());
-        location.cookie(authManager.createLoginCookie(realm, accessCode.getUser(), uriInfo));
+        Cookie remember = request.getHttpHeaders().getCookies().get(AuthenticationManager.KEYCLOAK_REMEMBER_ME);
+        rememberMe = rememberMe || remember != null;
+        location.cookie(authManager.createLoginCookie(realm, accessCode.getUser(), uriInfo, rememberMe));
         return location.build();
     }
 
@@ -89,6 +97,11 @@ public class OAuthFlows {
     }
 
     public Response processAccessCode(String scopeParam, String state, String redirect, UserModel client, UserModel user) {
+        return processAccessCode(scopeParam, state, redirect, client, user, false);
+    }
+
+
+    public Response processAccessCode(String scopeParam, String state, String redirect, UserModel client, UserModel user, boolean rememberMe) {
         isTotpConfigurationRequired(user);
         isEmailVerificationRequired(user);
 
@@ -121,7 +134,7 @@ public class OAuthFlows {
         }
 
         if (redirect != null) {
-            return redirectAccessCode(accessCode, state, redirect);
+            return redirectAccessCode(accessCode, state, redirect, rememberMe);
         } else {
             return null;
         }
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 4983d37..24d8090 100755
--- a/services/src/main/java/org/keycloak/services/resources/TokenService.java
+++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java
@@ -45,6 +45,7 @@ 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;
@@ -235,10 +236,20 @@ public class TokenService {
 
         AuthenticationStatus status = authManager.authenticateForm(realm, user, formData);
 
+        String rememberMe = formData.getFirst("rememberMe");
+        boolean remember = rememberMe != null && rememberMe.equalsIgnoreCase("on");
+        logger.debug("*** Remember me: " + remember);
+        if (remember) {
+            NewCookie cookie = authManager.createRememberMeCookie(realm, uriInfo);
+            response.addNewCookie(cookie);
+        } else {
+            authManager.expireRememberMeCookie(realm, uriInfo);
+        }
+
         switch (status) {
             case SUCCESS:
             case ACTIONS_REQUIRED:
-                return oauth.processAccessCode(scopeParam, state, redirect, client, user);
+                return oauth.processAccessCode(scopeParam, state, redirect, client, user, remember);
             case ACCOUNT_DISABLED:
                 return Flows.forms(realm, request, uriInfo).setError(Messages.ACCOUNT_DISABLED).setFormData(formData).createLogin();
             case MISSING_TOTP:
@@ -544,6 +555,7 @@ public class TokenService {
         if (user != null) {
             logger.info("Logging out: {0}", user.getLoginName());
             authManager.expireIdentityCookie(realm, uriInfo);
+            authManager.expireRememberMeCookie(realm, uriInfo);
             resourceAdminManager.singleLogOut(realm, user.getId());
         } else {
             logger.info("No user logged in for logout");