keycloak-uncached

Changes

pom.xml 6(+3 -3)

testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/BasicAuthExample.java 61(+0 -61)

testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/CustomerPortalExample.java 101(+0 -101)

testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/MultiTenantExample.java 64(+0 -64)

testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/ProductPortalExample.java 80(+0 -80)

testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SAMLPostSigExample.java 51(+0 -51)

testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SAMLRedirectSigExample.java 51(+0 -51)

testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractBasicAuthExampleAdapterTest.java 89(+0 -89)

testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractDemoExampleAdapterTest.java 373(+0 -373)

testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractSAMLExampleAdapterTest.java 126(+0 -126)

testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/example/EAPBasicAuthExampleAdapterTest.java 12(+0 -12)

testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/example/EAPDemoExampleAdapterTest.java 12(+0 -12)

testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/example/EAPSAMLExampleAdapterTest.java 11(+0 -11)

testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/example/EAP6BasicAuthExampleAdapterTest.java 12(+0 -12)

testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/example/EAP6DemoExampleAdapterTest.java 12(+0 -12)

testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/example/EAP6SAMLExampleAdapterTest.java 11(+0 -11)

testsuite/integration-arquillian/tests/other/adapters/jboss/relative/eap/src/test/java/org/keycloak/testsuite/adapter/example/RelativeEAPBasicAuthExampleAdapterTest.java 9(+0 -9)

testsuite/integration-arquillian/tests/other/adapters/jboss/relative/eap/src/test/java/org/keycloak/testsuite/adapter/example/RelativeEAPCorsExampleAdapterTest.java 9(+0 -9)

testsuite/integration-arquillian/tests/other/adapters/jboss/relative/eap/src/test/java/org/keycloak/testsuite/adapter/example/RelativeEAPDemoExampleAdapterTest.java 9(+0 -9)

testsuite/integration-arquillian/tests/other/adapters/jboss/relative/eap/src/test/java/org/keycloak/testsuite/adapter/example/RelativeEAPSAMLExampleAdapterTest.java 9(+0 -9)

testsuite/integration-arquillian/tests/other/adapters/jboss/remote/src/test/java/org/keycloak/testsuite/adapter/example/RemoteBasicAuthExampleAdapterTest.java 12(+0 -12)

testsuite/integration-arquillian/tests/other/adapters/jboss/remote/src/test/java/org/keycloak/testsuite/adapter/example/RemoteDemoExampleAdapterTest.java 12(+0 -12)

testsuite/integration-arquillian/tests/other/adapters/jboss/remote/src/test/java/org/keycloak/testsuite/adapter/example/RemoteSAMLExampleAdapterTest.java 11(+0 -11)

testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/WildflyBasicAuthExampleAdapterTest.java 12(+0 -12)

testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/WildflyDemoExampleAdapterTest.java 12(+0 -12)

testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/WildflySAMLExampleAdapterTest.java 11(+0 -11)

testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly10/src/test/java/org/keycloak/testsuite/adapter/example/Wildfly10BasicAuthExampleAdapterTest.java 12(+0 -12)

testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly10/src/test/java/org/keycloak/testsuite/adapter/example/Wildfly10DemoExampleAdapterTest.java 12(+0 -12)

testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly10/src/test/java/org/keycloak/testsuite/adapter/example/Wildfly10SAMLExampleAdapterTest.java 11(+0 -11)

testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly8/src/test/java/org/keycloak/testsuite/adapter/example/Wildfly8BasicAuthExampleAdapterTest.java 12(+0 -12)

testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly8/src/test/java/org/keycloak/testsuite/adapter/example/Wildfly8DemoExampleAdapterTest.java 12(+0 -12)

testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly9/src/test/java/org/keycloak/testsuite/adapter/example/Wildfly9BasicAuthExampleAdapterTest.java 12(+0 -12)

testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly9/src/test/java/org/keycloak/testsuite/adapter/example/Wildfly9DemoExampleAdapterTest.java 12(+0 -12)

testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly9/src/test/java/org/keycloak/testsuite/adapter/example/Wildfly9SAMLExampleAdapterTest.java 11(+0 -11)

testsuite/integration-arquillian/tests/other/adapters/tomcat/tomcat7/src/test/java/org/keycloak/testsuite/adapter/example/Tomcat7BasicAuthExampleAdapterTest.java 12(+0 -12)

testsuite/integration-arquillian/tests/other/adapters/tomcat/tomcat7/src/test/java/org/keycloak/testsuite/adapter/example/Tomcat7DemoExampleAdapterTest.java 12(+0 -12)

testsuite/integration-arquillian/tests/other/adapters/tomcat/tomcat8/src/test/java/org/keycloak/testsuite/adapter/example/Tomcat8BasicAuthExampleAdapterTest.java 12(+0 -12)

testsuite/integration-arquillian/tests/other/adapters/tomcat/tomcat8/src/test/java/org/keycloak/testsuite/adapter/example/Tomcat8DemoExampleAdapterTest.java 12(+0 -12)

testsuite/integration-arquillian/tests/other/adapters/tomcat/tomcat9/src/test/java/org/keycloak/testsuite/adapter/example/Tomcat9BasicAuthExampleAdapterTest.java 12(+0 -12)

testsuite/integration-arquillian/tests/other/adapters/tomcat/tomcat9/src/test/java/org/keycloak/testsuite/adapter/example/Tomcat9DemoExampleAdapterTest.java 12(+0 -12)

Details

diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PathMatcher.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PathMatcher.java
index 9efa614..c8bce94 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PathMatcher.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PathMatcher.java
@@ -89,7 +89,7 @@ class PathMatcher {
                     pathString = "/";
                 }
 
-                if (matchingUri.equals(targetUri)) {
+                if (matchingUri.equals(targetUri) || pathString.equals(targetUri)) {
                     cache.put(targetUri, entry);
                     return entry;
                 }
diff --git a/adapters/oidc/js/pom.xml b/adapters/oidc/js/pom.xml
index 8b4cc67..f1b83c1 100755
--- a/adapters/oidc/js/pom.xml
+++ b/adapters/oidc/js/pom.xml
@@ -38,6 +38,11 @@
             <plugin>
                 <groupId>com.samaxes.maven</groupId>
                 <artifactId>minify-maven-plugin</artifactId>
+                <configuration>
+                    <jsEngine>CLOSURE</jsEngine>
+                    <closureLanguageIn>ECMASCRIPT5</closureLanguageIn>
+                    <closureCreateSourceMap>true</closureCreateSourceMap>
+                </configuration>
                 <executions>
                     <execution>
                         <id>min-js</id>
diff --git a/adapters/oidc/js/src/main/resources/keycloak.js b/adapters/oidc/js/src/main/resources/keycloak.js
index a784936..ab6f788 100755
--- a/adapters/oidc/js/src/main/resources/keycloak.js
+++ b/adapters/oidc/js/src/main/resources/keycloak.js
@@ -221,7 +221,7 @@
             var callbackState = {
                 state: state,
                 nonce: nonce,
-                redirectUri: encodeURIComponent(redirectUri),
+                redirectUri: encodeURIComponent(redirectUri)
             }
 
             if (options && options.prompt) {
@@ -843,6 +843,7 @@
             }
 
             iframe.setAttribute('src', src );
+            iframe.setAttribute('title', 'keycloak-session-iframe' );
             iframe.style.display = 'none';
             document.body.appendChild(iframe);
 
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/pom.xml b/adapters/oidc/wildfly/wildfly-subsystem/pom.xml
index 1d8e6cf..8c18a39 100755
--- a/adapters/oidc/wildfly/wildfly-subsystem/pom.xml
+++ b/adapters/oidc/wildfly/wildfly-subsystem/pom.xml
@@ -69,6 +69,10 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <groupId>org.wildfly.security</groupId>
+            <artifactId>wildfly-elytron</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.jboss.logging</groupId>
             <artifactId>jboss-logging-annotations</artifactId>
             <!-- This is a compile-time dependency of this project, but is not needed at compile or runtime by other
@@ -101,5 +105,9 @@
             <artifactId>keycloak-wildfly-adapter</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-wildfly-elytron-oidc-adapter</artifactId>
+        </dependency>
     </dependencies>
 </project>
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/AbstractAdapterConfigurationDefinition.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/AbstractAdapterConfigurationDefinition.java
new file mode 100755
index 0000000..613c946
--- /dev/null
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/AbstractAdapterConfigurationDefinition.java
@@ -0,0 +1,164 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.subsystem.adapter.extension;
+
+import org.jboss.as.controller.AttributeDefinition;
+import org.jboss.as.controller.PathElement;
+import org.jboss.as.controller.SimpleAttributeDefinition;
+import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
+import org.jboss.as.controller.SimpleResourceDefinition;
+import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler;
+import org.jboss.as.controller.operations.validation.IntRangeValidator;
+import org.jboss.as.controller.operations.validation.StringLengthValidator;
+import org.jboss.as.controller.registry.ManagementResourceRegistration;
+import org.jboss.dmr.ModelNode;
+import org.jboss.dmr.ModelType;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Defines attributes and operations for a secure-deployment.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+abstract class AbstractAdapterConfigurationDefinition extends SimpleResourceDefinition {
+
+    protected static final SimpleAttributeDefinition REALM =
+            new SimpleAttributeDefinitionBuilder("realm", ModelType.STRING, true)
+                    .setXmlName("realm")
+                    .setAllowExpression(true)
+                    .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
+                    .build();
+    protected static final SimpleAttributeDefinition RESOURCE =
+            new SimpleAttributeDefinitionBuilder("resource", ModelType.STRING, true)
+                    .setXmlName("resource")
+                    .setAllowExpression(true)
+                    .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
+                    .build();
+    protected static final SimpleAttributeDefinition USE_RESOURCE_ROLE_MAPPINGS =
+            new SimpleAttributeDefinitionBuilder("use-resource-role-mappings", ModelType.BOOLEAN, true)
+                    .setXmlName("use-resource-role-mappings")
+                    .setAllowExpression(true)
+                    .setDefaultValue(new ModelNode(false))
+                    .build();
+    protected static final SimpleAttributeDefinition BEARER_ONLY =
+            new SimpleAttributeDefinitionBuilder("bearer-only", ModelType.BOOLEAN, true)
+                    .setXmlName("bearer-only")
+                    .setAllowExpression(true)
+                    .setDefaultValue(new ModelNode(false))
+                    .build();
+    protected static final SimpleAttributeDefinition ENABLE_BASIC_AUTH =
+            new SimpleAttributeDefinitionBuilder("enable-basic-auth", ModelType.BOOLEAN, true)
+                    .setXmlName("enable-basic-auth")
+                    .setAllowExpression(true)
+                    .setDefaultValue(new ModelNode(false))
+                    .build();
+    protected static final SimpleAttributeDefinition PUBLIC_CLIENT =
+            new SimpleAttributeDefinitionBuilder("public-client", ModelType.BOOLEAN, true)
+                    .setXmlName("public-client")
+                    .setAllowExpression(true)
+                    .setDefaultValue(new ModelNode(false))
+                    .build();
+    protected static final SimpleAttributeDefinition TURN_OFF_CHANGE_SESSION =
+            new SimpleAttributeDefinitionBuilder("turn-off-change-session-id-on-login", ModelType.BOOLEAN, true)
+                    .setXmlName("turn-off-change-session-id-on-login")
+                    .setAllowExpression(true)
+                    .setDefaultValue(new ModelNode(false))
+                    .build();
+    protected static final SimpleAttributeDefinition TOKEN_MINIMUM_TIME_TO_LIVE =
+            new SimpleAttributeDefinitionBuilder("token-minimum-time-to-live", ModelType.INT, true)
+                    .setXmlName("token-minimum-time-to-live")
+                    .setValidator(new IntRangeValidator(-1, true))
+                    .setAllowExpression(true)
+                    .build();
+    protected static final SimpleAttributeDefinition MIN_TIME_BETWEEN_JWKS_REQUESTS =
+            new SimpleAttributeDefinitionBuilder("min-time-between-jwks-requests", ModelType.INT, true)
+                    .setXmlName("min-time-between-jwks-requests")
+                    .setValidator(new IntRangeValidator(-1, true))
+                    .setAllowExpression(true)
+                    .build();
+
+    static final List<SimpleAttributeDefinition> DEPLOYMENT_ONLY_ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
+
+    static {
+        DEPLOYMENT_ONLY_ATTRIBUTES.add(REALM);
+        DEPLOYMENT_ONLY_ATTRIBUTES.add(RESOURCE);
+        DEPLOYMENT_ONLY_ATTRIBUTES.add(USE_RESOURCE_ROLE_MAPPINGS);
+        DEPLOYMENT_ONLY_ATTRIBUTES.add(BEARER_ONLY);
+        DEPLOYMENT_ONLY_ATTRIBUTES.add(ENABLE_BASIC_AUTH);
+        DEPLOYMENT_ONLY_ATTRIBUTES.add(PUBLIC_CLIENT);
+        DEPLOYMENT_ONLY_ATTRIBUTES.add(TURN_OFF_CHANGE_SESSION);
+        DEPLOYMENT_ONLY_ATTRIBUTES.add(TOKEN_MINIMUM_TIME_TO_LIVE);
+        DEPLOYMENT_ONLY_ATTRIBUTES.add(MIN_TIME_BETWEEN_JWKS_REQUESTS);
+    }
+
+    static final List<SimpleAttributeDefinition> ALL_ATTRIBUTES = new ArrayList();
+
+    static {
+        ALL_ATTRIBUTES.addAll(DEPLOYMENT_ONLY_ATTRIBUTES);
+        ALL_ATTRIBUTES.addAll(SharedAttributeDefinitons.ATTRIBUTES);
+    }
+
+    static final Map<String, SimpleAttributeDefinition> XML_ATTRIBUTES = new HashMap<String, SimpleAttributeDefinition>();
+
+    static {
+        for (SimpleAttributeDefinition def : ALL_ATTRIBUTES) {
+            XML_ATTRIBUTES.put(def.getXmlName(), def);
+        }
+    }
+
+    private static final Map<String, SimpleAttributeDefinition> DEFINITION_LOOKUP = new HashMap<String, SimpleAttributeDefinition>();
+    static {
+        for (SimpleAttributeDefinition def : ALL_ATTRIBUTES) {
+            DEFINITION_LOOKUP.put(def.getXmlName(), def);
+        }
+    }
+
+    private final AbstractAdapterConfigurationWriteAttributeHandler attrWriteHandler;
+    private final List<SimpleAttributeDefinition> attributes;
+
+    protected AbstractAdapterConfigurationDefinition(String name, List<SimpleAttributeDefinition> attributes, AbstractAdapterConfigurationAddHandler addHandler, AbstractAdapterConfigurationRemoveHandler removeHandler, AbstractAdapterConfigurationWriteAttributeHandler attrWriteHandler) {
+        super(PathElement.pathElement(name),
+                KeycloakExtension.getResourceDescriptionResolver(name),
+                addHandler,
+                removeHandler);
+        this.attributes = attributes;
+        this.attrWriteHandler = attrWriteHandler;
+    }
+
+    @Override
+    public void registerOperations(ManagementResourceRegistration resourceRegistration) {
+        super.registerOperations(resourceRegistration);
+        resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);
+    }
+
+    @Override
+    public void registerAttributes(ManagementResourceRegistration resourceRegistration) {
+        super.registerAttributes(resourceRegistration);
+        for (AttributeDefinition attrDef : this.attributes) {
+            resourceRegistration.registerReadWriteAttribute(attrDef, null, this.attrWriteHandler);
+        }
+    }
+
+    public static SimpleAttributeDefinition lookup(String name) {
+        return DEFINITION_LOOKUP.get(name);
+    }
+}
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakAdapterConfigDeploymentProcessor.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakAdapterConfigDeploymentProcessor.java
index 24e9ac0..1500808 100755
--- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakAdapterConfigDeploymentProcessor.java
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakAdapterConfigDeploymentProcessor.java
@@ -25,7 +25,9 @@ import org.jboss.as.web.common.WarMetaData;
 import org.jboss.logging.Logger;
 import org.jboss.metadata.javaee.spec.ParamValueMetaData;
 import org.jboss.metadata.web.jboss.JBossWebMetaData;
+import org.jboss.metadata.web.spec.ListenerMetaData;
 import org.jboss.metadata.web.spec.LoginConfigMetaData;
+import org.keycloak.adapters.elytron.KeycloakConfigurationServletListener;
 import org.keycloak.subsystem.adapter.logging.KeycloakLogger;
 
 import java.util.ArrayList;
@@ -69,6 +71,9 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
         KeycloakAdapterConfigService service = KeycloakAdapterConfigService.getInstance();
         if (service.isSecureDeployment(deploymentUnit) && service.isDeploymentConfigured(deploymentUnit)) {
             addKeycloakAuthData(phaseContext, service);
+        } else if (service.isElytronEnabled(deploymentUnit)) {
+            WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
+            addConfigurationListener(warMetaData);
         }
 
         // FYI, Undertow Extension will find deployments that have auth-method set to KEYCLOAK
@@ -99,6 +104,10 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
         loginConfig.setAuthMethod("KEYCLOAK");
         loginConfig.setRealmName(service.getRealmName(deploymentUnit));
         KeycloakLogger.ROOT_LOGGER.deploymentSecured(deploymentUnit.getName());
+
+        if (service.isElytronEnabled(deploymentUnit)) {
+            addConfigurationListener(warMetaData);
+        }
     }
 
     private void addJSONData(String json, WarMetaData warMetaData) {
@@ -121,6 +130,31 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
         webMetaData.setContextParams(contextParams);
     }
 
