keycloak-aplcache

Merge pull request #1951 from pedroigor/KEYCLOAK-2202 [KEYCLOAK-2202]

12/17/2015 10:04:17 PM

Changes

Details

diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-saml-protocol/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-saml-protocol/main/module.xml
index fbd65fd..81cd365 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-saml-protocol/main/module.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-saml-protocol/main/module.xml
@@ -26,6 +26,7 @@
         <module name="org.keycloak.keycloak-connections-http-client" services="import"/>
 
         <module name="javax.api"/>
+        <module name="javax.xml.soap.api"/>
     </dependencies>
 
 </module>
diff --git a/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java b/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java
index 3a105c4..c49f5b6 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java
@@ -24,6 +24,7 @@ public class DefaultAuthenticationFlows {
     public static final String DIRECT_GRANT_FLOW = "direct grant";
     public static final String RESET_CREDENTIALS_FLOW = "reset credentials";
     public static final String LOGIN_FORMS_FLOW = "forms";
+    public static final String SAML_ECP_FLOW = "saml ecp";
 
     public static final String CLIENT_AUTHENTICATION_FLOW = "clients";
     public static final String FIRST_BROKER_LOGIN_FLOW = "first broker login";
@@ -39,6 +40,7 @@ public class DefaultAuthenticationFlows {
         if (realm.getFlowByAlias(RESET_CREDENTIALS_FLOW) == null) resetCredentialsFlow(realm);
         if (realm.getFlowByAlias(CLIENT_AUTHENTICATION_FLOW) == null) clientAuthFlow(realm);
         if (realm.getFlowByAlias(FIRST_BROKER_LOGIN_FLOW) == null) firstBrokerLoginFlow(realm, false);
+        if (realm.getFlowByAlias(SAML_ECP_FLOW) == null) samlEcpProfile(realm);
     }
     public static void migrateFlows(RealmModel realm) {
         if (realm.getFlowByAlias(BROWSER_FLOW) == null) browserFlow(realm, true);
@@ -47,6 +49,7 @@ public class DefaultAuthenticationFlows {
         if (realm.getFlowByAlias(RESET_CREDENTIALS_FLOW) == null) resetCredentialsFlow(realm);
         if (realm.getFlowByAlias(CLIENT_AUTHENTICATION_FLOW) == null) clientAuthFlow(realm);
         if (realm.getFlowByAlias(FIRST_BROKER_LOGIN_FLOW) == null) firstBrokerLoginFlow(realm, true);
+        if (realm.getFlowByAlias(SAML_ECP_FLOW) == null) samlEcpProfile(realm);
     }
 
     public static void registrationFlow(RealmModel realm) {
@@ -447,4 +450,25 @@ public class DefaultAuthenticationFlows {
         execution.setAuthenticatorFlow(false);
         realm.addAuthenticatorExecution(execution);
     }
+
+    public static void samlEcpProfile(RealmModel realm) {
+        AuthenticationFlowModel ecpFlow = new AuthenticationFlowModel();
+
+        ecpFlow.setAlias(SAML_ECP_FLOW);
+        ecpFlow.setDescription("SAML ECP Profile Authentication Flow");
+        ecpFlow.setProviderId("basic-flow");
+        ecpFlow.setTopLevel(true);
+        ecpFlow.setBuiltIn(true);
+        ecpFlow = realm.addAuthenticationFlow(ecpFlow);
+
+        AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
+
+        execution.setParentFlow(ecpFlow.getId());
+        execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
+        execution.setAuthenticator("http-basic-authenticator");
+        execution.setPriority(10);
+        execution.setAuthenticatorFlow(false);
+
+        realm.addAuthenticatorExecution(execution);
+    }
 }
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/OnSessionCreated.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/OnSessionCreated.java
new file mode 100644
index 0000000..92f3b4d
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/OnSessionCreated.java
@@ -0,0 +1,9 @@
+package org.keycloak.adapters.saml;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface OnSessionCreated {
+
+    void onSessionCreated(SamlSession samlSession);
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/profile/AbstractSamlAuthenticationHandler.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/profile/AbstractSamlAuthenticationHandler.java
new file mode 100644
index 0000000..290b2d7
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/profile/AbstractSamlAuthenticationHandler.java
@@ -0,0 +1,484 @@
+package org.keycloak.adapters.saml.profile;
+
+import org.jboss.logging.Logger;
+import org.keycloak.adapters.saml.AbstractInitiateLogin;
+import org.keycloak.adapters.saml.OnSessionCreated;
+import org.keycloak.adapters.saml.SamlAuthenticationError;
+import org.keycloak.adapters.saml.SamlDeployment;
+import org.keycloak.adapters.saml.SamlPrincipal;
+import org.keycloak.adapters.saml.SamlSession;
+import org.keycloak.adapters.saml.SamlSessionStore;
+import org.keycloak.adapters.saml.SamlUtil;
+import org.keycloak.adapters.saml.profile.webbrowsersso.WebBrowserSsoAuthenticationHandler;
+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;
+import org.keycloak.dom.saml.v2.assertion.AuthnStatementType;
+import org.keycloak.dom.saml.v2.assertion.NameIDType;
+import org.keycloak.dom.saml.v2.assertion.StatementAbstractType;
+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.SAML2AuthnRequestBuilder;
+import org.keycloak.saml.SAMLRequestParser;
+import org.keycloak.saml.SignatureAlgorithm;
+import org.keycloak.saml.common.constants.GeneralConstants;
+import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
+import org.keycloak.saml.common.exceptions.ConfigurationException;
+import org.keycloak.saml.common.exceptions.ProcessingException;
+import org.keycloak.saml.common.util.Base64;
+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.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import java.io.IOException;
+import java.net.URI;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ */
+public abstract class AbstractSamlAuthenticationHandler implements SamlAuthenticationHandler {
+
+    protected static Logger log = Logger.getLogger(WebBrowserSsoAuthenticationHandler.class);
+
+    protected final HttpFacade facade;
+    protected final SamlSessionStore sessionStore;
+    protected  final SamlDeployment deployment;
+    protected AuthChallenge challenge;
+
+    public AbstractSamlAuthenticationHandler(HttpFacade facade, SamlDeployment deployment, SamlSessionStore sessionStore) {
+        this.facade = facade;
+        this.deployment = deployment;
+        this.sessionStore = sessionStore;
+    }
+
+    public AuthOutcome doHandle(SamlInvocationContext context, OnSessionCreated onCreateSession) {
+        String samlRequest = context.getSamlRequest();
+        String samlResponse = context.getSamlResponse();
+        String relayState = context.getRelayState();
+        if (samlRequest != null) {
+            return handleSamlRequest(samlRequest, relayState);
+        } else if (samlResponse != null) {
+            return handleSamlResponse(samlResponse, relayState, onCreateSession);
+        } else if (sessionStore.isLoggedIn()) {
+            if (verifySSL()) return AuthOutcome.FAILED;
+            log.debug("AUTHENTICATED: was cached");
+            return handleRequest();
+        }
+        return initiateLogin();
+    }
+
+    protected AuthOutcome handleRequest() {
+        return AuthOutcome.AUTHENTICATED;
+    }
+
+    @Override
+    public AuthChallenge getChallenge() {
+        return this.challenge;
+    }
+
+    protected AuthOutcome handleSamlRequest(String samlRequest, String relayState) {
+        SAMLDocumentHolder holder = null;
+        boolean postBinding = false;
+        String requestUri = facade.getRequest().getURI();
+        if (facade.getRequest().getMethod().equalsIgnoreCase("GET")) {
+            // strip out query params
+            int index = requestUri.indexOf('?');
+            if (index > -1) {
+                requestUri = requestUri.substring(0, index);
+            }
+            holder = SAMLRequestParser.parseRequestRedirectBinding(samlRequest);
+        } else {
+            postBinding = true;
+            holder = SAMLRequestParser.parseRequestPostBinding(samlRequest);
+        }
+        RequestAbstractType requestAbstractType = (RequestAbstractType) holder.getSamlObject();
+        if (!requestUri.equals(requestAbstractType.getDestination().toString())) {
+            log.error("expected destination '" + requestUri + "' got '" + requestAbstractType.getDestination() + "'");
+            return AuthOutcome.FAILED;
+        }
+
+        if (requestAbstractType instanceof LogoutRequestType) {
+            if (deployment.getIDP().getSingleLogoutService().validateRequestSignature()) {
+                try {
+                    validateSamlSignature(holder, postBinding, GeneralConstants.SAML_REQUEST_KEY);
+                } catch (VerificationException e) {
+                    log.error("Failed to verify saml request signature", e);
+                    return AuthOutcome.FAILED;
+                }
+            }
+            LogoutRequestType logout = (LogoutRequestType) requestAbstractType;
+            return logoutRequest(logout, relayState);
+
+        } else {
+            log.error("unknown SAML request type");
+            return AuthOutcome.FAILED;
+        }
+    }
+
+    protected abstract AuthOutcome logoutRequest(LogoutRequestType request, String relayState);
+
+    protected AuthOutcome handleSamlResponse(String samlResponse, String relayState, OnSessionCreated onCreateSession) {
+        SAMLDocumentHolder holder = null;
+        boolean postBinding = false;
+        String requestUri = facade.getRequest().getURI();
+        if (facade.getRequest().getMethod().equalsIgnoreCase("GET")) {
+            int index = requestUri.indexOf('?');
+            if (index > -1) {
+                requestUri = requestUri.substring(0, index);
+            }
+            holder = extractRedirectBindingResponse(samlResponse);
+        } else {
+            postBinding = true;
+            holder = extractPostBindingResponse(samlResponse);
+        }
+        final StatusResponseType statusResponse = (StatusResponseType) holder.getSamlObject();
+        // validate destination
+        if (!requestUri.equals(statusResponse.getDestination())) {
+            log.error("Request URI does not match SAML request destination");
+            return AuthOutcome.FAILED;
+        }
+
+        if (statusResponse instanceof ResponseType) {
+            try {
+                if (deployment.getIDP().getSingleSignOnService().validateResponseSignature()) {
+                    try {
+                        validateSamlSignature(holder, postBinding, GeneralConstants.SAML_RESPONSE_KEY);
+                    } catch (VerificationException e) {
+                        log.error("Failed to verify saml response signature", e);
+
+                        challenge = new AuthChallenge() {
+                            @Override
+                            public boolean challenge(HttpFacade exchange) {
+                                SamlAuthenticationError error = new SamlAuthenticationError(SamlAuthenticationError.Reason.INVALID_SIGNATURE);
+                                exchange.getRequest().setError(error);
+                                exchange.getResponse().sendError(403);
+                                return true;
+                            }
+
+                            @Override
+                            public int getResponseCode() {
+                                return 403;
+                            }
+                        };
+                        return AuthOutcome.FAILED;
+                    }
+                }
+                return handleLoginResponse((ResponseType) statusResponse, onCreateSession);
+            } finally {
+                sessionStore.setCurrentAction(SamlSessionStore.CurrentAction.NONE);
+            }
+
+        } else {
+            if (sessionStore.isLoggingOut()) {
+                try {
+                    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 {
+                    // 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) {
+                            SamlAuthenticationError error = new SamlAuthenticationError(SamlAuthenticationError.Reason.ERROR_STATUS, statusResponse);
+                            exchange.getRequest().setError(error);
+                            exchange.getResponse().sendError(403);
+                            return true;
+                        }
+
+                        @Override
+                        public int getResponseCode() {
+                            return 403;
+                        }
+                    };
+                    return AuthOutcome.FAILED;
+                } finally {
+                    sessionStore.setCurrentAction(SamlSessionStore.CurrentAction.NONE);
+                }
+            }
+            return AuthOutcome.NOT_ATTEMPTED;
+        }
+
+    }
+
+    private void validateSamlSignature(SAMLDocumentHolder holder, boolean postBinding, String paramKey) throws VerificationException {
+        if (postBinding) {
+            verifyPostBindingSignature(holder.getSamlDocument(), deployment.getIDP().getSignatureValidationKey());
+        } else {
+            verifyRedirectBindingSignature(deployment.getIDP().getSignatureValidationKey(), paramKey);
+        }
+    }
+
+    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, OnSessionCreated onCreateSession) {
+
+        AssertionType assertion = null;
+        try {
+            assertion = AssertionUtil.getAssertion(responseType, deployment.getDecryptionKey());
+            if (AssertionUtil.hasExpired(assertion)) {
+                return initiateLogin();
+            }
+        } catch (Exception e) {
+            log.error("Error extracting SAML assertion: " + e.getMessage());
+            challenge = new AuthChallenge() {
+                @Override
+                public boolean challenge(HttpFacade exchange) {
+                    SamlAuthenticationError error = new SamlAuthenticationError(SamlAuthenticationError.Reason.EXTRACTION_FAILURE);
+                    exchange.getRequest().setError(error);
+                    exchange.getResponse().sendError(403);
+                    return true;
+                }
+
+                @Override
+                public int getResponseCode() {
+                    return 403;
+                }
+            };
+        }
+
+        SubjectType subject = assertion.getSubject();
+        SubjectType.STSubType subType = subject.getSubType();
+        NameIDType subjectNameID = (NameIDType) subType.getBaseID();
+        String principalName = subjectNameID.getValue();
+
+        final Set<String> roles = new HashSet<>();
+        MultivaluedHashMap<String, String> attributes = new MultivaluedHashMap<>();
+        MultivaluedHashMap<String, String> friendlyAttributes = new MultivaluedHashMap<>();
+
+        Set<StatementAbstractType> statements = assertion.getStatements();
+        for (StatementAbstractType statement : statements) {
+            if (statement instanceof AttributeStatementType) {
+                AttributeStatementType attributeStatement = (AttributeStatementType) statement;
+                List<AttributeStatementType.ASTChoiceType> attList = attributeStatement.getAttributes();
+                for (AttributeStatementType.ASTChoiceType obj : attList) {
+                    AttributeType attr = obj.getAttribute();
+                    if (isRole(attr)) {
+                        List<Object> attributeValues = attr.getAttributeValue();
+                        if (attributeValues != null) {
+                            for (Object attrValue : attributeValues) {
+                                String role = getAttributeValue(attrValue);
+                                log.debugv("Add role: {0}", role);
+                                roles.add(role);
+                            }
+                        }
+                    } else {
+                        List<Object> attributeValues = attr.getAttributeValue();
+                        if (attributeValues != null) {
+                            for (Object attrValue : attributeValues) {
+                                String value = getAttributeValue(attrValue);
+                                if (attr.getName() != null) {
+                                    attributes.add(attr.getName(), value);
+                                }
+                                if (attr.getFriendlyName() != null) {
+                                    friendlyAttributes.add(attr.getFriendlyName(), value);
+                                }
+                            }
+                        }
+                    }
+
+                }
+            }
+        }
+        if (deployment.getPrincipalNamePolicy() == SamlDeployment.PrincipalNamePolicy.FROM_ATTRIBUTE) {
+            if (deployment.getPrincipalAttributeName() != null) {
+                String attribute = attributes.getFirst(deployment.getPrincipalAttributeName());
+                if (attribute != null) principalName = attribute;
+                else {
+                    attribute = friendlyAttributes.getFirst(deployment.getPrincipalAttributeName());
+                    if (attribute != null) principalName = attribute;
+                }
+            }
+        }
+
+        AuthnStatementType authn = null;
+        for (Object statement : assertion.getStatements()) {
+            if (statement instanceof AuthnStatementType) {
+                authn = (AuthnStatementType) statement;
+                break;
+            }
+        }
+
+
+        URI nameFormat = subjectNameID.getFormat();
+        String nameFormatString = nameFormat == null ? JBossSAMLURIConstants.NAMEID_FORMAT_UNSPECIFIED.get() : nameFormat.toString();
+        final SamlPrincipal principal = new SamlPrincipal(assertion, principalName, principalName, nameFormatString, attributes, friendlyAttributes);
+        String index = authn == null ? null : authn.getSessionIndex();
+        final String sessionIndex = index;
+        SamlSession account = new SamlSession(principal, roles, sessionIndex);
+        sessionStore.saveAccount(account);
+        onCreateSession.onSessionCreated(account);
+
+        // redirect to original request, it will be restored
+        String redirectUri = sessionStore.getRedirectUri();
+        if (redirectUri != null) {
+            facade.getResponse().setHeader("Location", redirectUri);
+            facade.getResponse().setStatus(302);
+            facade.getResponse().end();
+        } else {
+            log.debug("IDP initiated invocation");
+        }
+        log.debug("AUTHENTICATED authn");
+
+        return AuthOutcome.AUTHENTICATED;
+    }
+
+    private String getAttributeValue(Object attrValue) {
+        String value = null;
+        if (attrValue instanceof String) {
+            value = (String) attrValue;
+        } else if (attrValue instanceof Node) {
+            Node roleNode = (Node) attrValue;
+            value = roleNode.getFirstChild().getNodeValue();
+        } else if (attrValue instanceof NameIDType) {
+            NameIDType nameIdType = (NameIDType) attrValue;
+            value = nameIdType.getValue();
+        } else {
+            log.warn("Unable to extract unknown SAML assertion attribute value type: " + attrValue.getClass().getName());
+        }
+        return value;
+    }
+
+    protected boolean isRole(AttributeType attribute) {
+        return (attribute.getName() != null && deployment.getRoleAttributeNames().contains(attribute.getName())) || (attribute.getFriendlyName() != null && deployment.getRoleAttributeNames().contains(attribute.getFriendlyName()));
+    }
+
+    protected AuthOutcome handleLogoutResponse(SAMLDocumentHolder holder, StatusResponseType responseType, String relayState) {
+        boolean loggedIn = sessionStore.isLoggedIn();
+        if (!loggedIn || !"logout".equals(relayState)) {
+            return AuthOutcome.NOT_ATTEMPTED;
+        }
+        sessionStore.logoutAccount();
+        return AuthOutcome.LOGGED_OUT;
+    }
+
+    protected SAMLDocumentHolder extractRedirectBindingResponse(String response) {
+        return SAMLRequestParser.parseRequestRedirectBinding(response);
+    }
+
+
+    protected SAMLDocumentHolder extractPostBindingResponse(String response) {
+        byte[] samlBytes = PostBindingUtil.base64Decode(response);
+        return SAMLRequestParser.parseResponseDocument(samlBytes);
+    }
+
+
+    protected AuthOutcome initiateLogin() {
+        challenge = createChallenge();
+        return AuthOutcome.NOT_ATTEMPTED;
+    }
+
+    protected AbstractInitiateLogin createChallenge() {
+        return new AbstractInitiateLogin(deployment, sessionStore) {
+            @Override
+            protected void sendAuthnRequest(HttpFacade httpFacade, SAML2AuthnRequestBuilder authnRequestBuilder, BaseSAML2BindingBuilder binding) throws ProcessingException, ConfigurationException, IOException {
+                Document document = authnRequestBuilder.toDocument();
+                SamlDeployment.Binding samlBinding = deployment.getIDP().getSingleSignOnService().getRequestBinding();
+                SamlUtil.sendSaml(true, httpFacade, deployment.getIDP().getSingleSignOnService().getRequestBindingUrl(), binding, document, samlBinding);
+            }
+        };
+    }
+
+    protected boolean verifySSL() {
+        if (!facade.getRequest().isSecure() && deployment.getSslRequired().isRequired(facade.getRequest().getRemoteAddr())) {
+            log.warn("SSL is required to authenticate");
+            return true;
+        }
+        return false;
+    }
+
+    public void verifyPostBindingSignature(Document document, PublicKey publicKey) throws VerificationException {
+        SAML2Signature saml2Signature = new SAML2Signature();
+        try {
+            if (!saml2Signature.validate(document, publicKey)) {
+                throw new VerificationException("Invalid signature on document");
+            }
+        } catch (ProcessingException e) {
+            throw new VerificationException("Error validating signature", e);
+        }
+    }
+
+    public void verifyRedirectBindingSignature(PublicKey publicKey, String paramKey) throws VerificationException {
+        String request = facade.getRequest().getQueryParamValue(paramKey);
+        String algorithm = facade.getRequest().getQueryParamValue(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY);
+        String signature = facade.getRequest().getQueryParamValue(GeneralConstants.SAML_SIGNATURE_REQUEST_KEY);
+        String decodedAlgorithm = facade.getRequest().getQueryParamValue(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY);
+
+        if (request == null) {
+            throw new VerificationException("SAML Request was null");
+        }
+        if (algorithm == null) throw new VerificationException("SigAlg was null");
+        if (signature == null) throw new VerificationException("Signature was null");
+
+        // Shibboleth doesn't sign the document for redirect binding.
+        // todo maybe a flag?
+
+        String relayState = facade.getRequest().getQueryParamValue(GeneralConstants.RELAY_STATE);
+        KeycloakUriBuilder builder = KeycloakUriBuilder.fromPath("/")
+                .queryParam(paramKey, request);
+        if (relayState != null) {
+            builder.queryParam(GeneralConstants.RELAY_STATE, relayState);
+        }
+        builder.queryParam(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY, algorithm);
+        String rawQuery = builder.build().getRawQuery();
+
+        try {
+            //byte[] decodedSignature = RedirectBindingUtil.urlBase64Decode(signature);
+            byte[] decodedSignature = Base64.decode(signature);
+
+            SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.getFromXmlMethod(decodedAlgorithm);
+            Signature validator = signatureAlgorithm.createSignature(); // todo plugin signature alg
+            validator.initVerify(publicKey);
+            validator.update(rawQuery.getBytes("UTF-8"));
+            if (!validator.verify(decodedSignature)) {
+                throw new VerificationException("Invalid query param signature");
+            }
+        } catch (Exception e) {
+            throw new VerificationException(e);
+        }
+    }
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/profile/ecp/EcpAuthenticationHandler.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/profile/ecp/EcpAuthenticationHandler.java
new file mode 100644
index 0000000..5fa99a0
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/profile/ecp/EcpAuthenticationHandler.java
@@ -0,0 +1,146 @@
+package org.keycloak.adapters.saml.profile.ecp;
+
+import org.keycloak.adapters.saml.AbstractInitiateLogin;
+import org.keycloak.adapters.saml.OnSessionCreated;
+import org.keycloak.adapters.saml.SamlDeployment;
+import org.keycloak.adapters.saml.SamlSessionStore;
+import org.keycloak.adapters.saml.profile.AbstractSamlAuthenticationHandler;
+import org.keycloak.adapters.saml.profile.SamlAuthenticationHandler;
+import org.keycloak.adapters.saml.profile.SamlInvocationContext;
+import org.keycloak.adapters.spi.AuthOutcome;
+import org.keycloak.adapters.spi.HttpFacade;
+import org.keycloak.dom.saml.v2.protocol.LogoutRequestType;
+import org.keycloak.saml.BaseSAML2BindingBuilder;
+import org.keycloak.saml.SAML2AuthnRequestBuilder;
+import org.keycloak.saml.common.constants.JBossSAMLConstants;
+import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
+import org.keycloak.saml.processing.core.saml.v2.util.DocumentUtil;
+import org.keycloak.saml.processing.web.util.PostBindingUtil;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import javax.xml.soap.MessageFactory;
+import javax.xml.soap.SOAPBody;
+import javax.xml.soap.SOAPEnvelope;
+import javax.xml.soap.SOAPException;
+import javax.xml.soap.SOAPHeader;
+import javax.xml.soap.SOAPHeaderElement;
+import javax.xml.soap.SOAPMessage;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class EcpAuthenticationHandler extends AbstractSamlAuthenticationHandler {
+
+    public static final String PAOS_HEADER = "PAOS";
+    public static final String PAOS_CONTENT_TYPE = "application/vnd.paos+xml";
+    private static final String NS_PREFIX_PROFILE_ECP = "ecp";
+    private static final String NS_PREFIX_SAML_PROTOCOL = "samlp";
+    private static final String NS_PREFIX_SAML_ASSERTION = "saml";
+    private static final String NS_PREFIX_PAOS_BINDING = "paos";
+
+    public static boolean canHandle(HttpFacade httpFacade) {
+        HttpFacade.Request request = httpFacade.getRequest();
+        String acceptHeader = request.getHeader("Accept");
+        String contentTypeHeader = request.getHeader("Content-Type");
+
+        return (acceptHeader != null && acceptHeader.contains(PAOS_CONTENT_TYPE) && request.getHeader(PAOS_HEADER) != null)
+                || (contentTypeHeader != null && contentTypeHeader.contains(PAOS_CONTENT_TYPE));
+    }
+
+    public static SamlAuthenticationHandler create(HttpFacade facade, SamlDeployment deployment, SamlSessionStore sessionStore) {
+        return new EcpAuthenticationHandler(facade, deployment, sessionStore);
+    }
+
+    private  EcpAuthenticationHandler(HttpFacade facade, SamlDeployment deployment, SamlSessionStore sessionStore) {
+        super(facade, deployment, sessionStore);
+    }
+
+    @Override
+    protected AuthOutcome logoutRequest(LogoutRequestType request, String relayState) {
+        throw new RuntimeException("Not supported.");
+    }
+
+
+    @Override
+    public AuthOutcome handle(OnSessionCreated onCreateSession) {
+        String header = facade.getRequest().getHeader(PAOS_HEADER);
+
+        if (header != null) {
+            return doHandle(new SamlInvocationContext(), onCreateSession);
+        } else {
+            try {
+                MessageFactory messageFactory = MessageFactory.newInstance();
+                SOAPMessage soapMessage = messageFactory.createMessage(null, facade.getRequest().getInputStream());
+                SOAPBody soapBody = soapMessage.getSOAPBody();
+                Node authnRequestNode = soapBody.getFirstChild();
+                Document document = DocumentUtil.createDocument();
+
+                document.appendChild(document.importNode(authnRequestNode, true));
+
+                String samlResponse = PostBindingUtil.base64Encode(DocumentUtil.asString(document));
+
+                return doHandle(new SamlInvocationContext(null, samlResponse, null), onCreateSession);
+            } catch (Exception e) {
+                throw new RuntimeException("Error creating fault message.", e);
+            }
+        }
+    }
+
+    @Override
+    protected AbstractInitiateLogin createChallenge() {
+        return new AbstractInitiateLogin(deployment, sessionStore) {
+            @Override
+            protected void sendAuthnRequest(HttpFacade httpFacade, SAML2AuthnRequestBuilder authnRequestBuilder, BaseSAML2BindingBuilder binding) {
+                try {
+                    MessageFactory messageFactory = MessageFactory.newInstance();
+                    SOAPMessage message = messageFactory.createMessage();
+
+                    SOAPEnvelope envelope = message.getSOAPPart().getEnvelope();
+
+                    envelope.addNamespaceDeclaration(NS_PREFIX_SAML_ASSERTION, JBossSAMLURIConstants.ASSERTION_NSURI.get());
+                    envelope.addNamespaceDeclaration(NS_PREFIX_SAML_PROTOCOL, JBossSAMLURIConstants.PROTOCOL_NSURI.get());
+                    envelope.addNamespaceDeclaration(NS_PREFIX_PAOS_BINDING, JBossSAMLURIConstants.PAOS_BINDING.get());
+                    envelope.addNamespaceDeclaration(NS_PREFIX_PROFILE_ECP, JBossSAMLURIConstants.ECP_PROFILE.get());
+
+                    createPaosRequestHeader(envelope);
+                    createEcpRequestHeader(envelope);
+
+                    SOAPBody body = envelope.getBody();
+
+                    body.addDocument(binding.postBinding(authnRequestBuilder.toDocument()).getDocument());
+
+                    message.writeTo(httpFacade.getResponse().getOutputStream());
+                } catch (Exception e) {
+                    throw new RuntimeException("Could not create AuthnRequest.", e);
+                }
+            }
+
+            private void createEcpRequestHeader(SOAPEnvelope envelope) throws SOAPException {
+                SOAPHeader headers = envelope.getHeader();
+                SOAPHeaderElement ecpRequestHeader = headers.addHeaderElement(envelope.createQName(JBossSAMLConstants.REQUEST.get(), NS_PREFIX_PROFILE_ECP));
+
+                ecpRequestHeader.setMustUnderstand(true);
+                ecpRequestHeader.setActor("http://schemas.xmlsoap.org/soap/actor/next");
+                ecpRequestHeader.addAttribute(envelope.createName("ProviderName"), deployment.getEntityID());
+                ecpRequestHeader.addAttribute(envelope.createName("IsPassive"), "0");
+                ecpRequestHeader.addChildElement(envelope.createQName("Issuer", "saml")).setValue(deployment.getEntityID());
+                ecpRequestHeader.addChildElement(envelope.createQName("IDPList", "samlp"))
+                        .addChildElement(envelope.createQName("IDPEntry", "samlp"))
+                        .addAttribute(envelope.createName("ProviderID"), deployment.getIDP().getEntityID())
+                        .addAttribute(envelope.createName("Name"), deployment.getIDP().getEntityID())
+                        .addAttribute(envelope.createName("Loc"), deployment.getIDP().getSingleSignOnService().getRequestBindingUrl());
+            }
+
+            private void createPaosRequestHeader(SOAPEnvelope envelope) throws SOAPException {
+                SOAPHeader headers = envelope.getHeader();
+                SOAPHeaderElement paosRequestHeader = headers.addHeaderElement(envelope.createQName(JBossSAMLConstants.REQUEST.get(), NS_PREFIX_PAOS_BINDING));
+
+                paosRequestHeader.setMustUnderstand(true);
+                paosRequestHeader.setActor("http://schemas.xmlsoap.org/soap/actor/next");
+                paosRequestHeader.addAttribute(envelope.createName("service"), JBossSAMLURIConstants.ECP_PROFILE.get());
+                paosRequestHeader.addAttribute(envelope.createName("responseConsumerURL"), deployment.getAssertionConsumerServiceUrl());
+            }
+        };
+    }
+}
\ No newline at end of file
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/profile/SamlAuthenticationHandler.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/profile/SamlAuthenticationHandler.java
new file mode 100644
index 0000000..4f499c7
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/profile/SamlAuthenticationHandler.java
@@ -0,0 +1,13 @@
+package org.keycloak.adapters.saml.profile;
+
+import org.keycloak.adapters.saml.OnSessionCreated;
+import org.keycloak.adapters.spi.AuthChallenge;
+import org.keycloak.adapters.spi.AuthOutcome;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface SamlAuthenticationHandler {
+    AuthOutcome handle(OnSessionCreated onCreateSession);
+    AuthChallenge getChallenge();
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/profile/SamlInvocationContext.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/profile/SamlInvocationContext.java
new file mode 100644
index 0000000..1155b0d
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/profile/SamlInvocationContext.java
@@ -0,0 +1,37 @@
+package org.keycloak.adapters.saml.profile;
+
+import org.keycloak.adapters.saml.SamlDeployment;
+import org.keycloak.adapters.saml.SamlSessionStore;
+import org.keycloak.adapters.spi.HttpFacade;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class SamlInvocationContext {
+
+    private String samlRequest;
+    private String samlResponse;
+    private String relayState;
+
+    public SamlInvocationContext() {
+        this(null, null, null);
+    }
+
+    public SamlInvocationContext(String samlRequest, String samlResponse, String relayState) {
+        this.samlRequest = samlRequest;
+        this.samlResponse = samlResponse;
+        this.relayState = relayState;
+    }
+
+    public String getSamlRequest() {
+        return this.samlRequest;
+    }
+
+    public String getSamlResponse() {
+        return this.samlResponse;
+    }
+
+    public String getRelayState() {
+        return this.relayState;
+    }
+}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/profile/webbrowsersso/WebBrowserSsoAuthenticationHandler.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/profile/webbrowsersso/WebBrowserSsoAuthenticationHandler.java
new file mode 100644
index 0000000..f3e98e5
--- /dev/null
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/profile/webbrowsersso/WebBrowserSsoAuthenticationHandler.java
@@ -0,0 +1,111 @@
+package org.keycloak.adapters.saml.profile.webbrowsersso;
+
+import org.keycloak.adapters.saml.OnSessionCreated;
+import org.keycloak.adapters.saml.SamlDeployment;
+import org.keycloak.adapters.saml.SamlSession;
+import org.keycloak.adapters.saml.SamlSessionStore;
+import org.keycloak.adapters.saml.SamlUtil;
+import org.keycloak.adapters.saml.profile.AbstractSamlAuthenticationHandler;
+import org.keycloak.adapters.saml.profile.SamlAuthenticationHandler;
+import org.keycloak.adapters.saml.profile.SamlInvocationContext;
+import org.keycloak.adapters.spi.AuthOutcome;
+import org.keycloak.adapters.spi.HttpFacade;
+import org.keycloak.dom.saml.v2.protocol.LogoutRequestType;
+import org.keycloak.saml.BaseSAML2BindingBuilder;
+import org.keycloak.saml.SAML2LogoutRequestBuilder;
+import org.keycloak.saml.SAML2LogoutResponseBuilder;
+import org.keycloak.saml.common.constants.GeneralConstants;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class WebBrowserSsoAuthenticationHandler extends AbstractSamlAuthenticationHandler {
+
+    public static SamlAuthenticationHandler create(HttpFacade facade, SamlDeployment deployment, SamlSessionStore sessionStore) {
+        return new WebBrowserSsoAuthenticationHandler(facade, deployment, sessionStore);
+    }
+
+    private WebBrowserSsoAuthenticationHandler(HttpFacade facade, SamlDeployment deployment, SamlSessionStore sessionStore) {
+        super(facade, deployment, sessionStore);
+    }
+
+    @Override
+    public AuthOutcome handle(OnSessionCreated onCreateSession) {
+        return doHandle(new SamlInvocationContext(facade.getRequest().getFirstParam(GeneralConstants.SAML_REQUEST_KEY),
+                facade.getRequest().getFirstParam(GeneralConstants.SAML_RESPONSE_KEY),
+                facade.getRequest().getFirstParam(GeneralConstants.RELAY_STATE)), onCreateSession);
+    }
+
+    @Override
+    protected AuthOutcome handleRequest() {
+        boolean globalLogout = "true".equals(facade.getRequest().getQueryParamValue("GLO"));
+
+        if (globalLogout) {
+            return globalLogout();
+        }
+
+        return AuthOutcome.AUTHENTICATED;
+    }
+
+    @Override
+    protected AuthOutcome logoutRequest(LogoutRequestType request, String relayState) {
+        if (request.getSessionIndex() == null || request.getSessionIndex().isEmpty()) {
+            sessionStore.logoutByPrincipal(request.getNameID().getValue());
+        } else {
+            sessionStore.logoutBySsoId(request.getSessionIndex());
+        }
+
+        String issuerURL = deployment.getEntityID();
+        SAML2LogoutResponseBuilder builder = new SAML2LogoutResponseBuilder();
+        builder.logoutRequestID(request.getID());
+        builder.destination(deployment.getIDP().getSingleLogoutService().getResponseBindingUrl());
+        builder.issuer(issuerURL);
+        BaseSAML2BindingBuilder binding = new BaseSAML2BindingBuilder().relayState(relayState);
+        if (deployment.getIDP().getSingleLogoutService().signResponse()) {
+            binding.signatureAlgorithm(deployment.getSignatureAlgorithm())
+                    .signWith(deployment.getSigningKeyPair())
+                    .signDocument();
+            if (deployment.getSignatureCanonicalizationMethod() != null)
+                binding.canonicalizationMethod(deployment.getSignatureCanonicalizationMethod());
+        }
+
+
+        try {
+            SamlUtil.sendSaml(false, facade, deployment.getIDP().getSingleLogoutService().getResponseBindingUrl(), binding, builder.buildDocument(),
+                    deployment.getIDP().getSingleLogoutService().getResponseBinding());
+        } catch (Exception e) {
+            log.error("Could not send logout response SAML request", e);
+            return AuthOutcome.FAILED;
+        }
+        return AuthOutcome.NOT_ATTEMPTED;
+    }
+
+    private AuthOutcome globalLogout() {
+        SamlSession account = sessionStore.getAccount();
+        if (account == null) {
+            return AuthOutcome.NOT_ATTEMPTED;
+        }
+        SAML2LogoutRequestBuilder logoutBuilder = new SAML2LogoutRequestBuilder()
+                .assertionExpiration(30)
+                .issuer(deployment.getEntityID())
+                .sessionIndex(account.getSessionIndex())
+                .userPrincipal(account.getPrincipal().getSamlSubject(), account.getPrincipal().getNameIDFormat())
+                .destination(deployment.getIDP().getSingleLogoutService().getRequestBindingUrl());
+        BaseSAML2BindingBuilder binding = new BaseSAML2BindingBuilder();
+        if (deployment.getIDP().getSingleLogoutService().signRequest()) {
+            binding.signWith(deployment.getSigningKeyPair())
+                    .signDocument();
+        }
+
+        binding.relayState("logout");
+
+        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;
+        }
+        return AuthOutcome.NOT_ATTEMPTED;
+    }
+}
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 02097db..cd9affd 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,530 +1,49 @@
 package org.keycloak.adapters.saml;
 
 import org.jboss.logging.Logger;
+import org.keycloak.adapters.saml.profile.SamlAuthenticationHandler;
+import org.keycloak.adapters.saml.profile.ecp.EcpAuthenticationHandler;
+import org.keycloak.adapters.saml.profile.webbrowsersso.WebBrowserSsoAuthenticationHandler;
 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;
-import org.keycloak.dom.saml.v2.assertion.AuthnStatementType;
-import org.keycloak.dom.saml.v2.assertion.NameIDType;
-import org.keycloak.dom.saml.v2.assertion.StatementAbstractType;
-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;
-import org.keycloak.saml.SAMLRequestParser;
-import org.keycloak.saml.SignatureAlgorithm;
-import org.keycloak.saml.common.constants.GeneralConstants;
-import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
-import org.keycloak.saml.common.exceptions.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;
-import org.keycloak.saml.processing.web.util.PostBindingUtil;
-import org.w3c.dom.Document;
-import org.w3c.dom.Node;
-
-import java.net.URI;
-import java.security.PublicKey;
-import java.security.Signature;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
 public abstract class SamlAuthenticator {
+
     protected static Logger log = Logger.getLogger(SamlAuthenticator.class);
 
-    protected HttpFacade facade;
-    protected AuthChallenge challenge;
-    protected SamlDeployment deployment;
-    protected SamlSessionStore sessionStore;
+    private final SamlAuthenticationHandler handler;
 
-    public SamlAuthenticator(HttpFacade facade, SamlDeployment deployment, SamlSessionStore sessionStore) {
-        this.facade = facade;
-        this.deployment = deployment;
-        this.sessionStore = sessionStore;
+    public SamlAuthenticator(final HttpFacade facade, final SamlDeployment deployment, final SamlSessionStore sessionStore) {
+        this.handler = createAuthenticationHandler(facade, deployment, sessionStore);
     }
 
     public AuthChallenge getChallenge() {
-        return challenge;
+        return this.handler.getChallenge();
     }
 
     public AuthOutcome authenticate() {
-
-
-        String samlRequest = facade.getRequest().getFirstParam(GeneralConstants.SAML_REQUEST_KEY);
-        String samlResponse = facade.getRequest().getFirstParam(GeneralConstants.SAML_RESPONSE_KEY);
-        String relayState = facade.getRequest().getFirstParam(GeneralConstants.RELAY_STATE);
-        boolean globalLogout = "true".equals(facade.getRequest().getQueryParamValue("GLO"));
-        if (samlRequest != null) {
-            return handleSamlRequest(samlRequest, relayState);
-        } else if (samlResponse != null) {
-            return handleSamlResponse(samlResponse, relayState);
-        } else if (sessionStore.isLoggedIn()) {
-            if (globalLogout) {
-                return globalLogout();
+        log.debugf("SamlAuthenticator is using handler [%s]", this.handler);
+        return this.handler.handle(new OnSessionCreated() {
+            @Override
+            public void onSessionCreated(SamlSession samlSession) {
+                completeAuthentication(samlSession);
             }
-            if (verifySSL()) return AuthOutcome.FAILED;
-            log.debug("AUTHENTICATED: was cached");
-            return AuthOutcome.AUTHENTICATED;
-        }
-        return initiateLogin();
+        });
     }
 
-    protected AuthOutcome globalLogout() {
-        SamlSession account = sessionStore.getAccount();
-        if (account == null) {
-            return AuthOutcome.NOT_ATTEMPTED;
-        }
-        SAML2LogoutRequestBuilder logoutBuilder = new SAML2LogoutRequestBuilder()
-                .assertionExpiration(30)
-                .issuer(deployment.getEntityID())
-                .sessionIndex(account.getSessionIndex())
-                .userPrincipal(account.getPrincipal().getSamlSubject(), account.getPrincipal().getNameIDFormat())
-                .destination(deployment.getIDP().getSingleLogoutService().getRequestBindingUrl());
-        BaseSAML2BindingBuilder binding = new BaseSAML2BindingBuilder();
-        if (deployment.getIDP().getSingleLogoutService().signRequest()) {
-            binding.signWith(deployment.getSigningKeyPair())
-                    .signDocument();
-        }
-
-        binding.relayState("logout");
+    protected abstract void completeAuthentication(SamlSession samlSession);
 
-        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;
+    private SamlAuthenticationHandler createAuthenticationHandler(HttpFacade facade, SamlDeployment deployment, SamlSessionStore sessionStore) {
+        if (EcpAuthenticationHandler.canHandle(facade)) {
+            return EcpAuthenticationHandler.create(facade, deployment, sessionStore);
         }
-        return AuthOutcome.NOT_ATTEMPTED;
-    }
 
-    protected AuthOutcome handleSamlRequest(String samlRequest, String relayState) {
-        SAMLDocumentHolder holder = null;
-        boolean postBinding = false;
-        String requestUri = facade.getRequest().getURI();
-        if (facade.getRequest().getMethod().equalsIgnoreCase("GET")) {
-            // strip out query params
-            int index = requestUri.indexOf('?');
-            if (index > -1) {
-                requestUri = requestUri.substring(0, index);
-            }
-            holder = SAMLRequestParser.parseRequestRedirectBinding(samlRequest);
-        } else {
-            postBinding = true;
-            holder = SAMLRequestParser.parseRequestPostBinding(samlRequest);
-        }
-        RequestAbstractType requestAbstractType = (RequestAbstractType) holder.getSamlObject();
-        if (!requestUri.equals(requestAbstractType.getDestination().toString())) {
-            log.error("expected destination '" + requestUri + "' got '" + requestAbstractType.getDestination() + "'");
-            return AuthOutcome.FAILED;
-        }
-
-        if (requestAbstractType instanceof LogoutRequestType) {
-            if (deployment.getIDP().getSingleLogoutService().validateRequestSignature()) {
-                try {
-                    validateSamlSignature(holder, postBinding, GeneralConstants.SAML_REQUEST_KEY);
-                } catch (VerificationException e) {
-                    log.error("Failed to verify saml request signature", e);
-                    return AuthOutcome.FAILED;
-                }
-            }
-            LogoutRequestType logout = (LogoutRequestType) requestAbstractType;
-            return logoutRequest(logout, relayState);
-
-        } else {
-            log.error("unknown SAML request type");
-            return AuthOutcome.FAILED;
-        }
+        // defaults to the web browser sso profile
+        return WebBrowserSsoAuthenticationHandler.create(facade, deployment, sessionStore);
     }
-
-    protected AuthOutcome logoutRequest(LogoutRequestType request, String relayState) {
-        if (request.getSessionIndex() == null || request.getSessionIndex().isEmpty()) {
-            sessionStore.logoutByPrincipal(request.getNameID().getValue());
-        } else {
-            sessionStore.logoutBySsoId(request.getSessionIndex());
-        }
-
-        String issuerURL = deployment.getEntityID();
-        SAML2LogoutResponseBuilder builder = new SAML2LogoutResponseBuilder();
-        builder.logoutRequestID(request.getID());
-        builder.destination(deployment.getIDP().getSingleLogoutService().getResponseBindingUrl());
-        builder.issuer(issuerURL);
-        BaseSAML2BindingBuilder binding = new BaseSAML2BindingBuilder().relayState(relayState);
-        if (deployment.getIDP().getSingleLogoutService().signResponse()) {
-            binding.signatureAlgorithm(deployment.getSignatureAlgorithm())
-                    .signWith(deployment.getSigningKeyPair())
-                    .signDocument();
-            if (deployment.getSignatureCanonicalizationMethod() != null)
-                binding.canonicalizationMethod(deployment.getSignatureCanonicalizationMethod());
-        }
-
-
-        try {
-            SamlUtil.sendSaml(false, facade, deployment.getIDP().getSingleLogoutService().getResponseBindingUrl(), binding, builder.buildDocument(),
-                    deployment.getIDP().getSingleLogoutService().getResponseBinding());
-        } catch (Exception e) {
-            log.error("Could not send logout response SAML request", e);
-            return AuthOutcome.FAILED;
-        }
-        return AuthOutcome.NOT_ATTEMPTED;
-
-    }
-
-
-    protected AuthOutcome handleSamlResponse(String samlResponse, String relayState) {
-        SAMLDocumentHolder holder = null;
-        boolean postBinding = false;
-        String requestUri = facade.getRequest().getURI();
-        if (facade.getRequest().getMethod().equalsIgnoreCase("GET")) {
-            int index = requestUri.indexOf('?');
-            if (index > -1) {
-                requestUri = requestUri.substring(0, index);
-            }
-            holder = extractRedirectBindingResponse(samlResponse);
-        } else {
-            postBinding = true;
-            holder = extractPostBindingResponse(samlResponse);
-        }
-        final StatusResponseType statusResponse = (StatusResponseType) holder.getSamlObject();
-        // validate destination
-        if (!requestUri.equals(statusResponse.getDestination())) {
-            log.error("Request URI does not match SAML request destination");
-            return AuthOutcome.FAILED;
-        }
-        
-        if (statusResponse instanceof ResponseType) {            
-            try {
-                if (deployment.getIDP().getSingleSignOnService().validateResponseSignature()) {
-                    try {
-                        validateSamlSignature(holder, postBinding, GeneralConstants.SAML_RESPONSE_KEY);
-                    } catch (VerificationException e) {
-                        log.error("Failed to verify saml response signature", e);
-
-                        challenge = new AuthChallenge() {
-                            @Override
-                            public boolean challenge(HttpFacade exchange) {
-                                SamlAuthenticationError error = new SamlAuthenticationError(SamlAuthenticationError.Reason.INVALID_SIGNATURE);
-                                exchange.getRequest().setError(error);
-                                exchange.getResponse().sendError(403);
-                                return true;
-                            }
-
-                            @Override
-                            public int getResponseCode() {
-                                return 403;
-                            }
-                        };
-                        return AuthOutcome.FAILED;
-                    }
-                }
-                return handleLoginResponse((ResponseType) statusResponse);
-            } finally {
-                sessionStore.setCurrentAction(SamlSessionStore.CurrentAction.NONE);
-            }
-
-        } else {
-            if (sessionStore.isLoggingOut()) {
-                try {
-                    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 {
-                    // 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) {
-                            SamlAuthenticationError error = new SamlAuthenticationError(SamlAuthenticationError.Reason.ERROR_STATUS, statusResponse);
-                            exchange.getRequest().setError(error);
-                            exchange.getResponse().sendError(403);
-                            return true;
-                        }
-
-                        @Override
-                        public int getResponseCode() {
-                            return 403;
-                        }
-                    };
-                    return AuthOutcome.FAILED;
-                } finally {
-                    sessionStore.setCurrentAction(SamlSessionStore.CurrentAction.NONE);
-                }
-            }
-            return AuthOutcome.NOT_ATTEMPTED;
-        }
-
-    }
-
-    private void validateSamlSignature(SAMLDocumentHolder holder, boolean postBinding, String paramKey) throws VerificationException {
-        if (postBinding) {
-            verifyPostBindingSignature(holder.getSamlDocument(), deployment.getIDP().getSignatureValidationKey());
-        } else {
-            verifyRedirectBindingSignature(deployment.getIDP().getSignatureValidationKey(), paramKey);
-        }
-    }
-
-    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());
-            if (AssertionUtil.hasExpired(assertion)) {
-                return initiateLogin();
-            }
-        } catch (Exception e) {
-            log.error("Error extracting SAML assertion: " + e.getMessage());
-            challenge = new AuthChallenge() {
-                @Override
-                public boolean challenge(HttpFacade exchange) {
-                    SamlAuthenticationError error = new SamlAuthenticationError(SamlAuthenticationError.Reason.EXTRACTION_FAILURE);
-                    exchange.getRequest().setError(error);
-                    exchange.getResponse().sendError(403);
-                    return true;
-                }
-
-                @Override
-                public int getResponseCode() {
-                    return 403;
-                }
-            };
-        }
-
-        SubjectType subject = assertion.getSubject();
-        SubjectType.STSubType subType = subject.getSubType();
-        NameIDType subjectNameID = (NameIDType) subType.getBaseID();
-        String principalName = subjectNameID.getValue();
-
-        final Set<String> roles = new HashSet<>();
-        MultivaluedHashMap<String, String> attributes = new MultivaluedHashMap<>();
-        MultivaluedHashMap<String, String> friendlyAttributes = new MultivaluedHashMap<>();
-
-        Set<StatementAbstractType> statements = assertion.getStatements();
-        for (StatementAbstractType statement : statements) {
-            if (statement instanceof AttributeStatementType) {
-                AttributeStatementType attributeStatement = (AttributeStatementType) statement;
-                List<AttributeStatementType.ASTChoiceType> attList = attributeStatement.getAttributes();
-                for (AttributeStatementType.ASTChoiceType obj : attList) {
-                    AttributeType attr = obj.getAttribute();
-                    if (isRole(attr)) {
-                        List<Object> attributeValues = attr.getAttributeValue();
-                        if (attributeValues != null) {
-                            for (Object attrValue : attributeValues) {
-                                String role = getAttributeValue(attrValue);
-                                log.debugv("Add role: {0}", role);
-                                roles.add(role);
-                            }
-                        }
-                    } else {
-                        List<Object> attributeValues = attr.getAttributeValue();
-                        if (attributeValues != null) {
-                            for (Object attrValue : attributeValues) {
-                                String value = getAttributeValue(attrValue);
-                                if (attr.getName() != null) {
-                                    attributes.add(attr.getName(), value);
-                                }
-                                if (attr.getFriendlyName() != null) {
-                                    friendlyAttributes.add(attr.getFriendlyName(), value);
-                                }
-                            }
-                        }
-                    }
-
-                }
-            }
-        }
-        if (deployment.getPrincipalNamePolicy() == SamlDeployment.PrincipalNamePolicy.FROM_ATTRIBUTE) {
-            if (deployment.getPrincipalAttributeName() != null) {
-                String attribute = attributes.getFirst(deployment.getPrincipalAttributeName());
-                if (attribute != null) principalName = attribute;
-                else {
-                    attribute = friendlyAttributes.getFirst(deployment.getPrincipalAttributeName());
-                    if (attribute != null) principalName = attribute;
-                }
-            }
-        }
-
-        AuthnStatementType authn = null;
-        for (Object statement : assertion.getStatements()) {
-            if (statement instanceof AuthnStatementType) {
-                authn = (AuthnStatementType) statement;
-                break;
-            }
-        }
-
-
-        URI nameFormat = subjectNameID.getFormat();
-        String nameFormatString = nameFormat == null ? JBossSAMLURIConstants.NAMEID_FORMAT_UNSPECIFIED.get() : nameFormat.toString();
-        final SamlPrincipal principal = new SamlPrincipal(assertion, principalName, principalName, nameFormatString, attributes, friendlyAttributes);
-        String index = authn == null ? null : authn.getSessionIndex();
-        final String sessionIndex = index;
-        SamlSession account = new SamlSession(principal, roles, sessionIndex);
-        sessionStore.saveAccount(account);
-        completeAuthentication(account);
-
-
-        // redirect to original request, it will be restored
-        String redirectUri = sessionStore.getRedirectUri();
-        if (redirectUri != null) {
-            facade.getResponse().setHeader("Location", redirectUri);
-            facade.getResponse().setStatus(302);
-            facade.getResponse().end();
-        } else {
-            log.debug("IDP initiated invocation");
-        }
-        log.debug("AUTHENTICATED authn");
-
-        return AuthOutcome.AUTHENTICATED;
-    }
-
-    protected abstract void completeAuthentication(SamlSession account);
-
-    private String getAttributeValue(Object attrValue) {
-        String value = null;
-        if (attrValue instanceof String) {
-            value = (String) attrValue;
-        } else if (attrValue instanceof Node) {
-            Node roleNode = (Node) attrValue;
-            value = roleNode.getFirstChild().getNodeValue();
-        } else if (attrValue instanceof NameIDType) {
-            NameIDType nameIdType = (NameIDType) attrValue;
-            value = nameIdType.getValue();
-        } else {
-            log.warn("Unable to extract unknown SAML assertion attribute value type: " + attrValue.getClass().getName());
-        }
-        return value;
-    }
-
-    protected boolean isRole(AttributeType attribute) {
-        return (attribute.getName() != null && deployment.getRoleAttributeNames().contains(attribute.getName())) || (attribute.getFriendlyName() != null && deployment.getRoleAttributeNames().contains(attribute.getFriendlyName()));
-    }
-
-    protected AuthOutcome handleLogoutResponse(SAMLDocumentHolder holder, StatusResponseType responseType, String relayState) {
-        boolean loggedIn = sessionStore.isLoggedIn();
-        if (!loggedIn || !"logout".equals(relayState)) {
-            return AuthOutcome.NOT_ATTEMPTED;
-        }
-        sessionStore.logoutAccount();
-        return AuthOutcome.LOGGED_OUT;
-    }
-
-    protected SAMLDocumentHolder extractRedirectBindingResponse(String response) {
-        return SAMLRequestParser.parseRequestRedirectBinding(response);
-    }
-
-    
-    protected SAMLDocumentHolder extractPostBindingResponse(String response) {
-        byte[] samlBytes = PostBindingUtil.base64Decode(response);
-        return SAMLRequestParser.parseResponseDocument(samlBytes);
-    }
-
-
-    protected AuthOutcome initiateLogin() {
-        challenge = new InitiateLogin(deployment, sessionStore);
-        return AuthOutcome.NOT_ATTEMPTED;
-    }
-
-    protected boolean verifySSL() {
-        if (!facade.getRequest().isSecure() && deployment.getSslRequired().isRequired(facade.getRequest().getRemoteAddr())) {
-            log.warn("SSL is required to authenticate");
-            return true;
-        }
-        return false;
-    }
-
-    public void verifyPostBindingSignature(Document document, PublicKey publicKey) throws VerificationException {
-        SAML2Signature saml2Signature = new SAML2Signature();
-        try {
-            if (!saml2Signature.validate(document, publicKey)) {
-                throw new VerificationException("Invalid signature on document");
-            }
-        } catch (ProcessingException e) {
-            throw new VerificationException("Error validating signature", e);
-        }
-    }
-
-    public void verifyRedirectBindingSignature(PublicKey publicKey, String paramKey) throws VerificationException {
-        String request = facade.getRequest().getQueryParamValue(paramKey);
-        String algorithm = facade.getRequest().getQueryParamValue(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY);
-        String signature = facade.getRequest().getQueryParamValue(GeneralConstants.SAML_SIGNATURE_REQUEST_KEY);
-        String decodedAlgorithm = facade.getRequest().getQueryParamValue(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY);
-
-        if (request == null) {
-            throw new VerificationException("SAML Request was null");
-        }
-        if (algorithm == null) throw new VerificationException("SigAlg was null");
-        if (signature == null) throw new VerificationException("Signature was null");
-
-        // Shibboleth doesn't sign the document for redirect binding.
-        // todo maybe a flag?
-
-        String relayState = facade.getRequest().getQueryParamValue(GeneralConstants.RELAY_STATE);
-        KeycloakUriBuilder builder = KeycloakUriBuilder.fromPath("/")
-                .queryParam(paramKey, request);
-        if (relayState != null) {
-            builder.queryParam(GeneralConstants.RELAY_STATE, relayState);
-        }
-        builder.queryParam(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY, algorithm);
-        String rawQuery = builder.build().getRawQuery();
-
-        try {
-            //byte[] decodedSignature = RedirectBindingUtil.urlBase64Decode(signature);
-            byte[] decodedSignature = Base64.decode(signature);
-
-            SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.getFromXmlMethod(decodedAlgorithm);
-            Signature validator = signatureAlgorithm.createSignature(); // todo plugin signature alg
-            validator.initVerify(publicKey);
-            validator.update(rawQuery.getBytes("UTF-8"));
-            if (!validator.verify(decodedSignature)) {
-                throw new VerificationException("Invalid query param signature");
-            }
-        } catch (Exception e) {
-            throw new VerificationException(e);
-        }
-    }
-
-
-}
+}
\ No newline at end of file
diff --git a/saml/saml-core/src/main/java/org/keycloak/saml/common/constants/JBossSAMLConstants.java b/saml/saml-core/src/main/java/org/keycloak/saml/common/constants/JBossSAMLConstants.java
index fb90e17..219042b 100755
--- a/saml/saml-core/src/main/java/org/keycloak/saml/common/constants/JBossSAMLConstants.java
+++ b/saml/saml-core/src/main/java/org/keycloak/saml/common/constants/JBossSAMLConstants.java
@@ -65,7 +65,8 @@ public enum JBossSAMLConstants {
             "XACMLAuthzDecisionQuery"), XACML_AUTHZ_DECISION_QUERY_TYPE("XACMLAuthzDecisionQueryType"), XACML_AUTHZ_DECISION_STATEMENT_TYPE(
             "XACMLAuthzDecisionStatementType"), HTTP_POST_BINDING("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"), ONE_TIME_USE ("OneTimeUse"),
             UNSOLICITED_RESPONSE_TARGET("TARGET"), UNSOLICITED_RESPONSE_SAML_VERSION("SAML_VERSION"), UNSOLICITED_RESPONSE_SAML_BINDING("SAML_BINDING"),
