keycloak-uncached

Merge pull request #3871 from mhajas/KEYCLOAK-4626 KEYCLOAK-4626

2/20/2017 11:43:38 AM

Details

diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java
index 0e06ee1..b45bf84 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java
@@ -118,12 +118,8 @@ public class DeploymentArchiveProcessor implements ApplicationArchiveProcessor {
                     modifyDocElementAttribute(doc, "SingleLogoutService", "redirectBindingUrl", "8080", System.getProperty("auth.server.http.port"));
                 }
 
-                try {
-                    archive.add(new StringAsset(IOUtil.documentToString(doc)), adapterConfigPath);
-                } catch (TransformerException e) {
-                    log.error("Can't transform document to String");
-                    throw new RuntimeException(e);
-                }
+                archive.add(new StringAsset(IOUtil.documentToString(doc)), adapterConfigPath);
+
 
                 // For running SAML tests it is necessary to have few dependencies on app-server side.
                 // Few of them are not in adapter zip so we need to add them manually here
@@ -179,78 +175,68 @@ public class DeploymentArchiveProcessor implements ApplicationArchiveProcessor {
         String dependency = testClass.getAnnotation(UseServletFilter.class).filterDependency();
         ((WebArchive) archive).addAsLibraries(KeycloakDependenciesResolver.resolveDependencies((dependency + ":" + System.getProperty("project.version"))));
 
-        try {
-            Document jbossXmlDoc = loadXML(archive.get(JBOSS_DEPLOYMENT_XML_PATH).getAsset().openStream());
-            removeNodeByAttributeValue(jbossXmlDoc, "dependencies", "module", "name", "org.keycloak.keycloak-saml-core");
-            removeNodeByAttributeValue(jbossXmlDoc, "dependencies", "module", "name", "org.keycloak.keycloak-adapter-spi");
-            archive.add(new StringAsset((documentToString(jbossXmlDoc))), JBOSS_DEPLOYMENT_XML_PATH);
-        } catch (TransformerException e) {
-            log.error("Can't transform document to String");
-            throw new RuntimeException(e);
-        }
+        Document jbossXmlDoc = loadXML(archive.get(JBOSS_DEPLOYMENT_XML_PATH).getAsset().openStream());
+        removeNodeByAttributeValue(jbossXmlDoc, "dependencies", "module", "name", "org.keycloak.keycloak-saml-core");
+        removeNodeByAttributeValue(jbossXmlDoc, "dependencies", "module", "name", "org.keycloak.keycloak-adapter-spi");
+        archive.add(new StringAsset((documentToString(jbossXmlDoc))), JBOSS_DEPLOYMENT_XML_PATH);
 
     }
 
     protected void modifyWebXml(Archive<?> archive, TestClass testClass) {
-        try {
-            Document webXmlDoc = loadXML(
-                    archive.get(WEBXML_PATH).getAsset().openStream());
-            if (isTomcatAppServer(testClass.getJavaClass())) {
-                modifyDocElementValue(webXmlDoc, "auth-method", "KEYCLOAK", "BASIC");
-            }
+        Document webXmlDoc = loadXML(
+                archive.get(WEBXML_PATH).getAsset().openStream());
+        if (isTomcatAppServer(testClass.getJavaClass())) {
+            modifyDocElementValue(webXmlDoc, "auth-method", "KEYCLOAK", "BASIC");
+        }
 
-            if (testClass.getJavaClass().isAnnotationPresent(UseServletFilter.class)) {
+        if (testClass.getJavaClass().isAnnotationPresent(UseServletFilter.class)) {
 
-                addFilterDependencies(archive, testClass);
+            addFilterDependencies(archive, testClass);
 
-                //We need to add filter declaration to web.xml
-                log.info("Adding filter to " + testClass.getAnnotation(UseServletFilter.class).filterClass() + " with mapping " + testClass.getAnnotation(UseServletFilter.class).filterPattern() + " for " + archive.getName());
+            //We need to add filter declaration to web.xml
+            log.info("Adding filter to " + testClass.getAnnotation(UseServletFilter.class).filterClass() + " with mapping " + testClass.getAnnotation(UseServletFilter.class).filterPattern() + " for " + archive.getName());
 
-                Element filter = webXmlDoc.createElement("filter");
-                Element filterName = webXmlDoc.createElement("filter-name");
-                Element filterClass = webXmlDoc.createElement("filter-class");
+            Element filter = webXmlDoc.createElement("filter");
+            Element filterName = webXmlDoc.createElement("filter-name");
+            Element filterClass = webXmlDoc.createElement("filter-class");
 
-                filterName.setTextContent(testClass.getAnnotation(UseServletFilter.class).filterName());
-                filterClass.setTextContent(testClass.getAnnotation(UseServletFilter.class).filterClass());
+            filterName.setTextContent(testClass.getAnnotation(UseServletFilter.class).filterName());
+            filterClass.setTextContent(testClass.getAnnotation(UseServletFilter.class).filterClass());
 
-                filter.appendChild(filterName);
-                filter.appendChild(filterClass);
-                appendChildInDocument(webXmlDoc, "web-app", filter);
+            filter.appendChild(filterName);
+            filter.appendChild(filterClass);
+            appendChildInDocument(webXmlDoc, "web-app", filter);
 
-                Element filterMapping = webXmlDoc.createElement("filter-mapping");
+            Element filterMapping = webXmlDoc.createElement("filter-mapping");
 
 
-                Element urlPattern = webXmlDoc.createElement("url-pattern");
+            Element urlPattern = webXmlDoc.createElement("url-pattern");
 
-                filterName = webXmlDoc.createElement("filter-name");
+            filterName = webXmlDoc.createElement("filter-name");
 
-                filterName.setTextContent(testClass.getAnnotation(UseServletFilter.class).filterName());
-                urlPattern.setTextContent(getElementTextContent(webXmlDoc, "web-app/security-constraint/web-resource-collection/url-pattern"));
+            filterName.setTextContent(testClass.getAnnotation(UseServletFilter.class).filterName());
+            urlPattern.setTextContent(getElementTextContent(webXmlDoc, "web-app/security-constraint/web-resource-collection/url-pattern"));
 
-                filterMapping.appendChild(filterName);
-                filterMapping.appendChild(urlPattern);
+            filterMapping.appendChild(filterName);
+            filterMapping.appendChild(urlPattern);
 
-                if (!testClass.getAnnotation(UseServletFilter.class).dispatcherType().isEmpty()) {
-                    Element dispatcher = webXmlDoc.createElement("dispatcher");
-                    dispatcher.setTextContent(testClass.getAnnotation(UseServletFilter.class).dispatcherType());
-                    filterMapping.appendChild(dispatcher);
-                }
-                appendChildInDocument(webXmlDoc, "web-app", filterMapping);
+            if (!testClass.getAnnotation(UseServletFilter.class).dispatcherType().isEmpty()) {
+                Element dispatcher = webXmlDoc.createElement("dispatcher");
+                dispatcher.setTextContent(testClass.getAnnotation(UseServletFilter.class).dispatcherType());
+                filterMapping.appendChild(dispatcher);
+            }
+            appendChildInDocument(webXmlDoc, "web-app", filterMapping);
 
-                //finally we need to remove all keycloak related configuration from web.xml
-                removeElementsFromDoc(webXmlDoc, "web-app", "security-constraint");
-                removeElementsFromDoc(webXmlDoc, "web-app", "login-config");
-                removeElementsFromDoc(webXmlDoc, "web-app", "security-role");
+            //finally we need to remove all keycloak related configuration from web.xml
+            removeElementsFromDoc(webXmlDoc, "web-app", "security-constraint");
+            removeElementsFromDoc(webXmlDoc, "web-app", "login-config");
+            removeElementsFromDoc(webXmlDoc, "web-app", "security-role");
 
 
-            }
+        }
 
 
-            archive.add(new StringAsset((documentToString(webXmlDoc))), WEBXML_PATH);
-        } catch (TransformerException e) {
-            log.error("Can't transform document to String");
-            throw new RuntimeException(e);
-        }
+        archive.add(new StringAsset((documentToString(webXmlDoc))), WEBXML_PATH);
     }
 
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/IOUtil.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/IOUtil.java
index f11ac07..734a4fc 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/IOUtil.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/IOUtil.java
@@ -29,6 +29,7 @@ import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
 import javax.xml.transform.TransformerException;
 import javax.xml.transform.TransformerFactory;
 import javax.xml.transform.dom.DOMSource;