+    private void addConfigurationListener(WarMetaData warMetaData) {
+        if (warMetaData == null) {
+            return;
+        }
+
+        JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData();
+        if (webMetaData == null) {
+            webMetaData = new JBossWebMetaData();
+            warMetaData.setMergedJBossWebMetaData(webMetaData);
+        }
+
+        LoginConfigMetaData loginConfig = webMetaData.getLoginConfig();
+        if (loginConfig == null) {
+            return;
+        }
+        if (!loginConfig.getAuthMethod().equals("KEYCLOAK")) {
+            return;
+        }
+        ListenerMetaData listenerMetaData = new ListenerMetaData();
+
+        listenerMetaData.setListenerClass(KeycloakConfigurationServletListener.class.getName());
+
+        webMetaData.getListeners().add(listenerMetaData);
+    }
+
     @Override
     public void undeploy(DeploymentUnit du) {
 
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakAdapterConfigService.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakAdapterConfigService.java
index 5a71e61..496c311 100755
--- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakAdapterConfigService.java
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakAdapterConfigService.java
@@ -23,8 +23,11 @@ import org.jboss.dmr.ModelNode;
 import org.jboss.dmr.Property;
 import org.jboss.metadata.web.jboss.JBossWebMetaData;
 
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 
 import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADDRESS;
 
@@ -50,6 +53,7 @@ public final class KeycloakAdapterConfigService {
 
     // keycloak-secured deployments
     private final Map<String, ModelNode> secureDeployments = new HashMap<String, ModelNode>();
+    private final Set<String> elytronEnabledDeployments = new HashSet<>();
 
 
     private KeycloakAdapterConfigService() {
@@ -68,9 +72,13 @@ public final class KeycloakAdapterConfigService {
         this.realms.remove(realmNameFromOp(operation));
     }
 
-    public void addSecureDeployment(ModelNode operation, ModelNode model) {
+    public void addSecureDeployment(ModelNode operation, ModelNode model, boolean elytronEnabled) {
         ModelNode deployment = model.clone();
-        this.secureDeployments.put(deploymentNameFromOp(operation), deployment);
+        String name = deploymentNameFromOp(operation);
+        this.secureDeployments.put(name, deployment);
+        if (elytronEnabled) {
+            elytronEnabledDeployments.add(name);
+        }
     }
 
     public void updateSecureDeployment(ModelNode operation, String attrName, ModelNode resolvedValue) {
@@ -79,7 +87,9 @@ public final class KeycloakAdapterConfigService {
     }
 
     public void removeSecureDeployment(ModelNode operation) {
-        this.secureDeployments.remove(deploymentNameFromOp(operation));
+        String name = deploymentNameFromOp(operation);
+        this.secureDeployments.remove(name);
+        elytronEnabledDeployments.remove(name);
     }
 
     public void addCredential(ModelNode operation, ModelNode model) {
@@ -187,7 +197,19 @@ public final class KeycloakAdapterConfigService {
     }
 
     private String deploymentNameFromOp(ModelNode operation) {
-        return valueFromOpAddress(SecureDeploymentDefinition.TAG_NAME, operation);
+        String deploymentName = valueFromOpAddress(SecureDeploymentDefinition.TAG_NAME, operation);
+
+        if (deploymentName == null) {
+            deploymentName = valueFromOpAddress(KeycloakHttpServerAuthenticationMechanismFactoryDefinition.TAG_NAME, operation);
+        }
+
+        if (deploymentName == null) {
+            deploymentName = valueFromOpAddress(SecureServerDefinition.TAG_NAME, operation);
+        }
+
+        if (deploymentName == null) throw new RuntimeException("Can't find deployment name in address " + operation);
+
+        return deploymentName;
     }
 
     private String credentialNameFromOp(ModelNode operation) {
@@ -199,9 +221,7 @@ public final class KeycloakAdapterConfigService {
     }
 
     private String valueFromOpAddress(String addrElement, ModelNode operation) {
-        String deploymentName = getValueOfAddrElement(operation.get(ADDRESS), addrElement);
-        if (deploymentName == null) throw new RuntimeException("Can't find '" + addrElement + "' in address " + operation.toString());
-        return deploymentName;
+        return getValueOfAddrElement(operation.get(ADDRESS), addrElement);
     }
 
     private String getValueOfAddrElement(ModelNode address, String elementName) {
@@ -241,8 +261,22 @@ public final class KeycloakAdapterConfigService {
         return json.toJSONString(true);
     }
 
+    public String getJSON(String deploymentName) {
+        ModelNode deployment = this.secureDeployments.get(deploymentName);
+        String realmName = deployment.get(RealmDefinition.TAG_NAME).asString();
+        ModelNode realm = this.realms.get(realmName);
+
+        ModelNode json = new ModelNode();
+        json.get(RealmDefinition.TAG_NAME).set(realmName);
+
+        // Realm values set first.  Some can be overridden by deployment values.
+        if (realm != null) setJSONValues(json, realm);
+        setJSONValues(json, deployment);
+        return json.toJSONString(true);
+    }
+
     private void setJSONValues(ModelNode json, ModelNode values) {
-        for (Property prop : values.asPropertyList()) {
+        for (Property prop : new ArrayList<>(values.asPropertyList())) {
             String name = prop.getName();
             ModelNode value = prop.getValue();
             if (value.isDefined()) {
@@ -258,6 +292,10 @@ public final class KeycloakAdapterConfigService {
         return this.secureDeployments.containsKey(deploymentName);
     }
 
+    public boolean isElytronEnabled(DeploymentUnit deploymentUnit) {
+        return elytronEnabledDeployments.contains(preferredDeploymentName(deploymentUnit));
+    }
+
     private ModelNode getSecureDeployment(DeploymentUnit deploymentUnit) {
         String deploymentName = preferredDeploymentName(deploymentUnit);
         return this.secureDeployments.containsKey(deploymentName)
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakDependencyProcessorWildFly.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakDependencyProcessorWildFly.java
index e306a7f..61d670c 100755
--- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakDependencyProcessorWildFly.java
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakDependencyProcessorWildFly.java
@@ -29,13 +29,14 @@ import org.jboss.modules.ModuleLoader;
  */
 public class KeycloakDependencyProcessorWildFly extends KeycloakDependencyProcessor {
 
+    private static final ModuleIdentifier KEYCLOAK_ELYTRON_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-wildfly-elytron-oidc-adapter");
     private static final ModuleIdentifier KEYCLOAK_WILDFLY_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-wildfly-adapter");
     private static final ModuleIdentifier KEYCLOAK_UNDERTOW_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-undertow-adapter");
 
     @Override
     protected void addPlatformSpecificModules(ModuleSpecification moduleSpecification, ModuleLoader moduleLoader) {
-        // ModuleDependency(ModuleLoader moduleLoader, ModuleIdentifier identifier, boolean optional, boolean export, boolean importServices, boolean userSpecified)
         moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_WILDFLY_ADAPTER, false, false, true, false));
         moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_UNDERTOW_ADAPTER, false, false, false, false));
+        moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_ELYTRON_ADAPTER, true, false, false, false));
     }
 }
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakExtension.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakExtension.java
index d04e72d..52113c0 100755
--- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakExtension.java
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakExtension.java
@@ -47,8 +47,9 @@ public class KeycloakExtension implements Extension {
     private static final ResourceDefinition KEYCLOAK_SUBSYSTEM_RESOURCE = new KeycloakSubsystemDefinition();
     static final RealmDefinition REALM_DEFINITION = new RealmDefinition();
     static final SecureDeploymentDefinition SECURE_DEPLOYMENT_DEFINITION = new SecureDeploymentDefinition();
+    static final SecureServerDefinition SECURE_SERVER_DEFINITION = new SecureServerDefinition();
     static final CredentialDefinition CREDENTIAL_DEFINITION = new CredentialDefinition();
-     static final RedirecRewritetRuleDefinition REDIRECT_RULE_DEFINITON = new RedirecRewritetRuleDefinition();
+    static final RedirecRewritetRuleDefinition REDIRECT_RULE_DEFINITON = new RedirecRewritetRuleDefinition();
 
     public static StandardResourceDescriptionResolver getResourceDescriptionResolver(final String... keyPrefix) {
         StringBuilder prefix = new StringBuilder(SUBSYSTEM_NAME);
@@ -80,6 +81,10 @@ public class KeycloakExtension implements Extension {
         secureDeploymentRegistration.registerSubModel(CREDENTIAL_DEFINITION);
         secureDeploymentRegistration.registerSubModel(REDIRECT_RULE_DEFINITON);
 
+        ManagementResourceRegistration secureServerRegistration = registration.registerSubModel(SECURE_SERVER_DEFINITION);
+        secureServerRegistration.registerSubModel(CREDENTIAL_DEFINITION);
+        secureServerRegistration.registerSubModel(REDIRECT_RULE_DEFINITON);
+
         subsystem.registerXMLElementWriter(PARSER);
     }
 }
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakHttpAuthenticationFactoryService.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakHttpAuthenticationFactoryService.java
new file mode 100644
index 0000000..94fb8e5
--- /dev/null
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakHttpAuthenticationFactoryService.java
@@ -0,0 +1,67 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.subsystem.adapter.extension;
+
+import org.jboss.msc.service.Service;
+import org.jboss.msc.service.StartContext;
+import org.jboss.msc.service.StartException;
+import org.jboss.msc.service.StopContext;
+import org.jboss.msc.value.InjectedValue;
+import org.keycloak.adapters.AdapterDeploymentContext;
+import org.keycloak.adapters.KeycloakDeploymentBuilder;
+import org.keycloak.adapters.elytron.KeycloakHttpServerAuthenticationMechanismFactory;
+import org.wildfly.security.auth.server.SecurityDomain;
+import org.wildfly.security.http.HttpServerAuthenticationMechanismFactory;
+import org.wildfly.security.http.util.SetMechanismInformationMechanismFactory;
+
+import java.io.ByteArrayInputStream;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class KeycloakHttpAuthenticationFactoryService implements Service<HttpServerAuthenticationMechanismFactory> {
+
+    private final String factoryName;
+    private HttpServerAuthenticationMechanismFactory httpAuthenticationFactory;
+
+    public KeycloakHttpAuthenticationFactoryService(String factoryName) {
+        this.factoryName = factoryName;
+    }
+
+    @Override
+    public void start(StartContext context) throws StartException {
+        KeycloakAdapterConfigService adapterConfigService = KeycloakAdapterConfigService.getInstance();
+        String config = adapterConfigService.getJSON(this.factoryName);
+        this.httpAuthenticationFactory = new KeycloakHttpServerAuthenticationMechanismFactory(createDeploymentContext(config.getBytes()));
+    }
+
+    @Override
+    public void stop(StopContext context) {
+        this.httpAuthenticationFactory = null;
+    }
+
+    @Override
+    public HttpServerAuthenticationMechanismFactory getValue() throws IllegalStateException, IllegalArgumentException {
+        return new SetMechanismInformationMechanismFactory(this.httpAuthenticationFactory);
+    }
+
+    private AdapterDeploymentContext createDeploymentContext(byte[] config) {
+        return new AdapterDeploymentContext(KeycloakDeploymentBuilder.build(new ByteArrayInputStream(config)));
+    }
+}
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakHttpServerAuthenticationMechanismFactoryDefinition.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakHttpServerAuthenticationMechanismFactoryDefinition.java
new file mode 100644
index 0000000..1e177a4
--- /dev/null
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakHttpServerAuthenticationMechanismFactoryDefinition.java
@@ -0,0 +1,117 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.subsystem.adapter.extension;
+
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR;
+import static org.keycloak.subsystem.adapter.extension.KeycloakHttpServerAuthenticationMechanismFactoryDefinition.KeycloakHttpServerAuthenticationMechanismFactoryAddHandler.HTTP_SERVER_AUTHENTICATION_CAPABILITY;
+
+import org.jboss.as.controller.OperationContext;
+import org.jboss.as.controller.OperationFailedException;
+import org.jboss.as.controller.PathAddress;
+import org.jboss.as.controller.SimpleResourceDefinition;
+import org.jboss.as.controller.capability.RuntimeCapability;
+import org.jboss.dmr.ModelNode;
+import org.jboss.msc.service.ServiceController;
+import org.jboss.msc.service.ServiceName;
+import org.wildfly.security.http.HttpServerAuthenticationMechanismFactory;
+
+/**
+ * A {@link SimpleResourceDefinition} that can be used to configure a {@link org.keycloak.adapters.elytron.KeycloakHttpServerAuthenticationMechanismFactory}
+ * and expose it as a capability for other subsystems.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+class KeycloakHttpServerAuthenticationMechanismFactoryDefinition extends AbstractAdapterConfigurationDefinition {
+
+    static final String TAG_NAME = "http-server-mechanism-factory";
+
+    KeycloakHttpServerAuthenticationMechanismFactoryDefinition() {
+        this(TAG_NAME);
+    }
+
+    KeycloakHttpServerAuthenticationMechanismFactoryDefinition(String tagName) {
+        super(tagName, ALL_ATTRIBUTES, new KeycloakHttpServerAuthenticationMechanismFactoryAddHandler(), new KeycloakHttpServerAuthenticationMechanismFactoryRemoveHandler(), new KeycloakHttpServerAuthenticationMechanismFactoryWriteHandler());
+    }
+
+    /**
+     * A {@link AbstractAdapterConfigurationAddHandler} that exposes a {@link KeycloakHttpServerAuthenticationMechanismFactoryDefinition}
+     * as a capability through the installation of a {@link KeycloakHttpAuthenticationFactoryService}.
+     *
+     * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+     */
+    static final class KeycloakHttpServerAuthenticationMechanismFactoryAddHandler extends AbstractAdapterConfigurationAddHandler {
+
+        static final String HTTP_SERVER_AUTHENTICATION_CAPABILITY = "org.wildfly.security.http-server-mechanism-factory";
+        static final RuntimeCapability<Void> HTTP_SERVER_AUTHENTICATION_RUNTIME_CAPABILITY = RuntimeCapability
+                .Builder.of(HTTP_SERVER_AUTHENTICATION_CAPABILITY, true, HttpServerAuthenticationMechanismFactory.class)
+                .build();
+
+        KeycloakHttpServerAuthenticationMechanismFactoryAddHandler() {
+            super(HTTP_SERVER_AUTHENTICATION_RUNTIME_CAPABILITY, ALL_ATTRIBUTES);
+        }
+
+        @Override
+        protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
+            super.performRuntime(context, operation, model);
+            installCapability(context, operation);
+        }
+
+        static void installCapability(OperationContext context, ModelNode operation) {
+            PathAddress pathAddress = PathAddress.pathAddress(operation.get(OP_ADDR));
+            String factoryName = pathAddress.getLastElement().getValue();
+            ServiceName serviceName = context.getCapabilityServiceName(HTTP_SERVER_AUTHENTICATION_CAPABILITY, factoryName, HttpServerAuthenticationMechanismFactory.class);
+            KeycloakHttpAuthenticationFactoryService service = new KeycloakHttpAuthenticationFactoryService(factoryName);
+            context.getServiceTarget().addService(serviceName, service).setInitialMode(ServiceController.Mode.ACTIVE).install();
+        }
+    }
+
+    /**
+     * A {@link AbstractAdapterConfigurationRemoveHandler} that handles the removal of {@link KeycloakHttpServerAuthenticationMechanismFactoryDefinition}.
+     *
+     * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+     */
+    static final class KeycloakHttpServerAuthenticationMechanismFactoryRemoveHandler extends AbstractAdapterConfigurationRemoveHandler {
+        @Override
+        protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
+            super.performRuntime(context, operation, model);
+            PathAddress pathAddress = PathAddress.pathAddress(operation.get(OP_ADDR));
+            String factoryName = pathAddress.getLastElement().getValue();
+            ServiceName serviceName = context.getCapabilityServiceName(HTTP_SERVER_AUTHENTICATION_CAPABILITY, factoryName, HttpServerAuthenticationMechanismFactory.class);
+
+            context.removeService(serviceName);
+        }
+
+        @Override
+        protected void recoverServices(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
+            super.recoverServices(context, operation, model);
+            KeycloakHttpServerAuthenticationMechanismFactoryAddHandler.installCapability(context, operation);
+        }
+    }
+
+    /**
+     * A {@link AbstractAdapterConfigurationWriteAttributeHandler} that updates attributes on a {@link KeycloakHttpServerAuthenticationMechanismFactoryDefinition}.
+     *
+     * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+     */
+    static final class KeycloakHttpServerAuthenticationMechanismFactoryWriteHandler extends AbstractAdapterConfigurationWriteAttributeHandler {
+        KeycloakHttpServerAuthenticationMechanismFactoryWriteHandler() {
+            super(ALL_ATTRIBUTES);
+        }
+    }
+}
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakSubsystemParser.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakSubsystemParser.java
index 79555e3..a8d50ae 100755
--- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakSubsystemParser.java
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakSubsystemParser.java
@@ -62,6 +62,9 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
             else if (reader.getLocalName().equals(SecureDeploymentDefinition.TAG_NAME)) {
                 readDeployment(reader, list);
             }
+            else if (reader.getLocalName().equals(SecureServerDefinition.TAG_NAME)) {
+                readSecureServer(reader, list);
+            }
         }
     }
 
@@ -89,27 +92,35 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
     }
 
     private void readDeployment(XMLExtendedStreamReader reader, List<ModelNode> resourcesToAdd) throws XMLStreamException {
+        readSecureResource(KeycloakExtension.SECURE_DEPLOYMENT_DEFINITION.TAG_NAME, KeycloakExtension.SECURE_DEPLOYMENT_DEFINITION, reader, resourcesToAdd);
+    }
+
+    private void readSecureServer(XMLExtendedStreamReader reader, List<ModelNode> resourcesToAdd) throws XMLStreamException {
+        readSecureResource(KeycloakExtension.SECURE_SERVER_DEFINITION.TAG_NAME, KeycloakExtension.SECURE_SERVER_DEFINITION, reader, resourcesToAdd);
+    }
+
+    private void readSecureResource(String tagName, AbstractAdapterConfigurationDefinition resource, XMLExtendedStreamReader reader, List<ModelNode> resourcesToAdd) throws XMLStreamException {
         String name = readNameAttribute(reader);
         ModelNode addSecureDeployment = new ModelNode();
         addSecureDeployment.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.ADD);
         PathAddress addr = PathAddress.pathAddress(PathElement.pathElement(ModelDescriptionConstants.SUBSYSTEM, KeycloakExtension.SUBSYSTEM_NAME),
-                PathElement.pathElement(SecureDeploymentDefinition.TAG_NAME, name));
+                PathElement.pathElement(tagName, name));
         addSecureDeployment.get(ModelDescriptionConstants.OP_ADDR).set(addr.toModelNode());
         List<ModelNode> credentialsToAdd = new ArrayList<ModelNode>();
         List<ModelNode> redirectRulesToAdd = new ArrayList<ModelNode>();
         while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
-            String tagName = reader.getLocalName();
-            if (tagName.equals(CredentialDefinition.TAG_NAME)) {
+            String localName = reader.getLocalName();
+            if (localName.equals(CredentialDefinition.TAG_NAME)) {
                 readCredential(reader, addr, credentialsToAdd);
                 continue;
             }
-            if (tagName.equals(RedirecRewritetRuleDefinition.TAG_NAME)) {
+            if (localName.equals(RedirecRewritetRuleDefinition.TAG_NAME)) {
                 readRewriteRule(reader, addr, redirectRulesToAdd);
                 continue;
             }
 
-            SimpleAttributeDefinition def = SecureDeploymentDefinition.lookup(tagName);
-            if (def == null) throw new XMLStreamException("Unknown secure-deployment tag " + tagName);
+            SimpleAttributeDefinition def = resource.lookup(localName);
+            if (def == null) throw new XMLStreamException("Unknown secure-deployment tag " + localName);
             def.parseAndSetParameter(reader.getElementText(), addSecureDeployment, reader);
         }
 
@@ -236,6 +247,7 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
         context.startSubsystemElement(KeycloakExtension.NAMESPACE, false);
         writeRealms(writer, context);
         writeSecureDeployments(writer, context);
+        writeSecureServers(writer, context);
         writer.writeEndElement();
     }
 
@@ -256,14 +268,22 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
     }
 
     private void writeSecureDeployments(XMLExtendedStreamWriter writer, SubsystemMarshallingContext context) throws XMLStreamException {
-        if (!context.getModelNode().get(SecureDeploymentDefinition.TAG_NAME).isDefined()) {
+        writeSecureResource(SecureDeploymentDefinition.TAG_NAME, SecureDeploymentDefinition.ALL_ATTRIBUTES, writer, context);
+    }
+
+    private void writeSecureServers(XMLExtendedStreamWriter writer, SubsystemMarshallingContext context) throws XMLStreamException {
+        writeSecureResource(SecureServerDefinition.TAG_NAME, SecureServerDefinition.ALL_ATTRIBUTES, writer, context);
+    }
+
+    private void writeSecureResource(String tagName, List<SimpleAttributeDefinition> attributes, XMLExtendedStreamWriter writer, SubsystemMarshallingContext context) throws XMLStreamException {
+        if (!context.getModelNode().get(tagName).isDefined()) {
             return;
         }
-        for (Property deployment : context.getModelNode().get(SecureDeploymentDefinition.TAG_NAME).asPropertyList()) {
-            writer.writeStartElement(SecureDeploymentDefinition.TAG_NAME);
+        for (Property deployment : context.getModelNode().get(tagName).asPropertyList()) {
+            writer.writeStartElement(tagName);
             writer.writeAttribute("name", deployment.getName());
             ModelNode deploymentElements = deployment.getValue();
-            for (AttributeDefinition element : SecureDeploymentDefinition.ALL_ATTRIBUTES) {
+            for (AttributeDefinition element : attributes) {
                 element.marshallAsElement(deploymentElements, writer);
             }
 
@@ -271,7 +291,7 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
             if (credentials.isDefined()) {
                 writeCredentials(writer, credentials);
             }
-            
+
             ModelNode redirectRewriteRule = deploymentElements.get(RedirecRewritetRuleDefinition.TAG_NAME);
             if (redirectRewriteRule.isDefined()) {
                 writeRedirectRules(writer, redirectRewriteRule);
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/SecureDeploymentDefinition.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/SecureDeploymentDefinition.java
index 636e2ea..5a09b9a 100755
--- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/SecureDeploymentDefinition.java
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/SecureDeploymentDefinition.java
@@ -16,138 +16,88 @@
  */
 package org.keycloak.subsystem.adapter.extension;
 
-import org.jboss.as.controller.AttributeDefinition;
-import org.jboss.as.controller.PathElement;
-import org.jboss.as.controller.SimpleAttributeDefinition;
-import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
-import org.jboss.as.controller.SimpleResourceDefinition;
-import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler;
-import org.jboss.as.controller.operations.validation.IntRangeValidator;
-import org.jboss.as.controller.operations.validation.StringLengthValidator;
-import org.jboss.as.controller.registry.ManagementResourceRegistration;
-import org.jboss.dmr.ModelNode;
-import org.jboss.dmr.ModelType;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import org.jboss.as.controller.OperationContext;
+import org.jboss.as.controller.OperationFailedException;
+import org.jboss.as.controller.PathAddress;
+import org.jboss.as.controller.capability.RuntimeCapability;
+import org.jboss.dmr.ModelNode;
+import org.jboss.msc.service.ServiceController;
+import org.jboss.msc.service.ServiceName;
+import org.jboss.msc.service.ServiceTarget;
+import org.wildfly.security.http.HttpServerAuthenticationMechanismFactory;
 
 /**
  * Defines attributes and operations for a secure-deployment.
  *
  * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
  */
-public class SecureDeploymentDefinition extends SimpleResourceDefinition {
+final class SecureDeploymentDefinition extends AbstractAdapterConfigurationDefinition {
 
-    public static final String TAG_NAME = "secure-deployment";
+    static final String TAG_NAME = "secure-deployment";
 
-    protected static final SimpleAttributeDefinition REALM =
-            new SimpleAttributeDefinitionBuilder("realm", ModelType.STRING, true)
-                    .setXmlName("realm")
-                    .setAllowExpression(true)
-                    .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
-                    .build();
-    protected static final SimpleAttributeDefinition RESOURCE =
-            new SimpleAttributeDefinitionBuilder("resource", ModelType.STRING, true)
-                    .setXmlName("resource")
-                    .setAllowExpression(true)
-                    .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
-                    .build();
-    protected static final SimpleAttributeDefinition USE_RESOURCE_ROLE_MAPPINGS =
-            new SimpleAttributeDefinitionBuilder("use-resource-role-mappings", ModelType.BOOLEAN, true)
-            .setXmlName("use-resource-role-mappings")
-            .setAllowExpression(true)
-            .setDefaultValue(new ModelNode(false))
-            .build();
-    protected static final SimpleAttributeDefinition BEARER_ONLY =
-            new SimpleAttributeDefinitionBuilder("bearer-only", ModelType.BOOLEAN, true)
-                    .setXmlName("bearer-only")
-                    .setAllowExpression(true)
-                    .setDefaultValue(new ModelNode(false))
-                    .build();
-    protected static final SimpleAttributeDefinition ENABLE_BASIC_AUTH =
-            new SimpleAttributeDefinitionBuilder("enable-basic-auth", ModelType.BOOLEAN, true)
-                    .setXmlName("enable-basic-auth")
-                    .setAllowExpression(true)
-                    .setDefaultValue(new ModelNode(false))
-                    .build();
-    protected static final SimpleAttributeDefinition PUBLIC_CLIENT =
-            new SimpleAttributeDefinitionBuilder("public-client", ModelType.BOOLEAN, true)
-                    .setXmlName("public-client")
-                    .setAllowExpression(true)
-                    .setDefaultValue(new ModelNode(false))
-                    .build();
-    protected static final SimpleAttributeDefinition TURN_OFF_CHANGE_SESSION =
-            new SimpleAttributeDefinitionBuilder("turn-off-change-session-id-on-login", ModelType.BOOLEAN, true)
-                    .setXmlName("turn-off-change-session-id-on-login")
-                    .setAllowExpression(true)
-                    .setDefaultValue(new ModelNode(false))
-                    .build();
-    protected static final SimpleAttributeDefinition TOKEN_MINIMUM_TIME_TO_LIVE =
-            new SimpleAttributeDefinitionBuilder("token-minimum-time-to-live", ModelType.INT, true)
-                    .setXmlName("token-minimum-time-to-live")
-                    .setValidator(new IntRangeValidator(-1, true))
-                    .setAllowExpression(true)
-                    .build();
-    protected static final SimpleAttributeDefinition MIN_TIME_BETWEEN_JWKS_REQUESTS =
-            new SimpleAttributeDefinitionBuilder("min-time-between-jwks-requests", ModelType.INT, true)
-                    .setXmlName("min-time-between-jwks-requests")
-                    .setValidator(new IntRangeValidator(-1, true))
-                    .setAllowExpression(true)
-                    .build();
+    public SecureDeploymentDefinition() {
+        super(TAG_NAME, ALL_ATTRIBUTES, new SecureDeploymentAddHandler(), new SecureDeploymentRemoveHandler(), new SecureDeploymentWriteAttributeHandler());
+    }
 
+    /**
+     * Add a deployment to a realm.
+     *
+     * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+     */
+    static final class SecureDeploymentAddHandler extends AbstractAdapterConfigurationAddHandler {
 
-    protected static final List<SimpleAttributeDefinition> DEPLOYMENT_ONLY_ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
-    static {
-        DEPLOYMENT_ONLY_ATTRIBUTES.add(REALM);
-        DEPLOYMENT_ONLY_ATTRIBUTES.add(RESOURCE);
-        DEPLOYMENT_ONLY_ATTRIBUTES.add(USE_RESOURCE_ROLE_MAPPINGS);
-        DEPLOYMENT_ONLY_ATTRIBUTES.add(BEARER_ONLY);
-        DEPLOYMENT_ONLY_ATTRIBUTES.add(ENABLE_BASIC_AUTH);
-        DEPLOYMENT_ONLY_ATTRIBUTES.add(PUBLIC_CLIENT);
-        DEPLOYMENT_ONLY_ATTRIBUTES.add(TURN_OFF_CHANGE_SESSION);
-        DEPLOYMENT_ONLY_ATTRIBUTES.add(TOKEN_MINIMUM_TIME_TO_LIVE);
-        DEPLOYMENT_ONLY_ATTRIBUTES.add(MIN_TIME_BETWEEN_JWKS_REQUESTS);
-    }
+        static final String HTTP_SERVER_AUTHENTICATION_CAPABILITY = "org.wildfly.security.http-server-mechanism-factory";
+        static RuntimeCapability<Void> HTTP_SERVER_AUTHENTICATION_RUNTIME_CAPABILITY;
 
-    protected static final List<SimpleAttributeDefinition> ALL_ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
-    static {
-        ALL_ATTRIBUTES.addAll(DEPLOYMENT_ONLY_ATTRIBUTES);
-        ALL_ATTRIBUTES.addAll(SharedAttributeDefinitons.ATTRIBUTES);
-    }
+        static {
+            try {
+                HTTP_SERVER_AUTHENTICATION_RUNTIME_CAPABILITY = RuntimeCapability
+                        .Builder.of(HTTP_SERVER_AUTHENTICATION_CAPABILITY, true, HttpServerAuthenticationMechanismFactory.class)
+                        .build();
+            } catch (NoClassDefFoundError ncfe) {
+                // ignore, Elytron not present thus no capability will be published by this resource definition
+            }
+        }
 
-    private static final Map<String, SimpleAttributeDefinition> DEFINITION_LOOKUP = new HashMap<String, SimpleAttributeDefinition>();
-    static {
-        for (SimpleAttributeDefinition def : ALL_ATTRIBUTES) {
-            DEFINITION_LOOKUP.put(def.getXmlName(), def);
+        SecureDeploymentAddHandler() {
+            super(HTTP_SERVER_AUTHENTICATION_RUNTIME_CAPABILITY, ALL_ATTRIBUTES);
         }
-    }
 
-    private static SecureDeploymentWriteAttributeHandler attrHandler = new SecureDeploymentWriteAttributeHandler(ALL_ATTRIBUTES);
+        @Override
+        protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
+            super.performRuntime(context, operation, model);
+            if (HTTP_SERVER_AUTHENTICATION_RUNTIME_CAPABILITY != null) {
+                installCapability(context, operation);
+            }
+        }
 
-    public SecureDeploymentDefinition() {
-        super(PathElement.pathElement(TAG_NAME),
-                KeycloakExtension.getResourceDescriptionResolver(TAG_NAME),
-                SecureDeploymentAddHandler.INSTANCE,
-                SecureDeploymentRemoveHandler.INSTANCE);
+        static void installCapability(OperationContext context, ModelNode operation) {
+            PathAddress pathAddress = PathAddress.pathAddress(operation.get(OP_ADDR));
+            String factoryName = pathAddress.getLastElement().getValue();
+            ServiceName serviceName = context.getCapabilityServiceName(HTTP_SERVER_AUTHENTICATION_CAPABILITY, factoryName, HttpServerAuthenticationMechanismFactory.class);
+            KeycloakHttpAuthenticationFactoryService service = new KeycloakHttpAuthenticationFactoryService(factoryName);
+            ServiceTarget serviceTarget = context.getServiceTarget();
+            serviceTarget.addService(serviceName, service).setInitialMode(ServiceController.Mode.ACTIVE).install();
+        }
     }
 
-    @Override
-    public void registerOperations(ManagementResourceRegistration resourceRegistration) {
-        super.registerOperations(resourceRegistration);
-        resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);
-    }
+    /**
+     * Remove a secure-deployment from a realm.
+     *
+     * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+     */
+    static final class SecureDeploymentRemoveHandler extends AbstractAdapterConfigurationRemoveHandler {}
 
-    @Override
-    public void registerAttributes(ManagementResourceRegistration resourceRegistration) {
-        super.registerAttributes(resourceRegistration);
-        for (AttributeDefinition attrDef : ALL_ATTRIBUTES) {
-            resourceRegistration.registerReadWriteAttribute(attrDef, null, attrHandler);
+    /**
+     * Update an attribute on a secure-deployment.
+     *
+     * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+     */
+    static final class SecureDeploymentWriteAttributeHandler extends AbstractAdapterConfigurationWriteAttributeHandler {
+        SecureDeploymentWriteAttributeHandler() {
+            super(ALL_ATTRIBUTES);
         }
     }
-
-    public static SimpleAttributeDefinition lookup(String name) {
-        return DEFINITION_LOOKUP.get(name);
-    }
 }
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/SecureServerDefinition.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/SecureServerDefinition.java
new file mode 100755
index 0000000..7d8fd05
--- /dev/null
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/SecureServerDefinition.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.subsystem.adapter.extension;
+
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR;
+import static org.keycloak.subsystem.adapter.extension.KeycloakHttpServerAuthenticationMechanismFactoryDefinition.KeycloakHttpServerAuthenticationMechanismFactoryAddHandler.HTTP_SERVER_AUTHENTICATION_CAPABILITY;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+import io.undertow.io.IoCallback;
+import io.undertow.io.Sender;
+import io.undertow.server.HttpServerExchange;
+import io.undertow.server.handlers.resource.Resource;
+import io.undertow.server.handlers.resource.ResourceChangeListener;
+import io.undertow.server.handlers.resource.ResourceManager;
+import io.undertow.util.ETag;
+import io.undertow.util.MimeMappings;
+import org.jboss.as.controller.OperationContext;
+import org.jboss.as.controller.OperationFailedException;
+import org.jboss.as.controller.PathAddress;
+import org.jboss.as.controller.capability.RuntimeCapability;
+import org.jboss.as.server.mgmt.domain.ExtensibleHttpManagement;
+import org.jboss.dmr.ModelNode;
+import org.jboss.msc.service.Service;
+import org.jboss.msc.service.ServiceController.Mode;
+import org.jboss.msc.service.ServiceName;
+import org.jboss.msc.service.ServiceTarget;
+import org.jboss.msc.service.StartContext;
+import org.jboss.msc.service.StartException;
+import org.jboss.msc.service.StopContext;
+import org.jboss.msc.value.InjectedValue;
+import org.wildfly.security.http.HttpServerAuthenticationMechanismFactory;
+
+/**
+ * Defines attributes and operations for a secure-deployment.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+final class SecureServerDefinition extends AbstractAdapterConfigurationDefinition {
+
+    public static final String TAG_NAME = "secure-server";
+
+    SecureServerDefinition() {
+        super(TAG_NAME, ALL_ATTRIBUTES, new SecureServerAddHandler(), new SecureServerRemoveHandler(), new SecureServerWriteHandler());
+    }
+
+    /**
+     * A {@link AbstractAdapterConfigurationAddHandler} that exposes a {@link SecureServerDefinition}
+     * as a capability through the installation of a {@link KeycloakHttpAuthenticationFactoryService}.
+     *
+     * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+     */
+    static final class SecureServerAddHandler extends AbstractAdapterConfigurationAddHandler {
+
+        static final String HTTP_SERVER_AUTHENTICATION_CAPABILITY = "org.wildfly.security.http-server-mechanism-factory";
+        static final String HTTP_MANAGEMENT_HTTP_EXTENSIBLE_CAPABILITY = "org.wildfly.management.http.extensible";
+        static RuntimeCapability<Void> HTTP_SERVER_AUTHENTICATION_RUNTIME_CAPABILITY;
+
+        static {
+            try {
+                HTTP_SERVER_AUTHENTICATION_RUNTIME_CAPABILITY = RuntimeCapability
+                        .Builder.of(HTTP_SERVER_AUTHENTICATION_CAPABILITY, true, HttpServerAuthenticationMechanismFactory.class)
+                        .build();
+            } catch (NoClassDefFoundError ncfe) {
+                // ignore, Elytron not present thus no capability will be published by this resource definition
+            }
+        }
+
+        SecureServerAddHandler() {
+            super(HTTP_SERVER_AUTHENTICATION_RUNTIME_CAPABILITY, ALL_ATTRIBUTES);
+        }
+
+        @Override
+        protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
+            super.performRuntime(context, operation, model);
+            if (HTTP_SERVER_AUTHENTICATION_RUNTIME_CAPABILITY != null) {
+                installCapability(context, operation);
+            }
+        }
+
+        static void installCapability(OperationContext context, ModelNode operation) throws OperationFailedException {
+            PathAddress pathAddress = PathAddress.pathAddress(operation.get(OP_ADDR));
+            String factoryName = pathAddress.getLastElement().getValue();
+            ServiceName serviceName = context.getCapabilityServiceName(HTTP_SERVER_AUTHENTICATION_CAPABILITY, factoryName, HttpServerAuthenticationMechanismFactory.class);
+            boolean publicClient = SecureServerDefinition.PUBLIC_CLIENT.resolveModelAttribute(context, operation).asBoolean(false);
+
+            if (!publicClient) {
+                throw new OperationFailedException("Only public clients are allowed to have their configuration exposed through the management interface");
+            }
+
+            KeycloakHttpAuthenticationFactoryService service = new KeycloakHttpAuthenticationFactoryService(factoryName);
+            ServiceTarget serviceTarget = context.getServiceTarget();
+            InjectedValue<ExtensibleHttpManagement> injectedValue = new InjectedValue<>();
+            serviceTarget.addService(serviceName.append("http-management-context"), createHttpManagementConfigContextService(factoryName, injectedValue))
+                    .addDependency(context.getCapabilityServiceName(HTTP_MANAGEMENT_HTTP_EXTENSIBLE_CAPABILITY, ExtensibleHttpManagement.class), ExtensibleHttpManagement.class, injectedValue).setInitialMode(Mode.ACTIVE).install();
+            serviceTarget.addService(serviceName, service).setInitialMode(Mode.ACTIVE).install();
+        }
+    }
+
+    /**
+     * A {@link AbstractAdapterConfigurationRemoveHandler} that handles the removal of {@link SecureServerDefinition}.
+     *
+     * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+     */
+    static final class SecureServerRemoveHandler extends AbstractAdapterConfigurationRemoveHandler {
+        @Override
+        protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
+            super.performRuntime(context, operation, model);
+            PathAddress pathAddress = PathAddress.pathAddress(operation.get(OP_ADDR));
+            String factoryName = pathAddress.getLastElement().getValue();
+            ServiceName serviceName = context.getCapabilityServiceName(HTTP_SERVER_AUTHENTICATION_CAPABILITY, factoryName, HttpServerAuthenticationMechanismFactory.class);
+            context.removeService(serviceName);
+        }
+
+        @Override
+        protected void recoverServices(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
+            super.recoverServices(context, operation, model);
+            SecureServerDefinition.SecureServerAddHandler.installCapability(context, operation);
+        }
+    }
+
+    /**
+     * A {@link AbstractAdapterConfigurationWriteAttributeHandler} that updates attributes on a {@link SecureServerDefinition}.
+     *
+     * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+     */
+    static final class SecureServerWriteHandler extends AbstractAdapterConfigurationWriteAttributeHandler {
+        SecureServerWriteHandler() {
+            super(ALL_ATTRIBUTES);
+        }
+    }
+
+    private static Service<Void> createHttpManagementConfigContextService(final String factoryName, final InjectedValue<ExtensibleHttpManagement> httpConfigContext) {
+        final String contextName = "/keycloak/adapter/" + factoryName + "/";
+        return new Service<Void>() {
+            public void start(StartContext startContext) throws StartException {
+                ExtensibleHttpManagement extensibleHttpManagement = (ExtensibleHttpManagement)httpConfigContext.getValue();
+                extensibleHttpManagement.addStaticContext(contextName, new ResourceManager() {
+                    public Resource getResource(final String path) throws IOException {
+                        KeycloakAdapterConfigService adapterConfigService = KeycloakAdapterConfigService.getInstance();
+                        final String config = adapterConfigService.getJSON(factoryName);
+
+                        if (config == null) {
+                            return null;
+                        }
+
+                        return new Resource() {
+                            public String getPath() {
+                                return null;
+                            }
+
+                            public Date getLastModified() {
+                                return null;
+                            }
+
+                            public String getLastModifiedString() {
+                                return null;
+                            }
+
+                            public ETag getETag() {
+                                return null;
+                            }
+
+                            public String getName() {
+                                return null;
+                            }
+
+                            public boolean isDirectory() {
+                                return false;
+                            }
+
+                            public List<Resource> list() {
+                                return Collections.emptyList();
+                            }
+
+                            public String getContentType(MimeMappings mimeMappings) {
+                                return "application/json";
+                            }
+
+                            public void serve(Sender sender, HttpServerExchange exchange, IoCallback completionCallback) {
+                                sender.send(config);
+                            }
+
+                            public Long getContentLength() {
+                                return Long.valueOf((long)config.length());
+                            }
+
+                            public String getCacheKey() {
+                                return null;
+                            }
+
+                            public File getFile() {
+                                return null;
+                            }
+
+                            public Path getFilePath() {
+                                return null;
+                            }
+
+                            public File getResourceManagerRoot() {
+                                return null;
+                            }
+
+                            public Path getResourceManagerRootPath() {
+                                return null;
+                            }
+
+                            public URL getUrl() {
+                                return null;
+                            }
+                        };
+                    }
+
+                    public boolean isResourceChangeListenerSupported() {
+                        return false;
+                    }
+
+                    public void registerResourceChangeListener(ResourceChangeListener listener) {
+                    }
+
+                    public void removeResourceChangeListener(ResourceChangeListener listener) {
+                    }
+
+                    public void close() throws IOException {
+                    }
+                });
+            }
+
+            public void stop(StopContext stopContext) {
+                ((ExtensibleHttpManagement)httpConfigContext.getValue()).removeContext(contextName);
+            }
+
+            public Void getValue() throws IllegalStateException, IllegalArgumentException {
+                return null;
+            }
+        };
+    }
+}
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/extension/LocalDescriptions.properties b/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/extension/LocalDescriptions.properties
index c9cea77..f6097ae 100755
--- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/extension/LocalDescriptions.properties
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/extension/LocalDescriptions.properties
@@ -20,6 +20,8 @@ keycloak.subsystem.add=Operation Adds Keycloak adapter subsystem
 keycloak.subsystem.remove=Operation removes Keycloak adapter subsystem
 keycloak.subsystem.realm=A Keycloak realm.
 keycloak.subsystem.secure-deployment=A deployment secured by Keycloak.
+keycloak.subsystem.secure-server=A configuration exposed to the server.
+keycloak.subsystem.http-server-mechanism-factory=A http-server-mechanism-factory exposed to the server.
 
 keycloak.realm=A Keycloak realm.
 keycloak.realm.add=Add a realm definition to the subsystem.
@@ -90,7 +92,48 @@ keycloak.secure-deployment.token-minimum-time-to-live=The adapter will refresh t
 keycloak.secure-deployment.min-time-between-jwks-requests=If adapter recognize token signed by unknown public key, it will try to download new public key from keycloak server. However it won't try to download if already tried it in less than 'min-time-between-jwks-requests' seconds
 keycloak.secure-deployment.ignore-oauth-query-parameter=disable query parameter parsing for access_token
 
+keycloak.secure-server=A deployment secured by Keycloak
+keycloak.secure-server.add=Add a deployment to be secured by Keycloak
+keycloak.secure-server.realm=Keycloak realm
+keycloak.secure-server.remove=Remove a deployment to be secured by Keycloak
+keycloak.secure-server.realm-public-key=Public key of the realm
+keycloak.secure-server.auth-server-url=Base URL of the Realm Auth Server
+keycloak.secure-server.disable-trust-manager=Adapter will not use a trust manager when making adapter HTTPS requests
+keycloak.secure-server.ssl-required=Specify if SSL is required (valid values are all, external and none)
+keycloak.secure-server.allow-any-hostname=SSL Setting
+keycloak.secure-server.truststore=Truststore used for adapter client HTTPS requests
+keycloak.secure-server.truststore-password=Password of the Truststore
+keycloak.secure-server.connection-pool-size=Connection pool size for the client used by the adapter
+keycloak.secure-server.resource=Application name
+keycloak.secure-server.use-resource-role-mappings=Use resource level permissions from token
+keycloak.secure-server.credentials=Adapter credentials
+keycloak.secure-server.redirect-rewrite-rule=Apply a rewrite rule for the redirect URI
+keycloak.secure-server.bearer-only=Bearer Token Auth only
+keycloak.secure-server.enable-basic-auth=Enable Basic Authentication
+keycloak.secure-server.public-client=Public client
+keycloak.secure-server.enable-cors=Enable Keycloak CORS support
+keycloak.secure-server.autodetect-bearer-only=autodetect bearer-only requests
+keycloak.secure-server.client-keystore=n/a
+keycloak.secure-server.client-keystore-password=n/a
+keycloak.secure-server.client-key-password=n/a
+keycloak.secure-server.cors-max-age=CORS max-age header
+keycloak.secure-server.cors-allowed-headers=CORS allowed headers
+keycloak.secure-server.cors-allowed-methods=CORS allowed methods
+keycloak.secure-server.cors-exposed-headers=CORS exposed headers
+keycloak.secure-server.expose-token=Enable secure URL that exposes access token
+keycloak.secure-server.auth-server-url-for-backend-requests=URL to use to make background calls to auth server
+keycloak.secure-server.always-refresh-token=Refresh token on every single web request
+keycloak.secure-server.register-node-at-startup=Cluster setting
+keycloak.secure-server.register-node-period=how often to re-register node
+keycloak.secure-server.token-store=cookie or session storage for auth session data
+keycloak.secure-server.principal-attribute=token attribute to use to set Principal name
+keycloak.secure-server.turn-off-change-session-id-on-login=The session id is changed by default on a successful login.  Change this to true if you want to turn this off
+keycloak.secure-server.token-minimum-time-to-live=The adapter will refresh the token if the current token is expired OR will expire in 'token-minimum-time-to-live' seconds or less
+keycloak.secure-server.min-time-between-jwks-requests=If adapter recognize token signed by unknown public key, it will try to download new public key from keycloak server. However it won't try to download if already tried it in less than 'min-time-between-jwks-requests' seconds
+keycloak.secure-server.ignore-oauth-query-parameter=disable query parameter parsing for access_token
+
 keycloak.secure-deployment.credential=Credential value
+keycloak.secure-server.credential=Credential value
 
 keycloak.credential=Credential
 keycloak.credential.value=Credential value
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd b/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd
index d8f5bc3..caa147d 100755
--- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd
@@ -38,6 +38,7 @@
         <xs:choice minOccurs="0" maxOccurs="unbounded">
             <xs:element name="realm" maxOccurs="unbounded" minOccurs="0" type="realm-type"/>
             <xs:element name="secure-deployment" maxOccurs="unbounded" minOccurs="0" type="secure-deployment-type"/>
+            <xs:element name="secure-server" maxOccurs="unbounded" minOccurs="0" type="secure-deployment-type"/>
         </xs:choice>
     </xs:complexType>
 
@@ -84,7 +85,7 @@
             <xs:element name="allow-any-hostname" type="xs:boolean" minOccurs="0" maxOccurs="1" />
             <xs:element name="use-resource-role-mappings" type="xs:boolean" minOccurs="0" maxOccurs="1" />
             <xs:element name="cors-max-age" type="xs:integer" minOccurs="0" maxOccurs="1"/>
-            <xs:element name="auth-server-url" type="xs:string" minOccurs="1" maxOccurs="1"/>
+            <xs:element name="auth-server-url" type="xs:string" minOccurs="0" maxOccurs="1"/>
             <xs:element name="realm" type="xs:string" minOccurs="1" maxOccurs="1"/>
             <xs:element name="disable-trust-manager" type="xs:boolean" minOccurs="0" maxOccurs="1" />
             <xs:element name="cors-allowed-methods" type="xs:string" minOccurs="0" maxOccurs="1"/>
@@ -99,9 +100,9 @@
             <xs:element name="connection-pool-size" type="xs:integer" minOccurs="0" maxOccurs="1"/>
             <xs:element name="expose-token" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
             <xs:element name="ssl-required" type="xs:string" minOccurs="0" maxOccurs="1" />
-            <xs:element name="realm-public-key" type="xs:string" minOccurs="1" maxOccurs="1"/>
-            <xs:element name="credential" type="credential-type" minOccurs="1" maxOccurs="1"/>
-            <xs:element name="redirect-rewrite-rule" type="redirect-rewrite-rule-type" minOccurs="1" maxOccurs="1"/>
+            <xs:element name="realm-public-key" type="xs:string" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="credential" type="credential-type" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="redirect-rewrite-rule" type="redirect-rewrite-rule-type" minOccurs="0" maxOccurs="1"/>
             <xs:element name="auth-server-url-for-backend-requests" type="xs:string" minOccurs="0" maxOccurs="1"/>
             <xs:element name="always-refresh-token" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
             <xs:element name="register-node-at-startup" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/test/java/org/keycloak/subsystem/adapter/extension/SubsystemParsingTestCase.java b/adapters/oidc/wildfly/wildfly-subsystem/src/test/java/org/keycloak/subsystem/adapter/extension/SubsystemParsingTestCase.java
index 9d5f87a..4adad9f 100755
--- a/adapters/oidc/wildfly/wildfly-subsystem/src/test/java/org/keycloak/subsystem/adapter/extension/SubsystemParsingTestCase.java
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/test/java/org/keycloak/subsystem/adapter/extension/SubsystemParsingTestCase.java
@@ -73,7 +73,7 @@ public class SubsystemParsingTestCase extends AbstractSubsystemBaseTest {
         ModelNode deployment = new ModelNode();
         deployment.get("realm").set("demo");
         deployment.get("resource").set("customer-portal");
-        service.addSecureDeployment(deploymentOp, deployment);
+        service.addSecureDeployment(deploymentOp, deployment, false);
 
         addCredential(addr, service, "secret", "secret1");
         addCredential(addr, service, "jwt.client-keystore-file", "/tmp/foo.jks");
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/test/resources/org/keycloak/subsystem/adapter/extension/keycloak-1.1.xml b/adapters/oidc/wildfly/wildfly-subsystem/src/test/resources/org/keycloak/subsystem/adapter/extension/keycloak-1.1.xml
index 246d768..fce5c41 100755
--- a/adapters/oidc/wildfly/wildfly-subsystem/src/test/resources/org/keycloak/subsystem/adapter/extension/keycloak-1.1.xml
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/test/resources/org/keycloak/subsystem/adapter/extension/keycloak-1.1.xml
@@ -40,6 +40,10 @@
         <token-store>session</token-store>
         <principal-attribute>sub</principal-attribute>
     </realm>
+    <realm name="jboss-infra">
+        <realm-public-key>MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqKoq+a9MgXepmsPJDmo45qswuChW9pWjanX68oIBuI4hGvhQxFHryCow230A+sr7tFdMQMt8f1l/ysmV/fYAuW29WaoY4kI4Ou1yYPuwywKSsxT6PooTs83hKyZ1h4LZMj5DkLGDDDyVRHob2WmPaYg9RGVRw3iGGsD/p+Yb+L/gnBYQnZZ7lYqmN7h36p5CkzzlgXQA1Ha8sQxL+rJNH8+sZm0vBrKsoII3Of7TqHGsm1RwFV3XCuGJ7S61AbjJMXL5DQgJl9Z5scvxGAyoRLKC294UgMnQdzyBTMPw2GybxkRKmiK2KjQKmcopmrJp/Bt6fBR6ZkGSs9qUlxGHgwIDAQAB</realm-public-key>
+        <auth-server-url>http://localhost:8180/auth</auth-server-url>
+    </realm>
     <secure-deployment name="web-console">
         <realm>master</realm>
         <resource>web-console</resource>
@@ -69,4 +73,16 @@
         </credential>
         <redirect-rewrite-rule name="^/wsmaster/api/(.*)$">/api/$1/</redirect-rewrite-rule>
     </secure-deployment>
+    <secure-deployment name="wildfly-management">
+        <realm>jboss-infra</realm>
+        <resource>wildfly-management</resource>
+        <bearer-only>true</bearer-only>
+        <ssl-required>EXTERNAL</ssl-required>
+        <principal-attribute>preferred_username</principal-attribute>
+    </secure-deployment>
+    <secure-server name="wildfly-console">
+        <realm>jboss-infra</realm>
+        <resource>wildfly-console</resource>
+        <public-client>true</public-client>
+    </secure-server>
 </subsystem>
\ No newline at end of file
diff --git a/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/ElytronHttpFacade.java b/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/ElytronHttpFacade.java
index 4472af7..4941275 100644
--- a/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/ElytronHttpFacade.java
+++ b/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/ElytronHttpFacade.java
@@ -50,7 +50,9 @@ import java.net.InetSocketAddress;
 import java.net.URI;
 import java.net.URLDecoder;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.function.Consumer;
 
 /**
@@ -66,6 +68,7 @@ class ElytronHttpFacade implements OIDCHttpFacade {
     private ElytronAccount account;
     private SecurityIdentity securityIdentity;
     private boolean restored;
+    private final Map<String, String> headers = new HashMap<>();
 
     public ElytronHttpFacade(HttpServerRequest request, AdapterDeploymentContext deploymentContext, CallbackHandler handler) {
         this.request = request;
@@ -261,6 +264,7 @@ class ElytronHttpFacade implements OIDCHttpFacade {
     @Override
     public Response getResponse() {
         return new Response() {
+
             @Override
             public void setStatus(final int status) {
                 responseConsumer = responseConsumer.andThen(response -> response.setStatusCode(status));
@@ -268,7 +272,17 @@ class ElytronHttpFacade implements OIDCHttpFacade {
 
             @Override
             public void addHeader(final String name, final String value) {
-                responseConsumer = responseConsumer.andThen(response -> response.addResponseHeader(name, value));
+                headers.put(name, value);
+                responseConsumer = responseConsumer.andThen(new Consumer<HttpServerResponse>() {
+                    @Override
+                    public void accept(HttpServerResponse response) {
+                        String latestValue = headers.get(name);
+
+                        if (latestValue.equals(value)) {
+                            response.addResponseHeader(name, latestValue);
+                        }
+                    }
+                });
             }
 
             @Override
diff --git a/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/KeycloakSecurityRealm.java b/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/KeycloakSecurityRealm.java
index 6042ec8..eef2b26 100644
--- a/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/KeycloakSecurityRealm.java
+++ b/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/KeycloakSecurityRealm.java
@@ -17,6 +17,7 @@
 package org.keycloak.adapters.elytron;
 
 import java.security.Principal;
+import java.security.spec.AlgorithmParameterSpec;
 import java.util.Set;
 
 import org.keycloak.KeycloakPrincipal;
@@ -54,7 +55,7 @@ public class KeycloakSecurityRealm implements SecurityRealm {
             }
 
             @Override
-            public SupportLevel getCredentialAcquireSupport(Class<? extends Credential> credentialType, String algorithmName) throws RealmUnavailableException {
+            public SupportLevel getCredentialAcquireSupport(Class<? extends Credential> credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException {
                 return SupportLevel.UNSUPPORTED;
             }
 
@@ -92,7 +93,7 @@ public class KeycloakSecurityRealm implements SecurityRealm {
     }
 
     @Override
-    public SupportLevel getCredentialAcquireSupport(Class<? extends Credential> credentialType, String algorithmName) throws RealmUnavailableException {
+    public SupportLevel getCredentialAcquireSupport(Class<? extends Credential> credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException {
         return SupportLevel.UNSUPPORTED;
     }
 
diff --git a/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/KeycloakSecurityRealm.java b/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/KeycloakSecurityRealm.java
index 3207835..f79b60d 100644
--- a/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/KeycloakSecurityRealm.java
+++ b/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/KeycloakSecurityRealm.java
@@ -17,6 +17,7 @@
 package org.keycloak.adapters.saml.elytron;
 
 import java.security.Principal;
+import java.security.spec.AlgorithmParameterSpec;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -53,7 +54,7 @@ public class KeycloakSecurityRealm implements SecurityRealm {
             }
 
             @Override
-            public SupportLevel getCredentialAcquireSupport(Class<? extends Credential> credentialType, String algorithmName) throws RealmUnavailableException {
+            public SupportLevel getCredentialAcquireSupport(Class<? extends Credential> aClass, String s, AlgorithmParameterSpec algorithmParameterSpec) throws RealmUnavailableException {
                 return SupportLevel.UNSUPPORTED;
             }
 
@@ -90,7 +91,7 @@ public class KeycloakSecurityRealm implements SecurityRealm {
     }
 
     @Override
-    public SupportLevel getCredentialAcquireSupport(Class<? extends Credential> credentialType, String algorithmName) throws RealmUnavailableException {
+    public SupportLevel getCredentialAcquireSupport(Class<? extends Credential> credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException {
         return SupportLevel.UNSUPPORTED;
     }
 
diff --git a/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java
index 4dcea95..44458fd 100755
--- a/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java
@@ -55,6 +55,7 @@ public class UserRepresentation {
     protected List<String> realmRoles;
     protected Map<String, List<String>> clientRoles;
     protected List<UserConsentRepresentation> clientConsents;
+    protected Integer notBefore;
 
     @Deprecated
     protected Map<String, List<String>> applicationRoles;
@@ -216,6 +217,14 @@ public class UserRepresentation {
         this.clientConsents = clientConsents;
     }
 
+    public Integer getNotBefore() {
+        return notBefore;
+    }
+
+    public void setNotBefore(Integer notBefore) {
+        this.notBefore = notBefore;
+    }
+
     @Deprecated
     public Map<String, List<String>> getApplicationRoles() {
         return applicationRoles;
diff --git a/distribution/adapters/js-adapter-zip/assembly.xml b/distribution/adapters/js-adapter-zip/assembly.xml
index 14e0cc0..8111f78 100755
--- a/distribution/adapters/js-adapter-zip/assembly.xml
+++ b/distribution/adapters/js-adapter-zip/assembly.xml
@@ -30,6 +30,7 @@
             <outputDirectory></outputDirectory>
             <includes>
                 <include>**/*.js</include>
+                <include>**/*.map</include>
                 <include>**/*.d.ts</include>
                 <include>**/*.html</include>
             </includes>
diff --git a/distribution/adapters/js-adapter-zip/pom.xml b/distribution/adapters/js-adapter-zip/pom.xml
index be37dc8..8dcee56 100755
--- a/distribution/adapters/js-adapter-zip/pom.xml
+++ b/distribution/adapters/js-adapter-zip/pom.xml
@@ -50,7 +50,7 @@
                                     <groupId>org.keycloak</groupId>
                                     <artifactId>keycloak-js-adapter</artifactId>
                                     <outputDirectory>${project.build.directory}/unpacked/js-adapter</outputDirectory>
-                                    <includes>*.js,*.d.ts</includes>                                 
+                                    <includes>*.js,*.map,*.d.ts</includes>
                                 </artifactItem>
                             </artifactItems>
                             <excludes>**/welcome-content/*</excludes>
diff --git a/distribution/adapters/shared-cli/adapter-elytron-install.cli b/distribution/adapters/shared-cli/adapter-elytron-install.cli
index 16f17ce..6ef26d0 100644
--- a/distribution/adapters/shared-cli/adapter-elytron-install.cli
+++ b/distribution/adapters/shared-cli/adapter-elytron-install.cli
@@ -36,10 +36,10 @@ else
 end-if
 
 if (outcome != success) of /subsystem=elytron/aggregate-http-server-mechanism-factory=keycloak-http-server-mechanism-factory:read-resource
-    /subsystem=elytron/aggregate-http-server-mechanism-factory=keycloak-http-server-mechanism-factory:add(http-server-factories=[keycloak-oidc-http-server-mechanism-factory, global])
+    /subsystem=elytron/aggregate-http-server-mechanism-factory=keycloak-http-server-mechanism-factory:add(http-server-mechanism-factories=[keycloak-oidc-http-server-mechanism-factory, global])
 else
     echo Keycloak HTTP Mechanism Factory already installed. Trying to install Keycloak OpenID Connect HTTP Mechanism Factory.
-    /subsystem=elytron/aggregate-http-server-mechanism-factory=keycloak-http-server-mechanism-factory:list-add(name=http-server-factories, value=keycloak-oidc-http-server-mechanism-factory)
+    /subsystem=elytron/aggregate-http-server-mechanism-factory=keycloak-http-server-mechanism-factory:list-add(name=http-server-mechanism-factories, value=keycloak-oidc-http-server-mechanism-factory)
 end-if
 
 
diff --git a/distribution/adapters/wildfly-adapter/cli/adapter-elytron-install-offline.cli b/distribution/adapters/wildfly-adapter/cli/adapter-elytron-install-offline.cli
index 8e0335a..76ec08c 100644
--- a/distribution/adapters/wildfly-adapter/cli/adapter-elytron-install-offline.cli
+++ b/distribution/adapters/wildfly-adapter/cli/adapter-elytron-install-offline.cli
@@ -38,10 +38,10 @@ else
 end-if
 
 if (outcome != success) of /subsystem=elytron/aggregate-http-server-mechanism-factory=keycloak-http-server-mechanism-factory:read-resource
-    /subsystem=elytron/aggregate-http-server-mechanism-factory=keycloak-http-server-mechanism-factory:add(http-server-factories=[keycloak-oidc-http-server-mechanism-factory, global])
+    /subsystem=elytron/aggregate-http-server-mechanism-factory=keycloak-http-server-mechanism-factory:add(http-server-mechanism-factories=[keycloak-oidc-http-server-mechanism-factory, global])
 else
     echo Keycloak HTTP Mechanism Factory already installed. Trying to install Keycloak OpenID Connect HTTP Mechanism Factory.
-    /subsystem=elytron/aggregate-http-server-mechanism-factory=keycloak-http-server-mechanism-factory:list-add(name=http-server-factories, value=keycloak-oidc-http-server-mechanism-factory)
+    /subsystem=elytron/aggregate-http-server-mechanism-factory=keycloak-http-server-mechanism-factory:list-add(name=http-server-mechanism-factories, value=keycloak-oidc-http-server-mechanism-factory)
 end-if
 
 
diff --git a/distribution/demo-dist/src/main/xslt/standalone.xsl b/distribution/demo-dist/src/main/xslt/standalone.xsl
index 5ea7e93..f800ae2 100755
--- a/distribution/demo-dist/src/main/xslt/standalone.xsl
+++ b/distribution/demo-dist/src/main/xslt/standalone.xsl
@@ -17,10 +17,10 @@
 
 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                 xmlns:xalan="http://xml.apache.org/xalan"
-                xmlns:j="urn:jboss:domain:4.0"
-                xmlns:ds="urn:jboss:domain:datasources:4.0"
+                xmlns:j="urn:jboss:domain:5.0"
+                xmlns:ds="urn:jboss:domain:datasources:5.0"
                 xmlns:k="urn:jboss:domain:keycloak:1.1"
-                xmlns:sec="urn:jboss:domain:security:1.2"
+                xmlns:sec="urn:jboss:domain:security:2.0"
                 version="2.0"
                 exclude-result-prefixes="xalan j ds k sec">
 
diff --git a/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-wildfly-subsystem/main/module.xml b/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-wildfly-subsystem/main/module.xml
index 025f152..32849fc 100755
--- a/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-wildfly-subsystem/main/module.xml
+++ b/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-wildfly-subsystem/main/module.xml
@@ -38,5 +38,9 @@
         <module name="org.jboss.vfs"/>
         <module name="org.jboss.as.web-common"/>
         <module name="org.jboss.metadata"/>
+        <module name="org.keycloak.keycloak-adapter-core"/>
+        <module name="org.keycloak.keycloak-wildfly-elytron-oidc-adapter"/>
+        <module name="org.wildfly.security.elytron"/>
+        <module name="io.undertow.core"/>
     </dependencies>
 </module>
diff --git a/distribution/saml-adapters/shared-cli/adapter-elytron-install-saml.cli b/distribution/saml-adapters/shared-cli/adapter-elytron-install-saml.cli
index 1f24085..a76109b 100755
--- a/distribution/saml-adapters/shared-cli/adapter-elytron-install-saml.cli
+++ b/distribution/saml-adapters/shared-cli/adapter-elytron-install-saml.cli
@@ -36,10 +36,10 @@ else
 end-if
 
 if (outcome != success) of /subsystem=elytron/aggregate-http-server-mechanism-factory=keycloak-http-server-mechanism-factory:read-resource
-    /subsystem=elytron/aggregate-http-server-mechanism-factory=keycloak-http-server-mechanism-factory:add(http-server-factories=[keycloak-saml-http-server-mechanism-factory, global])
+    /subsystem=elytron/aggregate-http-server-mechanism-factory=keycloak-http-server-mechanism-factory:add(http-server-mechanism-factories=[keycloak-saml-http-server-mechanism-factory, global])
 else
     echo Keycloak HTTP Mechanism Factory already installed. Trying to install Keycloak SAML HTTP Mechanism Factory.
-    /subsystem=elytron/aggregate-http-server-mechanism-factory=keycloak-http-server-mechanism-factory:list-add(name=http-server-factories, value=keycloak-saml-http-server-mechanism-factory)
+    /subsystem=elytron/aggregate-http-server-mechanism-factory=keycloak-http-server-mechanism-factory:list-add(name=http-server-mechanism-factories, value=keycloak-saml-http-server-mechanism-factory)
 end-if
 
 if (outcome != success) of /subsystem=elytron/http-authentication-factory=keycloak-http-authentication:read-resource
diff --git a/distribution/server-overlay/src/main/modules/layers.conf b/distribution/server-overlay/src/main/modules/layers.conf
new file mode 100644
index 0000000..74f4485
--- /dev/null
+++ b/distribution/server-overlay/src/main/modules/layers.conf
@@ -0,0 +1 @@
+layers=keycloak
\ No newline at end of file
diff --git a/examples/providers/rest/src/main/java/org/keycloak/examples/rest/HelloResourceProvider.java b/examples/providers/rest/src/main/java/org/keycloak/examples/rest/HelloResourceProvider.java
index aebd677..ebd8d1c 100644
--- a/examples/providers/rest/src/main/java/org/keycloak/examples/rest/HelloResourceProvider.java
+++ b/examples/providers/rest/src/main/java/org/keycloak/examples/rest/HelloResourceProvider.java
@@ -22,7 +22,6 @@ import org.keycloak.services.resource.RealmResourceProvider;
 
 import javax.ws.rs.GET;
 import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@@ -41,7 +40,7 @@ public class HelloResourceProvider implements RealmResourceProvider {
     }
 
     @GET
-    @Produces(MediaType.TEXT_PLAIN)
+    @Produces("text/plain; charset=utf-8")
     public String get() {
         String name = session.getContext().getRealm().getDisplayName();
         if (name == null) {
diff --git a/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java
index 86f6074..a17e724 100755
--- a/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java
+++ b/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java
@@ -262,7 +262,14 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon
         sessionCacheConfiguration = sessionConfigBuilder.build();
         cacheManager.defineConfiguration(InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME, sessionCacheConfiguration);
 
-        cacheManager.defineConfiguration(InfinispanConnectionProvider.LOGIN_FAILURE_CACHE_NAME, sessionCacheConfigurationBase);
+        if (jdgEnabled) {
+            sessionConfigBuilder = new ConfigurationBuilder();
+            sessionConfigBuilder.read(sessionCacheConfigurationBase);
+            configureRemoteCacheStore(sessionConfigBuilder, async, InfinispanConnectionProvider.LOGIN_FAILURE_CACHE_NAME, KcRemoteStoreConfigurationBuilder.class);
+        }
+        sessionCacheConfiguration = sessionConfigBuilder.build();
+        cacheManager.defineConfiguration(InfinispanConnectionProvider.LOGIN_FAILURE_CACHE_NAME, sessionCacheConfiguration);
+
         cacheManager.defineConfiguration(InfinispanConnectionProvider.AUTHENTICATION_SESSIONS_CACHE_NAME, sessionCacheConfigurationBase);
 
         // Retrieve caches to enforce rebalance
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/ResourceUpdatedEvent.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/ResourceUpdatedEvent.java
index cc30f23..3f85a6d 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/ResourceUpdatedEvent.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/ResourceUpdatedEvent.java
@@ -33,8 +33,9 @@ public class ResourceUpdatedEvent extends InvalidationEvent implements Authoriza
     private String type;
     private String uri;
     private Set<String> scopes;
+    private String owner;
 
-    public static ResourceUpdatedEvent create(String id, String name, String type, String uri, Set<String> scopes, String serverId) {
+    public static ResourceUpdatedEvent create(String id, String name, String type, String uri, Set<String> scopes, String serverId, String owner) {
         ResourceUpdatedEvent event = new ResourceUpdatedEvent();
         event.id = id;
         event.name = name;
@@ -42,6 +43,7 @@ public class ResourceUpdatedEvent extends InvalidationEvent implements Authoriza
         event.uri = uri;
         event.scopes = scopes;
         event.serverId = serverId;
+        event.owner = owner;
         return event;
     }
 
@@ -57,6 +59,6 @@ public class ResourceUpdatedEvent extends InvalidationEvent implements Authoriza
 
     @Override
     public void addInvalidations(StoreFactoryCacheManager cache, Set<String> invalidations) {
-        cache.resourceUpdated(id, name, type, uri, scopes, serverId, invalidations);
+        cache.resourceUpdated(id, name, type, uri, scopes, serverId, owner, invalidations);
     }
 }
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/ResourceAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/ResourceAdapter.java
index dc721b7..5e9b7d3 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/ResourceAdapter.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/ResourceAdapter.java
@@ -46,7 +46,7 @@ public class ResourceAdapter implements Resource, CachedModel<Resource> {
     @Override
     public Resource getDelegateForUpdate() {
         if (updated == null) {
-            cacheSession.registerResourceInvalidation(cached.getId(), cached.getName(), cached.getType(), cached.getUri(), cached.getScopesIds(), cached.getResourceServerId());
+            cacheSession.registerResourceInvalidation(cached.getId(), cached.getName(), cached.getType(), cached.getUri(), cached.getScopesIds(), cached.getResourceServerId(), cached.getOwner());
             updated = cacheSession.getResourceStoreDelegate().findById(cached.getId(), cached.getResourceServerId());
             if (updated == null) throw new IllegalStateException("Not found in database");
         }
@@ -95,7 +95,7 @@ public class ResourceAdapter implements Resource, CachedModel<Resource> {
     @Override
     public void setName(String name) {
         getDelegateForUpdate();
-        cacheSession.registerResourceInvalidation(cached.getId(), name, cached.getType(), cached.getUri(), cached.getScopesIds(), cached.getResourceServerId());
+        cacheSession.registerResourceInvalidation(cached.getId(), name, cached.getType(), cached.getUri(), cached.getScopesIds(), cached.getResourceServerId(), cached.getOwner());
         updated.setName(name);
 
     }
@@ -127,7 +127,7 @@ public class ResourceAdapter implements Resource, CachedModel<Resource> {
     @Override
     public void setUri(String uri) {
         getDelegateForUpdate();
-        cacheSession.registerResourceInvalidation(cached.getId(), cached.getName(), cached.getType(), uri, cached.getScopesIds(), cached.getResourceServerId());
+        cacheSession.registerResourceInvalidation(cached.getId(), cached.getName(), cached.getType(), uri, cached.getScopesIds(), cached.getResourceServerId(), cached.getOwner());
         updated.setUri(uri);
     }
 
@@ -140,7 +140,7 @@ public class ResourceAdapter implements Resource, CachedModel<Resource> {
     @Override
     public void setType(String type) {
         getDelegateForUpdate();
-        cacheSession.registerResourceInvalidation(cached.getId(), cached.getName(), type, cached.getUri(), cached.getScopesIds(), cached.getResourceServerId());
+        cacheSession.registerResourceInvalidation(cached.getId(), cached.getName(), type, cached.getUri(), cached.getScopesIds(), cached.getResourceServerId(), cached.getOwner());
         updated.setType(type);
 
     }
@@ -168,7 +168,7 @@ public class ResourceAdapter implements Resource, CachedModel<Resource> {
     @Override
     public void updateScopes(Set<Scope> scopes) {
         getDelegateForUpdate();
-        cacheSession.registerResourceInvalidation(cached.getId(), cached.getName(), cached.getType(), cached.getUri(), scopes.stream().map(scope1 -> scope1.getId()).collect(Collectors.toSet()), cached.getResourceServerId());
+        cacheSession.registerResourceInvalidation(cached.getId(), cached.getName(), cached.getType(), cached.getUri(), scopes.stream().map(scope1 -> scope1.getId()).collect(Collectors.toSet()), cached.getResourceServerId(), cached.getOwner());
         updated.updateScopes(scopes);
     }
 
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheManager.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheManager.java
index 63eb8a7..e9853d6 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheManager.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheManager.java
@@ -75,9 +75,10 @@ public class StoreFactoryCacheManager extends CacheManager {
         addInvalidations(InScopePredicate.create().scope(id), invalidations);
     }
 
-    public void resourceUpdated(String id, String name, String type, String uri, Set<String> scopes, String serverId, Set<String> invalidations) {
+    public void resourceUpdated(String id, String name, String type, String uri, Set<String> scopes, String serverId, String owner, Set<String> invalidations) {
         invalidations.add(id);
         invalidations.add(StoreFactoryCacheSession.getResourceByNameCacheKey(name, serverId));
+        invalidations.add(StoreFactoryCacheSession.getResourceByOwnerCacheKey(owner, serverId));
 
         if (type != null) {
             invalidations.add(StoreFactoryCacheSession.getResourceByTypeCacheKey(type, serverId));
@@ -97,8 +98,7 @@ public class StoreFactoryCacheManager extends CacheManager {
     }
 
     public void resourceRemoval(String id, String name, String type, String uri, String owner, Set<String> scopes, String serverId, Set<String> invalidations) {
-        resourceUpdated(id, name, type, uri, scopes, serverId, invalidations);
-        invalidations.add(StoreFactoryCacheSession.getResourceByOwnerCacheKey(owner, serverId));
+        resourceUpdated(id, name, type, uri, scopes, serverId, owner, invalidations);
         addInvalidations(InResourcePredicate.create().resource(id), invalidations);
     }
 
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheSession.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheSession.java
index a169235..27bacc4 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheSession.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheSession.java
@@ -245,12 +245,12 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
         invalidationEvents.add(ScopeUpdatedEvent.create(id, name, serverId));
     }
 
-    public void registerResourceInvalidation(String id, String name, String type, String uri, Set<String> scopes, String serverId) {
-        cache.resourceUpdated(id, name, type, uri, scopes, serverId, invalidations);
+    public void registerResourceInvalidation(String id, String name, String type, String uri, Set<String> scopes, String serverId, String owner) {
+        cache.resourceUpdated(id, name, type, uri, scopes, serverId, owner, invalidations);
         ResourceAdapter adapter = managedResources.get(id);
         if (adapter != null) adapter.invalidateFlag();
 
-        invalidationEvents.add(ResourceUpdatedEvent.create(id, name, type, uri, scopes, serverId));
+        invalidationEvents.add(ResourceUpdatedEvent.create(id, name, type, uri, scopes, serverId, owner));
     }
 
     public void registerPolicyInvalidation(String id, String name, Set<String> resources, Set<String> scopes, String serverId) {
@@ -509,7 +509,7 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
         @Override
         public Resource create(String name, ResourceServer resourceServer, String owner) {
             Resource resource = getResourceStoreDelegate().create(name, resourceServer, owner);
-            registerResourceInvalidation(resource.getId(), resource.getName(), resource.getType(), resource.getUri(), resource.getScopes().stream().map(scope -> scope.getId()).collect(Collectors.toSet()), resourceServer.getId());
+            registerResourceInvalidation(resource.getId(), resource.getName(), resource.getType(), resource.getUri(), resource.getScopes().stream().map(scope -> scope.getId()).collect(Collectors.toSet()), resourceServer.getId(), resource.getOwner());
             return resource;
         }
 
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedUser.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedUser.java
index 1bf6e42..68dfc37 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedUser.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedUser.java
@@ -45,10 +45,11 @@ public class CachedUser extends AbstractExtendableRevisioned implements InRealm 
     private Set<String> requiredActions = new HashSet<>();
     private Set<String> roleMappings = new HashSet<>();
     private Set<String> groups = new HashSet<>();
+    private int notBefore;
 
 
 
-    public CachedUser(Long revision, RealmModel realm, UserModel user) {
+    public CachedUser(Long revision, RealmModel realm, UserModel user, int notBefore) {
         super(revision, user.getId());
         this.realm = realm.getId();
         this.username = user.getUsername();
@@ -71,6 +72,7 @@ public class CachedUser extends AbstractExtendableRevisioned implements InRealm 
                 groups.add(group.getId());
             }
         }
+        this.notBefore = notBefore;
     }
 
     public String getRealm() {
@@ -129,4 +131,7 @@ public class CachedUser extends AbstractExtendableRevisioned implements InRealm 
         return groups;
     }
 
+    public int getNotBefore() {
+        return notBefore;
+    }
 }
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java
index 0d971f7..390c25c 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java
@@ -334,6 +334,8 @@ public class UserCacheSession implements UserCache {
     }
 
     protected UserModel cacheUser(RealmModel realm, UserModel delegate, Long revision) {
+        int notBefore = getDelegate().getNotBeforeOfUser(realm, delegate);
+
         StorageId storageId = new StorageId(delegate.getId());
         CachedUser cached = null;
         if (!storageId.isLocal()) {
@@ -343,7 +345,7 @@ public class UserCacheSession implements UserCache {
             if (policy != null && policy == UserStorageProviderModel.CachePolicy.NO_CACHE) {
                 return delegate;
             }
-            cached = new CachedUser(revision, realm, delegate);
+            cached = new CachedUser(revision, realm, delegate, notBefore);
             if (policy == null || policy == UserStorageProviderModel.CachePolicy.DEFAULT) {
                 cache.addRevisioned(cached, startupRevision);
             } else {
@@ -366,7 +368,7 @@ public class UserCacheSession implements UserCache {
                 }
             }
         } else {
-            cached = new CachedUser(revision, realm, delegate);
+            cached = new CachedUser(revision, realm, delegate, notBefore);
             cache.addRevisioned(cached, startupRevision);
         }
         UserAdapter adapter = new UserAdapter(cached, this, session, realm);
@@ -765,6 +767,32 @@ public class UserCacheSession implements UserCache {
         return consentModel;
     }
 
+    @Override
+    public void setNotBeforeForUser(RealmModel realm, UserModel user, int notBefore) {
+        if (!isRegisteredForInvalidation(realm, user.getId())) {
+            UserModel foundUser = getUserById(user.getId(), realm);
+            if (foundUser instanceof UserAdapter) {
+                ((UserAdapter) foundUser).invalidate();
+            }
+        }
+
+        getDelegate().setNotBeforeForUser(realm, user, notBefore);
+
+    }
+
+    @Override
+    public int getNotBeforeOfUser(RealmModel realm, UserModel user) {
+        if (isRegisteredForInvalidation(realm, user.getId())) {
+            return getDelegate().getNotBeforeOfUser(realm, user);
+        }
+
+        UserModel foundUser = getUserById(user.getId(), realm);
+        if (foundUser instanceof UserAdapter) {
+            return ((UserAdapter) foundUser).cached.getNotBefore();
+        } else {
+            return getDelegate().getNotBeforeOfUser(realm, user);
+        }
+    }
 
     @Override
     public UserModel addUser(RealmModel realm, String id, String username, boolean addDefaultRoles, boolean addDefaultRequiredActions) {
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/InfinispanChangelogBasedTransaction.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/InfinispanChangelogBasedTransaction.java
index 43bb2b3..e5a7a47 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/InfinispanChangelogBasedTransaction.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/InfinispanChangelogBasedTransaction.java
@@ -33,18 +33,18 @@ import org.keycloak.models.sessions.infinispan.remotestore.RemoteCacheInvoker;
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
-public class InfinispanChangelogBasedTransaction<S extends SessionEntity> extends AbstractKeycloakTransaction {
+public class InfinispanChangelogBasedTransaction<K, V extends SessionEntity> extends AbstractKeycloakTransaction {
 
     public static final Logger logger = Logger.getLogger(InfinispanChangelogBasedTransaction.class);
 
     private final KeycloakSession kcSession;
     private final String cacheName;
-    private final Cache<String, SessionEntityWrapper<S>> cache;
+    private final Cache<K, SessionEntityWrapper<V>> cache;
     private final RemoteCacheInvoker remoteCacheInvoker;
 
-    private final Map<String, SessionUpdatesList<S>> updates = new HashMap<>();
+    private final Map<K, SessionUpdatesList<V>> updates = new HashMap<>();
 
-    public InfinispanChangelogBasedTransaction(KeycloakSession kcSession, String cacheName, Cache<String, SessionEntityWrapper<S>> cache, RemoteCacheInvoker remoteCacheInvoker) {
+    public InfinispanChangelogBasedTransaction(KeycloakSession kcSession, String cacheName, Cache<K, SessionEntityWrapper<V>> cache, RemoteCacheInvoker remoteCacheInvoker) {
         this.kcSession = kcSession;
         this.cacheName = cacheName;
         this.cache = cache;
@@ -52,11 +52,11 @@ public class InfinispanChangelogBasedTransaction<S extends SessionEntity> extend
     }
 
 
-    public void addTask(String key, SessionUpdateTask<S> task) {
-        SessionUpdatesList<S> myUpdates = updates.get(key);
+    public void addTask(K key, SessionUpdateTask<V> task) {
+        SessionUpdatesList<V> myUpdates = updates.get(key);
         if (myUpdates == null) {
             // Lookup entity from cache
-            SessionEntityWrapper<S> wrappedEntity = cache.get(key);
+            SessionEntityWrapper<V> wrappedEntity = cache.get(key);
             if (wrappedEntity == null) {
                 logger.warnf("Not present cache item for key %s", key);
                 return;
@@ -75,14 +75,14 @@ public class InfinispanChangelogBasedTransaction<S extends SessionEntity> extend
 
 
     // Create entity and new version for it
-    public void addTask(String key, SessionUpdateTask<S> task, S entity) {
+    public void addTask(K key, SessionUpdateTask<V> task, V entity) {
         if (entity == null) {
             throw new IllegalArgumentException("Null entity not allowed");
         }
 
         RealmModel realm = kcSession.realms().getRealm(entity.getRealm());
-        SessionEntityWrapper<S> wrappedEntity = new SessionEntityWrapper<>(entity);
-        SessionUpdatesList<S> myUpdates = new SessionUpdatesList<>(realm, wrappedEntity);
+        SessionEntityWrapper<V> wrappedEntity = new SessionEntityWrapper<>(entity);
+        SessionUpdatesList<V> myUpdates = new SessionUpdatesList<>(realm, wrappedEntity);
         updates.put(key, myUpdates);
 
         // Run the update now, so reader in same transaction can see it
@@ -91,19 +91,19 @@ public class InfinispanChangelogBasedTransaction<S extends SessionEntity> extend
     }
 
 
-    public void reloadEntityInCurrentTransaction(RealmModel realm, String key, SessionEntityWrapper<S> entity) {
+    public void reloadEntityInCurrentTransaction(RealmModel realm, K key, SessionEntityWrapper<V> entity) {
         if (entity == null) {
             throw new IllegalArgumentException("Null entity not allowed");
         }
 
-        SessionEntityWrapper<S> latestEntity = cache.get(key);
+        SessionEntityWrapper<V> latestEntity = cache.get(key);
         if (latestEntity == null) {
             return;
         }
 
-        SessionUpdatesList<S> newUpdates = new SessionUpdatesList<>(realm, latestEntity);
+        SessionUpdatesList<V> newUpdates = new SessionUpdatesList<>(realm, latestEntity);
 
-        SessionUpdatesList<S> existingUpdates = updates.get(key);
+        SessionUpdatesList<V> existingUpdates = updates.get(key);
         if (existingUpdates != null) {
             newUpdates.setUpdateTasks(existingUpdates.getUpdateTasks());
         }
@@ -112,10 +112,10 @@ public class InfinispanChangelogBasedTransaction<S extends SessionEntity> extend
     }
 
 
-    public SessionEntityWrapper<S> get(String key) {
-        SessionUpdatesList<S> myUpdates = updates.get(key);
+    public SessionEntityWrapper<V> get(K key) {
+        SessionUpdatesList<V> myUpdates = updates.get(key);
         if (myUpdates == null) {
-            SessionEntityWrapper<S> wrappedEntity = cache.get(key);
+            SessionEntityWrapper<V> wrappedEntity = cache.get(key);
             if (wrappedEntity == null) {
                 return null;
             }
@@ -127,7 +127,7 @@ public class InfinispanChangelogBasedTransaction<S extends SessionEntity> extend
 
             return wrappedEntity;
         } else {
-            S entity = myUpdates.getEntityWrapper().getEntity();
+            V entity = myUpdates.getEntityWrapper().getEntity();
 
             // If entity is scheduled for remove, we don't return it.
             boolean scheduledForRemove = myUpdates.getUpdateTasks().stream().filter((SessionUpdateTask task) -> {
@@ -143,13 +143,13 @@ public class InfinispanChangelogBasedTransaction<S extends SessionEntity> extend
 
     @Override
     protected void commitImpl() {
-        for (Map.Entry<String, SessionUpdatesList<S>> entry : updates.entrySet()) {
-            SessionUpdatesList<S> sessionUpdates = entry.getValue();
-            SessionEntityWrapper<S> sessionWrapper = sessionUpdates.getEntityWrapper();
+        for (Map.Entry<K, SessionUpdatesList<V>> entry : updates.entrySet()) {
+            SessionUpdatesList<V> sessionUpdates = entry.getValue();
+            SessionEntityWrapper<V> sessionWrapper = sessionUpdates.getEntityWrapper();
 
             RealmModel realm = sessionUpdates.getRealm();
 
-            MergedUpdate<S> merged = MergedUpdate.computeUpdate(sessionUpdates.getUpdateTasks(), sessionWrapper);
+            MergedUpdate<V> merged = MergedUpdate.computeUpdate(sessionUpdates.getUpdateTasks(), sessionWrapper);
 
             if (merged != null) {
                 // Now run the operation in our cluster
@@ -162,8 +162,8 @@ public class InfinispanChangelogBasedTransaction<S extends SessionEntity> extend
     }
 
 
-    private void runOperationInCluster(String key, MergedUpdate<S> task,  SessionEntityWrapper<S> sessionWrapper) {
-        S session = sessionWrapper.getEntity();
+    private void runOperationInCluster(K key, MergedUpdate<V> task,  SessionEntityWrapper<V> sessionWrapper) {
+        V session = sessionWrapper.getEntity();
         SessionUpdateTask.CacheOperation operation = task.getOperation(session);
 
         // Don't need to run update of underlying entity. Local updates were already run
@@ -182,9 +182,14 @@ public class InfinispanChangelogBasedTransaction<S extends SessionEntity> extend
                         .put(key, sessionWrapper, task.getLifespanMs(), TimeUnit.MILLISECONDS);
                 break;
             case ADD_IF_ABSENT:
-                SessionEntityWrapper existing = cache.putIfAbsent(key, sessionWrapper);
+                SessionEntityWrapper<V> existing = cache.putIfAbsent(key, sessionWrapper);
                 if (existing != null) {
-                    throw new IllegalStateException("There is already existing value in cache for key " + key);
+                    logger.debugf("Existing entity in cache for key: %s . Will update it", key);
+
+                    // Apply updates on the existing entity and replace it
+                    task.runUpdate(existing.getEntity());
+
+                    replace(key, task, existing);
                 }
                 break;
             case REPLACE:
@@ -197,12 +202,12 @@ public class InfinispanChangelogBasedTransaction<S extends SessionEntity> extend
     }
 
 
-    private void replace(String key, MergedUpdate<S> task, SessionEntityWrapper<S> oldVersionEntity) {
+    private void replace(K key, MergedUpdate<V> task, SessionEntityWrapper<V> oldVersionEntity) {
         boolean replaced = false;
-        S session = oldVersionEntity.getEntity();
+        V session = oldVersionEntity.getEntity();
 
         while (!replaced) {
-            SessionEntityWrapper<S> newVersionEntity = generateNewVersionAndWrapEntity(session, oldVersionEntity.getLocalMetadata());
+            SessionEntityWrapper<V> newVersionEntity = generateNewVersionAndWrapEntity(session, oldVersionEntity.getLocalMetadata());
 
             // Atomic cluster-aware replace
             replaced = cache.replace(key, oldVersionEntity, newVersionEntity);
@@ -235,7 +240,7 @@ public class InfinispanChangelogBasedTransaction<S extends SessionEntity> extend
     protected void rollbackImpl() {
     }
 
-    private SessionEntityWrapper<S> generateNewVersionAndWrapEntity(S entity, Map<String, String> localMetadata) {
+    private SessionEntityWrapper<V> generateNewVersionAndWrapEntity(V entity, Map<String, String> localMetadata) {
         return new SessionEntityWrapper<>(localMetadata, entity);
     }
 
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/MergedUpdate.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/MergedUpdate.java
index 1f24f84..8e0cf0e 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/MergedUpdate.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/MergedUpdate.java
@@ -32,7 +32,7 @@ class MergedUpdate<S extends SessionEntity> implements SessionUpdateTask<S> {
     private CrossDCMessageStatus crossDCMessageStatus;
 
 
-    public MergedUpdate(CacheOperation operation, CrossDCMessageStatus crossDCMessageStatus) {
+    private MergedUpdate(CacheOperation operation, CrossDCMessageStatus crossDCMessageStatus) {
         this.operation = operation;
         this.crossDCMessageStatus = crossDCMessageStatus;
     }
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/sessions/LastSessionRefreshChecker.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/sessions/LastSessionRefreshChecker.java
index f9adf9b..25e0df9 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/sessions/LastSessionRefreshChecker.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/sessions/LastSessionRefreshChecker.java
@@ -60,7 +60,7 @@ public class LastSessionRefreshChecker {
 
         Integer lsrr = sessionWrapper.getLocalMetadataNoteInt(UserSessionEntity.LAST_SESSION_REFRESH_REMOTE);
         if (lsrr == null) {
-            logger.warnf("Not available lsrr note on user session %s.", sessionWrapper.getEntity().getId());
+            logger.debugf("Not available lsrr note on user session %s.", sessionWrapper.getEntity().getId());
             return SessionUpdateTask.CrossDCMessageStatus.SYNC;
         }
 
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/SessionUpdateTask.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/SessionUpdateTask.java
index b79ea16..244a88b 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/SessionUpdateTask.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/SessionUpdateTask.java
@@ -49,7 +49,7 @@ public interface SessionUpdateTask<S extends SessionEntity> {
 
             if (this == ADD | this == ADD_IF_ABSENT) {
                 if (other == ADD | other == ADD_IF_ABSENT) {
-                    throw new IllegalStateException("Illegal state. Task already in progress for session " + entity.getId());
+                    throw new IllegalStateException("Illegal state. Task already in progress for session " + entity.toString());
                 }
 
                 return this;
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/UserSessionUpdateTask.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/UserSessionUpdateTask.java
index 4fd4bbe..1db36a1 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/UserSessionUpdateTask.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/UserSessionUpdateTask.java
@@ -17,8 +17,6 @@
 
 package org.keycloak.models.sessions.infinispan.changes;
 
-import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.RealmModel;
 import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity;
 
 /**
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/AuthenticationSessionEntity.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/AuthenticationSessionEntity.java
index 0b99225..cdb841e 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/AuthenticationSessionEntity.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/AuthenticationSessionEntity.java
@@ -21,6 +21,7 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import java.util.TreeSet;
 
 import org.keycloak.sessions.AuthenticationSessionModel;
 
@@ -29,6 +30,8 @@ import org.keycloak.sessions.AuthenticationSessionModel;
  */
 public class AuthenticationSessionEntity extends SessionEntity {
 
+    private String id;
+
     private String clientUuid;
     private String authUserId;
 
@@ -46,6 +49,14 @@ public class AuthenticationSessionEntity extends SessionEntity {
     private Set<String> requiredActions  = new HashSet<>();
     private Map<String, String> userSessionNotes;
 
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
     public String getClientUuid() {
         return clientUuid;
     }
@@ -149,4 +160,26 @@ public class AuthenticationSessionEntity extends SessionEntity {
     public void setAuthNotes(Map<String, String> authNotes) {
         this.authNotes = authNotes;
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof AuthenticationSessionEntity)) return false;
+
+        AuthenticationSessionEntity that = (AuthenticationSessionEntity) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return id != null ? id.hashCode() : 0;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("AuthenticationSessionEntity [id=%s, realm=%s, clientUuid=%s ]", getId(), getRealm(), getClientUuid());
+    }
 }
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/LoginFailureEntity.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/LoginFailureEntity.java
index d25f58b..5b328ec 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/LoginFailureEntity.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/LoginFailureEntity.java
@@ -17,15 +17,12 @@
 
 package org.keycloak.models.sessions.infinispan.entities;
 
-import java.io.Serializable;
-
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
-public class LoginFailureEntity implements Serializable {
+public class LoginFailureEntity extends SessionEntity {
 
     private String userId;
-    private String realm;
     private int failedLoginNotBefore;
     private int numFailures;
     private long lastFailure;
@@ -39,14 +36,6 @@ public class LoginFailureEntity implements Serializable {
         this.userId = userId;
     }
 
-    public String getRealm() {
-        return realm;
-    }
-
-    public void setRealm(String realm) {
-        this.realm = realm;
-    }
-
     public int getFailedLoginNotBefore() {
         return failedLoginNotBefore;
     }
@@ -85,4 +74,30 @@ public class LoginFailureEntity implements Serializable {
         this.lastFailure = 0;
         this.lastIPFailure = null;
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof LoginFailureEntity)) return false;
+
+        LoginFailureEntity that = (LoginFailureEntity) o;
+
+        if (userId != null ? !userId.equals(that.userId) : that.userId != null) return false;
+        if (getRealm() != null ? !getRealm().equals(that.getRealm()) : that.getRealm() != null) return false;
+
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hashCode = getRealm() != null ? getRealm().hashCode() : 0;
+        hashCode = hashCode * 13 + (userId != null ? userId.hashCode() : 0);
+        return hashCode;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("LoginFailureEntity [ userId=%s, realm=%s, numFailures=%d ]", userId, getRealm(), numFailures);
+    }
 }
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/LoginFailureKey.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/LoginFailureKey.java
index c452379..318b1ba 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/LoginFailureKey.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/LoginFailureKey.java
@@ -52,4 +52,9 @@ public class LoginFailureKey implements Serializable {
         return result;
     }
 
+
+    @Override
+    public String toString() {
+        return String.format("LoginFailureKey [ realm=%s. userId=%s ]", realm, userId);
+    }
 }
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/SessionEntity.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/SessionEntity.java
index 25ac2a4..37f8e08 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/SessionEntity.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/SessionEntity.java
@@ -26,17 +26,8 @@ import org.keycloak.models.sessions.infinispan.changes.SessionEntityWrapper;
  */
 public abstract class SessionEntity implements Serializable {
 
-    private String id;
-
     private String realm;
 
-    public String getId() {
-        return id;
-    }
-
-    public void setId(String id) {
-        this.id = id;
-    }
 
     public String getRealm() {
         return realm;
@@ -46,26 +37,13 @@ public abstract class SessionEntity implements Serializable {
         this.realm = realm;
     }
 
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (!(o instanceof SessionEntity)) return false;
-
-        SessionEntity that = (SessionEntity) o;
-
-        if (id != null ? !id.equals(that.id) : that.id != null) return false;
-
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        return id != null ? id.hashCode() : 0;
-    }
-
 
     public SessionEntityWrapper mergeRemoteEntityWithLocalEntity(SessionEntityWrapper localEntityWrapper) {
-        throw new IllegalStateException("Not yet implemented");
+        if (localEntityWrapper == null) {
+            return new SessionEntityWrapper<>(this);
+        } else {
+            return new SessionEntityWrapper<>(localEntityWrapper.getLocalMetadata(), this);
+        }
     };
 
 }
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/UserSessionEntity.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/UserSessionEntity.java
index 5d0edb0..8ac85a1 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/UserSessionEntity.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/UserSessionEntity.java
@@ -43,6 +43,8 @@ public class UserSessionEntity extends SessionEntity {
     // Metadata attribute, which contains the lastSessionRefresh available on remoteCache. Used in decide whether we need to write to remoteCache (DC) or not
     public static final String LAST_SESSION_REFRESH_REMOTE = "lsrr";
 
+    private String id;
+
     private String user;
 
     private String brokerSessionId;
@@ -62,6 +64,14 @@ public class UserSessionEntity extends SessionEntity {
 
     private UserSessionModel.State state;
 
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
     private Map<String, String> notes = new ConcurrentHashMap<>();
 
     private Map<String, AuthenticatedClientSessionEntity> authenticatedClientSessions  = new ConcurrentHashMap<>();
@@ -163,6 +173,23 @@ public class UserSessionEntity extends SessionEntity {
     }
 
     @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof UserSessionEntity)) return false;
+
+        UserSessionEntity that = (UserSessionEntity) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return id != null ? id.hashCode() : 0;
+    }
+
+    @Override
     public String toString() {
         return String.format("UserSessionEntity [id=%s, realm=%s, lastSessionRefresh=%d, clients=%s]", getId(), getRealm(), getLastSessionRefresh(),
           new TreeSet(this.authenticatedClientSessions.keySet()));
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
index 1eb58b5..48d1965 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
@@ -42,7 +42,6 @@ import org.keycloak.models.sessions.infinispan.entities.AuthenticatedClientSessi
 import org.keycloak.models.sessions.infinispan.entities.LoginFailureEntity;
 import org.keycloak.models.sessions.infinispan.entities.LoginFailureKey;
 import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity;
-import org.keycloak.models.sessions.infinispan.events.ClientRemovedSessionEvent;
 import org.keycloak.models.sessions.infinispan.events.RealmRemovedSessionEvent;
 import org.keycloak.models.sessions.infinispan.events.RemoveAllUserLoginFailuresEvent;
 import org.keycloak.models.sessions.infinispan.events.RemoveUserSessionsEvent;
@@ -76,11 +75,11 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
 
     protected final Cache<String, SessionEntityWrapper<UserSessionEntity>> sessionCache;
     protected final Cache<String, SessionEntityWrapper<UserSessionEntity>> offlineSessionCache;
-    protected final Cache<LoginFailureKey, LoginFailureEntity> loginFailureCache;
+    protected final Cache<LoginFailureKey, SessionEntityWrapper<LoginFailureEntity>> loginFailureCache;
 
-    protected final InfinispanChangelogBasedTransaction<UserSessionEntity> sessionTx;
-    protected final InfinispanChangelogBasedTransaction<UserSessionEntity> offlineSessionTx;
-    protected final InfinispanKeycloakTransaction tx;
+    protected final InfinispanChangelogBasedTransaction<String, UserSessionEntity> sessionTx;
+    protected final InfinispanChangelogBasedTransaction<String, UserSessionEntity> offlineSessionTx;
+    protected final InfinispanChangelogBasedTransaction<LoginFailureKey, LoginFailureEntity> loginFailuresTx;
 
     protected final SessionEventsSenderTransaction clusterEventsSenderTx;
 
@@ -93,7 +92,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
                                          LastSessionRefreshStore offlineLastSessionRefreshStore,
                                          Cache<String, SessionEntityWrapper<UserSessionEntity>> sessionCache,
                                          Cache<String, SessionEntityWrapper<UserSessionEntity>> offlineSessionCache,
-                                         Cache<LoginFailureKey, LoginFailureEntity> loginFailureCache) {
+                                         Cache<LoginFailureKey, SessionEntityWrapper<LoginFailureEntity>> loginFailureCache) {
         this.session = session;
 
         this.sessionCache = sessionCache;
@@ -103,24 +102,24 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
         this.sessionTx = new InfinispanChangelogBasedTransaction<>(session, InfinispanConnectionProvider.SESSION_CACHE_NAME, sessionCache, remoteCacheInvoker);
         this.offlineSessionTx = new InfinispanChangelogBasedTransaction<>(session, InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME, offlineSessionCache, remoteCacheInvoker);
 
-        this.tx = new InfinispanKeycloakTransaction();
+        this.loginFailuresTx = new InfinispanChangelogBasedTransaction<>(session, InfinispanConnectionProvider.LOGIN_FAILURE_CACHE_NAME, loginFailureCache, remoteCacheInvoker);
 
         this.clusterEventsSenderTx = new SessionEventsSenderTransaction(session);
 
         this.lastSessionRefreshStore = lastSessionRefreshStore;
         this.offlineLastSessionRefreshStore = offlineLastSessionRefreshStore;
 
-        session.getTransactionManager().enlistAfterCompletion(tx);
         session.getTransactionManager().enlistAfterCompletion(clusterEventsSenderTx);
         session.getTransactionManager().enlistAfterCompletion(sessionTx);
         session.getTransactionManager().enlistAfterCompletion(offlineSessionTx);
+        session.getTransactionManager().enlistAfterCompletion(loginFailuresTx);
     }
 
     protected Cache<String, SessionEntityWrapper<UserSessionEntity>> getCache(boolean offline) {
         return offline ? offlineSessionCache : sessionCache;
     }
 
-    protected InfinispanChangelogBasedTransaction<UserSessionEntity> getTransaction(boolean offline) {
+    protected InfinispanChangelogBasedTransaction<String, UserSessionEntity> getTransaction(boolean offline) {
         return offline ? offlineSessionTx : sessionTx;
     }
 
@@ -136,7 +135,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
     public AuthenticatedClientSessionModel createClientSession(RealmModel realm, ClientModel client, UserSessionModel userSession) {
         AuthenticatedClientSessionEntity entity = new AuthenticatedClientSessionEntity();
 
-        InfinispanChangelogBasedTransaction<UserSessionEntity> updateTx = getTransaction(false);
+        InfinispanChangelogBasedTransaction<String, UserSessionEntity> updateTx = getTransaction(false);
         AuthenticatedClientSessionAdapter adapter = new AuthenticatedClientSessionAdapter(entity, client, (UserSessionAdapter) userSession, this, updateTx);
         adapter.setUserSession(userSession);
         return adapter;
@@ -202,7 +201,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
     }
 
     private UserSessionEntity getUserSessionEntity(String id, boolean offline) {
-        InfinispanChangelogBasedTransaction<UserSessionEntity> tx = getTransaction(offline);
+        InfinispanChangelogBasedTransaction<String, UserSessionEntity> tx = getTransaction(offline);
         SessionEntityWrapper<UserSessionEntity> entityWrapper = tx.get(id);
         return entityWrapper==null ? null : entityWrapper.getEntity();
     }
@@ -311,7 +310,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
                 UserSessionModel remoteSessionAdapter = wrap(realm, remoteSessionEntity, offline);
                 if (predicate.test(remoteSessionAdapter)) {
 
-                    InfinispanChangelogBasedTransaction<UserSessionEntity> tx = getTransaction(offline);
+                    InfinispanChangelogBasedTransaction<String, UserSessionEntity> tx = getTransaction(offline);
 
                     // Remote entity contains our predicate. Update local cache with the remote entity
                     SessionEntityWrapper<UserSessionEntity> sessionWrapper = remoteSessionEntity.mergeRemoteEntityWithLocalEntity(tx.get(id));
@@ -511,7 +510,14 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
     @Override
     public UserLoginFailureModel getUserLoginFailure(RealmModel realm, String userId) {
         LoginFailureKey key = new LoginFailureKey(realm.getId(), userId);
-        return wrap(key, loginFailureCache.get(key));
+        LoginFailureEntity entity = getLoginFailureEntity(key);
+        return wrap(key, entity);
+    }
+
+    private LoginFailureEntity getLoginFailureEntity(LoginFailureKey key) {
+        InfinispanChangelogBasedTransaction<LoginFailureKey, LoginFailureEntity> tx = getLoginFailuresTx();
+        SessionEntityWrapper<LoginFailureEntity> entityWrapper = tx.get(key);
+        return entityWrapper==null ? null : entityWrapper.getEntity();
     }
 
     @Override
@@ -520,13 +526,53 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
         LoginFailureEntity entity = new LoginFailureEntity();
         entity.setRealm(realm.getId());
         entity.setUserId(userId);
-        tx.put(loginFailureCache, key, entity);
+
+        SessionUpdateTask<LoginFailureEntity> createLoginFailureTask = new SessionUpdateTask<LoginFailureEntity>() {
+
+            @Override
+            public void runUpdate(LoginFailureEntity session) {
+
+            }
+
+            @Override
+            public CacheOperation getOperation(LoginFailureEntity session) {
+                return CacheOperation.ADD_IF_ABSENT;
+            }
+
+            @Override
+            public CrossDCMessageStatus getCrossDCMessageStatus(SessionEntityWrapper<LoginFailureEntity> sessionWrapper) {
+                return CrossDCMessageStatus.SYNC;
+            }
+
+        };
+
+        loginFailuresTx.addTask(key, createLoginFailureTask, entity);
+
         return wrap(key, entity);
     }
 
     @Override
     public void removeUserLoginFailure(RealmModel realm, String userId) {
-        tx.remove(loginFailureCache, new LoginFailureKey(realm.getId(), userId));
+        SessionUpdateTask<LoginFailureEntity> removeTask = new SessionUpdateTask<LoginFailureEntity>() {
+
+            @Override
+            public void runUpdate(LoginFailureEntity entity) {
+
+            }
+
+            @Override
+            public CacheOperation getOperation(LoginFailureEntity entity) {
+                return CacheOperation.REMOVE;
+            }
+
+            @Override
+            public CrossDCMessageStatus getCrossDCMessageStatus(SessionEntityWrapper<LoginFailureEntity> sessionWrapper) {
+                return CrossDCMessageStatus.SYNC;
+            }
+
+        };
+
+        loginFailuresTx.addTask(new LoginFailureKey(realm.getId(), userId), removeTask);
     }
 
     @Override
@@ -543,9 +589,9 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
     private void removeAllLocalUserLoginFailuresEvent(String realmId) {
         FuturesHelper futures = new FuturesHelper();
 
-        Cache<LoginFailureKey, LoginFailureEntity> localCache = CacheDecorators.localCache(loginFailureCache);
+        Cache<LoginFailureKey, SessionEntityWrapper<LoginFailureEntity>> localCache = CacheDecorators.localCache(loginFailureCache);
 
-        Cache<LoginFailureKey, LoginFailureEntity> localCacheStoreIgnore = CacheDecorators.skipCacheLoaders(localCache);
+        Cache<LoginFailureKey, SessionEntityWrapper<LoginFailureEntity>> localCacheStoreIgnore = CacheDecorators.skipCacheLoaders(localCache);
 
         localCacheStoreIgnore
                 .entrySet()
@@ -593,8 +639,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
         removeUserSessions(realm, user, true);
         removeUserSessions(realm, user, false);
 
-        loginFailureCache.remove(new LoginFailureKey(realm.getId(), user.getUsername()));
-        loginFailureCache.remove(new LoginFailureKey(realm.getId(), user.getEmail()));
+        removeUserLoginFailure(realm, user.getId());
     }
 
     @Override
@@ -602,7 +647,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
     }
 
     protected void removeUserSession(UserSessionEntity sessionEntity, boolean offline) {
-        InfinispanChangelogBasedTransaction<UserSessionEntity> tx = getTransaction(offline);
+        InfinispanChangelogBasedTransaction<String, UserSessionEntity> tx = getTransaction(offline);
 
         SessionUpdateTask<UserSessionEntity> removeTask = new SessionUpdateTask<UserSessionEntity>() {
 
@@ -626,17 +671,17 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
         tx.addTask(sessionEntity.getId(), removeTask);
     }
 
-    InfinispanKeycloakTransaction getTx() {
-        return tx;
+    InfinispanChangelogBasedTransaction<LoginFailureKey, LoginFailureEntity> getLoginFailuresTx() {
+        return loginFailuresTx;
     }
 
     UserSessionAdapter wrap(RealmModel realm, UserSessionEntity entity, boolean offline) {
-        InfinispanChangelogBasedTransaction<UserSessionEntity> tx = getTransaction(offline);
+        InfinispanChangelogBasedTransaction<String, UserSessionEntity> tx = getTransaction(offline);
         return entity != null ? new UserSessionAdapter(session, this, tx, realm, entity, offline) : null;
     }
 
     UserLoginFailureModel wrap(LoginFailureKey key, LoginFailureEntity entity) {
-        return entity != null ? new UserLoginFailureAdapter(this, loginFailureCache, key, entity) : null;
+        return entity != null ? new UserLoginFailureAdapter(this, key, entity) : null;
     }
 
     UserSessionEntity getUserSessionEntity(UserSessionModel userSession, boolean offline) {
@@ -739,7 +784,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
         entity.setLastSessionRefresh(userSession.getLastSessionRefresh());
 
 
-        InfinispanChangelogBasedTransaction<UserSessionEntity> tx = getTransaction(offline);
+        InfinispanChangelogBasedTransaction<String, UserSessionEntity> tx = getTransaction(offline);
 
         SessionUpdateTask importTask = new SessionUpdateTask<UserSessionEntity>() {
 
@@ -775,7 +820,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
 
 
     private AuthenticatedClientSessionAdapter importClientSession(UserSessionAdapter importedUserSession, AuthenticatedClientSessionModel clientSession,
-                                                                  InfinispanChangelogBasedTransaction<UserSessionEntity> updateTx) {
+                                                                  InfinispanChangelogBasedTransaction<String, UserSessionEntity> updateTx) {
         AuthenticatedClientSessionEntity entity = new AuthenticatedClientSessionEntity();
 
         entity.setAction(clientSession.getAction());
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProviderFactory.java
index 489dd60..df30b4e 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProviderFactory.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProviderFactory.java
@@ -85,7 +85,7 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider
         InfinispanConnectionProvider connections = session.getProvider(InfinispanConnectionProvider.class);
         Cache<String, SessionEntityWrapper<UserSessionEntity>> cache = connections.getCache(InfinispanConnectionProvider.SESSION_CACHE_NAME);
         Cache<String, SessionEntityWrapper<UserSessionEntity>> offlineSessionsCache = connections.getCache(InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME);
-        Cache<LoginFailureKey, LoginFailureEntity> loginFailures = connections.getCache(InfinispanConnectionProvider.LOGIN_FAILURE_CACHE_NAME);
+        Cache<LoginFailureKey, SessionEntityWrapper<LoginFailureEntity>> loginFailures = connections.getCache(InfinispanConnectionProvider.LOGIN_FAILURE_CACHE_NAME);
 
         return new InfinispanUserSessionProvider(session, remoteCacheInvoker, lastSessionRefreshStore, offlineLastSessionRefreshStore, cache, offlineSessionsCache, loginFailures);
     }
@@ -224,6 +224,11 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider
         if (offlineSessionsRemoteCache) {
             offlineLastSessionRefreshStore = new LastSessionRefreshStoreFactory().createAndInit(session, offlineSessionsCache, true);
         }
+
+        Cache loginFailuresCache = ispn.getCache(InfinispanConnectionProvider.LOGIN_FAILURE_CACHE_NAME);
+        boolean loginFailuresRemoteCache = checkRemoteCache(session, loginFailuresCache, (RealmModel realm) -> {
+            return realm.getMaxDeltaTimeSeconds() * 1000;
+        });
     }
 
     private boolean checkRemoteCache(KeycloakSession session, Cache ispnCache, RemoteCacheInvoker.MaxIdleTimeLoader maxIdleLoader) {
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheInvoker.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheInvoker.java
index 89fd215..8891469 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheInvoker.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheInvoker.java
@@ -55,13 +55,13 @@ public class RemoteCacheInvoker {
     }
 
 
-    public <S extends SessionEntity> void runTask(KeycloakSession kcSession, RealmModel realm, String cacheName, String key, SessionUpdateTask<S> task, SessionEntityWrapper<S> sessionWrapper) {
+    public <K, V extends SessionEntity> void runTask(KeycloakSession kcSession, RealmModel realm, String cacheName, K key, SessionUpdateTask<V> task, SessionEntityWrapper<V> sessionWrapper) {
         RemoteCacheContext context = remoteCaches.get(cacheName);
         if (context == null) {
             return;
         }
 
-        S session = sessionWrapper.getEntity();
+        V session = sessionWrapper.getEntity();
 
         SessionUpdateTask.CacheOperation operation = task.getOperation(session);
         SessionUpdateTask.CrossDCMessageStatus status = task.getCrossDCMessageStatus(sessionWrapper);
@@ -82,8 +82,8 @@ public class RemoteCacheInvoker {
     }
 
 
-    private <S extends SessionEntity> void runOnRemoteCache(RemoteCache remoteCache, long maxIdleMs, String key, SessionUpdateTask<S> task, SessionEntityWrapper<S> sessionWrapper) {
-        S session = sessionWrapper.getEntity();
+    private <K, V extends SessionEntity> void runOnRemoteCache(RemoteCache<K, V> remoteCache, long maxIdleMs, K key, SessionUpdateTask<V> task, SessionEntityWrapper<V> sessionWrapper) {
+        V session = sessionWrapper.getEntity();
         SessionUpdateTask.CacheOperation operation = task.getOperation(session);
 
         switch (operation) {
@@ -96,13 +96,16 @@ public class RemoteCacheInvoker {
                 break;
             case ADD_IF_ABSENT:
                 final int currentTime = Time.currentTime();
-                SessionEntity existing = (SessionEntity) remoteCache
+                SessionEntity existing = remoteCache
                         .withFlags(Flag.FORCE_RETURN_VALUE)
                         .putIfAbsent(key, session, -1, TimeUnit.MILLISECONDS, maxIdleMs, TimeUnit.MILLISECONDS);
                 if (existing != null) {
-                    throw new IllegalStateException("There is already existing value in cache for key " + key);
+                    logger.debugf("Existing entity in remote cache for key: %s . Will update it", key);
+
+                    replace(remoteCache, task.getLifespanMs(), maxIdleMs, key, task);
+                } else {
+                    sessionWrapper.putLocalMetadataNoteInt(UserSessionEntity.LAST_SESSION_REFRESH_REMOTE, currentTime);
                 }
-                sessionWrapper.putLocalMetadataNoteInt(UserSessionEntity.LAST_SESSION_REFRESH_REMOTE, currentTime);
                 break;
             case REPLACE:
                 replace(remoteCache, task.getLifespanMs(), maxIdleMs, key, task);
@@ -113,16 +116,16 @@ public class RemoteCacheInvoker {
     }
 
 
-    private <S extends SessionEntity> void replace(RemoteCache remoteCache, long lifespanMs, long maxIdleMs, String key, SessionUpdateTask<S> task) {
+    private <K, V extends SessionEntity> void replace(RemoteCache<K, V> remoteCache, long lifespanMs, long maxIdleMs, K key, SessionUpdateTask<V> task) {
         boolean replaced = false;
         while (!replaced) {
-            VersionedValue<S> versioned = remoteCache.getVersioned(key);
+            VersionedValue<V> versioned = remoteCache.getVersioned(key);
             if (versioned == null) {
                 logger.warnf("Not found entity to replace for key '%s'", key);
                 return;
             }
 
-            S session = versioned.getValue();
+            V session = versioned.getValue();
 
             // Run task on the remote session
             task.runUpdate(session);
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheSessionListener.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheSessionListener.java
index d29e220..b186833 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheSessionListener.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheSessionListener.java
@@ -44,12 +44,12 @@ import org.infinispan.client.hotrod.VersionedValue;
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
 @ClientListener
-public class RemoteCacheSessionListener  {
+public class RemoteCacheSessionListener<K, V extends SessionEntity>  {
 
     protected static final Logger logger = Logger.getLogger(RemoteCacheSessionListener.class);
 
-    private Cache<String, SessionEntityWrapper> cache;
-    private RemoteCache remoteCache;
+    private Cache<K, SessionEntityWrapper<V>> cache;
+    private RemoteCache<K, V> remoteCache;
     private boolean distributed;
     private String myAddress;
 
@@ -58,7 +58,7 @@ public class RemoteCacheSessionListener  {
     }
 
 
-    protected void init(KeycloakSession session, Cache<String, SessionEntityWrapper> cache, RemoteCache remoteCache) {
+    protected void init(KeycloakSession session, Cache<K, SessionEntityWrapper<V>> cache, RemoteCache<K, V> remoteCache) {
         this.cache = cache;
         this.remoteCache = remoteCache;
 
@@ -73,7 +73,7 @@ public class RemoteCacheSessionListener  {
 
     @ClientCacheEntryCreated
     public void created(ClientCacheEntryCreatedEvent event) {
-        String key = (String) event.getKey();
+        K key = (K) event.getKey();
 
         if (shouldUpdateLocalCache(event.getType(), key, event.isCommandRetried())) {
             // Should load it from remoteStore
@@ -84,7 +84,7 @@ public class RemoteCacheSessionListener  {
 
     @ClientCacheEntryModified
     public void updated(ClientCacheEntryModifiedEvent event) {
-        String key = (String) event.getKey();
+        K key = (K) event.getKey();
 
         if (shouldUpdateLocalCache(event.getType(), key, event.isCommandRetried())) {
 
@@ -94,7 +94,7 @@ public class RemoteCacheSessionListener  {
 
     private static final int MAXIMUM_REPLACE_RETRIES = 10;
 
-    private void replaceRemoteEntityInCache(String key, long eventVersion) {
+    private void replaceRemoteEntityInCache(K key, long eventVersion) {
         // TODO can be optimized and remoteSession sent in the event itself?
         boolean replaced = false;
         int replaceRetries = 0;
@@ -102,8 +102,8 @@ public class RemoteCacheSessionListener  {
         do {
             replaceRetries++;
             
-            SessionEntityWrapper localEntityWrapper = cache.get(key);
-            VersionedValue remoteSessionVersioned = remoteCache.getVersioned(key);
+            SessionEntityWrapper<V> localEntityWrapper = cache.get(key);
+            VersionedValue<V> remoteSessionVersioned = remoteCache.getVersioned(key);
             if (remoteSessionVersioned == null || remoteSessionVersioned.getVersion() < eventVersion) {
                 try {
                     logger.debugf("Got replace remote entity event prematurely, will try again. Event version: %d, got: %d",
@@ -120,7 +120,7 @@ public class RemoteCacheSessionListener  {
 
             logger.debugf("Read session%s. Entity read from remote cache: %s", replaceRetries > 1 ? "" : " again", remoteSession);
 
-            SessionEntityWrapper sessionWrapper = remoteSession.mergeRemoteEntityWithLocalEntity(localEntityWrapper);
+            SessionEntityWrapper<V> sessionWrapper = remoteSession.mergeRemoteEntityWithLocalEntity(localEntityWrapper);
 
             // We received event from remoteCache, so we won't update it back
             replaced = cache.getAdvancedCache().withFlags(Flag.SKIP_CACHE_STORE, Flag.SKIP_CACHE_LOAD, Flag.IGNORE_RETURN_VALUES)
@@ -135,7 +135,7 @@ public class RemoteCacheSessionListener  {
 
     @ClientCacheEntryRemoved
     public void removed(ClientCacheEntryRemovedEvent event) {
-        String key = (String) event.getKey();
+        K key = (K) event.getKey();
 
         if (shouldUpdateLocalCache(event.getType(), key, event.isCommandRetried())) {
             // We received event from remoteCache, so we won't update it back
@@ -152,7 +152,7 @@ public class RemoteCacheSessionListener  {
 
 
     // For distributed caches, ensure that local modification is executed just on owner OR if event.isCommandRetried
-    protected boolean shouldUpdateLocalCache(ClientEvent.Type type, String key, boolean commandRetried) {
+    protected boolean shouldUpdateLocalCache(ClientEvent.Type type, K key, boolean commandRetried) {
         boolean result;
 
         // Case when cache is stopping or stopped already
@@ -184,7 +184,7 @@ public class RemoteCacheSessionListener  {
     }
 
 
-    public static RemoteCacheSessionListener createListener(KeycloakSession session, Cache<String, SessionEntityWrapper> cache, RemoteCache remoteCache) {
+    public static <K, V extends SessionEntity> RemoteCacheSessionListener createListener(KeycloakSession session, Cache<K, SessionEntityWrapper<V>> cache, RemoteCache<K, V> remoteCache) {
         /*boolean isCoordinator = InfinispanUtil.isCoordinator(cache);
 
         // Just cluster coordinator will fetch userSessions from remote cache.
@@ -198,7 +198,7 @@ public class RemoteCacheSessionListener  {
             listener = new DontFetchInitialStateCacheListener();
         }*/
 
-        RemoteCacheSessionListener listener = new RemoteCacheSessionListener();
+        RemoteCacheSessionListener<K, V> listener = new RemoteCacheSessionListener<>();
         listener.init(session, cache, remoteCache);
 
         return listener;
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheSessionsLoader.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheSessionsLoader.java
index 00c133a..789fc16 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheSessionsLoader.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheSessionsLoader.java
@@ -115,14 +115,14 @@ public class RemoteCacheSessionsLoader implements SessionLoader {
 
         for (Map.Entry<byte[], byte[]> entry : remoteObjects.entrySet()) {
             try {
-                String key = (String) marshaller.objectFromByteBuffer(entry.getKey());
+                Object key = marshaller.objectFromByteBuffer(entry.getKey());
                 SessionEntity entity = (SessionEntity) marshaller.objectFromByteBuffer(entry.getValue());
 
                 SessionEntityWrapper entityWrapper = new SessionEntityWrapper(entity);
 
                 decoratedCache.putAsync(key, entityWrapper);
             } catch (Exception e) {
-                log.warnf("Error loading session from remote cache", e);
+                log.warn("Error loading session from remote cache", e);
             }
         }
 
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/Mappers.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/Mappers.java
index f75391c..815a54d 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/Mappers.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/Mappers.java
@@ -48,7 +48,7 @@ public class Mappers {
         return new UserSessionEntityMapper();
     }
 
-    public static Function<Map.Entry<LoginFailureKey, LoginFailureEntity>, LoginFailureKey> loginFailureId() {
+    public static Function<Map.Entry<LoginFailureKey, SessionEntityWrapper<LoginFailureEntity>>, LoginFailureKey> loginFailureId() {
         return new LoginFailureIdMapper();
     }
 
@@ -103,9 +103,9 @@ public class Mappers {
 
     }
 
-    private static class LoginFailureIdMapper implements Function<Map.Entry<LoginFailureKey, LoginFailureEntity>, LoginFailureKey>, Serializable {
+    private static class LoginFailureIdMapper implements Function<Map.Entry<LoginFailureKey, SessionEntityWrapper<LoginFailureEntity>>, LoginFailureKey>, Serializable {
         @Override
-        public LoginFailureKey apply(Map.Entry<LoginFailureKey, LoginFailureEntity> entry) {
+        public LoginFailureKey apply(Map.Entry<LoginFailureKey, SessionEntityWrapper<LoginFailureEntity>> entry) {
             return entry.getKey();
         }
     }
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/UserLoginFailurePredicate.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/UserLoginFailurePredicate.java
index ae0b28d..4996000 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/UserLoginFailurePredicate.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/UserLoginFailurePredicate.java
@@ -17,6 +17,7 @@
 
 package org.keycloak.models.sessions.infinispan.stream;
 
+import org.keycloak.models.sessions.infinispan.changes.SessionEntityWrapper;
 import org.keycloak.models.sessions.infinispan.entities.LoginFailureEntity;
 import org.keycloak.models.sessions.infinispan.entities.LoginFailureKey;
 
@@ -27,7 +28,7 @@ import java.util.function.Predicate;
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
-public class UserLoginFailurePredicate implements Predicate<Map.Entry<LoginFailureKey, LoginFailureEntity>>, Serializable {
+public class UserLoginFailurePredicate implements Predicate<Map.Entry<LoginFailureKey, SessionEntityWrapper<LoginFailureEntity>>>, Serializable {
 
     private String realm;
 
@@ -40,8 +41,8 @@ public class UserLoginFailurePredicate implements Predicate<Map.Entry<LoginFailu
     }
 
     @Override
-    public boolean test(Map.Entry<LoginFailureKey, LoginFailureEntity> entry) {
-        LoginFailureEntity e = entry.getValue();
+    public boolean test(Map.Entry<LoginFailureKey, SessionEntityWrapper<LoginFailureEntity>> entry) {
+        LoginFailureEntity e = entry.getValue().getEntity();
         return realm.equals(e.getRealm());
     }
 
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UserLoginFailureAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UserLoginFailureAdapter.java
index a41777d..0971c26 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UserLoginFailureAdapter.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UserLoginFailureAdapter.java
@@ -17,8 +17,8 @@
 
 package org.keycloak.models.sessions.infinispan;
 
-import org.infinispan.Cache;
 import org.keycloak.models.UserLoginFailureModel;
+import org.keycloak.models.sessions.infinispan.changes.LoginFailuresUpdateTask;
 import org.keycloak.models.sessions.infinispan.entities.LoginFailureEntity;
 import org.keycloak.models.sessions.infinispan.entities.LoginFailureKey;
 
@@ -28,13 +28,11 @@ import org.keycloak.models.sessions.infinispan.entities.LoginFailureKey;
 public class UserLoginFailureAdapter implements UserLoginFailureModel {
 
     private InfinispanUserSessionProvider provider;
-    private Cache<LoginFailureKey, LoginFailureEntity> cache;
     private LoginFailureKey key;
     private LoginFailureEntity entity;
 
-    public UserLoginFailureAdapter(InfinispanUserSessionProvider provider, Cache<LoginFailureKey, LoginFailureEntity> cache, LoginFailureKey key, LoginFailureEntity entity) {
+    public UserLoginFailureAdapter(InfinispanUserSessionProvider provider, LoginFailureKey key, LoginFailureEntity entity) {
         this.provider = provider;
-        this.cache = cache;
         this.key = key;
         this.entity = entity;
     }
@@ -51,8 +49,16 @@ public class UserLoginFailureAdapter implements UserLoginFailureModel {
 
     @Override
     public void setFailedLoginNotBefore(int notBefore) {
-        entity.setFailedLoginNotBefore(notBefore);
-        update();
+        LoginFailuresUpdateTask task = new LoginFailuresUpdateTask() {
+
+            @Override
+            public void runUpdate(LoginFailureEntity entity) {
+                entity.setFailedLoginNotBefore(notBefore);
+            }
+
+        };
+
+        update(task);
     }
 
     @Override
@@ -62,14 +68,30 @@ public class UserLoginFailureAdapter implements UserLoginFailureModel {
 
     @Override
     public void incrementFailures() {
-        entity.setNumFailures(getNumFailures() + 1);
-        update();
+        LoginFailuresUpdateTask task = new LoginFailuresUpdateTask() {
+
+            @Override
+            public void runUpdate(LoginFailureEntity entity) {
+                entity.setNumFailures(entity.getNumFailures() + 1);
+            }
+
+        };
+
+        update(task);
     }
 
     @Override
     public void clearFailures() {
-        entity.clearFailures();
-        update();
+        LoginFailuresUpdateTask task = new LoginFailuresUpdateTask() {
+
+            @Override
+            public void runUpdate(LoginFailureEntity entity) {
+                entity.clearFailures();
+            }
+
+        };
+
+        update(task);
     }
 
     @Override
@@ -79,8 +101,16 @@ public class UserLoginFailureAdapter implements UserLoginFailureModel {
 
     @Override
     public void setLastFailure(long lastFailure) {
-        entity.setLastFailure(lastFailure);
-        update();
+        LoginFailuresUpdateTask task = new LoginFailuresUpdateTask() {
+
+            @Override
+            public void runUpdate(LoginFailureEntity entity) {
+                entity.setLastFailure(lastFailure);
+            }
+
+        };
+
+        update(task);
     }
 
     @Override
@@ -90,12 +120,20 @@ public class UserLoginFailureAdapter implements UserLoginFailureModel {
 
     @Override
     public void setLastIPFailure(String ip) {
-        entity.setLastIPFailure(ip);
-        update();
+        LoginFailuresUpdateTask task = new LoginFailuresUpdateTask() {
+
+            @Override
+            public void runUpdate(LoginFailureEntity entity) {
+                entity.setLastIPFailure(ip);
+            }
+
+        };
+
+        update(task);
     }
 
-    void update() {
-        provider.getTx().replace(cache, key, entity);
+    void update(LoginFailuresUpdateTask task) {
+        provider.getLoginFailuresTx().addTask(key, task);
     }
 
 }
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java
index 50b7760..771487f 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java
@@ -103,6 +103,9 @@ public class UserEntity {
     @Column(name="SERVICE_ACCOUNT_CLIENT_LINK")
     protected String serviceAccountClientLink;
 
+    @Column(name="NOT_BEFORE")
+    protected int notBefore;
+
     public String getId() {
         return id;
     }
@@ -224,6 +227,14 @@ public class UserEntity {
         this.serviceAccountClientLink = serviceAccountClientLink;
     }
 
+    public int getNotBefore() {
+        return notBefore;
+    }
+
+    public void setNotBefore(int notBefore) {
+        this.notBefore = notBefore;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
index b9352c0..b543d6b 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
@@ -39,7 +39,6 @@ import org.keycloak.models.UserProvider;
 import org.keycloak.models.jpa.entities.CredentialAttributeEntity;
 import org.keycloak.models.jpa.entities.CredentialEntity;
 import org.keycloak.models.jpa.entities.FederatedIdentityEntity;
-import org.keycloak.models.jpa.entities.UserAttributeEntity;
 import org.keycloak.models.jpa.entities.UserConsentEntity;
 import org.keycloak.models.jpa.entities.UserConsentProtocolMapperEntity;
 import org.keycloak.models.jpa.entities.UserConsentRoleEntity;
@@ -364,6 +363,18 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
 
 
     @Override
+    public void setNotBeforeForUser(RealmModel realm, UserModel user, int notBefore) {
+        UserEntity entity = em.getReference(UserEntity.class, user.getId());
+        entity.setNotBefore(notBefore);
+    }
+
+    @Override
+    public int getNotBeforeOfUser(RealmModel realm, UserModel user) {
+        UserEntity entity = em.getReference(UserEntity.class, user.getId());
+        return entity.getNotBefore();
+    }
+
+    @Override
     public void grantToAllUsers(RealmModel realm, RoleModel role) {
         int num = em.createNamedQuery("grantRoleToAllUsers")
                 .setParameter("realmId", realm.getId())
diff --git a/model/jpa/src/main/java/org/keycloak/storage/jpa/JpaUserFederatedStorageProvider.java b/model/jpa/src/main/java/org/keycloak/storage/jpa/JpaUserFederatedStorageProvider.java
index cded4e9..f6de431 100644
--- a/model/jpa/src/main/java/org/keycloak/storage/jpa/JpaUserFederatedStorageProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/storage/jpa/JpaUserFederatedStorageProvider.java
@@ -416,6 +416,20 @@ public class JpaUserFederatedStorageProvider implements
     }
 
 
+    @Override
+    public void setNotBeforeForUser(RealmModel realm, String userId, int notBefore) {
+        // Track it as attribute for now
+        String notBeforeStr = String.valueOf(notBefore);
+        setSingleAttribute(realm, userId, "fedNotBefore", notBeforeStr);
+    }
+
+    @Override
+    public int getNotBeforeOfUser(RealmModel realm, String userId) {
+        MultivaluedHashMap<String, String> attrs = getAttributes(realm, userId);
+        String notBeforeStr = attrs.getFirst("fedNotBefore");
+
+        return notBeforeStr==null ? 0 : Integer.parseInt(notBeforeStr);
+    }
 
     @Override
     public Set<GroupModel> getGroups(RealmModel realm, String userId) {
diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-3.3.0.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-3.3.0.xml
new file mode 100644
index 0000000..c6d201e
--- /dev/null
+++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-3.3.0.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+  ~ Copyright 2017 Red Hat, Inc. and/or its affiliates
+  ~ and other contributors as indicated by the @author tags.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
+
+    <changeSet author="keycloak" id="3.3.0">
+        <addColumn tableName="USER_ENTITY">
+            <column name="NOT_BEFORE" type="INT" defaultValueNumeric="0"/>
+        </addColumn>
+    </changeSet>
+
+</databaseChangeLog>
diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml
index ae7d98b..96b9a18 100755
--- a/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml
+++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml
@@ -48,4 +48,5 @@
     <include file="META-INF/jpa-changelog-2.5.1.xml"/>
     <include file="META-INF/jpa-changelog-3.0.0.xml"/>
     <include file="META-INF/jpa-changelog-3.2.0.xml"/>
+    <include file="META-INF/jpa-changelog-3.3.0.xml"/>
 </databaseChangeLog>

pom.xml 6(+3 -3)

diff --git a/pom.xml b/pom.xml
index 7ae460f..df441dc 100755
--- a/pom.xml
+++ b/pom.xml
@@ -49,8 +49,8 @@
         <eap.build-tools.version>1.2.2.Final</eap.build-tools.version>
         <wildfly.core.version>3.0.0.Beta30</wildfly.core.version>
 
-        <version.org.wildfly.security.wildfly-elytron>1.1.0.Beta32</version.org.wildfly.security.wildfly-elytron>
-        <version.org.wildfly.security.elytron-web.undertow-server>1.0.0.Beta14</version.org.wildfly.security.elytron-web.undertow-server>
+        <version.org.wildfly.security.wildfly-elytron>1.1.0.CR4</version.org.wildfly.security.wildfly-elytron>
+        <version.org.wildfly.security.elytron-web.undertow-server>1.0.0.CR1</version.org.wildfly.security.elytron-web.undertow-server>
 
         <aesh.version>0.66.15</aesh.version>
         <apache.httpcomponents.version>4.5</apache.httpcomponents.version>
@@ -120,7 +120,7 @@
         <jboss.as.plugin.version>7.5.Final</jboss.as.plugin.version>
         <jmeter.plugin.version>1.9.0</jmeter.plugin.version>
         <jmeter.analysis.plugin.version>1.0.4</jmeter.analysis.plugin.version>
-        <minify.plugin.version>1.7.2</minify.plugin.version>
+        <minify.plugin.version>1.7.6</minify.plugin.version>
         <osgi.bundle.plugin.version>2.3.7</osgi.bundle.plugin.version>
         <wildfly.plugin.version>1.1.0.Final</wildfly.plugin.version>
         <nexus.staging.plugin.version>1.6.5</nexus.staging.plugin.version>
diff --git a/server-spi/src/main/java/org/keycloak/models/UserProvider.java b/server-spi/src/main/java/org/keycloak/models/UserProvider.java
index 46bb4c3..c337f3a 100755
--- a/server-spi/src/main/java/org/keycloak/models/UserProvider.java
+++ b/server-spi/src/main/java/org/keycloak/models/UserProvider.java
@@ -51,6 +51,8 @@ public interface UserProvider extends Provider,
     void updateConsent(RealmModel realm, String userId, UserConsentModel consent);
     boolean revokeConsentForClient(RealmModel realm, String userId, String clientInternalId);
 
+    void setNotBeforeForUser(RealmModel realm, UserModel user, int notBefore);
+    int getNotBeforeOfUser(RealmModel realm, UserModel user);
 
     UserModel getServiceAccount(ClientModel client);
     List<UserModel> getUsers(RealmModel realm, boolean includeServiceAccounts);
diff --git a/server-spi/src/main/java/org/keycloak/storage/federated/UserFederatedStorageProvider.java b/server-spi/src/main/java/org/keycloak/storage/federated/UserFederatedStorageProvider.java
index 1d12d36..da42c4c 100755
--- a/server-spi/src/main/java/org/keycloak/storage/federated/UserFederatedStorageProvider.java
+++ b/server-spi/src/main/java/org/keycloak/storage/federated/UserFederatedStorageProvider.java
@@ -36,6 +36,7 @@ public interface UserFederatedStorageProvider extends Provider,
         UserAttributeFederatedStorage,
         UserBrokerLinkFederatedStorage,
         UserConsentFederatedStorage,
+        UserNotBeforeFederatedStorage,
         UserGroupMembershipFederatedStorage,
         UserRequiredActionsFederatedStorage,
         UserRoleMappingsFederatedStorage,
diff --git a/server-spi-private/src/main/java/org/keycloak/credential/hash/Pbkdf2PasswordHashProvider.java b/server-spi-private/src/main/java/org/keycloak/credential/hash/Pbkdf2PasswordHashProvider.java
index 6a6c1ff..f010dd3 100644
--- a/server-spi-private/src/main/java/org/keycloak/credential/hash/Pbkdf2PasswordHashProvider.java
+++ b/server-spi-private/src/main/java/org/keycloak/credential/hash/Pbkdf2PasswordHashProvider.java
@@ -49,7 +49,12 @@ public class Pbkdf2PasswordHashProvider implements PasswordHashProvider {
 
     @Override
     public boolean policyCheck(PasswordPolicy policy, CredentialModel credential) {
-        return credential.getHashIterations() == policy.getHashIterations() && providerId.equals(credential.getAlgorithm());
+        int policyHashIterations = policy.getHashIterations();
+        if (policyHashIterations == -1) {
+            policyHashIterations = defaultIterations;
+        }
+
+        return credential.getHashIterations() == policyHashIterations && providerId.equals(credential.getAlgorithm());
     }
 
     @Override
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
index 6b7016f..ef95c0a 100755
--- a/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
@@ -185,6 +185,8 @@ public class ModelToRepresentation {
         rep.setDisableableCredentialTypes(session.userCredentialManager().getDisableableCredentialTypes(realm, user));
         rep.setFederationLink(user.getFederationLink());
 
+        rep.setNotBefore(session.users().getNotBeforeOfUser(realm, user));
+
         List<String> reqActions = new ArrayList<String>();
         Set<String> requiredActions = user.getRequiredActions();
         for (String ra : requiredActions){
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index fe27fae..3fdddde 100755
--- a/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -1454,6 +1454,11 @@ public class RepresentationToModel {
                 session.users().addConsent(newRealm, user.getId(), consentModel);
             }
         }
+
+        if (userRep.getNotBefore() != null) {
+            session.users().setNotBeforeForUser(newRealm, user, userRep.getNotBefore());
+        }
+
         if (userRep.getServiceAccountClientId() != null) {
             String clientId = userRep.getServiceAccountClientId();
             ClientModel client = newRealm.getClientByClientId(clientId);
@@ -2378,6 +2383,9 @@ public class RepresentationToModel {
                 federatedStorage.addConsent(newRealm, userRep.getId(), consentModel);
             }
         }
+        if (userRep.getNotBefore() != null) {
+            federatedStorage.setNotBeforeForUser(newRealm, userRep.getId(), userRep.getNotBefore());
+        }
 
 
     }
diff --git a/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java b/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
index facce6c..fa1e238 100755
--- a/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
+++ b/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
@@ -530,6 +530,10 @@ public class ExportUtils {
             userRep.setClientConsents(consentReps);
         }
 
+        // Not Before
+        int notBefore = session.users().getNotBeforeOfUser(realm, user);
+        userRep.setNotBefore(notBefore);
+
         // Service account
         if (user.getServiceAccountClientLink() != null) {
             String clientInternalId = user.getServiceAccountClientLink();
@@ -717,6 +721,10 @@ public class ExportUtils {
             userRep.setClientConsents(consentReps);
         }
 
+        // Not Before
+        int notBefore = session.userFederatedStorage().getNotBeforeOfUser(realm, userRep.getId());
+        userRep.setNotBefore(notBefore);
+
         if (options.isGroupsAndRolesIncluded()) {
             List<String> groups = new LinkedList<>();
             for (GroupModel group : session.userFederatedStorage().getGroups(realm, id)) {
diff --git a/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java b/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java
index c06caa0..b854e52 100755
--- a/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java
+++ b/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java
@@ -180,9 +180,10 @@ public abstract class AuthorizationEndpointBase {
                 return new AuthorizationEndpointChecks(authSession);
 
             } else if (isNewRequest(authSession, client, requestState)) {
-                // Check if we have lastProcessedExecution and restart the session just if yes. Otherwise update just client information from the AuthorizationEndpoint request.
+                // Check if we have lastProcessedExecution note or if some request parameter beside state (eg. prompt, kc_idp_hint) changed. Restart the session just if yes.
+                // Otherwise update just client information from the AuthorizationEndpoint request.
                 // This difference is needed, because of logout from JS applications in multiple browser tabs.
-                if (hasProcessedExecution(authSession)) {
+                if (shouldRestartAuthSession(authSession)) {
                     logger.debug("New request from application received, but authentication session already exists. Restart existing authentication session");
                     authSession.restartSession(realm, client);
                 } else {
@@ -223,11 +224,18 @@ public abstract class AuthorizationEndpointBase {
 
     }
 
+
+    protected boolean shouldRestartAuthSession(AuthenticationSessionModel authSession) {
+        return hasProcessedExecution(authSession);
+    }
+
+
     private boolean hasProcessedExecution(AuthenticationSessionModel authSession) {
         String lastProcessedExecution = authSession.getAuthNote(AuthenticationProcessor.LAST_PROCESSED_EXECUTION);
         return (lastProcessedExecution != null);
     }
 
+
     // See if we have lastProcessedExecution note. If yes, we are expired. Also if we are in different flow than initial one. Otherwise it is browser refresh of initial username/password form
     private boolean shouldShowExpirePage(AuthenticationSessionModel authSession) {
         if (hasProcessedExecution(authSession)) {
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java
index 38dbc8f..f00c892 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java
@@ -49,6 +49,8 @@ import org.keycloak.util.TokenUtil;
 import javax.ws.rs.GET;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
+
+import java.util.Objects;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -370,7 +372,48 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
 
         // If state is same, we likely have the refresh of some previous request
         String stateFromSession = authSession.getClientNote(OIDCLoginProtocol.STATE_PARAM);
-        return !stateFromRequest.equals(stateFromSession);
+        boolean stateChanged =!stateFromRequest.equals(stateFromSession);
+        if (stateChanged) {
+            return true;
+        }
+
+        return isOIDCAuthenticationRelatedParamsChanged(authSession);
+    }
+
+
+    @Override
+    protected boolean shouldRestartAuthSession(AuthenticationSessionModel authSession) {
+        return super.shouldRestartAuthSession(authSession) || isOIDCAuthenticationRelatedParamsChanged(authSession);
+    }
+
+
+    // Check if some important OIDC parameters, which have impact on authentication, changed. If yes, we need to restart auth session
+    private boolean isOIDCAuthenticationRelatedParamsChanged(AuthenticationSessionModel authSession) {
+        if (isRequestParamChanged(authSession, OIDCLoginProtocol.LOGIN_HINT_PARAM, request.getLoginHint())) {
+            return true;
+        }
+        if (isRequestParamChanged(authSession, OIDCLoginProtocol.PROMPT_PARAM, request.getPrompt())) {
+            return true;
+        }
+        if (isRequestParamChanged(authSession, AdapterConstants.KC_IDP_HINT, request.getIdpHint())) {
+            return true;
+        }
+
+        String maxAgeValue = authSession.getClientNote(OIDCLoginProtocol.MAX_AGE_PARAM);
+        if (maxAgeValue == null && request.getMaxAge() == null) {
+            return false;
+        }
+        if (maxAgeValue != null && Integer.parseInt(maxAgeValue) == request.getMaxAge()) {
+            return false;
+        }
+
+        return true;
+    }
+
+
+    private boolean isRequestParamChanged(AuthenticationSessionModel authSession, String noteName, String requestParamValue) {
+        String authSessionNoteValue = authSession.getClientNote(noteName);
+        return !Objects.equals(authSessionNoteValue, requestParamValue);
     }
 
 
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java
index 6a26c69..769947a 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java
@@ -180,6 +180,9 @@ public class TokenManager {
         if (oldToken.getIssuedAt() < realm.getNotBefore()) {
             throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Stale token");
         }
+        if (oldToken.getIssuedAt() < session.users().getNotBeforeOfUser(realm, user)) {
+            throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Stale token");
+        }
 
 
         // recreate token.
@@ -207,9 +210,12 @@ public class TokenManager {
         if (!user.isEnabled()) {
             return false;
         }
+        if (token.getIssuedAt() < session.users().getNotBeforeOfUser(realm, user)) {
+            return false;
+        }
 
         ClientModel client = realm.getClientByClientId(token.getIssuedFor());
-        if (client == null || !client.isEnabled()) {
+        if (client == null || !client.isEnabled() || token.getIssuedAt() < client.getNotBefore()) {
             return false;
         }
 
@@ -816,9 +822,13 @@ public class TokenManager {
                     res.setRefreshExpiresIn(refreshToken.getExpiration() - Time.currentTime());
                 }
             }
+
             int notBefore = realm.getNotBefore();
             if (client.getNotBefore() > notBefore) notBefore = client.getNotBefore();
+            int userNotBefore = session.users().getNotBeforeOfUser(realm, userSession.getUser());
+            if (userNotBefore > notBefore) notBefore = userNotBefore;
             res.setNotBeforePolicy(notBefore);
+
             return res;
         }
     }
diff --git a/services/src/main/java/org/keycloak/protocol/saml/SamlService.java b/services/src/main/java/org/keycloak/protocol/saml/SamlService.java
index 589dde3..d63c98c 100755
--- a/services/src/main/java/org/keycloak/protocol/saml/SamlService.java
+++ b/services/src/main/java/org/keycloak/protocol/saml/SamlService.java
@@ -58,6 +58,7 @@ import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.messages.Messages;
 import org.keycloak.services.resources.RealmsResource;
 import org.keycloak.services.util.CacheControlUtil;
+import org.keycloak.utils.MediaType;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.FormParam;
@@ -67,7 +68,6 @@ import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
-import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 import java.io.IOException;
@@ -594,7 +594,7 @@ public class SamlService extends AuthorizationEndpointBase {
 
     @GET
     @Path("clients/{client}")
-    @Produces(MediaType.TEXT_HTML)
+    @Produces(MediaType.TEXT_HTML_UTF_8)
     public Response idpInitiatedSSO(@PathParam("client") String clientUrlName, @QueryParam("RelayState") String relayState) {
         event.event(EventType.LOGIN);
         CacheControlUtil.noBackButtonCacheControlHeader();
diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
index bc28fc4..0235120 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -817,6 +817,12 @@ public class AuthenticationManager {
                 return null;
             }
 
+            int userNotBefore = session.users().getNotBeforeOfUser(realm, user);
+            if (token.getIssuedAt() < userNotBefore) {
+                logger.debug("User notBefore newer than token");
+                return null;
+            }
+
             UserSessionModel userSession = session.sessions().getUserSession(realm, token.getSessionState());
             if (!isSessionValid(realm, userSession)) {
                 // Check if accessToken was for the offline session.
diff --git a/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java b/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java
index 3d71c2a..dbb0e78 100755
--- a/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java
@@ -107,6 +107,8 @@ public class ResourceAdminManager {
     }
 
     public void logoutUser(URI requestUri, RealmModel realm, UserModel user, KeycloakSession keycloakSession) {
+        keycloakSession.users().setNotBeforeForUser(realm, user, Time.currentTime());
+
         List<UserSessionModel> userSessions = keycloakSession.sessions().getUserSessions(realm, user);
         logoutUserSessions(requestUri, realm, userSessions);
     }
diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java
index ac9bf80..9b26b18 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -18,6 +18,7 @@ package org.keycloak.services.resources;
 
 import org.jboss.logging.Logger;
 import org.keycloak.common.util.Base64Url;
+import org.keycloak.common.util.Time;
 import org.keycloak.common.util.UriUtils;
 import org.keycloak.credential.CredentialModel;
 import org.keycloak.events.Details;
@@ -59,9 +60,9 @@ import org.keycloak.services.validation.Validation;
 import org.keycloak.sessions.AuthenticationSessionModel;
 import org.keycloak.storage.ReadOnlyException;
 import org.keycloak.util.JsonSerialization;
+import org.keycloak.utils.MediaType;
 
 import javax.ws.rs.*;
-import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
@@ -252,33 +253,24 @@ public class AccountService extends AbstractSecuredLocalService {
      */
     @Path("/")
     @GET
-    @Produces(MediaType.TEXT_HTML)
     public Response accountPage() {
-        return forwardToPage(null, AccountPages.ACCOUNT);
-    }
-
-    /**
-     * Get account information.
-     *
-     * @return
-     */
-    @Path("/")
-    @GET
-    @Produces(MediaType.APPLICATION_JSON)
-    public Response accountPageJson() {
-        requireOneOf(AccountRoles.MANAGE_ACCOUNT, AccountRoles.VIEW_PROFILE);
-
-        UserRepresentation rep = ModelToRepresentation.toRepresentation(session, realm, auth.getUser());
-        if (rep.getAttributes() != null) {
-            Iterator<String> itr = rep.getAttributes().keySet().iterator();
-            while (itr.hasNext()) {
-                if (itr.next().startsWith("keycloak.")) {
-                    itr.remove();
+        if (session.getContext().getRequestHeaders().getAcceptableMediaTypes().contains(MediaType.APPLICATION_JSON_TYPE)) {
+            requireOneOf(AccountRoles.MANAGE_ACCOUNT, AccountRoles.VIEW_PROFILE);
+
+            UserRepresentation rep = ModelToRepresentation.toRepresentation(session, realm, auth.getUser());
+            if (rep.getAttributes() != null) {
+                Iterator<String> itr = rep.getAttributes().keySet().iterator();
+                while (itr.hasNext()) {
+                    if (itr.next().startsWith("keycloak.")) {
+                        itr.remove();
+                    }
                 }
             }
-        }
 
-        return Cors.add(request, Response.ok(rep)).auth().allowedOrigins(auth.getToken()).build();
+            return Cors.add(request, Response.ok(rep).type(MediaType.APPLICATION_JSON_TYPE)).auth().allowedOrigins(auth.getToken()).build();
+        } else {
+            return forwardToPage(null, AccountPages.ACCOUNT);
+        }
     }
 
     public static UriBuilder totpUrl(UriBuilder base) {
@@ -514,6 +506,11 @@ public class AccountService extends AbstractSecuredLocalService {
         csrfCheck(stateChecker);
 
         UserModel user = auth.getUser();
+
+        // Rather decrease time a bit. To avoid situation when user is immediatelly redirected to login screen, then automatically authenticated (eg. with Kerberos) and then seeing issues due the stale token
+        // as time on the token will be same like notBefore
+        session.users().setNotBeforeForUser(realm, user, Time.currentTime() - 1);
+
         List<UserSessionModel> userSessions = session.sessions().getUserSessions(realm, user);
         for (UserSessionModel userSession : userSessions) {
             AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, clientConnection, headers, true);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java b/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java
index 61f6254..9503e02 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java
@@ -509,10 +509,13 @@ public class AuthenticationManagementResource {
                 rep.setId(execution.getId());
 
                 if (factory.isConfigurable()) {
-                    AuthenticatorConfigModel authenticatorConfig = realm.getAuthenticatorConfigById(execution.getAuthenticatorConfig());
+                    String authenticatorConfigId = execution.getAuthenticatorConfig();
+                    if(authenticatorConfigId != null) {
+                        AuthenticatorConfigModel authenticatorConfig = realm.getAuthenticatorConfigById(authenticatorConfigId);
 
-                    if (authenticatorConfig != null) {
-                        rep.setAlias(authenticatorConfig.getAlias());
+                        if (authenticatorConfig != null) {
+                            rep.setAlias(authenticatorConfig.getAlias());
+                        }
                     }
                 }
 
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java
index b00f2e4..7fd223f 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java
@@ -146,7 +146,8 @@ public class RealmsAdminResource {
 
             return Response.created(location).build();
         } catch (ModelDuplicateException e) {
-            return ErrorResponse.exists("Realm with same name exists");
+            logger.error("Conflict detected", e);
+            return ErrorResponse.exists("Conflict detected. See logs for details");
         }
     }
 
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java
index fbd318a..21943cc 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java
@@ -508,6 +508,8 @@ public class UserResource {
     public void logout() {
         auth.users().requireManage(user);
 
+        session.users().setNotBeforeForUser(realm, user, Time.currentTime());
+
         List<UserSessionModel> userSessions = session.sessions().getUserSessions(realm, user);
         for (UserSessionModel userSession : userSessions) {
             AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, clientConnection, headers, true);
diff --git a/services/src/main/java/org/keycloak/storage/UserStorageManager.java b/services/src/main/java/org/keycloak/storage/UserStorageManager.java
index 2baeb8c..8c5b633 100755
--- a/services/src/main/java/org/keycloak/storage/UserStorageManager.java
+++ b/services/src/main/java/org/keycloak/storage/UserStorageManager.java
@@ -231,6 +231,25 @@ public class UserStorageManager implements UserProvider, OnUserCache, OnCreateCo
         }
     }
 
+    @Override
+    public void setNotBeforeForUser(RealmModel realm, UserModel user, int notBefore) {
+        if (StorageId.isLocalStorage(user)) {
+            localStorage().setNotBeforeForUser(realm, user, notBefore);
+        } else {
+            getFederatedStorage().setNotBeforeForUser(realm, user.getId(), notBefore);
+        }
+    }
+
+    @Override
+    public int getNotBeforeOfUser(RealmModel realm, UserModel user) {
+        if (StorageId.isLocalStorage(user)) {
+            return localStorage().getNotBeforeOfUser(realm, user);
+
+        } else {
+            return getFederatedStorage().getNotBeforeOfUser(realm, user.getId());
+        }
+    }
+
     /**
      * Allows a UserStorageProvider to proxy and/or synchronize an imported user.
      *
diff --git a/services/src/main/java/org/keycloak/utils/MediaType.java b/services/src/main/java/org/keycloak/utils/MediaType.java
index acb0b41..4842f38 100644
--- a/services/src/main/java/org/keycloak/utils/MediaType.java
+++ b/services/src/main/java/org/keycloak/utils/MediaType.java
@@ -40,4 +40,8 @@ public class MediaType {
     public static final String APPLICATION_JWT = "application/jwt";
     public static final javax.ws.rs.core.MediaType APPLICATION_JWT_TYPE = new javax.ws.rs.core.MediaType("application", "jwt");
 
+    public static final String APPLICATION_XML = javax.ws.rs.core.MediaType.APPLICATION_XML;
+
+    public static final String TEXT_XML = javax.ws.rs.core.MediaType.TEXT_XML;
+
 }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java
index e006820..98215f7 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java
@@ -126,6 +126,14 @@ public class AdapterTestStrategy extends ExternalResource {
     protected void after() {
         super.after();
         webRule.after();
+
+        // Revert notBefore
+        KeycloakSession session = keycloakRule.startSession();
+        RealmModel realm = session.realms().getRealmByName("demo");
+        UserModel user = session.users().getUserByUsername("bburke@redhat.com", realm);
+        session.users().setNotBeforeForUser(realm, user, 0);
+        session.getTransactionManager().commit();
+        session.close();
     }
 
     public void testSavedPostRequest() throws Exception {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/IdentityProviderHintTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/IdentityProviderHintTest.java
index 05d1afa..a58b82f 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/IdentityProviderHintTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/IdentityProviderHintTest.java
@@ -95,6 +95,29 @@ public class IdentityProviderHintTest {
         assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8082/auth/"));
     }
 
+
+    // KEYCLOAK-5260
+    @Test
+    public void testSuccessfulRedirectToProviderAfterLoginPageShown() {
+        this.driver.navigate().to("http://localhost:8081/test-app");
+        String loginPageUrl = driver.getCurrentUrl();
+        assertTrue(loginPageUrl.startsWith("http://localhost:8081/auth/"));
+
+        // Manually add "kc_idp_hint" to URL . Should redirect to provider
+        loginPageUrl = loginPageUrl + "&kc_idp_hint=kc-oidc-idp-hidden";
+        this.driver.navigate().to(loginPageUrl);
+        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8082/auth/"));
+
+        // Redirect from the app with the "kc_idp_hint". Should redirect to provider
+        this.driver.navigate().to("http://localhost:8081/test-app?kc_idp_hint=kc-oidc-idp-hidden");
+        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8082/auth/"));
+
+        // Now redirect should't happen
+        this.driver.navigate().to("http://localhost:8081/test-app");
+        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/"));
+    }
+
+
     @Test
     public void testInvalidIdentityProviderHint() {
         this.driver.navigate().to("http://localhost:8081/test-app?kc_idp_hint=invalid-idp-id");
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/FederatedStorageExportImportTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/FederatedStorageExportImportTest.java
index 8a1bf5d..a1e4411 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/FederatedStorageExportImportTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/FederatedStorageExportImportTest.java
@@ -137,7 +137,7 @@ public class FederatedStorageExportImportTest {
 
         Assert.assertEquals(1, session.userFederatedStorage().getStoredUsersCount(realm));
         MultivaluedHashMap<String, String> attributes = session.userFederatedStorage().getAttributes(realm, userId);
-        Assert.assertEquals(2, attributes.size());
+        Assert.assertEquals(3, attributes.size());
         Assert.assertEquals("value1", attributes.getFirst("single1"));
         Assert.assertTrue(attributes.getList("list1").contains("1"));
         Assert.assertTrue(attributes.getList("list1").contains("2"));
@@ -174,6 +174,7 @@ public class FederatedStorageExportImportTest {
         session.userFederatedStorage().createCredential(realm, userId, credential);
         session.userFederatedStorage().grantRole(realm, userId, role);
         session.userFederatedStorage().joinGroup(realm, userId, group);
+        session.userFederatedStorage().setNotBeforeForUser(realm, userId, 50);
         keycloakRule.stopSession(session, true);
 
 
@@ -203,13 +204,14 @@ public class FederatedStorageExportImportTest {
 
         Assert.assertEquals(1, session.userFederatedStorage().getStoredUsersCount(realm));
         MultivaluedHashMap<String, String> attributes = session.userFederatedStorage().getAttributes(realm, userId);
-        Assert.assertEquals(2, attributes.size());
+        Assert.assertEquals(3, attributes.size());
         Assert.assertEquals("value1", attributes.getFirst("single1"));
         Assert.assertTrue(attributes.getList("list1").contains("1"));
         Assert.assertTrue(attributes.getList("list1").contains("2"));
         Assert.assertTrue(session.userFederatedStorage().getRequiredActions(realm, userId).contains("UPDATE_PASSWORD"));
         Assert.assertTrue(session.userFederatedStorage().getRoleMappings(realm, userId).contains(role));
         Assert.assertTrue(session.userFederatedStorage().getGroups(realm, userId).contains(group));
+        Assert.assertEquals(50, session.userFederatedStorage().getNotBeforeOfUser(realm, userId));
         List<CredentialModel> creds = session.userFederatedStorage().getStoredCredentials(realm, userId);
         Assert.assertEquals(1, creds.size());
         Assert.assertTrue(getHashProvider(session, realm.getPasswordPolicy()).verify("password", creds.get(0)));
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserModelTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserModelTest.java
index 191a39a..4e0dca5 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserModelTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserModelTest.java
@@ -241,7 +241,7 @@ public class UserModelTest extends AbstractModelTest {
     @Test
     public void testUpdateUserSingleAttribute() {
         Map<String, List<String>> expected = ImmutableMap.of(
-                "key1", Arrays.asList("value3"), 
+                "key1", Arrays.asList("value3"),
                 "key2", Arrays.asList("value2"));
         
         RealmModel realm = realmManager.createRealm("original");
@@ -398,6 +398,31 @@ public class UserModelTest extends AbstractModelTest {
         Assert.assertFalse(realm2User1.hasRole(role1));
     }
 
+    @Test
+    public void testUserNotBefore() throws Exception {
+        RealmModel realm = realmManager.createRealm("original");
+
+        UserModel user1 = session.users().addUser(realm, "user1");
+        session.users().setNotBeforeForUser(realm, user1, 10);
+
+        commit();
+
+        realm = realmManager.getRealmByName("original");
+        user1 = session.users().getUserByUsername("user1", realm);
+        int notBefore = session.users().getNotBeforeOfUser(realm, user1);
+        Assert.assertEquals(10, notBefore);
+
+        // Try to update
+        session.users().setNotBeforeForUser(realm, user1, 20);
+
+        commit();
+
+        realm = realmManager.getRealmByName("original");
+        user1 = session.users().getUserByUsername("user1", realm);
+        notBefore = session.users().getNotBeforeOfUser(realm, user1);
+        Assert.assertEquals(20, notBefore);
+    }
+
     public static void assertEquals(UserModel expected, UserModel actual) {
         Assert.assertEquals(expected.getUsername(), actual.getUsername());
         Assert.assertEquals(expected.getCreatedTimestamp(), actual.getCreatedTimestamp());
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
index 0312cbb..846267f 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
@@ -462,26 +462,6 @@ public class UserSessionProviderTest {
     }
 
 
-    @Test
-    public void testFailCreateExistingSession() {
-        UserSessionModel userSession = session.sessions().createUserSession("123", realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true, null, null);
-
-        // commit
-        resetSession();
-
-
-        try {
-            session.sessions().createUserSession("123", realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true, null, null);
-            kc.stopSession(session, true);
-            Assert.fail("Not expected to successfully create duplicated userSession");
-        } catch (IllegalStateException e) {
-            // Expected
-            session = kc.startSession();
-        }
-
-    }
-
-
     private void testAuthenticatedClientSession(AuthenticatedClientSessionModel clientSession, String expectedClientId, String expectedUserSessionId, String expectedAction, int expectedTimestamp) {
         Assert.assertEquals(expectedClientId, clientSession.getClient().getClientId());
         Assert.assertEquals(expectedUserSessionId, clientSession.getUserSession().getId());
@@ -531,6 +511,15 @@ public class UserSessionProviderTest {
 
         resetSession();
 
+        // Add the failure, which already exists
+        failure1 = session.sessions().addUserLoginFailure(realm, "user1");
+        failure1.incrementFailures();
+
+        resetSession();
+
+        failure1 = session.sessions().getUserLoginFailure(realm, "user1");
+        assertEquals(2, failure1.getNumFailures());
+
         failure1 = session.sessions().getUserLoginFailure(realm, "user1");
         failure1.clearFailures();
 
@@ -556,13 +545,15 @@ public class UserSessionProviderTest {
     public void testOnUserRemoved() {
         createSessions();
 
-        session.sessions().addUserLoginFailure(realm, "user1");
-        session.sessions().addUserLoginFailure(realm, "user1@localhost");
-        session.sessions().addUserLoginFailure(realm, "user2");
+        UserModel user1 = session.users().getUserByUsername("user1", realm);
+        UserModel user2 = session.users().getUserByUsername("user2", realm);
+
+        session.sessions().addUserLoginFailure(realm, user1.getId());
+        session.sessions().addUserLoginFailure(realm, user2.getId());
 
         resetSession();
 
-        UserModel user1 = session.users().getUserByUsername("user1", realm);
+        user1 = session.users().getUserByUsername("user1", realm);
         new UserManager(session).removeUser(realm, user1);
 
         resetSession();
@@ -570,9 +561,8 @@ public class UserSessionProviderTest {
         assertTrue(session.sessions().getUserSessions(realm, user1).isEmpty());
         assertFalse(session.sessions().getUserSessions(realm, session.users().getUserByUsername("user2", realm)).isEmpty());
 
-        assertNull(session.sessions().getUserLoginFailure(realm, "user1"));
-        assertNull(session.sessions().getUserLoginFailure(realm, "user1@localhost"));
-        assertNotNull(session.sessions().getUserLoginFailure(realm, "user2"));
+        assertNull(session.sessions().getUserLoginFailure(realm, user1.getId()));
+        assertNotNull(session.sessions().getUserLoginFailure(realm, user2.getId()));
     }
 
     private AuthenticatedClientSessionModel createClientSession(ClientModel client, UserSessionModel userSession, String redirect, String state, Set<String> roles, Set<String> protocolMappers) {
diff --git a/testsuite/integration-arquillian/pom.xml b/testsuite/integration-arquillian/pom.xml
index 0bcb2b8..78a7648 100644
--- a/testsuite/integration-arquillian/pom.xml
+++ b/testsuite/integration-arquillian/pom.xml
@@ -45,9 +45,9 @@
         <selenium.version>2.53.0</selenium.version>
         <arquillian-drone.version>2.0.1.Final</arquillian-drone.version>
         <arquillian-graphene.version>2.1.0.Alpha3</arquillian-graphene.version>
-        <arquillian-wildfly-container.version>2.1.0.Alpha2</arquillian-wildfly-container.version>
+        <arquillian-wildfly-container.version>2.1.0.Beta1</arquillian-wildfly-container.version>
         <arquillian-wls-container.version>1.0.1.Final</arquillian-wls-container.version>
-        <arquillian-infinispan-container.version>1.2.0.Beta2</arquillian-infinispan-container.version>
+        <arquillian-infinispan-container.version>1.2.0.Alpha2</arquillian-infinispan-container.version>
         <version.shrinkwrap.resolvers>2.2.2</version.shrinkwrap.resolvers>
         <undertow-embedded.version>1.0.0.Alpha2</undertow-embedded.version>
 
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/resource/TestCacheResource.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/resource/TestCacheResource.java
index 9847b27..964e80d 100644
--- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/resource/TestCacheResource.java
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/resource/TestCacheResource.java
@@ -27,18 +27,17 @@ import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
 
 import org.infinispan.Cache;
 import org.infinispan.client.hotrod.RemoteCache;
 import org.infinispan.remoting.transport.Transport;
-import org.infinispan.remoting.transport.jgroups.JGroupsTransport;
 import org.jgroups.JChannel;
 import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity;
 import org.keycloak.models.sessions.infinispan.util.InfinispanUtil;
 import org.keycloak.testsuite.rest.representation.JGroupsStats;
+import org.keycloak.utils.MediaType;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -82,7 +81,7 @@ public class TestCacheResource {
 
     @GET
     @Path("/clear")
-    @Consumes(MediaType.TEXT_PLAIN)
+    @Consumes(MediaType.TEXT_PLAIN_UTF_8)
     public void clear() {
         cache.clear();
     }
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProvider.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProvider.java
index f47061a..9881088 100644
--- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProvider.java
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProvider.java
@@ -17,7 +17,6 @@
 
 package org.keycloak.testsuite.rest;
 
-import org.jboss.resteasy.annotations.Query;
 import org.jboss.resteasy.annotations.cache.NoCache;
 import org.jboss.resteasy.spi.HttpRequest;
 import org.jboss.resteasy.spi.ResteasyProviderFactory;
@@ -30,6 +29,7 @@ import org.keycloak.representations.adapters.action.TestAvailabilityAction;
 import org.keycloak.services.resource.RealmResourceProvider;
 import org.keycloak.services.resources.RealmsResource;
 import org.keycloak.testsuite.rest.resource.TestingOIDCEndpointsApplicationResource;
+import org.keycloak.utils.MediaType;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.GET;
@@ -38,7 +38,6 @@ import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
-import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
@@ -69,21 +68,21 @@ public class TestApplicationResourceProvider implements RealmResourceProvider {
     }
 
     @POST
-    @Consumes(MediaType.TEXT_PLAIN)
+    @Consumes(MediaType.TEXT_PLAIN_UTF_8)
     @Path("/admin/k_logout")
     public void adminLogout(String data) throws JWSInputException {
         adminLogoutActions.add(new JWSInput(data).readJsonContent(LogoutAction.class));
     }
 
     @POST
-    @Consumes(MediaType.TEXT_PLAIN)
+    @Consumes(MediaType.TEXT_PLAIN_UTF_8)
     @Path("/admin/k_push_not_before")
     public void adminPushNotBefore(String data) throws JWSInputException {
         adminPushNotBeforeActions.add(new JWSInput(data).readJsonContent(PushNotBeforeAction.class));
     }
 
     @POST
-    @Consumes(MediaType.TEXT_PLAIN)
+    @Consumes(MediaType.TEXT_PLAIN_UTF_8)
     @Path("/admin/k_test_available")
     public void testAvailable(String data) throws JWSInputException {
         adminTestAvailabilityAction.add(new JWSInput(data).readJsonContent(TestAvailabilityAction.class));
@@ -119,7 +118,7 @@ public class TestApplicationResourceProvider implements RealmResourceProvider {
     }
 
     @POST
-    @Produces(MediaType.TEXT_HTML)
+    @Produces(MediaType.TEXT_HTML_UTF_8)
     @Path("/{action}")
     public String post(@PathParam("action") String action) {
         String title = "APP_REQUEST";
@@ -148,7 +147,7 @@ public class TestApplicationResourceProvider implements RealmResourceProvider {
     }
 
     @GET
-    @Produces(MediaType.TEXT_HTML)
+    @Produces(MediaType.TEXT_HTML_UTF_8)
     @Path("/{action}")
     public String get(@PathParam("action") String action) {
         //String requestUri = session.getContext().getUri().getRequestUri().toString();
@@ -171,7 +170,7 @@ public class TestApplicationResourceProvider implements RealmResourceProvider {
 
     @GET
     @NoCache
-    @Produces(MediaType.TEXT_HTML)
+    @Produces(MediaType.TEXT_HTML_UTF_8)
     @Path("/get-account-profile")
     public String getAccountProfile(@QueryParam("token") String token, @QueryParam("account-uri") String accountUri) {
         StringBuilder sb = new StringBuilder();
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestApplicationResource.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestApplicationResource.java
index afefde3..96c3c8d 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestApplicationResource.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestApplicationResource.java
@@ -17,18 +17,16 @@
 
 package org.keycloak.testsuite.client.resources;
 
-import org.jboss.resteasy.annotations.cache.NoCache;
 import org.keycloak.representations.adapters.action.LogoutAction;
 import org.keycloak.representations.adapters.action.PushNotBeforeAction;
 import org.keycloak.representations.adapters.action.TestAvailabilityAction;
+import org.keycloak.utils.MediaType;
 
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@@ -56,7 +54,7 @@ public interface TestApplicationResource {
     void clearAdminActions();
 
     @GET
-    @Produces(MediaType.TEXT_HTML)
+    @Produces(MediaType.TEXT_HTML_UTF_8)
     @Path("/get-account-profile")
     String getAccountProfile(@QueryParam("token") String token, @QueryParam("account-uri") String accountUri);
 
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestingCacheResource.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestingCacheResource.java
index e1aee2a..1c362ea 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestingCacheResource.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestingCacheResource.java
@@ -17,18 +17,16 @@
 
 package org.keycloak.testsuite.client.resources;
 
-import java.util.Map;
-import java.util.Set;
+import org.keycloak.testsuite.rest.representation.JGroupsStats;
+import org.keycloak.testsuite.rest.representation.RemoteCacheStats;
+import org.keycloak.utils.MediaType;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
-
-import org.keycloak.testsuite.rest.representation.JGroupsStats;
-import org.keycloak.testsuite.rest.representation.RemoteCacheStats;
+import java.util.Set;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -55,7 +53,7 @@ public interface TestingCacheResource {
 
     @GET
     @Path("/clear")
-    @Consumes(MediaType.TEXT_PLAIN)
+    @Consumes(MediaType.TEXT_PLAIN_UTF_8)
     void clear();
 
     @GET
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestingResource.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestingResource.java
index 2787c0e..080afcf 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestingResource.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestingResource.java
@@ -18,14 +18,13 @@
 package org.keycloak.testsuite.client.resources;
 
 import org.jboss.resteasy.annotations.cache.NoCache;
-import org.keycloak.common.util.MultivaluedHashMap;
 import org.keycloak.representations.idm.AdminEventRepresentation;
 import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
 import org.keycloak.representations.idm.EventRepresentation;
 import org.keycloak.representations.idm.UserRepresentation;
 import org.keycloak.testsuite.components.TestProvider;
 import org.keycloak.testsuite.rest.representation.AuthenticatorState;
-import org.keycloak.testsuite.rest.resource.TestCacheResource;
+import org.keycloak.utils.MediaType;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.GET;
@@ -35,8 +34,6 @@ import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
 import java.util.List;
 import java.util.Map;
 
@@ -259,8 +256,8 @@ public interface TestingResource {
 
     @POST
     @Path("/run-on-server")
-    @Consumes(MediaType.TEXT_PLAIN)
-    @Produces(MediaType.TEXT_PLAIN)
+    @Consumes(MediaType.TEXT_PLAIN_UTF_8)
+    @Produces(MediaType.TEXT_PLAIN_UTF_8)
     String runOnServer(String runOnServer);
 
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AppServerWelcomePage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AppServerWelcomePage.java
new file mode 100644
index 0000000..d7056ee
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AppServerWelcomePage.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.pages;
+
+import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.testsuite.adapter.page.AppServerContextRoot;
+import org.keycloak.testsuite.auth.page.login.OIDCLogin;
+import org.keycloak.testsuite.util.URLUtils;
+import org.keycloak.testsuite.util.WaitUtils;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class AppServerWelcomePage extends AppServerContextRoot {
+
+    @Page
+    protected OIDCLogin loginPage;
+
+    @FindBy(xpath = "//a[text() = 'Access Control']")
+    private WebElement accessControlLink;
+
+    @FindBy(xpath = "//a[text() = 'Manage user profile']")
+    private WebElement manageProfileLink;
+
+    @FindBy(xpath = "//div[text() = 'Logout']")
+    private WebElement logoutLink;
+
+    @Override
+    public boolean isCurrent() {
+        return driver.getPageSource().contains("Access Control");
+    }
+
+    public void navigateToConsole() {
+        WaitUtils.pause(2000);
+        URLUtils.navigateToUri(driver, getInjectedUrl().toString() + "/console", true);
+        waitForPageToLoad(driver);
+    }
+
+    public void login(String username, String password) {
+        loginPage.form().waitForLoginButtonPresent();
+        loginPage.form().login(username, password);
+        waitForPageToLoad(driver);
+    }
+
+    public void navigateToAccessControl() {
+        accessControlLink.click();
+        waitForPageToLoad(driver);
+    }
+
+    public void navigateManageProfile() {
+        manageProfileLink.click();
+        waitForPageToLoad(driver);
+    }
+
+    public void logout() {
+        logoutLink.click();
+        waitForPageToLoad(driver);
+    }
+
+    public boolean isLoginPage() {
+        return loginPage.isCurrent();
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java
index 2f9fede..7223b2a 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java
@@ -88,13 +88,6 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
     @Before
     public void beforePhotozExampleAdapterTest() throws FileNotFoundException {
         deleteAllCookiesForClientPage();
-
-        for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) {
-            if ("Only Owner Policy".equals(policy.getName())) {
-                policy.getConfig().put("mavenArtifactVersion", System.getProperty("project.version"));
-                getAuthorizationResource().policies().policy(policy.getId()).update(policy);
-            }
-        }
     }
 
     @Override
@@ -650,7 +643,13 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
     }
 
     private void importResourceServerSettings() throws FileNotFoundException {
-        getAuthorizationResource().importSettings(loadJson(new FileInputStream(new File(TEST_APPS_HOME_DIR + "/photoz/photoz-restful-api-authz-service.json")), ResourceServerRepresentation.class));
+        ResourceServerRepresentation authSettings = loadJson(new FileInputStream(new File(TEST_APPS_HOME_DIR + "/photoz/photoz-restful-api-authz-service.json")), ResourceServerRepresentation.class);
+
+        authSettings.getPolicies().stream()
+                .filter(x -> "Only Owner Policy".equals(x.getName()))
+                .forEach(x -> x.getConfig().put("mavenArtifactVersion", System.getProperty("project.version")));
+
+        getAuthorizationResource().importSettings(authSettings);
     }
 
     private AuthorizationResource getAuthorizationResource() throws FileNotFoundException {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/BruteForceCrossDCTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/BruteForceCrossDCTest.java
new file mode 100644
index 0000000..ec572e9
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/BruteForceCrossDCTest.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.crossdc;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+
+import javax.ws.rs.NotFoundException;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
+import org.keycloak.models.Constants;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserLoginFailureModel;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.testsuite.Assert;
+import org.keycloak.testsuite.Retry;
+import org.keycloak.testsuite.client.KeycloakTestingClient;
+import org.keycloak.testsuite.util.ClientBuilder;
+import org.keycloak.testsuite.util.OAuthClient;
+import org.keycloak.testsuite.util.RealmBuilder;
+import org.keycloak.testsuite.util.UserBuilder;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class BruteForceCrossDCTest extends AbstractAdminCrossDCTest {
+
+    private static final String REALM_NAME = "brute-force-test";
+
+    @Before
+    public void beforeTest() {
+        try {
+            adminClient.realm(REALM_NAME).remove();
+        } catch (NotFoundException ignore) {
+        }
+
+        UserRepresentation user = UserBuilder.create()
+                .id("login-test-1")
+                .username("login-test-1")
+                .email("login-1@test.com")
+                .enabled(true)
+                .password("password")
+                .addRoles(Constants.OFFLINE_ACCESS_ROLE)
+                .build();
+
+        UserRepresentation user2 = UserBuilder.create()
+                .id("login-test-2")
+                .username("login-test-2")
+                .email("login-2@test.com")
+                .enabled(true)
+                .password("password")
+                .addRoles(Constants.OFFLINE_ACCESS_ROLE)
+                .build();
+
+        ClientRepresentation client = ClientBuilder.create()
+                .clientId("test-app")
+                .directAccessGrants()
+                .redirectUris("http://localhost:8180/auth/realms/master/app/*")
+                .addWebOrigin("http://localhost:8180")
+                .secret("password")
+                .build();
+
+        RealmRepresentation realmRep = RealmBuilder.create()
+                .name(REALM_NAME)
+                .user(user)
+                .user(user2)
+                .client(client)
+                .bruteForceProtected(true)
+                .build();
+
+        adminClient.realms().create(realmRep);
+    }
+
+
+    @Test
+    public void testBruteForceWithUserOperations() throws Exception {
+        // Enable 1st DC only
+        enableDcOnLoadBalancer(DC.FIRST);
+        enableDcOnLoadBalancer(DC.SECOND);
+
+        // Clear all
+        adminClient.realms().realm(REALM_NAME).attackDetection().clearAllBruteForce();
+        assertStatistics("After brute force cleared", 0, 0, 0);
+
+        // Create 10 brute force statuses for user1. Assert available on both DC1 and DC2
+        createBruteForceFailures(10, "login-test-1");
+        assertStatistics("After brute force for user1 created", 10, 0, 1);
+
+        // Create 10 brute force statuses for user2. Assert available on both DC1 and DC2createBruteForceFailures(10, "login-test-2");createBruteForceFailures(10, "login-test-2");
+        createBruteForceFailures(10, "login-test-2");
+        assertStatistics("After brute force for user2 created", 10, 10, 2);
+
+        // Remove brute force for user1
+        adminClient.realms().realm(REALM_NAME).attackDetection().clearBruteForceForUser("login-test-1");
+        assertStatistics("After brute force for user1 cleared", 0, 10, 1);
+
+        // Re-add 10 brute force statuses for user1
+        createBruteForceFailures(10, "login-test-1");
+        assertStatistics("After brute force for user1 re-created", 10, 10, 2);
+
+        // Remove user1
+        adminClient.realms().realm(REALM_NAME).users().get("login-test-1").remove();
+        assertStatistics("After user1 removed", 0, 10, 1);
+    }
+
+
+    @Test
+    public void testBruteForceWithRealmOperations() throws Exception {
+        // Enable 1st DC only
+        enableDcOnLoadBalancer(DC.FIRST);
+        enableDcOnLoadBalancer(DC.SECOND);
+
+        // Clear all
+        adminClient.realms().realm(REALM_NAME).attackDetection().clearAllBruteForce();
+        assertStatistics("After brute force cleared", 0, 0, 0);
+
+        // Create 10 brute force statuses for user1 and user2.
+        createBruteForceFailures(10, "login-test-1");
+        createBruteForceFailures(10, "login-test-2");
+        assertStatistics("After brute force for users created", 10, 10, 2);
+
+        // Clear all
+        adminClient.realms().realm(REALM_NAME).attackDetection().clearAllBruteForce();
+        assertStatistics("After brute force cleared for realm", 0, 0, 0);
+
+        // Re-add 10 brute force statuses for users
+        createBruteForceFailures(10, "login-test-1");
+        createBruteForceFailures(10, "login-test-2");
+        assertStatistics("After brute force for users re-created", 10, 10, 2);
+
+        // Remove realm
+        adminClient.realms().realm(REALM_NAME).remove();
+
+        Retry.execute(() -> {
+            int dc0CacheSize = getTestingClientForStartedNodeInDc(0).testing().cache(InfinispanConnectionProvider.LOGIN_FAILURE_CACHE_NAME).size();
+            int dc1CacheSize = getTestingClientForStartedNodeInDc(1).testing().cache(InfinispanConnectionProvider.LOGIN_FAILURE_CACHE_NAME).size();
+            Assert.assertEquals(0, dc0CacheSize);
+            Assert.assertEquals(0, dc1CacheSize);
+        }, 50, 50);
+
+    }
+
+
+    @Test
+    public void testDuplicatedPutIfAbsentOperation() throws Exception {
+        // Enable 1st DC only
+        enableDcOnLoadBalancer(DC.FIRST);
+        enableDcOnLoadBalancer(DC.SECOND);
+
+        // Clear all
+        adminClient.realms().realm(REALM_NAME).attackDetection().clearAllBruteForce();
+        assertStatistics("After brute force cleared", 0, 0, 0);
+
+        // create the entry manually in DC0
+        addUserLoginFailure(getTestingClientForStartedNodeInDc(0));
+        assertStatistics("After create entry1", 1, 0, 1);
+
+        // try to create the entry manually in DC1 (not use real concurrency for now). It should still update the numFailures in existing entry rather then override it
+        addUserLoginFailure(getTestingClientForStartedNodeInDc(1));
+        assertStatistics("After create entry2", 2, 0, 1);
+
+    }
+
+
+    private void assertStatistics(String prefixMessage, int expectedUser1, int expectedUser2, int expectedCacheSize) {
+        Retry.execute(() -> {
+            int dc0user1 = (Integer) getAdminClientForStartedNodeInDc(0).realm(REALM_NAME).attackDetection().bruteForceUserStatus("login-test-1").get("numFailures");
+            int dc1user1 = (Integer) getAdminClientForStartedNodeInDc(1).realm(REALM_NAME).attackDetection().bruteForceUserStatus("login-test-1").get("numFailures");
+            int dc0user2 = (Integer) getAdminClientForStartedNodeInDc(0).realm(REALM_NAME).attackDetection().bruteForceUserStatus("login-test-2").get("numFailures");
+            int dc1user2 = (Integer) getAdminClientForStartedNodeInDc(1).realm(REALM_NAME).attackDetection().bruteForceUserStatus("login-test-2").get("numFailures");
+
+            int dc0CacheSize = getTestingClientForStartedNodeInDc(0).testing().cache(InfinispanConnectionProvider.LOGIN_FAILURE_CACHE_NAME).size();
+            int dc1CacheSize = getTestingClientForStartedNodeInDc(1).testing().cache(InfinispanConnectionProvider.LOGIN_FAILURE_CACHE_NAME).size();
+
+            log.infof("%s: dc0User1=%d, dc0user2=%d, dc1user1=%d, dc1user2=%d, dc0CacheSize=%d, dc1CacheSize=%d", prefixMessage, dc0user1, dc0user2, dc1user1, dc1user2, dc0CacheSize, dc1CacheSize);
+
+            Assert.assertEquals(dc0user1, expectedUser1);
+            Assert.assertEquals(dc0user2, expectedUser2);
+            Assert.assertEquals(dc1user1, expectedUser1);
+            Assert.assertEquals(dc1user2, expectedUser2);
+
+            Assert.assertEquals(expectedCacheSize, dc0CacheSize);
+            Assert.assertEquals(expectedCacheSize, dc1CacheSize);
+        }, 50, 50);
+    }
+
+
+
+
+
+    private void createBruteForceFailures(int count, String username) throws Exception {
+        oauth.realm(REALM_NAME);
+
+        for (int i=0 ; i<count ; i++) {
+            OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("password", username, "bad-password");
+            Assert.assertNull(response.getAccessToken());
+            Assert.assertNotNull(response.getError());
+        }
+
+    }
+
+
+    // TODO Having this working on Wildfly might be a challenge. Maybe require @Deployment with @TargetsContainer descriptor generated at runtime as we don't know the container qualifier at compile time... Maybe workaround by add endpoint to TestingResourceProvider if needed..
+    private void addUserLoginFailure(KeycloakTestingClient testingClient) throws URISyntaxException, IOException {
+        testingClient.server().run(session -> {
+            RealmModel realm = session.realms().getRealmByName(REALM_NAME);
+            UserLoginFailureModel loginFailure = session.sessions().addUserLoginFailure(realm, "login-test-1");
+            loginFailure.incrementFailures();
+        });
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/manual/SessionsPreloadCrossDCTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/manual/SessionsPreloadCrossDCTest.java
index a73d283..80949cf 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/manual/SessionsPreloadCrossDCTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/manual/SessionsPreloadCrossDCTest.java
@@ -23,7 +23,9 @@ import java.util.List;
 import org.junit.Test;
 import org.keycloak.OAuth2Constants;
 import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
+import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.testsuite.Assert;
+import org.keycloak.testsuite.admin.ApiUtil;
 import org.keycloak.testsuite.arquillian.AuthServerTestEnricher;
 import org.keycloak.testsuite.crossdc.AbstractAdminCrossDCTest;
 import org.keycloak.testsuite.crossdc.DC;
@@ -187,6 +189,52 @@ public class SessionsPreloadCrossDCTest extends AbstractAdminCrossDCTest {
     }
 
 
+    @Test
+    public void loginFailuresPreloadTest() throws Exception {
+        // Enable brute force protector
+        RealmRepresentation realmRep = getAdminClientForStartedNodeInDc(0).realms().realm("test").toRepresentation();
+        realmRep.setBruteForceProtected(true);
+        getAdminClientForStartedNodeInDc(0).realms().realm("test").update(realmRep);
+
+        String userId = ApiUtil.findUserByUsername(getAdminClientForStartedNodeInDc(0).realms().realm("test"), "test-user@localhost").getId();
+
+        int loginFailuresBefore = (Integer) getAdminClientForStartedNodeInDc(0).realm("test").attackDetection().bruteForceUserStatus(userId).get("numFailures");
+        log.infof("loginFailuresBefore: %d", loginFailuresBefore);
+
+        // Create initial brute force records
+        for (int i=0 ; i<SESSIONS_COUNT ; i++) {
+            OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("password", "test-user@localhost", "bad-password");
+            Assert.assertNull(response.getAccessToken());
+            Assert.assertNotNull(response.getError());
+        }
+
+        // Start 2nd DC.
+        containerController.start(getCacheServer(DC.SECOND).getQualifier());
+        startBackendNode(DC.SECOND, 0);
+        enableLoadBalancerNode(DC.SECOND, 0);
+
+        // Ensure loginFailures are loaded in both 1st DC and 2nd DC
+        int size1 = getTestingClientForStartedNodeInDc(0).testing().cache(InfinispanConnectionProvider.LOGIN_FAILURE_CACHE_NAME).size();
+        int size2 = getTestingClientForStartedNodeInDc(1).testing().cache(InfinispanConnectionProvider.LOGIN_FAILURE_CACHE_NAME).size();
+        int loginFailures1 = (Integer) getAdminClientForStartedNodeInDc(0).realm("test").attackDetection().bruteForceUserStatus(userId).get("numFailures");
+        int loginFailures2 = (Integer) getAdminClientForStartedNodeInDc(1).realm("test").attackDetection().bruteForceUserStatus(userId).get("numFailures");
+        log.infof("size1: %d, size2: %d, loginFailures1: %d, loginFailures2: %d", size1, size2, loginFailures1, loginFailures2);
+        Assert.assertEquals(size1, 1);
+        Assert.assertEquals(size2, 1);
+        Assert.assertEquals(loginFailures1, loginFailuresBefore + SESSIONS_COUNT);
+        Assert.assertEquals(loginFailures2, loginFailuresBefore + SESSIONS_COUNT);
+
+        // On DC2 sessions were preloaded from from remoteCache
+        Assert.assertTrue(getTestingClientForStartedNodeInDc(1).testing().cache(InfinispanConnectionProvider.WORK_CACHE_NAME).contains("distributed::remoteCacheLoad::loginFailures"));
+
+        // Disable brute force protector
+        realmRep = getAdminClientForStartedNodeInDc(0).realms().realm("test").toRepresentation();
+        realmRep.setBruteForceProtected(true);
+        getAdminClientForStartedNodeInDc(0).realms().realm("test").update(realmRep);
+    }
+
+
+
     private List<OAuthClient.AccessTokenResponse> createInitialSessions(boolean offline) throws Exception {
         if (offline) {
             oauth.scope(OAuth2Constants.OFFLINE_ACCESS);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/SessionExpirationCrossDCTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/SessionExpirationCrossDCTest.java
index 19e45d1..a7f9559 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/SessionExpirationCrossDCTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/SessionExpirationCrossDCTest.java
@@ -18,6 +18,9 @@
 package org.keycloak.testsuite.crossdc;
 
 
+import java.util.ArrayList;
+import java.util.List;
+
 import javax.ws.rs.NotFoundException;
 
 import org.hamcrest.Matchers;
@@ -101,7 +104,7 @@ public class SessionExpirationCrossDCTest extends AbstractAdminCrossDCTest {
             @JmxInfinispanCacheStatistics(dc=DC.FIRST, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.SESSION_CACHE_NAME) InfinispanStatistics cacheDc1Statistics,
             @JmxInfinispanCacheStatistics(dc=DC.SECOND, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.SESSION_CACHE_NAME) InfinispanStatistics cacheDc2Statistics,
             @JmxInfinispanChannelStatistics() InfinispanStatistics channelStatisticsCrossDc) throws Exception {
-        createInitialSessions(InfinispanConnectionProvider.SESSION_CACHE_NAME, false, cacheDc1Statistics, cacheDc2Statistics);
+        createInitialSessions(InfinispanConnectionProvider.SESSION_CACHE_NAME, false, cacheDc1Statistics, cacheDc2Statistics, true);
 
 //        log.infof("Sleeping!");
 //        Thread.sleep(10000000);
@@ -118,7 +121,7 @@ public class SessionExpirationCrossDCTest extends AbstractAdminCrossDCTest {
 
 
     // Return last used accessTokenResponse
-    private OAuthClient.AccessTokenResponse createInitialSessions(String cacheName, boolean offline, InfinispanStatistics cacheDc1Statistics, InfinispanStatistics cacheDc2Statistics) throws Exception {
+    private List<OAuthClient.AccessTokenResponse> createInitialSessions(String cacheName, boolean offline, InfinispanStatistics cacheDc1Statistics, InfinispanStatistics cacheDc2Statistics, boolean includeRemoteStats) throws Exception {
 
         // Enable second DC
         enableDcOnLoadBalancer(DC.SECOND);
@@ -137,9 +140,9 @@ public class SessionExpirationCrossDCTest extends AbstractAdminCrossDCTest {
             oauth.scope(OAuth2Constants.OFFLINE_ACCESS);
         }
 
-        OAuthClient.AccessTokenResponse lastAccessTokenResponse = null;
+        List<OAuthClient.AccessTokenResponse> responses = new ArrayList<>();
         for (int i=0 ; i<SESSIONS_COUNT ; i++) {
-            lastAccessTokenResponse = oauth.doGrantAccessTokenRequest("password", "login-test", "password");
+            responses.add(oauth.doGrantAccessTokenRequest("password", "login-test", "password"));
         }
 
         // Assert 20 sessions exists on node1 and node2 and on remote caches
@@ -152,11 +155,14 @@ public class SessionExpirationCrossDCTest extends AbstractAdminCrossDCTest {
 
             Assert.assertEquals(sessions11, sessions01 + SESSIONS_COUNT);
             Assert.assertEquals(sessions12, sessions02 + SESSIONS_COUNT);
-            Assert.assertEquals(remoteSessions11, remoteSessions01 + SESSIONS_COUNT);
-            Assert.assertEquals(remoteSessions12, remoteSessions02 + SESSIONS_COUNT);
+
+            if (includeRemoteStats) {
+                Assert.assertEquals(remoteSessions11, remoteSessions01 + SESSIONS_COUNT);
+                Assert.assertEquals(remoteSessions12, remoteSessions02 + SESSIONS_COUNT);
+            }
         }, 50, 50);
 
-        return lastAccessTokenResponse;
+        return responses;
     }
 
 
@@ -191,7 +197,7 @@ public class SessionExpirationCrossDCTest extends AbstractAdminCrossDCTest {
             @JmxInfinispanCacheStatistics(dc=DC.SECOND, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME) InfinispanStatistics cacheDc2Statistics,
             @JmxInfinispanChannelStatistics() InfinispanStatistics channelStatisticsCrossDc) throws Exception {
 
-        createInitialSessions(InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME, true, cacheDc1Statistics, cacheDc2Statistics);
+        createInitialSessions(InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME, true, cacheDc1Statistics, cacheDc2Statistics, true);
 
         channelStatisticsCrossDc.reset();
 
@@ -210,7 +216,7 @@ public class SessionExpirationCrossDCTest extends AbstractAdminCrossDCTest {
             @JmxInfinispanCacheStatistics(dc=DC.SECOND, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.SESSION_CACHE_NAME) InfinispanStatistics cacheDc2Statistics,
             @JmxInfinispanChannelStatistics() InfinispanStatistics channelStatisticsCrossDc) throws Exception {
 
-        createInitialSessions(InfinispanConnectionProvider.SESSION_CACHE_NAME, false, cacheDc1Statistics, cacheDc2Statistics);
+        createInitialSessions(InfinispanConnectionProvider.SESSION_CACHE_NAME, false, cacheDc1Statistics, cacheDc2Statistics, true);
 
         channelStatisticsCrossDc.reset();
 
@@ -229,7 +235,7 @@ public class SessionExpirationCrossDCTest extends AbstractAdminCrossDCTest {
             @JmxInfinispanCacheStatistics(dc=DC.SECOND, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.SESSION_CACHE_NAME) InfinispanStatistics cacheDc2Statistics,
             @JmxInfinispanChannelStatistics() InfinispanStatistics channelStatisticsCrossDc) throws Exception {
 
-        OAuthClient.AccessTokenResponse lastAccessTokenResponse = createInitialSessions(InfinispanConnectionProvider.SESSION_CACHE_NAME, false, cacheDc1Statistics, cacheDc2Statistics);
+        OAuthClient.AccessTokenResponse lastAccessTokenResponse = createInitialSessions(InfinispanConnectionProvider.SESSION_CACHE_NAME, false, cacheDc1Statistics, cacheDc2Statistics, true).get(SESSIONS_COUNT - 1);
 
         // Assert I am able to refresh
         OAuthClient.AccessTokenResponse refreshResponse = oauth.doRefreshTokenRequest(lastAccessTokenResponse.getRefreshToken(), "password");
@@ -273,7 +279,7 @@ public class SessionExpirationCrossDCTest extends AbstractAdminCrossDCTest {
             @JmxInfinispanCacheStatistics(dc=DC.FIRST, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.SESSION_CACHE_NAME) InfinispanStatistics cacheDc1Statistics,
             @JmxInfinispanCacheStatistics(dc=DC.SECOND, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.SESSION_CACHE_NAME) InfinispanStatistics cacheDc2Statistics,
             @JmxInfinispanChannelStatistics() InfinispanStatistics channelStatisticsCrossDc) throws Exception {
-        createInitialSessions(InfinispanConnectionProvider.SESSION_CACHE_NAME, false, cacheDc1Statistics, cacheDc2Statistics);
+        createInitialSessions(InfinispanConnectionProvider.SESSION_CACHE_NAME, false, cacheDc1Statistics, cacheDc2Statistics, true);
 
 //        log.infof("Sleeping!");
 //        Thread.sleep(10000000);
@@ -295,7 +301,7 @@ public class SessionExpirationCrossDCTest extends AbstractAdminCrossDCTest {
             @JmxInfinispanCacheStatistics(dc=DC.FIRST, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME) InfinispanStatistics cacheDc1Statistics,
             @JmxInfinispanCacheStatistics(dc=DC.SECOND, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME) InfinispanStatistics cacheDc2Statistics,
             @JmxInfinispanChannelStatistics() InfinispanStatistics channelStatisticsCrossDc) throws Exception {
-        createInitialSessions(InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME, true, cacheDc1Statistics, cacheDc2Statistics);
+        createInitialSessions(InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME, true, cacheDc1Statistics, cacheDc2Statistics, true);
 
 //        log.infof("Sleeping!");
 //        Thread.sleep(10000000);
@@ -318,7 +324,7 @@ public class SessionExpirationCrossDCTest extends AbstractAdminCrossDCTest {
             @JmxInfinispanCacheStatistics(dc=DC.SECOND, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.SESSION_CACHE_NAME) InfinispanStatistics cacheDc2Statistics,
             @JmxInfinispanChannelStatistics() InfinispanStatistics channelStatisticsCrossDc) throws Exception {
 
-        createInitialSessions(InfinispanConnectionProvider.SESSION_CACHE_NAME, false, cacheDc1Statistics, cacheDc2Statistics);
+        createInitialSessions(InfinispanConnectionProvider.SESSION_CACHE_NAME, false, cacheDc1Statistics, cacheDc2Statistics, true);
 
         channelStatisticsCrossDc.reset();
 
@@ -340,6 +346,39 @@ public class SessionExpirationCrossDCTest extends AbstractAdminCrossDCTest {
     }
 
 
+    @Test
+    public void testLogoutUserWithFailover(
+            @JmxInfinispanCacheStatistics(dc=DC.FIRST, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.SESSION_CACHE_NAME) InfinispanStatistics cacheDc1Statistics,
+            @JmxInfinispanCacheStatistics(dc=DC.SECOND, dcNodeIndex=0, cacheName=InfinispanConnectionProvider.SESSION_CACHE_NAME) InfinispanStatistics cacheDc2Statistics,
+            @JmxInfinispanChannelStatistics() InfinispanStatistics channelStatisticsCrossDc) throws Exception {
+
+        // Start node2 on first DC
+        startBackendNode(DC.FIRST, 1);
+
+        // Don't include remote stats. Size is smaller because of distributed cache
+        List<OAuthClient.AccessTokenResponse> responses = createInitialSessions(InfinispanConnectionProvider.SESSION_CACHE_NAME, false, cacheDc1Statistics, cacheDc2Statistics, false);
+
+        // Kill node2 now. Around 10 sessions (half of SESSIONS_COUNT) will be lost on Keycloak side. But not on infinispan side
+        stopBackendNode(DC.FIRST, 1);
+
+        channelStatisticsCrossDc.reset();
+
+        // Increase offset a bit to ensure logout happens later then token issued time
+        setTimeOffset(10);
+
+        // Logout user
+        ApiUtil.findUserByUsernameId(getAdminClient().realm(REALM_NAME), "login-test").logout();
+
+        // Assert it's not possible to refresh sessions. Works because user.notBefore
+        int i = 0;
+        for (OAuthClient.AccessTokenResponse response : responses) {
+            i++;
+            OAuthClient.AccessTokenResponse refreshTokenResponse = oauth.doRefreshTokenRequest(response.getRefreshToken(), "password");
+            Assert.assertNull("Failed in iteration " + i, refreshTokenResponse.getRefreshToken());
+            Assert.assertNotNull("Failed in iteration " + i, refreshTokenResponse.getError());
+        }
+    }
+
 
 
     // AUTH SESSIONS
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportUtil.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportUtil.java
index 677430d..d1c7007 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportUtil.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportUtil.java
@@ -154,6 +154,8 @@ public class ExportImportUtil {
 
         Assert.assertNull(realmRsc.users().get(wburke.getId()).roles().getAll().getRealmMappings());
 
+        Assert.assertEquals((Object) 159, wburke.getNotBefore());
+
         UserRepresentation loginclient = findByUsername(realmRsc, "loginclient");
         // user with creation timestamp as string in import
         Assert.assertEquals(new Long(123655), loginclient.getCreatedTimestamp());
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LogoutTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LogoutTest.java
index 7fc3f74..a3a03cb 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LogoutTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LogoutTest.java
@@ -21,8 +21,11 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.keycloak.events.Details;
 import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.testsuite.Assert;
 import org.keycloak.testsuite.AssertEvents;
 import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
+import org.keycloak.testsuite.admin.ApiUtil;
 import org.keycloak.testsuite.pages.AppPage;
 import org.keycloak.testsuite.pages.LoginPage;
 
@@ -210,4 +213,23 @@ public class LogoutTest extends AbstractTestRealmKeycloakTest {
         events.expectLogout(sessionId2).removeDetail(Details.REDIRECT_URI).assertEvent();
     }
 
+    @Test
+    public void logoutUserByAdmin() {
+        loginPage.open();
+        loginPage.login("test-user@localhost", "password");
+        assertTrue(appPage.isCurrent());
+        String sessionId = events.expectLogin().assertEvent().getSessionId();
+
+        UserRepresentation user = ApiUtil.findUserByUsername(adminClient.realm("test"), "test-user@localhost");
+        Assert.assertEquals((Object) 0, user.getNotBefore());
+
+        adminClient.realm("test").users().get(user.getId()).logout();
+
+        user = adminClient.realm("test").users().get(user.getId()).toRepresentation();
+        Assert.assertTrue(user.getNotBefore() > 0);
+
+        loginPage.open();
+        loginPage.assertCurrent();
+    }
+
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/PasswordHashingTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/PasswordHashingTest.java
index 6763e7d..8b978a4 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/PasswordHashingTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/PasswordHashingTest.java
@@ -45,6 +45,7 @@ import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
 import org.keycloak.testsuite.AssertEvents;
 import org.keycloak.testsuite.admin.ApiUtil;
 import org.keycloak.testsuite.client.KeycloakTestingClient;
+import org.keycloak.testsuite.pages.AccountUpdateProfilePage;
 import org.keycloak.testsuite.pages.AppPage;
 import org.keycloak.testsuite.pages.AppPage.RequestType;
 import org.keycloak.testsuite.pages.ErrorPage;
@@ -67,6 +68,7 @@ import java.security.NoSuchAlgorithmException;
 import java.security.spec.KeySpec;
 import java.util.Map;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
@@ -78,6 +80,9 @@ import static org.junit.Assert.fail;
  */
 public class PasswordHashingTest extends AbstractTestRealmKeycloakTest {
 
+    @Page
+    private AccountUpdateProfilePage updateProfilePage;
+
     @Deployment
     public static WebArchive deploy() {
         return RunOnServerDeployment.create(PasswordHashingTest.class, AbstractTestRealmKeycloakTest.class);
@@ -147,6 +152,42 @@ public class PasswordHashingTest extends AbstractTestRealmKeycloakTest {
         assertEncoded(credential, "password", credential.getSalt(), "PBKDF2WithHmacSHA256", 1);
     }
 
+    // KEYCLOAK-5282
+    @Test
+    public void testPasswordNotRehasedUnchangedIterations() throws Exception {
+        setPasswordPolicy("");
+
+        String username = "testPasswordNotRehasedUnchangedIterations";
+        createUser(username);
+
+        CredentialModel credential = fetchCredentials(username);
+        String credentialId = credential.getId();
+        byte[] salt = credential.getSalt();
+
+        setPasswordPolicy("hashIterations");
+
+        loginPage.open();
+        loginPage.login(username, "password");
+
+        credential = fetchCredentials(username);
+
+        assertEquals(credentialId, credential.getId());
+        assertArrayEquals(salt, credential.getSalt());
+
+        setPasswordPolicy("hashIterations(" + Pbkdf2Sha256PasswordHashProviderFactory.DEFAULT_ITERATIONS + ")");
+
+        updateProfilePage.open();
+        updateProfilePage.logout();
+
+        loginPage.open();
+        loginPage.login(username, "password");
+
+        credential = fetchCredentials(username);
+
+        assertEquals(credentialId, credential.getId());
+        assertArrayEquals(salt, credential.getSalt());
+    }
+
     @Test
     public void testPbkdf2Sha1() throws Exception {
         setPasswordPolicy("hashAlgorithm(" + Pbkdf2PasswordHashProviderFactory.ID + ")");
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/i18n/LoginPageTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/i18n/LoginPageTest.java
index 2d92719..ea3793a 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/i18n/LoginPageTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/i18n/LoginPageTest.java
@@ -81,6 +81,8 @@ public class LoginPageTest extends AbstractI18NTest {
 
     @Test
     public void languageDropdown() {
+        ProfileAssume.assumeCommunity();
+
         loginPage.open();
         Assert.assertEquals("English", loginPage.getLanguageDropdownText());
 
@@ -143,6 +145,8 @@ public class LoginPageTest extends AbstractI18NTest {
     // KEYCLOAK-3887
     @Test
     public void languageChangeRequiredActions() {
+        ProfileAssume.assumeCommunity();
+
         UserResource user = ApiUtil.findUserByUsernameId(testRealm(), "test-user@localhost");
         UserRepresentation userRep = user.toRepresentation();
         userRep.setRequiredActions(Arrays.asList(UserModel.RequiredAction.UPDATE_PASSWORD.toString()));
@@ -168,6 +172,8 @@ public class LoginPageTest extends AbstractI18NTest {
     // KEYCLOAK-3887
     @Test
     public void languageChangeConsentScreen() {
+        ProfileAssume.assumeCommunity();
+
         // Set client, which requires consent
         oauth.clientId("third-party");
 
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/model/testrealm.json b/testsuite/integration-arquillian/tests/base/src/test/resources/model/testrealm.json
index fb1a7e0..a0c5b30 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/model/testrealm.json
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/model/testrealm.json
@@ -120,6 +120,7 @@
             "username": "wburke",
             "enabled": true,
             "createdTimestamp" : 123654,
+            "notBefore": 159,
             "attributes": {
                 "email": "bburke@redhat.com"
             },
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/wildfly-integration/wildfly-management-realm.json b/testsuite/integration-arquillian/tests/base/src/test/resources/wildfly-integration/wildfly-management-realm.json
new file mode 100644
index 0000000..373ca9a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/wildfly-integration/wildfly-management-realm.json
@@ -0,0 +1,68 @@
+{
+  "realm": "jboss-infra",
+  "enabled": true,
+  "sslRequired": "external",
+  "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"
+  ],
+  "users": [
+    {
+      "username": "admin",
+      "enabled": true,
+      "email": "admin@admin.com",
+      "firstName": "Admin",
+      "lastName": "Istrator",
+      "credentials": [
+        {
+          "type": "password",
+          "value": "admin"
+        }
+      ],
+      "realmRoles": [
+        "Administrator"
+      ],
+      "clientRoles": {
+        "realm-management": [
+          "realm-admin"
+        ],
+        "account": [
+          "manage-account"
+        ]
+      }
+    }
+  ],
+  "roles": {
+    "realm": [
+      {
+        "name": "Administrator",
+        "description": "Administrator privileges"
+      }
+    ]
+  },
+  "clients": [
+    {
+      "clientId": "wildfly-console",
+      "enabled": true,
+      "adminUrl": "http://localhost:10190",
+      "baseUrl": "http://localhost:10190",
+      "publicClient": true,
+      "redirectUris": [
+        "http://localhost:10190/*"
+      ],
+      "webOrigins": ["http://localhost:10190"]
+    },
+    {
+      "clientId": "wildfly-management",
+      "secret": "secret",
+      "enabled": true,
+      "baseUrl": "/photoz-restful-api",
+      "publicClient": false,
+      "redirectUris": [
+        "/photoz-restful-api/*"
+      ],
+      "webOrigins" : ["*"]
+    }
+  ]
+}
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/example/authorization/EAPServletAuthzAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/example/authorization/EAPServletAuthzAdapterTest.java
index 8355037..0ff9905 100644
--- a/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/example/authorization/EAPServletAuthzAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/example/authorization/EAPServletAuthzAdapterTest.java
@@ -17,7 +17,6 @@
 package org.keycloak.testsuite.adapter.example.authorization;
 
 import org.jboss.arquillian.container.test.api.RunAsClient;
-import org.keycloak.testsuite.adapter.example.authorization.AbstractServletAuthzAdapterTest;
 import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
 
 /**
@@ -27,6 +26,6 @@ import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
 @RunAsClient
 @AppServerContainer("app-server-eap")
 //@AdapterLibsLocationProperty("adapter.libs.wildfly")
-public class EAPServletAuthzAdapterTest extends AbstractServletAuthzAdapterTest {
+public class EAPServletAuthzAdapterTest extends AbstractServletAuthzFunctionalAdapterTest {
 
 }
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/pom.xml
index 3a6e545..061e94e 100644
--- a/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/pom.xml
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/pom.xml
@@ -42,7 +42,7 @@
             <groupId>org.wildfly.core</groupId>
             <artifactId>wildfly-cli</artifactId>
             <scope>test</scope>
-            <version>2.2.0.Final</version>
+            <version>3.0.0.Beta30</version>
         </dependency>
     </dependencies>    
 
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyManagementProtectionTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyManagementProtectionTest.java
new file mode 100644
index 0000000..88d4200
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyManagementProtectionTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.adapter.example.authorization;
+
+import static org.junit.Assert.assertTrue;
+import static org.keycloak.testsuite.util.IOUtil.loadRealm;
+
+import java.util.List;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.adapter.AbstractAdapterTest;
+import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
+import org.keycloak.testsuite.pages.AccountUpdateProfilePage;
+import org.keycloak.testsuite.pages.AppServerWelcomePage;
+import org.keycloak.testsuite.util.WaitUtils;
+import org.wildfly.extras.creaper.core.ManagementClient;
+import org.wildfly.extras.creaper.core.online.OnlineManagementClient;
+import org.wildfly.extras.creaper.core.online.OnlineOptions;
+
+/**
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+@AppServerContainer("app-server-wildfly")
+//@AdapterLibsLocationProperty("adapter.libs.wildfly")
+public class WildflyManagementProtectionTest extends AbstractAdapterTest {
+
+    @Page
+    protected AppServerWelcomePage appServerWelcomePage;
+
+    @Page
+    protected AccountUpdateProfilePage accountUpdateProfilePage;
+
+    @Override
+    public void addAdapterTestRealms(List<RealmRepresentation> testRealms) {
+        testRealms.add(loadRealm("/wildfly-integration/wildfly-management-realm.json"));
+    }
+
+    @Before
+    public void beforeAuthTest() {
+        super.beforeAuthTest();
+
+        try {
+            OnlineManagementClient clientWorkerNodeClient = ManagementClient.online(OnlineOptions
+                    .standalone()
+                    .hostAndPort("localhost", 10190)
+                    .build());
+
+            // Create a realm for both wildfly console and mgmt interface
+            clientWorkerNodeClient.execute("/subsystem=keycloak/realm=jboss-infra:add(auth-server-url=http://localhost:8180/auth,realm-public-key=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB)");
+
+            // Create a secure-deployment in order to protect mgmt interface
+            clientWorkerNodeClient.execute("/subsystem=keycloak/secure-deployment=wildfly-management:add(realm=jboss-infra,resource=wildfly-management,principal-attribute=preferred_username,bearer-only=true,ssl-required=EXTERNAL)");
+
+            // Protect HTTP mgmt interface with Keycloak adapter
+            clientWorkerNodeClient.execute("/core-service=management/management-interface=http-interface:undefine-attribute(name=security-realm)");
+            clientWorkerNodeClient.execute("/subsystem=elytron/http-authentication-factory=keycloak-mgmt-http-authentication:add(security-domain=KeycloakDomain,http-server-mechanism-factory=wildfly-management,mechanism-configurations=[{mechanism-name=KEYCLOAK,mechanism-realm-configurations=[{realm-name=KeycloakOIDCRealm,realm-mapper=keycloak-oidc-realm-mapper}]}])");
+            clientWorkerNodeClient.execute("/core-service=management/management-interface=http-interface:write-attribute(name=http-authentication-factory,value=keycloak-mgmt-http-authentication)");
+            clientWorkerNodeClient.execute("/core-service=management/management-interface=http-interface:write-attribute(name=http-upgrade, value={enabled=true, sasl-authentication-factory=management-sasl-authentication})");
+
+            // Enable RBAC where roles are obtained from the identity
+            clientWorkerNodeClient.execute("/core-service=management/access=authorization:write-attribute(name=provider,value=rbac)");
+            clientWorkerNodeClient.execute("/core-service=management/access=authorization:write-attribute(name=use-identity-roles,value=true)");
+
+            // Create a secure-server in order to publish the wildfly console configuration via mgmt interface
+            clientWorkerNodeClient.execute("/subsystem=keycloak/secure-server=wildfly-console:add(realm=jboss-infra,resource=wildfly-console,public-client=true)");
+
+            // reload
+            clientWorkerNodeClient.execute("reload");
+        } catch (Exception cause) {
+            throw new RuntimeException("Failed to configure app server", cause);
+        }
+    }
+
+    @Test
+    public void testLogin() throws InterruptedException {
+        appServerWelcomePage.navigateToConsole();
+        appServerWelcomePage.login("admin", "admin");
+        WaitUtils.pause(2000);
+        assertTrue(appServerWelcomePage.isCurrent());
+    }
+
+    @Test
+    public void testUserCanAccessAccountService() throws InterruptedException {
+        appServerWelcomePage.navigateToConsole();
+        appServerWelcomePage.login("admin", "admin");
+        WaitUtils.pause(2000);
+        appServerWelcomePage.navigateToAccessControl();
+        appServerWelcomePage.navigateManageProfile();
+        assertTrue(accountUpdateProfilePage.isCurrent());
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/other/adapters/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/pom.xml
index 6cfc621..feaf52b 100644
--- a/testsuite/integration-arquillian/tests/other/adapters/pom.xml
+++ b/testsuite/integration-arquillian/tests/other/adapters/pom.xml
@@ -68,6 +68,7 @@
         <app.server.2.management.port.jmx>10399</app.server.2.management.port.jmx>
 
         <settings.path></settings.path>
+        <maven.repo.local></maven.repo.local>
         <repo.url></repo.url>
 
         <adapter.test.props>
@@ -81,6 +82,7 @@
             -Djava.security.krb5.conf=${project.build.directory}/dependency/kerberos/test-krb5.conf
             -Dkie.maven.settings.custom=${settings.path}
             -Drepo.url=${repo.url}
+            -Dmaven.repo.local=${maven.repo.local}
         </adapter.test.props>
 
         <app.server.home>${containers.home}/app-server-${app.server}</app.server.home>
@@ -266,24 +268,6 @@
                                     <configuration>
                                         <artifactItems>
                                             <artifactItem>
-                                                <groupId>org.keycloak.example.demo</groupId>
-                                                <artifactId>product-portal-example</artifactId>
-                                                <version>${project.version}</version>
-                                                <type>war</type>
-                                            </artifactItem>
-                                            <artifactItem>
-                                                <groupId>org.keycloak.example.demo</groupId>
-                                                <artifactId>customer-portal-example</artifactId>
-                                                <version>${project.version}</version>
-                                                <type>war</type>
-                                            </artifactItem>
-                                            <artifactItem>
-                                                <groupId>org.keycloak.example.demo</groupId>
-                                                <artifactId>database-service</artifactId>
-                                                <version>${project.version}</version>
-                                                <type>war</type>
-                                            </artifactItem>
-                                            <artifactItem>
                                                 <groupId>org.keycloak.testsuite</groupId>
                                                 <artifactId>integration-arquillian-test-apps-js-console</artifactId>
                                                 <version>${project.version}</version>
@@ -296,48 +280,6 @@
                                                 <type>war</type>
                                             </artifactItem>
                                             <artifactItem>
-                                                <groupId>org.keycloak</groupId>
-                                                <artifactId>examples-multitenant</artifactId>
-                                                <version>${project.version}</version>
-                                                <type>war</type>
-                                            </artifactItem>
-                                            <artifactItem>
-                                                <groupId>org.keycloak</groupId>
-                                                <artifactId>examples-basicauth</artifactId>
-                                                <version>${project.version}</version>
-                                                <type>war</type>
-                                            </artifactItem>
-                                            <artifactItem>
-                                                <groupId>org.keycloak.example.demo</groupId>
-                                                <artifactId>cors-angular-product-example</artifactId>
-                                                <version>${project.version}</version>
-                                                <type>war</type>
-                                            </artifactItem>
-                                            <artifactItem>
-                                                <groupId>org.keycloak.example.demo</groupId>
-                                                <artifactId>cors-database-service</artifactId>
-                                                <version>${project.version}</version>
-                                                <type>war</type>
-                                            </artifactItem>
-                                            <artifactItem>
-                                                <groupId>org.keycloak</groupId>
-                                                <artifactId>sales-post-sig</artifactId>
-                                                <version>${project.version}</version>
-                                                <type>war</type>
-                                            </artifactItem>
-                                            <artifactItem>
-                                                <groupId>org.keycloak</groupId>
-                                                <artifactId>saml-post-encryption</artifactId>
-                                                <version>${project.version}</version>
-                                                <type>war</type>
-                                            </artifactItem>
-                                            <artifactItem>
-                                                <groupId>org.keycloak</groupId>
-                                                <artifactId>saml-redirect-signatures</artifactId>
-                                                <version>${project.version}</version>
-                                                <type>war</type>
-                                            </artifactItem>
-                                            <artifactItem>
                                                 <groupId>org.keycloak.testsuite</groupId>
                                                 <artifactId>hello-world-authz-service</artifactId>
                                                 <version>${project.version}</version>
diff --git a/themes/src/main/resources/theme/base/account/messages/messages_en.properties b/themes/src/main/resources/theme/base/account/messages/messages_en.properties
index 47dbda1..906c525 100755
--- a/themes/src/main/resources/theme/base/account/messages/messages_en.properties
+++ b/themes/src/main/resources/theme/base/account/messages/messages_en.properties
@@ -160,9 +160,10 @@ locale_es=Espa\u00F1ol
 locale_fr=Fran\u00e7ais
 locale_it=Italian
 locale_ja=\u65E5\u672C\u8A9E
+locale_nl=Nederlands
 locale_no=Norsk
 locale_lt=Lietuvi\u0173
 locale_pt-BR=Portugu\u00EAs (Brasil)
 locale_ru=\u0420\u0443\u0441\u0441\u043A\u0438\u0439
+locale_sv=Svenska
 locale_zh-CN=\u4e2d\u6587\u7b80\u4f53
-locale_sv=Svenska
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/account/messages/messages_zh_CN.properties b/themes/src/main/resources/theme/base/account/messages/messages_zh_CN.properties
index 4691002..f263668 100644
--- a/themes/src/main/resources/theme/base/account/messages/messages_zh_CN.properties
+++ b/themes/src/main/resources/theme/base/account/messages/messages_zh_CN.properties
@@ -157,6 +157,7 @@ locale_es=Español
 locale_fr=Français
 locale_it=Italian
 locale_ja=日本語
+locale_nl=Nederlands
 locale_no=Norsk
 locale_lt=Lietuvių
 locale_pt-BR=Português (Brasil)
diff --git a/themes/src/main/resources/theme/base/account/theme.properties b/themes/src/main/resources/theme/base/account/theme.properties
index b9c3990..d7c6edf 100644
--- a/themes/src/main/resources/theme/base/account/theme.properties
+++ b/themes/src/main/resources/theme/base/account/theme.properties
@@ -1 +1 @@
-locales=ca,de,en,es,fr,it,ja,lt,no,pt-BR,ru,zh-CN
\ No newline at end of file
+locales=ca,de,en,es,fr,it,ja,lt,nl,no,pt-BR,ru,zh-CN
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js
index 14c9392..037a0c3 100644
--- a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js
@@ -971,12 +971,12 @@ module.controller('ResourceServerPolicyResourceDetailCtrl', function($scope, $ro
                 $scope.applyToResourceTypeFlag = true;
             }
 
-            $scope.selectedPolicies = [];
             ResourceServerPermission.associatedPolicies({
                 realm : $route.current.params.realm,
                 client : client.id,
                 id : policy.id
             }, function(policies) {
+                $scope.selectedPolicies = [];
                 for (i = 0; i < policies.length; i++) {
                     policies[i].text = policies[i].name;
                     $scope.selectedPolicies.push(policies[i]);
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
index 33cb93b..76c9a9b 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
@@ -730,12 +730,17 @@ module.controller('ClientImportCtrl', function($scope, $location, $upload, realm
 
 module.controller('ClientListCtrl', function($scope, realm, Client, serverInfo, $route, Dialog, Notifications, filterFilter) {
     $scope.realm = realm;
-    $scope.clients = Client.query({realm: realm.realm, viewableOnly: true});
+    $scope.clients = [];
     $scope.currentPage = 1;
     $scope.currentPageInput = 1;
+    $scope.numberOfPages = 1;
     $scope.pageSize = 20;
-    $scope.numberOfPages = Math.ceil($scope.clients.length/$scope.pageSize);
-
+    
+    Client.query({realm: realm.realm, viewableOnly: true}).$promise.then(function(clients) {
+        $scope.numberOfPages = Math.ceil(clients.length/$scope.pageSize);
+        $scope.clients = clients;
+    });
+    
     $scope.$watch('search', function (newVal, oldVal) {
         $scope.filtered = filterFilter($scope.clients, newVal);
         $scope.totalItems = $scope.filtered.length;
@@ -743,7 +748,7 @@ module.controller('ClientListCtrl', function($scope, realm, Client, serverInfo, 
         $scope.currentPage = 1;
         $scope.currentPageInput = 1;
   }, true);
-
+  
     $scope.removeClient = function(client) {
         Dialog.confirmDelete(client.clientId, 'client', function() {
             Client.remove({
@@ -1798,7 +1803,7 @@ module.controller('ClientProtocolMapperCtrl', function($scope, realm, serverInfo
         ClientProtocolMapper.update({
             realm : realm.realm,
             client: client.id,
-            id : mapper.id
+            id : $scope.model.mapper.id
         }, $scope.model.mapper, function() {
             $scope.model.changed = false;
             mapper = angular.copy($scope.mapper);
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
index 77dc9cd..2644379 100644
--- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
@@ -524,13 +524,11 @@ module.controller('RealmPasswordPolicyCtrl', function($scope, Realm, realm, $htt
         }
         var policyString = "";
         for (var i = 0; i < policies.length; i++) {
-            policyString += policies[i].id;
-            if (policies[i].value && policies[i].value != policies[i].defaultValue) {
-                policyString += '(' + policies[i].value + ')';
+            policyString += policies[i].id + '(' + policies[i].value + ')';
+            if (i != policies.length - 1) {
+                policyString += ' and ';
             }
-            policyString += " and ";
         }
-        policyString = policyString.substring(0, policyString.length - 5);
         return policyString;
     }
 
@@ -560,7 +558,7 @@ module.controller('RealmPasswordPolicyCtrl', function($scope, Realm, realm, $htt
 
     $scope.save = function() {
         $scope.realm.passwordPolicy = toString($scope.policy);
-        console.debug($scope.realm.passwordPolicy);
+        console.log($scope.realm.passwordPolicy);
 
         Realm.update($scope.realm, function () {
             $route.reload();
@@ -2515,6 +2513,7 @@ module.controller('ClientRegPolicyDetailCtrl', function($scope, realm, clientReg
 
     if ($scope.providerType.properties) {
         ComponentUtils.addLastEmptyValueToMultivaluedLists($scope.providerType.properties, $scope.instance.config);
+        ComponentUtils.addMvOptionsToMultivaluedLists($scope.providerType.properties);
     }
 
     var oldCopy = angular.copy($scope.instance);
@@ -2525,7 +2524,7 @@ module.controller('ClientRegPolicyDetailCtrl', function($scope, realm, clientReg
             $scope.changed = true;
         }
     }, true);
-
+    
     $scope.reset = function() {
         $route.reload();
     };
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/services.js b/themes/src/main/resources/theme/base/admin/resources/js/services.js
index fca7b33..b084a4f 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/services.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/services.js
@@ -232,6 +232,25 @@ module.factory('ComponentUtils', function() {
             }
         }
     }
+    
+    // Allows you to use ui-select2 with <input> tag.
+    // In HTML you will then use property.mvOptions like this:
+    // <input ui-select2="prop.mvOptions" ng-model="...
+    utils.addMvOptionsToMultivaluedLists = function(properties) {
+        if (!properties) return;
+        
+        for (var i=0 ; i<properties.length ; i++) {
+            var prop = properties[i];
+            if (prop.type !== 'MultivaluedList') continue;
+            
+            prop.mvOptions = {
+                'multiple' : true,
+                'simple_tags' : true,
+                'tags' : angular.copy(prop.options)
+            }
+        }
+        
+    }
 
     return utils;
 });
diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/kc-component-config.html b/themes/src/main/resources/theme/base/admin/resources/templates/kc-component-config.html
index c5172c0..0f99038 100755
--- a/themes/src/main/resources/theme/base/admin/resources/templates/kc-component-config.html
+++ b/themes/src/main/resources/theme/base/admin/resources/templates/kc-component-config.html
@@ -17,9 +17,7 @@
             </select>
         </div>
         <div class="col-md-6" data-ng-if="option.type == 'MultivaluedList'">
-            <select ui-select2 data-ng-model="config[ option.name ]" data-placeholder="{{:: 'selectMultiple' | translate}}..." multiple>
-                <option ng-repeat="val in option.options" value="{{val}}" ng-selected="true">{{val}}</option>
-            </select>
+            <input ui-select2="option.mvOptions" ng-model="config[ option.name ]" data-placeholder="{{:: 'selectMultiple' | translate}}..."/>
         </div>
         <div class="col-md-6" data-ng-if="option.type == 'Role'">
             <div class="row">
diff --git a/themes/src/main/resources/theme/base/admin/theme.properties b/themes/src/main/resources/theme/base/admin/theme.properties
index 4bd8da4..36d3234 100644
--- a/themes/src/main/resources/theme/base/admin/theme.properties
+++ b/themes/src/main/resources/theme/base/admin/theme.properties
@@ -1,2 +1,2 @@
 import=common/keycloak
-locales=ca,en,es,fr,it,ja,lt,no,pt-BR,ru,zh-CN
\ No newline at end of file
+locales=ca,en,es,fr,it,ja,lt,nl,no,pt-BR,ru,zh-CN
diff --git a/themes/src/main/resources/theme/base/email/theme.properties b/themes/src/main/resources/theme/base/email/theme.properties
index b9c3990..d7c6edf 100644
--- a/themes/src/main/resources/theme/base/email/theme.properties
+++ b/themes/src/main/resources/theme/base/email/theme.properties
@@ -1 +1 @@
-locales=ca,de,en,es,fr,it,ja,lt,no,pt-BR,ru,zh-CN
\ No newline at end of file
+locales=ca,de,en,es,fr,it,ja,lt,nl,no,pt-BR,ru,zh-CN
diff --git a/themes/src/main/resources/theme/base/login/messages/messages_en.properties b/themes/src/main/resources/theme/base/login/messages/messages_en.properties
index dbd0a3c..9abb4fe 100755
--- a/themes/src/main/resources/theme/base/login/messages/messages_en.properties
+++ b/themes/src/main/resources/theme/base/login/messages/messages_en.properties
@@ -230,6 +230,7 @@ locale_es=Espa\u00F1ol
 locale_fr=Fran\u00e7ais
 locale_it=Italian
 locale_ja=\u65E5\u672C\u8A9E
+locale_nl=Nederlands
 locale_no=Norsk
 locale_pt_BR=Portugu\u00EAs (Brasil)
 locale_pt-BR=Portugu\u00EAs (Brasil)
diff --git a/themes/src/main/resources/theme/base/login/messages/messages_zh_CN.properties b/themes/src/main/resources/theme/base/login/messages/messages_zh_CN.properties
index 7a1a072..39340bb 100644
--- a/themes/src/main/resources/theme/base/login/messages/messages_zh_CN.properties
+++ b/themes/src/main/resources/theme/base/login/messages/messages_zh_CN.properties
@@ -215,6 +215,7 @@ locale_es=Español
 locale_fr=Français
 locale_it=Italian
 locale_ja=日本語
+locale_nl=Nederlands
 locale_no=Norsk
 locale_pt_BR=Português (Brasil)
 locale_pt-BR=Português (Brasil)
diff --git a/themes/src/main/resources/theme/base/login/theme.properties b/themes/src/main/resources/theme/base/login/theme.properties
index b9c3990..d7c6edf 100644
--- a/themes/src/main/resources/theme/base/login/theme.properties
+++ b/themes/src/main/resources/theme/base/login/theme.properties
@@ -1 +1 @@
-locales=ca,de,en,es,fr,it,ja,lt,no,pt-BR,ru,zh-CN
\ No newline at end of file
+locales=ca,de,en,es,fr,it,ja,lt,nl,no,pt-BR,ru,zh-CN
diff --git a/themes/src/main/resources-community/theme/base/account/messages/messages_nl.properties b/themes/src/main/resources-community/theme/base/account/messages/messages_nl.properties
new file mode 100644
index 0000000..f0882fb
--- /dev/null
+++ b/themes/src/main/resources-community/theme/base/account/messages/messages_nl.properties
@@ -0,0 +1,133 @@
+doSave=Opslaan
+doCancel=Annuleer
+doLogOutAllSessions=Alle sessies uitloggen
+doRemove=Verwijder
+doAdd=Voeg toe
+doSignOut=Afmelden
+editAccountHtmlTitle=Bewerk account
+federatedIdentitiesHtmlTitle=Federated Identities
+accountLogHtmlTitle=Account log
+changePasswordHtmlTitle=Verander wachtwoord
+sessionsHtmlTitle=Sessies
+accountManagementTitle=Keycloak Accountbeheer
+authenticatorTitle=Authenticator
+applicationsHtmlTitle=Toepassingen
+authenticatorCode=Eenmalige code
+email=E-mailadres
+firstName=Voornaam
+givenName=Voornaam
+fullName=Volledige naam
+lastName=Achternaam
+familyName=Achternaam
+password=Wachtwoord
+passwordConfirm=Bevestiging
+passwordNew=Nieuw Wachtwoord
+username=Gebruikersnaam
+address=Adres
+street=Straat
+locality=Stad of plaats
+region=Staat, provincie of regio
+postal_code=Postcode
+country=Land
+emailVerified=E-mailadres geverifieerd
+gssDelegationCredential=GSS gedelegeerde aanmeldgegevens
+role_admin=Beheer
+role_realm-admin=Realmbeheer
+role_create-realm=Creëer realm
+role_view-realm=Bekijk realm
+role_view-users=Bekijk gebruikers
+role_view-applications=Bekijk toepassingen
+role_view-clients=Bekijk clients
+role_view-events=Bekijk gebeurtenissen
+role_view-identity-providers=Bekijk identity providers
+role_manage-realm=Beheer realm
+role_manage-users=Beheer gebruikers
+role_manage-applications=Beheer toepassingen
+role_manage-identity-providers=Beheer identity providers
+role_manage-clients=Beheer clients
+role_manage-events=Beheer gebeurtenissen
+role_view-profile=Bekijk profiel
+role_manage-account=Beheer account
+role_manage-account-links=Beheer accountkoppelingen
+role_read-token=Lees token
+role_offline-access=Offline toegang
+role_uma_authorization=Verkrijg UMA rechten
+client_account=Account
+client_security-admin-console=Console Veligheidsbeheer
+client_admin-cli=Beheer CLI
+client_realm-management=Realmbeheer
+client_broker=Broker
+requiredFields=Verplichte velden
+allFieldsRequired=Alle velden verplicht
+backToApplication=&laquo; Terug naar toepassing
+backTo=Terug naar {0}
+date=Datum
+event=Gebeurtenis
+ip=IP
+client=Client
+clients=Clients
+details=Details
+started=Gestart
+lastAccess=Laatste toegang
+expires=Vervalt
+applications=Toepassingen
+account=Account
+federatedIdentity=Federated Identity
+authenticator=Authenticator
+sessions=Sessies
+log=Log
+application=Toepassing
+availablePermissions=Beschikbare rechten
+grantedPermissions=Gegunde rechten
+grantedPersonalInfo=Gegunde Persoonsgegevens
+additionalGrants=Verdere vergunningen
+action=Actie
+inResource=in
+fullAccess=Volledige toegang
+offlineToken=Offline Token
+revoke=Vergunning intrekken
+configureAuthenticators=Ingestelde authenticators
+mobile=Mobiel nummer
+totpStep1=Installeer <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> of Google Authenticator op uw apparaat. Beide toepassingen zijn beschikbaar in <a href="https://play.google.com">Google Play</a> en de Apple App Store.
+totpStep2=Open de toepassing en scan de QR-code of voer de sleutel in.
+totpStep3=Voer de door de toepassing gegeven eenmalige code in en klik op Opslaan om de configuratie af te ronden.
+missingUsernameMessage=Gebruikersnaam ontbreekt.
+missingFirstNameMessage=Voornaam onbreekt.
+invalidEmailMessage=Ongeldig e-mailadres.
+missingLastNameMessage=Achternaam ontbreekt.
+missingEmailMessage=E-mailadres ontbreekt.
+missingPasswordMessage=Wachtwoord ontbreekt.
+notMatchPasswordMessage=Wachtwoorden komen niet overeen.
+missingTotpMessage=Authenticatiecode ontbreekt.
+invalidPasswordExistingMessage=Ongeldig bestaand wachtwoord.
+invalidPasswordConfirmMessage=Wachtwoordbevestiging komt niet overeen.
+invalidTotpMessage=Ongeldige authenticatiecode.
+emailExistsMessage=E-mailadres bestaat reeds.
+readOnlyUserMessage=U kunt uw account niet bijwerken aangezien het account alleen-lezen is.
+readOnlyPasswordMessage=U kunt uw wachtwoord niet wijzigen omdat uw account alleen-lezen is.
+successTotpMessage=Mobiele authenticator geconfigureerd.
+successTotpRemovedMessage=Mobiele authenticator verwijderd.
+successGrantRevokedMessage=Vergunning succesvol ingetrokken
+accountUpdatedMessage=Uw account is gewijzigd.
+accountPasswordUpdatedMessage=Uw wachtwoord is gewijzigd.
+missingIdentityProviderMessage=Geen identity provider aangegeven.
+invalidFederatedIdentityActionMessage=Ongeldige of ontbrekende actie op federated identity.
+identityProviderNotFoundMessage=Gespecificeerde identity provider niet gevonden.
+federatedIdentityLinkNotActiveMessage=Deze federated identity is niet langer geldig.
+federatedIdentityRemovingLastProviderMessage=U kunt de laatste federated identity provider niet verwijderen aangezien u dan niet langer zou kunnen inloggen.
+identityProviderRedirectErrorMessage=Kon niet herverwijzen naar identity provider.
+identityProviderRemovedMessage=Identity provider met succes verwijderd.
+identityProviderAlreadyLinkedMessage=Door {0} teruggegeven federated identity is al gekoppeld aan een andere gebruiker.
+staleCodeAccountMessage=De pagina is verlopen. Probeer het nogmaals.
+consentDenied=Toestemming geweigerd
+accountDisabledMessage=Account is gedeactiveerd. Contacteer de beheerder.
+accountTemporarilyDisabledMessage=Account is tijdelijk deactiveerd, neem contact op met de beheerder of probeer het later opnieuw.
+invalidPasswordMinLengthMessage=Ongeldig wachtwoord: de minimale lengte is {0} karakters.
+invalidPasswordMinLowerCaseCharsMessage=Ongeldig wachtwoord: het moet minstens {0} kleine letters bevatten.
+invalidPasswordMinDigitsMessage=Ongeldig wachtwoord: het moet minstens {0} getallen bevatten.
+invalidPasswordMinUpperCaseCharsMessage=Ongeldig wachtwoord: het moet minstens {0} hoofdletters bevatten.
+invalidPasswordMinSpecialCharsMessage=Ongeldig wachtwoord: het moet minstens {0} speciale karakters bevatten.
+invalidPasswordNotUsernameMessage=Ongeldig wachtwoord: het mag niet overeenkomen met de gebruikersnaam.
+invalidPasswordRegexPatternMessage=Ongeldig wachtwoord: het voldoet niet aan het door de beheerder ingestelde patroon.
+invalidPasswordHistoryMessage=Ongeldig wachtwoord: het mag niet overeen komen met een van de laatste {0} wachtwoorden.
+invalidPasswordGenericMessage=Ongeldig wachtwoord: het nieuwe wachtwoord voldoet niet aan het wachtwoordbeleid.
diff --git a/themes/src/main/resources-community/theme/base/account/messages/messages_no.properties b/themes/src/main/resources-community/theme/base/account/messages/messages_no.properties
index 948ff6c..2147735 100644
--- a/themes/src/main/resources-community/theme/base/account/messages/messages_no.properties
+++ b/themes/src/main/resources-community/theme/base/account/messages/messages_no.properties
@@ -159,6 +159,7 @@ locale_fr=Fran\u00e7ais
 locale_it=Italian
 locale_ja=\u65E5\u672C\u8A9E
 locale_no=Norsk
+locale_nl=Nederlands
 locale_pt-BR=Portugu\u00EAs (Brasil)
 locale_ru=\u0420\u0443\u0441\u0441\u043A\u0438\u0439
 locale_zh-CN=\u4e2d\u6587\u7b80\u4f53
diff --git a/themes/src/main/resources-community/theme/base/account/theme.properties b/themes/src/main/resources-community/theme/base/account/theme.properties
index 6b2b6e8..f1aeaf5 100644
--- a/themes/src/main/resources-community/theme/base/account/theme.properties
+++ b/themes/src/main/resources-community/theme/base/account/theme.properties
@@ -1 +1 @@
-locales=ca,de,en,es,fr,it,ja,lt,no,pt-BR,ru,sv
\ No newline at end of file
+locales=ca,de,en,es,fr,it,ja,lt,no,nl,pt-BR,ru,sv
diff --git a/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_nl.properties b/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_nl.properties
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_nl.properties
diff --git a/themes/src/main/resources-community/theme/base/admin/messages/messages_nl.properties b/themes/src/main/resources-community/theme/base/admin/messages/messages_nl.properties
new file mode 100644
index 0000000..4a04a52
--- /dev/null
+++ b/themes/src/main/resources-community/theme/base/admin/messages/messages_nl.properties
@@ -0,0 +1,27 @@
+invalidPasswordMinLengthMessage=Ongeldig wachtwoord: de minimale lengte is {0} karakters.
+invalidPasswordMinLowerCaseCharsMessage=Ongeldig wachtwoord: het moet minstens {0} kleine letters bevatten.
+invalidPasswordMinDigitsMessage=Ongeldig wachtwoord: het moet minstens {0} getallen bevatten.
+invalidPasswordMinUpperCaseCharsMessage=Ongeldig wachtwoord: het moet minstens {0} hoofdletters bevatten.
+invalidPasswordMinSpecialCharsMessage=Ongeldig wachtwoord: het moet minstens {0} speciale karakters bevatten.
+invalidPasswordNotUsernameMessage=Ongeldig wachtwoord: het mag niet overeenkomen met de gebruikersnaam.
+invalidPasswordRegexPatternMessage=Ongeldig wachtwoord: het voldoet niet aan het door de beheerder ingestelde patroon.
+invalidPasswordHistoryMessage=Ongeldig wachtwoord: het mag niet overeen komen met een van de laatste {0} wachtwoorden.
+invalidPasswordGenericMessage=Ongeldig wachtwoord: het nieuwe wachtwoord voldoet niet aan het wachtwoordbeleid.
+
+ldapErrorInvalidCustomFilter=LDAP filter met aangepaste configuratie start niet met "(" of eindigt niet met ")".
+ldapErrorConnectionTimeoutNotNumber=Verbindingstimeout moet een getal zijn
+ldapErrorReadTimeoutNotNumber=Lees-timeout moet een getal zijn
+ldapErrorMissingClientId=Client ID moet ingesteld zijn als Realm Roles Mapping niet gebruikt wordt.
+ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType=Kan groepsovererving niet behouden bij UID-lidmaatschapstype.
+ldapErrorCantWriteOnlyForReadOnlyLdap=Alleen-schrijven niet mogelijk als LDAP provider mode niet WRITABLE is
+ldapErrorCantWriteOnlyAndReadOnly=Alleen-schrijven en alleen-lezen mogen niet tegelijk ingesteld zijn
+
+clientRedirectURIsFragmentError=Redirect URIs mogen geen URI fragment bevatten
+clientRootURLFragmentError=Root URL mag geen URL fragment bevatten
+
+pairwiseMalformedClientRedirectURI=Client heeft een ongeldige redirect URI.
+pairwiseClientRedirectURIsMissingHost=Client redirect URIs moeten een geldige host-component bevatten.
+pairwiseClientRedirectURIsMultipleHosts=Zonder een geconfigureerde Sector Identifier URI mogen client redirect URIs niet meerdere host componenten hebben.
+pairwiseMalformedSectorIdentifierURI=Onjuist notatie in Sector Identifier URI.
+pairwiseFailedToGetRedirectURIs=Kon geen redirect URIs verkrijgen van de Sector Identifier URI.
+pairwiseRedirectURIsMismatch=Client redirect URIs komen niet overeen met redict URIs ontvangen van de Sector Identifier URI.
diff --git a/themes/src/main/resources-community/theme/base/admin/theme.properties b/themes/src/main/resources-community/theme/base/admin/theme.properties
index 4bd8da4..36d3234 100644
--- a/themes/src/main/resources-community/theme/base/admin/theme.properties
+++ b/themes/src/main/resources-community/theme/base/admin/theme.properties
@@ -1,2 +1,2 @@
 import=common/keycloak
-locales=ca,en,es,fr,it,ja,lt,no,pt-BR,ru,zh-CN
\ No newline at end of file
+locales=ca,en,es,fr,it,ja,lt,nl,no,pt-BR,ru,zh-CN
diff --git a/themes/src/main/resources-community/theme/base/email/messages/messages_nl.properties b/themes/src/main/resources-community/theme/base/email/messages/messages_nl.properties
new file mode 100644
index 0000000..af84d6d
--- /dev/null
+++ b/themes/src/main/resources-community/theme/base/email/messages/messages_nl.properties
@@ -0,0 +1,27 @@
+emailVerificationSubject=Bevestig e-mailadres
+emailVerificationBody=Iemand heeft een {2} account aangemaakt met dit e-mailadres. Als u dit was, klikt u op de onderstaande koppeling om uw e-mailadres te bevestigen \n\n{0}\n\nDeze koppeling zal binnen {1} minuten vervallen.\n\nU kunt dit bericht negeren indien u dit account niet heeft aangemaakt.
+emailVerificationBodyHtml=<p>Iemand heeft een {2} account aangemaakt met dit e-mailadres. Als u dit was, klikt u op de onderstaande koppeling om uw e-mailadres te bevestigen</p><p><a href="{0}">Koppeling naar e-mailadres bevestiging</a></p><p>Deze koppeling zal binnen {1} minuten vervallen.</p<p>U kunt dit bericht negeren indien u dit account niet heeft aangemaakt.</p>
+emailTestSubject=[KEYCLOAK] - SMTP testbericht
+emailTestBody=Dit is een testbericht
+emailTestBodyHtml=<p>Dit is een testbericht</p>
+identityProviderLinkSubject=Koppel {0}
+identityProviderLinkBody=Iemand wil uw "{1}" account koppelen met "{0}" account van gebruiker {2}. Als u dit was, klik dan op de onderstaande link om de accounts te koppelen\n\n{3}\n\nDeze link zal over {4} minuten vervallen.\n\nAls u de accounts niet wilt koppelen, negeer dan dit bericht. Als u accounts koppelt, dan kunt u bij {1} inloggen via {0}.
+identityProviderLinkBodyHtml=<p>Iemand wil uw "{1}" account koppelen met "{0}" account van gebruiker {2}. Als u dit was, klik dan op de onderstaande link om de accounts te koppelen</p><p><a href="{3}">Link om accounts te koppelen</a></p><p>Deze link zal over {4} minuten vervallen.</p><p>Als u de accounts niet wilt koppelen, negeer dan dit bericht. Als u accounts koppelt, dan kunt u bij {1} inloggen via {0}.</p>
+passwordResetSubject=Wijzig wachtwoord
+passwordResetBody=Iemand verzocht de aanmeldgegevens van uw {2} account te wijzigen. Als u dit was, klik dan op de onderstaande koppeling om ze te wijzigen.\n\n{0}\n\nDe link en de code zullen binnen {1} minuten vervallen.\n\nAls u uw aanmeldgegevens niet wilt wijzigen, negeer dan dit bericht en er zal niets gewijzigd worden.
+passwordResetBodyHtml=<p>Iemand verzocht de aanmeldgegevens van uw {2} account te wijzigen. Als u dit was, klik dan op de onderstaande koppeling om ze te wijzigen.</p><p><a href="{0}">Wijzig aanmeldgegevens</a></p><p>De link en de code zullen binnen {1} minuten vervallen.</p><p>Als u uw aanmeldgegevens niet wilt wijzigen, negeer dan dit bericht en er zal niets gewijzigd worden.</p>
+executeActionsSubject=Wijzig uw account
+executeActionsBody=Uw beheerder heeft u verzocht uw {2} account te wijzigen. Klik op de onderstaande koppeling om dit proces te starten. \n\n{0}\n\nDeze link zal over {1} minuten vervallen. \n\nAls u niet over dit verzoek op de hoogte was, negeer dan dit bericht om uw account ongewijzigd te laten.
+executeActionsBodyHtml=<p>Uw beheerder heeft u verzocht uw {2} account te wijzigen. Klik op de onderstaande koppeling om dit proces te starten.</p><p><a href="{0}">Link naar account wijziging</a></p><p>Deze link zal over {1} minuten vervallen.</p><p>Als u niet over dit verzoek op de hoogte was, negeer dan dit bericht om uw account ongewijzigd te laten.</p>
+eventLoginErrorSubject=Inlogfout
+eventLoginErrorBody=Er is een foutieve inlogpoging gedetecteerd op uw account om {0} vanuit {1}. Als u dit niet was, neem dan contact op met de beheerder.
+eventLoginErrorBodyHtml=<p>Er is een foutieve inlogpoging gedetecteerd op uw account om {0} vanuit {1}. Als u dit niet was, neem dan contact op met de beheerder.</p>
+eventRemoveTotpSubject=TOTP verwijderd
+eventRemoveTotpBody=TOTP is verwijderd van uw account om {0} vanuit {1}. Als u dit niet was, neem dan contact op met uw beheerder.
+eventRemoveTotpBodyHtml=<p>TOTP is verwijderd van uw account om {0} vanuit {1}. Als u dit niet was, neem dan contact op met uw beheerder.</p>
+eventUpdatePasswordSubject=Wachtwoord gewijzigd
+eventUpdatePasswordBody=Uw wachtwoord is gewijzigd om {0} door {1}. Als u dit niet was, neem dan contact op met uw beheerder.
+eventUpdatePasswordBodyHtml=<p>Uw wachtwoord is gewijzigd om {0} door {1}. Als u dit niet was, neem dan contact op met uw beheerder.</p>
+eventUpdateTotpSubject=TOTP gewijzigd
+eventUpdateTotpBody=TOTP is gewijzigd voor uw account om {0} door {1}. Als u dit niet was, neem dan contact op met uw beheerder.
+eventUpdateTotpBodyHtml=<p>TOTP is gewijzigd voor uw account om {0} door {1}. Als u dit niet was, neem dan contact op met uw beheerder.</p>
diff --git a/themes/src/main/resources-community/theme/base/email/theme.properties b/themes/src/main/resources-community/theme/base/email/theme.properties
index 6b2b6e8..b970c5e 100644
--- a/themes/src/main/resources-community/theme/base/email/theme.properties
+++ b/themes/src/main/resources-community/theme/base/email/theme.properties
@@ -1 +1 @@
-locales=ca,de,en,es,fr,it,ja,lt,no,pt-BR,ru,sv
\ No newline at end of file
+locales=ca,de,en,es,fr,it,ja,lt,nl,no,pt-BR,ru,sv
diff --git a/themes/src/main/resources-community/theme/base/login/messages/messages_nl.properties b/themes/src/main/resources-community/theme/base/login/messages/messages_nl.properties
new file mode 100644
index 0000000..13af95a
--- /dev/null
+++ b/themes/src/main/resources-community/theme/base/login/messages/messages_nl.properties
@@ -0,0 +1,224 @@
+doLogIn=Inloggen
+doRegister=Registeer
+doCancel=Annuleer
+doSubmit=Verzenden
+doYes=Ja
+doNo=Nee
+doContinue=Doorgaan
+doAccept=Accepteren
+doDecline=Afwijzen
+doForgotPassword=Wachtwoord vergeten?
+doClickHere=Klik hier
+doImpersonate=Identiteit overnemen
+kerberosNotConfigured=Kerberos is niet geconfigureerd
+kerberosNotConfiguredTitle=Kerberos is niet geconfigureerd
+bypassKerberosDetail=U bent niet ingelogd via Kerberos of uw browser kan niet met Kerberos inloggen. Klik op 'doorgaan' om via een andere manier in te loggen
+kerberosNotSetUp=Kerberos is onjuist geconfigureerd. U kunt niet inloggen.
+registerWithTitle=Registeer met {0}
+registerWithTitleHtml={0}
+loginTitle=Inloggen bij {0}
+loginTitleHtml={0}
+impersonateTitle={0} Identiteit overnemen
+impersonateTitleHtml=<strong>{0}</strong> Identiteit overnemen
+realmChoice=Realm
+unknownUser=Onbekende gebruiker
+loginTotpTitle=Mobile Authenticator Setup
+loginProfileTitle=Update accountinformatie
+loginTimeout=U bent te lang bezig geweest met inloggen. Het inlogproces begint overnieuw.
+oauthGrantTitle=Verleen Toegang
+oauthGrantTitleHtml={0}
+errorTitle=Er is een fout opgetreden...
+errorTitleHtml=Er is een fout opgetreden...
+emailVerifyTitle=E-mailadres-verificatie
+emailForgotTitle=Wachtwoord vergeten?
+updatePasswordTitle=Wachtwoord updaten
+codeSuccessTitle=Succescode
+codeErrorTitle=Foutcode: {0}
+
+termsTitle=Voorwaarden
+termsTitleHtml=Voorwaarden
+termsText=<p>Gedefinieerde voorwaarden</p>
+recaptchaFailed=Ongeldige Recaptcha
+recaptchaNotConfigured=Recaptcha is verplicht, maar niet geconfigureerd
+consentDenied=Toestemming geweigerd.
+
+noAccount=Nieuwe gebruiker?
+username=Gebruikersnaam
+usernameOrEmail=Gebruikersnaam of e-mailadres
+firstName=Voornaam
+givenName=Voornaam
+lastName=Achternaam
+familyName=Familienaam
+email=E-mailadres
+password=Wachtwoord
+passwordConfirm=Bevestig wachtwoord
+passwordNew=Nieuw wachtwoord
+passwordNewConfirm=Bevestiging nieuwe wachtwoord
+rememberMe=Ingelogd blijven
+authenticatorCode=Authenticatiecode
+address=Adres
+postal_code=Postcode
+country=Land
+emailVerified=E-mailadres geverifieerd
+gssDelegationCredential=GSS delegatie Credential
+loginTotpStep1=Installeer <a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> of Google Authenticator op uw mobiele telefoon. Beide applicaties zijn beschikbaar in de <a href="https://play.google.com">Google Play</a> en Apple App Store.
+loginTotpStep2=Open de applicatie en scan de barcode of voer de sleutel in
+loginTotpStep3=Voer de eenmalige code die door de applicatie is aangeleverd in en klik op 'Verzenden' om de setup te voltooien
+loginTotpOneTime=Eenmalige code
+
+oauthGrantRequest=Wilt u deze toegangsrechten verlenen?
+inResource=in
+
+emailVerifyInstruction1=Een e-mail met instructies om uw e-mailadres te verifiëren is zojuist verzonden.
+emailVerifyInstruction2=Heeft u geen verificatiecode ontvangen in uw e-mail?
+emailVerifyInstruction3=om opnieuw een e-mail te versturen.
+
+emailLinkIdpTitle=Link {0}
+emailLinkIdp1=Er is een e-mail met instructies verzonden om {0} account {1} te koppelen met uw {2} account.
+emailLinkIdp2=Heeft u geen verificatiecode in uw e-mail ontvangen?
+emailLinkIdp3=om opnieuw een e-mail te versturen.
+
+backToLogin=&laquo; Terug naar Inloggen
+
+emailInstruction=Voer uw gebruikersnaam of e-mailadres in en wij sturen u een e-mailbericht met instructies voor het aanmaken van een nieuw wachtwoord.
+
+copyCodeInstruction=Kopieer deze code en plak deze in uw applicatie:
+
+personalInfo=Persoonlijke informatie:
+role_realm-admin=Realm beheren
+role_create-realm=Realm aanmaken
+role_create-client=Client aanmaken
+role_view-realm=Bekijk realm
+role_view-users=Bekijk gebruikers
+role_view-clients=Bekijk clients
+role_view-events=Bekijk gebeurtenissen
+role_view-identity-providers=Bekijk identity providers
+role_manage-realm=Beheer realm
+role_manage-users=Gebruikers beheren
+role_manage-identity-providers=Beheer identity providers
+role_manage-clients=Beheer clients
+role_manage-events=Beheer gebeurtenissen
+role_view-profile=Profiel bekijken
+role_read-token=Token lezen
+role_offline-access=Offline toegang
+client_account=Account
+client_security-admin-console=Security Admin Console
+client_admin-cli=Admin CLI
+client_broker=Broker
+invalidUserMessage=Ongeldige gebruikersnaam of wachtwoord.
+invalidEmailMessage=Ongeldig e-mailadres.
+accountDisabledMessage=Account is uitgeschakeld, neem contact op met beheer.
+accountTemporarilyDisabledMessage=Account is tijdelijk uitgeschakeld, neem contact op met beheer of probeer het later opnieuw.
+expiredCodeMessage=Logintijd verlopen. Gelieve opnieuw in te loggen.
+
+missingFirstNameMessage=Voer uw voornaam in.
+missingLastNameMessage=Voer uw achternaam in.
+missingEmailMessage=Voer uw e-mailadres in.
+missingUsernameMessage=Voer uw gebruikersnaam in.
+missingPasswordMessage=Voer uw wachtwoord in.
+missingTotpMessage=Voer uw authenticatiecode in.
+notMatchPasswordMessage=Wachtwoorden komen niet overeen.
+invalidPasswordExistingMessage=Ongeldig bestaand wachtwoord.
+invalidPasswordConfirmMessage=Wachtwoord komt niet overeen met wachtwoordbevestiging.
+invalidTotpMessage=Ongeldige authenticatiecode.
+
+usernameExistsMessage=Gebruikersnaam bestaat al.
+emailExistsMessage=E-mailadres bestaat al.
+
+federatedIdentityExistsMessage=Gebruiker met {0} {1} bestaat al. Log in met het beheerdersaccount om het account te koppelen.
+
+confirmLinkIdpTitle=Account bestaat al
+federatedIdentityConfirmLinkMessage=Gebruiker met {0} {1} bestaat al. Hoe wilt u doorgaan?
+federatedIdentityConfirmReauthenticateMessage=Authenticeer als {0} om uw account te koppelen {1}
+confirmLinkIdpReviewProfile=Nalopen profiel
+confirmLinkIdpContinue=Voeg toe aan bestaande account
+
+configureTotpMessage=U moet de Mobile Authenticator configuren om uw account te activeren.
+updateProfileMessage=U moet uw gebruikersprofiel bijwerken om uw account te activeren.
+updatePasswordMessage=U moet uw wachtwoord wijzigen om uw account te activeren.
+verifyEmailMessage=U moet uw e-mailadres verifiëren om uw account te activeren.
+linkIdpMessage=U moet uw e-mailadres verifiëren om uw account te koppelen aan {0}.
+
+emailSentMessage=U ontvangt binnenkort een e-mail met verdere instructies.
+emailSendErrorMessage=Het versturen van de e-mail is mislukt, probeer het later opnieuw.
+
+accountUpdatedMessage=Uw account is gewijzigd.
+accountPasswordUpdatedMessage=Uw wachtwoord is gewijzigd.
+
+noAccessMessage=Geen toegang
+
+invalidPasswordMinLengthMessage=Ongeldig wachtwoord, de minimumlengte is {0} karakters.
+invalidPasswordMinDigitsMessage=Ongeldig wachtwoord, deze moet minstens {0} cijfers bevatten.
+invalidPasswordMinLowerCaseCharsMessage=Ongeldig wachtwoord, deze moet minstens {0} kleine letters bevatten.
+invalidPasswordMinUpperCaseCharsMessage=Ongeldig wachtwoord, deze moet minstens {0} hoofdletters bevatten.
+invalidPasswordMinSpecialCharsMessage=Ongeldig wachtwoord, deze moet minstens {0} speciale tekens bevatten.
+invalidPasswordNotUsernameMessage=Ongeldig wachtwoord, deze mag niet overeen komen met de gebruikersnaam.
+invalidPasswordRegexPatternMessage=Ongeldig wachtwoord, deze komt niet overeen met opgegeven reguliere expressie(s).
+invalidPasswordHistoryMessage=Ongeldig wachtwoord, deze mag niet overeen komen met een van de laatste {0} wachtwoorden.
+
+failedToProcessResponseMessage=Het verwerken van de respons is mislukt
+httpsRequiredMessage=HTTPS vereist
+realmNotEnabledMessage=Realm niet geactiveerd
+invalidRequestMessage=Ongeldige request
+failedLogout=Afmelden is mislukt
+unknownLoginRequesterMessage=De login requester is onbekend
+loginRequesterNotEnabledMessage=De login requester is niet geactiveerd
+bearerOnlyMessage=Bearer-only applicaties mogen geen browserlogin initiëren
+standardFlowDisabledMessage=Client mag geen browserlogin starten met het opgegeven response_type. Standard flow is uitgeschakeld voor de client.
+implicitFlowDisabledMessage=Client mag geen browserlogin starten met opgegeven response_type. Implicit flow is uitgeschakeld voor de klant.
+invalidRedirectUriMessage=Ongeldige redirect-URI
+unsupportedNameIdFormatMessage=Niet-ondersteunde NameIDFormat
+invalidRequesterMessage=Ongeldige requester
+registrationNotAllowedMessage=Registratie is niet toegestaan
+resetCredentialNotAllowedMessage=Het opnieuw instellen van de aanmeldgegevens is niet toegestaan
+permissionNotApprovedMessage=Recht verworpen.
+noRelayStateInResponseMessage=Geen relay state in antwoord van de identity provider.
+insufficientPermissionMessage=Onvoldoende rechten om identiteiten te koppelen.
+couldNotProceedWithAuthenticationRequestMessage=Het authenticatieverzoek naar de identity provider wordt afgebroken.
+couldNotObtainTokenMessage=Kon geen token bemachtigen van de identity provider.
+unexpectedErrorRetrievingTokenMessage=Onverwachte fout bij het ophalen van de token van de identity provider.
+unexpectedErrorHandlingResponseMessage=Onverwachte fout bij het verwerken van de respons van de identity provider.
+identityProviderAuthenticationFailedMessage=Verificatie mislukt. Er kon niet worden geauthenticeerd met de identity provider.
+identityProviderDifferentUserMessage=U bent geauthenticeerd als {0}, maar u werd verwacht als {1} geauthenticeerd te zijn
+couldNotSendAuthenticationRequestMessage=Kan het authenticatieverzoek niet verzenden naar de identity provider.
+unexpectedErrorHandlingRequestMessage=Onverwachte fout bij het verwerken van het authenticatieverzoek naar de identity provider.
+invalidAccessCodeMessage=Ongeldige toegangscode.
+sessionNotActiveMessage=Sessie inactief.
+invalidCodeMessage=Er is een fout opgetreden, probeer nogmaals in te loggen.
+identityProviderUnexpectedErrorMessage=Onverwachte fout tijdens de authenticatie met de identity provider
+identityProviderNotFoundMessage=Geen identity provider gevonden met deze naam.
+identityProviderLinkSuccess=Uw account is met succes gekoppeld aan {0} account {1}.
+staleCodeMessage=Deze pagina is verlopen. Keer terug naar uw applicatie om opnieuw in te loggen.
+realmSupportsNoCredentialsMessage=Realm ondersteunt geen enkel soort aanmeldgegeven.
+identityProviderNotUniqueMessage=Realm ondersteunt meerdere identity providers. Er kon niet bepaald worden welke identity provider er gebruikt zou moeten worden tijdens de authenticatie.
+emailVerifiedMessage=Uw e-mailadres is geverifieerd.
+staleEmailVerificationLink=De link die u gebruikt is verlopen, wellicht omdat u uw e-mailadres al eerder geverifieerd heeft.
+
+backToApplication=&laquo; Terug naar de applicatie
+missingParameterMessage=Missende parameters: {0}
+clientNotFoundMessage=Client niet gevonden.
+clientDisabledMessage=Client is inactief.
+invalidParameterMessage=Ongeldige parameter: {0}
+alreadyLoggedIn=U bent al ingelogd.
+
+p3pPolicy=CP="This is not a P3P policy!"
+fullName=Volledige naam
+street=Straat
+locality=Plaats
+region=Staat, provincie of regio
+emailLinkIdp4=Als u de e-mail al geverifieerd heeft in een andere browser
+emailLinkIdp5=om door te gaan.
+pageExpiredTitle=Pagina is verlopen
+pageExpiredMsg1=Om het inlogproces te herstarten
+pageExpiredMsg2=Om het inlogproces te vervolgen
+role_admin=Beheerder
+role_view-applications=Bekijk applicaties
+role_manage-applications=Beheer applicaties
+role_manage-account=Beheer account
+role_manage-account-links=Beheer account-links
+client_realm-management=Realm-beheer
+expiredActionMessage=Actie verlopen. Vervolg nu met inloggen.
+invalidPasswordGenericMessage=Ongeldig wachtwoord: het nieuwe wachtwoord voldoet niet aan het wachtwoordbeleid.
+identityProviderAlreadyLinkedMessage=De door {0} teruggegeven gefedereerde identiteit is al aan een andere gebruiker gekoppeld.
+differentUserAuthenticated=U bent in deze sessie al als de gebruiker "{0}" aangemeld. Log eerst uit.
+brokerLinkingSessionExpired=Broker account linking aangevraagd, maar de huidige sessie in verlopen.
diff --git a/themes/src/main/resources-community/theme/base/login/messages/messages_no.properties b/themes/src/main/resources-community/theme/base/login/messages/messages_no.properties
index 1f1a9d9..47922cd 100644
--- a/themes/src/main/resources-community/theme/base/login/messages/messages_no.properties
+++ b/themes/src/main/resources-community/theme/base/login/messages/messages_no.properties
@@ -214,6 +214,7 @@ locale_es=Espa\u00F1ol
 locale_fr=Fran\u00e7ais
 locale_it=Italian
 locale_ja=\u65E5\u672C\u8A9E
+locale_nl=Nederlands
 locale_no=Norsk
 locale_pt_BR=Portugu\u00EAs (Brasil)
 locale_pt-BR=Portugu\u00EAs (Brasil)
diff --git a/themes/src/main/resources-community/theme/base/login/theme.properties b/themes/src/main/resources-community/theme/base/login/theme.properties
index 6b2b6e8..f1aeaf5 100644
--- a/themes/src/main/resources-community/theme/base/login/theme.properties
+++ b/themes/src/main/resources-community/theme/base/login/theme.properties
@@ -1 +1 @@
-locales=ca,de,en,es,fr,it,ja,lt,no,pt-BR,ru,sv
\ No newline at end of file
+locales=ca,de,en,es,fr,it,ja,lt,no,nl,pt-BR,ru,sv