keycloak-uncached

Merge pull request #4523 from abustya/master KEYCLOAK-5616

10/9/2017 6:14:23 AM

Details

diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java
index 5fa46a0..7a1f005 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java
@@ -433,6 +433,7 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
         if (request.getPrompt() != null) authenticationSession.setClientNote(OIDCLoginProtocol.PROMPT_PARAM, request.getPrompt());
         if (request.getIdpHint() != null) authenticationSession.setClientNote(AdapterConstants.KC_IDP_HINT, request.getIdpHint());
         if (request.getResponseMode() != null) authenticationSession.setClientNote(OIDCLoginProtocol.RESPONSE_MODE_PARAM, request.getResponseMode());
+        if (request.getClaims()!= null) authenticationSession.setClientNote(OIDCLoginProtocol.CLAIMS_PARAM, request.getClaims());
 
         // https://tools.ietf.org/html/rfc7636#section-4
         if (request.getCodeChallenge() != null) authenticationSession.setClientNote(OIDCLoginProtocol.CODE_CHALLENGE_PARAM, request.getCodeChallenge());
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthorizationEndpointRequest.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthorizationEndpointRequest.java
index a0f874b..29b734f 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthorizationEndpointRequest.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthorizationEndpointRequest.java
@@ -36,6 +36,7 @@ public class AuthorizationEndpointRequest {
     String nonce;
     Integer maxAge;
     String idpHint;
+    String claims;
     Map<String, String> additionalReqParams = new HashMap<>();
 
     // https://tools.ietf.org/html/rfc7636#section-6.1
@@ -86,6 +87,10 @@ public class AuthorizationEndpointRequest {
         return idpHint;
     }
 
+    public String getClaims() {
+        return claims;
+    }
+
     public Map<String, String> getAdditionalReqParams() {
         return additionalReqParams;
     }
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestObjectParser.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestObjectParser.java
index 06de42c..6803680 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestObjectParser.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestObjectParser.java
@@ -14,12 +14,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.keycloak.protocol.oidc.endpoints.request;
 
+import com.fasterxml.jackson.databind.JsonNode;
 import java.security.PublicKey;
 import java.util.HashMap;
-import java.util.Map;
+import java.util.HashSet;
 import java.util.Set;
 
 import org.keycloak.jose.jws.Algorithm;
@@ -39,7 +39,7 @@ import org.keycloak.util.JsonSerialization;
  */
 class AuthzEndpointRequestObjectParser extends AuthzEndpointRequestParser {
 
-    private final Map<String, Object> requestParams;
+    private final JsonNode requestParams;
 
     public AuthzEndpointRequestObjectParser(KeycloakSession session, String requestObject, ClientModel client) throws Exception {
         JWSInput input = new JWSInput(requestObject);
@@ -52,7 +52,7 @@ class AuthzEndpointRequestObjectParser extends AuthzEndpointRequestParser {
         }
 
         if (header.getAlgorithm() == Algorithm.none) {
-            this.requestParams = JsonSerialization.readValue(input.getContent(), TypedHashMap.class);
+            this.requestParams = JsonSerialization.readValue(input.getContent(), JsonNode.class);
         } else if (header.getAlgorithm() == Algorithm.RS256) {
             PublicKey clientPublicKey = PublicKeyStorageManager.getClientPublicKey(session, client, input);
             if (clientPublicKey == null) {
@@ -64,7 +64,7 @@ class AuthzEndpointRequestObjectParser extends AuthzEndpointRequestParser {
                 throw new RuntimeException("Failed to verify signature on 'request' object");
             }
 
-            this.requestParams = JsonSerialization.readValue(input.getContent(), TypedHashMap.class);
+            this.requestParams = JsonSerialization.readValue(input.getContent(), JsonNode.class);
         } else {
             throw new RuntimeException("Unsupported JWA algorithm used for signed request");
         }
@@ -72,8 +72,14 @@ class AuthzEndpointRequestObjectParser extends AuthzEndpointRequestParser {
 
     @Override
     protected String getParameter(String paramName) {
-        Object val = this.requestParams.get(paramName);
-        return val==null ? null : val.toString();
+        JsonNode val = this.requestParams.get(paramName);
+        if (val == null) {
+            return null;
+        } else if (val.isValueNode()) {
+            return val.asText();
+        } else {
+            return val.toString();
+        }
     }
 
     @Override
@@ -84,7 +90,9 @@ class AuthzEndpointRequestObjectParser extends AuthzEndpointRequestParser {
 
     @Override
     protected Set<String> keySet() {
-        return requestParams.keySet();
+        HashSet<String> keys = new HashSet<>();
+        requestParams.fieldNames().forEachRemaining(keys::add);
+        return keys;
     }
 
     static class TypedHashMap extends HashMap<String, Object> {
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java
index 346b1a6..0f9d152 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java
@@ -61,6 +61,7 @@ abstract class AuthzEndpointRequestParser {
         KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.UI_LOCALES_PARAM);
         KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.REQUEST_PARAM);
         KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.REQUEST_URI_PARAM);
+        KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.CLAIMS_PARAM);
 
         // https://tools.ietf.org/html/rfc7636#section-6.1
         KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.CODE_CHALLENGE_PARAM);
@@ -87,6 +88,7 @@ abstract class AuthzEndpointRequestParser {
         request.idpHint = replaceIfNotNull(request.idpHint, getParameter(AdapterConstants.KC_IDP_HINT));
         request.nonce = replaceIfNotNull(request.nonce, getParameter(OIDCLoginProtocol.NONCE_PARAM));
         request.maxAge = replaceIfNotNull(request.maxAge, getIntParameter(OIDCLoginProtocol.MAX_AGE_PARAM));
+        request.claims = replaceIfNotNull(request.claims, getParameter(OIDCLoginProtocol.CLAIMS_PARAM));
 
         // https://tools.ietf.org/html/rfc7636#section-6.1
         request.codeChallenge = replaceIfNotNull(request.codeChallenge, getParameter(OIDCLoginProtocol.CODE_CHALLENGE_PARAM));
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCAdvancedRequestParamsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCAdvancedRequestParamsTest.java
index f558ede..5bbf71e 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCAdvancedRequestParamsTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCAdvancedRequestParamsTest.java
@@ -17,17 +17,24 @@
 
 package org.keycloak.testsuite.oidc;
 
+import org.jboss.arquillian.container.test.api.Deployment;
 import org.jboss.arquillian.graphene.page.Page;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.keycloak.OAuth2Constants;
 import org.keycloak.OAuthErrorException;
 import org.keycloak.admin.client.resource.ClientResource;
 import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator;
 import org.keycloak.common.util.Time;
 import org.keycloak.events.Details;
 import org.keycloak.jose.jws.Algorithm;
+import org.keycloak.jose.jws.JWSBuilder;
+import org.keycloak.models.AuthenticatedClientSessionModel;
 import org.keycloak.models.Constants;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserSessionModel;
 import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
 import org.keycloak.representations.IDToken;
@@ -49,10 +56,16 @@ import org.keycloak.testsuite.pages.ErrorPage;
 import org.keycloak.testsuite.pages.LoginPage;
 import org.keycloak.testsuite.pages.OAuthGrantPage;
 import org.keycloak.testsuite.rest.resource.TestingOIDCEndpointsApplicationResource;
+import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
 import org.keycloak.testsuite.util.ClientManager;
 import org.keycloak.testsuite.util.OAuthClient;
+import org.keycloak.util.JsonSerialization;
 
+import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -83,6 +96,10 @@ public class OIDCAdvancedRequestParamsTest extends AbstractTestRealmKeycloakTest
     @Page
     protected ErrorPage errorPage;
 
+    @Deployment
+    public static WebArchive deploy() {
+        return RunOnServerDeployment.create(OIDCAdvancedRequestParamsTest.class, AbstractTestRealmKeycloakTest.class);
+    }
 
     @Override
     public void configureTestRealm(RealmRepresentation testRealm) {
@@ -478,5 +495,78 @@ public class OIDCAdvancedRequestParamsTest extends AbstractTestRealmKeycloakTest
 
         events.expectLogin().detail(Details.USERNAME, "test-user@localhost").assertEvent();
     }
+    
+    // CLAIMS
+    // included in the session client notes, so custom providers can make use of it
+    
+    @Test
+    public void processClaimsQueryParam() throws IOException {
+        Map<String, Object> claims = ImmutableMap.of(
+                "id_token", ImmutableMap.of(
+                        "test_claim", ImmutableMap.of(
+                                "essential", true)));
+
+        String claimsJson = JsonSerialization.writeValueAsString(claims);
+
+        driver.navigate().to(oauth.getLoginFormUrl() + "&" + OIDCLoginProtocol.CLAIMS_PARAM + "=" + claimsJson);
+        
+        // need to login so session id can be read from event
+        loginPage.assertCurrent();
+        loginPage.login("test-user@localhost", "password");
+        Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
+
+        EventRepresentation loginEvent = events.expectLogin().detail(Details.USERNAME, "test-user@localhost").assertEvent();
+        String sessionId = loginEvent.getSessionId();
+        String clientId = loginEvent.getClientId();
+        
+        testingClient.server("test").run(session -> {
+            RealmModel realmModel = session.getContext().getRealm();
+            String clientUuid = realmModel.getClientByClientId(clientId).getId();
+            UserSessionModel userSession = session.sessions().getUserSession(realmModel, sessionId);
+            AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessions().get(clientUuid);
+            
+            String claimsInSession = clientSession.getNote(OIDCLoginProtocol.CLAIMS_PARAM);
+            assertEquals(claimsJson, claimsInSession);
+        });
+    }
+    
+    @Test
+    public void processClaimsRequestParam() throws Exception {
+        Map<String, Object> claims = ImmutableMap.of(
+                "id_token", ImmutableMap.of(
+                        "test_claim", ImmutableMap.of(
+                                "essential", true)));
+        
+        String claimsJson = JsonSerialization.writeValueAsString(claims);
+
+        Map<String, Object> oidcRequest = new HashMap<>();
+        oidcRequest.put(OIDCLoginProtocol.CLIENT_ID_PARAM, "test-app");
+        oidcRequest.put(OIDCLoginProtocol.RESPONSE_TYPE_PARAM, OAuth2Constants.CODE);
+        oidcRequest.put(OIDCLoginProtocol.REDIRECT_URI_PARAM, oauth.getRedirectUri());
+        oidcRequest.put(OIDCLoginProtocol.CLAIMS_PARAM, claims);
+
+        String request = new JWSBuilder().jsonContent(oidcRequest).none();
+        
+        driver.navigate().to(oauth.getLoginFormUrl() + "&" + OIDCLoginProtocol.REQUEST_PARAM + "=" + request);
+        
+        // need to login so session id can be read from event
+        loginPage.assertCurrent();
+        loginPage.login("test-user@localhost", "password");
+        Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
+
+        EventRepresentation loginEvent = events.expectLogin().detail(Details.USERNAME, "test-user@localhost").assertEvent();
+        String sessionId = loginEvent.getSessionId();
+        String clientId = loginEvent.getClientId();
+        
+        testingClient.server("test").run(session -> {
+            RealmModel realmModel = session.getContext().getRealm();
+            String clientUuid = realmModel.getClientByClientId(clientId).getId();
+            UserSessionModel userSession = session.sessions().getUserSession(realmModel, sessionId);
+            AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessions().get(clientUuid);
+            
+            String claimsInSession = clientSession.getNote(OIDCLoginProtocol.CLAIMS_PARAM);
+            assertEquals(claimsJson, claimsInSession);
+        });
+    }
 
 }