keycloak-uncached

Merge pull request #1840 from velias/KEYCLOAK-2075 KEYCLOAK-2075

11/25/2015 1:35:00 PM

Changes

Details

diff --git a/docbook/saml-adapter-docs/reference/en/en-US/modules/adapter-config.xml b/docbook/saml-adapter-docs/reference/en/en-US/modules/adapter-config.xml
index cce3faf..5335362 100755
--- a/docbook/saml-adapter-docs/reference/en/en-US/modules/adapter-config.xml
+++ b/docbook/saml-adapter-docs/reference/en/en-US/modules/adapter-config.xml
@@ -11,7 +11,8 @@
         sslPolicy="EXTERNAL"
         nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
         logoutPage="/logout.jsp"
-        forceAuthentication="false">
+        forceAuthentication="false"
+        isPassive="false">
         <Keys>
             <Key signing="true" >
                 <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
@@ -63,7 +64,8 @@
 <SP entityID="sp"
     sslPolicy="ssl"
     nameIDPolicyFormat="format"
-    forceAuthentication="true">
+    forceAuthentication="true"
+    isPassive="false">
 ...
 </SP>]]></programlisting>
         <para>
@@ -106,12 +108,23 @@
                     <listitem>
                         <para>
                             SAML clients can request that a user is re-authenticated even if
-                            they are already logged in at the IDP.  Set this to true if you
+                            they are already logged in at the IDP. Set this to <literal>true</literal> if you
                             want this.
                             <emphasis>OPTIONAL.</emphasis>.  Set to <literal>false</literal> by default.
                         </para>
                     </listitem>
                 </varlistentry>
+                <varlistentry>
+                    <term>isPassive</term>
+                    <listitem>
+                        <para>
+                            SAML clients can request that a user is never asked to authenticate even if
+                            they are not logged in at the IDP. Set this to <literal>true</literal> if you want this.
+                            Do not use together with <literal>forceAuthentication</literal> as they are opposite.
+                            <emphasis>OPTIONAL.</emphasis>. Set to <literal>false</literal> by default.
+                        </para>
+                    </listitem>
+                </varlistentry>
             </variablelist>
         </para>
     </section>
diff --git a/docbook/saml-adapter-docs/reference/en/en-US/modules/tomcat-adapter.xml b/docbook/saml-adapter-docs/reference/en/en-US/modules/tomcat-adapter.xml
index f6db1b0..37ffbe1 100755
--- a/docbook/saml-adapter-docs/reference/en/en-US/modules/tomcat-adapter.xml
+++ b/docbook/saml-adapter-docs/reference/en/en-US/modules/tomcat-adapter.xml
@@ -1,5 +1,5 @@
 <chapter id="tomcat-adapter">
-    <title>Tomcat 6, 7 and 8 SAML dapters</title>
+    <title>Tomcat 6, 7 and 8 SAML adapters</title>
     <para>
         To be able to secure WAR apps deployed on Tomcat 6, 7 and 8 you must install the Keycloak Tomcat 6, 7 or 8 SAML adapter
         into your Tomcat installation.  You then have to provide some extra configuration in each WAR you deploy to
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/OIDCAuthenticationError.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/OIDCAuthenticationError.java
index 5b3f45d..089b689 100755
--- a/integration/adapter-core/src/main/java/org/keycloak/adapters/OIDCAuthenticationError.java
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/OIDCAuthenticationError.java
@@ -36,4 +36,11 @@ public class OIDCAuthenticationError implements AuthenticationError {
     public String getDescription() {
         return description;
     }
+
+    @Override
+    public String toString() {
+        return "OIDCAuthenticationError [reason=" + reason + ", description=" + description + "]";
+    }
+    
+    
 }