-            ROLE_DESCRIPTOR("RoleDescriptor");
+            ROLE_DESCRIPTOR("RoleDescriptor"),
+            REQUEST_AUTHENTICATED("RequestAuthenticated");
 
     private String name;
 
diff --git a/saml/saml-core/src/main/java/org/keycloak/saml/common/constants/JBossSAMLURIConstants.java b/saml/saml-core/src/main/java/org/keycloak/saml/common/constants/JBossSAMLURIConstants.java
index 3833c56..ad7bee5 100755
--- a/saml/saml-core/src/main/java/org/keycloak/saml/common/constants/JBossSAMLURIConstants.java
+++ b/saml/saml-core/src/main/java/org/keycloak/saml/common/constants/JBossSAMLURIConstants.java
@@ -73,12 +73,15 @@ public enum JBossSAMLURIConstants {
             "urn:oasis:names:tc:SAML:2.0:nameid-format:entity"),
 
     PROTOCOL_NSURI("urn:oasis:names:tc:SAML:2.0:protocol"),
+    ECP_PROFILE("urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp"),
+    PAOS_BINDING("urn:liberty:paos:2003-08"),
 
     SIGNATURE_DSA_SHA1("http://www.w3.org/2000/09/xmldsig#dsa-sha1"), SIGNATURE_RSA_SHA1(
             "http://www.w3.org/2000/09/xmldsig#rsa-sha1"),
 
