keycloak-aplcache

cookie login

7/25/2013 10:47:52 AM

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 1334d92..cddd1ca 100755
--- a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
@@ -11,8 +11,8 @@ import java.util.Set;
 public class RealmRepresentation {
     protected String self; // link
     protected String realm;
-    protected long tokenLifespan;
-    protected long accessCodeLifespan;
+    protected int tokenLifespan;
+    protected int accessCodeLifespan;
     protected boolean enabled;
     protected boolean sslNotRequired;
     protected boolean cookieLoginAllowed;
@@ -98,11 +98,11 @@ public class RealmRepresentation {
         this.cookieLoginAllowed = cookieLoginAllowed;
     }
 
-    public long getTokenLifespan() {
+    public int getTokenLifespan() {
         return tokenLifespan;
     }
 
-    public void setTokenLifespan(long tokenLifespan) {
+    public void setTokenLifespan(int tokenLifespan) {
         this.tokenLifespan = tokenLifespan;
     }
 
@@ -138,11 +138,11 @@ public class RealmRepresentation {
         this.requiredCredentials = requiredCredentials;
     }
 
-    public long getAccessCodeLifespan() {
+    public int getAccessCodeLifespan() {
         return accessCodeLifespan;
     }
 
-    public void setAccessCodeLifespan(long accessCodeLifespan) {
+    public void setAccessCodeLifespan(int accessCodeLifespan) {
         this.accessCodeLifespan = accessCodeLifespan;
     }
 
diff --git a/examples/as7-eap-demo/product-app/src/main/webapp/WEB-INF/resteasy-oauth.json b/examples/as7-eap-demo/product-app/src/main/webapp/WEB-INF/resteasy-oauth.json
index 095cd12..53ad29f 100755
--- a/examples/as7-eap-demo/product-app/src/main/webapp/WEB-INF/resteasy-oauth.json
+++ b/examples/as7-eap-demo/product-app/src/main/webapp/WEB-INF/resteasy-oauth.json
@@ -1,7 +1,7 @@
 {
   "realm" : "demo",
   "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
-  "auth-url" : "http://localhost:8080/auth-server/rest/realms/demo/tokens/auth/request",
+  "auth-url" : "http://localhost:8080/auth-server/rest/realms/demo/tokens/login",
   "code-url" : "http://localhost:8080/auth-server/rest/realms/demo/tokens/access/codes",
    "ssl-not-required" : true,
    "client-id" : "product-portal",
diff --git a/examples/as7-eap-demo/server/src/main/webapp/META-INF/testrealm.json b/examples/as7-eap-demo/server/src/main/webapp/META-INF/testrealm.json
index 2d8d016..150c218 100755
--- a/examples/as7-eap-demo/server/src/main/webapp/META-INF/testrealm.json
+++ b/examples/as7-eap-demo/server/src/main/webapp/META-INF/testrealm.json
@@ -3,6 +3,8 @@
    "enabled" : true,
    "tokenLifespan" : 6000,
    "accessCodeLifespan" : 30,
+   "sslNotRequired" : true,
+   "cookieLoginAllowed" : true,
    "privateKey" : "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
    "publicKey" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
    "requiredCredentials" : [
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 e830b9a..fc62795 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -1,6 +1,8 @@
 package org.keycloak.services.managers;
 
 import org.jboss.resteasy.logging.Logger;
+import org.jboss.resteasy.spi.HttpResponse;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
 import org.keycloak.RSATokenVerifier;
 import org.keycloak.VerificationException;
 import org.keycloak.representations.SkeletonKeyToken;
@@ -14,8 +16,10 @@ import org.picketlink.idm.credential.UsernamePasswordCredentials;
 import org.picketlink.idm.model.User;
 
 import javax.ws.rs.NotAuthorizedException;
+import javax.ws.rs.core.Cookie;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.NewCookie;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -41,6 +45,39 @@ public class AuthenticationManager {
         return realm.isRealmAdmin(user);
     }
 
+    protected void expireIdentityCookie(Cookie cookie) {
+        HttpResponse response = ResteasyProviderFactory.getContextData(HttpResponse.class);
+        if (response == null) return;
+        NewCookie expireIt = new NewCookie(cookie.getName(), "", cookie.getPath(), null, "Expiring cookie", 0, false);
+        response.addNewCookie(expireIt);
+    }
+
+    public User authenticateIdentityCookie(RealmModel realm, HttpHeaders headers) {
+        Cookie cookie = headers.getCookies().get(TokenManager.KEYCLOAK_IDENTITY_COOKIE);
+        if (cookie == null) return null;
+
+        String tokenString = cookie.getValue();
+        try {
+            SkeletonKeyToken token = RSATokenVerifier.verifyToken(tokenString, realm.getPublicKey(), realm.getId());
+            if (!token.isActive()) {
+                logger.info("identity cookie expired");
+                expireIdentityCookie(cookie);
+                return null;
+            }
+            User user = realm.getIdm().getUser(token.getPrincipal());
+            if (user == null || !user.isEnabled()) {
+                logger.info("Unknown user in identity cookie");
+                expireIdentityCookie(cookie);
+                return null;
+            }
+            return user;
+        } catch (VerificationException e) {
+            logger.info("Failed to verify identity cookie", e);
+            expireIdentityCookie(cookie);
+        }
+        return null;
+    }
+
     public User authenticateBearerToken(RealmModel realm, HttpHeaders headers) {
         String tokenString = null;
         String authHeader = headers.getHeaderString(HttpHeaders.AUTHORIZATION);
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 aceeab8..6e7f287 100755
--- a/services/src/main/java/org/keycloak/services/managers/TokenManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/TokenManager.java
@@ -7,12 +7,17 @@ import org.keycloak.representations.SkeletonKeyScope;
 import org.keycloak.representations.SkeletonKeyToken;
 import org.keycloak.services.models.RealmModel;
 import org.keycloak.services.models.ResourceModel;
+import org.keycloak.services.resources.RealmsResource;
+import org.keycloak.services.resources.TokenService;
 import org.picketlink.idm.model.User;
 
 import javax.ws.rs.ForbiddenException;
+import javax.ws.rs.core.NewCookie;
 import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
+import java.net.URI;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -26,6 +31,7 @@ import java.util.concurrent.ConcurrentHashMap;
  */
 public class TokenManager {
 
+    public static final String KEYCLOAK_IDENTITY_COOKIE = "KEYCLOAK_IDENTITY";
     protected Map<String, AccessCodeEntry> accessCodeMap = new ConcurrentHashMap<String, AccessCodeEntry>();
 
     public void clearAccessCodes() {
@@ -36,6 +42,15 @@ public class TokenManager {
         return accessCodeMap.remove(key);
     }
 
+    public NewCookie createLoginCookie(RealmModel realm, User user, UriInfo uriInfo) {
+        SkeletonKeyToken identityToken = createIdentityToken(realm, user.getLoginName());
+        String encoded = encodeToken(realm, identityToken);
+        URI uri = RealmsResource.realmBaseUrl(uriInfo).build(realm.getId());
+        boolean secureOnly = !realm.isSslNotRequired();
+        NewCookie cookie = new NewCookie(KEYCLOAK_IDENTITY_COOKIE, encoded, uri.getPath(), null, null, realm.getTokenLifespan(), secureOnly, true);
+        return cookie;
+    }
+
     public String createAccessCode(String scopeParam, RealmModel realm, User client, User user)
     {
         SkeletonKeyToken token = null;
diff --git a/services/src/main/java/org/keycloak/services/models/RealmModel.java b/services/src/main/java/org/keycloak/services/models/RealmModel.java
index ec69d1d..76f5d4d 100755
--- a/services/src/main/java/org/keycloak/services/models/RealmModel.java
+++ b/services/src/main/java/org/keycloak/services/models/RealmModel.java
@@ -114,20 +114,20 @@ public class RealmModel {
         realmAgent.setAttribute(new Attribute<Boolean>(REALM_IS_REGISTRATION_ALLOWED, registrationAllowed));
     }
 
-    public long getTokenLifespan() {
-        return (Long) realmAgent.getAttribute(REALM_TOKEN_LIFESPAN).getValue();
+    public int getTokenLifespan() {
+        return (Integer) realmAgent.getAttribute(REALM_TOKEN_LIFESPAN).getValue();
     }
 
-    public void setTokenLifespan(long tokenLifespan) {
-        realmAgent.setAttribute(new Attribute<Long>(REALM_TOKEN_LIFESPAN, tokenLifespan));
+    public void setTokenLifespan(int tokenLifespan) {
+        realmAgent.setAttribute(new Attribute<Integer>(REALM_TOKEN_LIFESPAN, tokenLifespan));
     }
 
-    public long getAccessCodeLifespan() {
-        return (Long) realmAgent.getAttribute(REALM_ACCESS_CODE_LIFESPAN).getValue();
+    public int getAccessCodeLifespan() {
+        return (Integer) realmAgent.getAttribute(REALM_ACCESS_CODE_LIFESPAN).getValue();
     }
 
-    public void setAccessCodeLifespan(long accessCodeLifespan) {
-        realmAgent.setAttribute(new Attribute<Long>(REALM_ACCESS_CODE_LIFESPAN, accessCodeLifespan));
+    public void setAccessCodeLifespan(int accessCodeLifespan) {
+        realmAgent.setAttribute(new Attribute<Integer>(REALM_ACCESS_CODE_LIFESPAN, accessCodeLifespan));
     }
 
     public String getPublicKeyPem() {
diff --git a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
index ecb0e53..f5cef80 100755
--- a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
@@ -51,6 +51,10 @@ public class RealmsResource {
         this.tokenManager = tokenManager;
     }
 
+    public static UriBuilder realmBaseUrl(UriInfo uriInfo) {
+        return uriInfo.getBaseUriBuilder().path(RealmsResource.class).path(RealmsResource.class, "getRealmResource");
+    }
+
     @Path("{realm}/tokens")
     public TokenService getTokenService(@PathParam("realm") String id) {
         RealmManager realmManager = new RealmManager(identitySession);
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 cc839fd..0f10ec8 100755
--- a/services/src/main/java/org/keycloak/services/resources/TokenService.java
+++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java
@@ -31,6 +31,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;
@@ -208,10 +209,18 @@ public class TokenService {
             return null;
         }
 
+        return redirectAccessCode(scopeParam, state, redirect, client, user);
+    }
+
+    protected Response redirectAccessCode(String scopeParam, String state, String redirect, User client, User user) {
         String accessCode = tokenManager.createAccessCode(scopeParam, realm, client, user);
         UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam("code", accessCode);
         if (state != null) redirectUri.queryParam("state", state);
-        return Response.status(302).location(redirectUri.build()).build();
+        Response.ResponseBuilder location = Response.status(302).location(redirectUri.build());
+        if (realm.isCookieLoginAllowed()) {
+           location.cookie(tokenManager.createLoginCookie(realm, user, uriInfo));
+        }
+        return location.build();
     }
 
     @Path("access/codes")
@@ -380,6 +389,11 @@ public class TokenService {
             identitySession.close();
             return null;
         }
+
+        User user = authManager.authenticateIdentityCookie(realm, headers);
+        if (user != null) {
+            return redirectAccessCode(scopeParam, state, redirect, client, user);
+        }
         // todo make sure client is allowed to request a login
 
         forwardToLoginForm(redirect, clientId, scopeParam, state);