keycloak-memoizeit

KEYCLOAK-3281 OIDC 'state' parameter is url-encoded twice

7/13/2016 7:13:58 AM

Details

diff --git a/services/src/main/java/org/keycloak/protocol/oidc/utils/OIDCRedirectUriBuilder.java b/services/src/main/java/org/keycloak/protocol/oidc/utils/OIDCRedirectUriBuilder.java
index d09bc47..95de1b9 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/utils/OIDCRedirectUriBuilder.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/utils/OIDCRedirectUriBuilder.java
@@ -125,7 +125,7 @@ public abstract class OIDCRedirectUriBuilder {
 
         @Override
         public OIDCRedirectUriBuilder addParam(String paramName, String paramValue) {
-            params.put(paramName, Encode.encodeQueryParam(paramValue));
+            params.put(paramName, paramValue);
             return this;
         }
 
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProvider.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProvider.java
index f75d93c..bc63c99 100644
--- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProvider.java
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProvider.java
@@ -17,6 +17,8 @@
 
 package org.keycloak.testsuite.rest;
 
+import org.jboss.resteasy.spi.HttpRequest;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
 import org.keycloak.jose.jws.JWSInput;
 import org.keycloak.jose.jws.JWSInputException;
 import org.keycloak.models.KeycloakSession;
@@ -25,7 +27,6 @@ import org.keycloak.representations.adapters.action.PushNotBeforeAction;
 import org.keycloak.representations.adapters.action.TestAvailabilityAction;
 import org.keycloak.services.resource.RealmResourceProvider;
 import org.keycloak.services.resources.RealmsResource;
-import org.keycloak.testsuite.events.EventsListenerProvider;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.GET;
@@ -34,8 +35,10 @@ import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
+
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.TimeUnit;
 
@@ -110,6 +113,35 @@ public class TestApplicationResourceProvider implements RealmResourceProvider {
         return Response.noContent().build();
     }
 
+    @POST
+    @Produces(MediaType.TEXT_HTML)
+    @Path("/{action}")
+    public String post(@PathParam("action") String action) {
+        String title = "APP_REQUEST";
+        if (action.equals("auth")) {
+            title = "AUTH_RESPONSE";
+        } else if (action.equals("logout")) {
+            title = "LOGOUT_REQUEST";
+        }
+
+        StringBuilder sb = new StringBuilder();
+        sb.append("<html><head><title>" + title + "</title></head><body>");
+
+        sb.append("<b>Form parameters: </b><br>");
+        HttpRequest request = ResteasyProviderFactory.getContextData(HttpRequest.class);
+        MultivaluedMap<String, String> formParams = request.getDecodedFormParameters();
+        for (String paramName : formParams.keySet()) {
+            sb.append(paramName).append(": ").append("<span id=\"").append(paramName).append("\">").append(formParams.getFirst(paramName)).append("</span><br>");
+        }
+        sb.append("<br>");
+
+        UriBuilder base = UriBuilder.fromUri("http://localhost:8180/auth");
+        sb.append("<a href=\"" + RealmsResource.accountUrl(base).build("test").toString() + "\" id=\"account\">account</a>");
+
+        sb.append("</body></html>");
+        return sb.toString();
+    }
+
     @GET
     @Produces(MediaType.TEXT_HTML)
     @Path("/{action}")
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java
index cbfcc5b..be9127d 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java
@@ -36,9 +36,6 @@ import org.keycloak.admin.client.Keycloak;
 import org.keycloak.common.VerificationException;
 import org.keycloak.common.util.PemUtils;
 import org.keycloak.constants.AdapterConstants;
-import org.keycloak.jose.jwk.JWK;
-import org.keycloak.jose.jwk.JWKBuilder;
-import org.keycloak.jose.jwk.JWKParser;
 import org.keycloak.jose.jws.JWSInput;
 import org.keycloak.jose.jws.crypto.RSAProvider;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
@@ -99,6 +96,10 @@ public class OAuthClient {
 
     private String maxAge;
 
+    private String responseType = OAuth2Constants.CODE;
+
+    private String responseMode;
+
     private Map<String, PublicKey> publicKeys = new HashMap<>();
 
     public void init(Keycloak adminClient, WebDriver driver) {
@@ -486,7 +487,12 @@ public class OAuthClient {
 
     public String getLoginFormUrl() {
         UriBuilder b = OIDCLoginProtocolService.authUrl(UriBuilder.fromUri(AUTH_SERVER_ROOT));
-        b.queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE);
+        if (responseType != null) {
+            b.queryParam(OAuth2Constants.RESPONSE_TYPE, responseType);
+        }
+        if (responseMode != null) {
+            b.queryParam(OIDCLoginProtocol.RESPONSE_MODE_PARAM, responseMode);
+        }
         if (clientId != null) {
             b.queryParam(OAuth2Constants.CLIENT_ID, clientId);
         }
@@ -598,6 +604,16 @@ public class OAuthClient {
         return this;
     }
 
+    public OAuthClient responseType(String responseType) {
+        this.responseType = responseType;
+        return this;
+    }
+
+    public OAuthClient responseMode(String responseMode) {
+        this.responseMode = responseMode;
+        return this;
+    }
+
     public String getRealm() {
         return realm;
     }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/AuthorizationCodeTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/AuthorizationCodeTest.java
index 3fc08b4..a5082c5 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/AuthorizationCodeTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/AuthorizationCodeTest.java
@@ -18,12 +18,14 @@ package org.keycloak.testsuite.oauth;
 
 import org.jboss.arquillian.graphene.page.Page;
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.keycloak.OAuth2Constants;
 import org.keycloak.events.Details;
 import org.keycloak.events.Errors;
 import org.keycloak.models.Constants;
+import org.keycloak.protocol.oidc.utils.OIDCResponseMode;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.testsuite.AbstractKeycloakTest;
 import org.keycloak.testsuite.AssertEvents;
@@ -65,15 +67,21 @@ public class AuthorizationCodeTest extends AbstractKeycloakTest {
 
     }
 
+    @Before
+    public void clientConfiguration() {
+        oauth.responseType(OAuth2Constants.CODE);
+        oauth.responseMode(null);
+    }
+
     @Test
     public void authorizationRequest() throws IOException {
-        oauth.state("mystate");
+        oauth.state("OpenIdConnect.AuthenticationProperties=2302984sdlk");
 
         OAuthClient.AuthorizationCodeResponse response = oauth.doLogin("test-user@localhost", "password");
 
         Assert.assertTrue(response.isRedirected());
         Assert.assertNotNull(response.getCode());
-        assertEquals("mystate", response.getState());
+        assertEquals("OpenIdConnect.AuthenticationProperties=2302984sdlk", response.getState());
         Assert.assertNull(response.getError());
 
         testingClient.testing().verifyCode("test", response.getCode());
@@ -137,8 +145,8 @@ public class AuthorizationCodeTest extends AbstractKeycloakTest {
 
     @Test
     public void authorizationRequestImplicitFlowDisabled() throws IOException {
+        oauth.responseType("token id_token");
         UriBuilder b = UriBuilder.fromUri(oauth.getLoginFormUrl());
-        b.replaceQueryParam(OAuth2Constants.RESPONSE_TYPE, "token id_token");
         driver.navigate().to(b.build().toURL());
         assertEquals("Client is not allowed to initiate browser login with given response_type. Implicit flow is disabled for the client.", errorPage.getError());
         events.expectLogin().error(Errors.NOT_ALLOWED).user((String) null).session((String) null).clearDetails().detail(Details.RESPONSE_TYPE, "token id_token").assertEvent();
@@ -146,13 +154,33 @@ public class AuthorizationCodeTest extends AbstractKeycloakTest {
 
     @Test
     public void authorizationRequestInvalidResponseType() throws IOException {
+        oauth.responseType("tokenn");
         UriBuilder b = UriBuilder.fromUri(oauth.getLoginFormUrl());
-        b.replaceQueryParam(OAuth2Constants.RESPONSE_TYPE, "tokenn");
         driver.navigate().to(b.build().toURL());
         assertEquals("Invalid parameter: response_type", errorPage.getError());
         events.expectLogin().error(Errors.INVALID_REQUEST).client((String) null).user((String) null).session((String) null).clearDetails().detail(Details.RESPONSE_TYPE, "tokenn").assertEvent();
     }
 
+    // KEYCLOAK-3281
+    @Test
+    public void authorizationRequestFormPostResponseMode() throws IOException {
+        oauth.responseMode(OIDCResponseMode.FORM_POST.toString().toLowerCase());
+        oauth.state("OpenIdConnect.AuthenticationProperties=2302984sdlk");
+        oauth.doLoginGrant("test-user@localhost", "password");
+
+        String sources = driver.getPageSource();
+        System.out.println(sources);
+
+        String code = driver.findElement(By.id("code")).getText();
+        String state = driver.findElement(By.id("state")).getText();
+
+        assertEquals("OpenIdConnect.AuthenticationProperties=2302984sdlk", state);
+
+        testingClient.testing().verifyCode("test", code);
+        String codeId = events.expectLogin().assertEvent().getDetails().get(Details.CODE_ID);
+        assertCode(codeId, code);
+    }
+
     private void assertCode(String expectedCodeId, String actualCode) {
         String code = testingClient.testing().verifyCode("test", actualCode);
         assertEquals(expectedCodeId, code);