-    SAML_HTTP_POST_BINDING("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"), SAML_HTTP_REDIRECT_BINDING(
-            "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"),
+    SAML_HTTP_POST_BINDING("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"),
+    SAML_HTTP_SOAP_BINDING("urn:oasis:names:tc:SAML:2.0:bindings:SOAP"),
+    SAML_HTTP_REDIRECT_BINDING("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"),
 
     SAML_11_NS("urn:oasis:names:tc:SAML:1.0:assertion"),
 
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/profile/ecp/authenticator/HttpBasicAuthenticator.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/profile/ecp/authenticator/HttpBasicAuthenticator.java
new file mode 100644
index 0000000..2e15502
--- /dev/null
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/profile/ecp/authenticator/HttpBasicAuthenticator.java
@@ -0,0 +1,174 @@
+package org.keycloak.protocol.saml.profile.ecp.authenticator;
+
+import org.jboss.resteasy.spi.HttpRequest;
+import org.keycloak.Config;
+import org.keycloak.authentication.AuthenticationFlowContext;
+import org.keycloak.authentication.AuthenticationFlowError;
+import org.keycloak.authentication.Authenticator;
+import org.keycloak.authentication.AuthenticatorFactory;
+import org.keycloak.common.util.Base64;
+import org.keycloak.events.Errors;
+import org.keycloak.models.*;
+import org.keycloak.models.AuthenticationExecutionModel.Requirement;
+import org.keycloak.provider.ProviderConfigProperty;
+import org.keycloak.representations.AccessTokenResponse;
+import org.keycloak.representations.idm.CredentialRepresentation;
+
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class HttpBasicAuthenticator implements AuthenticatorFactory {
+
+    public static final String PROVIDER_ID = "http-basic-authenticator";
+
+    @Override
+    public String getDisplayType() {
+        return null;
+    }
+
+    @Override
+    public String getReferenceCategory() {
+        return null;
+    }
+
+    @Override
+    public boolean isConfigurable() {
+        return false;
+    }
+
+    @Override
+    public Requirement[] getRequirementChoices() {
+        return new Requirement[0];
+    }
+
+    @Override
+    public boolean isUserSetupAllowed() {
+        return false;
+    }
+
+    @Override
+    public String getHelpText() {
+        return null;
+    }
+
+    @Override
+    public List<ProviderConfigProperty> getConfigProperties() {
+        return null;
+    }
+
+    @Override
+    public Authenticator create(KeycloakSession session) {
+        return new Authenticator() {
+
+            private static final String BASIC = "Basic";
+            private static final String BASIC_PREFIX = BASIC + " ";
+
+            @Override
+            public void authenticate(AuthenticationFlowContext context) {
+                HttpRequest httpRequest = context.getHttpRequest();
+                HttpHeaders httpHeaders = httpRequest.getHttpHeaders();
+                String[] usernameAndPassword = getUsernameAndPassword(httpHeaders);
+
+                context.attempted();
+
+                if (usernameAndPassword != null) {
+                    RealmModel realm = context.getRealm();
+                    UserModel user = context.getSession().users().getUserByUsername(usernameAndPassword[0], realm);
+
+                    if (user != null) {
+                        String password = usernameAndPassword[1];
+                        boolean valid = context.getSession().users().validCredentials(context.getSession(), realm, user, UserCredentialModel.password(password));
+
+                        if (valid) {
+                            context.getClientSession().setAuthenticatedUser(user);
+                            context.success();
+                        } else {
+                            context.getEvent().user(user);
+                            context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
+                            context.failure(AuthenticationFlowError.INVALID_USER, Response.status(Response.Status.UNAUTHORIZED)
+                                    .header(HttpHeaders.WWW_AUTHENTICATE, BASIC_PREFIX + "realm=\"" + realm.getName() + "\"")
+                                    .build());
+                        }
+                    }
+                }
+            }
+
+            private String[] getUsernameAndPassword(HttpHeaders httpHeaders) {
+                List<String> authHeaders = httpHeaders.getRequestHeader(HttpHeaders.AUTHORIZATION);
+
+                if (authHeaders == null || authHeaders.size() == 0) {
+                    return null;
+                }
+
+                String credentials = null;
+
+                for (String authHeader : authHeaders) {
+                    if (authHeader.startsWith(BASIC_PREFIX)) {
+                        String[] split = authHeader.trim().split("\\s+");
+
+                        if (split == null || split.length != 2) return null;
+
+                        credentials = split[1];
+                    }
+                }
+
+                try {
+                    return new String(Base64.decode(credentials)).split(":");
+                } catch (IOException e) {
+                    throw new RuntimeException("Failed to parse credentials.", e);
+                }
+            }
+
+            @Override
+            public void action(AuthenticationFlowContext context) {
+
+            }
+
+            @Override
+            public boolean requiresUser() {
+                return false;
+            }
+
+            @Override
+            public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
+                return false;
+            }
+
+            @Override
+            public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
+
+            }
+
+            @Override
+            public void close() {
+
+            }
+        };
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public String getId() {
+        return PROVIDER_ID;
+    }
+}
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/profile/ecp/SamlEcpProfileProtocolFactory.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/profile/ecp/SamlEcpProfileProtocolFactory.java
new file mode 100644
index 0000000..dba1b29
--- /dev/null
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/profile/ecp/SamlEcpProfileProtocolFactory.java
@@ -0,0 +1,109 @@
+package org.keycloak.protocol.saml.profile.ecp;
+
+import org.keycloak.Config;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.protocol.LoginProtocol;
+import org.keycloak.protocol.saml.JaxrsSAML2BindingBuilder;
+import org.keycloak.protocol.saml.SamlProtocol;
+import org.keycloak.protocol.saml.SamlProtocolFactory;
+import org.keycloak.protocol.saml.profile.ecp.util.Soap;
+import org.keycloak.protocol.saml.profile.ecp.util.Soap.SoapMessageBuilder;
+import org.keycloak.saml.SAML2LogoutResponseBuilder;
+import org.keycloak.saml.common.constants.JBossSAMLConstants;
+import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
+import org.keycloak.saml.common.exceptions.ConfigurationException;
+import org.keycloak.saml.common.exceptions.ProcessingException;
+import org.keycloak.services.managers.AuthenticationManager;
+import org.w3c.dom.Document;
+
+import javax.ws.rs.core.Response;
+import javax.xml.soap.SOAPException;
+import javax.xml.soap.SOAPHeaderElement;
+import java.io.IOException;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class SamlEcpProfileProtocolFactory extends SamlProtocolFactory {
+
+    static final String ID = "saml-ecp-profile";
+
+    private static final String NS_PREFIX_PROFILE_ECP = "ecp";
+    private static final String NS_PREFIX_SAML_PROTOCOL = "samlp";
+    private static final String NS_PREFIX_SAML_ASSERTION = "saml";
+
+    @Override
+    public Object createProtocolEndpoint(RealmModel realm, EventBuilder event, AuthenticationManager authManager) {
+        return new SamlEcpProfileService(realm, event, authManager);
+    }
+
+    @Override
+    public LoginProtocol create(KeycloakSession session) {
+        return new SamlProtocol() {
+            // method created to send a SOAP Binding response instead of a HTTP POST response
+            @Override
+            protected Response buildAuthenticatedResponse(ClientSessionModel clientSession, String redirectUri, Document samlDocument, JaxrsSAML2BindingBuilder bindingBuilder) throws ConfigurationException, ProcessingException, IOException {
+                Document document = bindingBuilder.postBinding(samlDocument).getDocument();
+
+                try {
+                    SoapMessageBuilder messageBuilder = Soap.createMessage()
+                            .addNamespace(NS_PREFIX_SAML_ASSERTION, JBossSAMLURIConstants.ASSERTION_NSURI.get())
+                            .addNamespace(NS_PREFIX_SAML_PROTOCOL, JBossSAMLURIConstants.PROTOCOL_NSURI.get())
+                            .addNamespace(NS_PREFIX_PROFILE_ECP, JBossSAMLURIConstants.ECP_PROFILE.get());
+
+                    createEcpResponseHeader(redirectUri, messageBuilder);
+                    createRequestAuthenticatedHeader(clientSession, messageBuilder);
+
+                    messageBuilder.addToBody(document);
+
+                    return messageBuilder.build();
+                } catch (Exception e) {
+                    throw new RuntimeException("Error while creating SAML response.", e);
+                }
+            }
+
+            private void createRequestAuthenticatedHeader(ClientSessionModel clientSession, SoapMessageBuilder messageBuilder) {
+                ClientModel client = clientSession.getClient();
+
+                if ("true".equals(client.getAttribute(SamlProtocol.SAML_CLIENT_SIGNATURE_ATTRIBUTE))) {
+                    SOAPHeaderElement ecpRequestAuthenticated = messageBuilder.addHeader(JBossSAMLConstants.REQUEST_AUTHENTICATED.get(), NS_PREFIX_PROFILE_ECP);
+
+                    ecpRequestAuthenticated.setMustUnderstand(true);
+                    ecpRequestAuthenticated.setActor("http://schemas.xmlsoap.org/soap/actor/next");
+                }
+            }
+
+            private void createEcpResponseHeader(String redirectUri, SoapMessageBuilder messageBuilder) throws SOAPException {
+                SOAPHeaderElement ecpResponseHeader = messageBuilder.addHeader(JBossSAMLConstants.RESPONSE.get(), NS_PREFIX_PROFILE_ECP);
+
+                ecpResponseHeader.setMustUnderstand(true);
+                ecpResponseHeader.setActor("http://schemas.xmlsoap.org/soap/actor/next");
+                ecpResponseHeader.addAttribute(messageBuilder.createName(JBossSAMLConstants.ASSERTION_CONSUMER_SERVICE_URL.get()), redirectUri);
+            }
+
+            @Override
+            protected Response buildErrorResponse(ClientSessionModel clientSession, JaxrsSAML2BindingBuilder binding, Document document) throws ConfigurationException, ProcessingException, IOException {
+                return Soap.createMessage().addToBody(document).build();
+            }
+
+            @Override
+            protected Response buildLogoutResponse(UserSessionModel userSession, String logoutBindingUri, SAML2LogoutResponseBuilder builder, JaxrsSAML2BindingBuilder binding) throws ConfigurationException, ProcessingException, IOException {
+                return Soap.createFault().reason("Logout not supported.").build();
+            }
+        }.setSession(session);
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+    }
+
+    @Override
+    public String getId() {
+        return ID;
+    }
+}
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/profile/ecp/SamlEcpProfileService.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/profile/ecp/SamlEcpProfileService.java
new file mode 100644
index 0000000..c16b997
--- /dev/null
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/profile/ecp/SamlEcpProfileService.java
@@ -0,0 +1,70 @@
+package org.keycloak.protocol.saml.profile.ecp;
+
+import org.keycloak.dom.saml.v2.protocol.AuthnRequestType;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.models.AuthenticationFlowModel;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.utils.DefaultAuthenticationFlows;
+import org.keycloak.protocol.saml.SamlProtocol;
+import org.keycloak.protocol.saml.SamlService;
+import org.keycloak.protocol.saml.profile.ecp.util.Soap;
+import org.keycloak.services.managers.AuthenticationManager;
+
+import javax.ws.rs.core.Response;
+import java.io.InputStream;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class SamlEcpProfileService extends SamlService {
+
+    public SamlEcpProfileService(RealmModel realm, EventBuilder event, AuthenticationManager authManager) {
+        super(realm, event, authManager);
+    }
+
+    public Response authenticate(InputStream inputStream) {
+        try {
+            return new PostBindingProtocol() {
+                @Override
+                protected String getBindingType(AuthnRequestType requestAbstractType) {
+                    return SamlProtocol.SAML_SOAP_BINDING;
+                }
+
+                @Override
+                protected Response loginRequest(String relayState, AuthnRequestType requestAbstractType, ClientModel client) {
+                    // force passive authentication when executing this profile
+                    requestAbstractType.setIsPassive(true);
+                    requestAbstractType.setDestination(uriInfo.getAbsolutePath());
+                    return super.loginRequest(relayState, requestAbstractType, client);
+                }
+            }.execute(Soap.toSamlHttpPostMessage(inputStream), null, null);
+        } catch (Exception e) {
+            String reason = "Some error occurred while processing the AuthnRequest.";
+            String detail = e.getMessage();
+
+            if (detail == null) {
+                detail = reason;
+            }
+
+            return Soap.createFault().reason(reason).detail(detail).build();
+        }
+    }
+
+    @Override
+    protected String getLoginProtocol() {
+        return SamlEcpProfileProtocolFactory.ID;
+    }
+
+    @Override
+    protected AuthenticationFlowModel getAuthenticationFlow() {
+        for (AuthenticationFlowModel flowModel : realm.getAuthenticationFlows()) {
+            if (flowModel.getAlias().equals(DefaultAuthenticationFlows.SAML_ECP_FLOW)) {
+                return flowModel;
+            }
+        }
+
+        throw new RuntimeException("Could not resolve authentication flow for SAML ECP Profile.");
+    }
+}
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/profile/ecp/util/Soap.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/profile/ecp/util/Soap.java
new file mode 100644
index 0000000..4bdf76a
--- /dev/null
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/profile/ecp/util/Soap.java
@@ -0,0 +1,177 @@
+package org.keycloak.protocol.saml.profile.ecp.util;
+
+import org.keycloak.saml.common.constants.JBossSAMLConstants;
+import org.keycloak.saml.processing.core.saml.v2.util.DocumentUtil;
+import org.keycloak.saml.processing.web.util.PostBindingUtil;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import javax.xml.soap.MessageFactory;
+import javax.xml.soap.Name;
+import javax.xml.soap.SOAPBody;
+import javax.xml.soap.SOAPEnvelope;
+import javax.xml.soap.SOAPException;
+import javax.xml.soap.SOAPFault;
+import javax.xml.soap.SOAPHeaderElement;
+import javax.xml.soap.SOAPMessage;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.util.Locale;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public final class Soap {
+
+    public static SoapFaultBuilder createFault() {
+        return new SoapFaultBuilder();
+    }
+
+    public static SoapMessageBuilder createMessage() {
+        return new SoapMessageBuilder();
+    }
+
+    /**
+     * <p>Returns a string encoded accordingly with the SAML HTTP POST Binding specification based on the
+     * given <code>inputStream</code> which must contain a valid SOAP message.
+     *
+     * <p>The resulting string is based on the Body of the SOAP message, which should map to a valid SAML message.
+     *
+     * @param inputStream the input stream containing a valid SOAP message with a Body that contains a SAML message
+     *
+     * @return a string encoded accordingly with the SAML HTTP POST Binding specification
+     */
+    public static String toSamlHttpPostMessage(InputStream inputStream) {
+        try {
+            MessageFactory messageFactory = MessageFactory.newInstance();
+            SOAPMessage soapMessage = messageFactory.createMessage(null, inputStream);
+            SOAPBody soapBody = soapMessage.getSOAPBody();
+            Node authnRequestNode = soapBody.getFirstChild();
+            Document document = DocumentUtil.createDocument();
+
+            document.appendChild(document.importNode(authnRequestNode, true));
+
+            return PostBindingUtil.base64Encode(DocumentUtil.asString(document));
+        } catch (Exception e) {
+            throw new RuntimeException("Error creating fault message.", e);
+        }
+    }
+
+    public static class SoapMessageBuilder {
+        private final SOAPMessage message;
+        private final SOAPBody body;
+        private final SOAPEnvelope envelope;
+
+        private SoapMessageBuilder() {
+            try {
+                this.message = MessageFactory.newInstance().createMessage();
+                this.envelope = message.getSOAPPart().getEnvelope();
+                this.body = message.getSOAPBody();
+            } catch (Exception e) {
+                throw new RuntimeException("Error creating fault message.", e);
+            }
+        }
+
+        public SoapMessageBuilder addToBody(Document document) {
+            try {
+                this.body.addDocument(document);
+            } catch (SOAPException e) {
+                throw new RuntimeException("Could not add document to SOAP body.", e);
+            }
+            return this;
+        }
+
+        public SoapMessageBuilder addNamespace(String prefix, String ns) {
+            try {
+                envelope.addNamespaceDeclaration(prefix, ns);
+            } catch (SOAPException e) {
+                throw new RuntimeException("Could not add namespace to SOAP Envelope.", e);
+            }
+            return this;
+        }
+
+        public SOAPHeaderElement addHeader(String name, String prefix) {
+            try {
+                return this.envelope.getHeader().addHeaderElement(envelope.createQName(name, prefix));
+            } catch (SOAPException e) {
+                throw new RuntimeException("Could not add SOAP Header.", e);
+            }
+        }
+
+        public Name createName(String name) {
+            try {
+                return this.envelope.createName(name);
+            } catch (SOAPException e) {
+                throw new RuntimeException("Could not create Name.", e);
+            }
+        }
+
+        public Response build() {
+            return build(Status.OK);
+        }
+
+        Response build(Status status) {
+            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+
+            try {
+                this.message.writeTo(outputStream);
+            } catch (Exception e) {
+                throw new RuntimeException("Error while building SOAP Fault.", e);
+            }
+
+            return Response.status(status).entity(outputStream.toByteArray()).build();
+        }
+
+        SOAPMessage getMessage() {
+            return this.message;
+        }
+    }
+
+    public static class SoapFaultBuilder {
+
+        private final SOAPFault fault;
+        private final SoapMessageBuilder messageBuilder;
+
+        private SoapFaultBuilder() {
+            this.messageBuilder = createMessage();
+            try {
+                this.fault = messageBuilder.getMessage().getSOAPBody().addFault();
+            } catch (SOAPException e) {
+                throw new RuntimeException("Could not create SOAP Fault.", e);
+            }
+        }
+
+        public SoapFaultBuilder detail(String detail) {
+            try {
+                this.fault.addDetail().setValue(detail);
+            } catch (SOAPException e) {
+                throw new RuntimeException("Error creating fault message.", e);
+            }
+            return this;
+        }
+
+        public SoapFaultBuilder reason(String reason) {
+            try {
+                this.fault.setFaultString(reason);
+            } catch (SOAPException e) {
+                throw new RuntimeException("Error creating fault message.", e);
+            }
+            return this;
+        }
+
+        public SoapFaultBuilder code(String code) {
+            try {
+                this.fault.setFaultCode(code);
+            } catch (SOAPException e) {
+                throw new RuntimeException("Error creating fault message.", e);
+            }
+            return this;
+        }
+
+        public Response build() {
+            return this.messageBuilder.build(Status.INTERNAL_SERVER_ERROR);
+        }
+    }
+}
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 0bc3ede..07c528d 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
@@ -84,6 +84,7 @@ public class SamlProtocol implements LoginProtocol {
     public static final String SAML_BINDING = "saml_binding";
     public static final String SAML_IDP_INITIATED_LOGIN = "saml_idp_initiated_login";
     public static final String SAML_POST_BINDING = "post";
+    public static final String SAML_SOAP_BINDING = "soap";
     public static final String SAML_REDIRECT_BINDING = "get";
     public static final String SAML_SERVER_SIGNATURE = "saml.server.signature";
     public static final String SAML_ASSERTION_SIGNATURE = "saml.assertion.signature";
@@ -165,11 +166,7 @@ public class SamlProtocol implements LoginProtocol {
                 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());
-                    }
+                    return buildErrorResponse(clientSession, binding, document);
                 } catch (Exception e) {
                     return ErrorPage.error(session, Messages.FAILED_TO_PROCESS_RESPONSE);
                 }