@@ -89,13 +90,18 @@ public class IOUtil {
         }
     }
 
-    public static String documentToString(Document newDoc) throws TransformerException {
-        DOMSource domSource = new DOMSource(newDoc);
-        Transformer transformer = TransformerFactory.newInstance().newTransformer();
-        StringWriter sw = new StringWriter();
-        StreamResult sr = new StreamResult(sw);
-        transformer.transform(domSource, sr);
-        return sw.toString();
+    public static String documentToString(Document newDoc) {
+        try {
+            DOMSource domSource = new DOMSource(newDoc);
+            Transformer transformer = TransformerFactory.newInstance().newTransformer();
+            StringWriter sw = new StringWriter();
+            StreamResult sr = new StreamResult(sw);
+            transformer.transform(domSource, sr);
+            return sw.toString();
+        } catch (TransformerException e) {
+            log.error("Can't transform document to String");
+            throw new RuntimeException(e);
+        }
     }
 
     public static void modifyDocElementAttribute(Document doc, String tagName, String attributeName, String regex, String replacement) {
@@ -152,6 +158,22 @@ public class IOUtil {
         node.setTextContent(node.getTextContent().replace(regex, replacement));
     }
 
+    public static void setDocElementAttributeValue(Document doc, String tagName, String attributeName, String value) {
+        NodeList nodes = doc.getElementsByTagName(tagName);
+        if (nodes.getLength() != 1) {
+            log.warn("Not able or ambiguous to find element: " + tagName);
+            return;
+        }
+
+        Element node = (Element) nodes.item(0);
+        if (node == null) {
+            log.warn("Not able to find element: " + tagName);
+            return;
+        }
+
+        node.setAttribute(attributeName, value);
+    }
+
     public static void removeElementsFromDoc(Document doc, String parentTag, String removeNode) {
         NodeList nodes = doc.getElementsByTagName(parentTag);
         if (nodes.getLength() != 1) {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLServletsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLServletsAdapterTest.java
index 24d15c1..8fc9053 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLServletsAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLServletsAdapterTest.java
@@ -1009,7 +1009,7 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
         try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
             HttpClientContext context = HttpClientContext.create();
 
-            HttpUriRequest post = SamlClient.Binding.POST.createSamlPostUnsignedRequest(getAppServerSamlEndpoint(employee2ServletPage), null, responseDoc);
+            HttpUriRequest post = SamlClient.Binding.POST.createSamlUnsignedResponse(getAppServerSamlEndpoint(employee2ServletPage), null, responseDoc);
             response = client.execute(post, context);
             assertThat(response, statusCodeIsHC(Response.Status.FOUND));
             response.close();
@@ -1049,7 +1049,7 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
             HttpClientContext context = HttpClientContext.create();
 
             log.debug("Sending response to SP");
-            HttpUriRequest post = SamlClient.Binding.POST.createSamlPostUnsignedRequest(getAppServerSamlEndpoint(salesPostSigEmailServletPage), null, responseDoc);
+            HttpUriRequest post = SamlClient.Binding.POST.createSamlUnsignedResponse(getAppServerSamlEndpoint(salesPostSigEmailServletPage), null, responseDoc);
             response = client.execute(post, context);
             System.out.println(EntityUtils.toString(response.getEntity()));
             assertThat(response, statusCodeIsHC(Response.Status.FOUND));
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/AbstractSamlTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/AbstractSamlTest.java
new file mode 100644
index 0000000..b72305d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/AbstractSamlTest.java
@@ -0,0 +1,50 @@
+package org.keycloak.testsuite.saml;
+
+import org.keycloak.dom.saml.v2.protocol.AuthnRequestType;
+import org.keycloak.protocol.saml.SamlProtocol;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.services.resources.RealmsResource;
+import org.keycloak.testsuite.AbstractAuthTest;
+import org.keycloak.testsuite.adapter.page.SAMLServlet;
+import org.keycloak.testsuite.util.SamlClient;
+
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriBuilderException;
+import java.net.URI;
+import java.util.List;
+
+import static org.keycloak.testsuite.util.IOUtil.loadRealm;
+
+/**
+ * @author mhajas
+ */
+public class AbstractSamlTest extends AbstractAuthTest {
+
+    protected static final String REALM_NAME = "demo";
+    protected static final String REALM_PRIVATE_KEY = "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=";
+    protected static final String REALM_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB";
+
+    protected static final String SAML_ASSERTION_CONSUMER_URL_SALES_POST = "http://localhost:8080/sales-post/";
+    protected static final String SAML_CLIENT_ID_SALES_POST = "http://localhost:8081/sales-post/";
+
+    protected static final String SAML_ASSERTION_CONSUMER_URL_SALES_POST_ENC = "http://localhost:8080/sales-post-enc/";
+    protected static final String SAML_CLIENT_ID_SALES_POST_ENC = "http://localhost:8081/sales-post-enc/";
+    protected static final String SAML_CLIENT_SALES_POST_ENC_PRIVATE_KEY = "MIICXQIBAAKBgQDb7kwJPkGdU34hicplwfp6/WmNcaLh94TSc7Jyr9Undp5pkyLgb0DE7EIE+6kSs4LsqCb8HDkB0nLD5DXbBJFd8n0WGoKstelvtg6FtVJMnwN7k7yZbfkPECWH9zF70VeOo9vbzrApNRnct8ZhH5fbflRB4JMA9L9R+LbURdoSKQIDAQABAoGBANtbZG9bruoSGp2s5zhzLzd4hczT6Jfk3o9hYjzNb5Z60ymN3Z1omXtQAdEiiNHkRdNxK+EM7TcKBfmoJqcaeTkW8cksVEAW23ip8W9/XsLqmbU2mRrJiKa+KQNDSHqJi1VGyimi4DDApcaqRZcaKDFXg2KDr/Qt5JFD/o9IIIPZAkEA+ZENdBIlpbUfkJh6Ln+bUTss/FZ1FsrcPZWu13rChRMrsmXsfzu9kZUWdUeQ2Dj5AoW2Q7L/cqdGXS7Mm5XhcwJBAOGZq9axJY5YhKrsksvYRLhQbStmGu5LG75suF+rc/44sFq+aQM7+oeRr4VY88Mvz7mk4esdfnk7ae+cCazqJvMCQQCx1L1cZw3yfRSn6S6u8XjQMjWE/WpjulujeoRiwPPY9WcesOgLZZtYIH8nRL6ehEJTnMnahbLmlPFbttxPRUanAkA11MtSIVcKzkhp2KV2ipZrPJWwI18NuVJXb+3WtjypTrGWFZVNNkSjkLnHIeCYlJIGhDd8OL9zAiBXEm6kmgLNAkBWAg0tK2hCjvzsaA505gWQb4X56uKWdb0IzN+fOLB3Qt7+fLqbVQNQoNGzqey6B4MoS1fUKAStqdGTFYPG/+9t";
+    protected static final String SAML_CLIENT_SALES_POST_ENC_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDb7kwJPkGdU34hicplwfp6/WmNcaLh94TSc7Jyr9Undp5pkyLgb0DE7EIE+6kSs4LsqCb8HDkB0nLD5DXbBJFd8n0WGoKstelvtg6FtVJMnwN7k7yZbfkPECWH9zF70VeOo9vbzrApNRnct8ZhH5fbflRB4JMA9L9R+LbURdoSKQIDAQAB";
+
+
+    @Override
+    public void addTestRealms(List<RealmRepresentation> testRealms) {
+        testRealms.add(loadRealm("/adapter-test/keycloak-saml/testsaml.json"));
+    }
+
+    protected AuthnRequestType createLoginRequestDocument(String issuer, String assertionConsumerURL, String realmName) {
+        return SamlClient.createLoginRequestDocument(issuer, assertionConsumerURL, getAuthServerSamlEndpoint(realmName));
+    }
+
+    protected URI getAuthServerSamlEndpoint(String realm) throws IllegalArgumentException, UriBuilderException {
+        return RealmsResource
+                .protocolUrl(UriBuilder.fromUri(getAuthServerRoot()))
+                .build(realm, SamlProtocol.LOGIN_PROTOCOL);
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/AuthnRequestNameIdFormatTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/AuthnRequestNameIdFormatTest.java
index 3bf08c9..4f94260 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/AuthnRequestNameIdFormatTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/AuthnRequestNameIdFormatTest.java
@@ -60,27 +60,7 @@ import static org.keycloak.testsuite.util.Matchers.statusCodeIsHC;
  *
  * @author hmlnarik
  */
-public class AuthnRequestNameIdFormatTest extends AbstractAuthTest {
-
-    private static final String REALM_NAME = "demo";
-
-    private static final String SAML_ASSERTION_CONSUMER_URL_SALES_POST = "http://localhost:8080/sales-post/";
-    private static final String SAML_CLIENT_ID_SALES_POST = "http://localhost:8081/sales-post/";
-
-    @Override
-    public void addTestRealms(List<RealmRepresentation> testRealms) {
-        testRealms.add(loadRealm("/adapter-test/keycloak-saml/testsaml.json"));
-    }
-
-    public AuthnRequestType createLoginRequestDocument(String issuer, String assertionConsumerURL, String realmName) {
-        return SamlClient.createLoginRequestDocument(issuer, assertionConsumerURL, getAuthServerSamlEndpoint(realmName));
-    }
-
-    private URI getAuthServerSamlEndpoint(String realm) throws IllegalArgumentException, UriBuilderException {
-        return RealmsResource
-          .protocolUrl(UriBuilder.fromUri(getAuthServerRoot()))
-          .build(realm, SamlProtocol.LOGIN_PROTOCOL);
-    }
+public class AuthnRequestNameIdFormatTest extends AbstractSamlTest {
 
     private void testLoginWithNameIdPolicy(Binding requestBinding, Binding responseBinding, NameIDPolicyType nameIDPolicy, Matcher<String> nameIdMatcher) throws Exception {
         AuthnRequestType loginRep = createLoginRequestDocument(SAML_CLIENT_ID_SALES_POST, SAML_ASSERTION_CONSUMER_URL_SALES_POST, REALM_NAME);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/BasicSamlTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/BasicSamlTest.java
new file mode 100644
index 0000000..0bd0793
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/BasicSamlTest.java
@@ -0,0 +1,38 @@
+package org.keycloak.testsuite.saml;
+
+import org.junit.Test;
+import org.keycloak.dom.saml.v2.protocol.AuthnRequestType;
+import org.keycloak.saml.common.exceptions.ConfigurationException;
+import org.keycloak.saml.common.exceptions.ParsingException;
+import org.keycloak.saml.common.exceptions.ProcessingException;
+import org.keycloak.saml.processing.api.saml.v2.request.SAML2Request;
+import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
+import org.keycloak.testsuite.util.SamlClient;
+import org.w3c.dom.Document;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertThat;
+import static org.keycloak.testsuite.util.IOUtil.documentToString;
+import static org.keycloak.testsuite.util.IOUtil.setDocElementAttributeValue;
+import static org.keycloak.testsuite.util.SamlClient.login;
+
+/**
+ * @author mhajas
+ */
+public class BasicSamlTest extends AbstractSamlTest {
+
+    // KEYCLOAK-4160
+    @Test
+    public void testPropertyValueInAssertion() throws ParsingException, ConfigurationException, ProcessingException {
+        AuthnRequestType loginRep = createLoginRequestDocument(SAML_CLIENT_ID_SALES_POST, SAML_ASSERTION_CONSUMER_URL_SALES_POST, REALM_NAME);
+
+        Document doc = SAML2Request.convert(loginRep);
+
+        setDocElementAttributeValue(doc, "samlp:AuthnRequest", "ID", "${java.version}" );
+
+        SAMLDocumentHolder document = login(bburkeUser, getAuthServerSamlEndpoint(REALM_NAME), doc, null, SamlClient.Binding.POST, SamlClient.Binding.POST);
+
+        assertThat(documentToString(document.getSamlDocument()), not(containsString("InResponseTo=\"" + System.getProperty("java.version") + "\"")));
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/ConcurrentAuthnRequestTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/ConcurrentAuthnRequestTest.java
index b207c04..31cc14d 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/ConcurrentAuthnRequestTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/ConcurrentAuthnRequestTest.java
@@ -17,12 +17,9 @@
 package org.keycloak.testsuite.saml;
 
 import org.keycloak.dom.saml.v2.protocol.AuthnRequestType;
-import org.keycloak.protocol.saml.SamlProtocol;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.representations.idm.UserRepresentation;
 import org.keycloak.saml.processing.api.saml.v2.request.SAML2Request;
-import org.keycloak.services.resources.RealmsResource;
-import org.keycloak.testsuite.AbstractAuthTest;
 import org.keycloak.testsuite.util.SamlClient;
 
 import java.io.IOException;
@@ -33,8 +30,7 @@ import java.util.List;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
-import javax.ws.rs.core.UriBuilder;
-import javax.ws.rs.core.UriBuilderException;
+
 import org.apache.http.client.methods.CloseableHttpResponse;
 import org.apache.http.client.methods.HttpUriRequest;
 import org.apache.http.client.protocol.HttpClientContext;
@@ -53,12 +49,7 @@ import static org.keycloak.testsuite.util.IOUtil.loadRealm;
  * @author hmlnarik
  */
 @Ignore
-public class ConcurrentAuthnRequestTest extends AbstractAuthTest {
-
-    private static final String REALM_NAME = "demo";
-
-    private static final String SAML_ASSERTION_CONSUMER_URL_SALES_POST = "http://localhost:8080/sales-post/";
-    private static final String SAML_CLIENT_ID_SALES_POST = "http://localhost:8081/sales-post/";
+public class ConcurrentAuthnRequestTest extends AbstractSamlTest {
 
     public static final int ITERATIONS = 10000;
     public static final int CONCURRENT_THREADS = 5;
@@ -70,7 +61,7 @@ public class ConcurrentAuthnRequestTest extends AbstractAuthTest {
         ExecutorService threadPool = Executors.newFixedThreadPool(CONCURRENT_THREADS);
 
         try (CloseableHttpClient client = HttpClientBuilder.create().setRedirectStrategy(strategy).build()) {
-            HttpUriRequest post = requestBinding.createSamlRequest(samlEndpoint, relayState, samlRequest);
+            HttpUriRequest post = requestBinding.createSamlUnsignedRequest(samlEndpoint, relayState, samlRequest);
             
             Collection<Callable<Void>> futures = new LinkedList<>();
             for (int i = 0; i < ITERATIONS; i ++) {
@@ -123,12 +114,6 @@ public class ConcurrentAuthnRequestTest extends AbstractAuthTest {
         return SamlClient.createLoginRequestDocument(issuer, assertionConsumerURL, getAuthServerSamlEndpoint(realmName));
     }
 
-    private URI getAuthServerSamlEndpoint(String realm) throws IllegalArgumentException, UriBuilderException {
-        return RealmsResource
-          .protocolUrl(UriBuilder.fromUri(getAuthServerRoot()))
-          .build(realm, SamlProtocol.LOGIN_PROTOCOL);
-    }
-
     private void testLogin(Binding requestBinding) throws Exception {
         AuthnRequestType loginRep = createLoginRequestDocument(SAML_CLIENT_ID_SALES_POST, SAML_ASSERTION_CONSUMER_URL_SALES_POST, REALM_NAME);
         Document samlRequest = SAML2Request.convert(loginRep);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/SamlConsentTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/SamlConsentTest.java
new file mode 100644
index 0000000..3fcf0c3
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/SamlConsentTest.java
@@ -0,0 +1,60 @@
+package org.keycloak.testsuite.saml;
+
+import org.junit.Test;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.saml.common.exceptions.ConfigurationException;
+import org.keycloak.saml.common.exceptions.ParsingException;
+import org.keycloak.saml.common.exceptions.ProcessingException;
+import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
+import org.keycloak.testsuite.util.ClientBuilder;
+import org.keycloak.testsuite.util.IOUtil;
+import org.keycloak.testsuite.util.SamlClient;
+
+import java.net.URI;
+import java.util.List;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertThat;
+import static org.keycloak.testsuite.util.IOUtil.loadRealm;
+import static org.keycloak.testsuite.util.SamlClient.idpInitiatedLoginWithRequiredConsent;
+
+/**
+ * @author mhajas
+ */
+public class SamlConsentTest extends AbstractSamlTest {
+
+    @Override
+    public void addTestRealms(List<RealmRepresentation> testRealms) {
+        testRealms.add(loadRealm("/adapter-test/keycloak-saml/testsaml.json"));
+    }
+
+    @Test
+    public void rejectedConsentResponseTest() throws ParsingException, ConfigurationException, ProcessingException {
+        ClientRepresentation client = adminClient.realm(REALM_NAME)
+                .clients()
+                .findByClientId(SAML_CLIENT_ID_SALES_POST_ENC)
+                .get(0);
+
+        adminClient.realm(REALM_NAME)
+                .clients()
+                .get(client.getId())
+                .update(ClientBuilder.edit(client)
+                        .consentRequired(true)
+                        .attribute("saml.encrypt", "false") //remove after RHSSO-797
+                        .attribute("saml_idp_initiated_sso_url_name", "sales-post-enc")
+                        .attribute("saml_assertion_consumer_url_post", SAML_ASSERTION_CONSUMER_URL_SALES_POST_ENC + "saml")
+                        .build());
+
+        log.debug("Log in using idp initiated login");
+        String idpInitiatedLogin = getAuthServerRoot() + "realms/" + REALM_NAME + "/protocol/saml/clients/sales-post-enc";
+        SAMLDocumentHolder documentHolder = idpInitiatedLoginWithRequiredConsent(bburkeUser, URI.create(idpInitiatedLogin), SamlClient.Binding.POST, false);
+
+        assertThat(IOUtil.documentToString(documentHolder.getSamlDocument()), containsString("<dsig:Signature")); // KEYCLOAK-4262
+        assertThat(IOUtil.documentToString(documentHolder.getSamlDocument()), not(containsString("<samlp:LogoutResponse"))); // KEYCLOAK-4261
+        assertThat(IOUtil.documentToString(documentHolder.getSamlDocument()), containsString("<samlp:Response")); // KEYCLOAK-4261
+        assertThat(IOUtil.documentToString(documentHolder.getSamlDocument()), containsString("<samlp:Status")); // KEYCLOAK-4181
+        assertThat(IOUtil.documentToString(documentHolder.getSamlDocument()), containsString("<samlp:StatusCode Value=\"urn:oasis:names:tc:SAML:2.0:status:RequestDenied\"")); // KEYCLOAK-4181
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientBuilder.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientBuilder.java
index 12fe4a1..7530275 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientBuilder.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientBuilder.java
@@ -106,7 +106,10 @@ public class ClientBuilder {
     }
 
     public ClientBuilder attribute(String name, String value) {
-        Map<String, String> attributes = new HashMap<String, String>();
+        Map<String, String> attributes = rep.getAttributes();
+        if (attributes == null) {
+            attributes = new HashMap<>();
+        }
         attributes.put(name, value);
         rep.setAttributes(attributes);
         return this;
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/KeyUtils.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/KeyUtils.java
new file mode 100644
index 0000000..afe2f51
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/KeyUtils.java
@@ -0,0 +1,42 @@
+package org.keycloak.testsuite.util;
+
+import org.keycloak.common.util.BouncyIntegration;
+
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Base64;
+
+/**
+ * @author mhajas
+ */
+public class KeyUtils {
+    static {
+        BouncyIntegration.init();
+    }
+
+
+    public static PublicKey publicKeyFromString(String key) {
+        try {
+            KeyFactory kf = KeyFactory.getInstance("RSA");
+            byte[] encoded = Base64.getDecoder().decode(key);
+            return kf.generatePublic(new X509EncodedKeySpec(encoded));
+        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static PrivateKey privateKeyFromString(String key) {
+        try {
+            KeyFactory kf = KeyFactory.getInstance("RSA");
+            byte[] encoded = Base64.getDecoder().decode(key);
+            return kf.generatePrivate(new PKCS8EncodedKeySpec(encoded));
+        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/SamlClient.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/SamlClient.java
index 8a27896..ccaca6c 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/SamlClient.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/SamlClient.java
@@ -16,52 +16,56 @@
  */
 package org.keycloak.testsuite.util;
 
+import org.apache.http.NameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpUriRequest;
 import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.client.utils.URLEncodedUtils;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.client.LaxRedirectStrategy;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.util.EntityUtils;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Element;
+import org.jsoup.select.Elements;
+import org.keycloak.common.util.KeyUtils;
 import org.keycloak.dom.saml.v2.protocol.AuthnRequestType;
 import org.keycloak.representations.idm.UserRepresentation;
 import org.keycloak.saml.BaseSAML2BindingBuilder;
 import org.keycloak.saml.SAMLRequestParser;
+import org.keycloak.saml.SignatureAlgorithm;
 import org.keycloak.saml.common.constants.GeneralConstants;
 import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
 import org.keycloak.saml.common.exceptions.ConfigurationException;
 import org.keycloak.saml.common.exceptions.ProcessingException;
 import org.keycloak.saml.processing.api.saml.v2.request.SAML2Request;
 import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
+import org.w3c.dom.Document;
 
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.net.URI;
+import java.security.PrivateKey;
+import java.security.PublicKey;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Objects;
 import java.util.UUID;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.UriBuilder;
-import org.apache.http.NameValuePair;
-import org.apache.http.client.entity.UrlEncodedFormEntity;
-import org.apache.http.client.methods.CloseableHttpResponse;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.client.methods.HttpUriRequest;
-import org.apache.http.client.utils.URLEncodedUtils;
-import org.apache.http.impl.client.LaxRedirectStrategy;
-import org.apache.http.message.BasicNameValuePair;
-import org.apache.http.util.EntityUtils;
-import org.jsoup.Jsoup;
-import org.jsoup.nodes.Element;
-import org.jsoup.select.Elements;
-
-import org.w3c.dom.Document;
 
 import static org.hamcrest.Matchers.*;
 import static org.junit.Assert.assertThat;
 import static org.keycloak.testsuite.admin.Users.getPasswordOf;
-import static org.keycloak.testsuite.util.Matchers.*;
+import static org.keycloak.testsuite.arquillian.AuthServerTestEnricher.getAuthServerContextRoot;
+import static org.keycloak.testsuite.util.IOUtil.documentToString;
+import static org.keycloak.testsuite.util.Matchers.statusCodeIsHC;
 
 /**
- *
  * @author hmlnarik
  */
 public class SamlClient {
@@ -80,45 +84,41 @@ public class SamlClient {
             }
 
             @Override
-            public HttpPost createSamlRequest(URI samlEndpoint, String relayState, Document samlRequest) {
-                HttpPost post = new HttpPost(samlEndpoint);
-
-                List<NameValuePair> parameters = new LinkedList<>();
-                try {
-                    parameters.add(
-                      new BasicNameValuePair(GeneralConstants.SAML_REQUEST_KEY,
-                      new BaseSAML2BindingBuilder()
-                        .postBinding(samlRequest)
-                        .encoded())
-                    );
-                } catch (ProcessingException | ConfigurationException | IOException ex) {
-                    throw new RuntimeException(ex);
-                }
-                if (relayState != null) {
-                    parameters.add(new BasicNameValuePair(GeneralConstants.RELAY_STATE, relayState));
-                }
-
-                UrlEncodedFormEntity formEntity;
-                try {
-                    formEntity = new UrlEncodedFormEntity(parameters, "UTF-8");
-                } catch (UnsupportedEncodingException e) {
-                    throw new RuntimeException(e);
-                }
-                post.setEntity(formEntity);
+            public HttpPost createSamlUnsignedRequest(URI samlEndpoint, String relayState, Document samlRequest) {
+                return createSamlPostMessage(samlEndpoint, relayState, samlRequest, GeneralConstants.SAML_REQUEST_KEY, null, null);
+            }
 
-                return post;
+            @Override
+            public HttpPost createSamlUnsignedResponse(URI samlEndpoint, String relayState, Document samlRequest) {
+                return createSamlPostMessage(samlEndpoint, relayState, samlRequest, GeneralConstants.SAML_RESPONSE_KEY, null, null);
             }
 
             @Override
-            public HttpPost createSamlPostUnsignedRequest(URI samlEndpoint, String relayState, Document samlRequest) {
+            public HttpPost createSamlSignedRequest(URI samlEndpoint, String relayState, Document samlRequest, String realmPrivateKey, String realmPublicKey) {
+                return createSamlPostMessage(samlEndpoint, relayState, samlRequest, GeneralConstants.SAML_REQUEST_KEY, realmPrivateKey, realmPublicKey);
+            }
+
+            private HttpPost createSamlPostMessage(URI samlEndpoint, String relayState, Document samlRequest, String messageType, String privateKeyStr, String publicKeyStr) {
                 HttpPost post = new HttpPost(samlEndpoint);
 
                 List<NameValuePair> parameters = new LinkedList<>();
 
+
                 try {
+                    BaseSAML2BindingBuilder binding = new BaseSAML2BindingBuilder();
+
+                    if (privateKeyStr != null && publicKeyStr != null) {
+                        PrivateKey privateKey = org.keycloak.testsuite.util.KeyUtils.privateKeyFromString(privateKeyStr);
+                        PublicKey publicKey = org.keycloak.testsuite.util.KeyUtils.publicKeyFromString(publicKeyStr);
+                        binding
+                                .signatureAlgorithm(SignatureAlgorithm.RSA_SHA256)
+                                .signWith(KeyUtils.createKeyId(privateKey), privateKey, publicKey)
+                                .signDocument();
+                    }
+
                     parameters.add(
-                            new BasicNameValuePair(GeneralConstants.SAML_RESPONSE_KEY,
-                                    new BaseSAML2BindingBuilder()
+                            new BasicNameValuePair(messageType,
+                                    binding
                                             .postBinding(samlRequest)
                                             .encoded())
                     );
@@ -159,12 +159,12 @@ public class SamlClient {
             }
 
             @Override
-            public HttpGet createSamlRequest(URI samlEndpoint, String relayState, Document samlRequest) {
+            public HttpGet createSamlUnsignedRequest(URI samlEndpoint, String relayState, Document samlRequest) {
                 try {
                     URI requestURI = new BaseSAML2BindingBuilder()
-                      .relayState(relayState)
-                      .redirectBinding(samlRequest)
-                      .requestURI(samlEndpoint.toString());
+                            .relayState(relayState)
+                            .redirectBinding(samlRequest)
+                            .requestURI(samlEndpoint.toString());
                     return new HttpGet(requestURI);
                 } catch (ProcessingException | ConfigurationException | IOException ex) {
                     throw new RuntimeException(ex);
@@ -177,15 +177,25 @@ public class SamlClient {
             }
 
             @Override
-            public HttpUriRequest createSamlPostUnsignedRequest(URI samlEndpoint, String relayState, Document samlRequest) {
+            public HttpUriRequest createSamlUnsignedResponse(URI samlEndpoint, String relayState, Document samlRequest) {
+                return null;
+            }
+
+            @Override
+            public HttpUriRequest createSamlSignedRequest(URI samlEndpoint, String relayState, Document samlRequest, String realmPrivateKey, String realmPublicKey) {
                 return null;
             }
         };
 
         public abstract SAMLDocumentHolder extractResponse(CloseableHttpResponse response) throws IOException;
-        public abstract HttpUriRequest createSamlRequest(URI samlEndpoint, String relayState, Document samlRequest);
+
+        public abstract HttpUriRequest createSamlUnsignedRequest(URI samlEndpoint, String relayState, Document samlRequest);
+
+        public abstract HttpUriRequest createSamlSignedRequest(URI samlEndpoint, String relayState, Document samlRequest, String realmPrivateKey, String realmPublicKey);
+
         public abstract URI getBindingUri();
-        public abstract HttpUriRequest createSamlPostUnsignedRequest(URI samlEndpoint, String relayState, Document samlRequest);
+
+        public abstract HttpUriRequest createSamlUnsignedResponse(URI samlEndpoint, String relayState, Document samlRequest);
     }
 
     public static class RedirectStrategyWithSwitchableFollowRedirect extends LaxRedirectStrategy {
@@ -204,6 +214,7 @@ public class SamlClient {
 
     /**
      * Extracts and parses value of SAMLResponse input field of a form present in the given page.
+     *
      * @param responsePage HTML code of the page
      * @return
      */
@@ -219,6 +230,7 @@ public class SamlClient {
 
     /**
      * Extracts and parses value of SAMLResponse query parameter from the given URI.
+     *
      * @param responseUri
      * @return
      */
@@ -239,6 +251,7 @@ public class SamlClient {
     /**
      * Prepares a GET/POST request for logging the given user into the given login page. The login page is expected
      * to have at least input fields with id "username" and "password".
+     *
      * @param user
      * @param loginPage
      * @return
@@ -289,7 +302,61 @@ public class SamlClient {
     }
 
     /**
+     * Prepares a GET/POST request for consent granting . The consent page is expected
+     * to have at least input fields with id "kc-login" and "kc-cancel".
+     *
+     * @param consentPage
+     * @param consent
+     * @return
+     */
+    public static HttpUriRequest handleConsentPage(String consentPage, boolean consent) {
+        org.jsoup.nodes.Document theLoginPage = Jsoup.parse(consentPage);
+
+        List<NameValuePair> parameters = new LinkedList<>();
+        for (Element form : theLoginPage.getElementsByTag("form")) {
+            String method = form.attr("method");
+            String action = form.attr("action");
+            boolean isPost = method != null && "post".equalsIgnoreCase(method);
+
+            for (Element input : form.getElementsByTag("input")) {
+                if (Objects.equals(input.id(), "kc-login")) {
+                    if (consent)
+                        parameters.add(new BasicNameValuePair(input.attr("name"), input.attr("value")));
+                } else if (Objects.equals(input.id(), "kc-cancel")) {
+                    if (!consent)
+                        parameters.add(new BasicNameValuePair(input.attr("name"), input.attr("value")));
+                } else {
+                    parameters.add(new BasicNameValuePair(input.attr("name"), input.val()));
+                }
+            }
+
+            if (isPost) {
+                HttpPost res = new HttpPost(getAuthServerContextRoot() + action);
+
+                UrlEncodedFormEntity formEntity;
+                try {
+                    formEntity = new UrlEncodedFormEntity(parameters, "UTF-8");
+                } catch (UnsupportedEncodingException e) {
+                    throw new RuntimeException(e);
+                }
+                res.setEntity(formEntity);
+
+                return res;
+            } else {
+                UriBuilder b = UriBuilder.fromPath(action);
+                for (NameValuePair parameter : parameters) {
+                    b.queryParam(parameter.getName(), parameter.getValue());
+                }
+                return new HttpGet(b.build());
+            }
+        }
+
+        throw new IllegalArgumentException("Invalid consent page: " + consentPage);
+    }
+
+    /**
      * Creates a SAML login request document with the given parameters. See SAML &lt;AuthnRequest&gt; description for more details.
+     *
      * @param issuer
      * @param assertionConsumerURL
      * @param destination
@@ -307,7 +374,8 @@ public class SamlClient {
     }
 
     /**
-     * Send request for login form and then login using user param
+     * Send request for login form and then login using user param. This method is designed for clients without required consent
+     *
      * @param user
      * @param samlEndpoint
      * @param samlRequest
@@ -318,12 +386,46 @@ public class SamlClient {
      */
     public static SAMLDocumentHolder login(UserRepresentation user, URI samlEndpoint,
                                            Document samlRequest, String relayState, Binding requestBinding, Binding expectedResponseBinding) {
+        return login(user, samlEndpoint, samlRequest, relayState, requestBinding, expectedResponseBinding, false, true);
+    }
+
+    /**
+     * Send request for login form and then login using user param. This method is designed for clients which requires consent
+     *
+     * @param user
+     * @param samlEndpoint
+     * @param samlRequest
+     * @param relayState
+     * @param requestBinding
+     * @param expectedResponseBinding
+     * @return
+     */
+    public static SAMLDocumentHolder loginWithRequiredConsent(UserRepresentation user, URI samlEndpoint,
+                                                              Document samlRequest, String relayState, Binding requestBinding, Binding expectedResponseBinding, boolean consent) {
+        return login(user, samlEndpoint, samlRequest, relayState, requestBinding, expectedResponseBinding, true, consent);
+    }
+
+    /**
+     * Send request for login form and then login using user param. Check whether client requires consent and handle consent page.
+     *
+     * @param user
+     * @param samlEndpoint
+     * @param samlRequest
+     * @param relayState
+     * @param requestBinding
+     * @param expectedResponseBinding
+     * @param consentRequired
+     * @param consent
+     * @return
+     */
+    public static SAMLDocumentHolder login(UserRepresentation user, URI samlEndpoint,
+                                           Document samlRequest, String relayState, Binding requestBinding, Binding expectedResponseBinding, boolean consentRequired, boolean consent) {
         CloseableHttpResponse response = null;
         SamlClient.RedirectStrategyWithSwitchableFollowRedirect strategy = new SamlClient.RedirectStrategyWithSwitchableFollowRedirect();
         try (CloseableHttpClient client = HttpClientBuilder.create().setRedirectStrategy(strategy).build()) {
             HttpClientContext context = HttpClientContext.create();
 
-            HttpUriRequest post = requestBinding.createSamlRequest(samlEndpoint, relayState, samlRequest);
+            HttpUriRequest post = requestBinding.createSamlUnsignedRequest(samlEndpoint, relayState, samlRequest);
             response = client.execute(post, context);
 
             assertThat(response, statusCodeIsHC(Response.Status.OK));
@@ -334,28 +436,65 @@ public class SamlClient {
 
             HttpUriRequest loginRequest = handleLoginPage(user, loginPageText);
 
+            if (consentRequired) {
+                // Client requires consent
+                response = client.execute(loginRequest, context);
+                String consentPageText = EntityUtils.toString(response.getEntity(), "UTF-8");
+                loginRequest = handleConsentPage(consentPageText, consent);
+            }
+
             strategy.setRedirectable(false);
             response = client.execute(loginRequest, context);
-
+            
             return expectedResponseBinding.extractResponse(response);
         } catch (Exception ex) {
             throw new RuntimeException(ex);
         } finally {
             if (response != null) {
                 EntityUtils.consumeQuietly(response.getEntity());
-                try { response.close(); } catch (IOException ex) { }
+                try {
+                    response.close();
+                } catch (IOException ex) {
+                }
             }
         }
     }
 
     /**
-     * Send request for login form and then login using user param
+     * Send request for login form and then login using user param for clients which doesn't require consent
+     *
      * @param user
      * @param idpInitiatedURI
      * @param expectedResponseBinding
      * @return
      */
     public static SAMLDocumentHolder idpInitiatedLogin(UserRepresentation user, URI idpInitiatedURI, Binding expectedResponseBinding) {
+        return idpInitiatedLogin(user, idpInitiatedURI, expectedResponseBinding, false, true);
+    }
+
+    /**
+     * Send request for login form and then login using user param. For clients which requires consent
+     *
+     * @param user
+     * @param idpInitiatedURI
+     * @param expectedResponseBinding
+     * @param consent
+     * @return
+     */
+    public static SAMLDocumentHolder idpInitiatedLoginWithRequiredConsent(UserRepresentation user, URI idpInitiatedURI, Binding expectedResponseBinding, boolean consent) {
+        return idpInitiatedLogin(user, idpInitiatedURI, expectedResponseBinding, true, consent);
+    }
+
+    /**
+     * Send request for login form and then login using user param. Checks whether client requires consent and handle consent page.
+     *
+     * @param user
+     * @param idpInitiatedURI
+     * @param expectedResponseBinding
+     * @param consent
+     * @return
+     */
+    public static SAMLDocumentHolder idpInitiatedLogin(UserRepresentation user, URI idpInitiatedURI, Binding expectedResponseBinding, boolean consentRequired, boolean consent) {
         CloseableHttpResponse response = null;
         SamlClient.RedirectStrategyWithSwitchableFollowRedirect strategy = new SamlClient.RedirectStrategyWithSwitchableFollowRedirect();
         try (CloseableHttpClient client = HttpClientBuilder.create().setRedirectStrategy(strategy).build()) {
@@ -373,6 +512,13 @@ public class SamlClient {
 
             HttpUriRequest loginRequest = handleLoginPage(user, loginPageText);
 
+            if (consentRequired) {
+                // Client requires consent
+                response = client.execute(loginRequest, context);
+                String consentPageText = EntityUtils.toString(response.getEntity(), "UTF-8");
+                loginRequest = handleConsentPage(consentPageText, consent);
+            }
+
             strategy.setRedirectable(false);
             response = client.execute(loginRequest, context);
 
@@ -382,9 +528,13 @@ public class SamlClient {
         } finally {
             if (response != null) {
                 EntityUtils.consumeQuietly(response.getEntity());
-                try { response.close(); } catch (IOException ex) { }
+                try {
+                    response.close();
+                } catch (IOException ex) {
+                }
             }
         }
     }
 
+
 }