diff --git a/integration/adapter-spi/src/main/java/org/keycloak/adapters/spi/AuthOutcome.java b/integration/adapter-spi/src/main/java/org/keycloak/adapters/spi/AuthOutcome.java
index 60a34b2..2479884 100755
--- a/integration/adapter-spi/src/main/java/org/keycloak/adapters/spi/AuthOutcome.java
+++ b/integration/adapter-spi/src/main/java/org/keycloak/adapters/spi/AuthOutcome.java
@@ -5,8 +5,5 @@ package org.keycloak.adapters.spi;
  * @version $Revision: 1 $
  */
 public enum AuthOutcome {
-    NOT_ATTEMPTED,
-    FAILED,
-    AUTHENTICATED,
-    LOGGED_OUT
+    NOT_ATTEMPTED, FAILED, AUTHENTICATED, NOT_AUTHENTICATED, LOGGED_OUT
 }
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java
index 404fc58..91a016c 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java
@@ -5,17 +5,17 @@ package org.keycloak.adapters.saml.config.parsers;
  * @version $Revision: 1 $
  */
 public class ConfigXmlConstants {
-    public static final String KEYCLOAK_SAML_ADAPTER ="keycloak-saml-adapter";
-    public static final String SP_ELEMENT="SP";
+    public static final String KEYCLOAK_SAML_ADAPTER = "keycloak-saml-adapter";
+    public static final String SP_ELEMENT = "SP";
     public static final String ENTITY_ID_ATTR = "entityID";
     public static final String SSL_POLICY_ATTR = "sslPolicy";
     public static final String NAME_ID_POLICY_FORMAT_ATTR = "nameIDPolicyFormat";
     public static final String FORCE_AUTHENTICATION_ATTR = "forceAuthentication";
+    public static final String IS_PASSIVE_ATTR = "isPassive";
     public static final String SIGNATURE_ALGORITHM_ATTR = "signatureAlgorithm";
     public static final String SIGNATURE_CANONICALIZATION_METHOD_ATTR = "signatureCanonicalizationMethod";
     public static final String LOGOUT_PAGE_ATTR = "logoutPage";
 
-
     public static final String KEYS_ELEMENT = "Keys";
     public static final String KEY_ELEMENT = "Key";
     public static final String SIGNING_ATTR = "signing";
@@ -36,7 +36,6 @@ public class ConfigXmlConstants {
     public static final String POLICY_ATTR = "policy";
     public static final String ATTRIBUTE_ATTR = "attribute";
 
-
     public static final String ROLE_IDENTIFIERS_ELEMENT = "RoleIdentifiers";
     public static final String ATTRIBUTE_ELEMENT = "Attribute";
     public static final String NAME_ATTR = "name";
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java
index b200b61..5de2072 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java
@@ -41,6 +41,7 @@ public class DeploymentBuilder {
         deployment.setConfigured(true);
         deployment.setEntityID(sp.getEntityID());
         deployment.setForceAuthentication(sp.isForceAuthentication());
+        deployment.setIsPassive(sp.isIsPassive());
         deployment.setNameIDPolicyFormat(sp.getNameIDPolicyFormat());
         deployment.setLogoutPage(sp.getLogoutPage());
         deployment.setSignatureCanonicalizationMethod(sp.getIdp().getSignatureCanonicalizationMethod());
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java
index 3446f20..14b04e1 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java
@@ -1,21 +1,22 @@
 package org.keycloak.adapters.saml.config.parsers;
 
-import org.keycloak.adapters.saml.config.IDP;
-import org.keycloak.adapters.saml.config.Key;
-import org.keycloak.adapters.saml.config.SP;
-import org.keycloak.saml.common.exceptions.ParsingException;
-import org.keycloak.saml.common.parsers.AbstractParser;
-import org.keycloak.saml.common.util.StaxParserUtil;
-import org.keycloak.common.util.StringPropertyReplacer;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
 
 import javax.xml.namespace.QName;
 import javax.xml.stream.XMLEventReader;
 import javax.xml.stream.events.EndElement;
 import javax.xml.stream.events.StartElement;
 import javax.xml.stream.events.XMLEvent;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+
+import org.keycloak.adapters.saml.config.IDP;
+import org.keycloak.adapters.saml.config.Key;
+import org.keycloak.adapters.saml.config.SP;
+import org.keycloak.common.util.StringPropertyReplacer;
+import org.keycloak.saml.common.exceptions.ParsingException;
+import org.keycloak.saml.common.parsers.AbstractParser;
+import org.keycloak.saml.common.util.StaxParserUtil;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -25,13 +26,16 @@ public class SPXmlParser extends AbstractParser {
 
     public static String getAttributeValue(StartElement startElement, String tag) {
         String str = StaxParserUtil.getAttributeValue(startElement, tag);
-        if (str != null) return StringPropertyReplacer.replaceProperties(str);
-        else return str;
+        if (str != null)
+            return StringPropertyReplacer.replaceProperties(str);
+        else
+            return str;
     }
 
     public static boolean getBooleanAttributeValue(StartElement startElement, String tag, boolean defaultValue) {
         String result = getAttributeValue(startElement, tag);
-        if (result == null) return defaultValue;
+        if (result == null)
+            return defaultValue;
         return Boolean.valueOf(result);
     }
 
@@ -41,11 +45,11 @@ public class SPXmlParser extends AbstractParser {
 
     public static String getElementText(XMLEventReader xmlEventReader) throws ParsingException {
         String result = StaxParserUtil.getElementText(xmlEventReader);
-        if (result != null) result = StringPropertyReplacer.replaceProperties(result);
+        if (result != null)
+            result = StringPropertyReplacer.replaceProperties(result);
         return result;
     }
 
-
     @Override
     public Object parse(XMLEventReader xmlEventReader) throws ParsingException {
         StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
@@ -61,6 +65,7 @@ public class SPXmlParser extends AbstractParser {
         sp.setLogoutPage(getAttributeValue(startElement, ConfigXmlConstants.LOGOUT_PAGE_ATTR));
         sp.setNameIDPolicyFormat(getAttributeValue(startElement, ConfigXmlConstants.NAME_ID_POLICY_FORMAT_ATTR));
         sp.setForceAuthentication(getBooleanAttributeValue(startElement, ConfigXmlConstants.FORCE_AUTHENTICATION_ATTR));
+        sp.setIsPassive(getBooleanAttributeValue(startElement, ConfigXmlConstants.IS_PASSIVE_ATTR));
         while (xmlEventReader.hasNext()) {
             XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
             if (xmlEvent == null)
@@ -79,7 +84,7 @@ public class SPXmlParser extends AbstractParser {
             String tag = StaxParserUtil.getStartElementName(startElement);
             if (tag.equals(ConfigXmlConstants.KEYS_ELEMENT)) {
                 KeysXmlParser parser = new KeysXmlParser();
-                List<Key> keys = (List<Key>)parser.parse(xmlEventReader);
+                List<Key> keys = (List<Key>) parser.parse(xmlEventReader);
                 sp.setKeys(keys);
             } else if (tag.equals(ConfigXmlConstants.PRINCIPAL_NAME_MAPPING_ELEMENT)) {
                 StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
@@ -98,7 +103,7 @@ public class SPXmlParser extends AbstractParser {
                 parseRoleMapping(xmlEventReader, sp);
             } else if (tag.equals(ConfigXmlConstants.IDP_ELEMENT)) {
                 IDPXmlParser parser = new IDPXmlParser();
-                IDP idp = (IDP)parser.parse(xmlEventReader);
+                IDP idp = (IDP) parser.parse(xmlEventReader);
                 sp.setIdp(idp);
             } else {
                 StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
@@ -108,7 +113,7 @@ public class SPXmlParser extends AbstractParser {
         return sp;
     }
 
-    protected void parseRoleMapping(XMLEventReader xmlEventReader, SP sp)  throws ParsingException {
+    protected void parseRoleMapping(XMLEventReader xmlEventReader, SP sp) throws ParsingException {
         StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
         StaxParserUtil.validate(startElement, ConfigXmlConstants.ROLE_IDENTIFIERS_ELEMENT);
         Set<String> roleAttributes = new HashSet<>();
@@ -144,7 +149,6 @@ public class SPXmlParser extends AbstractParser {
         sp.setRoleAttributes(roleAttributes);
     }
 
-
     @Override
     public boolean supports(QName qname) {
         return false;
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/SP.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/SP.java
index f37f930..5203b13 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/SP.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/SP.java
@@ -33,6 +33,7 @@ public class SP implements Serializable {
     private String entityID;
     private String sslPolicy;
     private boolean forceAuthentication;
+    private boolean isPassive;
     private String logoutPage;
     private List<Key> keys;
     private String nameIDPolicyFormat;
@@ -64,6 +65,14 @@ public class SP implements Serializable {
         this.forceAuthentication = forceAuthentication;
     }
 
+    public boolean isIsPassive() {
+        return isPassive;
+    }
+
+    public void setIsPassive(boolean isPassive) {
+        this.isPassive = isPassive;
+    }
+
     public List<Key> getKeys() {
         return keys;
     }
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/DefaultSamlDeployment.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/DefaultSamlDeployment.java
index 26fad92..7aab095 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/DefaultSamlDeployment.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/DefaultSamlDeployment.java
@@ -1,13 +1,13 @@
 package org.keycloak.adapters.saml;
 
-import org.keycloak.common.enums.SslRequired;
-import org.keycloak.saml.SignatureAlgorithm;
-
 import java.security.KeyPair;
 import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.util.Set;
 
+import org.keycloak.common.enums.SslRequired;
+import org.keycloak.saml.SignatureAlgorithm;
+
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
@@ -31,7 +31,7 @@ public class DefaultSamlDeployment implements SamlDeployment {
             return validateResponseSignature;
         }
 
-         @Override
+        @Override
         public Binding getRequestBinding() {
             return requestBinding;
         }
@@ -97,7 +97,7 @@ public class DefaultSamlDeployment implements SamlDeployment {
             return signResponse;
         }
 
-         @Override
+        @Override
         public Binding getRequestBinding() {
             return requestBinding;
         }
@@ -150,12 +150,8 @@ public class DefaultSamlDeployment implements SamlDeployment {
         }
     }
 
-
-
     public static class DefaultIDP implements IDP {
 
-
-
         private String entityID;
         private PublicKey signatureValidationKey;
         private SingleSignOnService singleSignOnService;
@@ -204,6 +200,7 @@ public class DefaultSamlDeployment implements SamlDeployment {
     private String entityID;
     private String nameIDPolicyFormat;
     private boolean forceAuthentication;
+    private boolean isPassive;
     private PrivateKey decryptionKey;
     private KeyPair signingKeyPair;
     private String assertionConsumerServiceUrl;
@@ -214,7 +211,6 @@ public class DefaultSamlDeployment implements SamlDeployment {
     private SignatureAlgorithm signatureAlgorithm;
     private String signatureCanonicalizationMethod;
 
-
     @Override
     public IDP getIDP() {
         return idp;
@@ -244,6 +240,11 @@ public class DefaultSamlDeployment implements SamlDeployment {
     public boolean isForceAuthentication() {
         return forceAuthentication;
     }
+    
+   @Override
+    public boolean isIsPassive() {
+        return isPassive;
+    }
 
     @Override
     public PrivateKey getDecryptionKey() {
@@ -265,7 +266,7 @@ public class DefaultSamlDeployment implements SamlDeployment {
         return roleAttributeNames;
     }
 
-   @Override
+    @Override
     public PrincipalNamePolicy getPrincipalNamePolicy() {
         return principalNamePolicy;
     }
@@ -298,6 +299,10 @@ public class DefaultSamlDeployment implements SamlDeployment {
     public void setForceAuthentication(boolean forceAuthentication) {
         this.forceAuthentication = forceAuthentication;
     }
+    
+    public void setIsPassive(boolean isPassive){
+        this.isPassive = isPassive;
+    }
 
     public void setDecryptionKey(PrivateKey decryptionKey) {
         this.decryptionKey = decryptionKey;
@@ -332,7 +337,7 @@ public class DefaultSamlDeployment implements SamlDeployment {
         this.logoutPage = logoutPage;
     }
 
-     @Override
+    @Override
     public String getSignatureCanonicalizationMethod() {
         return signatureCanonicalizationMethod;
     }
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/InitiateLogin.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/InitiateLogin.java
index 158bae8..4c7cbab 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/InitiateLogin.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/InitiateLogin.java
@@ -48,7 +48,7 @@ public class InitiateLogin implements AuthChallenge {
             SAML2AuthnRequestBuilder authnRequestBuilder = new SAML2AuthnRequestBuilder()
                     .destination(destinationUrl)
                     .issuer(issuerURL)
-                    .forceAuthn(deployment.isForceAuthentication())
+                    .forceAuthn(deployment.isForceAuthentication()).isPassive(deployment.isIsPassive())
                     .nameIdPolicy(SAML2NameIDPolicyBuilder.format(nameIDPolicyFormat));
             if (deployment.getIDP().getSingleSignOnService().getResponseBinding() != null) {
                 String protocolBinding = JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get();
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlAuthenticationError.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlAuthenticationError.java
index 8b63103..c85fd63 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlAuthenticationError.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlAuthenticationError.java
@@ -40,4 +40,10 @@ public class SamlAuthenticationError implements AuthenticationError {
     public StatusResponseType getStatus() {
         return status;
     }
+
+    @Override
+    public String toString() {
+        return "SamlAuthenticationError [reason=" + reason + ", status=" + status + "]";
+    }
+    
 }
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlAuthenticator.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlAuthenticator.java
index 4c5f6a7..02097db 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlAuthenticator.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlAuthenticator.java
@@ -17,7 +17,9 @@ import org.keycloak.dom.saml.v2.assertion.SubjectType;
 import org.keycloak.dom.saml.v2.protocol.LogoutRequestType;
 import org.keycloak.dom.saml.v2.protocol.RequestAbstractType;
 import org.keycloak.dom.saml.v2.protocol.ResponseType;
+import org.keycloak.dom.saml.v2.protocol.StatusCodeType;
 import org.keycloak.dom.saml.v2.protocol.StatusResponseType;
+import org.keycloak.dom.saml.v2.protocol.StatusType;
 import org.keycloak.saml.BaseSAML2BindingBuilder;
 import org.keycloak.saml.SAML2LogoutRequestBuilder;
 import org.keycloak.saml.SAML2LogoutResponseBuilder;
@@ -27,6 +29,7 @@ import org.keycloak.saml.common.constants.GeneralConstants;
 import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
 import org.keycloak.saml.common.exceptions.ProcessingException;
 import org.keycloak.saml.common.util.Base64;
+import org.keycloak.saml.common.util.StringUtil;
 import org.keycloak.saml.processing.api.saml.v2.sig.SAML2Signature;
 import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
 import org.keycloak.saml.processing.core.saml.v2.util.AssertionUtil;
@@ -207,7 +210,8 @@ public abstract class SamlAuthenticator {
             log.error("Request URI does not match SAML request destination");
             return AuthOutcome.FAILED;
         }
-        if (statusResponse instanceof ResponseType) {
+        
+        if (statusResponse instanceof ResponseType) {            
             try {
                 if (deployment.getIDP().getSingleSignOnService().validateResponseSignature()) {
                     try {
@@ -254,7 +258,15 @@ public abstract class SamlAuthenticator {
                 }
 
             } else if (sessionStore.isLoggingIn()) {
+
                 try {
+                    // KEYCLOAK-2107 - handle user not authenticated due passive mode. Return special outcome so different authentication mechanisms can behave accordingly.
+                    StatusType status = statusResponse.getStatus();
+                    if(checkStatusCodeValue(status.getStatusCode(), JBossSAMLURIConstants.STATUS_RESPONDER.get()) && checkStatusCodeValue(status.getStatusCode().getStatusCode(), JBossSAMLURIConstants.STATUS_NO_PASSIVE.get())){
+                        log.debug("Not authenticated due passive mode Status found in SAML response: " + status.toString());
+                        return AuthOutcome.NOT_AUTHENTICATED;
+                    }
+
                     challenge = new AuthChallenge() {
                         @Override
                         public boolean challenge(HttpFacade exchange) {
@@ -287,7 +299,16 @@ public abstract class SamlAuthenticator {
         }
     }
 
+    private boolean checkStatusCodeValue(StatusCodeType statusCode, String expectedValue){
+        if(statusCode != null && statusCode.getValue()!=null){
+            String v = statusCode.getValue().toString();
+            return expectedValue.equals(v);
+        }
+        return false;
+    }
+    
     protected AuthOutcome handleLoginResponse(ResponseType responseType) {
+        
         AssertionType assertion = null;
         try {
             assertion = AssertionUtil.getAssertion(responseType, deployment.getDecryptionKey());
@@ -295,7 +316,7 @@ public abstract class SamlAuthenticator {
                 return initiateLogin();
             }
         } catch (Exception e) {
-            log.error("Error extracting SAML assertion, e");
+            log.error("Error extracting SAML assertion: " + e.getMessage());
             challenge = new AuthChallenge() {
                 @Override
                 public boolean challenge(HttpFacade exchange) {
@@ -434,9 +455,9 @@ public abstract class SamlAuthenticator {
         return SAMLRequestParser.parseRequestRedirectBinding(response);
     }
 
+    
     protected SAMLDocumentHolder extractPostBindingResponse(String response) {
         byte[] samlBytes = PostBindingUtil.base64Decode(response);
-        String xml = new String(samlBytes);
         return SAMLRequestParser.parseResponseDocument(samlBytes);
     }
 
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java
index 0728447..8c53236 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java
@@ -56,6 +56,7 @@ public interface SamlDeployment {
     String getEntityID();
     String getNameIDPolicyFormat();
     boolean isForceAuthentication();
+    boolean isIsPassive();
     PrivateKey getDecryptionKey();
     KeyPair getSigningKeyPair();
     String getSignatureCanonicalizationMethod();
diff --git a/saml/client-adapter/core/src/main/resources/schema/keycloak_saml_adapter_1_6.xsd b/saml/client-adapter/core/src/main/resources/schema/keycloak_saml_adapter_1_6.xsd
index 534c9ae..d3e55f9 100755
--- a/saml/client-adapter/core/src/main/resources/schema/keycloak_saml_adapter_1_6.xsd
+++ b/saml/client-adapter/core/src/main/resources/schema/keycloak_saml_adapter_1_6.xsd
@@ -33,6 +33,7 @@
         <xs:attribute name="nameIDPolicyFormat" type="xs:string" use="optional"/>
         <xs:attribute name="logoutPage" type="xs:string" use="optional"/>
         <xs:attribute name="forceAuthentication" type="xs:boolean" use="optional"/>
+        <xs:attribute name="isPassive" type="xs:boolean" use="optional"/>
     </xs:complexType>
 
     <xs:complexType name="keys-type">
diff --git a/saml/client-adapter/core/src/test/java/org/keycloak/test/adapters/saml/XmlParserTest.java b/saml/client-adapter/core/src/test/java/org/keycloak/test/adapters/saml/XmlParserTest.java
index c92fec6..5a6e037 100755
--- a/saml/client-adapter/core/src/test/java/org/keycloak/test/adapters/saml/XmlParserTest.java
+++ b/saml/client-adapter/core/src/test/java/org/keycloak/test/adapters/saml/XmlParserTest.java
@@ -68,6 +68,7 @@ public class XmlParserTest {
         Assert.assertEquals("ssl", sp.getSslPolicy());
         Assert.assertEquals("format", sp.getNameIDPolicyFormat());
         Assert.assertTrue(sp.isForceAuthentication());
+        Assert.assertTrue(sp.isIsPassive());
         Assert.assertEquals(2, sp.getKeys().size());
         Key signing = sp.getKeys().get(0);
         Assert.assertTrue(signing.isSigning());
diff --git a/saml/client-adapter/core/src/test/resources/keycloak-saml.xml b/saml/client-adapter/core/src/test/resources/keycloak-saml.xml
index ef910dc..ae8c96d 100755
--- a/saml/client-adapter/core/src/test/resources/keycloak-saml.xml
+++ b/saml/client-adapter/core/src/test/resources/keycloak-saml.xml
@@ -2,7 +2,8 @@
     <SP entityID="sp"
         sslPolicy="ssl"
         nameIDPolicyFormat="format"
-        forceAuthentication="true">
+        forceAuthentication="true"
+        isPassive="true">
         <Keys>
             <Key signing="true" >
                 <KeyStore file="file" resource="cp" password="pw">
diff --git a/saml/client-adapter/core/src/test/resources/keycloak-saml2.xml b/saml/client-adapter/core/src/test/resources/keycloak-saml2.xml
index ee6388d..7662e61 100755
--- a/saml/client-adapter/core/src/test/resources/keycloak-saml2.xml
+++ b/saml/client-adapter/core/src/test/resources/keycloak-saml2.xml
@@ -4,7 +4,8 @@
         nameIDPolicyFormat="format"
         signatureAlgorithm=""
         signatureCanonicalizationMethod=""
-        forceAuthentication="true">
+        forceAuthentication="true"
+        isPassive="true">
         <Keys>
             <Key signing="true" >
                 <KeyStore file="file" resource="cp" password="pw">
diff --git a/saml/client-adapter/servlet-filter/src/main/java/org/keycloak/adapters/saml/servlet/SamlFilter.java b/saml/client-adapter/servlet-filter/src/main/java/org/keycloak/adapters/saml/servlet/SamlFilter.java
index b2a0ded..ac95784 100755
--- a/saml/client-adapter/servlet-filter/src/main/java/org/keycloak/adapters/saml/servlet/SamlFilter.java
+++ b/saml/client-adapter/servlet-filter/src/main/java/org/keycloak/adapters/saml/servlet/SamlFilter.java
@@ -1,18 +1,11 @@
 package org.keycloak.adapters.saml.servlet;
 
-import org.keycloak.adapters.spi.AuthChallenge;
-import org.keycloak.adapters.spi.AuthOutcome;
-import org.keycloak.adapters.spi.InMemorySessionIdMapper;
-import org.keycloak.adapters.spi.SessionIdMapper;
-import org.keycloak.adapters.saml.DefaultSamlDeployment;
-import org.keycloak.adapters.saml.SamlAuthenticator;
-import org.keycloak.adapters.saml.SamlDeployment;
-import org.keycloak.adapters.saml.SamlDeploymentContext;
-import org.keycloak.adapters.saml.SamlSession;
-import org.keycloak.adapters.saml.config.parsers.DeploymentBuilder;
-import org.keycloak.adapters.saml.config.parsers.ResourceLoader;
-import org.keycloak.adapters.servlet.ServletHttpFacade;
-import org.keycloak.saml.common.exceptions.ParsingException;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 import javax.servlet.Filter;
 import javax.servlet.FilterChain;
@@ -24,12 +17,20 @@ import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletRequestWrapper;
 import javax.servlet.http.HttpServletResponse;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.logging.Level;
-import java.util.logging.Logger;
+
+import org.keycloak.adapters.saml.DefaultSamlDeployment;
+import org.keycloak.adapters.saml.SamlAuthenticator;
+import org.keycloak.adapters.saml.SamlDeployment;
+import org.keycloak.adapters.saml.SamlDeploymentContext;
+import org.keycloak.adapters.saml.SamlSession;
+import org.keycloak.adapters.saml.config.parsers.DeploymentBuilder;
+import org.keycloak.adapters.saml.config.parsers.ResourceLoader;
+import org.keycloak.adapters.servlet.ServletHttpFacade;
+import org.keycloak.adapters.spi.AuthChallenge;
+import org.keycloak.adapters.spi.AuthOutcome;
+import org.keycloak.adapters.spi.InMemorySessionIdMapper;
+import org.keycloak.adapters.spi.SessionIdMapper;
+import org.keycloak.saml.common.exceptions.ParsingException;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -38,7 +39,7 @@ import java.util.logging.Logger;
 public class SamlFilter implements Filter {
     protected SamlDeploymentContext deploymentContext;
     protected SessionIdMapper idMapper = new InMemorySessionIdMapper();
-    private final static Logger log = Logger.getLogger(""+SamlFilter.class);
+    private final static Logger log = Logger.getLogger("" + SamlFilter.class);
 
     @Override
     public void init(final FilterConfig filterConfig) throws ServletException {
@@ -46,12 +47,14 @@ public class SamlFilter implements Filter {
         if (configResolverClass != null) {
             try {
                 throw new RuntimeException("Not implemented yet");
-                //KeycloakConfigResolver configResolver = (KeycloakConfigResolver) context.getLoader().getClassLoader().loadClass(configResolverClass).newInstance();
-                //deploymentContext = new SamlDeploymentContext(configResolver);
-                //log.log(Level.INFO, "Using {0} to resolve Keycloak configuration on a per-request basis.", configResolverClass);
+                // KeycloakConfigResolver configResolver = (KeycloakConfigResolver)
+                // context.getLoader().getClassLoader().loadClass(configResolverClass).newInstance();
+                // deploymentContext = new SamlDeploymentContext(configResolver);
+                // log.log(Level.INFO, "Using {0} to resolve Keycloak configuration on a per-request basis.",
+                // configResolverClass);
             } catch (Exception ex) {
-                log.log(Level.FINE, "The specified resolver {0} could NOT be loaded. Keycloak is unconfigured and will deny all requests. Reason: {1}", new Object[]{configResolverClass, ex.getMessage()});
-                //deploymentContext = new AdapterDeploymentContext(new KeycloakDeployment());
+                log.log(Level.FINE, "The specified resolver {0} could NOT be loaded. Keycloak is unconfigured and will deny all requests. Reason: {1}", new Object[] { configResolverClass, ex.getMessage() });
+                // deploymentContext = new AdapterDeploymentContext(new KeycloakDeployment());
             }
         } else {
             String fp = filterConfig.getInitParameter("keycloak.config.file");
@@ -65,7 +68,8 @@ public class SamlFilter implements Filter {
             } else {
                 String path = "/WEB-INF/keycloak-saml.xml";
                 String pathParam = filterConfig.getInitParameter("keycloak.config.path");
-                if (pathParam != null) path = pathParam;
+                if (pathParam != null)
+                    path = pathParam;
                 is = filterConfig.getServletContext().getResourceAsStream(path);
             }
             final SamlDeployment deployment;
@@ -105,7 +109,6 @@ public class SamlFilter implements Filter {
         }
         FilterSamlSessionStore tokenStore = new FilterSamlSessionStore(request, facade, 100000, idMapper);
 
-
         SamlAuthenticator authenticator = new SamlAuthenticator(facade, deployment, tokenStore) {
             @Override
             protected void completeAuthentication(SamlSession account) {
@@ -139,6 +142,16 @@ public class SamlFilter implements Filter {
             challenge.challenge(facade);
             return;
         }
+
+        if (deployment.isIsPassive() && outcome == AuthOutcome.NOT_AUTHENTICATED) {
+            log.fine("PASSIVE_NOT_AUTHENTICATED");
+            if (facade.isEnded()) {
+                return;
+            }
+            chain.doFilter(req, res);
+            return;
+        }
+
         if (!facade.isEnded()) {
             response.sendError(403);
         }
diff --git a/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/AbstractSamlAuthMech.java b/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/AbstractSamlAuthMech.java
index 8f1929a..3d632dd 100755
--- a/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/AbstractSamlAuthMech.java
+++ b/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/AbstractSamlAuthMech.java
@@ -16,6 +16,15 @@
  */
 package org.keycloak.adapters.saml.undertow;
 
+import org.keycloak.adapters.saml.SamlDeployment;
+import org.keycloak.adapters.saml.SamlDeploymentContext;
+import org.keycloak.adapters.saml.SamlSessionStore;
+import org.keycloak.adapters.spi.AuthChallenge;
+import org.keycloak.adapters.spi.AuthOutcome;
+import org.keycloak.adapters.spi.HttpFacade;
+import org.keycloak.adapters.undertow.UndertowHttpFacade;
+import org.keycloak.adapters.undertow.UndertowUserSessionManagement;
+
 import io.undertow.security.api.AuthenticationMechanism;
 import io.undertow.security.api.NotificationReceiver;
 import io.undertow.security.api.SecurityContext;
@@ -24,14 +33,6 @@ import io.undertow.server.HttpServerExchange;
 import io.undertow.util.AttachmentKey;
 import io.undertow.util.Headers;
 import io.undertow.util.StatusCodes;
-import org.keycloak.adapters.spi.AuthChallenge;
-import org.keycloak.adapters.spi.AuthOutcome;
-import org.keycloak.adapters.spi.HttpFacade;
-import org.keycloak.adapters.saml.SamlDeployment;
-import org.keycloak.adapters.saml.SamlDeploymentContext;
-import org.keycloak.adapters.saml.SamlSessionStore;
-import org.keycloak.adapters.undertow.UndertowHttpFacade;
-import org.keycloak.adapters.undertow.UndertowUserSessionManagement;
 
 /**
  * Abstract base class for a Keycloak-enabled Undertow AuthenticationMechanism.
@@ -44,8 +45,7 @@ public abstract class AbstractSamlAuthMech implements AuthenticationMechanism {
     protected UndertowUserSessionManagement sessionManagement;
     protected String errorPage;
 
-    public AbstractSamlAuthMech(SamlDeploymentContext deploymentContext, UndertowUserSessionManagement sessionManagement,
-                                String errorPage) {
+    public AbstractSamlAuthMech(SamlDeploymentContext deploymentContext, UndertowUserSessionManagement sessionManagement, String errorPage) {
         this.deploymentContext = deploymentContext;
         this.sessionManagement = sessionManagement;
         this.errorPage = errorPage;
@@ -69,19 +69,19 @@ public abstract class AbstractSamlAuthMech implements AuthenticationMechanism {
     }
 
     static void sendRedirect(final HttpServerExchange exchange, final String location) {
-        // TODO - String concatenation to construct URLS is extremely error prone - switch to a URI which will better handle this.
+        // TODO - String concatenation to construct URLS is extremely error prone - switch to a URI which will better
+        // handle this.
         String loc = exchange.getRequestScheme() + "://" + exchange.getHostAndPort() + location;
         exchange.getResponseHeaders().put(Headers.LOCATION, loc);
     }
 
-
-
     protected void registerNotifications(final SecurityContext securityContext) {
 
         final NotificationReceiver logoutReceiver = new NotificationReceiver() {
             @Override
             public void handleNotification(SecurityNotification notification) {
-                if (notification.getEventType() != SecurityNotification.EventType.LOGGED_OUT) return;
+                if (notification.getEventType() != SecurityNotification.EventType.LOGGED_OUT)
+                    return;
 
                 HttpServerExchange exchange = notification.getExchange();
                 UndertowHttpFacade facade = createFacade(exchange);
@@ -104,13 +104,16 @@ public abstract class AbstractSamlAuthMech implements AuthenticationMechanism {
             return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
         }
         SamlSessionStore sessionStore = getTokenStore(exchange, facade, deployment, securityContext);
-        UndertowSamlAuthenticator authenticator = new UndertowSamlAuthenticator(securityContext, facade,
-                deploymentContext.resolveDeployment(facade), sessionStore);
+        UndertowSamlAuthenticator authenticator = new UndertowSamlAuthenticator(securityContext, facade, deploymentContext.resolveDeployment(facade), sessionStore);
         AuthOutcome outcome = authenticator.authenticate();
         if (outcome == AuthOutcome.AUTHENTICATED) {
             registerNotifications(securityContext);
             return AuthenticationMechanismOutcome.AUTHENTICATED;
         }
+        if (outcome == AuthOutcome.NOT_AUTHENTICATED) {
+            // we are in passive mode and user is not authenticated, let app server to try another auth mechanism
+            return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
+        }
         if (outcome == AuthOutcome.LOGGED_OUT) {
             securityContext.logout();
             if (deployment.getLogoutPage() != null) {
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/StatusCodeType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/StatusCodeType.java
index 482834b..826928a 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/StatusCodeType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/StatusCodeType.java
@@ -80,4 +80,10 @@ public class StatusCodeType implements Serializable {
     public void setValue(URI value) {
         this.value = value;
     }
+
+    @Override
+    public String toString() {
+        return "StatusCodeType [value=" + value + ", statusCode=" + statusCode + "]";
+    }
+
 }
\ No newline at end of file
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/StatusType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/StatusType.java
index 2e2eab9..ec92dd4 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/StatusType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/StatusType.java
@@ -100,4 +100,9 @@ public class StatusType implements Serializable {
         this.statusDetail = value;
     }
 
+    @Override
+    public String toString() {
+        return "StatusType [statusCode=" + statusCode + ", statusMessage=" + statusMessage + ", statusDetail=" + statusDetail + "]";
+    }
+
 }
diff --git a/saml/saml-core/src/main/java/org/keycloak/saml/SAML2AuthnRequestBuilder.java b/saml/saml-core/src/main/java/org/keycloak/saml/SAML2AuthnRequestBuilder.java
index 40db78c..b043a12 100755
--- a/saml/saml-core/src/main/java/org/keycloak/saml/SAML2AuthnRequestBuilder.java
+++ b/saml/saml-core/src/main/java/org/keycloak/saml/SAML2AuthnRequestBuilder.java
@@ -17,16 +17,16 @@
  */
 package org.keycloak.saml;
 
+import java.net.URI;
+
+import org.keycloak.dom.saml.v2.assertion.NameIDType;
+import org.keycloak.dom.saml.v2.protocol.AuthnRequestType;
 import org.keycloak.saml.common.exceptions.ConfigurationException;
 import org.keycloak.saml.processing.api.saml.v2.request.SAML2Request;
 import org.keycloak.saml.processing.core.saml.v2.common.IDGenerator;
 import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil;
-import org.keycloak.dom.saml.v2.assertion.NameIDType;
-import org.keycloak.dom.saml.v2.protocol.AuthnRequestType;
 import org.w3c.dom.Document;
 
-import java.net.URI;
-
 /**
  * @author pedroigor
  */
@@ -64,6 +64,11 @@ public class SAML2AuthnRequestBuilder {
         return this;
     }
 
+    public SAML2AuthnRequestBuilder isPassive(boolean isPassive) {
+        this.authnRequestType.setIsPassive(isPassive);
+        return this;
+    }
+
     public SAML2AuthnRequestBuilder nameIdPolicy(SAML2NameIDPolicyBuilder nameIDPolicy) {
         this.authnRequestType.setNameIDPolicy(nameIDPolicy.build());
         return this;
@@ -74,7 +79,7 @@ public class SAML2AuthnRequestBuilder {
         return this;
     }
 
-    public Document toDocument()  {
+    public Document toDocument() {
         try {
             AuthnRequestType authnRequestType = this.authnRequestType;
 
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
index 8d6fa15..3a30c2e 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
@@ -1,5 +1,22 @@
 package org.keycloak.protocol.saml;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpResponse;
 import org.apache.http.NameValuePair;
@@ -36,30 +53,14 @@ import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
 import org.keycloak.saml.common.exceptions.ConfigurationException;
 import org.keycloak.saml.common.exceptions.ParsingException;
 import org.keycloak.saml.common.exceptions.ProcessingException;
+import org.keycloak.services.ErrorPage;
 import org.keycloak.services.managers.ClientSessionCode;
 import org.keycloak.services.managers.ResourceAdminManager;
 import org.keycloak.services.messages.Messages;
 import org.keycloak.services.resources.RealmsResource;
 import org.keycloak.services.resources.admin.ClientAttributeCertificateResource;
-import org.keycloak.services.ErrorPage;
 import org.w3c.dom.Document;
 
-import javax.ws.rs.core.HttpHeaders;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.UriBuilder;
-import javax.ws.rs.core.UriInfo;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
-import java.security.PublicKey;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
-
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
@@ -67,7 +68,6 @@ import java.util.UUID;
 public class SamlProtocol implements LoginProtocol {
     protected static final Logger logger = Logger.getLogger(SamlProtocol.class);
 
-
     public static final String ATTRIBUTE_TRUE_VALUE = "true";
     public static final String ATTRIBUTE_FALSE_VALUE = "false";
     public static final String SAML_SIGNING_CERTIFICATE_ATTRIBUTE = "saml.signing." + ClientAttributeCertificateResource.X509CERTIFICATE;
@@ -115,7 +115,6 @@ public class SamlProtocol implements LoginProtocol {
 
     protected EventBuilder event;
 
-
     @Override
     public SamlProtocol setSession(KeycloakSession session) {
         this.session = session;
@@ -135,7 +134,7 @@ public class SamlProtocol implements LoginProtocol {
     }
 
     @Override
-    public SamlProtocol setHttpHeaders(HttpHeaders headers){
+    public SamlProtocol setHttpHeaders(HttpHeaders headers) {
         this.headers = headers;
         return this;
     }
@@ -146,48 +145,72 @@ public class SamlProtocol implements LoginProtocol {
         return this;
     }
 
-
     @Override
-    public Response cancelLogin(ClientSessionModel clientSession) {
-        RestartLoginCookie.expireRestartCookie(realm, session.getContext().getConnection(), uriInfo);
-        if ("true".equals(clientSession.getClient().getAttribute(SAML_IDP_INITIATED_LOGIN))) {
-            UriBuilder builder = RealmsResource.protocolUrl(uriInfo).path(SamlService.class, "idpInitiatedSSO");
-            Map<String, String> params = new HashMap<>();
-            params.put("realm", realm.getName());
-            params.put("protocol", LOGIN_PROTOCOL);
-            params.put("client", clientSession.getClient().getAttribute(SAML_IDP_INITIATED_SSO_URL_NAME));
-            session.sessions().removeClientSession(realm, clientSession);
-            URI redirect = builder.buildFromMap(params);
-            return Response.status(302).location(redirect).build();
-        } else {
+    public Response sendError(ClientSessionModel clientSession, Error error) {
+        try {
+            if ("true".equals(clientSession.getClient().getAttribute(SAML_IDP_INITIATED_LOGIN))) {
+                if (error == Error.CANCELLED_BY_USER) {
+                    UriBuilder builder = RealmsResource.protocolUrl(uriInfo).path(SamlService.class, "idpInitiatedSSO");
+                    Map<String, String> params = new HashMap<>();
+                    params.put("realm", realm.getName());
+                    params.put("protocol", LOGIN_PROTOCOL);
+                    params.put("client", clientSession.getClient().getAttribute(SAML_IDP_INITIATED_SSO_URL_NAME));
+                    URI redirect = builder.buildFromMap(params);
+                    return Response.status(302).location(redirect).build();
+                } else {
+                    return ErrorPage.error(session, translateErrorToIdpInitiatedErrorMessage(error));
+                }
+            } else {
+                SAML2ErrorResponseBuilder builder = new SAML2ErrorResponseBuilder().destination(clientSession.getRedirectUri()).issuer(getResponseIssuer(realm)).status(translateErrorToSAMLStatus(error).get());
+                try {
+                    JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder().relayState(clientSession.getNote(GeneralConstants.RELAY_STATE));
+                    Document document = builder.buildDocument();
+                    if (isPostBinding(clientSession)) {
+                        return binding.postBinding(document).response(clientSession.getRedirectUri());
+                    } else {
+                        return binding.redirectBinding(document).response(clientSession.getRedirectUri());
+                    }
+                } catch (Exception e) {
+                    return ErrorPage.error(session, Messages.FAILED_TO_PROCESS_RESPONSE);
+                }
+            }
+        } finally {
+            RestartLoginCookie.expireRestartCookie(realm, session.getContext().getConnection(), uriInfo);
             session.sessions().removeClientSession(realm, clientSession);
-            return getErrorResponse(clientSession, JBossSAMLURIConstants.STATUS_REQUEST_DENIED.get());
         }
     }
 
-    protected String getResponseIssuer(RealmModel realm) {
-        return RealmsResource.realmBaseUrl(uriInfo).build(realm.getName()).toString();
+    private JBossSAMLURIConstants translateErrorToSAMLStatus(Error error) {
+        switch (error) {
+        case CANCELLED_BY_USER:
+        case CONSENT_DENIED:
+            return JBossSAMLURIConstants.STATUS_REQUEST_DENIED;
+        case PASSIVE_INTERACTION_REQUIRED:
+        case PASSIVE_LOGIN_REQUIRED:
+            return JBossSAMLURIConstants.STATUS_NO_PASSIVE;
+        default:
+            logger.warn("Untranslated protocol Error: " + error.name() + " so we return default SAML error");
+            return JBossSAMLURIConstants.STATUS_REQUEST_DENIED;
+        }
     }
 
-    protected Response getErrorResponse(ClientSessionModel clientSession, String status) {
-        SAML2ErrorResponseBuilder builder = new SAML2ErrorResponseBuilder()
-                .destination(clientSession.getRedirectUri())
-                .issuer(getResponseIssuer(realm))
-                .status(status);
-      try {
-          JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder()
-                  .relayState(clientSession.getNote(GeneralConstants.RELAY_STATE));
-          Document document = builder.buildDocument();
-          if (isPostBinding(clientSession)) {
-              return binding.postBinding(document).response(clientSession.getRedirectUri());
-          } else {
-              return binding.redirectBinding(document).response(clientSession.getRedirectUri());
-          }
-        } catch (Exception e) {
-            return ErrorPage.error(session, Messages.FAILED_TO_PROCESS_RESPONSE);
+    private String translateErrorToIdpInitiatedErrorMessage(Error error) {
+        switch (error) {
+        case CONSENT_DENIED:
+            return Messages.CONSENT_DENIED;
+        case PASSIVE_INTERACTION_REQUIRED:
+        case PASSIVE_LOGIN_REQUIRED:
+            return Messages.UNEXPECTED_ERROR_HANDLING_REQUEST;
+        default:
+            logger.warn("Untranslated protocol Error: " + error.name() + " so we return default error message");
+            return Messages.UNEXPECTED_ERROR_HANDLING_REQUEST;
         }
     }
 
+    protected String getResponseIssuer(RealmModel realm) {
+        return RealmsResource.realmBaseUrl(uriInfo).build(realm.getName()).toString();
+    }
+
     protected boolean isPostBinding(ClientSessionModel clientSession) {
         ClientModel client = clientSession.getClient();
         return SamlProtocol.SAML_POST_BINDING.equals(clientSession.getNote(SamlProtocol.SAML_BINDING)) || forcePostBinding(client);
@@ -198,8 +221,6 @@ public class SamlProtocol implements LoginProtocol {
         return SamlProtocol.SAML_POST_BINDING.equals(note);
     }
 
-
-
     protected boolean isLogoutPostBindingForClient(ClientSessionModel clientSession) {
         ClientModel client = clientSession.getClient();
         String logoutPostUrl = client.getAttribute(SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE);
@@ -207,7 +228,8 @@ public class SamlProtocol implements LoginProtocol {
 
         if (logoutPostUrl == null) {
             // if we don't have a redirect uri either, return true and default to the admin url + POST binding
-            if (logoutRedirectUrl == null) return true;
+            if (logoutRedirectUrl == null)
+                return true;
             return false;
         }
 
@@ -218,11 +240,13 @@ public class SamlProtocol implements LoginProtocol {
         String bindingType = clientSession.getNote(SAML_BINDING);
 
         // if the login binding was POST, return true
-        if (SAML_POST_BINDING.equals(bindingType)) return true;
+        if (SAML_POST_BINDING.equals(bindingType))
+            return true;
 
-        if (logoutRedirectUrl == null) return true; // we don't have a redirect binding url, so use post binding
+        if (logoutRedirectUrl == null)
+            return true; // we don't have a redirect binding url, so use post binding
 
-        return false;  // redirect binding
+        return false; // redirect binding
 
     }
 
@@ -248,7 +272,8 @@ public class SamlProtocol implements LoginProtocol {
                 nameIdFormat = JBossSAMLURIConstants.NAMEID_FORMAT_UNSPECIFIED.get();
             }
         }
-        if(nameIdFormat == null) return SAML_DEFAULT_NAMEID_FORMAT;
+        if (nameIdFormat == null)
+            return SAML_DEFAULT_NAMEID_FORMAT;
         return nameIdFormat;
     }
 
@@ -259,20 +284,21 @@ public class SamlProtocol implements LoginProtocol {
     protected String getNameId(String nameIdFormat, ClientSessionModel clientSession, UserSessionModel userSession) {
         if (nameIdFormat.equals(JBossSAMLURIConstants.NAMEID_FORMAT_EMAIL.get())) {
             return userSession.getUser().getEmail();
-        } else if(nameIdFormat.equals(JBossSAMLURIConstants.NAMEID_FORMAT_TRANSIENT.get())) {
+        } else if (nameIdFormat.equals(JBossSAMLURIConstants.NAMEID_FORMAT_TRANSIENT.get())) {
             // "G-" stands for "generated" Add this for the slight possibility of collisions.
             return "G-" + UUID.randomUUID().toString();
-        } else if(nameIdFormat.equals(JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.get())) {
+        } else if (nameIdFormat.equals(JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.get())) {
             // generate a persistent user id specifically for each client.
             UserModel user = userSession.getUser();
             String name = SAML_PERSISTENT_NAME_ID_FOR + "." + clientSession.getClient().getClientId();
             String samlPersistentId = user.getFirstAttribute(name);
-            if (samlPersistentId != null) return samlPersistentId;
+            if (samlPersistentId != null)
+                return samlPersistentId;
             // "G-" stands for "generated"
             samlPersistentId = "G-" + UUID.randomUUID().toString();
             user.setSingleAttribute(name, samlPersistentId);
             return samlPersistentId;
-        } else if(nameIdFormat.equals(JBossSAMLURIConstants.NAMEID_FORMAT_UNSPECIFIED.get())){
+        } else if (nameIdFormat.equals(JBossSAMLURIConstants.NAMEID_FORMAT_UNSPECIFIED.get())) {
             // TODO: Support for persistent NameID (pseudo-random identifier persisted in user object)
             return userSession.getUser().getUsername();
         } else {
@@ -297,15 +323,8 @@ public class SamlProtocol implements LoginProtocol {
         clientSession.setNote(SAML_NAME_ID_FORMAT, nameIdFormat);
 
         SAML2LoginResponseBuilder builder = new SAML2LoginResponseBuilder();
-        builder.requestID(requestID)
-               .destination(redirectUri)
-               .issuer(responseIssuer)
-               .assertionExpiration(realm.getAccessCodeLifespan())
-               .subjectExpiration(realm.getAccessTokenLifespan())
-               .sessionIndex(clientSession.getId())
-               .requestIssuer(clientSession.getClient().getClientId())
-               .nameIdentifier(nameIdFormat, nameId)
-               .authMethod(JBossSAMLURIConstants.AC_UNSPECIFIED.get());
+        builder.requestID(requestID).destination(redirectUri).issuer(responseIssuer).assertionExpiration(realm.getAccessCodeLifespan()).subjectExpiration(realm.getAccessTokenLifespan()).sessionIndex(clientSession.getId())
+                .requestIssuer(clientSession.getClient().getClientId()).nameIdentifier(nameIdFormat, nameId).authMethod(JBossSAMLURIConstants.AC_UNSPECIFIED.get());
         if (!includeAuthnStatement(client)) {
             builder.disableAuthnStatement(true);
         }
@@ -317,20 +336,20 @@ public class SamlProtocol implements LoginProtocol {
         Set<ProtocolMapperModel> mappings = accessCode.getRequestedProtocolMappers();
         for (ProtocolMapperModel mapping : mappings) {
 
-            ProtocolMapper mapper = (ProtocolMapper)session.getKeycloakSessionFactory().getProviderFactory(ProtocolMapper.class, mapping.getProtocolMapper());
-            if (mapper == null) continue;
+            ProtocolMapper mapper = (ProtocolMapper) session.getKeycloakSessionFactory().getProviderFactory(ProtocolMapper.class, mapping.getProtocolMapper());
+            if (mapper == null)
+                continue;
             if (mapper instanceof SAMLAttributeStatementMapper) {
-                attributeStatementMappers.add(new ProtocolMapperProcessor<SAMLAttributeStatementMapper>((SAMLAttributeStatementMapper)mapper, mapping));
+                attributeStatementMappers.add(new ProtocolMapperProcessor<SAMLAttributeStatementMapper>((SAMLAttributeStatementMapper) mapper, mapping));
             }
             if (mapper instanceof SAMLLoginResponseMapper) {
-                loginResponseMappers.add(new ProtocolMapperProcessor<SAMLLoginResponseMapper>((SAMLLoginResponseMapper)mapper, mapping));
+                loginResponseMappers.add(new ProtocolMapperProcessor<SAMLLoginResponseMapper>((SAMLLoginResponseMapper) mapper, mapping));
             }
             if (mapper instanceof SAMLRoleListMapper) {
-                roleListMapper = new ProtocolMapperProcessor<SAMLRoleListMapper>((SAMLRoleListMapper)mapper, mapping);
+                roleListMapper = new ProtocolMapperProcessor<SAMLRoleListMapper>((SAMLRoleListMapper) mapper, mapping);
             }
         }
 
-
         Document samlDocument = null;
         try {
             ResponseType samlModel = builder.buildModel();
@@ -351,18 +370,14 @@ public class SamlProtocol implements LoginProtocol {
             if (canonicalization != null) {
                 bindingBuilder.canonicalizationMethod(canonicalization);
             }
-            bindingBuilder.signatureAlgorithm(getSignatureAlgorithm(client))
-                   .signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
-                   .signDocument();
+            bindingBuilder.signatureAlgorithm(getSignatureAlgorithm(client)).signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate()).signDocument();
         }
         if (requiresAssertionSignature(client)) {
             String canonicalization = client.getAttribute(SAML_CANONICALIZATION_METHOD_ATTRIBUTE);
             if (canonicalization != null) {
                 bindingBuilder.canonicalizationMethod(canonicalization);
             }
-            bindingBuilder.signatureAlgorithm(getSignatureAlgorithm(client))
-                    .signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
-                    .signAssertions();
+            bindingBuilder.signatureAlgorithm(getSignatureAlgorithm(client)).signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate()).signAssertions();
         }
         if (requiresEncryption(client)) {
             PublicKey publicKey = null;
@@ -402,7 +417,8 @@ public class SamlProtocol implements LoginProtocol {
         String alg = client.getAttribute(SAML_SIGNATURE_ALGORITHM);
         if (alg != null) {
             SignatureAlgorithm algorithm = SignatureAlgorithm.valueOf(alg);
-            if (algorithm != null) return algorithm;
+            if (algorithm != null)
+                return algorithm;
         }
         return SignatureAlgorithm.RSA_SHA256;
     }
@@ -421,10 +437,8 @@ public class SamlProtocol implements LoginProtocol {
         }
     }
 
-    public void transformAttributeStatement(List<ProtocolMapperProcessor<SAMLAttributeStatementMapper>> attributeStatementMappers,
-                                            ResponseType response,
-                                            KeycloakSession session,
-                                            UserSessionModel userSession, ClientSessionModel clientSession) {
+    public void transformAttributeStatement(List<ProtocolMapperProcessor<SAMLAttributeStatementMapper>> attributeStatementMappers, ResponseType response, KeycloakSession session, UserSessionModel userSession,
+            ClientSessionModel clientSession) {
         AssertionType assertion = response.getAssertions().get(0).getAssertion();
         AttributeStatementType attributeStatement = new AttributeStatementType();
 
@@ -432,50 +446,32 @@ public class SamlProtocol implements LoginProtocol {
             processor.mapper.transformAttributeStatement(attributeStatement, processor.model, session, userSession, clientSession);
         }
 
-        //SAML Spec 2.7.3 AttributeStatement must contain one or more Attribute or EncryptedAttribute
-        if(attributeStatement.getAttributes().size() > 0) {
+        // SAML Spec 2.7.3 AttributeStatement must contain one or more Attribute or EncryptedAttribute
+        if (attributeStatement.getAttributes().size() > 0) {
             assertion.addStatement(attributeStatement);
         }
     }
 
-    public ResponseType transformLoginResponse(List<ProtocolMapperProcessor<SAMLLoginResponseMapper>> mappers,
-                                            ResponseType response,
-                                            KeycloakSession session,
-                                            UserSessionModel userSession, ClientSessionModel clientSession) {
+    public ResponseType transformLoginResponse(List<ProtocolMapperProcessor<SAMLLoginResponseMapper>> mappers, ResponseType response, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) {
         for (ProtocolMapperProcessor<SAMLLoginResponseMapper> processor : mappers) {
             response = processor.mapper.transformLoginResponse(response, processor.model, session, userSession, clientSession);
         }
         return response;
     }
 
-    public void populateRoles(ProtocolMapperProcessor<SAMLRoleListMapper> roleListMapper,
-                              ResponseType response,
-                              KeycloakSession session,
-                              UserSessionModel userSession, ClientSessionModel clientSession) {
-        if (roleListMapper == null) return;
+    public void populateRoles(ProtocolMapperProcessor<SAMLRoleListMapper> roleListMapper, ResponseType response, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) {
+        if (roleListMapper == null)
+            return;
         AssertionType assertion = response.getAssertions().get(0).getAssertion();
         AttributeStatementType attributeStatement = new AttributeStatementType();
         roleListMapper.mapper.mapRoles(attributeStatement, roleListMapper.model, session, userSession, clientSession);
 
-        //SAML Spec 2.7.3 AttributeStatement must contain one or more Attribute or EncryptedAttribute
-        if(attributeStatement.getAttributes().size() > 0) {
+        // SAML Spec 2.7.3 AttributeStatement must contain one or more Attribute or EncryptedAttribute
+        if (attributeStatement.getAttributes().size() > 0) {
             assertion.addStatement(attributeStatement);
         }
     }
 
-
-    @Override
-    public Response consentDenied(ClientSessionModel clientSession) {
-        RestartLoginCookie.expireRestartCookie(realm, session.getContext().getConnection(), uriInfo);
-        if ("true".equals(clientSession.getClient().getAttribute(SAML_IDP_INITIATED_LOGIN))) {
-            session.sessions().removeClientSession(realm, clientSession);
-            return ErrorPage.error(session, Messages.CONSENT_DENIED);
-        } else {
-            session.sessions().removeClientSession(realm, clientSession);
-            return getErrorResponse(clientSession, JBossSAMLURIConstants.STATUS_REQUEST_DENIED.get());
-        }
-    }
-
     public static String getLogoutServiceUrl(UriInfo uriInfo, ClientModel client, String bindingType) {
         String logoutServiceUrl = null;
         if (SAML_POST_BINDING.equals(bindingType)) {
@@ -483,8 +479,10 @@ public class SamlProtocol implements LoginProtocol {
         } else {
             logoutServiceUrl = client.getAttribute(SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT_ATTRIBUTE);
         }
-        if (logoutServiceUrl == null && client instanceof ClientModel) logoutServiceUrl = ((ClientModel)client).getManagementUrl();
-        if (logoutServiceUrl == null || logoutServiceUrl.trim().equals("")) return null;
+        if (logoutServiceUrl == null && client instanceof ClientModel)
+            logoutServiceUrl = ((ClientModel) client).getManagementUrl();
+        if (logoutServiceUrl == null || logoutServiceUrl.trim().equals(""))
+            return null;
         return ResourceAdminManager.resolveUri(uriInfo.getRequestUri(), client.getRootUrl(), logoutServiceUrl);
 
     }
@@ -492,7 +490,8 @@ public class SamlProtocol implements LoginProtocol {
     @Override
     public Response frontchannelLogout(UserSessionModel userSession, ClientSessionModel clientSession) {
         ClientModel client = clientSession.getClient();
-        if (!(client instanceof ClientModel)) return null;
+        if (!(client instanceof ClientModel))
+            return null;
         try {
             if (isLogoutPostBindingForClient(clientSession)) {
                 String bindingUri = getLogoutServiceUrl(uriInfo, client, SAML_POST_BINDING);
@@ -541,9 +540,7 @@ public class SamlProtocol implements LoginProtocol {
             if (canonicalization != null) {
                 binding.canonicalizationMethod(canonicalization);
             }
-            binding.signatureAlgorithm(algorithm)
-                    .signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
-                    .signDocument();
+            binding.signatureAlgorithm(algorithm).signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate()).signDocument();
         }
 
         try {
@@ -561,8 +558,6 @@ public class SamlProtocol implements LoginProtocol {
         }
     }
 
-
-
     @Override
     public void backchannelLogout(UserSessionModel userSession, ClientSessionModel clientSession) {
         ClientModel client = clientSession.getClient();
@@ -573,7 +568,6 @@ public class SamlProtocol implements LoginProtocol {
         }
         SAML2LogoutRequestBuilder logoutBuilder = createLogoutRequest(logoutUrl, clientSession, client);
 
-
         String logoutRequestString = null;
         try {
             JaxrsSAML2BindingBuilder binding = createBindingBuilder(client);
@@ -583,20 +577,21 @@ public class SamlProtocol implements LoginProtocol {
             return;
         }
 
-
         HttpClient httpClient = session.getProvider(HttpClientProvider.class).getHttpClient();
         for (int i = 0; i < 2; i++) { // follow redirects once
             try {
                 List<NameValuePair> formparams = new ArrayList<NameValuePair>();
                 formparams.add(new BasicNameValuePair(GeneralConstants.SAML_REQUEST_KEY, logoutRequestString));
-                formparams.add(new BasicNameValuePair("BACK_CHANNEL_LOGOUT", "BACK_CHANNEL_LOGOUT")); // for Picketlink todo remove this
+                formparams.add(new BasicNameValuePair("BACK_CHANNEL_LOGOUT", "BACK_CHANNEL_LOGOUT")); // for Picketlink
+                                                                                                      // todo remove
+                                                                                                      // this
                 UrlEncodedFormEntity form = new UrlEncodedFormEntity(formparams, "UTF-8");
                 HttpPost post = new HttpPost(logoutUrl);
                 post.setEntity(form);
                 HttpResponse response = httpClient.execute(post);
                 try {
                     int status = response.getStatusLine().getStatusCode();
-                    if (status == 302  && !logoutUrl.endsWith("/")) {
+                    if (status == 302 && !logoutUrl.endsWith("/")) {
                         String redirect = response.getFirstHeader(HttpHeaders.LOCATION).getValue();
                         String withSlash = logoutUrl + "/";
                         if (withSlash.equals(redirect)) {
@@ -608,7 +603,8 @@ public class SamlProtocol implements LoginProtocol {
                     HttpEntity entity = response.getEntity();
                     if (entity != null) {
                         InputStream is = entity.getContent();
-                        if (is != null) is.close();
+                        if (is != null)
+                            is.close();
                     }
 
                 }
@@ -622,21 +618,15 @@ public class SamlProtocol implements LoginProtocol {
 
     protected SAML2LogoutRequestBuilder createLogoutRequest(String logoutUrl, ClientSessionModel clientSession, ClientModel client) {
         // build userPrincipal with subject used at login
-        SAML2LogoutRequestBuilder logoutBuilder = new SAML2LogoutRequestBuilder()
-                                         .assertionExpiration(realm.getAccessCodeLifespan())
-                                         .issuer(getResponseIssuer(realm))
-                                         .sessionIndex(clientSession.getId())
-                                         .userPrincipal(clientSession.getNote(SAML_NAME_ID), clientSession.getNote(SAML_NAME_ID_FORMAT))
-                                         .destination(logoutUrl);
+        SAML2LogoutRequestBuilder logoutBuilder = new SAML2LogoutRequestBuilder().assertionExpiration(realm.getAccessCodeLifespan()).issuer(getResponseIssuer(realm)).sessionIndex(clientSession.getId())
+                .userPrincipal(clientSession.getNote(SAML_NAME_ID), clientSession.getNote(SAML_NAME_ID_FORMAT)).destination(logoutUrl);
         return logoutBuilder;
     }
 
     private JaxrsSAML2BindingBuilder createBindingBuilder(ClientModel client) {
         JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder();
         if (requiresRealmSignature(client)) {
-            binding.signatureAlgorithm(getSignatureAlgorithm(client))
-                   .signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
-                   .signDocument();
+            binding.signatureAlgorithm(getSignatureAlgorithm(client)).signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate()).signDocument();
         }
         return binding;
     }
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java
index c370d67..ff1275a 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java
@@ -1,11 +1,23 @@
 package org.keycloak.protocol.saml;
 
+import java.io.InputStream;
+import java.net.URI;
+import java.security.PublicKey;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
 import org.jboss.logging.Logger;
-import org.jboss.resteasy.spi.HttpRequest;
-import org.jboss.resteasy.spi.HttpResponse;
-import org.keycloak.common.ClientConnection;
 import org.keycloak.common.VerificationException;
-import org.keycloak.authentication.AuthenticationProcessor;
+import org.keycloak.common.util.StreamUtil;
 import org.keycloak.dom.saml.v2.SAML2Object;
 import org.keycloak.dom.saml.v2.protocol.AuthnRequestType;
 import org.keycloak.dom.saml.v2.protocol.LogoutRequestType;
@@ -16,15 +28,12 @@ import org.keycloak.events.Details;
 import org.keycloak.events.Errors;
 import org.keycloak.events.EventBuilder;
 import org.keycloak.events.EventType;
-import org.keycloak.models.AuthenticationFlowModel;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.ClientSessionModel;
-import org.keycloak.models.IdentityProviderModel;
-import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.models.utils.KeycloakModelUtils;
-import org.keycloak.protocol.RestartLoginCookie;
+import org.keycloak.protocol.AuthorizationEndpointBase;
 import org.keycloak.protocol.oidc.utils.RedirectUtils;
 import org.keycloak.saml.SAML2LogoutResponseBuilder;
 import org.keycloak.saml.SAMLRequestParser;
@@ -33,33 +42,10 @@ import org.keycloak.saml.common.constants.GeneralConstants;
 import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
 import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
 import org.keycloak.services.ErrorPage;
-import org.keycloak.services.Urls;
 import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.managers.ClientSessionCode;
 import org.keycloak.services.messages.Messages;
-import org.keycloak.services.resources.LoginActionsService;
 import org.keycloak.services.resources.RealmsResource;
-import org.keycloak.common.util.StreamUtil;
-
-import javax.ws.rs.Consumes;
-import javax.ws.rs.FormParam;
-import javax.ws.rs.GET;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.HttpHeaders;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.SecurityContext;
-import javax.ws.rs.core.UriInfo;
-import javax.ws.rs.ext.Providers;
-import java.io.InputStream;
-import java.net.URI;
-import java.security.PublicKey;
-import java.util.List;
 
 /**
  * Resource class for the oauth/openid connect token service
@@ -67,40 +53,12 @@ import java.util.List;
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
-public class SamlService {
+public class SamlService extends AuthorizationEndpointBase {
 
     protected static final Logger logger = Logger.getLogger(SamlService.class);
 
-    protected RealmModel realm;
-    private EventBuilder event;
-    protected AuthenticationManager authManager;
-
-    @Context
-    protected Providers providers;
-    @Context
-    protected SecurityContext securityContext;
-    @Context
-    protected UriInfo uriInfo;
-    @Context
-    protected HttpHeaders headers;
-    @Context
-    protected HttpRequest request;
-    @Context
-    protected HttpResponse response;
-    @Context
-    protected KeycloakSession session;
-    @Context
-    protected ClientConnection clientConnection;
-
-    /*
-    @Context
-    protected ResourceContext resourceContext;
-    */
-
     public SamlService(RealmModel realm, EventBuilder event, AuthenticationManager authManager) {
-        this.realm = realm;
-        this.event = event;
-        this.authManager = authManager;
+        super(realm, event, authManager);
     }
 
     public abstract class BindingProtocol {
@@ -243,7 +201,7 @@ public class SamlService {
                 bindingType = SamlProtocol.SAML_POST_BINDING;
             String redirect = null;
             URI redirectUri = requestAbstractType.getAssertionConsumerServiceURL();
-            if (redirectUri != null && !"null".equals(redirectUri)) {  // "null" is for testing purposes
+            if (redirectUri != null && !"null".equals(redirectUri)) { // "null" is for testing purposes
                 redirect = RedirectUtils.verifyRedirectUri(uriInfo, redirectUri.toString(), realm, client);
             } else {
                 if (bindingType.equals(SamlProtocol.SAML_POST_BINDING)) {
@@ -262,7 +220,6 @@ public class SamlService {
                 return ErrorPage.error(session, Messages.INVALID_REDIRECT_URI);
             }
 
-
             ClientSessionModel clientSession = session.sessions().createClientSession(realm, client);
             clientSession.setAuthMethod(SamlProtocol.LOGIN_PROTOCOL);
             clientSession.setRedirectUri(redirect);
@@ -286,13 +243,9 @@ public class SamlService {
                 }
             }
 
-            return newBrowserAuthentication(clientSession);
+            return newBrowserAuthentication(clientSession, requestAbstractType.isIsPassive());
         }
 
-
-
-
-
         private String getBindingType(AuthnRequestType requestAbstractType) {
             URI requestedProtocolBinding = requestAbstractType.getProtocolBinding();
 
@@ -308,10 +261,8 @@ public class SamlService {
         }
 
         private boolean isSupportedNameIdFormat(String nameIdFormat) {
-            if (nameIdFormat.equals(JBossSAMLURIConstants.NAMEID_FORMAT_EMAIL.get()) ||
-                    nameIdFormat.equals(JBossSAMLURIConstants.NAMEID_FORMAT_TRANSIENT.get()) ||
-                    nameIdFormat.equals(JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.get()) ||
-                    nameIdFormat.equals(JBossSAMLURIConstants.NAMEID_FORMAT_UNSPECIFIED.get())) {
+            if (nameIdFormat.equals(JBossSAMLURIConstants.NAMEID_FORMAT_EMAIL.get()) || nameIdFormat.equals(JBossSAMLURIConstants.NAMEID_FORMAT_TRANSIENT.get()) || nameIdFormat.equals(JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.get())
+                    || nameIdFormat.equals(JBossSAMLURIConstants.NAMEID_FORMAT_UNSPECIFIED.get())) {
                 return true;
             }
             return false;
@@ -340,7 +291,8 @@ public class SamlService {
                     userSession.setNote(SamlProtocol.SAML_LOGOUT_SIGNATURE_ALGORITHM, SamlProtocol.getSignatureAlgorithm(client).toString());
 
                 }
-                if (relayState != null) userSession.setNote(SamlProtocol.SAML_LOGOUT_RELAY_STATE, relayState);
+                if (relayState != null)
+                    userSession.setNote(SamlProtocol.SAML_LOGOUT_RELAY_STATE, relayState);
                 userSession.setNote(SamlProtocol.SAML_LOGOUT_REQUEST_ID, logoutRequest.getID());
                 userSession.setNote(SamlProtocol.SAML_LOGOUT_BINDING, logoutBinding);
                 userSession.setNote(SamlProtocol.SAML_LOGOUT_CANONICALIZATION, client.getAttribute(SamlProtocol.SAML_CANONICALIZATION_METHOD_ATTRIBUTE));
@@ -356,7 +308,8 @@ public class SamlService {
             } else if (logoutRequest.getSessionIndex() != null) {
                 for (String sessionIndex : logoutRequest.getSessionIndex()) {
                     ClientSessionModel clientSession = session.sessions().getClientSession(realm, sessionIndex);
-                    if (clientSession == null) continue;
+                    if (clientSession == null)
+                        continue;
                     UserSessionModel userSession = clientSession.getUserSession();
                     if (clientSession.getClient().getClientId().equals(client.getClientId())) {
                         // remove requesting client from logout
@@ -391,13 +344,10 @@ public class SamlService {
             builder.logoutRequestID(logoutRequest.getID());
             builder.destination(logoutBindingUri);
             builder.issuer(RealmsResource.realmBaseUrl(uriInfo).build(realm.getName()).toString());
-            JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder()
-                    .relayState(logoutRelayState);
+            JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder().relayState(logoutRelayState);
             if (SamlProtocol.requiresRealmSignature(client)) {
                 SignatureAlgorithm algorithm = SamlProtocol.getSignatureAlgorithm(client);
-                binding.signatureAlgorithm(algorithm)
-                        .signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
-                        .signDocument();
+                binding.signatureAlgorithm(algorithm).signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate()).signDocument();
 
             }
             try {
@@ -420,7 +370,6 @@ public class SamlService {
         }
     }
 
-
     protected class PostBindingProtocol extends BindingProtocol {
 
         @Override
@@ -443,12 +392,14 @@ public class SamlService {
             return SamlProtocol.SAML_POST_BINDING;
         }
 
-
         public Response execute(String samlRequest, String samlResponse, String relayState) {
             Response response = basicChecks(samlRequest, samlResponse);
-            if (response != null) return response;
-            if (samlRequest != null) return handleSamlRequest(samlRequest, relayState);
-            else return handleSamlResponse(samlResponse, relayState);
+            if (response != null)
+                return response;
+            if (samlRequest != null)
+                return handleSamlRequest(samlRequest, relayState);
+            else
+                return handleSamlResponse(samlResponse, relayState);
         }
 
     }
@@ -464,7 +415,6 @@ public class SamlService {
             SamlProtocolUtils.verifyRedirectSignature(publicKey, uriInfo, GeneralConstants.SAML_REQUEST_KEY);
         }
 
-
         @Override
         protected SAMLDocumentHolder extractRequestDocument(String samlRequest) {
             return SAMLRequestParser.parseRequestRedirectBinding(samlRequest);
@@ -480,74 +430,35 @@ public class SamlService {
             return SamlProtocol.SAML_REDIRECT_BINDING;
         }
 
-
         public Response execute(String samlRequest, String samlResponse, String relayState) {
             Response response = basicChecks(samlRequest, samlResponse);
-            if (response != null) return response;
-            if (samlRequest != null) return handleSamlRequest(samlRequest, relayState);
-            else return handleSamlResponse(samlResponse, relayState);
+            if (response != null)
+                return response;
+            if (samlRequest != null)
+                return handleSamlRequest(samlRequest, relayState);
+            else
+                return handleSamlResponse(samlResponse, relayState);
         }
 
     }
 
-
-    private Response buildRedirectToIdentityProvider(String providerId, String accessCode) {
-        logger.debug("Automatically redirect to identity provider: " + providerId);
-        return Response.temporaryRedirect(
-                Urls.identityProviderAuthnRequest(uriInfo.getBaseUri(), providerId, realm.getName(), accessCode))
-                .build();
-    }
-
-    protected Response newBrowserAuthentication(ClientSessionModel clientSession) {
-        List<IdentityProviderModel> identityProviders = realm.getIdentityProviders();
-        for (IdentityProviderModel identityProvider : identityProviders) {
-            if (identityProvider.isAuthenticateByDefault()) {
-                return buildRedirectToIdentityProvider(identityProvider.getAlias(), new ClientSessionCode(realm, clientSession).getCode() );
-            }
-        }
-        AuthenticationFlowModel flow = realm.getBrowserFlow();
-        String flowId = flow.getId();
-        AuthenticationProcessor processor = new AuthenticationProcessor();
-        processor.setClientSession(clientSession)
-                .setFlowPath(LoginActionsService.AUTHENTICATE_PATH)
-                .setFlowId(flowId)
-                .setBrowserFlow(true)
-                .setConnection(clientConnection)
-                .setEventBuilder(event)
-                .setProtector(authManager.getProtector())
-                .setRealm(realm)
-                .setSession(session)
-                .setUriInfo(uriInfo)
-                .setRequest(request);
-
-        try {
-            RestartLoginCookie.setRestartCookie(realm, clientConnection, uriInfo, clientSession);
-            return processor.authenticate();
-        } catch (Exception e) {
-            return processor.handleBrowserException(e);
-        }
+    protected Response newBrowserAuthentication(ClientSessionModel clientSession, boolean isPassive) {
+        return handleBrowserAuthenticationRequest(clientSession, new SamlProtocol().setEventBuilder(event).setHttpHeaders(headers).setRealm(realm).setSession(session).setUriInfo(uriInfo), isPassive);
     }
 
-
-
     /**
      */
     @GET
-    public Response redirectBinding(@QueryParam(GeneralConstants.SAML_REQUEST_KEY) String samlRequest,
-                                    @QueryParam(GeneralConstants.SAML_RESPONSE_KEY) String samlResponse,
-                                    @QueryParam(GeneralConstants.RELAY_STATE) String relayState) {
+    public Response redirectBinding(@QueryParam(GeneralConstants.SAML_REQUEST_KEY) String samlRequest, @QueryParam(GeneralConstants.SAML_RESPONSE_KEY) String samlResponse, @QueryParam(GeneralConstants.RELAY_STATE) String relayState) {
         logger.debug("SAML GET");
         return new RedirectBindingProtocol().execute(samlRequest, samlResponse, relayState);
     }
 
-
     /**
      */
     @POST
     @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
-    public Response postBinding(@FormParam(GeneralConstants.SAML_REQUEST_KEY) String samlRequest,
-                                @FormParam(GeneralConstants.SAML_RESPONSE_KEY) String samlResponse,
-                                @FormParam(GeneralConstants.RELAY_STATE) String relayState) {
+    public Response postBinding(@FormParam(GeneralConstants.SAML_REQUEST_KEY) String samlRequest, @FormParam(GeneralConstants.SAML_RESPONSE_KEY) String samlResponse, @FormParam(GeneralConstants.RELAY_STATE) String relayState) {
         logger.debug("SAML POST");
         return new PostBindingProtocol().execute(samlRequest, samlResponse, relayState);
     }
@@ -570,13 +481,13 @@ public class SamlService {
     @GET
     @Path("clients/{client}")
     @Produces(MediaType.TEXT_HTML)
-    public Response idpInitiatedSSO(@PathParam("client") String clientUrlName,
-                                    @QueryParam("RelayState") String relayState) {
+    public Response idpInitiatedSSO(@PathParam("client") String clientUrlName, @QueryParam("RelayState") String relayState) {
         event.event(EventType.LOGIN);
         ClientModel client = null;
         for (ClientModel c : realm.getClients()) {
             String urlName = c.getAttribute(SamlProtocol.SAML_IDP_INITIATED_SSO_URL_NAME);
-            if (urlName == null) continue;
+            if (urlName == null)
+                continue;
             if (urlName.equals(clientUrlName)) {
                 client = c;
                 break;
@@ -586,18 +497,14 @@ public class SamlService {
             event.error(Errors.CLIENT_NOT_FOUND);
             return ErrorPage.error(session, Messages.CLIENT_NOT_FOUND);
         }
-        if (client.getManagementUrl() == null
-                && client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE) == null
-                && client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_REDIRECT_ATTRIBUTE) == null) {
+        if (client.getManagementUrl() == null && client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE) == null && client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_REDIRECT_ATTRIBUTE) == null) {
             logger.error("SAML assertion consumer url not set up");
             event.error(Errors.INVALID_REDIRECT_URI);
             return ErrorPage.error(session, Messages.INVALID_REDIRECT_URI);
         }
 
         String bindingType = SamlProtocol.SAML_POST_BINDING;
-        if (client.getManagementUrl() == null
-                && client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE) == null
-                && client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_REDIRECT_ATTRIBUTE) != null) {
+        if (client.getManagementUrl() == null && client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE) == null && client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_REDIRECT_ATTRIBUTE) != null) {
             bindingType = SamlProtocol.SAML_REDIRECT_BINDING;
         }
 
@@ -626,8 +533,7 @@ public class SamlService {
             clientSession.setNote(GeneralConstants.RELAY_STATE, relayState);
         }
 
-
-        return newBrowserAuthentication(clientSession);
+        return newBrowserAuthentication(clientSession, false);
 
     }
 
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
index 951bb94..dafaf1a 100755
--- a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
@@ -21,6 +21,7 @@ import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.models.utils.FormMessage;
 import org.keycloak.protocol.LoginProtocol;
+import org.keycloak.protocol.LoginProtocol.Error;
 import org.keycloak.protocol.oidc.TokenManager;
 import org.keycloak.services.ErrorPage;
 import org.keycloak.services.managers.AuthenticationManager;
@@ -470,7 +471,7 @@ public class AuthenticationProcessor {
             protocol.setRealm(getRealm())
                     .setHttpHeaders(getHttpRequest().getHttpHeaders())
                     .setUriInfo(getUriInfo());
-            Response response = protocol.cancelLogin(getClientSession());
+            Response response = protocol.sendError(getClientSession(), Error.CANCELLED_BY_USER);
             forceChallenge(response);
         }
 
diff --git a/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java b/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java
new file mode 100644
index 0000000..a1fc4a7
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java
@@ -0,0 +1,137 @@
+package org.keycloak.protocol;
+
+import java.util.List;
+
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import org.jboss.logging.Logger;
+import org.jboss.resteasy.spi.HttpRequest;
+import org.keycloak.authentication.AuthenticationProcessor;
+import org.keycloak.common.ClientConnection;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.models.AuthenticationFlowModel;
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.protocol.LoginProtocol.Error;
+import org.keycloak.services.Urls;
+import org.keycloak.services.managers.AuthenticationManager;
+import org.keycloak.services.managers.ClientSessionCode;
+import org.keycloak.services.resources.LoginActionsService;
+
+/**
+ * Common base class for Authorization REST endpoints implementation, which have to be implemented by each protocol.
+ *
+ * @author Vlastimil Elias (velias at redhat dot com)
+ */
+public abstract class AuthorizationEndpointBase {
+
+    private static final Logger logger = Logger.getLogger(AuthorizationEndpointBase.class);
+
+    protected RealmModel realm;
+    protected EventBuilder event;
+    protected AuthenticationManager authManager;
+
+    @Context
+    protected UriInfo uriInfo;
+    @Context
+    protected HttpHeaders headers;
+    @Context
+    protected HttpRequest request;
+    @Context
+    protected KeycloakSession session;
+    @Context
+    protected ClientConnection clientConnection;
+
+    public AuthorizationEndpointBase(RealmModel realm, EventBuilder event, AuthenticationManager authManager) {
+        this.realm = realm;
+        this.event = event;
+        this.authManager = authManager;
+    }
+
+    protected AuthenticationProcessor createProcessor(ClientSessionModel clientSession, String flowId, String flowPath) {
+        AuthenticationProcessor processor = new AuthenticationProcessor();
+        processor.setClientSession(clientSession)
+                .setFlowPath(flowPath)
+                .setFlowId(flowId)
+                .setBrowserFlow(true)
+                .setConnection(clientConnection)
+                .setEventBuilder(event)
+                .setProtector(authManager.getProtector())
+                .setRealm(realm)
+                .setSession(session)
+                .setUriInfo(uriInfo)
+                .setRequest(request);
+        return processor;
+    }
+
+    /**
+     * Common method to handle browser authentication request in protocols unified way.
+     * 
+     * @param clientSession for current request
+     * @param protocol handler for protocol used to initiate login
+     * @param isPassive set to true if login should be passive (without login screen shown)
+     * @return response to be returned to the browser
+     */
+    protected Response handleBrowserAuthenticationRequest(ClientSessionModel clientSession, LoginProtocol protocol, boolean isPassive) {
+
+        List<IdentityProviderModel> identityProviders = realm.getIdentityProviders();
+        for (IdentityProviderModel identityProvider : identityProviders) {
+            if (identityProvider.isAuthenticateByDefault()) {
+                // TODO if we are isPassive we should propagate this flag to default identity provider also if possible
+                return buildRedirectToIdentityProvider(identityProvider.getAlias(), new ClientSessionCode(realm, clientSession).getCode());
+            }
+        }
+
+        AuthenticationFlowModel flow = realm.getBrowserFlow();
+        String flowId = flow.getId();
+        AuthenticationProcessor processor = createProcessor(clientSession, flowId, LoginActionsService.AUTHENTICATE_PATH);
+
+        if (isPassive) {
+            // OIDC prompt == NONE or SAML 2 IsPassive flag
+            // This means that client is just checking if the user is already completely logged in.
+            // We cancel login if any authentication action or required action is required
+            Response challenge = null;
+            Response challenge2 = null;
+            try {
+                challenge = processor.authenticateOnly();
+                if (challenge == null) {
+                    challenge2 = processor.attachSessionExecutionRequiredActions();
+                }
+            } catch (Exception e) {
+                return processor.handleBrowserException(e);
+            }
+
+            if (challenge != null || challenge2 != null) {
+                if (processor.isUserSessionCreated()) {
+                    session.sessions().removeUserSession(realm, processor.getUserSession());
+                }
+                if (challenge != null)
+                    return protocol.sendError(clientSession, Error.PASSIVE_LOGIN_REQUIRED);
+                else
+                    return protocol.sendError(clientSession, Error.PASSIVE_INTERACTION_REQUIRED);
+            } else {
+                return processor.finishAuthentication();
+            }
+        } else {
+            try {
+                RestartLoginCookie.setRestartCookie(realm, clientConnection, uriInfo, clientSession);
+                return processor.authenticate();
+            } catch (Exception e) {
+                return processor.handleBrowserException(e);
+            }
+        }
+    }
+
+    protected Response buildRedirectToIdentityProvider(String providerId, String accessCode) {
+        logger.debug("Automatically redirect to identity provider: " + providerId);
+        return Response.temporaryRedirect(
+                Urls.identityProviderAuthnRequest(this.uriInfo.getBaseUri(), providerId, this.realm.getName(), accessCode))
+                .build();
+    }
+
+}
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/protocol/LoginProtocol.java b/services/src/main/java/org/keycloak/protocol/LoginProtocol.java
index 4a7836d..5cc4503 100755
--- a/services/src/main/java/org/keycloak/protocol/LoginProtocol.java
+++ b/services/src/main/java/org/keycloak/protocol/LoginProtocol.java
@@ -17,6 +17,28 @@ import javax.ws.rs.core.UriInfo;
  * @version $Revision: 1 $
  */
 public interface LoginProtocol extends Provider {
+
+    public static enum Error {
+
+        /**
+         * Login cancelled by the user
+         */
+        CANCELLED_BY_USER,
+        /**
+         * Consent denied by the user
+         */
+        CONSENT_DENIED,
+        /**
+         * Passive authentication mode requested but nobody is logged in
+         */
+        PASSIVE_LOGIN_REQUIRED,
+        /**
+         * Passive authentication mode requested, user is logged in, but some other user interaction is necessary (eg. some required login actions exist or Consent approval is necessary for logged in
+         * user)
+         */
+        PASSIVE_INTERACTION_REQUIRED;
+    }
+
     LoginProtocol setSession(KeycloakSession session);
 
     LoginProtocol setRealm(RealmModel realm);
@@ -27,11 +49,12 @@ public interface LoginProtocol extends Provider {
 
     LoginProtocol setEventBuilder(EventBuilder event);
 
-    Response cancelLogin(ClientSessionModel clientSession);
     Response authenticated(UserSessionModel userSession, ClientSessionCode accessCode);
-    Response consentDenied(ClientSessionModel clientSession);
+
+    Response sendError(ClientSessionModel clientSession, Error error);
 
     void backchannelLogout(UserSessionModel userSession, ClientSessionModel clientSession);
     Response frontchannelLogout(UserSessionModel userSession, ClientSessionModel clientSession);
     Response finishLogout(UserSessionModel userSession);
+
 }
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 e013857..c8d5656 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
@@ -1,10 +1,19 @@
 package org.keycloak.protocol.oidc.endpoints;
 
+import java.util.List;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
 import org.jboss.logging.Logger;
 import org.jboss.resteasy.spi.HttpRequest;
-import org.keycloak.common.ClientConnection;
 import org.keycloak.OAuth2Constants;
 import org.keycloak.authentication.AuthenticationProcessor;
+import org.keycloak.common.ClientConnection;
 import org.keycloak.constants.AdapterConstants;
 import org.keycloak.events.Details;
 import org.keycloak.events.Errors;
@@ -18,6 +27,7 @@ import org.keycloak.models.IdentityProviderModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.protocol.AuthorizationEndpointBase;
 import org.keycloak.protocol.RestartLoginCookie;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
 import org.keycloak.protocol.oidc.utils.RedirectUtils;
@@ -28,45 +38,19 @@ import org.keycloak.services.managers.ClientSessionCode;
 import org.keycloak.services.messages.Messages;
 import org.keycloak.services.resources.LoginActionsService;
 
-import javax.ws.rs.GET;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.HttpHeaders;
-import javax.ws.rs.core.MultivaluedMap;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.UriInfo;
-import java.util.List;
-
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
-public class AuthorizationEndpoint {
+public class AuthorizationEndpoint extends AuthorizationEndpointBase {
 
     private static final Logger logger = Logger.getLogger(AuthorizationEndpoint.class);
+
     public static final String CODE_AUTH_TYPE = "code";
 
     private enum Action {
         REGISTER, CODE, FORGOT_CREDENTIALS
     }
 
-    @Context
-    private KeycloakSession session;
-
-    @Context
-    private HttpRequest request;
-
-    @Context
-    private HttpHeaders headers;
-
-    @Context
-    private UriInfo uriInfo;
-
-    @Context
-    private ClientConnection clientConnection;
-
-    private final AuthenticationManager authManager;
-    private final RealmModel realm;
-    private final EventBuilder event;
-
     private ClientModel client;
     private ClientSessionModel clientSession;
 
@@ -86,9 +70,7 @@ public class AuthorizationEndpoint {
     private String legacyResponseType;
 
     public AuthorizationEndpoint(AuthenticationManager authManager, RealmModel realm, EventBuilder event) {
-        this.authManager = authManager;
-        this.realm = realm;
-        this.event = event;
+        super(realm, event, authManager);
         event.event(EventType.LOGIN);
     }
 
@@ -249,7 +231,6 @@ public class AuthorizationEndpoint {
     }
 
     private Response buildAuthorizationCodeAuthorizationResponse() {
-        String accessCode = new ClientSessionCode(realm, clientSession).getCode();
 
         if (idpHint != null && !"".equals(idpHint)) {
             IdentityProviderModel identityProviderModel = realm.getIdentityProviderByAlias(idpHint);
@@ -259,65 +240,13 @@ public class AuthorizationEndpoint {
                         .setError(Messages.IDENTITY_PROVIDER_NOT_FOUND, idpHint)
                         .createErrorPage();
             }
-            return buildRedirectToIdentityProvider(idpHint, accessCode);
+            return buildRedirectToIdentityProvider(idpHint, new ClientSessionCode(realm, clientSession).getCode());
         }
 
-        return browserAuthentication(accessCode);
-    }
-
-    protected Response browserAuthentication(String accessCode) {
         this.event.event(EventType.LOGIN);
-        List<IdentityProviderModel> identityProviders = realm.getIdentityProviders();
-        for (IdentityProviderModel identityProvider : identityProviders) {
-            if (identityProvider.isAuthenticateByDefault()) {
-                return buildRedirectToIdentityProvider(identityProvider.getAlias(), accessCode);
-            }
-        }
         clientSession.setNote(Details.AUTH_TYPE, CODE_AUTH_TYPE);
 
-
-        AuthenticationFlowModel flow = realm.getBrowserFlow();
-        String flowId = flow.getId();
-        AuthenticationProcessor processor = createProcessor(flowId, LoginActionsService.AUTHENTICATE_PATH);
-
-        if (prompt != null && prompt.equals("none")) {
-            // OIDC prompt == NONE
-            // This means that client is just checking if the user is already completely logged in.
-            //
-            // here we cancel login if any authentication action or required action is required
-            Response challenge = null;
-            try {
-                challenge = processor.authenticateOnly();
-                if (challenge == null) {
-                    challenge = processor.attachSessionExecutionRequiredActions();
-                }
-            } catch (Exception e) {
-                return processor.handleBrowserException(e);
-            }
-
-            if (challenge != null) {
-                if (processor.isUserSessionCreated()) {
-                    session.sessions().removeUserSession(realm, processor.getUserSession());
-                }
-                OIDCLoginProtocol oauth = new OIDCLoginProtocol(session, realm, uriInfo, headers, event);
-                return oauth.cancelLogin(clientSession);
-            }
-
-            if (challenge == null) {
-                return processor.finishAuthentication();
-            } else {
-                RestartLoginCookie.setRestartCookie(realm, clientConnection, uriInfo, clientSession);
-                return challenge;
-            }
-        } else {
-            try {
-                RestartLoginCookie.setRestartCookie(realm, clientConnection, uriInfo, clientSession);
-                return processor.authenticate();
-            } catch (Exception e) {
-                return processor.handleBrowserException(e);
-            }
-
-        }
+        return handleBrowserAuthenticationRequest(clientSession, new OIDCLoginProtocol(session, realm, uriInfo, headers, event), prompt != null && prompt.equals("none"));
     }
 
     private Response buildRegister() {
@@ -326,7 +255,7 @@ public class AuthorizationEndpoint {
         AuthenticationFlowModel flow = realm.getRegistrationFlow();
         String flowId = flow.getId();
 
-        AuthenticationProcessor processor = createProcessor(flowId, LoginActionsService.REGISTRATION_PATH);
+        AuthenticationProcessor processor = createProcessor(clientSession, flowId, LoginActionsService.REGISTRATION_PATH);
 
         return processor.authenticate();
     }
@@ -337,32 +266,12 @@ public class AuthorizationEndpoint {
         AuthenticationFlowModel flow = realm.getResetCredentialsFlow();
         String flowId = flow.getId();
 
-        AuthenticationProcessor processor = createProcessor(flowId, LoginActionsService.RESET_CREDENTIALS_PATH);
+        AuthenticationProcessor processor = createProcessor(clientSession, flowId, LoginActionsService.RESET_CREDENTIALS_PATH);
 
         return processor.authenticate();
     }
 
-    private AuthenticationProcessor createProcessor(String flowId, String flowPath) {
-        AuthenticationProcessor processor = new AuthenticationProcessor();
-        processor.setClientSession(clientSession)
-                .setFlowPath(flowPath)
-                .setFlowId(flowId)
-                .setBrowserFlow(true)
-                .setConnection(clientConnection)
-                .setEventBuilder(event)
-                .setProtector(authManager.getProtector())
-                .setRealm(realm)
-                .setSession(session)
-                .setUriInfo(uriInfo)
-                .setRequest(request);
-        return processor;
-    }
 
-    private Response buildRedirectToIdentityProvider(String providerId, String accessCode) {
-        logger.debug("Automatically redirect to identity provider: " + providerId);
-        return Response.temporaryRedirect(
-                Urls.identityProviderAuthnRequest(this.uriInfo.getBaseUri(), providerId, this.realm.getName(), accessCode))
-                .build();
-    }
+
 
 }
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocol.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocol.java
index 332a547..b9d55db 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocol.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocol.java
@@ -82,7 +82,7 @@ public class OIDCLoginProtocol implements LoginProtocol {
         this.event = event;
     }
 
-    public OIDCLoginProtocol(){
+    public OIDCLoginProtocol() {
 
     }
 
@@ -105,7 +105,7 @@ public class OIDCLoginProtocol implements LoginProtocol {
     }
 
     @Override
-    public OIDCLoginProtocol setHttpHeaders(HttpHeaders headers){
+    public OIDCLoginProtocol setHttpHeaders(HttpHeaders headers) {
         this.headers = headers;
         return this;
     }
@@ -117,19 +117,6 @@ public class OIDCLoginProtocol implements LoginProtocol {
     }
 
     @Override
-    public Response cancelLogin(ClientSessionModel clientSession) {
-        String redirect = clientSession.getRedirectUri();
-        String state = clientSession.getNote(OIDCLoginProtocol.STATE_PARAM);
-        UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam(OAuth2Constants.ERROR, "access_denied");
-        if (state != null) {
-            redirectUri.queryParam(OAuth2Constants.STATE, state);
-        }
-        session.sessions().removeClientSession(realm, clientSession);
-        RestartLoginCookie.expireRestartCookie(realm, session.getContext().getConnection(), uriInfo);
-        return Response.status(302).location(redirectUri.build()).build();
-    }
-
-    @Override
     public Response authenticated(UserSessionModel userSession, ClientSessionCode accessCode) {
         ClientSessionModel clientSession = accessCode.getClientSession();
         String redirect = clientSession.getRedirectUri();
@@ -144,10 +131,11 @@ public class OIDCLoginProtocol implements LoginProtocol {
         return location.build();
     }
 
-    public Response consentDenied(ClientSessionModel clientSession) {
+    @Override
+    public Response sendError(ClientSessionModel clientSession, Error error) {
         String redirect = clientSession.getRedirectUri();
         String state = clientSession.getNote(OIDCLoginProtocol.STATE_PARAM);
-        UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam(OAuth2Constants.ERROR, "access_denied");
+        UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam(OAuth2Constants.ERROR, translateError(error));
         if (state != null)
             redirectUri.queryParam(OAuth2Constants.STATE, state);
         session.sessions().removeClientSession(realm, clientSession);
@@ -156,20 +144,25 @@ public class OIDCLoginProtocol implements LoginProtocol {
         return location.build();
     }
 
-
-    public Response invalidSessionError(ClientSessionModel clientSession) {
-        String redirect = clientSession.getRedirectUri();
-        String state = clientSession.getNote(OIDCLoginProtocol.STATE_PARAM);
-        UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam(OAuth2Constants.ERROR, "access_denied");
-        if (state != null) {
-            redirectUri.queryParam(OAuth2Constants.STATE, state);
+    private String translateError(Error error) {
+        switch (error) {
+            case CANCELLED_BY_USER:
+            case CONSENT_DENIED:
+                return "access_denied";
+            case PASSIVE_INTERACTION_REQUIRED:
+                return "interaction_required";
+            case PASSIVE_LOGIN_REQUIRED:
+                return "login_required";
+            default:
+                log.warn("Untranslated protocol Error: " + error.name() + " so we return default SAML error");
+                return "access_denied";
         }
-        return Response.status(302).location(redirectUri.build()).build();
     }
 
     @Override
     public void backchannelLogout(UserSessionModel userSession, ClientSessionModel clientSession) {
-        if (!(clientSession.getClient() instanceof ClientModel)) return;
+        if (!(clientSession.getClient() instanceof ClientModel))
+            return;
         ClientModel app = clientSession.getClient();
         new ResourceAdminManager(session).logoutClientSession(uriInfo.getRequestUri(), realm, app, clientSession);
     }
@@ -190,10 +183,10 @@ public class OIDCLoginProtocol implements LoginProtocol {
         }
         event.user(userSession.getUser()).session(userSession).success();
 
-
         if (redirectUri != null) {
             UriBuilder uriBuilder = UriBuilder.fromUri(redirectUri);
-            if (state != null) uriBuilder.queryParam(STATE_PARAM, state);
+            if (state != null)
+                uriBuilder.queryParam(STATE_PARAM, state);
             return Response.status(302).location(uriBuilder.build()).build();
         } else {
             return Response.ok().build();
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 ea47ddc..c820e4d 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -36,6 +36,7 @@ import org.keycloak.login.LoginFormsProvider;
 import org.keycloak.models.*;
 import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.protocol.LoginProtocol;
+import org.keycloak.protocol.LoginProtocol.Error;
 import org.keycloak.protocol.RestartLoginCookie;
 import org.keycloak.protocol.oidc.TokenManager;
 import org.keycloak.representations.AccessToken;
@@ -523,7 +524,7 @@ public class AuthenticationManager {
                         .setHttpHeaders(context.getHttpRequest().getHttpHeaders())
                         .setUriInfo(context.getUriInfo());
                 event.error(Errors.REJECTED_BY_USER);
-                return protocol.consentDenied(context.getClientSession());
+                return protocol.sendError(context.getClientSession(), Error.CONSENT_DENIED);
             }
             else if (context.getStatus() == RequiredActionContext.Status.CHALLENGE) {
                 clientSession.setNote(CURRENT_REQUIRED_ACTION, model.getProviderId());
diff --git a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
index 7f15d2d..7927991 100755
--- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
@@ -57,6 +57,7 @@ import org.keycloak.models.utils.FormMessage;
 import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.protocol.LoginProtocol;
 import org.keycloak.protocol.RestartLoginCookie;
+import org.keycloak.protocol.LoginProtocol.Error;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
 import org.keycloak.services.ErrorPage;
 import org.keycloak.services.Urls;
@@ -591,7 +592,7 @@ public class LoginActionsService {
                     .setHttpHeaders(headers)
                     .setUriInfo(uriInfo);
             event.error(Errors.REJECTED_BY_USER);
-            return protocol.consentDenied(clientSession);
+            return protocol.sendError(clientSession, Error.CONSENT_DENIED);
         }
 
         UserConsentModel grantedConsent = user.getConsentByClient(client.getId());
@@ -828,7 +829,7 @@ public class LoginActionsService {
                     .setHttpHeaders(context.getHttpRequest().getHttpHeaders())
                     .setUriInfo(context.getUriInfo());
             event.detail(Details.CUSTOM_REQUIRED_ACTION, action).error(Errors.REJECTED_BY_USER);
-            return protocol.consentDenied(context.getClientSession());
+            return protocol.sendError(context.getClientSession(), Error.CONSENT_DENIED);
         }
 
         throw new RuntimeException("Unreachable");
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlAdapterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlAdapterTest.java
index 0d20f7a..9f23b70 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlAdapterTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlAdapterTest.java
@@ -19,6 +19,7 @@ public class SamlAdapterTest {
              ClassLoader classLoader = SamlAdapterTest.class.getClassLoader();
 
             initializeSamlSecuredWar("/keycloak-saml/simple-post", "/sales-post",  "post.war", classLoader);
+            initializeSamlSecuredWar("/keycloak-saml/simple-post-passive", "/sales-post-passive", "post-passive.war", classLoader);
             initializeSamlSecuredWar("/keycloak-saml/signed-post", "/sales-post-sig",  "post-sig.war", classLoader);
             initializeSamlSecuredWar("/keycloak-saml/signed-post-email", "/sales-post-sig-email",  "post-sig-email.war", classLoader);
             initializeSamlSecuredWar("/keycloak-saml/signed-post-transient", "/sales-post-sig-transient",  "post-sig-transient.war", classLoader);
@@ -97,6 +98,11 @@ public class SamlAdapterTest {
     }
 
     @Test
+    public void testPostPassiveLoginLogout() {
+        testStrategy.testPostPassiveLoginLogout(true);
+    }
+
+    @Test
     public void testPostSignedLoginLogoutTransientNameID() {
         testStrategy.testPostSignedLoginLogoutTransientNameID();
     }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlAdapterTestStrategy.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlAdapterTestStrategy.java
index b63c960..f2d8b68 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlAdapterTestStrategy.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlAdapterTestStrategy.java
@@ -139,6 +139,38 @@ public class SamlAdapterTestStrategy  extends ExternalResource {
         checkLoggedOut(APP_SERVER_BASE_URL + "/sales-post/");
     }
 
+    public void testPostPassiveLoginLogout(boolean forbiddenIfNotauthenticated) {
+        // first request on passive app - no login page shown, user not logged in as we are in passive mode.
+        // Shown page depends on used authentication mechanism, some may return forbidden error, some return requested page with anonymous user (not logged in)
+        driver.navigate().to(APP_SERVER_BASE_URL + "/sales-post-passive/");
+        assertEquals(APP_SERVER_BASE_URL + "/sales-post-passive/", driver.getCurrentUrl());
+        System.out.println(driver.getPageSource());
+        if (forbiddenIfNotauthenticated) {
+            Assert.assertTrue(driver.getPageSource().contains("HTTP status code: 403"));
+        } else {
+            Assert.assertTrue(driver.getPageSource().contains("principal=null"));
+        }
+
+        // login user by asking login from other app
+        driver.navigate().to(APP_SERVER_BASE_URL + "/sales-post/");
+        loginPage.login("bburke", "password");
+
+        // navigate to the passive app again, we have to be logged in now
+        driver.navigate().to(APP_SERVER_BASE_URL + "/sales-post-passive/");
+        assertEquals(APP_SERVER_BASE_URL + "/sales-post-passive/", driver.getCurrentUrl());
+        System.out.println(driver.getPageSource());
+        Assert.assertTrue(driver.getPageSource().contains("bburke"));
+
+        // logout from both app
+        driver.navigate().to(APP_SERVER_BASE_URL + "/sales-post-passive?GLO=true");
+        driver.navigate().to(APP_SERVER_BASE_URL + "/sales-post?GLO=true");
+
+        // refresh passive app page, not logged in again as we are in passive mode
+        driver.navigate().to(APP_SERVER_BASE_URL + "/sales-post-passive/");
+        assertEquals(APP_SERVER_BASE_URL + "/sales-post-passive/", driver.getCurrentUrl());
+        Assert.assertFalse(driver.getPageSource().contains("bburke"));
+    }
+
     public void testPostSimpleUnauthorized(CheckAuthError error) {
         driver.navigate().to(APP_SERVER_BASE_URL + "/sales-post/");
         assertEquals(driver.getCurrentUrl(), AUTH_SERVER_URL + "/realms/demo/protocol/saml");
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/ErrorServlet.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/ErrorServlet.java
index 68410d4..0cffa3c 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/ErrorServlet.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/ErrorServlet.java
@@ -6,7 +6,6 @@ import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
 import java.io.IOException;
 import java.io.PrintWriter;
 
@@ -20,10 +19,17 @@ public class ErrorServlet extends HttpServlet {
     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         authError = (AuthenticationError)req.getAttribute(AuthenticationError.class.getName());
 
+        Integer statusCode = (Integer) req.getAttribute("javax.servlet.error.status_code");
+
         resp.setContentType("text/html");
         PrintWriter pw = resp.getWriter();
         pw.printf("<html><head><title>%s</title></head><body>", "Error Page");
-        pw.print("<h1>There was an error</h1></body></html>");
+        pw.print("<h1>There was an error</h1>");
+        if (statusCode != null)
+            pw.print("<br/>HTTP status code: " + statusCode);
+        if (authError != null)
+            pw.print("<br/>Error info: " + authError.toString());
+        pw.print("</body></html>");
         pw.flush();
 
 
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlBindingTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlBindingTest.java
index 019bfea..7878843 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlBindingTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlBindingTest.java
@@ -1,35 +1,24 @@
 package org.keycloak.testsuite.saml;
 
 import org.apache.commons.io.IOUtils;
-import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput;
 import org.junit.Assert;
 import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
-import org.keycloak.Config;
 import org.keycloak.admin.client.Keycloak;
 import org.keycloak.admin.client.resource.RealmResource;
 import org.keycloak.models.ClientModel;
-import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.Constants;
-import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.ProtocolMapperModel;
 import org.keycloak.models.RealmModel;
-import org.keycloak.models.UserModel;
-import org.keycloak.models.UserSessionModel;
-import org.keycloak.protocol.oidc.OIDCLoginProtocol;
-import org.keycloak.protocol.oidc.TokenManager;
 import org.keycloak.protocol.saml.mappers.AttributeStatementHelper;
 import org.keycloak.protocol.saml.mappers.HardcodedAttributeMapper;
 import org.keycloak.protocol.saml.mappers.HardcodedRole;
 import org.keycloak.protocol.saml.mappers.RoleListMapper;
 import org.keycloak.protocol.saml.mappers.RoleNameMapper;
-import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.services.managers.RealmManager;
-import org.keycloak.services.resources.admin.AdminRoot;
 import org.keycloak.testsuite.pages.LoginPage;
-import org.keycloak.testsuite.rule.AbstractKeycloakRule;
 import org.keycloak.testsuite.rule.KeycloakRule;
 import org.keycloak.testsuite.rule.WebResource;
 import org.keycloak.testsuite.rule.WebRule;
@@ -47,19 +36,10 @@ import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import javax.ws.rs.client.Client;
-import javax.ws.rs.client.ClientBuilder;
-import javax.ws.rs.client.ClientRequestContext;
-import javax.ws.rs.client.ClientRequestFilter;
-import javax.ws.rs.client.Entity;
-import javax.ws.rs.client.WebTarget;
-import javax.ws.rs.core.HttpHeaders;
-import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
-import java.io.InputStream;
 
 import static org.junit.Assert.assertEquals;
 
@@ -166,6 +146,7 @@ public class SamlBindingTest {
         driver.navigate().to("http://localhost:8081/sales-post?GLO=true");
         checkLoggedOut("http://localhost:8081/sales-post/");
     }
+
     @Test
     public void testPostSimpleLoginLogoutIdpInitiated() {
         driver.navigate().to("http://localhost:8081/auth/realms/demo/protocol/saml/clients/sales-post");
@@ -188,6 +169,7 @@ public class SamlBindingTest {
         checkLoggedOut("http://localhost:8081/sales-post-sig/");
 
     }
+
     @Test
     public void testPostSignedLoginLogoutTransientNameID() {
         driver.navigate().to("http://localhost:8081/sales-post-sig-transient/");
@@ -452,23 +434,10 @@ public class SamlBindingTest {
         Assert.assertTrue(driver.getPageSource().contains("null"));
     }
 
-    private static String createToken() {
-        KeycloakSession session = keycloakRule.startSession();
-        try {
-            RealmManager manager = new RealmManager(session);
-
-            RealmModel adminRealm = manager.getRealm(Config.getAdminRealm());
-            ClientModel adminConsole = adminRealm.getClientByClientId(Constants.ADMIN_CONSOLE_CLIENT_ID);
-            TokenManager tm = new TokenManager();
-            UserModel admin = session.users().getUserByUsername("admin", adminRealm);
-            ClientSessionModel clientSession = session.sessions().createClientSession(adminRealm, adminConsole);
-            clientSession.setNote(OIDCLoginProtocol.ISSUER, "http://localhost:8081/auth/realms/master");
-            UserSessionModel userSession = session.sessions().createUserSession(adminRealm, admin, "admin", null, "form", false, null, null);
-            AccessToken token = tm.createClientAccessToken(session, tm.getAccess(null, true, adminConsole, admin), adminRealm, adminConsole, admin, userSession, clientSession);
-            return tm.encodeToken(adminRealm, token);
-        } finally {
-            keycloakRule.stopSession(session, true);
-        }
+    @Test
+    public void testPassiveMode() {
+        // KEYCLOAK-2075 test SAML IsPassive handling - PicketLink SP client library doesn't support this option unfortunately.
+        // But the test of server side is included in test of SAML Keycloak adapter
     }
 
 
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/samlfilter/SamlAdapterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/samlfilter/SamlAdapterTest.java
index 9a47b44..d0c5d21 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/samlfilter/SamlAdapterTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/samlfilter/SamlAdapterTest.java
@@ -5,7 +5,6 @@ import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
 import org.keycloak.testsuite.keycloaksaml.SamlAdapterTestStrategy;
-import org.keycloak.testsuite.keycloaksaml.SamlSPFacade;
 import org.keycloak.testsuite.keycloaksaml.SendUsernameServlet;
 import org.openqa.selenium.WebDriver;
 
@@ -25,6 +24,7 @@ public class SamlAdapterTest {
              ClassLoader classLoader = SamlAdapterTest.class.getClassLoader();
 
             initializeSamlSecuredWar("/keycloak-saml/simple-post", "/sales-post",  "post.war", classLoader);
+            initializeSamlSecuredWar("/keycloak-saml/simple-post-passive", "/sales-post-passive", "post-passive.war", classLoader);
             initializeSamlSecuredWar("/keycloak-saml/signed-post", "/sales-post-sig",  "post-sig.war", classLoader);
             initializeSamlSecuredWar("/keycloak-saml/signed-post-email", "/sales-post-sig-email",  "post-sig-email.war", classLoader);
             initializeSamlSecuredWar("/keycloak-saml/signed-post-transient", "/sales-post-sig-transient",  "post-sig-transient.war", classLoader);
@@ -37,9 +37,6 @@ public class SamlAdapterTest {
             initializeSamlSecuredWar("/keycloak-saml/bad-realm-signed-post", "/bad-realm-sales-post-sig",  "bad-realm-post-sig.war", classLoader);
             initializeSamlSecuredWar("/keycloak-saml/encrypted-post", "/sales-post-enc", "post-enc.war", classLoader);
             SamlAdapterTestStrategy.uploadSP("http://localhost:8081/auth");
-
-
-
         }
 
         @Override
@@ -106,6 +103,11 @@ public class SamlAdapterTest {
     }
 
     @Test
+    public void testPostPassiveLoginLogout() {
+        testStrategy.testPostPassiveLoginLogout(false);
+    }
+
+    @Test
     public void testPostSignedLoginLogoutTransientNameID() {
         testStrategy.testPostSignedLoginLogoutTransientNameID();
     }
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/simple-post-passive/WEB-INF/keycloak-saml.xml b/testsuite/integration/src/test/resources/keycloak-saml/simple-post-passive/WEB-INF/keycloak-saml.xml
new file mode 100755
index 0000000..3154627
--- /dev/null
+++ b/testsuite/integration/src/test/resources/keycloak-saml/simple-post-passive/WEB-INF/keycloak-saml.xml
@@ -0,0 +1,25 @@
+<keycloak-saml-adapter>
+    <SP entityID="http://localhost:8081/sales-post-passive/"
+        sslPolicy="EXTERNAL"
+        nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
+        logoutPage="/logout.jsp"
+        forceAuthentication="false"
+        isPassive="true">
+        <PrincipalNameMapping policy="FROM_NAME_ID"/>
+        <RoleIdentifiers>
+            <Attribute name="Role"/>
+        </RoleIdentifiers>
+        <IDP entityID="idp">
+            <SingleSignOnService requestBinding="POST"
+                                 bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+                    />
+
+            <SingleLogoutService
+                    requestBinding="POST"
+                    responseBinding="POST"
+                    postBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+                    redirectBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+                    />
+        </IDP>
+     </SP>
+</keycloak-saml-adapter>
\ No newline at end of file
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/testsaml.json b/testsuite/integration/src/test/resources/keycloak-saml/testsaml.json
index 95b9fb9..7a50a91 100755
--- a/testsuite/integration/src/test/resources/keycloak-saml/testsaml.json
+++ b/testsuite/integration/src/test/resources/keycloak-saml/testsaml.json
@@ -86,6 +86,24 @@
             }
         },
         {
+            "name": "http://localhost:8081/sales-post-passive/",
+            "enabled": true,
+            "fullScopeAllowed": true,
+            "protocol": "saml",
+            "baseUrl": "http://localhost:8081/sales-post-passive",
+            "redirectUris": [
+                "http://localhost:8081/sales-post-passive/*"
+            ],
+            "attributes": {
+                "saml.authnstatement": "true",
+                "saml_assertion_consumer_url_post": "http://localhost:8081/sales-post-passive/",
+                "saml_assertion_consumer_url_redirect": "http://localhost:8081/sales-post-passive/",
+                "saml_single_logout_service_url_post": "http://localhost:8081/sales-post-passive/",
+                "saml_single_logout_service_url_redirect": "http://localhost:8081/sales-post-passive/",
+                "saml_idp_initiated_sso_url_name": "sales-post-passive"
+            }
+        },
+        {
             "name": "http://localhost:8081/sales-post-sig/",
             "enabled": true,
             "protocol": "saml",