keycloak-aplcache

saml error handling

11/21/2015 11:58:02 AM

Changes

Details

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 a34fb1b..bd78fd2 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
@@ -87,6 +87,7 @@ public class InitiateLogin implements AuthChallenge {
             Document document = authnRequestBuilder.toDocument();
             SamlDeployment.Binding samlBinding = deployment.getIDP().getSingleSignOnService().getRequestBinding();
             SamlUtil.sendSaml(true, httpFacade, actionUrl, binding, document, samlBinding);
+            sessionStore.setCurrentAction(SamlSessionStore.CurrentAction.LOGGING_IN);
         } catch (Exception e) {
             throw new RuntimeException("Could not create authentication request.", e);
         }
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 39f026a..5478edf 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
@@ -1,10 +1,12 @@
 package org.keycloak.adapters.saml;
 
 import org.jboss.logging.Logger;
-import org.keycloak.common.VerificationException;
 import org.keycloak.adapters.spi.AuthChallenge;
 import org.keycloak.adapters.spi.AuthOutcome;
 import org.keycloak.adapters.spi.HttpFacade;
+import org.keycloak.common.VerificationException;
+import org.keycloak.common.util.KeycloakUriBuilder;
+import org.keycloak.common.util.MultivaluedHashMap;
 import org.keycloak.dom.saml.v2.assertion.AssertionType;
 import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
 import org.keycloak.dom.saml.v2.assertion.AttributeType;
@@ -29,8 +31,6 @@ 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;
 import org.keycloak.saml.processing.web.util.PostBindingUtil;
-import org.keycloak.common.util.KeycloakUriBuilder;
-import org.keycloak.common.util.MultivaluedHashMap;
 import org.w3c.dom.Document;
 import org.w3c.dom.Node;
 