@@ -180,6 +177,14 @@ public class SamlProtocol implements LoginProtocol {
         }
     }
 
+    protected Response buildErrorResponse(ClientSessionModel clientSession, JaxrsSAML2BindingBuilder binding, Document document) throws ConfigurationException, ProcessingException, IOException {
+        if (isPostBinding(clientSession)) {
+            return binding.postBinding(document).response(clientSession.getRedirectUri());
+        } else {
+            return binding.redirectBinding(document).response(clientSession.getRedirectUri());
+        }
+    }
+
     private JBossSAMLURIConstants translateErrorToSAMLStatus(Error error) {
         switch (error) {
         case CANCELLED_BY_USER:
@@ -390,17 +395,21 @@ public class SamlProtocol implements LoginProtocol {
             bindingBuilder.encrypt(publicKey);
         }
         try {
-            if (isPostBinding(clientSession)) {
-                return bindingBuilder.postBinding(samlDocument).response(redirectUri);
-            } else {
-                return bindingBuilder.redirectBinding(samlDocument).response(redirectUri);
-            }
+            return buildAuthenticatedResponse(clientSession, redirectUri, samlDocument, bindingBuilder);
         } catch (Exception e) {
             logger.error("failed", e);
             return ErrorPage.error(session, Messages.FAILED_TO_PROCESS_RESPONSE);
         }
     }
 
+    protected Response buildAuthenticatedResponse(ClientSessionModel clientSession, String redirectUri, Document samlDocument, JaxrsSAML2BindingBuilder bindingBuilder) throws ConfigurationException, ProcessingException, IOException {
+        if (isPostBinding(clientSession)) {
+            return bindingBuilder.postBinding(samlDocument).response(redirectUri);
+        } else {
+            return bindingBuilder.redirectBinding(samlDocument).response(redirectUri);
+        }
+    }
+
     public static boolean requiresRealmSignature(ClientModel client) {
         return "true".equals(client.getAttribute(SAML_SERVER_SIGNATURE));
     }
@@ -544,11 +553,7 @@ public class SamlProtocol implements LoginProtocol {
         }
 
         try {
-            if (isLogoutPostBindingForInitiator(userSession)) {
-                return binding.postBinding(builder.buildDocument()).response(logoutBindingUri);
-            } else {
-                return binding.redirectBinding(builder.buildDocument()).response(logoutBindingUri);
-            }
+            return buildLogoutResponse(userSession, logoutBindingUri, builder, binding);
         } catch (ConfigurationException e) {
             throw new RuntimeException(e);
         } catch (ProcessingException e) {
@@ -558,6 +563,14 @@ public class SamlProtocol implements LoginProtocol {
         }
     }
 
+    protected Response buildLogoutResponse(UserSessionModel userSession, String logoutBindingUri, SAML2LogoutResponseBuilder builder, JaxrsSAML2BindingBuilder binding) throws ConfigurationException, ProcessingException, IOException {
+        if (isLogoutPostBindingForInitiator(userSession)) {
+            return binding.postBinding(builder.buildDocument()).response(logoutBindingUri);
+        } else {
+            return binding.redirectBinding(builder.buildDocument()).response(logoutBindingUri);
+        }
+    }
+
     @Override
     public void backchannelLogout(UserSessionModel userSession, ClientSessionModel clientSession) {
         ClientModel client = clientSession.getClient();
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolFactory.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolFactory.java
index a7a86ed..7dcc866 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolFactory.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolFactory.java
@@ -42,7 +42,7 @@ public class SamlProtocolFactory extends AbstractLoginProtocolFactory {
 
     @Override
     public String getId() {
-        return "saml";
+        return SamlProtocol.LOGIN_PROTOCOL;
     }
 
     @Override
@@ -90,8 +90,9 @@ public class SamlProtocolFactory extends AbstractLoginProtocolFactory {
 
     @Override
     protected void addDefaults(ClientModel client) {
-        for (ProtocolMapperModel model : defaultBuiltins) client.addProtocolMapper(model);
-
+        for (ProtocolMapperModel model : defaultBuiltins) {
+            model.setProtocol(getId());
+            client.addProtocolMapper(model);
+        }
     }
-
 }
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 3402593..f9aa30b 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
@@ -16,6 +16,7 @@ import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 
 import org.jboss.logging.Logger;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
 import org.keycloak.common.VerificationException;
 import org.keycloak.common.util.StreamUtil;
 import org.keycloak.dom.saml.v2.SAML2Object;
@@ -34,7 +35,9 @@ import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.protocol.AuthorizationEndpointBase;
+import org.keycloak.protocol.LoginProtocol;
 import org.keycloak.protocol.oidc.utils.RedirectUtils;
+import org.keycloak.protocol.saml.profile.ecp.SamlEcpProfileService;
 import org.keycloak.saml.SAML2LogoutResponseBuilder;
 import org.keycloak.saml.SAMLRequestParser;
 import org.keycloak.saml.SignatureAlgorithm;
@@ -221,7 +224,7 @@ public class SamlService extends AuthorizationEndpointBase {
             }
 
             ClientSessionModel clientSession = session.sessions().createClientSession(realm, client);
-            clientSession.setAuthMethod(SamlProtocol.LOGIN_PROTOCOL);
+            clientSession.setAuthMethod(getLoginProtocol());
             clientSession.setRedirectUri(redirect);
             clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
             clientSession.setNote(ClientSessionCode.ACTION_KEY, KeycloakModelUtils.generateCodeSecret());
@@ -246,7 +249,7 @@ public class SamlService extends AuthorizationEndpointBase {
             return newBrowserAuthentication(clientSession, requestAbstractType.isIsPassive());
         }
 
-        private String getBindingType(AuthnRequestType requestAbstractType) {
+        protected String getBindingType(AuthnRequestType requestAbstractType) {
             URI requestedProtocolBinding = requestAbstractType.getProtocolBinding();
 
             if (requestedProtocolBinding != null) {
@@ -370,7 +373,7 @@ public class SamlService extends AuthorizationEndpointBase {
         }
     }
 
-    protected class PostBindingProtocol extends BindingProtocol {
+    public class PostBindingProtocol extends BindingProtocol {
 
         @Override
         protected void verifySignature(SAMLDocumentHolder documentHolder, ClientModel client) throws VerificationException {
@@ -443,7 +446,12 @@ public class SamlService extends AuthorizationEndpointBase {
     }
 
     protected Response newBrowserAuthentication(ClientSessionModel clientSession, boolean isPassive) {
-        return handleBrowserAuthenticationRequest(clientSession, new SamlProtocol().setEventBuilder(event).setHttpHeaders(headers).setRealm(realm).setSession(session).setUriInfo(uriInfo), isPassive);
+        LoginProtocol protocol = session.getProvider(LoginProtocol.class, clientSession.getAuthMethod());
+        protocol.setRealm(realm)
+                .setHttpHeaders(request.getHttpHeaders())
+                .setUriInfo(uriInfo)
+                .setEventBuilder(event);
+        return handleBrowserAuthenticationRequest(clientSession, protocol, isPassive);
     }
 
     /**
@@ -463,6 +471,16 @@ public class SamlService extends AuthorizationEndpointBase {
         return new PostBindingProtocol().execute(samlRequest, samlResponse, relayState);
     }
 
+    @POST
+    @Consumes("application/soap+xml")
+    public Response soapBinding(InputStream inputStream) {
+        SamlEcpProfileService bindingService = new SamlEcpProfileService(realm, event, authManager);
+
+        ResteasyProviderFactory.getInstance().injectProperties(bindingService);
+
+        return bindingService.authenticate(inputStream);
+    }
+
     @GET
     @Path("descriptor")
     @Produces(MediaType.APPLICATION_XML)
@@ -519,7 +537,7 @@ public class SamlService extends AuthorizationEndpointBase {
         }
 
         ClientSessionModel clientSession = session.sessions().createClientSession(realm, client);
-        clientSession.setAuthMethod(SamlProtocol.LOGIN_PROTOCOL);
+        clientSession.setAuthMethod(getLoginProtocol());
         clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
         clientSession.setNote(ClientSessionCode.ACTION_KEY, KeycloakModelUtils.generateCodeSecret());
         clientSession.setNote(SamlProtocol.SAML_BINDING, SamlProtocol.SAML_POST_BINDING);
@@ -537,4 +555,8 @@ public class SamlService extends AuthorizationEndpointBase {
 
     }
 
+    protected String getLoginProtocol() {
+        return SamlProtocol.LOGIN_PROTOCOL;
+    }
+
 }
diff --git a/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory b/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory
new file mode 100755
index 0000000..9ac8020
--- /dev/null
+++ b/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory
@@ -0,0 +1 @@
+org.keycloak.protocol.saml.profile.ecp.authenticator.HttpBasicAuthenticator
\ No newline at end of file
diff --git a/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.protocol.LoginProtocolFactory b/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.protocol.LoginProtocolFactory
index d0a2dd0..ae434f6 100755
--- a/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.protocol.LoginProtocolFactory
+++ b/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.protocol.LoginProtocolFactory
@@ -1 +1,2 @@
-org.keycloak.protocol.saml.SamlProtocolFactory
\ No newline at end of file
+org.keycloak.protocol.saml.SamlProtocolFactory
+org.keycloak.protocol.saml.profile.ecp.SamlEcpProfileProtocolFactory
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java b/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java
index a1fc4a7..9dc5548 100644
--- a/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java
+++ b/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java
@@ -87,7 +87,7 @@ public abstract class AuthorizationEndpointBase {
             }
         }
 
-        AuthenticationFlowModel flow = realm.getBrowserFlow();
+        AuthenticationFlowModel flow = getAuthenticationFlow();
         String flowId = flow.getId();
         AuthenticationProcessor processor = createProcessor(clientSession, flowId, LoginActionsService.AUTHENTICATE_PATH);
 
@@ -127,6 +127,10 @@ public abstract class AuthorizationEndpointBase {
         }
     }
 
+    protected AuthenticationFlowModel getAuthenticationFlow() {
+        return realm.getBrowserFlow();
+    }
+
     protected Response buildRedirectToIdentityProvider(String providerId, String accessCode) {
         logger.debug("Automatically redirect to identity provider: " + providerId);
         return Response.temporaryRedirect(
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlEcpProfileTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlEcpProfileTest.java
new file mode 100755
index 0000000..c02deeb
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlEcpProfileTest.java
@@ -0,0 +1,230 @@
+package org.keycloak.testsuite.saml;
+
+import org.jboss.resteasy.util.Base64;
+import org.junit.ClassRule;
+import org.junit.Test;
+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.saml.common.constants.JBossSAMLConstants;
+import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
+import org.keycloak.saml.common.util.DocumentUtil;
+import org.keycloak.saml.processing.core.parsers.saml.SAMLParser;
+import org.keycloak.testsuite.samlfilter.SamlAdapterTest;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.Invocation.Builder;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.NewCookie;
+import javax.ws.rs.core.Response;
+import javax.xml.namespace.QName;
+import javax.xml.soap.MessageFactory;
+import javax.xml.soap.SOAPHeader;
+import javax.xml.soap.SOAPHeaderElement;
+import javax.xml.soap.SOAPMessage;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.stream.StreamResult;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.util.Iterator;
+import java.util.Map;
+
+import static javax.ws.rs.core.Response.Status.OK;
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class SamlEcpProfileTest {
+
+    protected String APP_SERVER_BASE_URL = "http://localhost:8081";
+
+    @ClassRule
+    public static org.keycloak.testsuite.samlfilter.SamlKeycloakRule keycloakRule = new org.keycloak.testsuite.samlfilter.SamlKeycloakRule() {
+        @Override
+        public void initWars() {
+            ClassLoader classLoader = SamlAdapterTest.class.getClassLoader();
+
+            initializeSamlSecuredWar("/keycloak-saml/ecp/ecp-sp", "/ecp-sp",  "ecp-sp.war", classLoader);
+        }
+
+        @Override
+        public String getRealmJson() {
+            return "/keycloak-saml/ecp/testsamlecp.json";
+        }
+    };
+
+    @Test
+    public void testSuccessfulEcpFlow() throws Exception {
+        Response authnRequestResponse = ClientBuilder.newClient().target(APP_SERVER_BASE_URL + "/ecp-sp/").request()
+                .header("Accept", "text/html; application/vnd.paos+xml")
+                .header("PAOS", "ver='urn:liberty:paos:2003-08' ;'urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp'")
+                .get();
+
+        SOAPMessage authnRequestMessage = MessageFactory.newInstance().createMessage(null, new ByteArrayInputStream(authnRequestResponse.readEntity(byte[].class)));
+
+        printDocument(authnRequestMessage.getSOAPPart().getContent(), System.out);
+
+        Iterator<SOAPHeaderElement> it = authnRequestMessage.getSOAPHeader().<SOAPHeaderElement>getChildElements(new QName("urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp", "Request"));
+        SOAPHeaderElement ecpRequestHeader = it.next();
+        NodeList idpList = ecpRequestHeader.getElementsByTagNameNS("urn:oasis:names:tc:SAML:2.0:protocol", "IDPList");
+
+        assertEquals("No IDPList returned from Service Provider", 1, idpList.getLength());
+
+        NodeList idpEntries = idpList.item(0).getChildNodes();
+
+        assertEquals("No IDPEntry returned from Service Provider", 1, idpEntries.getLength());
+
+        String singleSignOnService = null;
+
+        for (int i = 0; i < idpEntries.getLength(); i++) {
+            Node item = idpEntries.item(i);
+            NamedNodeMap attributes = item.getAttributes();
+            Node location = attributes.getNamedItem("Loc");
+
+            singleSignOnService = location.getNodeValue();
+        }
+
+        assertNotNull("Could not obtain SSO Service URL", singleSignOnService);
+
+        Document authenticationRequest = authnRequestMessage.getSOAPBody().getFirstChild().getOwnerDocument();
+        String username = "pedroigor";
+        String password = "password";
+        String pair = username + ":" + password;
+        String authHeader = "Basic " + new String(Base64.encodeBytes(pair.getBytes()));
+
+        Response authenticationResponse = ClientBuilder.newClient().target(singleSignOnService).request()
+                .header(HttpHeaders.AUTHORIZATION, authHeader)
+                .post(Entity.entity(DocumentUtil.asString(authenticationRequest), "application/soap+xml"));
+
+        assertEquals(OK.getStatusCode(), authenticationResponse.getStatus());
+
+        SOAPMessage responseMessage  = MessageFactory.newInstance().createMessage(null, new ByteArrayInputStream(authenticationResponse.readEntity(byte[].class)));
+
+        printDocument(responseMessage.getSOAPPart().getContent(), System.out);
+
+        SOAPHeader responseMessageHeaders = responseMessage.getSOAPHeader();
+
+        NodeList ecpResponse = responseMessageHeaders.getElementsByTagNameNS(JBossSAMLURIConstants.ECP_PROFILE.get(), JBossSAMLConstants.RESPONSE.get());
+
+        assertEquals("No ECP Response", 1, ecpResponse.getLength());
+
+        Node samlResponse = responseMessage.getSOAPBody().getFirstChild();
+
+        assertNotNull(samlResponse);
+
+        ResponseType responseType = (ResponseType) new SAMLParser().parse(DocumentUtil.getNodeAsStream(samlResponse));
+        StatusCodeType statusCode = responseType.getStatus().getStatusCode();
+
+        assertEquals(statusCode.getValue().toString(), JBossSAMLURIConstants.STATUS_SUCCESS.get());
+        assertEquals("http://localhost:8081/ecp-sp/", responseType.getDestination());
+        assertNotNull(responseType.getSignature());
+        assertEquals(1, responseType.getAssertions().size());
+
+        SOAPMessage samlResponseRequest = MessageFactory.newInstance().createMessage();
+
+        samlResponseRequest.getSOAPBody().addDocument(responseMessage.getSOAPBody().extractContentAsDocument());
+
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+
+        samlResponseRequest.writeTo(os);
+
+        Response serviceProviderFinalResponse = ClientBuilder.newClient().target(responseType.getDestination()).request()
+                .post(Entity.entity(os.toByteArray(), "application/vnd.paos+xml"));
+
+        Map<String, NewCookie> cookies = serviceProviderFinalResponse.getCookies();
+
+        Builder resourceRequest = ClientBuilder.newClient().target(responseType.getDestination() + "/index.html").request();
+
+        for (NewCookie cookie : cookies.values()) {
+            resourceRequest.cookie(cookie);
+        }
+
+        Response resourceResponse = resourceRequest.get();
+
+        assertTrue(resourceResponse.readEntity(String.class).contains("pedroigor"));
+    }
+
+    @Test
+    public void testInvalidCredentials() throws Exception {
+        Response authnRequestResponse = ClientBuilder.newClient().target(APP_SERVER_BASE_URL + "/ecp-sp/").request()
+                .header("Accept", "text/html; application/vnd.paos+xml")
+                .header("PAOS", "ver='urn:liberty:paos:2003-08' ;'urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp'")
+                .get();
+
+        SOAPMessage authnRequestMessage = MessageFactory.newInstance().createMessage(null, new ByteArrayInputStream(authnRequestResponse.readEntity(byte[].class)));
+        Iterator<SOAPHeaderElement> it = authnRequestMessage.getSOAPHeader().<SOAPHeaderElement>getChildElements(new QName("urn:liberty:paos:2003-08", "Request"));
+
+        it.next();
+
+        it = authnRequestMessage.getSOAPHeader().<SOAPHeaderElement>getChildElements(new QName("urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp", "Request"));
+        SOAPHeaderElement ecpRequestHeader = it.next();
+        NodeList idpList = ecpRequestHeader.getElementsByTagNameNS("urn:oasis:names:tc:SAML:2.0:protocol", "IDPList");
+
+        assertEquals("No IDPList returned from Service Provider", 1, idpList.getLength());
+
+        NodeList idpEntries = idpList.item(0).getChildNodes();
+
+        assertEquals("No IDPEntry returned from Service Provider", 1, idpEntries.getLength());
+
+        String singleSignOnService = null;
+
+        for (int i = 0; i < idpEntries.getLength(); i++) {
+            Node item = idpEntries.item(i);
+            NamedNodeMap attributes = item.getAttributes();
+            Node location = attributes.getNamedItem("Loc");
+
+            singleSignOnService = location.getNodeValue();
+        }
+
+        assertNotNull("Could not obtain SSO Service URL", singleSignOnService);
+
+        Document authenticationRequest = authnRequestMessage.getSOAPBody().getFirstChild().getOwnerDocument();
+        String username = "pedroigor";
+        String password = "baspassword";
+        String pair = username + ":" + password;
+        String authHeader = "Basic " + new String(Base64.encodeBytes(pair.getBytes()));
+
+        Response authenticationResponse = ClientBuilder.newClient().target(singleSignOnService).request()
+                .header(HttpHeaders.AUTHORIZATION, authHeader)
+                .post(Entity.entity(DocumentUtil.asString(authenticationRequest), "application/soap+xml"));
+
+        assertEquals(OK.getStatusCode(), authenticationResponse.getStatus());
+
+        SOAPMessage responseMessage  = MessageFactory.newInstance().createMessage(null, new ByteArrayInputStream(authenticationResponse.readEntity(byte[].class)));
+        Node samlResponse = responseMessage.getSOAPBody().getFirstChild();
+
+        assertNotNull(samlResponse);
+
+        StatusResponseType responseType = (StatusResponseType) new SAMLParser().parse(DocumentUtil.getNodeAsStream(samlResponse));
+        StatusCodeType statusCode = responseType.getStatus().getStatusCode();
+
+        assertNotEquals(statusCode.getStatusCode().getValue().toString(), JBossSAMLURIConstants.STATUS_SUCCESS.get());
+    }
+
+    public static void printDocument(Source doc, OutputStream out) throws IOException, TransformerException {
+        TransformerFactory tf = TransformerFactory.newInstance();
+        Transformer transformer = tf.newTransformer();
+        transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
+        transformer.setOutputProperty(OutputKeys.METHOD, "xml");
+        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
+        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
+
+        transformer.transform(doc,
+                new StreamResult(new OutputStreamWriter(out, "UTF-8")));
+    }
+}
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/ecp/ecp-sp/WEB-INF/keycloak-saml.xml b/testsuite/integration/src/test/resources/keycloak-saml/ecp/ecp-sp/WEB-INF/keycloak-saml.xml
new file mode 100755
index 0000000..df39712
--- /dev/null
+++ b/testsuite/integration/src/test/resources/keycloak-saml/ecp/ecp-sp/WEB-INF/keycloak-saml.xml
@@ -0,0 +1,40 @@
+<keycloak-saml-adapter>
+    <SP entityID="http://localhost:8081/ecp-sp/"
+        sslPolicy="EXTERNAL"
+        nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
+        logoutPage="/logout.jsp"
+        forceAuthentication="false">
+        <Keys>
+            <Key signing="true" >
+                <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+                    <PrivateKey alias="http://localhost:8080/sales-post-sig/" password="test123"/>
+                    <Certificate alias="http://localhost:8080/sales-post-sig/"/>
+                </KeyStore>
+            </Key>
+        </Keys>
+        <PrincipalNameMapping policy="FROM_NAME_ID"/>
+        <RoleIdentifiers>
+            <Attribute name="Role"/>
+        </RoleIdentifiers>
+        <IDP entityID="idp"
+             signaturesRequired="true">
+        <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"
+                    />
+            <Keys>
+                <Key signing="true">
+                    <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+                        <Certificate alias="demo"/>
+                    </KeyStore>
+                </Key>
+            </Keys>
+        </IDP>
+     </SP>
+</keycloak-saml-adapter>
\ No newline at end of file
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/ecp/ecp-sp/WEB-INF/keystore.jks b/testsuite/integration/src/test/resources/keycloak-saml/ecp/ecp-sp/WEB-INF/keystore.jks
new file mode 100755
index 0000000..144830b
Binary files /dev/null and b/testsuite/integration/src/test/resources/keycloak-saml/ecp/ecp-sp/WEB-INF/keystore.jks differ
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/ecp/testsamlecp.json b/testsuite/integration/src/test/resources/keycloak-saml/ecp/testsamlecp.json
new file mode 100755
index 0000000..981cbda
--- /dev/null
+++ b/testsuite/integration/src/test/resources/keycloak-saml/ecp/testsamlecp.json
@@ -0,0 +1,67 @@
+{
+    "id": "demo",
+    "realm": "demo",
+    "enabled": true,
+    "sslRequired": "external",
+    "registrationAllowed": true,
+    "resetPasswordAllowed": true,
+    "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
+    "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+    "requiredCredentials": [ "password" ],
+    "defaultRoles": [ "user" ],
+    "smtpServer": {
+        "from": "auto@keycloak.org",
+        "host": "localhost",
+        "port":"3025"
+    },
+    "users" : [
+        {
+            "username" : "pedroigor",
+            "enabled": true,
+            "email" : "psilva@redhat.com",
+            "credentials" : [
+                { "type" : "password",
+                    "value" : "password" }
+            ],
+            "attributes" : {
+                "phone": "617"
+            },
+            "realmRoles": ["manager", "user"]
+        }
+    ],
+    "applications": [
+        {
+            "name": "http://localhost:8081/ecp-sp/",
+            "enabled": true,
+            "protocol": "saml",
+            "fullScopeAllowed": true,
+            "baseUrl": "http://localhost:8081/ecp-sp",
+            "redirectUris": [
+                "http://localhost:8081/ecp-sp/*"
+            ],
+            "attributes": {
+                "saml_assertion_consumer_url_post": "http://localhost:8081/ecp-sp/",
+                "saml_assertion_consumer_url_redirect": "http://localhost:8081/ecp-sp/",
+                "saml_single_logout_service_url_post": "http://localhost:8081/ecp-sp/",
+                "saml_single_logout_service_url_redirect": "http://localhost:8081/ecp-sp/",
+                "saml.server.signature": "true",
+                "saml.signature.algorithm": "RSA_SHA256",
+                "saml.client.signature": "true",
+                "saml.authnstatement": "true",
+                "saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
+            }
+        }
+    ],
+    "roles" : {
+        "realm" : [
+            {
+                "name": "manager",
+                "description": "Have Manager privileges"
+            },
+            {
+                "name": "user",
+                "description": "Have User privileges"
+            }
+        ]
+    }
+}