@@ -74,7 +74,7 @@ public abstract class SamlAuthenticator {
             return handleSamlRequest(samlRequest, relayState);
         } else if (samlResponse != null) {
             return handleSamlResponse(samlResponse, relayState);
-        }  else if (sessionStore.isLoggedIn()) {
+        } else if (sessionStore.isLoggedIn()) {
             if (globalLogout) {
                 return globalLogout();
             }
@@ -106,6 +106,7 @@ public abstract class SamlAuthenticator {
 
         try {
             SamlUtil.sendSaml(true, facade, deployment.getIDP().getSingleLogoutService().getRequestBindingUrl(), binding, logoutBuilder.buildDocument(), deployment.getIDP().getSingleLogoutService().getRequestBinding());
+            sessionStore.setCurrentAction(SamlSessionStore.CurrentAction.LOGGING_OUT);
         } catch (Exception e) {
             log.error("Could not send global logout SAML request", e);
             return AuthOutcome.FAILED;
@@ -155,7 +156,7 @@ public abstract class SamlAuthenticator {
     protected AuthOutcome logoutRequest(LogoutRequestType request, String relayState) {
         if (request.getSessionIndex() == null || request.getSessionIndex().isEmpty()) {
             sessionStore.logoutByPrincipal(request.getNameID().getValue());
-        }  else {
+        } else {
             sessionStore.logoutBySsoId(request.getSessionIndex());
         }
 
@@ -169,7 +170,8 @@ public abstract class SamlAuthenticator {
             binding.signatureAlgorithm(deployment.getSignatureAlgorithm())
                     .signWith(deployment.getSigningKeyPair())
                     .signDocument();
-            if (deployment.getSignatureCanonicalizationMethod() != null) binding.canonicalizationMethod(deployment.getSignatureCanonicalizationMethod());
+            if (deployment.getSignatureCanonicalizationMethod() != null)
+                binding.canonicalizationMethod(deployment.getSignatureCanonicalizationMethod());
         }
 
 
@@ -199,7 +201,7 @@ public abstract class SamlAuthenticator {
             postBinding = true;
             holder = extractPostBindingResponse(samlResponse);
         }
-        StatusResponseType statusResponse = (StatusResponseType)holder.getSamlObject();
+        final StatusResponseType statusResponse = (StatusResponseType) holder.getSamlObject();
         // validate destination
         if (!requestUri.equals(statusResponse.getDestination())) {
             log.error("Request URI does not match SAML request destination");
@@ -214,19 +216,49 @@ public abstract class SamlAuthenticator {
                     return AuthOutcome.FAILED;
                 }
             }
-            return handleLoginResponse((ResponseType)statusResponse);
+            return handleLoginResponse((ResponseType) statusResponse);
 
         } else {
-            if (deployment.getIDP().getSingleLogoutService().validateResponseSignature()) {
+            if (sessionStore.isLoggingOut()) {
                 try {
-                    validateSamlSignature(holder, postBinding, GeneralConstants.SAML_RESPONSE_KEY);
-                } catch (VerificationException e) {
-                    log.error("Failed to verify saml response signature", e);
+                    if (deployment.getIDP().getSingleLogoutService().validateResponseSignature()) {
+                        try {
+                            validateSamlSignature(holder, postBinding, GeneralConstants.SAML_RESPONSE_KEY);
+                        } catch (VerificationException e) {
+                            log.error("Failed to verify saml response signature", e);
+                            return AuthOutcome.FAILED;
+                        }
+                    }
+                    return handleLogoutResponse(holder, statusResponse, relayState);
+                } finally {
+                    sessionStore.setCurrentAction(SamlSessionStore.CurrentAction.NONE);
+                }
+
+            } else if (sessionStore.isLoggingIn()) {
+                try {
+                    challenge = new AuthChallenge() {
+                        @Override
+                        public boolean challenge(HttpFacade exchange) {
+                            exchange.getResponse().sendError(500, statusResponse.getStatus().getStatusCode().getValue().toString());
+                            return true;
+                        }
+
+                        @Override
+                        public boolean errorPage() {
+                            return true;
+                        }
+
+                        @Override
+                        public int getResponseCode() {
+                            return 500;
+                        }
+                    };
                     return AuthOutcome.FAILED;
+                } finally {
+                    sessionStore.setCurrentAction(SamlSessionStore.CurrentAction.NONE);
                 }
             }
-            // todo need to check that it is actually a LogoutResponse
-            return handleLogoutResponse(holder, statusResponse, relayState);
+            return AuthOutcome.NOT_ATTEMPTED;
         }
 
     }
@@ -239,7 +271,7 @@ public abstract class SamlAuthenticator {
         }
     }
 
-    protected AuthOutcome handleLoginResponse(ResponseType responseType)  {
+    protected AuthOutcome handleLoginResponse(ResponseType responseType) {
         AssertionType assertion = null;
         try {
             assertion = AssertionUtil.getAssertion(responseType, deployment.getDecryptionKey());
@@ -308,14 +340,14 @@ public abstract class SamlAuthenticator {
         AuthnStatementType authn = null;
         for (Object statement : assertion.getStatements()) {
             if (statement instanceof AuthnStatementType) {
-                authn = (AuthnStatementType)statement;
+                authn = (AuthnStatementType) statement;
                 break;
             }
         }
 
 
         URI nameFormat = subjectNameID.getFormat();
-        String nameFormatString = nameFormat == null ?  JBossSAMLURIConstants.NAMEID_FORMAT_UNSPECIFIED.get() : nameFormat.toString();
+        String nameFormatString = nameFormat == null ? JBossSAMLURIConstants.NAMEID_FORMAT_UNSPECIFIED.get() : nameFormat.toString();
         final SamlPrincipal principal = new SamlPrincipal(principalName, principalName, nameFormatString, attributes, friendlyAttributes);
         String index = authn == null ? null : authn.getSessionIndex();
         final String sessionIndex = index;
@@ -341,9 +373,9 @@ public abstract class SamlAuthenticator {
     protected abstract void completeAuthentication(SamlSession account);
 
     private String getAttributeValue(Object attrValue) {
-        String value =  null;
+        String value = null;
         if (attrValue instanceof String) {
-            value = (String)attrValue;
+            value = (String) attrValue;
         } else if (attrValue instanceof Node) {
             Node roleNode = (Node) attrValue;
             value = roleNode.getFirstChild().getNodeValue();
@@ -372,6 +404,7 @@ public abstract class SamlAuthenticator {
     protected SAMLDocumentHolder extractRedirectBindingResponse(String response) {
         return SAMLRequestParser.parseRequestRedirectBinding(response);
     }
+
     protected SAMLDocumentHolder extractPostBindingResponse(String response) {
         byte[] samlBytes = PostBindingUtil.base64Decode(response);
         String xml = new String(samlBytes);
@@ -379,7 +412,6 @@ public abstract class SamlAuthenticator {
     }
 
 
-
     protected AuthOutcome initiateLogin() {
         challenge = new InitiateLogin(deployment, sessionStore);
         return AuthOutcome.NOT_ATTEMPTED;
@@ -445,5 +477,4 @@ public abstract class SamlAuthenticator {
     }
 
 
-
 }
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlSessionStore.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlSessionStore.java
index da20026..1a19464 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlSessionStore.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlSessionStore.java
@@ -1,6 +1,8 @@
 package org.keycloak.adapters.saml;
 
 import org.keycloak.adapters.spi.AdapterSessionStore;
+import org.keycloak.dom.saml.v2.protocol.StatusResponseType;
+import org.keycloak.dom.saml.v2.protocol.StatusType;
 
 import java.util.List;
 
@@ -9,6 +11,19 @@ import java.util.List;
  * @version $Revision: 1 $
  */
 public interface SamlSessionStore extends AdapterSessionStore {
+    public static final String CURRENT_ACTION = "SAML_CURRENT_ACTION";
+    public static final String SAML_LOGIN_ERROR_STATUS = "SAML_LOGIN_ERROR_STATUS";
+    public static final String SAML_LOGOUT_ERROR_STATUS = "SAML_LOGOUT_ERROR_STATUS";
+
+    enum CurrentAction {
+        NONE,
+        LOGGING_IN,
+        LOGGING_OUT
+    }
+    void setCurrentAction(CurrentAction action);
+    boolean isLoggingIn();
+    boolean isLoggingOut();
+
     boolean isLoggedIn();
     SamlSession getAccount();
     void saveAccount(SamlSession account);
diff --git a/saml/client-adapter/jetty/jetty-core/src/main/java/org/keycloak/adapters/saml/jetty/JettySamlSessionStore.java b/saml/client-adapter/jetty/jetty-core/src/main/java/org/keycloak/adapters/saml/jetty/JettySamlSessionStore.java
index d86184b..6e51f11 100755
--- a/saml/client-adapter/jetty/jetty-core/src/main/java/org/keycloak/adapters/saml/jetty/JettySamlSessionStore.java
+++ b/saml/client-adapter/jetty/jetty-core/src/main/java/org/keycloak/adapters/saml/jetty/JettySamlSessionStore.java
@@ -8,6 +8,7 @@ import org.keycloak.adapters.spi.SessionIdMapper;
 import org.keycloak.adapters.jetty.spi.JettyUserSessionManagement;
 import org.keycloak.adapters.saml.SamlSession;
 import org.keycloak.adapters.saml.SamlSessionStore;
+import org.keycloak.dom.saml.v2.protocol.StatusType;
 
 import javax.servlet.http.HttpSession;
 
@@ -38,6 +39,28 @@ public class JettySamlSessionStore implements SamlSessionStore {
     }
 
     @Override
+    public void setCurrentAction(CurrentAction action) {
+        if (action == CurrentAction.NONE && request.getSession(false) == null) return;
+        request.getSession().setAttribute(CURRENT_ACTION, action);
+    }
+
+    @Override
+    public boolean isLoggingIn() {
+        HttpSession session = request.getSession(false);
+        if (session == null) return false;
+        CurrentAction action = (CurrentAction)session.getAttribute(CURRENT_ACTION);
+        return action == CurrentAction.LOGGING_IN;
+    }
+
+    @Override
+    public boolean isLoggingOut() {
+        HttpSession session = request.getSession(false);
+        if (session == null) return false;
+        CurrentAction action = (CurrentAction)session.getAttribute(CURRENT_ACTION);
+        return action == CurrentAction.LOGGING_OUT;
+    }
+
+    @Override
     public void logoutAccount() {
         HttpSession session = request.getSession(false);
         if (session != null) {
diff --git a/saml/client-adapter/servlet-filter/src/main/java/org/keycloak/adapters/saml/servlet/FilterSamlSessionStore.java b/saml/client-adapter/servlet-filter/src/main/java/org/keycloak/adapters/saml/servlet/FilterSamlSessionStore.java
index cbd036e..e690db5 100755
--- a/saml/client-adapter/servlet-filter/src/main/java/org/keycloak/adapters/saml/servlet/FilterSamlSessionStore.java
+++ b/saml/client-adapter/servlet-filter/src/main/java/org/keycloak/adapters/saml/servlet/FilterSamlSessionStore.java
@@ -7,6 +7,7 @@ import org.keycloak.adapters.spi.SessionIdMapper;
 import org.keycloak.adapters.saml.SamlSession;
 import org.keycloak.adapters.saml.SamlSessionStore;
 import org.keycloak.adapters.servlet.FilterSessionStore;
+import org.keycloak.dom.saml.v2.protocol.StatusType;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletRequestWrapper;
@@ -30,6 +31,28 @@ public class FilterSamlSessionStore extends FilterSessionStore implements SamlSe
     }
 
     @Override
+    public void setCurrentAction(CurrentAction action) {
+        if (action == CurrentAction.NONE && request.getSession(false) == null) return;
+        request.getSession().setAttribute(CURRENT_ACTION, action);
+    }
+
+    @Override
+    public boolean isLoggingIn() {
+        HttpSession session = request.getSession(false);
+        if (session == null) return false;
+        CurrentAction action = (CurrentAction)session.getAttribute(CURRENT_ACTION);
+        return action == CurrentAction.LOGGING_IN;
+    }
+
+    @Override
+    public boolean isLoggingOut() {
+        HttpSession session = request.getSession(false);
+        if (session == null) return false;
+        CurrentAction action = (CurrentAction)session.getAttribute(CURRENT_ACTION);
+        return action == CurrentAction.LOGGING_OUT;
+    }
+
+    @Override
     public void logoutAccount() {
         HttpSession session = request.getSession(false);
         if (session == null) return;
diff --git a/saml/client-adapter/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/saml/CatalinaSamlSessionStore.java b/saml/client-adapter/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/saml/CatalinaSamlSessionStore.java
index adbc441..804acf3 100755
--- a/saml/client-adapter/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/saml/CatalinaSamlSessionStore.java
+++ b/saml/client-adapter/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/saml/CatalinaSamlSessionStore.java
@@ -9,6 +9,8 @@ import org.keycloak.adapters.spi.HttpFacade;
 import org.keycloak.adapters.spi.SessionIdMapper;
 import org.keycloak.adapters.tomcat.CatalinaUserSessionManagement;
 import org.keycloak.adapters.tomcat.GenericPrincipalFactory;
+import org.keycloak.dom.saml.v2.protocol.StatusResponseType;
+import org.keycloak.dom.saml.v2.protocol.StatusType;
 
 import javax.servlet.http.HttpSession;
 import java.io.IOException;
@@ -42,6 +44,28 @@ public class CatalinaSamlSessionStore implements SamlSessionStore {
     }
 
     @Override
+    public void setCurrentAction(CurrentAction action) {
+        if (action == CurrentAction.NONE && request.getSession(false) == null) return;
+        request.getSession().setAttribute(CURRENT_ACTION, action);
+    }
+
+    @Override
+    public boolean isLoggingIn() {
+        HttpSession session = request.getSession(false);
+        if (session == null) return false;
+        CurrentAction action = (CurrentAction)session.getAttribute(CURRENT_ACTION);
+        return action == CurrentAction.LOGGING_IN;
+    }
+
+    @Override
+    public boolean isLoggingOut() {
+        HttpSession session = request.getSession(false);
+        if (session == null) return false;
+        CurrentAction action = (CurrentAction)session.getAttribute(CURRENT_ACTION);
+        return action == CurrentAction.LOGGING_OUT;
+    }
+
+    @Override
     public void logoutAccount() {
         Session sessionInternal = request.getSessionInternal(false);
         if (sessionInternal == null) return;
diff --git a/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/ServletSamlSessionStore.java b/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/ServletSamlSessionStore.java
index 8afcc1f..34d718b 100755
--- a/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/ServletSamlSessionStore.java
+++ b/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/ServletSamlSessionStore.java
@@ -13,9 +13,12 @@ import org.keycloak.adapters.saml.SamlSession;
 import org.keycloak.adapters.saml.SamlSessionStore;
 import org.keycloak.adapters.undertow.UndertowUserSessionManagement;
 import org.keycloak.common.util.KeycloakUriBuilder;
+import org.keycloak.dom.saml.v2.protocol.StatusType;
 
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
+import java.io.IOException;
 import java.security.Principal;
 import java.util.LinkedList;
 import java.util.List;
@@ -34,6 +37,7 @@ public class ServletSamlSessionStore implements SamlSessionStore {
     private final SecurityContext securityContext;
     private final SessionIdMapper idMapper;
 
+
     public ServletSamlSessionStore(HttpServerExchange exchange, UndertowUserSessionManagement sessionManagement,
                                    SecurityContext securityContext,
                                    SessionIdMapper idMapper) {
@@ -44,6 +48,28 @@ public class ServletSamlSessionStore implements SamlSessionStore {
     }
 
     @Override
+    public void setCurrentAction(CurrentAction action) {
+        if (action == CurrentAction.NONE && getRequest().getSession(false) == null) return;
+        getRequest().getSession().setAttribute(CURRENT_ACTION, action);
+    }
+
+    @Override
+    public boolean isLoggingIn() {
+        HttpSession session = getRequest().getSession(false);
+        if (session == null) return false;
+        CurrentAction action = (CurrentAction)session.getAttribute(CURRENT_ACTION);
+        return action == CurrentAction.LOGGING_IN;
+    }
+
+    @Override
+    public boolean isLoggingOut() {
+        HttpSession session = getRequest().getSession(false);
+        if (session == null) return false;
+        CurrentAction action = (CurrentAction)session.getAttribute(CURRENT_ACTION);
+        return action == CurrentAction.LOGGING_OUT;
+    }
+
+    @Override
     public void logoutAccount() {
         HttpSession session = getSession(false);
         if (session != null) {
@@ -170,8 +196,18 @@ public class ServletSamlSessionStore implements SamlSessionStore {
     }
 
     protected HttpSession getSession(boolean create) {
-        final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
-        HttpServletRequest req = (HttpServletRequest) servletRequestContext.getServletRequest();
+        HttpServletRequest req = getRequest();
         return req.getSession(create);
     }
+
+    private HttpServletResponse getResponse() {
+        final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
+        return (HttpServletResponse)servletRequestContext.getServletResponse();
+
+    }
+
+    private HttpServletRequest getRequest() {
+        final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
+        return (HttpServletRequest) servletRequestContext.getServletRequest();
+    }
 }
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 2ce85eb..482834b 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
@@ -17,6 +17,7 @@
  */
 package org.keycloak.dom.saml.v2.protocol;
 
+import java.io.Serializable;
 import java.net.URI;
 
 /**
@@ -39,7 +40,7 @@ import java.net.URI;
  * </complexType>
  * </pre>
  */
-public class StatusCodeType {
+public class StatusCodeType implements Serializable {
 
     protected StatusCodeType statusCode;
     protected URI value;
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 9918879..2e2eab9 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
@@ -17,6 +17,8 @@
  */
 package org.keycloak.dom.saml.v2.protocol;
 
+import java.io.Serializable;
+
 /**
  * <p>
  * Java class for StatusType complex type.
@@ -38,7 +40,7 @@ package org.keycloak.dom.saml.v2.protocol;
  * &lt;/complexType>
  * </pre>
  */
-public class StatusType {
+public class StatusType implements Serializable {
 
     protected String statusMessage;
     protected StatusCodeType statusCode;
diff --git a/saml/saml-core/src/main/java/org/keycloak/saml/SAML2ErrorResponseBuilder.java b/saml/saml-core/src/main/java/org/keycloak/saml/SAML2ErrorResponseBuilder.java
index db7ac7e..2373656 100755
--- a/saml/saml-core/src/main/java/org/keycloak/saml/SAML2ErrorResponseBuilder.java
+++ b/saml/saml-core/src/main/java/org/keycloak/saml/SAML2ErrorResponseBuilder.java
@@ -1,6 +1,12 @@
 package org.keycloak.saml;
 
+import org.keycloak.dom.saml.v2.assertion.NameIDType;
+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.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.saml.processing.api.saml.v2.response.SAML2Response;
 import org.keycloak.saml.processing.core.saml.v2.common.IDGenerator;
@@ -9,8 +15,11 @@ import org.keycloak.saml.processing.core.saml.v2.holders.IDPInfoHolder;
 import org.keycloak.saml.processing.core.saml.v2.holders.IssuerInfoHolder;
 import org.keycloak.saml.processing.core.saml.v2.holders.SPInfoHolder;
 import org.keycloak.dom.saml.v2.protocol.ResponseType;
+import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil;
 import org.w3c.dom.Document;
 
+import java.net.URI;
+
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
@@ -38,29 +47,25 @@ public class SAML2ErrorResponseBuilder {
 
 
     public Document buildDocument() throws ProcessingException {
-        Document samlResponse = null;
-        ResponseType responseType = null;
-
-        SAML2Response saml2Response = new SAML2Response();
-
-        // Create a response type
-        String id = IDGenerator.create("ID_");
 
-        IssuerInfoHolder issuerHolder = new IssuerInfoHolder(issuer);
-        issuerHolder.setStatusCode(status);
+        try {
+            StatusResponseType statusResponse = new StatusResponseType(IDGenerator.create("ID_"), XMLTimeUtil.getIssueInstant());
 
-        IDPInfoHolder idp = new IDPInfoHolder();
-        idp.setNameIDFormatValue(null);
-        idp.setNameIDFormat(JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.get());
+            statusResponse.setStatus(JBossSAMLAuthnResponseFactory.createStatusTypeForResponder(status));
+            NameIDType issuer = new NameIDType();
+            issuer.setValue(this.issuer);
 
-        SPInfoHolder sp = new SPInfoHolder();
-        sp.setResponseDestinationURI(destination);
+            statusResponse.setIssuer(issuer);
+            statusResponse.setDestination(destination);
 
-        responseType = saml2Response.createResponseType(id);
-        responseType.setStatus(JBossSAMLAuthnResponseFactory.createStatusTypeForResponder(status));
-        responseType.setDestination(destination);
+            SAML2Response saml2Response = new SAML2Response();
+            return saml2Response.convert(statusResponse);
+        } catch (ConfigurationException e) {
+            throw new ProcessingException(e);
+        } catch (ParsingException e) {
+            throw new ProcessingException(e);
+        }
 
-        return samlResponse;
     }
 
 
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 f97a05e..84fa65c 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
@@ -67,6 +67,10 @@ public class SamlAdapterTest {
     }
 
     @Test
+    public void testErrorHandling() throws Exception {
+        testStrategy.testErrorHandling();
+    }
+    @Test
     public void testMetadataPostSignedLoginLogout() throws Exception {
         testStrategy.testMetadataPostSignedLoginLogout();
     }
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 fbced9f..abe108a 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
@@ -1,27 +1,16 @@
 package org.keycloak.testsuite.keycloaksaml;
 
-import com.mongodb.util.Hash;
 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.junit.rules.ExternalResource;
-import org.keycloak.Config;
 import org.keycloak.adapters.saml.SamlPrincipal;
 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.GroupMembershipMapper;
 import org.keycloak.protocol.saml.mappers.HardcodedAttributeMapper;
@@ -29,33 +18,27 @@ import org.keycloak.protocol.saml.mappers.HardcodedRole;
 import org.keycloak.protocol.saml.mappers.RoleListMapper;
 import org.keycloak.protocol.saml.mappers.RoleNameMapper;
 import org.keycloak.protocol.saml.mappers.UserAttributeStatementMapper;
-import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.saml.BaseSAML2BindingBuilder;
+import org.keycloak.saml.SAML2ErrorResponseBuilder;
+import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
 import org.keycloak.saml.processing.core.saml.v2.constants.X500SAMLProfileConstants;
 import org.keycloak.services.managers.RealmManager;
-import org.keycloak.services.resources.admin.AdminRoot;
 import org.keycloak.testsuite.KeycloakServer;
 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;
-import org.keycloak.util.JsonSerialization;
 import org.openqa.selenium.WebDriver;
+import org.w3c.dom.Document;
 
 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.IOException;
-import java.io.InputStream;
+import java.net.URI;
 import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
@@ -116,6 +99,27 @@ public class SamlAdapterTestStrategy  extends ExternalResource {
         Assert.assertTrue(driver.getCurrentUrl().startsWith(AUTH_SERVER_URL + "/realms/demo/protocol/saml"));
     }
 
+    public void testErrorHandling() throws Exception {
+        Client client = ClientBuilder.newClient();
+        // make sure
+        Response response = client.target(APP_SERVER_BASE_URL + "/employee-sig/").request().get();
+        response.close();
+        SAML2ErrorResponseBuilder builder = new SAML2ErrorResponseBuilder()
+                .destination(APP_SERVER_BASE_URL + "/employee-sig/")
+                        .issuer(AUTH_SERVER_URL + "/realms/demo")
+                        .status(JBossSAMLURIConstants.STATUS_REQUEST_DENIED.get());
+        BaseSAML2BindingBuilder binding = new BaseSAML2BindingBuilder()
+                .relayState(null);
+        Document document = builder.buildDocument();
+        URI uri = binding.redirectBinding(document).generateURI(APP_SERVER_BASE_URL + "/employee-sig/", false);
+        response = client.target(uri).request().get();
+        String errorPage = response.readEntity(String.class);
+        response.close();
+        Assert.assertTrue(errorPage.contains(JBossSAMLURIConstants.STATUS_RESPONDER.get()));
+        client.close();
+
+    }
+
     public void testPostSimpleLoginLogout() {
         driver.navigate().to(APP_SERVER_BASE_URL + "/sales-post/");
         assertEquals(driver.getCurrentUrl(), AUTH_SERVER_URL + "/realms/demo/protocol/saml");
diff --git a/testsuite/jetty/jetty81/src/test/java/org/keycloak/testsuite/JettySamlTest.java b/testsuite/jetty/jetty81/src/test/java/org/keycloak/testsuite/JettySamlTest.java
index 1506b0c..3eb9a80 100755
--- a/testsuite/jetty/jetty81/src/test/java/org/keycloak/testsuite/JettySamlTest.java
+++ b/testsuite/jetty/jetty81/src/test/java/org/keycloak/testsuite/JettySamlTest.java
@@ -105,6 +105,11 @@ public class JettySamlTest {
     }
 
     @Test
+    public void testErrorHandling() throws Exception {
+        testStrategy.testErrorHandling();
+    }
+
+    @Test
     public void testPostSimpleLoginLogout() {
         testStrategy.testPostSimpleLoginLogout();
     }
diff --git a/testsuite/jetty/jetty91/src/test/java/org/keycloak/testsuite/JettySamlTest.java b/testsuite/jetty/jetty91/src/test/java/org/keycloak/testsuite/JettySamlTest.java
index e71887e..25cf046 100755
--- a/testsuite/jetty/jetty91/src/test/java/org/keycloak/testsuite/JettySamlTest.java
+++ b/testsuite/jetty/jetty91/src/test/java/org/keycloak/testsuite/JettySamlTest.java
@@ -104,6 +104,11 @@ public class JettySamlTest {
     }
 
     @Test
+    public void testErrorHandling() throws Exception {
+        testStrategy.testErrorHandling();
+    }
+
+    @Test
     public void testPostSimpleLoginLogout() {
         testStrategy.testPostSimpleLoginLogout();
     }
diff --git a/testsuite/jetty/jetty92/src/test/java/org/keycloak/testsuite/JettySamlTest.java b/testsuite/jetty/jetty92/src/test/java/org/keycloak/testsuite/JettySamlTest.java
index e71887e..25cf046 100755
--- a/testsuite/jetty/jetty92/src/test/java/org/keycloak/testsuite/JettySamlTest.java
+++ b/testsuite/jetty/jetty92/src/test/java/org/keycloak/testsuite/JettySamlTest.java
@@ -104,6 +104,11 @@ public class JettySamlTest {
     }
 
     @Test
+    public void testErrorHandling() throws Exception {
+        testStrategy.testErrorHandling();
+    }
+
+    @Test
     public void testPostSimpleLoginLogout() {
         testStrategy.testPostSimpleLoginLogout();
     }
diff --git a/testsuite/tomcat6/src/test/java/org/keycloak/testsuite/TomcatSamlTest.java b/testsuite/tomcat6/src/test/java/org/keycloak/testsuite/TomcatSamlTest.java
index c72ab01..ecda388 100755
--- a/testsuite/tomcat6/src/test/java/org/keycloak/testsuite/TomcatSamlTest.java
+++ b/testsuite/tomcat6/src/test/java/org/keycloak/testsuite/TomcatSamlTest.java
@@ -113,6 +113,11 @@ public class TomcatSamlTest {
     }
 
     @Test
+    public void testErrorHandling() throws Exception {
+        testStrategy.testErrorHandling();
+    }
+
+    @Test
     public void testPostSignedLoginLogoutEmailNameID() {
         testStrategy.testPostSignedLoginLogoutEmailNameID();
     }
diff --git a/testsuite/tomcat7/src/test/java/org/keycloak/testsuite/TomcatSamlTest.java b/testsuite/tomcat7/src/test/java/org/keycloak/testsuite/TomcatSamlTest.java
index f9cb853..9653f31 100755
--- a/testsuite/tomcat7/src/test/java/org/keycloak/testsuite/TomcatSamlTest.java
+++ b/testsuite/tomcat7/src/test/java/org/keycloak/testsuite/TomcatSamlTest.java
@@ -93,6 +93,11 @@ public class TomcatSamlTest {
 
 
     @Test
+    public void testErrorHandling() throws Exception {
+        testStrategy.testErrorHandling();
+    }
+
+    @Test
     public void testPostSimpleLoginLogout() {
         testStrategy.testPostSimpleLoginLogout();
     }
diff --git a/testsuite/tomcat8/src/test/java/org/keycloak/testsuite/TomcatSamlTest.java b/testsuite/tomcat8/src/test/java/org/keycloak/testsuite/TomcatSamlTest.java
index 405c6ee..88d9316 100755
--- a/testsuite/tomcat8/src/test/java/org/keycloak/testsuite/TomcatSamlTest.java
+++ b/testsuite/tomcat8/src/test/java/org/keycloak/testsuite/TomcatSamlTest.java
@@ -93,6 +93,11 @@ public class TomcatSamlTest {
     public SamlAdapterTestStrategy testStrategy = new SamlAdapterTestStrategy("http://localhost:8081/auth", "http://localhost:8082", keycloakRule);
 
     @Test
+    public void testErrorHandling() throws Exception {
+        testStrategy.testErrorHandling();
+    }
+
+    @Test
     public void testPostSimpleLoginLogout() {
         testStrategy.testPostSimpleLoginLogout();
     }