keycloak-uncached

Merge branch 'master-upstream' into feature-lithuanian-locale #

9/4/2016 4:20:03 PM

Changes

distribution/feature-packs/server-feature-pack/src/main/resources/content/standalone/configuration/keycloak-server.json 87(+0 -87)

examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/photoz-ds.xml 12(+0 -12)

README.md 1(+1 -0)

services/pom.xml 5(+5 -0)

testsuite/integration-arquillian/servers/auth-server/jboss/build.xml 42(+0 -42)

testsuite/integration-arquillian/servers/auth-server/jboss/build-truststore.xml 52(+0 -52)

testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/WEB-INF/photoz-ds.xml 12(+0 -12)

testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/SAMLFilterDependency.java 87(+0 -87)

Details

diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java
index cfe5ff9..bbbf573 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java
@@ -32,6 +32,7 @@ import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathCon
 import org.keycloak.representations.idm.authorization.Permission;
 
 import java.net.URI;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
@@ -55,7 +56,7 @@ public abstract class AbstractPolicyEnforcer {
         this.enforcerConfig = policyEnforcer.getEnforcerConfig();
         this.authzClient = policyEnforcer.getClient();
         this.pathMatcher = new PathMatcher();
-        this.paths = policyEnforcer.getPaths();
+        this.paths = new ArrayList<>(policyEnforcer.getPaths());
     }
 
     public AuthorizationContext authorize(OIDCHttpFacade httpFacade) {
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java
index aa6d3d2..88ef9ce 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java
@@ -33,8 +33,12 @@ import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathCon
 import org.keycloak.representations.idm.authorization.Permission;
 
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -95,8 +99,8 @@ public class PolicyEnforcer {
         return authzClient;
     }
 
-    List<PathConfig> getPaths() {
-        return paths;
+    public List<PathConfig> getPaths() {
+        return Collections.unmodifiableList(paths);
     }
 
     KeycloakDeployment getDeployment() {
@@ -154,13 +158,27 @@ public class PolicyEnforcer {
 
                     pathConfig.setId(registrationResponse.getId());
                 } else {
-                    throw new RuntimeException("Could not find matching resource on server with uri [" + path + "] or name [" + resourceName + ". Make sure you have created a resource on the server that matches with the path configuration.");
+                    throw new RuntimeException("Could not find matching resource on server with uri [" + path + "] or name [" + resourceName + "]. Make sure you have created a resource on the server that matches with the path configuration.");
                 }
             } else {
                 pathConfig.setId(search.iterator().next());
             }
 
-            paths.add(pathConfig);
+            PathConfig existingPath = null;
+
+            for (PathConfig current : paths) {
+                if (current.getId().equals(pathConfig.getId()) && current.getPath().equals(pathConfig.getPath())) {
+                    existingPath = current;
+                    break;
+                }
+            }
+
+            if (existingPath == null) {
+                paths.add(pathConfig);
+            } else {
+                existingPath.getMethods().addAll(pathConfig.getMethods());
+                existingPath.getScopes().addAll(pathConfig.getScopes());
+            }
         }
 
         return paths;
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyAdminResource.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyAdminResource.java
index 75de911..96c1d24 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyAdminResource.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyAdminResource.java
@@ -37,12 +37,10 @@ public class TimePolicyAdminResource implements PolicyProviderAdminService {
         String nbf = policy.getConfig().get("nbf");
         String noa = policy.getConfig().get("noa");
 
-        if (nbf == null && noa == null) {
-            throw new RuntimeException("You must provide NotBefore, NotOnOrAfter or both.");
+        if (nbf != null && noa != null) {
+            validateFormat(nbf);
+            validateFormat(noa);
         }
-
-        validateFormat(nbf);
-        validateFormat(noa);
     }
 
     @Override
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProvider.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProvider.java
index dc6af9f..3205cc5 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProvider.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProvider.java
@@ -22,8 +22,11 @@ import org.keycloak.authorization.policy.evaluation.Evaluation;
 import org.keycloak.authorization.policy.provider.PolicyProvider;
 
 import java.text.SimpleDateFormat;
+import java.util.Calendar;
 import java.util.Date;
 
+import static com.sun.corba.se.spi.activation.IIOP_CLEAR_TEXT.value;
+
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
@@ -45,9 +48,7 @@ public class TimePolicyProvider implements PolicyProvider {
     public void evaluate(Evaluation evaluation) {
         try {
             String notBefore = this.policy.getConfig().get("nbf");
-
             if (notBefore != null) {
-
                 if (this.currentDate.before(this.dateFormat.parse(format(notBefore)))) {
                     evaluation.deny();
                     return;
@@ -55,7 +56,6 @@ public class TimePolicyProvider implements PolicyProvider {
             }
 
             String notOnOrAfter = this.policy.getConfig().get("noa");
-
             if (notOnOrAfter != null) {
                 if (this.currentDate.after(this.dateFormat.parse(format(notOnOrAfter)))) {
                     evaluation.deny();
@@ -63,12 +63,48 @@ public class TimePolicyProvider implements PolicyProvider {
                 }
             }
 
+            if (isInvalid(Calendar.DAY_OF_MONTH, "dayMonth")
+                    || isInvalid(Calendar.MONTH, "month")
+                    || isInvalid(Calendar.YEAR, "year")
+                    || isInvalid(Calendar.HOUR_OF_DAY, "hour")
+                    || isInvalid(Calendar.MINUTE, "minute")) {
+                evaluation.deny();
+                return;
+            }
+
             evaluation.grant();
         } catch (Exception e) {
             throw new RuntimeException("Could not evaluate time-based policy [" + this.policy.getName() + "].", e);
         }
     }
 
+    private boolean isInvalid(int timeConstant, String configName) {
+        Calendar calendar = Calendar.getInstance();
+
+        calendar.setTime(this.currentDate);
+
+        int dateField = calendar.get(timeConstant);
+
+        if (Calendar.MONTH == timeConstant) {
+            dateField++;
+        }
+
+        String start = this.policy.getConfig().get(configName);
+        if (start != null) {
+            String end = this.policy.getConfig().get(configName + "End");
+            if (end != null) {
+                if (dateField < Integer.parseInt(start)  || dateField > Integer.parseInt(end)) {
+                    return true;
+                }
+            } else {
+                if (dateField != Integer.parseInt(start)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     static String format(String notBefore) {
         String trimmed = notBefore.trim();
 
diff --git a/core/src/main/java/org/keycloak/representations/UserInfo.java b/core/src/main/java/org/keycloak/representations/UserInfo.java
index 200e8e8..7849718 100755
--- a/core/src/main/java/org/keycloak/representations/UserInfo.java
+++ b/core/src/main/java/org/keycloak/representations/UserInfo.java
@@ -16,12 +16,31 @@
  */
 package org.keycloak.representations;
 
+import java.util.HashMap;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonAnyGetter;
+import com.fasterxml.jackson.annotation.JsonAnySetter;
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import org.keycloak.json.StringOrArrayDeserializer;
+import org.keycloak.json.StringOrArraySerializer;
 
 /**
  * @author pedroigor
  */
 public class UserInfo {
+
+    // Should be in signed UserInfo response
+    @JsonProperty("iss")
+    protected String issuer;
+    @JsonProperty("aud")
+    @JsonSerialize(using = StringOrArraySerializer.class)
+    @JsonDeserialize(using = StringOrArrayDeserializer.class)
+    protected String[] audience;
+
     @JsonProperty("sub")
     protected String sub;
 
@@ -85,6 +104,34 @@ public class UserInfo {
     @JsonProperty("claims_locales")
     protected String claimsLocales;
 
+    protected Map<String, Object> otherClaims = new HashMap<>();
+
+    public String getIssuer() {
+        return issuer;
+    }
+
+    public void setIssuer(String issuer) {
+        this.issuer = issuer;
+    }
+
+    @JsonIgnore
+    public String[] getAudience() {
+        return audience;
+    }
+
+    public boolean hasAudience(String audience) {
+        for (String a : this.audience) {
+            if (a.equals(audience)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public void setAudience(String... audience) {
+        this.audience = audience;
+    }
+
     public String getSubject() {
         return this.sub;
     }
@@ -260,4 +307,19 @@ public class UserInfo {
     public void setClaimsLocales(String claimsLocales) {
         this.claimsLocales = claimsLocales;
     }
+
+    /**
+     * This is a map of any other claims and data that might be in the UserInfo.  Could be custom claims set up by the auth server
+     *
+     * @return
+     */
+    @JsonAnyGetter
+    public Map<String, Object> getOtherClaims() {
+        return otherClaims;
+    }
+
+    @JsonAnySetter
+    public void setOtherClaims(String name, Object value) {
+        otherClaims.put(name, value);
+    }
 }
diff --git a/distribution/demo-dist/pom.xml b/distribution/demo-dist/pom.xml
index 187d62d..71a5a1a 100755
--- a/distribution/demo-dist/pom.xml
+++ b/distribution/demo-dist/pom.xml
@@ -202,6 +202,31 @@
                     </execution>
                 </executions>
             </plugin>
+            
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>unpack</id>
+                        <phase>compile</phase>
+                        <goals>
+                            <goal>unpack</goal>
+                        </goals>
+                        <configuration>
+                            <artifactItems>
+                                <artifactItem>
+                                    <groupId>org.keycloak</groupId>
+                                    <artifactId>keycloak-wildfly-server-subsystem</artifactId>
+                                    <version>${project.version}</version>
+                                    <type>jar</type>
+                                    <includes>default-config/*.xml</includes>
+                                </artifactItem>
+                            </artifactItems>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
         </plugins>
     </build>
 
diff --git a/distribution/demo-dist/src/main/xslt/standalone.xsl b/distribution/demo-dist/src/main/xslt/standalone.xsl
index 4ef3f0e..855efc0 100755
--- a/distribution/demo-dist/src/main/xslt/standalone.xsl
+++ b/distribution/demo-dist/src/main/xslt/standalone.xsl
@@ -1,19 +1,19 @@
 <!--
-  ~ 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.
-  -->
+~ 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.
+-->
 
 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                 xmlns:xalan="http://xml.apache.org/xalan"
@@ -42,7 +42,7 @@
     <xsl:template match="//ds:datasources">
         <xsl:copy>
             <xsl:apply-templates select="node()[name(.)='datasource']"/>
-            <datasource jndi-name="java:jboss/datasources/KeycloakDS" pool-name="KeycloakDS" use-java-context="true">
+            <datasource jndi-name="java:jboss/datasources/KeycloakDS" jta="false" pool-name="KeycloakDS" use-java-context="true">
                 <connection-url>jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE</connection-url>
                 <driver>h2</driver>
                 <security>
@@ -57,9 +57,7 @@
     <xsl:template match="//j:profile">
         <xsl:copy>
             <xsl:apply-templates select="node()|@*"/>
-            <subsystem xmlns="urn:jboss:domain:keycloak-server:1.1">
-                <web-context>auth</web-context>
-            </subsystem>
+            <xsl:copy-of select="document('../../../target/dependency/default-config/keycloak-server-default-config.xml')"/>
             <subsystem xmlns="urn:jboss:domain:keycloak:1.1"/>
             <subsystem xmlns="urn:jboss:domain:keycloak-saml:1.1"/>
         </xsl:copy>
diff --git a/distribution/feature-packs/server-feature-pack/assembly.xml b/distribution/feature-packs/server-feature-pack/assembly.xml
index 41ef173..861c698 100644
--- a/distribution/feature-packs/server-feature-pack/assembly.xml
+++ b/distribution/feature-packs/server-feature-pack/assembly.xml
@@ -59,15 +59,4 @@
             </includes>
         </fileSet>
     </fileSets>
-
-    <files>
-        <file>
-            <source>src/main/resources/content/standalone/configuration/keycloak-server.json</source>
-            <outputDirectory>content/domain/servers/server-one/configuration</outputDirectory>
-        </file>
-        <file>
-            <source>src/main/resources/content/standalone/configuration/keycloak-server.json</source>
-            <outputDirectory>content/domain/servers/server-two/configuration</outputDirectory>
-        </file>
-    </files>
 </assembly>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml
index e769a7b..de03ed8 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml
@@ -55,6 +55,7 @@
         <module name="org.jboss.resteasy.resteasy-jaxrs"/>
         <module name="org.jboss.resteasy.resteasy-crypto"/>
         <module name="org.jboss.resteasy.resteasy-multipart-provider"/>
+        <module name="org.jboss.dmr"/>
         <module name="javax.servlet.api"/>
         <module name="com.fasterxml.jackson.core.jackson-core"/>
         <module name="com.fasterxml.jackson.core.jackson-annotations"/>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-server-subsystem/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-server-subsystem/main/module.xml
index 9f44e59..d82e1a9 100644
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-server-subsystem/main/module.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-server-subsystem/main/module.xml
@@ -28,6 +28,8 @@
     </resources>
 
     <dependencies>
+        <module name="com.fasterxml.jackson.core.jackson-core"/>
+        <module name="com.fasterxml.jackson.core.jackson-databind"/>
         <module name="javax.api"/>
         <module name="org.jboss.staxmapper"/>
         <module name="org.jboss.as.controller"/>
diff --git a/distribution/server-overlay/assembly.xml b/distribution/server-overlay/assembly.xml
index 325cadf..162bd00 100755
--- a/distribution/server-overlay/assembly.xml
+++ b/distribution/server-overlay/assembly.xml
@@ -85,10 +85,6 @@
 
     <files>
         <file>
-            <source>${project.build.directory}/unpacked/keycloak-${project.version}/standalone/configuration/keycloak-server.json</source>
-            <outputDirectory>standalone/configuration</outputDirectory>
-        </file>
-        <file>
             <source>${project.build.directory}/unpacked/keycloak-${project.version}/bin/add-user-keycloak.sh</source>
             <outputDirectory>bin</outputDirectory>
             <destName>add-user-keycloak.sh</destName>
@@ -98,6 +94,11 @@
             <outputDirectory>bin</outputDirectory>
             <destName>add-user-keycloak.bat</destName>
         </file>
+        <file>
+            <source>${project.build.directory}/cli/default-keycloak-subsys-config.cli</source>
+            <outputDirectory>bin</outputDirectory>
+            <destName>default-keycloak-subsys-config.cli</destName>
+        </file>
     </files>
 
 </assembly>
diff --git a/distribution/server-overlay/cli/keycloak-install.cli b/distribution/server-overlay/cli/keycloak-install.cli
index c29cd5f..dbb2c32 100644
--- a/distribution/server-overlay/cli/keycloak-install.cli
+++ b/distribution/server-overlay/cli/keycloak-install.cli
@@ -1,5 +1,5 @@
 embed-server --server-config=standalone.xml
-/subsystem=datasources/data-source=KeycloakDS/:add(connection-url="jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE",driver-name=h2,jndi-name=java:jboss/datasources/KeycloakDS,password=sa,user-name=sa,use-java-context=true)
+/subsystem=datasources/data-source=KeycloakDS/:add(connection-url="jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE",jta=false,driver-name=h2,jndi-name=java:jboss/datasources/KeycloakDS,password=sa,user-name=sa,use-java-context=true)
 /subsystem=infinispan/cache-container=keycloak:add(jndi-name="infinispan/Keycloak")
 /subsystem=infinispan/cache-container=keycloak/local-cache=realms:add()
 /subsystem=infinispan/cache-container=keycloak/local-cache=users:add()
@@ -11,4 +11,4 @@ embed-server --server-config=standalone.xml
 /subsystem=infinispan/cache-container=keycloak/local-cache=authorization:add()
 /subsystem=infinispan/cache-container=keycloak/local-cache=authorization/eviction=EVICTION:add(max-entries=100,strategy=LRU)
 /extension=org.keycloak.keycloak-server-subsystem/:add(module=org.keycloak.keycloak-server-subsystem)
-/subsystem=keycloak-server:add(web-context=auth)
\ No newline at end of file
+run-batch --file=default-keycloak-subsys-config.cli
\ No newline at end of file
diff --git a/distribution/server-overlay/cli/keycloak-install-ha.cli b/distribution/server-overlay/cli/keycloak-install-ha.cli
index a84a34a..bc7d863 100644
--- a/distribution/server-overlay/cli/keycloak-install-ha.cli
+++ b/distribution/server-overlay/cli/keycloak-install-ha.cli
@@ -1,5 +1,5 @@
 embed-server --server-config=standalone-ha.xml
-/subsystem=datasources/data-source=KeycloakDS/:add(connection-url="jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE",driver-name=h2,jndi-name=java:jboss/datasources/KeycloakDS,password=sa,user-name=sa,use-java-context=true)
+/subsystem=datasources/data-source=KeycloakDS/:add(connection-url="jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE",jta=false,driver-name=h2,jndi-name=java:jboss/datasources/KeycloakDS,password=sa,user-name=sa,use-java-context=true)
 /subsystem=infinispan/cache-container=keycloak:add(jndi-name="infinispan/Keycloak")
 /subsystem=infinispan/cache-container=keycloak/transport=TRANSPORT:add(lock-timeout=60000)
 /subsystem=infinispan/cache-container=keycloak/invalidation-cache=realms:add(mode="SYNC")
@@ -11,4 +11,4 @@ embed-server --server-config=standalone-ha.xml
 /subsystem=infinispan/cache-container=keycloak/distributed-cache=authorization:add(mode="SYNC",owners="1")
 /subsystem=infinispan/cache-container=keycloak/replicated-cache=work:add(mode="SYNC")
 /extension=org.keycloak.keycloak-server-subsystem/:add(module=org.keycloak.keycloak-server-subsystem)
-/subsystem=keycloak-server:add(web-context=auth)
\ No newline at end of file
+run-batch --file=default-keycloak-subsys-config.cli
diff --git a/distribution/server-overlay/pom.xml b/distribution/server-overlay/pom.xml
index 96400d1..900bc29 100755
--- a/distribution/server-overlay/pom.xml
+++ b/distribution/server-overlay/pom.xml
@@ -46,6 +46,25 @@
                 <artifactId>maven-dependency-plugin</artifactId>
                 <executions>
                     <execution>
+                        <id>unpack</id>
+                        <phase>compile</phase>
+                        <goals>
+                            <goal>unpack</goal>
+                        </goals>
+                        <configuration>
+                            <artifactItems>
+                                <artifactItem>
+                                    <groupId>org.keycloak</groupId>
+                                    <artifactId>keycloak-wildfly-server-subsystem</artifactId>
+                                    <version>${project.version}</version>
+                                    <type>jar</type>
+                                    <includes>cli/*.cli</includes>
+                                    <outputDirectory>${project.build.directory}</outputDirectory>
+                                </artifactItem>
+                            </artifactItems>
+                        </configuration>
+                    </execution>
+                    <execution>
                         <id>unpack-server-dist</id>
                         <phase>prepare-package</phase>
                         <goals>
diff --git a/examples/authz/photoz/photoz-restful-api/pom.xml b/examples/authz/photoz/photoz-restful-api/pom.xml
index ba7a880..7622af4 100755
--- a/examples/authz/photoz/photoz-restful-api/pom.xml
+++ b/examples/authz/photoz/photoz-restful-api/pom.xml
@@ -40,6 +40,12 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <groupId>javax.enterprise</groupId>
+            <artifactId>cdi-api</artifactId>
+            <version>1.0-SP4</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <groupId>org.keycloak</groupId>
             <artifactId>keycloak-authz-client</artifactId>
             <version>${project.version}</version>
diff --git a/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/admin/AdminAlbumService.java b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/admin/AdminAlbumService.java
index b349e02..428ba07 100644
--- a/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/admin/AdminAlbumService.java
+++ b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/admin/AdminAlbumService.java
@@ -19,9 +19,8 @@ package org.keycloak.example.photoz.admin;
 
 import org.keycloak.example.photoz.entity.Album;
 
-import javax.ejb.Stateless;
+import javax.inject.Inject;
 import javax.persistence.EntityManager;
-import javax.persistence.PersistenceContext;
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
@@ -36,12 +35,11 @@ import java.util.List;
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
 @Path("/admin/album")
-@Stateless
 public class AdminAlbumService {
 
     public static final String SCOPE_ADMIN_ALBUM_MANAGE = "urn:photoz.com:scopes:album:admin:manage";
 
-    @PersistenceContext
+    @Inject
     private EntityManager entityManager;
 
     @Context
diff --git a/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java
index a5d7f16..d0e9c2d 100644
--- a/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java
+++ b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java
@@ -7,10 +7,12 @@ import org.keycloak.authorization.client.representation.ScopeRepresentation;
 import org.keycloak.authorization.client.resource.ProtectionResource;
 import org.keycloak.example.photoz.ErrorResponse;
 import org.keycloak.example.photoz.entity.Album;
+import org.keycloak.example.photoz.util.Transaction;
 import org.keycloak.representations.adapters.config.AdapterConfig;
 import org.keycloak.util.JsonSerialization;
 
 import javax.ejb.Stateless;
+import javax.inject.Inject;
 import javax.persistence.EntityManager;
 import javax.persistence.PersistenceContext;
 import javax.persistence.Query;
@@ -31,14 +33,14 @@ import java.util.List;
 import java.util.Set;
 
 @Path("/album")
-@Stateless
+@Transaction
 public class AlbumService {
 
     public static final String SCOPE_ALBUM_VIEW = "urn:photoz.com:scopes:album:view";
     public static final String SCOPE_ALBUM_CREATE = "urn:photoz.com:scopes:album:create";
     public static final String SCOPE_ALBUM_DELETE = "urn:photoz.com:scopes:album:delete";
 
-    @PersistenceContext
+    @Inject
     private EntityManager entityManager;
 
     @Context
diff --git a/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/ProfileService.java b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/ProfileService.java
index be638b6..f7b55cb 100644
--- a/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/ProfileService.java
+++ b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/ProfileService.java
@@ -18,6 +18,7 @@
 package org.keycloak.example.photoz.album;
 
 import javax.ejb.Stateless;
+import javax.inject.Inject;
 import javax.persistence.EntityManager;
 import javax.persistence.PersistenceContext;
 import javax.servlet.http.HttpServletRequest;
@@ -34,12 +35,11 @@ import java.util.List;
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
 @Path("/profile")
-@Stateless
 public class ProfileService {
 
     private static final String PROFILE_VIEW = "urn:photoz.com:scopes:profile:view";
 
-    @PersistenceContext
+    @Inject
     private EntityManager entityManager;
 
     @GET
diff --git a/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/util/Resources.java b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/util/Resources.java
new file mode 100644
index 0000000..c917da2
--- /dev/null
+++ b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/util/Resources.java
@@ -0,0 +1,51 @@
+/*
+ * 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.example.photoz.util;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.context.RequestScoped;
+import javax.enterprise.inject.Produces;
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.Persistence;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+@ApplicationScoped
+public class Resources {
+
+    private EntityManagerFactory entityManagerFactory;
+
+    @PostConstruct
+    public void init() {
+        entityManagerFactory = Persistence.createEntityManagerFactory("primary");
+    }
+
+    @PreDestroy
+    public void dispose() {
+        entityManagerFactory.close();
+    }
+
+    @RequestScoped
+    @Produces
+    public EntityManager createEntityManager() {
+        return entityManagerFactory.createEntityManager();
+    }
+}
diff --git a/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/util/Transaction.java b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/util/Transaction.java
new file mode 100644
index 0000000..a3caa78
--- /dev/null
+++ b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/util/Transaction.java
@@ -0,0 +1,34 @@
+/*
+ * 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.example.photoz.util;
+
+import javax.interceptor.InterceptorBinding;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+@InterceptorBinding
+@Target({ TYPE })
+@Retention(RUNTIME)
+public @interface Transaction {
+}
diff --git a/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/util/TransactionInterceptor.java b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/util/TransactionInterceptor.java
new file mode 100644
index 0000000..36d35f3
--- /dev/null
+++ b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/util/TransactionInterceptor.java
@@ -0,0 +1,56 @@
+/*
+ * 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.example.photoz.util;
+
+import javax.enterprise.inject.Instance;
+import javax.inject.Inject;
+import javax.interceptor.AroundInvoke;
+import javax.interceptor.Interceptor;
+import javax.interceptor.InvocationContext;
+import javax.persistence.EntityManager;
+import javax.persistence.EntityTransaction;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+@Interceptor
+@Transaction
+public class TransactionInterceptor {
+
+    @Inject
+    private Instance<EntityManager> entityManager;
+
+    @AroundInvoke
+    public Object aroundInvoke(InvocationContext context) {
+        EntityManager entityManager = this.entityManager.get();
+        EntityTransaction transaction = entityManager.getTransaction();
+
+        try {
+            transaction.begin();
+            Object proceed = context.proceed();
+            transaction.commit();
+            return proceed;
+        } catch (Exception cause) {
+            if (transaction != null && transaction.isActive()) {
+                transaction.rollback();
+            }
+            throw new RuntimeException(cause);
+        } finally {
+            entityManager.close();
+        }
+    }
+}
diff --git a/examples/authz/photoz/photoz-restful-api/src/main/resources/META-INF/beans.xml b/examples/authz/photoz/photoz-restful-api/src/main/resources/META-INF/beans.xml
index 957dc8a..fbf2a32 100644
--- a/examples/authz/photoz/photoz-restful-api/src/main/resources/META-INF/beans.xml
+++ b/examples/authz/photoz/photoz-restful-api/src/main/resources/META-INF/beans.xml
@@ -3,5 +3,7 @@
    xsi:schemaLocation="
         http://java.sun.com/xml/ns/javaee 
         http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
-        
+    <interceptors>
+        <class>org.keycloak.example.photoz.util.TransactionInterceptor</class>
+    </interceptors>
 </beans>
diff --git a/examples/authz/photoz/photoz-restful-api/src/main/resources/META-INF/persistence.xml b/examples/authz/photoz/photoz-restful-api/src/main/resources/META-INF/persistence.xml
index 9323182..c15d34f 100644
--- a/examples/authz/photoz/photoz-restful-api/src/main/resources/META-INF/persistence.xml
+++ b/examples/authz/photoz/photoz-restful-api/src/main/resources/META-INF/persistence.xml
@@ -4,14 +4,18 @@
 	xsi:schemaLocation="
         http://java.sun.com/xml/ns/persistence
         http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
-	<persistence-unit name="primary">
-		<non-jta-data-source>java:jboss/datasources/PhotozDS</non-jta-data-source>
+	<persistence-unit name="primary" transaction-type="RESOURCE_LOCAL">
+		<provider>org.hibernate.ejb.HibernatePersistence</provider>
 
 		<class>org.keycloak.example.photoz.entity.Album</class>
 		<class>org.keycloak.example.photoz.entity.Photo</class>
 
 		<properties>
-			<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
+			<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" />
+			<property name="hibernate.connection.driver_class" value="org.h2.Driver" />
+			<property name="hibernate.connection.url" value="jdbc:h2:~/keycloak-photoz-example" />
+			<property name="hibernate.connection.user" value="sa" />
+			<property name="hibernate.flushMode" value="FLUSH_AUTO" />
 			<property name="hibernate.hbm2ddl.auto" value="update" />
 			<property name="hibernate.show_sql" value="false" />
 		</properties>
diff --git a/examples/authz/photoz/photoz-restful-api/src/main/webapp/META-INF/jboss-deployment-structure.xml b/examples/authz/photoz/photoz-restful-api/src/main/webapp/META-INF/jboss-deployment-structure.xml
index 4b23be6..4553358 100644
--- a/examples/authz/photoz/photoz-restful-api/src/main/webapp/META-INF/jboss-deployment-structure.xml
+++ b/examples/authz/photoz/photoz-restful-api/src/main/webapp/META-INF/jboss-deployment-structure.xml
@@ -20,6 +20,7 @@
     <deployment>
         <dependencies>
             <module name="org.keycloak.keycloak-authz-client" services="import"/>
+            <module name="com.h2database.h2" services="import"/>
         </dependencies>
     </deployment>
 </jboss-deployment-structure>
diff --git a/examples/broker/google-authentication/README.md b/examples/broker/google-authentication/README.md
index 0825aed..48ddca7 100644
--- a/examples/broker/google-authentication/README.md
+++ b/examples/broker/google-authentication/README.md
@@ -3,7 +3,7 @@
 What is it?
 -----------
 
-This example demonstrates how to use Social Ientity Providers with Keycloak to authenticate users. In this case,
+This example demonstrates how to use Social Identity Providers with Keycloak to authenticate users. In this case,
 users are authenticated with Google using Keycloak Identity Broker capabilities using the oAuth 2 protocol.
 
 From this example, you'll learn how to:
@@ -180,4 +180,4 @@ Debug the Application
 If you want to debug the source code or look at the Javadocs of any library in the project, run either of the following commands to pull them into your local repository. The IDE should then detect them.
 
         mvn dependency:sources
-        mvn dependency:resolve -Dclassifier=javadoc
\ No newline at end of file
+        mvn dependency:resolve -Dclassifier=javadoc
diff --git a/examples/broker/twitter-authentication/README.md b/examples/broker/twitter-authentication/README.md
index 5e93083..8f33d2b 100644
--- a/examples/broker/twitter-authentication/README.md
+++ b/examples/broker/twitter-authentication/README.md
@@ -3,7 +3,7 @@
 What is it?
 -----------
 
-This example demonstrates how to use Social Ientity Providers with Keycloak to authenticate users. In this case,
+This example demonstrates how to use Social Identity Providers with Keycloak to authenticate users. In this case,
 users are authenticated with Twitter using Keycloak Identity Broker capabilities using the oAuth 2 protocol.
 
 From this example, you'll learn how to:
@@ -179,4 +179,4 @@ Debug the Application
 If you want to debug the source code or look at the Javadocs of any library in the project, run either of the following commands to pull them into your local repository. The IDE should then detect them.
 
         mvn dependency:sources
-        mvn dependency:resolve -Dclassifier=javadoc
\ No newline at end of file
+        mvn dependency:resolve -Dclassifier=javadoc
diff --git a/misc/UpdatingServerConfig.md b/misc/UpdatingServerConfig.md
new file mode 100644
index 0000000..a3282af
--- /dev/null
+++ b/misc/UpdatingServerConfig.md
@@ -0,0 +1,65 @@
+# Changing the Default *keycloak-subsystem* Configuration
+
+If you need to make a change to the default keycloak-subsystem 
+configuration that is packaged with our distributions, you will need to edit this file:
+https://github.com/keycloak/keycloak/blob/master/wildfly/server-subsystem/src/main/config/default-server-subsys-config.properties
+
+This file contains a single multi-line property containing the subsystem 
+xml declaration.  Maven filtering is used to read this property and 
+inject it everywhere it needs to go.  Editing this file will also take 
+care of propagating it to the distributions like server-dist and demo-dist.
+
+Also, you need to create CLI commands for each change by editing this file:
+https://github.com/keycloak/keycloak/blob/master/wildfly/server-subsystem/src/main/resources/cli/default-keycloak-subsys-config.cli
+
+This CLI snippet is used in the scripts required by the overlay distribution.
+
+## Updating an SPI
+The changes you will likely make are when you need to add a new SPI, change an existing SPI, or add/change a provider within an SPI.
+
+All elements in an SPI declaration are optional, but a full SPI declaration
+  looks like this:
+````xml
+<spi name="dblock">
+     <default-provider>mongo</default-provider>
+     <provider name="jpa" enabled="true">
+         <properties>
+             <property name="lockWaitTimeout" value="800"/>
+         </properties>
+     </provider>
+     <provider name="mongo" enabled="true">
+         <properties>
+             <property name="lockRecheckTime" value="2"/>
+             <property name="lockWaitTimeout" value="600"/>
+         </properties>
+     </provider>
+</spi>
+````
+Here we have two providers defined for the SPI `dblock`.  The 
+`default-provider` is listed as `mongo`.  However it is up to the SPI to decide how it will 
+treat this setting.  Some SPIs allow more than one provider and some do not.  So
+`default-provider` can help the SPI to choose.
+
+Also notice that each provider defines its own set of configuration 
+properties.  The fact that both providers above have a property called 
+`lockWaitTimeout` is just a coincidence.
+
+## Values of type *List*
+The type of each property value is interpreted by the provider. However, 
+there is one exception.  Consider the `jpa` provider for the `eventStore` API:
+````xml
+<spi name="eventsStore">
+     <provider name="jpa" enabled="true">
+         <properties>
+             <property name="exclude-events" value="[&quot;EVENT1&quot;,&quot;EVENT2&quot;]"/>
+         </properties>
+     </provider>
+</spi>
+````
+We see that the value begins and ends with square brackets.  That means that
+the value will be passed to the provider as a list.  In this example, 
+the system will pass the
+provider a list with two element values `EVENT1` and `EVENT2`. To add 
+more values to the list, just separate each list element with a comma. Unfortunately,
+you do need to escape the quotes surrounding each list element with 
+`&quot;`.
diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java
index 4272fb8..3c74264 100755
--- a/model/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java
+++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java
@@ -17,6 +17,7 @@
 
 package org.keycloak.connections.jpa;
 
+import java.io.File;
 import java.sql.Connection;
 import java.sql.DatabaseMetaData;
 import java.sql.DriverManager;
@@ -42,6 +43,7 @@ import org.keycloak.models.dblock.DBLockProvider;
 import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.provider.ServerInfoAwareProviderFactory;
 import org.keycloak.models.dblock.DBLockManager;
+import org.keycloak.ServerStartupError;
 import org.keycloak.timer.TimerProvider;
 
 /**
@@ -51,6 +53,10 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
 
     private static final Logger logger = Logger.getLogger(DefaultJpaConnectionProviderFactory.class);
 
+    enum MigrationStrategy {
+        UPDATE, VALIDATE, MANUAL
+    }
+
     private volatile EntityManagerFactory emf;
 
     private Config.Scope config;
@@ -125,22 +131,9 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
                         properties.put(JpaUtils.HIBERNATE_DEFAULT_SCHEMA, schema);
                     }
 
-
-                    String databaseSchema;
-                    String databaseSchemaConf = config.get("databaseSchema");
-                    if (databaseSchemaConf == null) {
-                        throw new RuntimeException("Property 'databaseSchema' needs to be specified in the configuration");
-                    }
-                    
-                    if (databaseSchemaConf.equals("development-update")) {
-                        properties.put("hibernate.hbm2ddl.auto", "update");
-                        databaseSchema = null;
-                    } else if (databaseSchemaConf.equals("development-validate")) {
-                        properties.put("hibernate.hbm2ddl.auto", "validate");
-                        databaseSchema = null;
-                    } else {
-                        databaseSchema = databaseSchemaConf;
-                    }
+                    MigrationStrategy migrationStrategy = getMigrationStrategy();
+                    boolean initializeEmpty = config.getBoolean("initializeEmpty", true);
+                    File databaseUpdateFile = getDatabaseUpdateFile();
 
                     properties.put("hibernate.show_sql", config.getBoolean("showSql", false));
                     properties.put("hibernate.format_sql", config.getBoolean("formatSql", true));
@@ -153,39 +146,8 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
                         if (driverDialect != null) {
                             properties.put("hibernate.dialect", driverDialect);
                         }
-	                    
-	                    if (databaseSchema != null) {
-	                        logger.trace("Updating database");
-	
-	                        JpaUpdaterProvider updater = session.getProvider(JpaUpdaterProvider.class);
-	                        if (updater == null) {
-	                            throw new RuntimeException("Can't update database: JPA updater provider not found");
-	                        }
 
-                            // Check if having DBLock before trying to initialize hibernate
-                            DBLockProvider dbLock = new DBLockManager(session).getDBLock();
-                            if (dbLock.hasLock()) {
-                                updateOrValidateDB(databaseSchema, connection, updater, schema);
-                            } else {
-                                logger.trace("Don't have DBLock retrieved before upgrade. Needs to acquire lock first in separate transaction");
-
-                                KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), new KeycloakSessionTask() {
-
-                                    @Override
-                                    public void run(KeycloakSession lockSession) {
-                                        DBLockManager dbLockManager = new DBLockManager(lockSession);
-                                        DBLockProvider dbLock2 = dbLockManager.getDBLock();
-                                        dbLock2.waitForLock();
-                                        try {
-                                            updateOrValidateDB(databaseSchema, connection, updater, schema);
-                                        } finally {
-                                            dbLock2.releaseLock();
-                                        }
-                                    }
-
-                                });
-                            }
-	                    }
+                        migration(migrationStrategy, initializeEmpty, schema, databaseUpdateFile, connection, session);
 
                         int globalStatsInterval = config.getInt("globalStatsInterval", -1);
                         if (globalStatsInterval != -1) {
@@ -199,18 +161,6 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
                         if (globalStatsInterval != -1) {
                             startGlobalStats(session, globalStatsInterval);
                         }
-
-                    } catch (Exception e) {
-                        // Safe rollback
-                        if (connection != null) {
-                            try {
-                                connection.rollback();
-                            } catch (SQLException e2) {
-                                logger.warn("Can't rollback connection", e2);
-                            }
-                        }
-
-                        throw e;
                     } finally {
 	                    // Close after creating EntityManagerFactory to prevent in-mem databases from closing
 	                    if (connection != null) {
@@ -226,6 +176,11 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
         }
     }
 
+    private File getDatabaseUpdateFile() {
+        String databaseUpdateFile = config.get("migrationExport", "keycloak-database-update.sql");
+        return new File(databaseUpdateFile);
+    }
+
     protected void prepareOperationalInfo(Connection connection) {
   		try {
   			operationalInfo = new LinkedHashMap<>();
@@ -282,20 +237,82 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
         timer.scheduleTask(new HibernateStatsReporter(emf), globalStatsIntervalSecs * 1000, "ReportHibernateGlobalStats");
     }
 
+    public void migration(MigrationStrategy strategy, boolean initializeEmpty, String schema, File databaseUpdateFile, Connection connection, KeycloakSession session) {
+        JpaUpdaterProvider updater = session.getProvider(JpaUpdaterProvider.class);
+
+        JpaUpdaterProvider.Status status = updater.validate(connection, schema);
+        if (status == JpaUpdaterProvider.Status.VALID) {
+            logger.debug("Database is up-to-date");
+        } else if (status == JpaUpdaterProvider.Status.EMPTY) {
+            if (initializeEmpty) {
+                update(connection, schema, session, updater);
+            } else {
+                switch (strategy) {
+                    case UPDATE:
+                        update(connection, schema, session, updater);
+                        break;
+                    case MANUAL:
+                        export(connection, schema, databaseUpdateFile, session, updater);
+                        throw new ServerStartupError("Database not initialized, please initialize database with " + databaseUpdateFile.getAbsolutePath(), false);
+                    case VALIDATE:
+                        throw new ServerStartupError("Database not initialized, please enable database initialization", false);
+                }
+            }
+        } else {
+            switch (strategy) {
+                case UPDATE:
+                    update(connection, schema, session, updater);
+                    break;
+                case MANUAL:
+                    export(connection, schema, databaseUpdateFile, session, updater);
+                    throw new ServerStartupError("Database not up-to-date, please migrate database with " + databaseUpdateFile.getAbsolutePath(), false);
+                case VALIDATE:
+                    throw new ServerStartupError("Database not up-to-date, please enable database migration", false);
+            }
+        }
+    }
 
-    // Needs to be called with acquired DBLock
-    protected void updateOrValidateDB(String databaseSchema, Connection connection, JpaUpdaterProvider updater, String schema) {
-        if (databaseSchema.equals("update")) {
+    protected void update(Connection connection, String schema, KeycloakSession session, JpaUpdaterProvider updater) {
+        DBLockProvider dbLock = new DBLockManager(session).getDBLock();
+        if (dbLock.hasLock()) {
             updater.update(connection, schema);
-            logger.trace("Database update completed");
-        } else if (databaseSchema.equals("validate")) {
-            updater.validate(connection, schema);
-            logger.trace("Database validation completed");
         } else {
-            throw new RuntimeException("Invalid value for databaseSchema: " + databaseSchema);
+            KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), new KeycloakSessionTask() {
+                @Override
+                public void run(KeycloakSession lockSession) {
+                    DBLockManager dbLockManager = new DBLockManager(lockSession);
+                    DBLockProvider dbLock2 = dbLockManager.getDBLock();
+                    dbLock2.waitForLock();
+                    try {
+                        updater.update(connection, schema);
+                    } finally {
+                        dbLock2.releaseLock();
+                    }
+                }
+            });
         }
     }
 
+    protected void export(Connection connection, String schema, File databaseUpdateFile, KeycloakSession session, JpaUpdaterProvider updater) {
+        DBLockProvider dbLock = new DBLockManager(session).getDBLock();
+        if (dbLock.hasLock()) {
+            updater.export(connection, schema, databaseUpdateFile);
+        } else {
+            KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), new KeycloakSessionTask() {
+                @Override
+                public void run(KeycloakSession lockSession) {
+                    DBLockManager dbLockManager = new DBLockManager(lockSession);
+                    DBLockProvider dbLock2 = dbLockManager.getDBLock();
+                    dbLock2.waitForLock();
+                    try {
+                        updater.export(connection, schema, databaseUpdateFile);
+                    } finally {
+                        dbLock2.releaseLock();
+                    }
+                }
+            });
+        }
+    }
 
     @Override
     public Connection getConnection() {
@@ -323,4 +340,18 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
   		return operationalInfo;
   	}
 
+    private MigrationStrategy getMigrationStrategy() {
+        String migrationStrategy = config.get("migrationStrategy");
+        if (migrationStrategy == null) {
+            // Support 'databaseSchema' for backwards compatibility
+            migrationStrategy = config.get("databaseSchema");
+        }
+
+        if (migrationStrategy != null) {
+            return MigrationStrategy.valueOf(migrationStrategy.toUpperCase());
+        } else {
+            return MigrationStrategy.UPDATE;
+        }
+    }
+
 }
diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProvider.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProvider.java
index b087535..37228e3 100755
--- a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProvider.java
@@ -19,6 +19,7 @@ package org.keycloak.connections.jpa.updater;
 
 import org.keycloak.provider.Provider;
 
+import java.io.File;
 import java.sql.Connection;
 
 /**
@@ -26,10 +27,14 @@ import java.sql.Connection;
  */
 public interface JpaUpdaterProvider extends Provider {
 
-    public String FIRST_VERSION = "1.0.0.Final";
+    enum Status {
+        VALID, EMPTY, OUTDATED
+    }
 
-    public void update(Connection connection, String defaultSchema);
+    void update(Connection connection, String defaultSchema);
 
-    public void validate(Connection connection, String defaultSchema);
+    Status validate(Connection connection, String defaultSchema);
+
+    void export(Connection connection, String defaultSchema, File file);
 
 }
diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProvider.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProvider.java
index 2610174..b4a50a9 100755
--- a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProvider.java
@@ -30,6 +30,9 @@ import org.keycloak.connections.jpa.updater.liquibase.conn.LiquibaseConnectionPr
 import org.keycloak.connections.jpa.util.JpaUtils;
 import org.keycloak.models.KeycloakSession;
 
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
 import java.lang.reflect.Method;
 import java.sql.Connection;
 import java.util.List;
@@ -53,6 +56,15 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
 
     @Override
     public void update(Connection connection, String defaultSchema) {
+        update(connection, null, defaultSchema);
+    }
+
+    @Override
+    public void export(Connection connection, String defaultSchema, File file) {
+        update(connection, file, defaultSchema);
+    }
+
+    private void update(Connection connection, File file, String defaultSchema) {
         logger.debug("Starting database update");
 
         // Need ThreadLocal as liquibase doesn't seem to have API to inject custom objects into tasks
@@ -61,7 +73,7 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
         try {
             // Run update with keycloak master changelog first
             Liquibase liquibase = getLiquibaseForKeycloakUpdate(connection, defaultSchema);
-            updateChangeSet(liquibase, liquibase.getChangeLogFile());
+            updateChangeSet(liquibase, liquibase.getChangeLogFile(), file);
 
             // Run update for each custom JpaEntityProvider
             Set<JpaEntityProvider> jpaProviders = session.getAllProviders(JpaEntityProvider.class);
@@ -71,7 +83,7 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
                     String factoryId = jpaProvider.getFactoryId();
                     String changelogTableName = JpaUtils.getCustomChangelogTableName(factoryId);
                     liquibase = getLiquibaseForCustomProviderUpdate(connection, defaultSchema, customChangelog, jpaProvider.getClass().getClassLoader(), changelogTableName);
-                    updateChangeSet(liquibase, liquibase.getChangeLogFile());
+                    updateChangeSet(liquibase, liquibase.getChangeLogFile(), file);
                 }
             }
         } catch (Exception e) {
@@ -81,7 +93,8 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
         }
     }
 
-    protected void updateChangeSet(Liquibase liquibase, String changelog) throws LiquibaseException {
+
+    protected void updateChangeSet(Liquibase liquibase, String changelog, File exportFile) throws LiquibaseException, IOException {
         List<ChangeSet> changeSets = liquibase.listUnrunChangeSets((Contexts) null);
         if (!changeSets.isEmpty()) {
             List<RanChangeSet> ranChangeSets = liquibase.getDatabase().getRanChangeSetList();
@@ -95,7 +108,12 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
                 }
             }
 
-            liquibase.update((Contexts) null);
+            if (exportFile != null) {
+                liquibase.update((Contexts) null, new FileWriter(exportFile));
+            } else {
+                liquibase.update((Contexts) null);
+            }
+
             logger.debugv("Completed database update for changelog {0}", changelog);
         } else {
             logger.debugv("Database is up to date for changelog {0}", changelog);
@@ -107,13 +125,18 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
     }
 
     @Override
-    public void validate(Connection connection, String defaultSchema) {
+    public Status validate(Connection connection, String defaultSchema) {
         logger.debug("Validating if database is updated");
+        ThreadLocalSessionContext.setCurrentSession(session);
 
         try {
             // Validate with keycloak master changelog first
             Liquibase liquibase = getLiquibaseForKeycloakUpdate(connection, defaultSchema);
-            validateChangeSet(liquibase, liquibase.getChangeLogFile());
+
+            Status status = validateChangeSet(liquibase, liquibase.getChangeLogFile());
+            if (status != Status.VALID) {
+                return status;
+            }
 
             // Validate each custom JpaEntityProvider
             Set<JpaEntityProvider> jpaProviders = session.getAllProviders(JpaEntityProvider.class);
@@ -123,24 +146,30 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
                     String factoryId = jpaProvider.getFactoryId();
                     String changelogTableName = JpaUtils.getCustomChangelogTableName(factoryId);
                     liquibase = getLiquibaseForCustomProviderUpdate(connection, defaultSchema, customChangelog, jpaProvider.getClass().getClassLoader(), changelogTableName);
-                    validateChangeSet(liquibase, liquibase.getChangeLogFile());
+                    if (validateChangeSet(liquibase, liquibase.getChangeLogFile()) != Status.VALID) {
+                        return Status.OUTDATED;
+                    }
                 }
             }
-
         } catch (LiquibaseException e) {
             throw new RuntimeException("Failed to validate database", e);
         }
+
+        return Status.VALID;
     }
 
-    protected void validateChangeSet(Liquibase liquibase, String changelog) throws LiquibaseException {
+    protected Status validateChangeSet(Liquibase liquibase, String changelog) throws LiquibaseException {
         List<ChangeSet> changeSets = liquibase.listUnrunChangeSets((Contexts) null);
         if (!changeSets.isEmpty()) {
-            List<RanChangeSet> ranChangeSets = liquibase.getDatabase().getRanChangeSetList();
-            String errorMessage = String.format("Failed to validate database schema. Schema needs updating database from %s to %s. Please change databaseSchema to 'update' or use other database. Used changelog was %s",
-                    ranChangeSets.get(ranChangeSets.size() - 1).getId(), changeSets.get(changeSets.size() - 1).getId(), changelog);
-            throw new RuntimeException(errorMessage);
+            if (changeSets.size() == liquibase.getDatabaseChangeLog().getChangeSets().size()) {
+                return Status.EMPTY;
+            } else {
+                logger.debugf("Validation failed. Database is not up-to-date for changelog %s", changelog);
+                return Status.OUTDATED;
+            }
         } else {
             logger.debugf("Validation passed. Database is up-to-date for changelog %s", changelog);
+            return Status.VALID;
         }
     }
 
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
index 8e126e5..e1a546e 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
@@ -498,6 +498,18 @@ public class ClientAdapter implements ClientModel, JpaModel<ClientEntity> {
     @Override
     public void updateClient() {
         em.flush();
+        session.getKeycloakSessionFactory().publish(new RealmModel.ClientUpdatedEvent() {
+
+            @Override
+            public ClientModel getUpdatedClient() {
+                return ClientAdapter.this;
+            }
+
+            @Override
+            public KeycloakSession getKeycloakSession() {
+                return session;
+            }
+        });
     }
 
     @Override
diff --git a/model/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java b/model/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java
index b899e0d..ce09046 100755
--- a/model/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java
+++ b/model/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java
@@ -51,6 +51,10 @@ import com.mongodb.ServerAddress;
  */
 public class DefaultMongoConnectionFactoryProvider implements MongoConnectionProviderFactory, ServerInfoAwareProviderFactory {
 
+    enum MigrationStrategy {
+        UPDATE, VALIDATE
+    }
+
     // TODO Make it dynamic
     private String[] entities = new String[]{
             "org.keycloak.models.mongo.keycloak.entities.MongoRealmEntity",
@@ -165,46 +169,34 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
     }
 
     private void update(KeycloakSession session) {
-        String databaseSchema = config.get("databaseSchema");
+        MigrationStrategy strategy = getMigrationStrategy();
+
+        MongoUpdaterProvider mongoUpdater = session.getProvider(MongoUpdaterProvider.class);
+        if (mongoUpdater == null) {
+            throw new RuntimeException("Can't update database: Mongo updater provider not found");
+        }
 
-        if (databaseSchema == null) {
-            throw new RuntimeException("Property 'databaseSchema' needs to be specified in the configuration of mongo connections");
+        DBLockProvider dbLock = new DBLockManager(session).getDBLock();
+        if (dbLock.hasLock()) {
+            updateOrValidateDB(strategy, session, mongoUpdater);
         } else {
-            MongoUpdaterProvider mongoUpdater = session.getProvider(MongoUpdaterProvider.class);
-            if (mongoUpdater == null) {
-                throw new RuntimeException("Can't update database: Mongo updater provider not found");
-            }
+            logger.trace("Don't have DBLock retrieved before upgrade. Needs to acquire lock first in separate transaction");
 
-            DBLockProvider dbLock = new DBLockManager(session).getDBLock();
-            if (dbLock.hasLock()) {
-                updateOrValidateDB(databaseSchema, session, mongoUpdater);
-            } else {
-                logger.trace("Don't have DBLock retrieved before upgrade. Needs to acquire lock first in separate transaction");
-
-                KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), new KeycloakSessionTask() {
-
-                    @Override
-                    public void run(KeycloakSession lockSession) {
-                        DBLockManager dbLockManager = new DBLockManager(lockSession);
-                        DBLockProvider dbLock2 = dbLockManager.getDBLock();
-                        dbLock2.waitForLock();
-                        try {
-                            updateOrValidateDB(databaseSchema, session, mongoUpdater);
-                        } finally {
-                            dbLock2.releaseLock();
-                        }
-                    }
+            KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), new KeycloakSessionTask() {
 
-                });
-            }
+                @Override
+                public void run(KeycloakSession lockSession) {
+                    DBLockManager dbLockManager = new DBLockManager(lockSession);
+                    DBLockProvider dbLock2 = dbLockManager.getDBLock();
+                    dbLock2.waitForLock();
+                    try {
+                        updateOrValidateDB(strategy, session, mongoUpdater);
+                    } finally {
+                        dbLock2.releaseLock();
+                    }
+                }
 
-            if (databaseSchema.equals("update")) {
-                mongoUpdater.update(session, db);
-            } else if (databaseSchema.equals("validate")) {
-                mongoUpdater.validate(session, db);
-            } else {
-                throw new RuntimeException("Invalid value for databaseSchema: " + databaseSchema);
-            }
+            });
         }
     }
 
@@ -217,13 +209,14 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
         return entityClasses;
     }
 
-    protected void updateOrValidateDB(String databaseSchema, KeycloakSession session, MongoUpdaterProvider mongoUpdater) {
-        if (databaseSchema.equals("update")) {
-            mongoUpdater.update(session, db);
-        } else if (databaseSchema.equals("validate")) {
-            mongoUpdater.validate(session, db);
-        } else {
-            throw new RuntimeException("Invalid value for databaseSchema: " + databaseSchema);
+    protected void updateOrValidateDB(MigrationStrategy strategy, KeycloakSession session, MongoUpdaterProvider mongoUpdater) {
+        switch (strategy) {
+            case UPDATE:
+                mongoUpdater.update(session, db);
+                break;
+            case VALIDATE:
+                mongoUpdater.validate(session, db);
+                break;
         }
     }
 
@@ -345,4 +338,18 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
   		return operationalInfo;
   	}
 
+    private MigrationStrategy getMigrationStrategy() {
+        String migrationStrategy = config.get("migrationStrategy");
+        if (migrationStrategy == null) {
+            // Support 'databaseSchema' for backwards compatibility
+            migrationStrategy = config.get("databaseSchema");
+        }
+
+        if (migrationStrategy != null) {
+            return MigrationStrategy.valueOf(migrationStrategy.toUpperCase());
+        } else {
+            return MigrationStrategy.UPDATE;
+        }
+    }
+
 }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java
index f4983f9..5d6e053 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java
@@ -17,8 +17,6 @@
 
 package org.keycloak.models.mongo.keycloak.adapters;
 
-import com.mongodb.DBObject;
-import com.mongodb.QueryBuilder;
 import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.ClientTemplateModel;
@@ -65,6 +63,19 @@ public class ClientAdapter extends AbstractMongoAdapter<MongoClientEntity> imple
     @Override
     public void updateClient() {
         updateMongoEntity();
+
+        session.getKeycloakSessionFactory().publish(new RealmModel.ClientUpdatedEvent() {
+
+            @Override
+            public ClientModel getUpdatedClient() {
+                return ClientAdapter.this;
+            }
+
+            @Override
+            public KeycloakSession getKeycloakSession() {
+                return session;
+            }
+        });
     }
 
 

README.md 1(+1 -0)

diff --git a/README.md b/README.md
index c52a172..14addf7 100755
--- a/README.md
+++ b/README.md
@@ -65,6 +65,7 @@ Contributing
     * [Testsuite](misc/Testsuite.md) - Details about testsuite, but also how to quickly run Keycloak during development and a few test tools (OTP generation, LDAP server, Mail server)
     * [Database Testing](misc/DatabaseTesting.md) - How to do testing of Keycloak on different databases
     * [Updating Database](misc/UpdatingDatabaseSchema.md) - How to change the Keycloak database
+    * [Changing the Default keycloak-subsystem Configuration](misc/UpdatingServerConfig.md) - How to update the default keycloak-subsystem config
 * [Developer Mailing List](https://lists.jboss.org/mailman/listinfo/keycloak-dev) - Mailing list to discuss development of Keycloak
 
 
diff --git a/server-spi/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderFactory.java b/server-spi/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderFactory.java
index e8be6d6..953797b 100755
--- a/server-spi/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderFactory.java
+++ b/server-spi/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderFactory.java
@@ -50,7 +50,7 @@ public abstract class AbstractIdentityProviderFactory<T extends IdentityProvider
     }
 
     @Override
-    public Map<String, String> parseConfig(InputStream inputStream) {
+    public Map<String, String> parseConfig(KeycloakSession session, InputStream inputStream) {
         return new HashMap<String, String>();
     }
 }
diff --git a/server-spi/src/main/java/org/keycloak/broker/provider/IdentityProviderFactory.java b/server-spi/src/main/java/org/keycloak/broker/provider/IdentityProviderFactory.java
index fba8e64..93ef720 100755
--- a/server-spi/src/main/java/org/keycloak/broker/provider/IdentityProviderFactory.java
+++ b/server-spi/src/main/java/org/keycloak/broker/provider/IdentityProviderFactory.java
@@ -17,6 +17,7 @@
 package org.keycloak.broker.provider;
 
 import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.models.KeycloakSession;
 import org.keycloak.provider.ProviderFactory;
 
 import java.io.InputStream;
@@ -47,8 +48,9 @@ public interface IdentityProviderFactory<T extends IdentityProvider> extends Pro
      * <p>Creates an {@link IdentityProvider} based on the configuration from
      * <code>inputStream</code>.</p>
      *
+     * @param session
      * @param inputStream The input stream from where configuration will be loaded from..
      * @return
      */
-    Map<String, String> parseConfig(InputStream inputStream);
+    Map<String, String> parseConfig(KeycloakSession session, InputStream inputStream);
 }
\ No newline at end of file
diff --git a/server-spi/src/main/java/org/keycloak/events/Details.java b/server-spi/src/main/java/org/keycloak/events/Details.java
index e5da713..772eaa7 100755
--- a/server-spi/src/main/java/org/keycloak/events/Details.java
+++ b/server-spi/src/main/java/org/keycloak/events/Details.java
@@ -58,4 +58,7 @@ public interface Details {
 
     String CLIENT_AUTH_METHOD = "client_auth_method";
 
+    String SIGNATURE_REQUIRED = "signature_required";
+    String SIGNATURE_ALGORITHM = "signature_algorithm";
+
 }
diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrationUtils.java b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrationUtils.java
index 08da081..2afbbcc 100644
--- a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrationUtils.java
+++ b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrationUtils.java
@@ -49,8 +49,8 @@ public class MigrationUtils {
 
     public static void updateOTPRequiredAction(RequiredActionProviderModel otpAction) {
         if (otpAction == null) return;
-        if (!otpAction.getProviderId().equals(UserModel.RequiredAction.CONFIGURE_TOTP.name())) return;
-        if (!otpAction.getName().equals("Configure Totp")) return;
+        if (!UserModel.RequiredAction.CONFIGURE_TOTP.name().equals(otpAction.getProviderId())) return;
+        if (!"Configure Totp".equals(otpAction.getName())) return;
 
         otpAction.setName("Configure OTP");
     }
diff --git a/server-spi/src/main/java/org/keycloak/models/RealmModel.java b/server-spi/src/main/java/org/keycloak/models/RealmModel.java
index 04f1476..61dc9c1 100755
--- a/server-spi/src/main/java/org/keycloak/models/RealmModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/RealmModel.java
@@ -56,6 +56,12 @@ public interface RealmModel extends RoleContainerModel {
         ClientModel getCreatedClient();
     }
 
+    // Called also during client creation after client is fully initialized (including all attributes etc)
+    interface ClientUpdatedEvent extends ProviderEvent {
+        ClientModel getUpdatedClient();
+        KeycloakSession getKeycloakSession();
+    }
+
     interface ClientRemovedEvent extends ProviderEvent {
         ClientModel getClient();
         KeycloakSession getKeycloakSession();
diff --git a/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index f36f010..d1d5b05 100755
--- a/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -943,7 +943,6 @@ public class RepresentationToModel {
         } else {
             client.setNodeReRegistrationTimeout(-1);
         }
-        client.updateClient();
 
         if (resourceRep.getNotBefore() != null) {
             client.setNotBefore(resourceRep.getNotBefore());
@@ -1043,6 +1042,8 @@ public class RepresentationToModel {
         if (resourceRep.isUseTemplateMappers() != null) client.setUseTemplateMappers(resourceRep.isUseTemplateMappers());
         else client.setUseTemplateMappers(resourceRep.getClientTemplate() != null);
 
+        client.updateClient();
+
         return client;
     }
 
@@ -1066,7 +1067,6 @@ public class RepresentationToModel {
         if (rep.isSurrogateAuthRequired() != null) resource.setSurrogateAuthRequired(rep.isSurrogateAuthRequired());
         if (rep.getNodeReRegistrationTimeout() != null) resource.setNodeReRegistrationTimeout(rep.getNodeReRegistrationTimeout());
         if (rep.getClientAuthenticatorType() != null) resource.setClientAuthenticatorType(rep.getClientAuthenticatorType());
-        resource.updateClient();
 
         if (rep.getProtocol() != null) resource.setProtocol(rep.getProtocol());
         if (rep.getAttributes() != null) {
@@ -1122,7 +1122,7 @@ public class RepresentationToModel {
             }
         }
 
-
+        resource.updateClient();
     }
 
     // CLIENT TEMPLATES
diff --git a/server-spi/src/main/java/org/keycloak/ServerStartupError.java b/server-spi/src/main/java/org/keycloak/ServerStartupError.java
new file mode 100644
index 0000000..967e66f
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/ServerStartupError.java
@@ -0,0 +1,48 @@
+/*
+ * 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;
+
+/**
+ * Non-recoverable error thrown during server startup
+ *
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class ServerStartupError extends Error {
+
+    private final boolean fillStackTrace;
+
+    public ServerStartupError(String message) {
+        super(message);
+        fillStackTrace = true;
+    }
+
+    public ServerStartupError(String message, boolean fillStackTrace) {
+        super(message);
+        this.fillStackTrace = fillStackTrace;
+    }
+
+    @Override
+    public synchronized Throwable fillInStackTrace() {
+        if (fillStackTrace) {
+            return super.fillInStackTrace();
+        } else {
+            return this;
+        }
+    }
+
+}

services/pom.xml 5(+5 -0)

diff --git a/services/pom.xml b/services/pom.xml
index a8a009d..57d7984 100755
--- a/services/pom.xml
+++ b/services/pom.xml
@@ -73,6 +73,11 @@
         </dependency>
 
         <dependency>
+            <groupId>org.wildfly.core</groupId>
+            <artifactId>wildfly-controller</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <groupId>org.jboss.logging</groupId>
             <artifactId>jboss-logging</artifactId>
         </dependency>
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientAuthenticator.java
index 5139e3a..86dd4e8 100644
--- a/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientAuthenticator.java
@@ -30,6 +30,7 @@ import java.util.Set;
 
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
 
 import org.keycloak.OAuth2Constants;
 import org.keycloak.authentication.AuthenticationFlowError;
@@ -39,9 +40,11 @@ import org.keycloak.jose.jws.JWSInput;
 import org.keycloak.jose.jws.crypto.RSAProvider;
 import org.keycloak.models.AuthenticationExecutionModel;
 import org.keycloak.models.ClientModel;
+import org.keycloak.models.ModelException;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
 import org.keycloak.provider.ProviderConfigProperty;
 import org.keycloak.representations.JsonWebToken;
 import org.keycloak.representations.idm.CertificateRepresentation;
@@ -139,10 +142,11 @@ public class JWTClientAuthenticator extends AbstractClientAuthenticator {
                 throw new RuntimeException("Signature on JWT token failed validation");
             }
 
-            // Validate other things
-            String expectedAudience = Urls.realmIssuer(context.getUriInfo().getBaseUri(), realm.getName());
-            if (!token.hasAudience(expectedAudience)) {
-                throw new RuntimeException("Token audience doesn't match domain. Realm audience is '" + expectedAudience + "' but audience from token is '" + Arrays.asList(token.getAudience()).toString() + "'");
+            // Allow both "issuer" or "token-endpoint" as audience
+            String issuerUrl = Urls.realmIssuer(context.getUriInfo().getBaseUri(), realm.getName());
+            String tokenUrl = OIDCLoginProtocolService.tokenUrl(context.getUriInfo().getBaseUriBuilder()).build(realm.getName()).toString();
+            if (!token.hasAudience(issuerUrl) && !token.hasAudience(tokenUrl)) {
+                throw new RuntimeException("Token audience doesn't match domain. Realm issuer is '" + issuerUrl + "' but audience from token is '" + Arrays.asList(token.getAudience()).toString() + "'");
             }
 
             if (!token.isActive()) {
@@ -163,30 +167,13 @@ public class JWTClientAuthenticator extends AbstractClientAuthenticator {
     }
 
     protected PublicKey getSignatureValidationKey(ClientModel client, ClientAuthenticationFlowContext context) {
-        CertificateRepresentation certInfo = CertificateInfoHelper.getCertificateFromClient(client, ATTR_PREFIX);
-
-        String encodedCertificate = certInfo.getCertificate();
-        String encodedPublicKey = certInfo.getPublicKey();
-
-        if (encodedCertificate == null && encodedPublicKey == null) {
-            Response challengeResponse = ClientAuthUtil.errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "unauthorized_client", "Client '" + client.getClientId() + "' doesn't have certificate or publicKey configured");
-            context.failure(AuthenticationFlowError.CLIENT_CREDENTIALS_SETUP_REQUIRED, challengeResponse);
-            return null;
-        }
-
-        if (encodedCertificate != null && encodedPublicKey != null) {
-            Response challengeResponse = ClientAuthUtil.errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "unauthorized_client", "Client '" + client.getClientId() + "' has both publicKey and certificate configured");
+        try {
+            return CertificateInfoHelper.getSignatureValidationKey(client, ATTR_PREFIX);
+        } catch (ModelException me) {
+            Response challengeResponse = ClientAuthUtil.errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "unauthorized_client", me.getMessage());
             context.failure(AuthenticationFlowError.CLIENT_CREDENTIALS_SETUP_REQUIRED, challengeResponse);
             return null;
         }
-
-        // TODO: Caching of publicKeys / certificates, so it doesn't need to be always computed from pem. For performance reasons...
-        if (encodedCertificate != null) {
-            X509Certificate clientCert = KeycloakModelUtils.getCertificate(encodedCertificate);
-            return clientCert.getPublicKey();
-        } else {
-            return KeycloakModelUtils.getPublicKey(encodedPublicKey);
-        }
     }
 
     @Override
diff --git a/services/src/main/java/org/keycloak/broker/oidc/KeycloakOIDCIdentityProviderFactory.java b/services/src/main/java/org/keycloak/broker/oidc/KeycloakOIDCIdentityProviderFactory.java
index d3e8eba..56f24a2 100755
--- a/services/src/main/java/org/keycloak/broker/oidc/KeycloakOIDCIdentityProviderFactory.java
+++ b/services/src/main/java/org/keycloak/broker/oidc/KeycloakOIDCIdentityProviderFactory.java
@@ -18,6 +18,7 @@ package org.keycloak.broker.oidc;
 
 import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
 import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.models.KeycloakSession;
 
 import java.io.InputStream;
 import java.util.Map;
@@ -45,8 +46,8 @@ public class KeycloakOIDCIdentityProviderFactory extends AbstractIdentityProvide
     }
 
     @Override
-    public Map<String, String> parseConfig(InputStream inputStream) {
-        return OIDCIdentityProviderFactory.parseOIDCConfig(inputStream);
+    public Map<String, String> parseConfig(KeycloakSession session, InputStream inputStream) {
+        return OIDCIdentityProviderFactory.parseOIDCConfig(session, inputStream);
 
     }
 
diff --git a/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderFactory.java b/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderFactory.java
index 82c2cdd..a0e5017 100755
--- a/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderFactory.java
+++ b/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderFactory.java
@@ -19,6 +19,7 @@ package org.keycloak.broker.oidc;
 import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
 import org.keycloak.jose.jwk.JWK;
 import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.jose.jwk.JSONWebKeySet;
 import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation;
@@ -56,11 +57,11 @@ public class OIDCIdentityProviderFactory extends AbstractIdentityProviderFactory
     }
 
     @Override
-    public Map<String, String> parseConfig(InputStream inputStream) {
-        return parseOIDCConfig(inputStream);
+    public Map<String, String> parseConfig(KeycloakSession session, InputStream inputStream) {
+        return parseOIDCConfig(session, inputStream);
     }
 
-    protected static Map<String, String> parseOIDCConfig(InputStream inputStream) {
+    protected static Map<String, String> parseOIDCConfig(KeycloakSession session, InputStream inputStream) {
         OIDCConfigurationRepresentation rep;
         try {
             rep = JsonSerialization.readValue(inputStream, OIDCConfigurationRepresentation.class);
@@ -74,14 +75,14 @@ public class OIDCIdentityProviderFactory extends AbstractIdentityProviderFactory
         config.setTokenUrl(rep.getTokenEndpoint());
         config.setUserInfoUrl(rep.getUserinfoEndpoint());
         if (rep.getJwksUri() != null) {
-            sendJwksRequest(rep, config);
+            sendJwksRequest(session, rep, config);
         }
         return config.getConfig();
     }
 
-    protected static void sendJwksRequest(OIDCConfigurationRepresentation rep, OIDCIdentityProviderConfig config) {
+    protected static void sendJwksRequest(KeycloakSession session, OIDCConfigurationRepresentation rep, OIDCIdentityProviderConfig config) {
         try {
-            JSONWebKeySet keySet = JWKSUtils.sendJwksRequest(rep.getJwksUri());
+            JSONWebKeySet keySet = JWKSUtils.sendJwksRequest(session, rep.getJwksUri());
             PublicKey key = JWKSUtils.getKeyForUse(keySet, JWK.Use.SIG);
             if (key == null) {
                 logger.supportedJwkNotFound(JWK.Use.SIG.asString());
diff --git a/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderFactory.java b/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderFactory.java
index dac6750..2617995 100755
--- a/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderFactory.java
+++ b/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderFactory.java
@@ -24,6 +24,7 @@ import org.keycloak.dom.saml.v2.metadata.IDPSSODescriptorType;
 import org.keycloak.dom.saml.v2.metadata.KeyDescriptorType;
 import org.keycloak.dom.saml.v2.metadata.KeyTypes;
 import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.models.KeycloakSession;
 import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
 import org.keycloak.saml.common.exceptions.ParsingException;
 import org.keycloak.saml.common.util.DocumentUtil;
@@ -54,7 +55,7 @@ public class SAMLIdentityProviderFactory extends AbstractIdentityProviderFactory
     }
 
     @Override
-    public Map<String, String> parseConfig(InputStream inputStream) {
+    public Map<String, String> parseConfig(KeycloakSession session, InputStream inputStream) {
         try {
             Object parsedObject = new SAMLParser().parse(inputStream);
             EntityDescriptorType entityType;
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 931b04c..1b4db29 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
@@ -17,11 +17,6 @@
 
 package org.keycloak.protocol.oidc.endpoints;
 
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
 import javax.ws.rs.GET;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
@@ -42,6 +37,8 @@ import org.keycloak.models.IdentityProviderModel;
 import org.keycloak.models.RealmModel;
 import org.keycloak.protocol.AuthorizationEndpointBase;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.protocol.oidc.endpoints.request.AuthorizationEndpointRequest;
+import org.keycloak.protocol.oidc.endpoints.request.AuthorizationEndpointRequestParserProcessor;
 import org.keycloak.protocol.oidc.utils.OIDCRedirectUriBuilder;
 import org.keycloak.protocol.oidc.utils.OIDCResponseMode;
 import org.keycloak.protocol.oidc.utils.OIDCResponseType;
@@ -67,43 +64,10 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
     /**
      * Prefix used to store additional HTTP GET params from original client request into {@link ClientSessionModel} note to be available later in Authenticators, RequiredActions etc. Prefix is used to
      * prevent collisions with internally used notes.
-     * 
+     *
      * @see ClientSessionModel#getNote(String)
-     * @see #KNOWN_REQ_PARAMS
-     * @see #additionalReqParams
      */
     public static final String CLIENT_SESSION_NOTE_ADDITIONAL_REQ_PARAMS_PREFIX = "client_request_param_";
-    /**
-     * Max number of additional req params copied into client session note to prevent DoS attacks
-     * 
-     * @see #additionalReqParams
-     */
-    public static final int ADDITIONAL_REQ_PARAMS_MAX_MUMBER = 5;
-    /**
-     * Max size of additional req param value copied into client session note to prevent DoS attacks - params with longer value are ignored
-     * 
-     * @see #additionalReqParams
-     */
-    public static final int ADDITIONAL_REQ_PARAMS_MAX_SIZE = 200;
-
-    /** Set of known protocol GET params not to be stored into {@link #additionalReqParams} */
-    private static final Set<String> KNOWN_REQ_PARAMS = new HashSet<>();
-    static {
-        KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.CLIENT_ID_PARAM);
-        KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.RESPONSE_TYPE_PARAM);
-        KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.RESPONSE_MODE_PARAM);
-        KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.REDIRECT_URI_PARAM);
-        KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.STATE_PARAM);
-        KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.SCOPE_PARAM);
-        KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.LOGIN_HINT_PARAM);
-        KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.PROMPT_PARAM);
-        KNOWN_REQ_PARAMS.add(AdapterConstants.KC_IDP_HINT);
-        KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.NONCE_PARAM);
-        KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.MAX_AGE_PARAM);
-        KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.UI_LOCALES_PARAM);
-        KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.REQUEST_PARAM);
-        KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.REQUEST_URI_PARAM);
-    }
 
     private enum Action {
         REGISTER, CODE, FORGOT_CREDENTIALS
@@ -116,19 +80,8 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
     private OIDCResponseType parsedResponseType;
     private OIDCResponseMode parsedResponseMode;
 
-    private String clientId;
+    private AuthorizationEndpointRequest request;
     private String redirectUri;
-    private String redirectUriParam;
-    private String responseType;
-    private String responseMode;
-    private String state;
-    private String scope;
-    private String loginHint;
-    private String prompt;
-    private String nonce;
-    private String maxAge;
-    private String idpHint;
-    protected Map<String, String> additionalReqParams = new HashMap<>();
 
     public AuthorizationEndpoint(RealmModel realm, EventBuilder event) {
         super(realm, event);
@@ -139,34 +92,25 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
     public Response build() {
         MultivaluedMap<String, String> params = uriInfo.getQueryParameters();
 
-        clientId = params.getFirst(OIDCLoginProtocol.CLIENT_ID_PARAM);
-        responseType = params.getFirst(OIDCLoginProtocol.RESPONSE_TYPE_PARAM);
-        responseMode = params.getFirst(OIDCLoginProtocol.RESPONSE_MODE_PARAM);
-        redirectUriParam = params.getFirst(OIDCLoginProtocol.REDIRECT_URI_PARAM);
-        state = params.getFirst(OIDCLoginProtocol.STATE_PARAM);
-        scope = params.getFirst(OIDCLoginProtocol.SCOPE_PARAM);
-        loginHint = params.getFirst(OIDCLoginProtocol.LOGIN_HINT_PARAM);
-        prompt = params.getFirst(OIDCLoginProtocol.PROMPT_PARAM);
-        idpHint = params.getFirst(AdapterConstants.KC_IDP_HINT);
-        nonce = params.getFirst(OIDCLoginProtocol.NONCE_PARAM);
-        maxAge = params.getFirst(OIDCLoginProtocol.MAX_AGE_PARAM);
-
-        extractAdditionalReqParams(params);
+        String clientId = params.getFirst(OIDCLoginProtocol.CLIENT_ID_PARAM);
 
         checkSsl();
         checkRealm();
-        checkClient();
+        checkClient(clientId);
+
+        request = AuthorizationEndpointRequestParserProcessor.parseRequest(event, session, client, params);
+
         checkRedirectUri();
         Response errorResponse = checkResponseType();
         if (errorResponse != null) {
             return errorResponse;
         }
 
-        if (!TokenUtil.isOIDCRequest(scope)) {
+        if (!TokenUtil.isOIDCRequest(request.getScope())) {
             logger.oidcScopeMissing();
         }
 
-        errorResponse = checkOIDCParams(params);
+        errorResponse = checkOIDCParams();
         if (errorResponse != null) {
             return errorResponse;
         }
@@ -186,27 +130,6 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
         throw new RuntimeException("Unknown action " + action);
     }
 
-    protected void extractAdditionalReqParams(MultivaluedMap<String, String> params) {
-        for (String paramName : params.keySet()) {
-            if (!KNOWN_REQ_PARAMS.contains(paramName)) {
-                String value = params.getFirst(paramName);
-                if (value != null && value.trim().isEmpty()) {
-                    value = null;
-                }
-                if (value != null && value.length() <= ADDITIONAL_REQ_PARAMS_MAX_SIZE) {
-                    if (additionalReqParams.size() >= ADDITIONAL_REQ_PARAMS_MAX_MUMBER) {
-                        logger.debug("Maximal number of additional OIDC params (" + ADDITIONAL_REQ_PARAMS_MAX_MUMBER + ") exceeded, ignoring rest of them!");
-                        break;
-                    }
-                    additionalReqParams.put(paramName, value);
-                } else {
-                    logger.debug("OIDC Additional param " + paramName + " ignored because value is empty or longer than " + ADDITIONAL_REQ_PARAMS_MAX_SIZE);
-                }
-            }
-
-        }
-    }
-
     public AuthorizationEndpoint register() {
         event.event(EventType.REGISTER);
         action = Action.REGISTER;
@@ -243,7 +166,7 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
         }
     }
 
-    private void checkClient() {
+    private void checkClient(String clientId) {
         if (clientId == null) {
             event.error(Errors.INVALID_REQUEST);
             throw new ErrorPageException(session, Messages.MISSING_PARAMETER, OIDCLoginProtocol.CLIENT_ID_PARAM);
@@ -271,6 +194,8 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
     }
 
     private Response checkResponseType() {
+        String responseType = request.getResponseType();
+
         if (responseType == null) {
             logger.missingParameter(OAuth2Constants.RESPONSE_TYPE);
             event.error(Errors.INVALID_REQUEST);
@@ -292,7 +217,7 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
 
         OIDCResponseMode parsedResponseMode = null;
         try {
-            parsedResponseMode = OIDCResponseMode.parse(responseMode, parsedResponseType);
+            parsedResponseMode = OIDCResponseMode.parse(request.getResponseMode(), parsedResponseType);
         } catch (IllegalArgumentException iae) {
             logger.invalidParameter(OIDCLoginProtocol.RESPONSE_MODE_PARAM);
             event.error(Errors.INVALID_REQUEST);
@@ -325,20 +250,8 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
         return null;
     }
 
-    private Response checkOIDCParams(MultivaluedMap<String, String> params) {
-        if (params.getFirst(OIDCLoginProtocol.REQUEST_PARAM) != null) {
-            logger.unsupportedParameter(OIDCLoginProtocol.REQUEST_PARAM);
-            event.error(Errors.INVALID_REQUEST);
-            return redirectErrorToClient(parsedResponseMode, OAuthErrorException.REQUEST_NOT_SUPPORTED, null);
-        }
-
-        if (params.getFirst(OIDCLoginProtocol.REQUEST_URI_PARAM) != null) {
-            logger.unsupportedParameter(OIDCLoginProtocol.REQUEST_URI_PARAM);
-            event.error(Errors.INVALID_REQUEST);
-            return redirectErrorToClient(parsedResponseMode, OAuthErrorException.REQUEST_URI_NOT_SUPPORTED, null);
-        }
-
-        if (parsedResponseType.isImplicitOrHybridFlow() && nonce == null) {
+    private Response checkOIDCParams() {
+        if (parsedResponseType.isImplicitOrHybridFlow() && request.getNonce() == null) {
             logger.missingParameter(OIDCLoginProtocol.NONCE_PARAM);
             event.error(Errors.INVALID_REQUEST);
             return redirectErrorToClient(parsedResponseMode, OAuthErrorException.INVALID_REQUEST, "Missing parameter: nonce");
@@ -355,14 +268,16 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
             errorResponseBuilder.addParam(OAuth2Constants.ERROR_DESCRIPTION, errorDescription);
         }
 
-        if (state != null) {
-            errorResponseBuilder.addParam(OAuth2Constants.STATE, state);
+        if (request.getState() != null) {
+            errorResponseBuilder.addParam(OAuth2Constants.STATE, request.getState());
         }
 
         return errorResponseBuilder.build();
     }
 
     private void checkRedirectUri() {
+        String redirectUriParam = request.getRedirectUriParam();
+
         event.detail(Details.REDIRECT_URI, redirectUriParam);
 
         redirectUri = RedirectUtils.verifyRedirectUri(uriInfo, redirectUriParam, realm, client);
@@ -377,27 +292,28 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
         clientSession.setAuthMethod(OIDCLoginProtocol.LOGIN_PROTOCOL);
         clientSession.setRedirectUri(redirectUri);
         clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
-        clientSession.setNote(OIDCLoginProtocol.RESPONSE_TYPE_PARAM, responseType);
-        clientSession.setNote(OIDCLoginProtocol.REDIRECT_URI_PARAM, redirectUriParam);
+        clientSession.setNote(OIDCLoginProtocol.RESPONSE_TYPE_PARAM, request.getResponseType());
+        clientSession.setNote(OIDCLoginProtocol.REDIRECT_URI_PARAM, request.getRedirectUriParam());
         clientSession.setNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()));
 
-        if (state != null) clientSession.setNote(OIDCLoginProtocol.STATE_PARAM, state);
-        if (nonce != null) clientSession.setNote(OIDCLoginProtocol.NONCE_PARAM, nonce);
-        if (maxAge != null) clientSession.setNote(OIDCLoginProtocol.MAX_AGE_PARAM, maxAge);
-        if (scope != null) clientSession.setNote(OIDCLoginProtocol.SCOPE_PARAM, scope);
-        if (loginHint != null) clientSession.setNote(OIDCLoginProtocol.LOGIN_HINT_PARAM, loginHint);
-        if (prompt != null) clientSession.setNote(OIDCLoginProtocol.PROMPT_PARAM, prompt);
-        if (idpHint != null) clientSession.setNote(AdapterConstants.KC_IDP_HINT, idpHint);
-        if (responseMode != null) clientSession.setNote(OIDCLoginProtocol.RESPONSE_MODE_PARAM, responseMode);
-
-        if (additionalReqParams != null) {
-            for (String paramName : additionalReqParams.keySet()) {
-                clientSession.setNote(CLIENT_SESSION_NOTE_ADDITIONAL_REQ_PARAMS_PREFIX + paramName, additionalReqParams.get(paramName));
+        if (request.getState() != null) clientSession.setNote(OIDCLoginProtocol.STATE_PARAM, request.getState());
+        if (request.getNonce() != null) clientSession.setNote(OIDCLoginProtocol.NONCE_PARAM, request.getNonce());
+        if (request.getMaxAge() != null) clientSession.setNote(OIDCLoginProtocol.MAX_AGE_PARAM, String.valueOf(request.getMaxAge()));
+        if (request.getScope() != null) clientSession.setNote(OIDCLoginProtocol.SCOPE_PARAM, request.getScope());
+        if (request.getLoginHint() != null) clientSession.setNote(OIDCLoginProtocol.LOGIN_HINT_PARAM, request.getLoginHint());
+        if (request.getPrompt() != null) clientSession.setNote(OIDCLoginProtocol.PROMPT_PARAM, request.getPrompt());
+        if (request.getIdpHint() != null) clientSession.setNote(AdapterConstants.KC_IDP_HINT, request.getIdpHint());
+        if (request.getResponseMode() != null) clientSession.setNote(OIDCLoginProtocol.RESPONSE_MODE_PARAM, request.getResponseMode());
+
+        if (request.getAdditionalReqParams() != null) {
+            for (String paramName : request.getAdditionalReqParams().keySet()) {
+                clientSession.setNote(CLIENT_SESSION_NOTE_ADDITIONAL_REQ_PARAMS_PREFIX + paramName, request.getAdditionalReqParams().get(paramName));
             }
         }
     }
 
     private Response buildAuthorizationCodeAuthorizationResponse() {
+        String idpHint = request.getIdpHint();
 
         if (idpHint != null && !"".equals(idpHint)) {
             IdentityProviderModel identityProviderModel = realm.getIdentityProviderByAlias(idpHint);
@@ -413,7 +329,7 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
         this.event.event(EventType.LOGIN);
         clientSession.setNote(Details.AUTH_TYPE, CODE_AUTH_TYPE);
 
-        return handleBrowserAuthenticationRequest(clientSession, new OIDCLoginProtocol(session, realm, uriInfo, headers, event), TokenUtil.hasPrompt(prompt, OIDCLoginProtocol.PROMPT_VALUE_NONE), false);
+        return handleBrowserAuthenticationRequest(clientSession, new OIDCLoginProtocol(session, realm, uriInfo, headers, event), TokenUtil.hasPrompt(request.getPrompt(), OIDCLoginProtocol.PROMPT_VALUE_NONE), false);
     }
 
     private Response buildRegister() {
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthorizationEndpointRequest.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthorizationEndpointRequest.java
new file mode 100644
index 0000000..998a58c
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthorizationEndpointRequest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.protocol.oidc.endpoints.request;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class AuthorizationEndpointRequest {
+
+    String clientId;
+    String redirectUriParam;
+    String responseType;
+    String responseMode;
+    String state;
+    String scope;
+    String loginHint;
+    String prompt;
+    String nonce;
+    Integer maxAge;
+    String idpHint;
+    Map<String, String> additionalReqParams = new HashMap<>();
+
+    public String getClientId() {
+        return clientId;
+    }
+
+    public String getRedirectUriParam() {
+        return redirectUriParam;
+    }
+
+    public String getResponseType() {
+        return responseType;
+    }
+
+    public String getResponseMode() {
+        return responseMode;
+    }
+
+    public String getState() {
+        return state;
+    }
+
+    public String getScope() {
+        return scope;
+    }
+
+    public String getLoginHint() {
+        return loginHint;
+    }
+
+    public String getPrompt() {
+        return prompt;
+    }
+
+    public String getNonce() {
+        return nonce;
+    }
+
+    public Integer getMaxAge() {
+        return maxAge;
+    }
+
+    public String getIdpHint() {
+        return idpHint;
+    }
+
+    public Map<String, String> getAdditionalReqParams() {
+        return additionalReqParams;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthorizationEndpointRequestParserProcessor.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthorizationEndpointRequestParserProcessor.java
new file mode 100644
index 0000000..8db219c
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthorizationEndpointRequestParserProcessor.java
@@ -0,0 +1,73 @@
+/*
+ * 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.protocol.oidc.endpoints.request;
+
+import java.io.InputStream;
+import java.util.Map;
+
+import javax.ws.rs.core.MultivaluedMap;
+
+import org.keycloak.common.util.StreamUtil;
+import org.keycloak.connections.httpclient.HttpClientProvider;
+import org.keycloak.events.Errors;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.services.ErrorPageException;
+import org.keycloak.services.ServicesLogger;
+import org.keycloak.services.messages.Messages;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class AuthorizationEndpointRequestParserProcessor {
+
+    private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER;
+
+    public static AuthorizationEndpointRequest parseRequest(EventBuilder event, KeycloakSession session, ClientModel client, MultivaluedMap<String, String> requestParams) {
+        try {
+            AuthorizationEndpointRequest request = new AuthorizationEndpointRequest();
+
+            new AuthzEndpointQueryStringParser(requestParams).parseRequest(request);
+
+            String requestParam = requestParams.getFirst(OIDCLoginProtocol.REQUEST_PARAM);
+            String requestUriParam = requestParams.getFirst(OIDCLoginProtocol.REQUEST_URI_PARAM);
+
+            if (requestParam != null && requestUriParam != null) {
+                throw new RuntimeException("Illegal to use both 'request' and 'request_uri' parameters together");
+            }
+
+            if (requestParam != null) {
+                new AuthzEndpointRequestObjectParser(requestParam, client).parseRequest(request);
+            } else if (requestUriParam != null) {
+                InputStream is = session.getProvider(HttpClientProvider.class).get(requestUriParam);
+                String retrievedRequest = StreamUtil.readString(is);
+
+                new AuthzEndpointRequestObjectParser(retrievedRequest, client).parseRequest(request);
+            }
+
+            return request;
+
+        } catch (Exception e) {
+            logger.invalidRequest(e);
+            event.error(Errors.INVALID_REQUEST);
+            throw new ErrorPageException(session, Messages.INVALID_REQUEST);
+        }
+    }
+}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointQueryStringParser.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointQueryStringParser.java
new file mode 100644
index 0000000..8384fdc
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointQueryStringParser.java
@@ -0,0 +1,52 @@
+/*
+ * 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.protocol.oidc.endpoints.request;
+
+import java.util.Set;
+
+import javax.ws.rs.core.MultivaluedMap;
+
+/**
+ * Parse the parameters from request queryString
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+class AuthzEndpointQueryStringParser extends AuthzEndpointRequestParser {
+
+    private final MultivaluedMap<String, String> requestParams;
+
+    public AuthzEndpointQueryStringParser(MultivaluedMap<String, String> requestParams) {
+        this.requestParams = requestParams;
+    }
+
+    @Override
+    protected String getParameter(String paramName) {
+        return requestParams.getFirst(paramName);
+    }
+
+    @Override
+    protected Integer getIntParameter(String paramName) {
+        String paramVal = requestParams.getFirst(paramName);
+        return paramVal==null ? null : Integer.parseInt(paramVal);
+    }
+
+    @Override
+    protected Set<String> keySet() {
+        return requestParams.keySet();
+    }
+}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestObjectParser.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestObjectParser.java
new file mode 100644
index 0000000..658a2c0
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestObjectParser.java
@@ -0,0 +1,88 @@
+/*
+ * 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.protocol.oidc.endpoints.request;
+
+import java.security.PublicKey;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator;
+import org.keycloak.jose.jws.Algorithm;
+import org.keycloak.jose.jws.JWSHeader;
+import org.keycloak.jose.jws.JWSInput;
+import org.keycloak.jose.jws.crypto.RSAProvider;
+import org.keycloak.models.ClientModel;
+import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
+import org.keycloak.services.util.CertificateInfoHelper;
+import org.keycloak.util.JsonSerialization;
+
+/**
+ * Parse the parameters from OIDC "request" object
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+class AuthzEndpointRequestObjectParser extends AuthzEndpointRequestParser {
+
+    private final Map<String, Object> requestParams;
+
+    public AuthzEndpointRequestObjectParser(String requestObject, ClientModel client) throws Exception {
+        JWSInput input = new JWSInput(requestObject);
+        JWSHeader header = input.getHeader();
+
+        Algorithm requestedSignatureAlgorithm = OIDCAdvancedConfigWrapper.fromClientModel(client).getRequestObjectSignatureAlg();
+
+        if (requestedSignatureAlgorithm != null && requestedSignatureAlgorithm != header.getAlgorithm()) {
+            throw new RuntimeException("Request object signed with different algorithm than client requested algorithm");
+        }
+
+        if (header.getAlgorithm() == Algorithm.none) {
+            this.requestParams = JsonSerialization.readValue(input.getContent(), TypedHashMap.class);
+        } else if (header.getAlgorithm() == Algorithm.RS256) {
+            PublicKey clientPublicKey = CertificateInfoHelper.getSignatureValidationKey(client, JWTClientAuthenticator.ATTR_PREFIX);
+            boolean verified = RSAProvider.verify(input, clientPublicKey);
+            if (!verified) {
+                throw new RuntimeException("Failed to verify signature on 'request' object");
+            }
+
+            this.requestParams = JsonSerialization.readValue(input.getContent(), TypedHashMap.class);
+        } else {
+            throw new RuntimeException("Unsupported JWA algorithm used for signed request");
+        }
+    }
+
+    @Override
+    protected String getParameter(String paramName) {
+        Object val = this.requestParams.get(paramName);
+        return val==null ? null : val.toString();
+    }
+
+    @Override
+    protected Integer getIntParameter(String paramName) {
+        Object val = this.requestParams.get(paramName);
+        return val==null ? null : Integer.parseInt(getParameter(paramName));
+    }
+
+    @Override
+    protected Set<String> keySet() {
+        return requestParams.keySet();
+    }
+
+    static class TypedHashMap extends HashMap<String, Object> {
+    }
+}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java
new file mode 100644
index 0000000..e322d4b
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java
@@ -0,0 +1,122 @@
+/*
+ * 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.protocol.oidc.endpoints.request;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.keycloak.constants.AdapterConstants;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.services.ServicesLogger;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+abstract class AuthzEndpointRequestParser {
+
+    private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER;
+
+    /**
+     * Max number of additional req params copied into client session note to prevent DoS attacks
+     *
+     */
+    public static final int ADDITIONAL_REQ_PARAMS_MAX_MUMBER = 5;
+
+    /**
+     * Max size of additional req param value copied into client session note to prevent DoS attacks - params with longer value are ignored
+     *
+     */
+    public static final int ADDITIONAL_REQ_PARAMS_MAX_SIZE = 200;
+
+    /** Set of known protocol GET params not to be stored into additionalReqParams} */
+    private static final Set<String> KNOWN_REQ_PARAMS = new HashSet<>();
+    static {
+        KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.CLIENT_ID_PARAM);
+        KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.RESPONSE_TYPE_PARAM);
+        KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.RESPONSE_MODE_PARAM);
+        KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.REDIRECT_URI_PARAM);
+        KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.STATE_PARAM);
+        KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.SCOPE_PARAM);
+        KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.LOGIN_HINT_PARAM);
+        KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.PROMPT_PARAM);
+        KNOWN_REQ_PARAMS.add(AdapterConstants.KC_IDP_HINT);
+        KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.NONCE_PARAM);
+        KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.MAX_AGE_PARAM);
+        KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.UI_LOCALES_PARAM);
+        KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.REQUEST_PARAM);
+        KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.REQUEST_URI_PARAM);
+    }
+
+
+    public void parseRequest(AuthorizationEndpointRequest request) {
+        String clientId = getParameter(OIDCLoginProtocol.CLIENT_ID_PARAM);
+
+        if (request.clientId != null && !request.clientId.equals(clientId)) {
+            throw new IllegalArgumentException("The client_id parameter doesn't match the one from OIDC 'request' or 'request_uri'");
+        }
+
+        request.clientId = clientId;
+        request.responseType = replaceIfNotNull(request.responseType, getParameter(OIDCLoginProtocol.RESPONSE_TYPE_PARAM));
+        request.responseMode = replaceIfNotNull(request.responseMode, getParameter(OIDCLoginProtocol.RESPONSE_MODE_PARAM));
+        request.redirectUriParam = replaceIfNotNull(request.redirectUriParam, getParameter(OIDCLoginProtocol.REDIRECT_URI_PARAM));
+        request.state = replaceIfNotNull(request.state, getParameter(OIDCLoginProtocol.STATE_PARAM));
+        request.scope = replaceIfNotNull(request.scope, getParameter(OIDCLoginProtocol.SCOPE_PARAM));
+        request.loginHint = replaceIfNotNull(request.loginHint, getParameter(OIDCLoginProtocol.LOGIN_HINT_PARAM));
+        request.prompt = replaceIfNotNull(request.prompt, getParameter(OIDCLoginProtocol.PROMPT_PARAM));
+        request.idpHint = replaceIfNotNull(request.idpHint, getParameter(AdapterConstants.KC_IDP_HINT));
+        request.nonce = replaceIfNotNull(request.nonce, getParameter(OIDCLoginProtocol.NONCE_PARAM));
+        request.maxAge = replaceIfNotNull(request.maxAge, getIntParameter(OIDCLoginProtocol.MAX_AGE_PARAM));
+
+        extractAdditionalReqParams(request.additionalReqParams);
+    }
+
+
+    protected void extractAdditionalReqParams(Map<String, String> additionalReqParams) {
+        for (String paramName : keySet()) {
+            if (!KNOWN_REQ_PARAMS.contains(paramName)) {
+                String value = getParameter(paramName);
+                if (value != null && value.trim().isEmpty()) {
+                    value = null;
+                }
+                if (value != null && value.length() <= ADDITIONAL_REQ_PARAMS_MAX_SIZE) {
+                    if (additionalReqParams.size() >= ADDITIONAL_REQ_PARAMS_MAX_MUMBER) {
+                        logger.debug("Maximal number of additional OIDC params (" + ADDITIONAL_REQ_PARAMS_MAX_MUMBER + ") exceeded, ignoring rest of them!");
+                        break;
+                    }
+                    additionalReqParams.put(paramName, value);
+                } else {
+                    logger.debug("OIDC Additional param " + paramName + " ignored because value is empty or longer than " + ADDITIONAL_REQ_PARAMS_MAX_SIZE);
+                }
+            }
+
+        }
+    }
+
+    protected <T> T replaceIfNotNull(T previousVal, T newVal) {
+        return newVal==null ? previousVal : newVal;
+    }
+
+
+    protected abstract String getParameter(String paramName);
+
+    protected abstract Integer getIntParameter(String paramName);
+
+    protected abstract Set<String> keySet();
+
+}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java
index ad23283..f5e4caa 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java
@@ -19,6 +19,7 @@ package org.keycloak.protocol.oidc.endpoints;
 import org.jboss.resteasy.annotations.cache.NoCache;
 import org.jboss.resteasy.spi.HttpRequest;
 import org.jboss.resteasy.spi.HttpResponse;
+import org.keycloak.OAuth2Constants;
 import org.keycloak.common.ClientConnection;
 import org.keycloak.OAuthErrorException;
 import org.keycloak.RSATokenVerifier;
@@ -27,12 +28,15 @@ import org.keycloak.events.Details;
 import org.keycloak.events.Errors;
 import org.keycloak.events.EventBuilder;
 import org.keycloak.events.EventType;
+import org.keycloak.jose.jws.Algorithm;
+import org.keycloak.jose.jws.JWSBuilder;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
+import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
 import org.keycloak.protocol.oidc.TokenManager;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.services.ErrorResponseException;
@@ -40,17 +44,18 @@ import org.keycloak.services.managers.AppAuthManager;
 import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.resources.Cors;
 import org.keycloak.services.Urls;
+import org.keycloak.utils.MediaType;
 
 import javax.ws.rs.GET;
 import javax.ws.rs.OPTIONS;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.HttpHeaders;
-import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
+
+import java.security.PrivateKey;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -86,7 +91,6 @@ public class UserInfoEndpoint {
 
     @Path("/")
     @OPTIONS
-    @Produces(MediaType.APPLICATION_JSON)
     public Response issueUserInfoPreflight() {
         return Cors.add(this.request, Response.ok()).auth().preflight().build();
     }
@@ -94,7 +98,6 @@ public class UserInfoEndpoint {
     @Path("/")
     @GET
     @NoCache
-    @Produces(MediaType.APPLICATION_JSON)
     public Response issueUserInfoGet(@Context final HttpHeaders headers) {
         String accessToken = this.appAuthManager.extractAuthorizationHeaderToken(headers);
         return issueUserInfo(accessToken);
@@ -103,7 +106,6 @@ public class UserInfoEndpoint {
     @Path("/")
     @POST
     @NoCache
-    @Produces(MediaType.APPLICATION_JSON)
     public Response issueUserInfoPost() {
         // Try header first
         HttpHeaders headers = request.getHttpHeaders();
@@ -176,12 +178,39 @@ public class UserInfoEndpoint {
         AccessToken userInfo = new AccessToken();
         tokenManager.transformUserInfoAccessToken(session, userInfo, realm, clientModel, userModel, userSession, clientSession);
 
-        event.success();
-
         Map<String, Object> claims = new HashMap<String, Object>();
         claims.putAll(userInfo.getOtherClaims());
         claims.put("sub", userModel.getId());
-        return Cors.add(request, Response.ok(claims)).auth().allowedOrigins(token).build();
+
+        Response.ResponseBuilder responseBuilder;
+        OIDCAdvancedConfigWrapper cfg = OIDCAdvancedConfigWrapper.fromClientModel(clientModel);
+
+        if (cfg.isUserInfoSignatureRequired()) {
+            String issuerUrl = Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName());
+            String audience = clientModel.getClientId();
+            claims.put("iss", issuerUrl);
+            claims.put("aud", audience);
+
+            Algorithm signatureAlg = cfg.getUserInfoSignedResponseAlg();
+            PrivateKey privateKey = realm.getPrivateKey();
+
+            String signedUserInfo = new JWSBuilder()
+                    .jsonContent(claims)
+                    .sign(signatureAlg, privateKey);
+
+            responseBuilder = Response.ok(signedUserInfo).header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JWT);
+
+            event.detail(Details.SIGNATURE_REQUIRED, "true");
+            event.detail(Details.SIGNATURE_ALGORITHM, cfg.getUserInfoSignedResponseAlg().toString());
+        } else {
+            responseBuilder = Response.ok(claims).header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
+
+            event.detail(Details.SIGNATURE_REQUIRED, "false");
+        }
+
+        event.success();
+
+        return Cors.add(request, responseBuilder).auth().allowedOrigins(token).build();
     }
 
 }
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCAdvancedConfigWrapper.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCAdvancedConfigWrapper.java
new file mode 100644
index 0000000..3233690
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCAdvancedConfigWrapper.java
@@ -0,0 +1,106 @@
+/*
+ * 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.protocol.oidc;
+
+import java.util.HashMap;
+
+import org.keycloak.jose.jws.Algorithm;
+import org.keycloak.models.ClientModel;
+import org.keycloak.representations.idm.ClientRepresentation;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class OIDCAdvancedConfigWrapper {
+
+    private static final String USER_INFO_RESPONSE_SIGNATURE_ALG = "user.info.response.signature.alg";
+
+    private static final String REQUEST_OBJECT_SIGNATURE_ALG = "request.object.signature.alg";
+
+    private final ClientModel clientModel;
+    private final ClientRepresentation clientRep;
+
+    private OIDCAdvancedConfigWrapper(ClientModel client, ClientRepresentation clientRep) {
+        this.clientModel = client;
+        this.clientRep = clientRep;
+    }
+
+
+    public static OIDCAdvancedConfigWrapper fromClientModel(ClientModel client) {
+        return new OIDCAdvancedConfigWrapper(client, null);
+    }
+
+    public static OIDCAdvancedConfigWrapper fromClientRepresentation(ClientRepresentation clientRep) {
+        return new OIDCAdvancedConfigWrapper(null, clientRep);
+    }
+
+
+    public Algorithm getUserInfoSignedResponseAlg() {
+        String alg = getAttribute(USER_INFO_RESPONSE_SIGNATURE_ALG);
+        return alg==null ? null : Enum.valueOf(Algorithm.class, alg);
+    }
+
+    public void setUserInfoSignedResponseAlg(Algorithm alg) {
+        String algStr = alg==null ? null : alg.toString();
+        setAttribute(USER_INFO_RESPONSE_SIGNATURE_ALG, algStr);
+    }
+
+    public boolean isUserInfoSignatureRequired() {
+        return getUserInfoSignedResponseAlg() != null;
+    }
+
+    public Algorithm getRequestObjectSignatureAlg() {
+        String alg = getAttribute(REQUEST_OBJECT_SIGNATURE_ALG);
+        return alg==null ? null : Enum.valueOf(Algorithm.class, alg);
+    }
+
+    public void setRequestObjectSignatureAlg(Algorithm alg) {
+        String algStr = alg==null ? null : alg.toString();
+        setAttribute(REQUEST_OBJECT_SIGNATURE_ALG, algStr);
+    }
+
+
+    private String getAttribute(String attrKey) {
+        if (clientModel != null) {
+            return clientModel.getAttribute(attrKey);
+        } else {
+            return clientRep.getAttributes()==null ? null : clientRep.getAttributes().get(attrKey);
+        }
+    }
+
+    private void setAttribute(String attrKey, String attrValue) {
+        if (clientModel != null) {
+            if (attrValue != null) {
+                clientModel.setAttribute(attrKey, attrValue);
+            } else {
+                clientModel.removeAttribute(attrKey);
+            }
+        } else {
+            if (attrValue != null) {
+                if (clientRep.getAttributes() == null) {
+                    clientRep.setAttributes(new HashMap<>());
+                }
+                clientRep.getAttributes().put(attrKey, attrValue);
+            } else {
+                if (clientRep.getAttributes() != null) {
+                    clientRep.getAttributes().put(attrKey, null);
+                }
+            }
+        }
+    }
+}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocol.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocol.java
index 3c4c3aa..c263405 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocol.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocol.java
@@ -66,6 +66,7 @@ public class OIDCLoginProtocol implements LoginProtocol {
     public static final String REQUEST_PARAM = "request";
     public static final String REQUEST_URI_PARAM = "request_uri";
     public static final String UI_LOCALES_PARAM = OAuth2Constants.UI_LOCALES_PARAM;
+    public static final String CLAIMS_PARAM = "claims";
 
     public static final String LOGOUT_REDIRECT_URI = "OIDC_LOGOUT_REDIRECT_URI";
     public static final String ISSUER = "iss";
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java
index eb5b5b4..800630c 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java
@@ -48,6 +48,10 @@ public class OIDCWellKnownProvider implements WellKnownProvider {
 
     public static final List<String> DEFAULT_ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED = list(Algorithm.RS256.toString());
 
+    public static final List<String> DEFAULT_USER_INFO_SIGNING_ALG_VALUES_SUPPORTED  = list(Algorithm.RS256.toString());
+
+    public static final List<String> DEFAULT_REQUEST_OBJECT_SIGNING_ALG_VALUES_SUPPORTED  = list(Algorithm.none.toString(), Algorithm.RS256.toString());
+
     public static final List<String> DEFAULT_GRANT_TYPES_SUPPORTED = list(OAuth2Constants.AUTHORIZATION_CODE, OAuth2Constants.IMPLICIT, OAuth2Constants.REFRESH_TOKEN, OAuth2Constants.PASSWORD, OAuth2Constants.CLIENT_CREDENTIALS);
 
     public static final List<String> DEFAULT_RESPONSE_TYPES_SUPPORTED = list(OAuth2Constants.CODE, OIDCResponseType.NONE, OIDCResponseType.ID_TOKEN, OIDCResponseType.TOKEN, "id_token token", "code id_token", "code token", "code id_token token");
@@ -90,6 +94,8 @@ public class OIDCWellKnownProvider implements WellKnownProvider {
         config.setRegistrationEndpoint(RealmsResource.clientRegistrationUrl(uriInfo).path(ClientRegistrationService.class, "provider").build(realm.getName(), OIDCClientRegistrationProviderFactory.ID).toString());
 
         config.setIdTokenSigningAlgValuesSupported(DEFAULT_ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED);
+        config.setUserInfoSigningAlgValuesSupported(DEFAULT_USER_INFO_SIGNING_ALG_VALUES_SUPPORTED);
+        config.setRequestObjectSigningAlgValuesSupported(DEFAULT_REQUEST_OBJECT_SIGNING_ALG_VALUES_SUPPORTED);
         config.setResponseTypesSupported(DEFAULT_RESPONSE_TYPES_SUPPORTED);
         config.setSubjectTypesSupported(DEFAULT_SUBJECT_TYPES_SUPPORTED);
         config.setResponseModesSupported(DEFAULT_RESPONSE_MODES_SUPPORTED);
@@ -104,8 +110,8 @@ public class OIDCWellKnownProvider implements WellKnownProvider {
 
         config.setScopesSupported(SCOPES_SUPPORTED);
 
-        config.setRequestParameterSupported(false);
-        config.setRequestUriParameterSupported(false);
+        config.setRequestParameterSupported(true);
+        config.setRequestUriParameterSupported(true);
 
         return config;
     }
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/representations/OIDCConfigurationRepresentation.java b/services/src/main/java/org/keycloak/protocol/oidc/representations/OIDCConfigurationRepresentation.java
index 0421e16..181e0d2 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/representations/OIDCConfigurationRepresentation.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/representations/OIDCConfigurationRepresentation.java
@@ -64,6 +64,12 @@ public class OIDCConfigurationRepresentation {
     @JsonProperty("id_token_signing_alg_values_supported")
     private List<String> idTokenSigningAlgValuesSupported;
 
+    @JsonProperty("userinfo_signing_alg_values_supported")
+    private List<String> userInfoSigningAlgValuesSupported;
+
+    @JsonProperty("request_object_signing_alg_values_supported")
+    private List<String> requestObjectSigningAlgValuesSupported;
+
     @JsonProperty("response_modes_supported")
     private List<String> responseModesSupported;
 
@@ -184,6 +190,22 @@ public class OIDCConfigurationRepresentation {
         this.idTokenSigningAlgValuesSupported = idTokenSigningAlgValuesSupported;
     }
 
+    public List<String> getUserInfoSigningAlgValuesSupported() {
+        return userInfoSigningAlgValuesSupported;
+    }
+
+    public void setUserInfoSigningAlgValuesSupported(List<String> userInfoSigningAlgValuesSupported) {
+        this.userInfoSigningAlgValuesSupported = userInfoSigningAlgValuesSupported;
+    }
+
+    public List<String> getRequestObjectSigningAlgValuesSupported() {
+        return requestObjectSigningAlgValuesSupported;
+    }
+
+    public void setRequestObjectSigningAlgValuesSupported(List<String> requestObjectSigningAlgValuesSupported) {
+        this.requestObjectSigningAlgValuesSupported = requestObjectSigningAlgValuesSupported;
+    }
+
     public List<String> getResponseModesSupported() {
         return responseModesSupported;
     }
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/utils/JWKSUtils.java b/services/src/main/java/org/keycloak/protocol/oidc/utils/JWKSUtils.java
index c856a81..d8f7fe7 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/utils/JWKSUtils.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/utils/JWKSUtils.java
@@ -18,12 +18,15 @@
 package org.keycloak.protocol.oidc.utils;
 
 import java.io.IOException;
+import java.io.InputStream;
 import java.security.PublicKey;
 
-import org.keycloak.broker.provider.util.SimpleHttp;
+import org.keycloak.common.util.StreamUtil;
+import org.keycloak.connections.httpclient.HttpClientProvider;
 import org.keycloak.jose.jwk.JSONWebKeySet;
 import org.keycloak.jose.jwk.JWK;
 import org.keycloak.jose.jwk.JWKParser;
+import org.keycloak.models.KeycloakSession;
 import org.keycloak.util.JsonSerialization;
 
 /**
@@ -31,8 +34,9 @@ import org.keycloak.util.JsonSerialization;
  */
 public class JWKSUtils {
 
-    public static JSONWebKeySet sendJwksRequest(String jwksURI) throws IOException {
-        String keySetString = SimpleHttp.doGet(jwksURI).asString();
+    public static JSONWebKeySet sendJwksRequest(KeycloakSession session, String jwksURI) throws IOException {
+        InputStream is = session.getProvider(HttpClientProvider.class).get(jwksURI);
+        String keySetString = StreamUtil.readString(is);
         return JsonSerialization.readValue(keySetString, JSONWebKeySet.class);
     }
 
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/utils/RedirectUtils.java b/services/src/main/java/org/keycloak/protocol/oidc/utils/RedirectUtils.java
index 441cd57..b7d9ad4 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/utils/RedirectUtils.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/utils/RedirectUtils.java
@@ -77,7 +77,7 @@ public class RedirectUtils {
         } else {
             redirectUri = lowerCaseHostname(redirectUri);
 
-            String r = redirectUri.indexOf('?') != -1 ? redirectUri.substring(0, redirectUri.indexOf('?')) : redirectUri;
+            String r = redirectUri;
             Set<String> resolveValidRedirects = resolveValidRedirects(uriInfo, rootUrl, validRedirects);
 
             boolean valid = matchesRedirects(resolveValidRedirects, r);
@@ -134,15 +134,17 @@ public class RedirectUtils {
 
     private static boolean matchesRedirects(Set<String> validRedirects, String redirect) {
         for (String validRedirect : validRedirects) {
-            if (validRedirect.endsWith("*")) {
+            if (validRedirect.endsWith("*") && !validRedirect.contains("?")) {
+                // strip off the query component - we don't check them when wildcards are effective
+                String r = redirect.contains("?") ? redirect.substring(0, redirect.indexOf("?")) : redirect;
                 // strip off *
                 int length = validRedirect.length() - 1;
                 validRedirect = validRedirect.substring(0, length);
-                if (redirect.startsWith(validRedirect)) return true;
+                if (r.startsWith(validRedirect)) return true;
                 // strip off trailing '/'
                 if (length - 1 > 0 && validRedirect.charAt(length - 1) == '/') length--;
                 validRedirect = validRedirect.substring(0, length);
-                if (validRedirect.equals(redirect)) return true;
+                if (validRedirect.equals(r)) return true;
             } else if (validRedirect.equals(redirect)) return true;
         }
         return false;
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/AbstractClientRegistrationProvider.java b/services/src/main/java/org/keycloak/services/clientregistration/AbstractClientRegistrationProvider.java
index 88b5a34..d634b00 100755
--- a/services/src/main/java/org/keycloak/services/clientregistration/AbstractClientRegistrationProvider.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/AbstractClientRegistrationProvider.java
@@ -29,8 +29,12 @@ import org.keycloak.models.utils.RepresentationToModel;
 import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.services.ErrorResponseException;
 import org.keycloak.services.ForbiddenException;
+import org.keycloak.services.resources.admin.AdminRoot;
+import org.keycloak.services.validation.ClientValidator;
+import org.keycloak.services.validation.ValidationMessages;
 
 import javax.ws.rs.core.Response;
+import java.util.Properties;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@@ -50,6 +54,16 @@ public abstract class AbstractClientRegistrationProvider implements ClientRegist
 
         auth.requireCreate();
 
+        ValidationMessages validationMessages = new ValidationMessages();
+        if (!ClientValidator.validate(client, validationMessages)) {
+            String errorCode = validationMessages.fieldHasError("redirectUris") ? ErrorCodes.INVALID_REDIRECT_URI : ErrorCodes.INVALID_CLIENT_METADATA;
+            throw new ErrorResponseException(
+                    errorCode,
+                    validationMessages.getStringMessages(),
+                    Response.Status.BAD_REQUEST
+            );
+        }
+
         try {
             ClientModel clientModel = RepresentationToModel.createClient(session, session.getContext().getRealm(), client, true);
 
@@ -104,6 +118,16 @@ public abstract class AbstractClientRegistrationProvider implements ClientRegist
             throw new ErrorResponseException(ErrorCodes.INVALID_CLIENT_METADATA, "Client Identifier modified", Response.Status.BAD_REQUEST);
         }
 
+        ValidationMessages validationMessages = new ValidationMessages();
+        if (!ClientValidator.validate(rep, validationMessages)) {
+            String errorCode = validationMessages.fieldHasError("redirectUris") ? ErrorCodes.INVALID_REDIRECT_URI : ErrorCodes.INVALID_CLIENT_METADATA;
+            throw new ErrorResponseException(
+                    errorCode,
+                    validationMessages.getStringMessages(),
+                    Response.Status.BAD_REQUEST
+            );
+        }
+
         RepresentationToModel.updateClient(rep, client);
         rep = ModelToRepresentation.toRepresentation(client);
 
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/oidc/DescriptionConverter.java b/services/src/main/java/org/keycloak/services/clientregistration/oidc/DescriptionConverter.java
index 3ddcc59..e1939ef 100644
--- a/services/src/main/java/org/keycloak/services/clientregistration/oidc/DescriptionConverter.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/oidc/DescriptionConverter.java
@@ -24,8 +24,10 @@ import org.keycloak.authentication.authenticators.client.ClientIdAndSecretAuthen
 import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator;
 import org.keycloak.jose.jwk.JSONWebKeySet;
 import org.keycloak.jose.jwk.JWK;
+import org.keycloak.jose.jws.Algorithm;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
 import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil;
 import org.keycloak.protocol.oidc.utils.JWKSUtils;
@@ -87,14 +89,12 @@ public class DescriptionConverter {
         }
         client.setClientAuthenticatorType(clientAuthFactory.getId());
 
-        // Externalize to ClientAuthenticator itself?
-        if (authMethod != null && authMethod.equals(OIDCLoginProtocol.PRIVATE_KEY_JWT)) {
-
-            PublicKey publicKey = retrievePublicKey(clientOIDC);
-            if (publicKey == null) {
-                throw new ClientRegistrationException("Didn't find key of supported keyType for use " + JWK.Use.SIG.asString());
-            }
+        PublicKey publicKey = retrievePublicKey(session, clientOIDC);
+        if (authMethod != null && authMethod.equals(OIDCLoginProtocol.PRIVATE_KEY_JWT) && publicKey == null) {
+            throw new ClientRegistrationException("Didn't find key of supported keyType for use " + JWK.Use.SIG.asString());
+        }
 
+        if (publicKey != null) {
             String publicKeyPem = KeycloakModelUtils.getPemFromKey(publicKey);
 
             CertificateRepresentation rep = new CertificateRepresentation();
@@ -102,13 +102,24 @@ public class DescriptionConverter {
             CertificateInfoHelper.updateClientRepresentationCertificateInfo(client, rep, JWTClientAuthenticator.ATTR_PREFIX);
         }
 
+        OIDCAdvancedConfigWrapper configWrapper = OIDCAdvancedConfigWrapper.fromClientRepresentation(client);
+        if (clientOIDC.getUserinfoSignedResponseAlg() != null) {
+            Algorithm algorithm = Enum.valueOf(Algorithm.class, clientOIDC.getUserinfoSignedResponseAlg());
+            configWrapper.setUserInfoSignedResponseAlg(algorithm);
+        }
+
+        if (clientOIDC.getRequestObjectSigningAlg() != null) {
+            Algorithm algorithm = Enum.valueOf(Algorithm.class, clientOIDC.getRequestObjectSigningAlg());
+            configWrapper.setRequestObjectSignatureAlg(algorithm);
+        }
+
         return client;
     }
 
 
-    private static PublicKey retrievePublicKey(OIDCClientRepresentation clientOIDC) {
+    private static PublicKey retrievePublicKey(KeycloakSession session, OIDCClientRepresentation clientOIDC) {
         if (clientOIDC.getJwksUri() == null && clientOIDC.getJwks() == null) {
-            throw new ClientRegistrationException("Requested client authentication method '%s' but jwks_uri nor jwks were available in config");
+            return null;
         }
 
         if (clientOIDC.getJwksUri() != null && clientOIDC.getJwks() != null) {
@@ -120,7 +131,7 @@ public class DescriptionConverter {
             keySet = clientOIDC.getJwks();
         } else {
             try {
-                keySet = JWKSUtils.sendJwksRequest(clientOIDC.getJwksUri());
+                keySet = JWKSUtils.sendJwksRequest(session, clientOIDC.getJwksUri());
             } catch (IOException ioe) {
                 throw new ClientRegistrationException("Failed to send JWKS request to specified jwks_uri", ioe);
             }
@@ -152,6 +163,15 @@ public class DescriptionConverter {
         response.setRegistrationClientUri(uri.toString());
         response.setResponseTypes(getOIDCResponseTypes(client));
         response.setGrantTypes(getOIDCGrantTypes(client));
+
+        OIDCAdvancedConfigWrapper config = OIDCAdvancedConfigWrapper.fromClientRepresentation(client);
+        if (config.isUserInfoSignatureRequired()) {
+            response.setUserinfoSignedResponseAlg(config.getUserInfoSignedResponseAlg().toString());
+        }
+        if (config.getRequestObjectSignatureAlg() != null) {
+            response.setRequestObjectSigningAlg(config.getRequestObjectSignatureAlg().toString());
+        }
+
         return response;
     }
 
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java
index f29ed8c..152d632 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java
@@ -66,6 +66,10 @@ import java.util.Map;
  */
 public class ClientAttributeCertificateResource {
 
+    public static final String CERTIFICATE_PEM = "Certificate PEM";
+    public static final String PUBLIC_KEY_PEM = "Public Key PEM";
+    public static final String JSON_WEB_KEY_SET = "JSON Web Key Set";
+
     protected RealmModel realm;
     private RealmAuth auth;
     protected ClientModel client;
@@ -195,12 +199,23 @@ public class ClientAttributeCertificateResource {
         Map<String, List<InputPart>> uploadForm = input.getFormDataMap();
         String keystoreFormat = uploadForm.get("keystoreFormat").get(0).getBodyAsString();
         List<InputPart> inputParts = uploadForm.get("file");
-        if (keystoreFormat.equals("Certificate PEM")) {
+        if (keystoreFormat.equals(CERTIFICATE_PEM)) {
             String pem = StreamUtil.readString(inputParts.get(0).getBody(InputStream.class, null));
+
+            // Validate format
+            KeycloakModelUtils.getCertificate(pem);
+
             info.setCertificate(pem);
             return info;
+        } else if (keystoreFormat.equals(PUBLIC_KEY_PEM)) {
+            String pem = StreamUtil.readString(inputParts.get(0).getBody(InputStream.class, null));
+
+            // Validate format
+            KeycloakModelUtils.getPublicKey(pem);
 
-        } else if (keystoreFormat.equals("JSON Web Key Set (JWK)")) {
+            info.setPublicKey(pem);
+            return info;
+        } else if (keystoreFormat.equals(JSON_WEB_KEY_SET)) {
             InputStream stream = inputParts.get(0).getBody(InputStream.class, null);
             JSONWebKeySet keySet = JsonSerialization.readValue(stream, JSONWebKeySet.class);
             PublicKey publicKey = JWKSUtils.getKeyForUse(keySet, JWK.Use.SIG);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java
index fc5b3c9..dd17b07 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java
@@ -40,6 +40,7 @@ import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.representations.idm.UserRepresentation;
 import org.keycloak.representations.idm.UserSessionRepresentation;
+import org.keycloak.services.ErrorResponseException;
 import org.keycloak.services.ServicesLogger;
 import org.keycloak.services.clientregistration.ClientRegistrationTokenUtils;
 import org.keycloak.services.managers.ClientManager;
@@ -48,6 +49,8 @@ import org.keycloak.services.managers.ResourceAdminManager;
 import org.keycloak.services.resources.KeycloakApplication;
 import org.keycloak.services.ErrorResponse;
 import org.keycloak.common.util.Time;
+import org.keycloak.services.validation.ClientValidator;
+import org.keycloak.services.validation.ValidationMessages;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
@@ -63,10 +66,7 @@ import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 import static java.lang.Boolean.TRUE;
 
@@ -126,6 +126,16 @@ public class ClientResource {
             throw new NotFoundException("Could not find client");
         }
 
+        ValidationMessages validationMessages = new ValidationMessages();
+        if (!ClientValidator.validate(rep, validationMessages)) {
+            Properties messages = AdminRoot.getMessages(session, realm, auth.getAuth().getToken().getLocale());
+            throw new ErrorResponseException(
+                    validationMessages.getStringMessages(),
+                    validationMessages.getStringMessages(messages),
+                    Response.Status.BAD_REQUEST
+            );
+        }
+
         try {
             updateClientFromRep(rep, client, session);
             adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo).representation(rep).success();
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java
index 207f8d1..dacea26 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java
@@ -17,7 +17,6 @@
 package org.keycloak.services.resources.admin;
 
 import org.jboss.resteasy.annotations.cache.NoCache;
-import org.jboss.resteasy.spi.NotFoundException;
 import org.jboss.resteasy.spi.ResteasyProviderFactory;
 import org.keycloak.events.admin.OperationType;
 import org.keycloak.events.admin.ResourceType;
@@ -28,16 +27,13 @@ import org.keycloak.models.RealmModel;
 import org.keycloak.models.utils.ModelToRepresentation;
 import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.services.ErrorResponse;
+import org.keycloak.services.ErrorResponseException;
 import org.keycloak.services.ServicesLogger;
 import org.keycloak.services.managers.ClientManager;
+import org.keycloak.services.validation.ClientValidator;
+import org.keycloak.services.validation.ValidationMessages;
 
-import javax.ws.rs.Consumes;
-import javax.ws.rs.GET;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
+import javax.ws.rs.*;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
@@ -45,6 +41,7 @@ import javax.ws.rs.core.UriInfo;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Properties;
 
 /**
  * Base resource class for managing a realm's clients.
@@ -122,6 +119,16 @@ public class ClientsResource {
     public Response createClient(final @Context UriInfo uriInfo, final ClientRepresentation rep) {
         auth.requireManage();
 
+        ValidationMessages validationMessages = new ValidationMessages();
+        if (!ClientValidator.validate(rep, validationMessages)) {
+            Properties messages = AdminRoot.getMessages(session, realm, auth.getAuth().getToken().getLocale());
+            throw new ErrorResponseException(
+                    validationMessages.getStringMessages(),
+                    validationMessages.getStringMessages(messages),
+                    Response.Status.BAD_REQUEST
+            );
+        }
+
         try {
             ClientModel clientModel = ClientManager.createClient(session, realm, rep, true);
 
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java
index 958a849..8e7c9ac 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java
@@ -116,7 +116,7 @@ public class IdentityProvidersResource {
         InputPart file = formDataMap.get("file").get(0);
         InputStream inputStream = file.getBody(InputStream.class, null);
         IdentityProviderFactory providerFactory = getProviderFactorytById(providerId);
-        Map<String, String> config = providerFactory.parseConfig(inputStream);
+        Map<String, String> config = providerFactory.parseConfig(session, inputStream);
         return config;
     }
 
@@ -143,7 +143,7 @@ public class IdentityProvidersResource {
         try {
             IdentityProviderFactory providerFactory = getProviderFactorytById(providerId);
             Map<String, String> config;
-            config = providerFactory.parseConfig(inputStream);
+            config = providerFactory.parseConfig(session, inputStream);
             return config;
         } finally {
             try {
diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
index 5e1cd9f..6cdf52e 100644
--- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
+++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
@@ -56,15 +56,22 @@ import java.io.*;
 import java.net.URI;
 import java.net.URL;
 import java.util.*;
+import org.jboss.dmr.ModelNode;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
 public class KeycloakApplication extends Application {
+    // This param name is defined again in Keycloak Server Subsystem class
+    // org.keycloak.subsystem.server.extension.KeycloakServerDeploymentProcessor.  We have this value in
+    // two places to avoid dependency between Keycloak Subsystem and Keycloak Services module.
+    public static final String KEYCLOAK_CONFIG_PARAM_NAME = "org.keycloak.server-subsystem.Config";
 
     private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER;
 
+    protected boolean embedded = false;
+
     protected Set<Object> singletons = new HashSet<Object>();
     protected Set<Class<?>> classes = new HashSet<Class<?>>();
 
@@ -72,69 +79,79 @@ public class KeycloakApplication extends Application {
     protected String contextPath;
 
     public KeycloakApplication(@Context ServletContext context, @Context Dispatcher dispatcher) {
-        loadConfig();
+        try {
+            if ("true".equals(context.getInitParameter("keycloak.embedded"))) {
+                embedded = true;
+            }
 
-        this.contextPath = context.getContextPath();
-        this.sessionFactory = createSessionFactory();
+            loadConfig(context);
 
-        dispatcher.getDefaultContextObjects().put(KeycloakApplication.class, this);
-        ResteasyProviderFactory.pushContext(KeycloakApplication.class, this); // for injection
-        context.setAttribute(KeycloakSessionFactory.class.getName(), this.sessionFactory);
+            this.contextPath = context.getContextPath();
+            this.sessionFactory = createSessionFactory();
 
-        singletons.add(new ServerVersionResource());
-        singletons.add(new RobotsResource());
-        singletons.add(new RealmsResource());
-        singletons.add(new AdminRoot());
-        classes.add(ThemeResource.class);
-        classes.add(JsResource.class);
+            dispatcher.getDefaultContextObjects().put(KeycloakApplication.class, this);
+            ResteasyProviderFactory.pushContext(KeycloakApplication.class, this); // for injection
+            context.setAttribute(KeycloakSessionFactory.class.getName(), this.sessionFactory);
 
-        classes.add(KeycloakTransactionCommitter.class);
+            singletons.add(new ServerVersionResource());
+            singletons.add(new RobotsResource());
+            singletons.add(new RealmsResource());
+            singletons.add(new AdminRoot());
+            classes.add(ThemeResource.class);
+            classes.add(JsResource.class);
 
-        singletons.add(new ObjectMapperResolver(Boolean.parseBoolean(System.getProperty("keycloak.jsonPrettyPrint", "false"))));
+            classes.add(KeycloakTransactionCommitter.class);
 
-        ExportImportManager[] exportImportManager = new ExportImportManager[1];
+            singletons.add(new ObjectMapperResolver(Boolean.parseBoolean(System.getProperty("keycloak.jsonPrettyPrint", "false"))));
 
-        KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
+            ExportImportManager[] exportImportManager = new ExportImportManager[1];
 
-            @Override
-            public void run(KeycloakSession lockSession) {
-                DBLockManager dbLockManager = new DBLockManager(lockSession);
-                dbLockManager.checkForcedUnlock();
-                DBLockProvider dbLock = dbLockManager.getDBLock();
-                dbLock.waitForLock();
-                try {
-                    exportImportManager[0] = migrateAndBootstrap();
-                } finally {
-                    dbLock.releaseLock();
+            KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
+
+                @Override
+                public void run(KeycloakSession lockSession) {
+                    DBLockManager dbLockManager = new DBLockManager(lockSession);
+                    dbLockManager.checkForcedUnlock();
+                    DBLockProvider dbLock = dbLockManager.getDBLock();
+                    dbLock.waitForLock();
+                    try {
+                        exportImportManager[0] = migrateAndBootstrap();
+                    } finally {
+                        dbLock.releaseLock();
+                    }
                 }
-            }
 
-        });
+            });
 
 
-        if (exportImportManager[0].isRunExport()) {
-            exportImportManager[0].runExport();
-        }
+            if (exportImportManager[0].isRunExport()) {
+                exportImportManager[0].runExport();
+            }
 
-        boolean bootstrapAdminUser = false;
-        KeycloakSession session = sessionFactory.create();
-        try {
-            session.getTransactionManager().begin();
-            bootstrapAdminUser = new ApplianceBootstrap(session).isNoMasterUser();
+            boolean bootstrapAdminUser = false;
+            KeycloakSession session = sessionFactory.create();
+            try {
+                session.getTransactionManager().begin();
+                bootstrapAdminUser = new ApplianceBootstrap(session).isNoMasterUser();
 
-            session.getTransactionManager().commit();
-        } finally {
-            session.close();
-        }
+                session.getTransactionManager().commit();
+            } finally {
+                session.close();
+            }
 
-        sessionFactory.publish(new PostMigrationEvent());
+            sessionFactory.publish(new PostMigrationEvent());
 
-        singletons.add(new WelcomeResource(bootstrapAdminUser));
+            singletons.add(new WelcomeResource(bootstrapAdminUser));
 
-        setupScheduledTasks(sessionFactory);
+            setupScheduledTasks(sessionFactory);
+        } catch (Throwable t) {
+            if (!embedded) {
+                exit(1);
+            }
+            throw t;
+        }
     }
 
-
     // Migrate model, bootstrap master realm, import realms and create admin user. This is done with acquired dbLock
     protected ExportImportManager migrateAndBootstrap() {
         ExportImportManager exportImportManager;
@@ -185,7 +202,6 @@ public class KeycloakApplication extends Application {
             session.getTransactionManager().commit();
         } catch (Exception e) {
             session.getTransactionManager().rollback();
-            logger.migrationFailure(e);
             throw e;
         } finally {
             session.close();
@@ -206,12 +222,18 @@ public class KeycloakApplication extends Application {
         return uriInfo.getBaseUriBuilder().replacePath(getContextPath()).build();
     }
 
-    public static void loadConfig() {
+    public static void loadConfig(ServletContext context) {
         try {
             JsonNode node = null;
+            
+            String dmrConfig = loadDmrConfig(context);
+            if (dmrConfig != null) {
+                node = new ObjectMapper().readTree(dmrConfig);
+                logger.loadingFrom("standalone.xml or domain.xml");
+            }
 
             String configDir = System.getProperty("jboss.server.config.dir");
-            if (configDir != null) {
+            if (node == null && configDir != null) {
                 File f = new File(configDir + File.separator + "keycloak-server.json");
                 if (f.isFile()) {
                     logger.loadingFrom(f.getAbsolutePath());
@@ -230,14 +252,24 @@ public class KeycloakApplication extends Application {
             if (node != null) {
                 Properties properties = new SystemEnvProperties();
                 Config.init(new JsonConfigProvider(node, properties));
-                return;
             } else {
-                throw new RuntimeException("Config 'keycloak-server.json' not found");
+                throw new RuntimeException("Keycloak config not found.");
             }
         } catch (IOException e) {
             throw new RuntimeException("Failed to load config", e);
         }
     }
+    
+    private static String loadDmrConfig(ServletContext context) {
+        String dmrConfig = context.getInitParameter(KEYCLOAK_CONFIG_PARAM_NAME);
+        if (dmrConfig == null) return null;
+
+        ModelNode dmrConfigNode = ModelNode.fromString(dmrConfig);
+        if (dmrConfigNode.asPropertyList().isEmpty()) return null;
+        
+        // note that we need to resolve expressions BEFORE we convert to JSON
+        return dmrConfigNode.resolve().toJSONString(true);
+    }
 
     public static KeycloakSessionFactory createSessionFactory() {
         DefaultKeycloakSessionFactory factory = new DefaultKeycloakSessionFactory();
@@ -386,4 +418,13 @@ public class KeycloakApplication extends Application {
         }
     }
 
+    private void exit(int status) {
+        new Thread() {
+            @Override
+            public void run() {
+                System.exit(status);
+            }
+        }.start();
+    }
+
 }
diff --git a/services/src/main/java/org/keycloak/services/ServicesLogger.java b/services/src/main/java/org/keycloak/services/ServicesLogger.java
index de7eef7..af6c4c1 100644
--- a/services/src/main/java/org/keycloak/services/ServicesLogger.java
+++ b/services/src/main/java/org/keycloak/services/ServicesLogger.java
@@ -430,4 +430,8 @@ public interface ServicesLogger extends BasicLogger {
     @Message(id=96, value="Not found JWK of supported keyType under jwks_uri for usage: %s")
     void supportedJwkNotFound(String usage);
 
+    @LogMessage(level = WARN)
+    @Message(id=97, value="Invalid request")
+    void invalidRequest(@Cause Throwable t);
+
 }
diff --git a/services/src/main/java/org/keycloak/services/util/CertificateInfoHelper.java b/services/src/main/java/org/keycloak/services/util/CertificateInfoHelper.java
index b309927..359d28d 100644
--- a/services/src/main/java/org/keycloak/services/util/CertificateInfoHelper.java
+++ b/services/src/main/java/org/keycloak/services/util/CertificateInfoHelper.java
@@ -17,9 +17,18 @@
 
 package org.keycloak.services.util;
 
+import java.security.PublicKey;
+import java.security.cert.X509Certificate;
 import java.util.HashMap;
 
+import javax.ws.rs.core.Response;
+
+import org.keycloak.authentication.AuthenticationFlowError;
+import org.keycloak.authentication.ClientAuthenticationFlowContext;
+import org.keycloak.authentication.authenticators.client.ClientAuthUtil;
 import org.keycloak.models.ClientModel;
+import org.keycloak.models.ModelException;
+import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.representations.idm.CertificateRepresentation;
 import org.keycloak.representations.idm.ClientRepresentation;
 
@@ -34,6 +43,8 @@ public class CertificateInfoHelper {
     public static final String PUBLIC_KEY = "public.key";
 
 
+    // CLIENT MODEL METHODS
+
     public static CertificateRepresentation getCertificateFromClient(ClientModel client, String attributePrefix) {
         String privateKeyAttribute = attributePrefix + "." + PRIVATE_KEY;
         String certificateAttribute = attributePrefix + "." + X509CERTIFICATE;
@@ -75,6 +86,32 @@ public class CertificateInfoHelper {
     }
 
 
+    public static PublicKey getSignatureValidationKey(ClientModel client, String attributePrefix) throws ModelException {
+        CertificateRepresentation certInfo = getCertificateFromClient(client, attributePrefix);
+
+        String encodedCertificate = certInfo.getCertificate();
+        String encodedPublicKey = certInfo.getPublicKey();
+
+        if (encodedCertificate == null && encodedPublicKey == null) {
+            throw new ModelException("Client doesn't have certificate or publicKey configured");
+        }
+
+        if (encodedCertificate != null && encodedPublicKey != null) {
+            throw new ModelException("Client has both publicKey and certificate configured");
+        }
+
+        // TODO: Caching of publicKeys / certificates, so it doesn't need to be always computed from pem. For performance reasons...
+        if (encodedCertificate != null) {
+            X509Certificate clientCert = KeycloakModelUtils.getCertificate(encodedCertificate);
+            return clientCert.getPublicKey();
+        } else {
+            return KeycloakModelUtils.getPublicKey(encodedPublicKey);
+        }
+    }
+
+
+    // CLIENT REPRESENTATION METHODS
+
     public static void updateClientRepresentationCertificateInfo(ClientRepresentation client, CertificateRepresentation rep, String attributePrefix) {
         String privateKeyAttribute = attributePrefix + "." + PRIVATE_KEY;
         String certificateAttribute = attributePrefix + "." + X509CERTIFICATE;
diff --git a/services/src/main/java/org/keycloak/services/validation/ClientValidator.java b/services/src/main/java/org/keycloak/services/validation/ClientValidator.java
new file mode 100644
index 0000000..22290c5
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/validation/ClientValidator.java
@@ -0,0 +1,54 @@
+/*
+ *
+ *  * 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.services.validation;
+
+import org.keycloak.representations.idm.ClientRepresentation;
+
+/**
+ * @author Vaclav Muzikar <vmuzikar@redhat.com>
+ */
+public class ClientValidator {
+    /**
+     * Checks if the Client's Redirect URIs doesn't contain any URI fragments (like http://example.org/auth#fragment)
+     *
+     * @see <a href="https://issues.jboss.org/browse/KEYCLOAK-3421">KEYCLOAK-3421</a>
+     * @param client
+     * @param messages
+     * @return true if Redirect URIs doesn't contain any URI with fragments
+     */
+    public static boolean validate(ClientRepresentation client, ValidationMessages messages) {
+        boolean isValid = true;
+
+        if (client.getRedirectUris() != null) {
+            long urisWithFragmentCount = client.getRedirectUris().stream().filter(p -> p.contains("#")).count();
+            if (urisWithFragmentCount > 0) {
+                messages.add("redirectUris", "Redirect URIs must not contain an URI fragment", "clientRedirectURIsFragmentError");
+                isValid = false;
+            }
+        }
+
+        if (client.getRootUrl() != null && client.getRootUrl().contains("#")) {
+            messages.add("rootUrl", "Root URL must not contain an URL fragment", "clientRootURLFragmentError");
+            isValid = false;
+        }
+
+        return isValid;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/services/validation/ValidationMessage.java b/services/src/main/java/org/keycloak/services/validation/ValidationMessage.java
new file mode 100644
index 0000000..7e4dac1
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/validation/ValidationMessage.java
@@ -0,0 +1,100 @@
+/*
+ *
+ *  * 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.services.validation;
+
+import java.text.MessageFormat;
+import java.util.Properties;
+
+/**
+ * @author Vaclav Muzikar <vmuzikar@redhat.com>
+ */
+public class ValidationMessage {
+    private String fieldId;
+    private String message;
+    private String localizedMessageKey;
+    private Object[] localizedMessageParameters;
+
+    public ValidationMessage(String message) {
+        this.message = message;
+    }
+
+    public ValidationMessage(String message, String localizedMessageKey, Object... localizedMessageParameters) {
+        this.message = message;
+        this.localizedMessageKey = localizedMessageKey;
+        this.localizedMessageParameters = localizedMessageParameters;
+    }
+
+    public String getFieldId() {
+        return fieldId;
+    }
+
+    public void setFieldId(String fieldId) {
+        this.fieldId = fieldId;
+    }
+
+    public String getLocalizedMessageKey() {
+        return localizedMessageKey;
+    }
+
+    public void setLocalizedMessageKey(String localizedMessageKey) {
+        this.localizedMessageKey = localizedMessageKey;
+    }
+
+    public Object[] getLocalizedMessageParameters() {
+        return localizedMessageParameters;
+    }
+
+    public void setLocalizedMessageParameters(Object[] localizedMessageParameters) {
+        this.localizedMessageParameters = localizedMessageParameters;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public String getMessage(Properties localizedMessages) {
+        if (getLocalizedMessageKey() != null) {
+            return MessageFormat.format(localizedMessages.getProperty(getLocalizedMessageKey(), getMessage()), getLocalizedMessageParameters());
+        }
+        else {
+            return getMessage();
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        ValidationMessage message1 = (ValidationMessage) o;
+
+        if (getFieldId() != null ? !getFieldId().equals(message1.getFieldId()) : message1.getFieldId() != null)
+            return false;
+        return getMessage() != null ? getMessage().equals(message1.getMessage()) : message1.getMessage() == null;
+
+    }
+
+    @Override
+    public int hashCode() {
+        int result = getFieldId() != null ? getFieldId().hashCode() : 0;
+        result = 31 * result + (getMessage() != null ? getMessage().hashCode() : 0);
+        return result;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/services/validation/ValidationMessages.java b/services/src/main/java/org/keycloak/services/validation/ValidationMessages.java
new file mode 100644
index 0000000..e26ebff
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/validation/ValidationMessages.java
@@ -0,0 +1,83 @@
+/*
+ *
+ *  * 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.services.validation;
+
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+
+/**
+ * @author Vaclav Muzikar <vmuzikar@redhat.com>
+ */
+public class ValidationMessages {
+    private Set<ValidationMessage> messages = new LinkedHashSet<>();
+
+    public ValidationMessages() {}
+
+    public ValidationMessages(String... messages) {
+        for (String message : messages) {
+            add(message);
+        }
+    }
+
+    public void add(String message) {
+        messages.add(new ValidationMessage(message));
+    }
+
+    public void add(String message, String localizedMessageKey) {
+        messages.add(new ValidationMessage(message, localizedMessageKey));
+    }
+
+    public void add(String fieldId, String message, String localizedMessageKey) {
+        ValidationMessage validationMessage = new ValidationMessage(message, localizedMessageKey);
+        validationMessage.setFieldId(fieldId);
+        add(validationMessage);
+    }
+
+    public void add(ValidationMessage message) {
+        messages.add(message);
+    }
+
+    public boolean fieldHasError(String fieldId) {
+        for (ValidationMessage message : messages) {
+            if (message.getFieldId().equals(fieldId)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public Set<ValidationMessage> getMessages() {
+        return Collections.unmodifiableSet(messages);
+    }
+
+    protected String getStringMessages(Function<? super ValidationMessage, ? extends String> function) {
+        return messages.stream().map(function).collect(Collectors.joining("; "));
+    }
+
+    public String getStringMessages() {
+        return getStringMessages(ValidationMessage::getMessage);
+    }
+
+    public String getStringMessages(Properties localizedMessages) {
+        return getStringMessages(x -> x.getMessage(localizedMessages));
+    }
+}
diff --git a/services/src/main/java/org/keycloak/truststore/SSLSocketFactory.java b/services/src/main/java/org/keycloak/truststore/SSLSocketFactory.java
index fd1574a..f7b5a1d 100755
--- a/services/src/main/java/org/keycloak/truststore/SSLSocketFactory.java
+++ b/services/src/main/java/org/keycloak/truststore/SSLSocketFactory.java
@@ -29,7 +29,7 @@ import java.net.Socket;
  * <p>
  * This SSLSocketFactory can only use truststore configured by TruststoreProvider after the ProviderFactory was
  * initialized using standard Spi load / init mechanism. That will only happen if "truststore" provider is configured
- * in keycloak-server.json.
+ * in standalone.xml or domain.xml.
  * <p>
  * If TruststoreProvider is not available this SSLSocketFactory will delegate all operations to javax.net.ssl.SSLSocketFactory.getDefault().
  *
diff --git a/services/src/main/java/org/keycloak/utils/MediaType.java b/services/src/main/java/org/keycloak/utils/MediaType.java
index 31ab972..c34858d 100644
--- a/services/src/main/java/org/keycloak/utils/MediaType.java
+++ b/services/src/main/java/org/keycloak/utils/MediaType.java
@@ -31,4 +31,7 @@ public class MediaType {
     public static final String APPLICATION_FORM_URLENCODED = javax.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED;
     public static final javax.ws.rs.core.MediaType APPLICATION_FORM_URLENCODED_TYPE = javax.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED_TYPE;
 
+    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");
+
 }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/KeycloakServer.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/KeycloakServer.java
index 3b8d68b..8806514 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/KeycloakServer.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/KeycloakServer.java
@@ -309,6 +309,8 @@ public class KeycloakServer {
             di.setDeploymentName("Keycloak");
             di.setDefaultEncoding("UTF-8");
 
+            di.addInitParameter("keycloak.embedded", "true");
+
             di.setDefaultServletConfig(new DefaultServletConfig(true));
 
             ServletInfo restEasyDispatcher = Servlets.servlet("Keycloak REST Interface", HttpServlet30Dispatcher.class);
diff --git a/testsuite/integration/src/test/resources/META-INF/keycloak-server.json b/testsuite/integration/src/test/resources/META-INF/keycloak-server.json
index d3f87c9..b676b99 100755
--- a/testsuite/integration/src/test/resources/META-INF/keycloak-server.json
+++ b/testsuite/integration/src/test/resources/META-INF/keycloak-server.json
@@ -65,7 +65,8 @@
             "driverDialect": "${keycloak.connectionsJpa.driverDialect:}",
             "user": "${keycloak.connectionsJpa.user:sa}",
             "password": "${keycloak.connectionsJpa.password:}",
-            "databaseSchema": "${keycloak.connectionsJpa.databaseSchema:update}",
+            "initializeEmpty": true,
+            "migrationStrategy": "update",
             "showSql": "${keycloak.connectionsJpa.showSql:false}",
             "formatSql": "${keycloak.connectionsJpa.formatSql:true}",
             "globalStatsInterval": "${keycloak.connectionsJpa.globalStatsInterval:-1}"
diff --git a/testsuite/integration-arquillian/servers/auth-server/jboss/common/keycloak-server-subsystem.xsl b/testsuite/integration-arquillian/servers/auth-server/jboss/common/keycloak-server-subsystem.xsl
new file mode 100644
index 0000000..f32c036
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/jboss/common/keycloak-server-subsystem.xsl
@@ -0,0 +1,65 @@
+<!--
+~ 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.
+-->
+
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                xmlns:xalan="http://xml.apache.org/xalan"
+                version="2.0"
+                exclude-result-prefixes="xalan">
+
+    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
+    <xsl:strip-space elements="*"/>
+
+    <xsl:variable name="nsKS" select="'urn:jboss:domain:keycloak-server'"/>
+    <xsl:variable name="truststoreDefinition">
+        <spi name="truststore">
+            <provider name="file" enabled="true">
+                <properties>
+                    <property name="file" value="${{jboss.home.dir}}/standalone/configuration/keycloak.truststore"/>
+                    <property name="password" value="secret"/>
+                    <property name="hostname-verification-policy" value="WILDCARD"/>
+                    <property name="disabled" value="false"/>
+                </properties>
+            </provider>
+        </spi>
+    </xsl:variable>
+    
+    <!--inject provider-->
+    <xsl:template match="//*[local-name()='providers']/*[local-name()='provider']">
+        <xsl:copy>
+            <xsl:apply-templates select="@*|node()" />
+        </xsl:copy>
+        <provider>
+            <xsl:text>module:org.keycloak.testsuite.integration-arquillian-testsuite-providers</xsl:text>
+        </provider>
+    </xsl:template>
+    
+    <!--inject truststore-->
+    <xsl:template match="//*[local-name()='subsystem' and starts-with(namespace-uri(), $nsKS)]">
+        <xsl:copy>
+            <xsl:apply-templates select="@*|node()" />
+            <xsl:copy-of select="$truststoreDefinition"/>
+        </xsl:copy>
+    </xsl:template>
+
+    <!--copy everything else-->
+    <xsl:template match="@*|node()">
+        <xsl:copy>
+            <xsl:apply-templates select="@*|node()" />
+        </xsl:copy>
+    </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml b/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml
index 4fab304..eae0512 100644
--- a/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml
+++ b/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml
@@ -143,74 +143,6 @@
                         </executions>
                     </plugin>
                     <plugin>
-                        <groupId>org.apache.maven.plugins</groupId>
-                        <artifactId>maven-antrun-plugin</artifactId>
-                        <version>1.8</version>
-                        <executions>
-                            <execution>
-                                <id>inject-into-keycloak-server-json</id>
-                                <phase>process-resources</phase>
-                                <goals>
-                                    <goal>run</goal>
-                                </goals>
-                                <configuration>
-                                    <target>
-                                        <ant antfile="../build.xml" inheritRefs="true">
-                                            <target name="inject-provider"/>
-                                        </ant>
-                                    </target>
-                                </configuration>
-                            </execution>
-                            <execution>
-                                <id>inject-truststore-into-keycloak-server-json</id>
-                                <phase>process-resources</phase>
-                                <goals>
-                                    <goal>run</goal>
-                                </goals>
-                                <configuration>
-                                    <target>
-                                        <ant antfile="../build-truststore.xml" inheritRefs="true">
-                                            <target name="inject-truststore"/>
-                                        </ant>
-                                    </target>
-                                </configuration>
-                            </execution>
-                        </executions>
-                        <dependencies>
-                            <dependency>
-                                <groupId>ant-contrib</groupId>
-                                <artifactId>ant-contrib</artifactId>
-                                <version>1.0b3</version>
-                                <exclusions>
-                                    <exclusion>
-                                        <groupId>ant</groupId>
-                                        <artifactId>ant</artifactId>
-                                    </exclusion>
-                                </exclusions>
-                            </dependency>
-                            <dependency>
-                                <groupId>org.apache.ant</groupId>
-                                <artifactId>ant-apache-bsf</artifactId>
-                                <version>1.9.3</version>
-                            </dependency>
-                            <dependency>
-                                <groupId>org.apache.bsf</groupId>
-                                <artifactId>bsf-api</artifactId>
-                                <version>3.1</version>
-                            </dependency>
-                            <dependency>
-                                <groupId>rhino</groupId>
-                                <artifactId>js</artifactId>
-                                <version>1.7R2</version>
-                            </dependency>
-                            <dependency>
-                                <groupId>org.keycloak</groupId>
-                                <artifactId>keycloak-core</artifactId>
-                                <version>${project.version}</version>
-                            </dependency>
-                        </dependencies>
-                    </plugin>
-                    <plugin>
                         <artifactId>maven-enforcer-plugin</artifactId>
                     </plugin>
                     <plugin>
@@ -247,6 +179,25 @@
                                     </transformationSets>
                                 </configuration>
                             </execution>
+                            <execution>
+                                <id>inject-provider-and-truststore</id>
+                                <phase>process-resources</phase>
+                                <goals>
+                                    <goal>transform</goal>
+                                </goals>
+                                <configuration>
+                                    <transformationSets>
+                                        <transformationSet>
+                                            <dir>${auth.server.home}/standalone/configuration</dir>
+                                            <includes>
+                                                <include>standalone.xml</include>
+                                            </includes>
+                                            <stylesheet>${common.resources}/keycloak-server-subsystem.xsl</stylesheet>
+                                            <outputDir>${auth.server.home}/standalone/configuration</outputDir>
+                                        </transformationSet>
+                                    </transformationSets>
+                                </configuration>
+                            </execution>
                         </executions>
                     </plugin>
                     <plugin>
@@ -318,7 +269,6 @@
             </build>
         </profile>
 
-
         <!-- OPT-IN -->
         <profile>
             <id>server-overlay</id>
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/resource/TestingExportImportResource.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/resource/TestingExportImportResource.java
new file mode 100644
index 0000000..4f9151c
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/resource/TestingExportImportResource.java
@@ -0,0 +1,142 @@
+/*
+ * 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.testsuite.rest.resource;
+
+import java.io.File;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+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;
+
+import org.keycloak.exportimport.ExportImportManager;
+import org.keycloak.models.KeycloakSession;
+
+import static org.keycloak.exportimport.ExportImportConfig.ACTION;
+import static org.keycloak.exportimport.ExportImportConfig.DEFAULT_USERS_PER_FILE;
+import static org.keycloak.exportimport.ExportImportConfig.DIR;
+import static org.keycloak.exportimport.ExportImportConfig.FILE;
+import static org.keycloak.exportimport.ExportImportConfig.PROVIDER;
+import static org.keycloak.exportimport.ExportImportConfig.REALM_NAME;
+import static org.keycloak.exportimport.ExportImportConfig.USERS_PER_FILE;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class TestingExportImportResource {
+
+    private final KeycloakSession session;
+
+    public TestingExportImportResource(KeycloakSession session) {
+        this.session = session;
+    }
+
+    @GET
+    @Path("/run-import")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response runImport() {
+        new ExportImportManager(session).runImport();
+        return Response.ok().build();
+    }
+
+    @GET
+    @Path("/run-export")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response runExport() {
+        new ExportImportManager(session).runExport();
+        return Response.ok().build();
+    }
+
+    @GET
+    @Path("/get-users-per-file")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Integer getUsersPerFile() {
+        String usersPerFile = System.getProperty(USERS_PER_FILE, String.valueOf(DEFAULT_USERS_PER_FILE));
+        return Integer.parseInt(usersPerFile.trim());
+    }
+
+    @PUT
+    @Path("/set-users-per-file")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void setUsersPerFile(@QueryParam("usersPerFile") Integer usersPerFile) {
+        System.setProperty(USERS_PER_FILE, String.valueOf(usersPerFile));
+    }
+
+    @GET
+    @Path("/get-dir")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public String getDir() {
+        return System.getProperty(DIR);
+    }
+
+    @PUT
+    @Path("/set-dir")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public String setDir(@QueryParam("dir") String dir) {
+        return System.setProperty(DIR, dir);
+    }
+
+    @PUT
+    @Path("/export-import-provider")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void setProvider(@QueryParam("exportImportProvider") String exportImportProvider) {
+        System.setProperty(PROVIDER, exportImportProvider);
+    }
+
+    @PUT
+    @Path("/export-import-file")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void setFile(@QueryParam("file") String file) {
+        System.setProperty(FILE, file);
+    }
+
+    @PUT
+    @Path("/export-import-action")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void setAction(@QueryParam("exportImportAction") String exportImportAction) {
+        System.setProperty(ACTION, exportImportAction);
+    }
+
+    @PUT
+    @Path("/set-realm-name")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void setRealmName(@QueryParam("realmName") String realmName) {
+        if (realmName != null && !realmName.isEmpty()) {
+            System.setProperty(REALM_NAME, realmName);
+        } else {
+            System.getProperties().remove(REALM_NAME);
+        }
+    }
+
+    @GET
+    @Path("/get-test-dir")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public String getExportImportTestDirectory() {
+        System.setProperty("project.build.directory", "target");
+        String absolutePath = new File(System.getProperty("project.build.directory", "target")).getAbsolutePath();
+        return absolutePath;
+    }
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/resource/TestingOIDCEndpointsApplicationResource.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/resource/TestingOIDCEndpointsApplicationResource.java
new file mode 100644
index 0000000..6ea488f
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/resource/TestingOIDCEndpointsApplicationResource.java
@@ -0,0 +1,137 @@
+/*
+ * 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.testsuite.rest.resource;
+
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.jboss.resteasy.spi.BadRequestException;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.jose.jwk.JSONWebKeySet;
+import org.keycloak.jose.jwk.JWK;
+import org.keycloak.jose.jwk.JWKBuilder;
+import org.keycloak.jose.jws.Algorithm;
+import org.keycloak.jose.jws.JWSBuilder;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.testsuite.rest.TestApplicationResourceProviderFactory;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class TestingOIDCEndpointsApplicationResource {
+
+    public static final String PRIVATE_KEY = "privateKey";
+    public static final String PUBLIC_KEY = "publicKey";
+
+    private final TestApplicationResourceProviderFactory.OIDCClientData clientData;
+
+    public TestingOIDCEndpointsApplicationResource(TestApplicationResourceProviderFactory.OIDCClientData oidcClientData) {
+        this.clientData = oidcClientData;
+    }
+
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @Path("/generate-keys")
+    @NoCache
+    public Map<String, String> generateKeys() {
+        try {
+            KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
+            generator.initialize(2048);
+            clientData.setSigningKeyPair(generator.generateKeyPair());
+        } catch (NoSuchAlgorithmException e) {
+            throw new BadRequestException("Error generating signing keypair", e);
+        }
+
+        String privateKeyPem = KeycloakModelUtils.getPemFromKey(clientData.getSigningKeyPair().getPrivate());
+        String publicKeyPem = KeycloakModelUtils.getPemFromKey(clientData.getSigningKeyPair().getPublic());
+
+        Map<String, String> res = new HashMap<>();
+        res.put(PRIVATE_KEY, privateKeyPem);
+        res.put(PUBLIC_KEY, publicKeyPem);
+        return res;
+    }
+
+
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @Path("/get-jwks")
+    @NoCache
+    public JSONWebKeySet getJwks() {
+        JSONWebKeySet keySet = new JSONWebKeySet();
+
+        if (clientData.getSigningKeyPair() == null) {
+            keySet.setKeys(new JWK[] {});
+        } else {
+            keySet.setKeys(new JWK[] { JWKBuilder.create().rs256(clientData.getSigningKeyPair().getPublic()) });
+        }
+
+        return keySet;
+    }
+
+
+    @GET
+    @Path("/set-oidc-request")
+    @Produces(org.keycloak.utils.MediaType.APPLICATION_JWT)
+    @NoCache
+    public void setOIDCRequest(@QueryParam("realmName") String realmName, @QueryParam("clientId") String clientId,
+                               @QueryParam("redirectUri") String redirectUri, @QueryParam("maxAge") String maxAge,
+                               @QueryParam("jwaAlgorithm") String jwaAlgorithm) {
+        Map<String, Object> oidcRequest = new HashMap<>();
+        oidcRequest.put(OIDCLoginProtocol.CLIENT_ID_PARAM, clientId);
+        oidcRequest.put(OIDCLoginProtocol.RESPONSE_TYPE_PARAM, OAuth2Constants.CODE);
+        oidcRequest.put(OIDCLoginProtocol.REDIRECT_URI_PARAM, redirectUri);
+        if (maxAge != null) {
+            oidcRequest.put(OIDCLoginProtocol.MAX_AGE_PARAM, Integer.parseInt(maxAge));
+        }
+
+        Algorithm alg = Enum.valueOf(Algorithm.class, jwaAlgorithm);
+        if (alg == Algorithm.none) {
+            clientData.setOidcRequest(new JWSBuilder().jsonContent(oidcRequest).none());
+        } else if (alg == Algorithm.RS256) {
+            if (clientData.getSigningKeyPair() == null) {
+                throw new BadRequestException("Requested RS256, but signing key not set");
+            }
+
+            PrivateKey privateKey = clientData.getSigningKeyPair().getPrivate();
+            clientData.setOidcRequest(new JWSBuilder().jsonContent(oidcRequest).rsa256(privateKey));
+        } else {
+            throw new BadRequestException("Unknown argument: " + jwaAlgorithm);
+        }
+    }
+
+
+    @GET
+    @Path("/get-oidc-request")
+    @Produces(org.keycloak.utils.MediaType.APPLICATION_JWT)
+    @NoCache
+    public String getOIDCRequest() {
+        return clientData.getOidcRequest();
+    }
+}
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 bc63c99..5f392a0 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
@@ -27,6 +27,8 @@ import org.keycloak.representations.adapters.action.PushNotBeforeAction;
 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.TestingExportImportResource;
+import org.keycloak.testsuite.rest.resource.TestingOIDCEndpointsApplicationResource;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.GET;
@@ -53,14 +55,16 @@ public class TestApplicationResourceProvider implements RealmResourceProvider {
     private final BlockingQueue<LogoutAction> adminLogoutActions;
     private final BlockingQueue<PushNotBeforeAction> adminPushNotBeforeActions;
     private final BlockingQueue<TestAvailabilityAction> adminTestAvailabilityAction;
+    private final TestApplicationResourceProviderFactory.OIDCClientData oidcClientData;
 
     public TestApplicationResourceProvider(KeycloakSession session, BlockingQueue<LogoutAction> adminLogoutActions,
             BlockingQueue<PushNotBeforeAction> adminPushNotBeforeActions,
-            BlockingQueue<TestAvailabilityAction> adminTestAvailabilityAction) {
+            BlockingQueue<TestAvailabilityAction> adminTestAvailabilityAction, TestApplicationResourceProviderFactory.OIDCClientData oidcClientData) {
         this.session = session;
         this.adminLogoutActions = adminLogoutActions;
         this.adminPushNotBeforeActions = adminPushNotBeforeActions;
         this.adminTestAvailabilityAction = adminTestAvailabilityAction;
+        this.oidcClientData = oidcClientData;
     }
 
     @POST
@@ -164,6 +168,11 @@ public class TestApplicationResourceProvider implements RealmResourceProvider {
         return sb.toString();
     }
 
+    @Path("/oidc-client-endpoints")
+    public TestingOIDCEndpointsApplicationResource getTestingOIDCClientEndpoints() {
+        return new TestingOIDCEndpointsApplicationResource(oidcClientData);
+    }
+
     @Override
     public Object getResource() {
         return this;
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProviderFactory.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProviderFactory.java
index 98ca2ba..6bd7dc2 100644
--- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProviderFactory.java
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProviderFactory.java
@@ -28,6 +28,7 @@ import org.keycloak.representations.adapters.action.TestAvailabilityAction;
 import org.keycloak.services.resource.RealmResourceProvider;
 import org.keycloak.services.resource.RealmResourceProviderFactory;
 
+import java.security.KeyPair;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingDeque;
 
@@ -40,9 +41,11 @@ public class TestApplicationResourceProviderFactory implements RealmResourceProv
     private BlockingQueue<PushNotBeforeAction> pushNotBeforeActions = new LinkedBlockingDeque<>();
     private BlockingQueue<TestAvailabilityAction> testAvailabilityActions = new LinkedBlockingDeque<>();
 
+    private final OIDCClientData oidcClientData = new OIDCClientData();
+
     @Override
     public RealmResourceProvider create(KeycloakSession session) {
-        return new TestApplicationResourceProvider(session, adminLogoutActions, pushNotBeforeActions, testAvailabilityActions);
+        return new TestApplicationResourceProvider(session, adminLogoutActions, pushNotBeforeActions, testAvailabilityActions, oidcClientData);
     }
 
     @Override
@@ -62,4 +65,26 @@ public class TestApplicationResourceProviderFactory implements RealmResourceProv
         return "app";
     }
 
+
+    public static class OIDCClientData {
+
+        private KeyPair signingKeyPair;
+        private String oidcRequest;
+
+        public KeyPair getSigningKeyPair() {
+            return signingKeyPair;
+        }
+
+        public void setSigningKeyPair(KeyPair signingKeyPair) {
+            this.signingKeyPair = signingKeyPair;
+        }
+
+        public String getOidcRequest() {
+            return oidcRequest;
+        }
+
+        public void setOidcRequest(String oidcRequest) {
+            this.oidcRequest = oidcRequest;
+        }
+    }
 }
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java
index b658d35..2085cfa 100644
--- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java
@@ -23,15 +23,20 @@ import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Date;
 import org.infinispan.Cache;
+import org.keycloak.OAuth2Constants;
 import org.keycloak.common.util.Time;
 import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
 import org.keycloak.events.Event;
 import org.keycloak.events.admin.AdminEvent;
 import org.keycloak.events.admin.ResourceType;
+import org.keycloak.jose.jws.Algorithm;
+import org.keycloak.jose.jws.JWSBuilder;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.protocol.oidc.endpoints.request.AuthorizationEndpointRequest;
 import org.keycloak.representations.idm.AdminEventRepresentation;
 import org.keycloak.representations.idm.EventRepresentation;
 import org.keycloak.services.managers.ClientSessionCode;
@@ -76,6 +81,7 @@ import org.keycloak.models.UserProvider;
 import org.keycloak.representations.idm.AuthDetailsRepresentation;
 import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
 import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.testsuite.rest.resource.TestingExportImportResource;
 
 import static org.keycloak.exportimport.ExportImportConfig.*;
 
@@ -565,22 +571,6 @@ public class TestingResourceProvider implements RealmResourceProvider {
     }
 
     @GET
-    @Path("/run-import")
-    @Produces(MediaType.APPLICATION_JSON)
-    public Response runImport() {
-        new ExportImportManager(session).runImport();
-        return Response.ok().build();
-    }
-
-    @GET
-    @Path("/run-export")
-    @Produces(MediaType.APPLICATION_JSON)
-    public Response runExport() {
-        new ExportImportManager(session).runExport();
-        return Response.ok().build();
-    }
-
-    @GET
     @Path("/valid-credentials")
     @Produces(MediaType.APPLICATION_JSON)
     public boolean validCredentials(@QueryParam("realmName") String realmName, @QueryParam("userName") String userName, @QueryParam("password") String password) {
@@ -647,83 +637,14 @@ public class TestingResourceProvider implements RealmResourceProvider {
         return ModelToRepresentation.toRepresentation(user);
     }
 
+    @Path("/export-import")
+    public TestingExportImportResource getExportImportResource() {
+        return new TestingExportImportResource(session);
+    }
+
     private RealmModel getRealmByName(String realmName) {
         RealmProvider realmProvider = session.getProvider(RealmProvider.class);
         return realmProvider.getRealmByName(realmName);
     }
 
-    @GET
-    @Path("/get-users-per-file")
-    @Consumes(MediaType.APPLICATION_JSON)
-    @Produces(MediaType.APPLICATION_JSON)
-    public Integer getUsersPerFile() {
-        String usersPerFile = System.getProperty(USERS_PER_FILE, String.valueOf(DEFAULT_USERS_PER_FILE));
-        return Integer.parseInt(usersPerFile.trim());
-    }
-
-    @PUT
-    @Path("/set-users-per-file")
-    @Consumes(MediaType.APPLICATION_JSON)
-    public void setUsersPerFile(@QueryParam("usersPerFile") Integer usersPerFile) {
-        System.setProperty(USERS_PER_FILE, String.valueOf(usersPerFile));
-    }
-
-    @GET
-    @Path("/get-dir")
-    @Consumes(MediaType.APPLICATION_JSON)
-    @Produces(MediaType.APPLICATION_JSON)
-    public String getDir() {
-        return System.getProperty(DIR);
-    }
-
-    @PUT
-    @Path("/set-dir")
-    @Consumes(MediaType.APPLICATION_JSON)
-    @Produces(MediaType.APPLICATION_JSON)
-    public String setDir(@QueryParam("dir") String dir) {
-        return System.setProperty(DIR, dir);
-    }
-
-    @PUT
-    @Path("/export-import-provider")
-    @Consumes(MediaType.APPLICATION_JSON)
-    public void setProvider(@QueryParam("exportImportProvider") String exportImportProvider) {
-        System.setProperty(PROVIDER, exportImportProvider);
-    }
-
-    @PUT
-    @Path("/export-import-file")
-    @Consumes(MediaType.APPLICATION_JSON)
-    public void setFile(@QueryParam("file") String file) {
-        System.setProperty(FILE, file);
-    }
-
-    @PUT
-    @Path("/export-import-action")
-    @Consumes(MediaType.APPLICATION_JSON)
-    public void setAction(@QueryParam("exportImportAction") String exportImportAction) {
-        System.setProperty(ACTION, exportImportAction);
-    }
-
-    @PUT
-    @Path("/set-realm-name")
-    @Consumes(MediaType.APPLICATION_JSON)
-    public void setRealmName(@QueryParam("realmName") String realmName) {
-        if (realmName != null && !realmName.isEmpty()) {
-            System.setProperty(REALM_NAME, realmName);
-        } else {
-            System.getProperties().remove(REALM_NAME);
-        }
-    }
-
-    @GET
-    @Path("/get-test-dir")
-    @Consumes(MediaType.APPLICATION_JSON)
-    @Produces(MediaType.APPLICATION_JSON)
-    public String getExportImportTestDirectory() {
-        System.setProperty("project.build.directory", "target");
-        String absolutePath = new File(System.getProperty("project.build.directory", "target")).getAbsolutePath();
-        return absolutePath;
-    }
-
 }
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/pom.xml b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/pom.xml
index 7885857..f5e2d4c 100755
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/pom.xml
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/pom.xml
@@ -40,6 +40,12 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <groupId>javax.enterprise</groupId>
+            <artifactId>cdi-api</artifactId>
+            <version>1.0-SP4</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <groupId>org.keycloak</groupId>
             <artifactId>keycloak-authz-client</artifactId>
             <version>${project.version}</version>
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/admin/AdminAlbumService.java b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/admin/AdminAlbumService.java
index b349e02..428ba07 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/admin/AdminAlbumService.java
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/admin/AdminAlbumService.java
@@ -19,9 +19,8 @@ package org.keycloak.example.photoz.admin;
 
 import org.keycloak.example.photoz.entity.Album;
 
-import javax.ejb.Stateless;
+import javax.inject.Inject;
 import javax.persistence.EntityManager;
-import javax.persistence.PersistenceContext;
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
@@ -36,12 +35,11 @@ import java.util.List;
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
 @Path("/admin/album")
-@Stateless
 public class AdminAlbumService {
 
     public static final String SCOPE_ADMIN_ALBUM_MANAGE = "urn:photoz.com:scopes:album:admin:manage";
 
-    @PersistenceContext
+    @Inject
     private EntityManager entityManager;
 
     @Context
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java
index a5d7f16..d0e9c2d 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java
@@ -7,10 +7,12 @@ import org.keycloak.authorization.client.representation.ScopeRepresentation;
 import org.keycloak.authorization.client.resource.ProtectionResource;
 import org.keycloak.example.photoz.ErrorResponse;
 import org.keycloak.example.photoz.entity.Album;
+import org.keycloak.example.photoz.util.Transaction;
 import org.keycloak.representations.adapters.config.AdapterConfig;
 import org.keycloak.util.JsonSerialization;
 
 import javax.ejb.Stateless;
+import javax.inject.Inject;
 import javax.persistence.EntityManager;
 import javax.persistence.PersistenceContext;
 import javax.persistence.Query;
@@ -31,14 +33,14 @@ import java.util.List;
 import java.util.Set;
 
 @Path("/album")
-@Stateless
+@Transaction
 public class AlbumService {
 
     public static final String SCOPE_ALBUM_VIEW = "urn:photoz.com:scopes:album:view";
     public static final String SCOPE_ALBUM_CREATE = "urn:photoz.com:scopes:album:create";
     public static final String SCOPE_ALBUM_DELETE = "urn:photoz.com:scopes:album:delete";
 
-    @PersistenceContext
+    @Inject
     private EntityManager entityManager;
 
     @Context
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/ProfileService.java b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/ProfileService.java
index be638b6..f7b55cb 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/ProfileService.java
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/ProfileService.java
@@ -18,6 +18,7 @@
 package org.keycloak.example.photoz.album;
 
 import javax.ejb.Stateless;
+import javax.inject.Inject;
 import javax.persistence.EntityManager;
 import javax.persistence.PersistenceContext;
 import javax.servlet.http.HttpServletRequest;
@@ -34,12 +35,11 @@ import java.util.List;
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
 @Path("/profile")
-@Stateless
 public class ProfileService {
 
     private static final String PROFILE_VIEW = "urn:photoz.com:scopes:profile:view";
 
-    @PersistenceContext
+    @Inject
     private EntityManager entityManager;
 
     @GET
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/util/Resources.java b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/util/Resources.java
new file mode 100644
index 0000000..c917da2
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/util/Resources.java
@@ -0,0 +1,51 @@
+/*
+ * 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.example.photoz.util;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.context.RequestScoped;
+import javax.enterprise.inject.Produces;
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.Persistence;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+@ApplicationScoped
+public class Resources {
+
+    private EntityManagerFactory entityManagerFactory;
+
+    @PostConstruct
+    public void init() {
+        entityManagerFactory = Persistence.createEntityManagerFactory("primary");
+    }
+
+    @PreDestroy
+    public void dispose() {
+        entityManagerFactory.close();
+    }
+
+    @RequestScoped
+    @Produces
+    public EntityManager createEntityManager() {
+        return entityManagerFactory.createEntityManager();
+    }
+}
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/util/Transaction.java b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/util/Transaction.java
new file mode 100644
index 0000000..a3caa78
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/util/Transaction.java
@@ -0,0 +1,34 @@
+/*
+ * 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.example.photoz.util;
+
+import javax.interceptor.InterceptorBinding;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+@InterceptorBinding
+@Target({ TYPE })
+@Retention(RUNTIME)
+public @interface Transaction {
+}
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/util/TransactionInterceptor.java b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/util/TransactionInterceptor.java
new file mode 100644
index 0000000..36d35f3
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/util/TransactionInterceptor.java
@@ -0,0 +1,56 @@
+/*
+ * 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.example.photoz.util;
+
+import javax.enterprise.inject.Instance;
+import javax.inject.Inject;
+import javax.interceptor.AroundInvoke;
+import javax.interceptor.Interceptor;
+import javax.interceptor.InvocationContext;
+import javax.persistence.EntityManager;
+import javax.persistence.EntityTransaction;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+@Interceptor
+@Transaction
+public class TransactionInterceptor {
+
+    @Inject
+    private Instance<EntityManager> entityManager;
+
+    @AroundInvoke
+    public Object aroundInvoke(InvocationContext context) {
+        EntityManager entityManager = this.entityManager.get();
+        EntityTransaction transaction = entityManager.getTransaction();
+
+        try {
+            transaction.begin();
+            Object proceed = context.proceed();
+            transaction.commit();
+            return proceed;
+        } catch (Exception cause) {
+            if (transaction != null && transaction.isActive()) {
+                transaction.rollback();
+            }
+            throw new RuntimeException(cause);
+        } finally {
+            entityManager.close();
+        }
+    }
+}
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/resources/META-INF/beans.xml b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/resources/META-INF/beans.xml
index 957dc8a..fbf2a32 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/resources/META-INF/beans.xml
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/resources/META-INF/beans.xml
@@ -3,5 +3,7 @@
    xsi:schemaLocation="
         http://java.sun.com/xml/ns/javaee 
         http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
-        
+    <interceptors>
+        <class>org.keycloak.example.photoz.util.TransactionInterceptor</class>
+    </interceptors>
 </beans>
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/resources/META-INF/persistence.xml b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/resources/META-INF/persistence.xml
index 9323182..8b6d226 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/resources/META-INF/persistence.xml
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/resources/META-INF/persistence.xml
@@ -4,14 +4,18 @@
 	xsi:schemaLocation="
         http://java.sun.com/xml/ns/persistence
         http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
-	<persistence-unit name="primary">
-		<non-jta-data-source>java:jboss/datasources/PhotozDS</non-jta-data-source>
+	<persistence-unit name="primary" transaction-type="RESOURCE_LOCAL">
+		<provider>org.hibernate.ejb.HibernatePersistence</provider>
 
 		<class>org.keycloak.example.photoz.entity.Album</class>
 		<class>org.keycloak.example.photoz.entity.Photo</class>
 
 		<properties>
-			<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
+			<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" />
+			<property name="hibernate.connection.driver_class" value="org.h2.Driver" />
+			<property name="hibernate.connection.url" value="jdbc:h2:mem:test-keycloak-photoz-example" />
+			<property name="hibernate.connection.user" value="sa" />
+			<property name="hibernate.flushMode" value="FLUSH_AUTO" />
 			<property name="hibernate.hbm2ddl.auto" value="update" />
 			<property name="hibernate.show_sql" value="false" />
 		</properties>
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/META-INF/jboss-deployment-structure.xml b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/META-INF/jboss-deployment-structure.xml
index 4b23be6..4553358 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/META-INF/jboss-deployment-structure.xml
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/META-INF/jboss-deployment-structure.xml
@@ -20,6 +20,7 @@
     <deployment>
         <dependencies>
             <module name="org.keycloak.keycloak-authz-client" services="import"/>
+            <module name="com.h2database.h2" services="import"/>
         </dependencies>
     </deployment>
 </jboss-deployment-structure>
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/BadAssertionSalesPostSig.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/BadAssertionSalesPostSig.java
new file mode 100644
index 0000000..8d10b22
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/BadAssertionSalesPostSig.java
@@ -0,0 +1,39 @@
+/*
+ * 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.testsuite.adapter.page;
+
+import org.jboss.arquillian.container.test.api.OperateOnDeployment;
+import org.jboss.arquillian.test.api.ArquillianResource;
+
+import java.net.URL;
+
+/**
+ * @author mhajas
+ */
+public class BadAssertionSalesPostSig extends SAMLServlet {
+    public static final String DEPLOYMENT_NAME = "bad-assertion-sales-post-sig";
+
+    @ArquillianResource
+    @OperateOnDeployment(DEPLOYMENT_NAME)
+    private URL url;
+
+    @Override
+    public URL getInjectedUrl() {
+        return url;
+    }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/EmployeeServlet.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/EmployeeServlet.java
new file mode 100644
index 0000000..c8a0d66
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/EmployeeServlet.java
@@ -0,0 +1,39 @@
+/*
+ * 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.testsuite.adapter.page;
+
+import org.jboss.arquillian.container.test.api.OperateOnDeployment;
+import org.jboss.arquillian.test.api.ArquillianResource;
+
+import java.net.URL;
+
+/**
+ * @author mhajas
+ */
+public class EmployeeServlet extends SAMLServlet {
+    public static final String DEPLOYMENT_NAME = "employee";
+
+    @ArquillianResource
+    @OperateOnDeployment(DEPLOYMENT_NAME)
+    private URL url;
+
+    @Override
+    public URL getInjectedUrl() {
+        return url;
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/InputPortal.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/InputPortal.java
index 6e66e2c..7edc5c5 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/InputPortal.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/InputPortal.java
@@ -17,18 +17,18 @@
 
 package org.keycloak.testsuite.adapter.page;
 
-import java.net.URL;
 import org.jboss.arquillian.container.test.api.OperateOnDeployment;
 import org.jboss.arquillian.test.api.ArquillianResource;
-import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
 import org.openqa.selenium.WebElement;
 import org.openqa.selenium.support.FindBy;
 
+import java.net.URL;
+
 /**
  *
  * @author tkyjovsk
  */
-public class InputPortal extends AbstractPageWithInjectedUrl {
+public class InputPortal extends SAMLServlet {
 
     public static final String DEPLOYMENT_NAME = "input-portal";
 
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/MissingAssertionSig.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/MissingAssertionSig.java
new file mode 100644
index 0000000..78ff38c
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/MissingAssertionSig.java
@@ -0,0 +1,39 @@
+/*
+ * 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.testsuite.adapter.page;
+
+import org.jboss.arquillian.container.test.api.OperateOnDeployment;
+import org.jboss.arquillian.test.api.ArquillianResource;
+
+import java.net.URL;
+
+/**
+ * @author mhajas
+ */
+public class MissingAssertionSig extends SAMLServlet {
+    public static final String DEPLOYMENT_NAME = "missing-assertion-sig";
+
+    @ArquillianResource
+    @OperateOnDeployment(DEPLOYMENT_NAME)
+    private URL url;
+
+    @Override
+    public URL getInjectedUrl() {
+        return url;
+    }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SalesPost2Servlet.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SalesPost2Servlet.java
new file mode 100644
index 0000000..d821d86
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SalesPost2Servlet.java
@@ -0,0 +1,39 @@
+/*
+ * 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.testsuite.adapter.page;
+
+import org.jboss.arquillian.container.test.api.OperateOnDeployment;
+import org.jboss.arquillian.test.api.ArquillianResource;
+
+import java.net.URL;
+
+/**
+ * @author mhajas
+ */
+public class SalesPost2Servlet extends SAMLServlet {
+    public static final String DEPLOYMENT_NAME = "sales-post2";
+
+    @ArquillianResource
+    @OperateOnDeployment(DEPLOYMENT_NAME)
+    private URL url;
+
+    @Override
+    public URL getInjectedUrl() {
+        return url;
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SalesPostAssertionAndResponseSig.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SalesPostAssertionAndResponseSig.java
new file mode 100644
index 0000000..a4522b4
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SalesPostAssertionAndResponseSig.java
@@ -0,0 +1,39 @@
+/*
+ * 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.testsuite.adapter.page;
+
+import org.jboss.arquillian.container.test.api.OperateOnDeployment;
+import org.jboss.arquillian.test.api.ArquillianResource;
+
+import java.net.URL;
+
+/**
+ * @author mhajas
+ */
+public class SalesPostAssertionAndResponseSig extends SAMLServlet {
+    public static final String DEPLOYMENT_NAME = "sales-post-assertion-and-response-sig";
+
+    @ArquillianResource
+    @OperateOnDeployment(DEPLOYMENT_NAME)
+    private URL url;
+
+    @Override
+    public URL getInjectedUrl() {
+        return url;
+    }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SAMLServlet.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SAMLServlet.java
index fe856dc..cc4a419 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SAMLServlet.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SAMLServlet.java
@@ -40,8 +40,8 @@ public abstract class SAMLServlet extends AbstractPageWithInjectedUrl {
         }
     }
 
-    public void checkRolesEndPoint() {
-        driver.navigate().to(getUriBuilder().build().toASCIIString() + "/checkRoles");
+    public void checkRolesEndPoint(boolean value) {
+        driver.navigate().to(getUriBuilder().build().toASCIIString() + "/" + (value ? "" : "un") + "checkRoles");
         pause(300);
     }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/InputServlet.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/InputServlet.java
index a2038bf..5fb04e9 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/InputServlet.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/InputServlet.java
@@ -17,6 +17,8 @@
 
 package org.keycloak.testsuite.adapter.servlet;
 
+import org.junit.Assert;
+
 import javax.servlet.ServletException;
 import javax.servlet.annotation.WebServlet;
 import javax.servlet.http.HttpServlet;
@@ -31,6 +33,8 @@ import java.io.PrintWriter;
 @WebServlet("/input-portal")
 public class InputServlet extends HttpServlet {
 
+    private static final String FORM_URLENCODED = "application/x-www-form-urlencoded";
+
     @Override
     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         String appBase;
@@ -41,6 +45,16 @@ public class InputServlet extends HttpServlet {
         }
         String actionUrl = appBase + "/input-portal/secured/post";
 
+        if (req.getRequestURI().endsWith("insecure")) {
+            if (System.getProperty("insecure.user.principal.unsupported") == null) Assert.assertNotNull(req.getUserPrincipal());
+            resp.setContentType("text/html");
+            PrintWriter pw = resp.getWriter();
+            pw.printf("<html><head><title>Input Servlet</title></head><body>%s\n", "Insecure Page");
+            if (req.getUserPrincipal() != null) pw.printf("UserPrincipal: " + req.getUserPrincipal().getName());
+            pw.print("</body></html>");
+            pw.flush();
+            return;
+        }
 
         resp.setContentType("text/html");
         PrintWriter pw = resp.getWriter();
@@ -56,6 +70,16 @@ public class InputServlet extends HttpServlet {
 
     @Override
     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        if (!FORM_URLENCODED.equals(req.getContentType())) {
+            resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+            PrintWriter pw = resp.getWriter();
+            resp.setContentType("text/plain");
+            pw.printf("Expecting content type " + FORM_URLENCODED +
+                    ", received " + req.getContentType() + " instead");
+            pw.flush();
+            return;
+        }
+
         resp.setContentType("text/plain");
         PrintWriter pw = resp.getWriter();
         pw.printf("parameter="+req.getParameter("parameter"));
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/SamlSPFacade.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/SamlSPFacade.java
new file mode 100755
index 0000000..21f07b3
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/SamlSPFacade.java
@@ -0,0 +1,69 @@
+/*
+ * 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.testsuite.adapter.servlet;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.UriBuilder;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+* @version $Revision: 1 $
+*/
+public class SamlSPFacade extends HttpServlet {
+    public static String samlResponse;
+    public static String RELAY_STATE = "http://test.com/foo/bar";
+    public static String sentRelayState;
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        handler(req, resp);
+    }
+
+    @Override
+    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        handler(req, resp);
+    }
+
+    private void handler(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        System.out.println("In SamlSPFacade Servlet handler()");
+        if (req.getParameterMap().isEmpty()) {
+            System.out.println("ParameterMap is empty, redirecting to keycloak server ");
+            resp.setStatus(302);
+            // Redirect
+            // UriBuilder builder = UriBuilder.fromUri("http://localhost:8081/auth/realms/demo/protocol/saml?SAMLRequest=jVLRTsIwFP2Vpe%2BjG4wxG0YyWYxL0BBAH3wx3XYnTbp29nYof%2B8YEvEBNOlD03vOveec2ynyWjYsae1WreC9BbTOZy0Vsr4Qk9YopjkKZIrXgMwWbJ08LNhw4LHGaKsLLcmRch3MEcFYoRVxktN1rhW2NZg1mJ0o4Gm1iMnW2oZRKnXB5VajZZEX%2BRTqRuo9ACVO2mkUih%2F4l9C8s0MNcFkjLaHW9KSUHlwR506bAnrPMam4RCBOlsYkS1%2BD3MvLcDJxAx9KN4jCkXszrG5cP%2BCVH4y8IM8PYFx2dsQOfuiILWQKLVc2JkPPH7te6HrRxh%2BzUdidwSSIXoiz%2FBZyK1Qp1Nv1yPIjCNn9ZrN0V1AKA4UlzjMY7N13IDKbHjyxXoA5291%2FtzH7I%2FApPet%2FHNawx65hli61FMXeSaTUH%2FMubtvlYU0LfcA1t5cl%2BAO%2FfxGlW%2FVQ1ipsoBCVgJLQ2XHo7385%2BwI%3D");
+            UriBuilder builder = UriBuilder.fromUri("http://localhost:8180/auth/realms/demo/protocol/saml?SAMLRequest=jZJdS8MwFIbvBf9DyX2XNG62hnUwHeLAj7JNL7yRmJ65QJrUnNSPf29WHQp%2BIOQiJM%2FJed%2F3ZIyyMa2YdmFjF%2FDYAYbkpTEWRX9Rks5b4SRqFFY2gCIosZxenAs%2BYKL1LjjlDHkv%2BRuWiOCDdpYk0932xFnsGvBL8E9awfXivCSbEFpBqXFKmo3DIApeMApNa9wrACXJLGrUVm7rf6KzSMtoh3qQpkFaQ%2BPoTinduiLJqfMKes8lWUuDQJL5rCTz2d2wLmCkgKc5Z4fpMOf3qSyO8pTXxUHOjphibBRhrKId%2FQSf5YgdzC0GaUNJOMtGKTtI2eGKcxFXlg%2BK0fCWJNWHkGNta20f%2Fo7s%2Fh1CcbZaVWl1tVyR5AY89s4jQCb7e%2BOtI9G3918m999ZTL4HyIrsM%2B4x%2FfL%2Brl0rLuOT81nljFavydQY93wS4w4xj%2BA76ANuZPhdRDbI%2BhNdp%2BseFZ3FFpRea6gJ3Tai33%2Fm5A0%3D");
+            builder.queryParam("RelayState", RELAY_STATE);
+            resp.setHeader("Location", builder.build().toString());
+            return;
+        }
+
+        System.out.println("Response was received");
+        samlResponse = req.getParameter("SAMLResponse");
+        sentRelayState = req.getParameter("RelayState");
+
+        PrintWriter pw = resp.getWriter();
+        pw.println("Relay state: " + sentRelayState);
+        pw.println("SAML response: " + samlResponse);
+        pw.flush();
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/SendUsernameServlet.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/SendUsernameServlet.java
index 58feae3..6e70f15 100755
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/SendUsernameServlet.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/SendUsernameServlet.java
@@ -19,6 +19,10 @@ package org.keycloak.testsuite.adapter.servlet;
 
 
 import org.jboss.resteasy.annotations.cache.NoCache;
+import org.keycloak.adapters.saml.SamlAuthenticationError;
+import org.keycloak.adapters.saml.SamlPrincipal;
+import org.keycloak.adapters.spi.AuthenticationError;
+import org.keycloak.saml.processing.core.saml.v2.constants.X500SAMLProfileConstants;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
@@ -28,6 +32,9 @@ import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import java.io.IOException;
 import java.security.Principal;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -38,6 +45,9 @@ import java.security.Principal;
 public class SendUsernameServlet {
 
     private static boolean checkRoles = false;
+    private static SamlAuthenticationError authError;
+    private static Principal sentPrincipal;
+    private static List<String> checkRolesList = Collections.singletonList("manager");
 
     @Context
     private HttpServletRequest httpServletRequest;
@@ -62,7 +72,15 @@ public class SendUsernameServlet {
             throw new RuntimeException("User: " + httpServletRequest.getUserPrincipal() + " do not have required role");
         }
 
-        return Response.ok(getOutput(), MediaType.TEXT_PLAIN).build();
+        return Response.ok(getOutput(), MediaType.TEXT_HTML_TYPE).build();
+    }
+
+    @GET
+    @Path("getAttributes")
+    public Response getSentPrincipal() throws IOException {
+        System.out.println("In SendUsername Servlet getSentPrincipal()");
+
+        return Response.ok(getAttributes(), MediaType.TEXT_HTML_TYPE).build();
     }
 
     @GET
@@ -79,6 +97,23 @@ public class SendUsernameServlet {
         return doPost(checkRolesFlag);
     }
 
+    @POST
+    @Path("error.html")
+    public Response errorPagePost() {
+        authError = (SamlAuthenticationError) httpServletRequest.getAttribute(AuthenticationError.class.getName());
+        Integer statusCode = (Integer) httpServletRequest.getAttribute("javax.servlet.error.status_code");
+        System.out.println("In SendUsername Servlet errorPage() status code: " + statusCode);
+
+        return Response.ok(getErrorOutput(statusCode), MediaType.TEXT_HTML_TYPE).build();
+    }
+
+    @GET
+    @Path("error.html")
+    public Response errorPageGet() {
+        return errorPagePost();
+    }
+
+
     @GET
     @Path("checkRoles")
     public String checkRolesEndPoint() {
@@ -87,8 +122,35 @@ public class SendUsernameServlet {
         return "Roles will be checked";
     }
 
+    @GET
+    @Path("uncheckRoles")
+    public String uncheckRolesEndPoint() {
+        checkRoles = false;
+        System.out.println("Setting checkRoles to false");
+        checkRolesList = Collections.singletonList("manager");
+        return "Roles will not be checked";
+    }
+
+    @GET
+    @Path("setCheckRoles")
+    public String setCheckRoles(@QueryParam("roles") String roles) {
+        checkRolesList = Arrays.asList(roles.split(","));
+        checkRoles = true;
+        System.out.println("Setting checkRolesList to " + checkRolesList.toString());
+        return "These roles will be checked: " + checkRolesList.toString();
+    }
+
+
     private boolean checkRoles() {
-        return httpServletRequest.isUserInRole("manager");
+        for (String role : checkRolesList) {
+            System.out.println("In checkRoles() checking role " + role + " for user " + httpServletRequest.getUserPrincipal().getName());
+            if (!httpServletRequest.isUserInRole(role)) {
+                System.out.println("User is not in role " + role);
+                return false;
+            }
+        }
+
+        return true;
     }
 
     private String getOutput() {
@@ -102,6 +164,31 @@ public class SendUsernameServlet {
             return output + "null";
         }
 
+        sentPrincipal = principal;
+
         return output + principal.getName();
     }
+
+    private String getErrorOutput(Integer statusCode) {
+        String output = "<html><head><title>Error Page</title></head><body><h1>There was an error</h1>";
+        if (statusCode != null)
+            output += "<br/>HTTP status code: " + statusCode;
+        if (authError != null)
+            output += "<br/>Error info: " + authError.toString();
+        return output + "</body></html>";
+    }
+
+    private String getAttributes() {
+        SamlPrincipal principal = (SamlPrincipal) sentPrincipal;
+        String output = "attribute email: " + principal.getAttribute(X500SAMLProfileConstants.EMAIL.get());
+        output += "<br /> topAttribute: " + principal.getAttribute("topAttribute");
+        output += "<br /> level2Attribute: " + principal.getAttribute("level2Attribute");
+        output += "<br /> group: " + principal.getAttributes("group").toString();
+        output += "<br /> friendlyAttribute email: " + principal.getFriendlyAttribute("email");
+        output += "<br /> phone: " + principal.getAttribute("phone");
+        output += "<br /> friendlyAttribute phone: " + principal.getFriendlyAttribute("phone");
+        output += "<br /> hardcoded-attribute: " + principal.getAttribute("hardcoded-attribute");
+
+        return output;
+    }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java
index 68d7183..9cd7625 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java
@@ -17,7 +17,6 @@
 
 package org.keycloak.testsuite.arquillian;
 
-import org.apache.commons.io.IOUtils;
 import org.apache.tools.ant.DirectoryScanner;
 import org.jboss.arquillian.container.test.spi.client.deployment.ApplicationArchiveProcessor;
 import org.jboss.arquillian.test.spi.TestClass;
@@ -31,6 +30,7 @@ import org.keycloak.testsuite.arquillian.annotation.UseServletFilter;
 import org.keycloak.testsuite.util.IOUtil;
 import org.keycloak.util.JsonSerialization;
 import org.w3c.dom.Document;
+import org.w3c.dom.Element;
 
 import javax.xml.transform.TransformerException;
 import java.io.File;
@@ -112,6 +112,12 @@ public class DeploymentArchiveProcessor implements ApplicationArchiveProcessor {
                     log.error("Can't transform document to String");
                     throw new RuntimeException(e);
                 }
+
+                // For running SAML tests it is necessary to have few dependencies on app-server side.
+                // Few of them are not in adapter zip so we need to add them manually here
+                log.info("Adding SAMLFilter dependencies to " + archive.getName());
+                ((WebArchive) archive).addAsLibraries(KeycloakDependenciesResolver.resolveDependencies("org.keycloak:keycloak-saml-servlet-filter-adapter:" + System.getProperty("project.version")));
+
             } else { // OIDC adapter config
                 try {
                     AdapterConfig adapterConfig = loadJson(archive.get(adapterConfigPath)
@@ -156,45 +162,58 @@ public class DeploymentArchiveProcessor implements ApplicationArchiveProcessor {
 
     protected void modifyWebXml(Archive<?> archive, TestClass testClass) {
         try {
-            String webXmlContent = IOUtils.toString(
+            Document webXmlDoc = loadXML(
                     archive.get(WEBXML_PATH).getAsset().openStream());
             if (isTomcatAppServer(testClass.getJavaClass())) {
-                webXmlContent = webXmlContent.replace("<auth-method>KEYCLOAK</auth-method>", "<auth-method>BASIC</auth-method>");
+                modifyDocElementValue(webXmlDoc, "auth-method", "KEYCLOAK", "BASIC");
             }
 
             if (testClass.getJavaClass().isAnnotationPresent(UseServletFilter.class)) {
                 //We need to add filter declaration to web.xml
                 log.info("Adding filter to " + testClass.getAnnotation(UseServletFilter.class).filterClass() + " with mapping " + testClass.getAnnotation(UseServletFilter.class).filterPattern() + " for " + archive.getName());
-                String filter = "\n<filter>\n" +
-                        "<filter-name>" + testClass.getAnnotation(UseServletFilter.class).filterName() + "</filter-name>\n" +
-                        "<filter-class>" + testClass.getAnnotation(UseServletFilter.class).filterClass() + "</filter-class>\n" +
-                        "</filter>\n" +
-                        "\n<filter-mapping>\n" +
-                        "<filter-name>" + testClass.getAnnotation(UseServletFilter.class).filterName() + "</filter-name>\n" +
-                        "<url-pattern>" + testClass.getAnnotation(UseServletFilter.class).filterPattern() + "</url-pattern>\n";
-                if (!testClass.getAnnotation(UseServletFilter.class).dispatcherType().isEmpty()) {
-                    filter += "<dispatcher>" + testClass.getAnnotation(UseServletFilter.class).dispatcherType() + "</dispatcher>\n";
-                }
-                filter += "</filter-mapping>\n";
 
-                webXmlContent = webXmlContent.replace("</module-name>", "</module-name> " + filter);
+                Element filter = webXmlDoc.createElement("filter");
+                Element filterName = webXmlDoc.createElement("filter-name");
+                Element filterClass = webXmlDoc.createElement("filter-class");
 
-                //Also we need to add all dependencies within war lib directory, because filter needs to work without installed adapter
-                log.info("Adding SAMLFilter dependencies to " + archive.getName());
-                ((WebArchive) archive).addAsLibraries(new SAMLFilterDependency().getDependencies());
+                filterName.setTextContent(testClass.getAnnotation(UseServletFilter.class).filterName());
+                filterClass.setTextContent(testClass.getAnnotation(UseServletFilter.class).filterClass());
 
+                filter.appendChild(filterName);
+                filter.appendChild(filterClass);
+                appendChildInDocument(webXmlDoc, "web-app", filter);
+
+                Element filterMapping = webXmlDoc.createElement("filter-mapping");
 
-                //finally we need to remove all keycloak related configuration from web.xml
-                int start = webXmlContent.indexOf("<security-constraint>");
-                int end = webXmlContent.indexOf("</security-role>") + "</security-role>".length();
 
+                Element urlPattern = webXmlDoc.createElement("url-pattern");
 
-                webXmlContent = webXmlContent.substring(0, start) + webXmlContent.substring(end);
+                filterName = webXmlDoc.createElement("filter-name");
+
+                filterName.setTextContent(testClass.getAnnotation(UseServletFilter.class).filterName());
+                urlPattern.setTextContent(getElementTextContent(webXmlDoc, "web-app/security-constraint/web-resource-collection/url-pattern"));
+
+                filterMapping.appendChild(filterName);
+                filterMapping.appendChild(urlPattern);
+
+                if (!testClass.getAnnotation(UseServletFilter.class).dispatcherType().isEmpty()) {
+                    Element dispatcher = webXmlDoc.createElement("dispatcher");
+                    dispatcher.setTextContent(testClass.getAnnotation(UseServletFilter.class).dispatcherType());
+                    filterMapping.appendChild(dispatcher);
+                }
+                appendChildInDocument(webXmlDoc, "web-app", filterMapping);
+
+                //finally we need to remove all keycloak related configuration from web.xml
+                removeElementFromDoc(webXmlDoc, "web-app", "security-constraint");
+                removeElementFromDoc(webXmlDoc, "web-app", "login-config");
+                removeElementFromDoc(webXmlDoc, "web-app", "security-role");
             }
 
-            archive.add(new StringAsset((webXmlContent)), WEBXML_PATH);
-        } catch (IOException ex) {
-            throw new RuntimeException("Cannot load web.xml from archive.");
+
+            archive.add(new StringAsset((documentToString(webXmlDoc))), WEBXML_PATH);
+        } catch (TransformerException e) {
+            log.error("Can't transform document to String");
+            throw new RuntimeException(e);
         }
     }
 
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/KeycloakDependenciesResolver.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/KeycloakDependenciesResolver.java
new file mode 100644
index 0000000..2f62c2b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/KeycloakDependenciesResolver.java
@@ -0,0 +1,39 @@
+package org.keycloak.testsuite.arquillian;
+
+import org.jboss.logging.Logger;
+import org.jboss.shrinkwrap.resolver.api.maven.Maven;
+import org.jboss.shrinkwrap.resolver.api.maven.PomEquippedResolveStage;
+import org.jboss.shrinkwrap.resolver.api.maven.ScopeType;
+import org.jboss.shrinkwrap.resolver.api.maven.coordinate.MavenDependencies;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author mhajas
+ */
+public class KeycloakDependenciesResolver {
+
+    private static Map<String, File[]> dependencies = new HashMap<>();
+
+    protected static final Logger log = org.jboss.logging.Logger.getLogger(KeycloakDependenciesResolver.class);
+
+    public static File[] resolveDependencies(String canonicalForm) {
+        if (dependencies.containsKey(canonicalForm)) {
+            return dependencies.get(canonicalForm);
+        }
+
+        log.info("Resolving " + canonicalForm + "'s dependencies");
+        PomEquippedResolveStage resolver = Maven.configureResolverViaPlugin();
+
+        File[] files = resolver.addDependency(MavenDependencies.createDependency(canonicalForm, ScopeType.COMPILE, false))
+                .resolve().withTransitivity().asFile();
+
+        dependencies.put(canonicalForm, files);
+
+        log.info("Resolving dependencies is finished with " + files.length + " files");
+
+        return dependencies.get(canonicalForm);
+    }
+}
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 2efc26b..2d277fd 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
@@ -20,7 +20,9 @@ package org.keycloak.testsuite.client.resources;
 import org.keycloak.representations.adapters.action.LogoutAction;
 import org.keycloak.representations.adapters.action.PushNotBeforeAction;
 import org.keycloak.representations.adapters.action.TestAvailabilityAction;
+import org.keycloak.testsuite.rest.resource.TestingOIDCEndpointsApplicationResource;
 
+import javax.ws.rs.Consumes;
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
@@ -53,4 +55,6 @@ public interface TestApplicationResource {
     @Path("/clear-admin-actions")
     Response clearAdminActions();
 
+    @Path("/oidc-client-endpoints")
+    TestOIDCEndpointsApplicationResource oidcClientEndpoints();
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestApplicationResourceUrls.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestApplicationResourceUrls.java
new file mode 100644
index 0000000..8c5f98b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestApplicationResourceUrls.java
@@ -0,0 +1,48 @@
+/*
+ * 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.testsuite.client.resources;
+
+import javax.ws.rs.core.UriBuilder;
+
+import org.keycloak.testsuite.util.OAuthClient;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class TestApplicationResourceUrls {
+
+    private static UriBuilder oidcClientEndpoints() {
+        return UriBuilder.fromUri(OAuthClient.AUTH_SERVER_ROOT)
+                .path(TestApplicationResource.class)
+                .path(TestApplicationResource.class, "oidcClientEndpoints");
+    }
+
+    public static String clientRequestUri() {
+        UriBuilder builder = oidcClientEndpoints()
+                .path(TestOIDCEndpointsApplicationResource.class, "getOIDCRequest");
+
+        return builder.build().toString();
+    }
+
+    public static String clientJwksUri() {
+        UriBuilder builder = oidcClientEndpoints()
+                .path(TestOIDCEndpointsApplicationResource.class, "getJwks");
+
+        return builder.build().toString();
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestingExportImportResource.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestingExportImportResource.java
new file mode 100644
index 0000000..27fa360
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestingExportImportResource.java
@@ -0,0 +1,93 @@
+/*
+ * 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.testsuite.client.resources;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+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:mposolda@redhat.com">Marek Posolda</a>
+ */
+public interface TestingExportImportResource {
+
+    @GET
+    @Path("/run-import")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response runImport();
+
+    @GET
+    @Path("/run-export")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response runExport();
+
+    @GET
+    @Path("/get-users-per-file")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Integer getUsersPerFile();
+
+    @PUT
+    @Path("/set-users-per-file")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void setUsersPerFile(@QueryParam("usersPerFile") Integer usersPerFile);
+
+    @GET
+    @Path("/get-dir")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public String getDir();
+
+    @PUT
+    @Path("/set-dir")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public String setDir(@QueryParam("dir") String dir);
+
+    @PUT
+    @Path("/export-import-provider")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void setProvider(@QueryParam("exportImportProvider") String exportImportProvider);
+
+    @PUT
+    @Path("/export-import-file")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void setFile(@QueryParam("file") String file);
+
+    @PUT
+    @Path("/export-import-action")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void setAction(@QueryParam("exportImportAction") String exportImportAction);
+
+    @PUT
+    @Path("/set-realm-name")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void setRealmName(@QueryParam("realmName") String realmName);
+
+    @GET
+    @Path("/get-test-dir")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public String getExportImportTestDirectory();
+
+}
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 fcf5d83..0dbcd58 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
@@ -17,8 +17,8 @@
 
 package org.keycloak.testsuite.client.resources;
 
-import java.util.Date;
 import java.util.List;
+
 import org.keycloak.representations.idm.AdminEventRepresentation;
 import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
 import org.keycloak.representations.idm.EventRepresentation;
@@ -37,7 +37,6 @@ import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import java.util.Map;
 import org.jboss.resteasy.annotations.cache.NoCache;
-import org.keycloak.exportimport.ExportImportManager;
 
 /**
  * @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
@@ -205,16 +204,6 @@ public interface TestingResource {
     @Path("/update-pass-through-auth-state")
     @Produces(MediaType.APPLICATION_JSON)
     AuthenticatorState updateAuthenticator(AuthenticatorState state);
-
-    @GET
-    @Path("/run-import")
-    @Produces(MediaType.APPLICATION_JSON)
-    public Response runImport();
-
-    @GET
-    @Path("/run-export")
-    @Produces(MediaType.APPLICATION_JSON)
-    public Response runExport();
     
     @GET
     @Path("/valid-credentials")
@@ -250,53 +239,7 @@ public interface TestingResource {
     @Produces(MediaType.APPLICATION_JSON)
     public UserRepresentation getUserByServiceAccountClient(@QueryParam("realmName") String realmName, @QueryParam("clientId") String clientId);
 
+    @Path("export-import")
+    TestingExportImportResource exportImport();
 
-    @GET
-    @Path("/get-users-per-file")
-    @Consumes(MediaType.APPLICATION_JSON)
-    @Produces(MediaType.APPLICATION_JSON)
-    public Integer getUsersPerFile();
-
-    @PUT
-    @Path("/set-users-per-file")
-    @Consumes(MediaType.APPLICATION_JSON)
-    public void setUsersPerFile(@QueryParam("usersPerFile") Integer usersPerFile);
-
-    @GET
-    @Path("/get-dir")
-    @Consumes(MediaType.APPLICATION_JSON)
-    @Produces(MediaType.APPLICATION_JSON)
-    public String getDir();
-
-    @PUT
-    @Path("/set-dir")
-    @Consumes(MediaType.APPLICATION_JSON)
-    @Produces(MediaType.APPLICATION_JSON)
-    public String setDir(@QueryParam("dir") String dir);
-
-    @PUT
-    @Path("/export-import-provider")
-    @Consumes(MediaType.APPLICATION_JSON)
-    public void setProvider(@QueryParam("exportImportProvider") String exportImportProvider);
-
-    @PUT
-    @Path("/export-import-file")
-    @Consumes(MediaType.APPLICATION_JSON)
-    public void setFile(@QueryParam("file") String file);
-
-    @PUT
-    @Path("/export-import-action")
-    @Consumes(MediaType.APPLICATION_JSON)
-    public void setAction(@QueryParam("exportImportAction") String exportImportAction);
-
-    @PUT
-    @Path("/set-realm-name")
-    @Consumes(MediaType.APPLICATION_JSON)
-    public void setRealmName(@QueryParam("realmName") String realmName);
-
-    @GET
-    @Path("/get-test-dir")
-    @Consumes(MediaType.APPLICATION_JSON)
-    @Produces(MediaType.APPLICATION_JSON)
-    public String getExportImportTestDirectory();
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestOIDCEndpointsApplicationResource.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestOIDCEndpointsApplicationResource.java
new file mode 100644
index 0000000..54d6c35
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestOIDCEndpointsApplicationResource.java
@@ -0,0 +1,58 @@
+/*
+ * 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.testsuite.client.resources;
+
+import java.util.Map;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+
+import org.keycloak.jose.jwk.JSONWebKeySet;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public interface TestOIDCEndpointsApplicationResource {
+
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @Path("/generate-keys")
+    Map<String, String> generateKeys();
+
+
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @Path("/get-jwks")
+    JSONWebKeySet getJwks();
+
+
+    @GET
+    @Path("/set-oidc-request")
+    @Produces(org.keycloak.utils.MediaType.APPLICATION_JWT)
+    void setOIDCRequest(@QueryParam("realmName") String realmName, @QueryParam("clientId") String clientId,
+                        @QueryParam("redirectUri") String redirectUri, @QueryParam("maxAge") String maxAge,
+                        @QueryParam("jwaAlgorithm") String jwaAlgorithm);
+
+    @GET
+    @Path("/get-oidc-request")
+    @Produces(org.keycloak.utils.MediaType.APPLICATION_JWT)
+    String getOIDCRequest();
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/IOUtil.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/IOUtil.java
index d872914..89084d4 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/IOUtil.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/IOUtil.java
@@ -16,9 +16,11 @@
  */
 package org.keycloak.testsuite.util;
 
+import org.jboss.logging.Logger;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.util.JsonSerialization;
 import org.w3c.dom.Document;
+import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 import org.xml.sax.SAXException;
@@ -33,7 +35,6 @@ import javax.xml.transform.dom.DOMSource;
 import javax.xml.transform.stream.StreamResult;
 import java.io.*;
 import java.util.concurrent.TimeUnit;
-import org.jboss.logging.Logger;
 
 /**
  *
@@ -93,18 +94,99 @@ public class IOUtil {
     public static void modifyDocElementAttribute(Document doc, String tagName, String attributeName, String regex, String replacement) {
         NodeList nodes = doc.getElementsByTagName(tagName);
         if (nodes.getLength() != 1) {
-            System.out.println("Not able to find element: " + tagName);
+            log.warn("Not able or ambiguous to find element: " + tagName);
             return;
         }
 
         Node node = nodes.item(0).getAttributes().getNamedItem(attributeName);
         if (node == null) {
-            System.out.println("Not able to find attribute " + attributeName + " within element: " + tagName);
+            log.warn("Not able to find attribute " + attributeName + " within element: " + tagName);
+            return;
+        }
+        node.setTextContent(node.getTextContent().replace(regex, replacement));
+    }
+
+    public static void modifyDocElementValue(Document doc, String tagName, String regex, String replacement) {
+        NodeList nodes = doc.getElementsByTagName(tagName);
+        if (nodes.getLength() != 1) {
+            log.warn("Not able or ambiguous to find element: " + tagName);
+            return;
+        }
+
+        Node node = nodes.item(0);
+        if (node == null) {
+            log.warn("Not able to find element: " + tagName);
             return;
         }
+
         node.setTextContent(node.getTextContent().replace(regex, replacement));
     }
 
+    public static void removeElementFromDoc(Document doc, String parentTag, String removeNode) {
+        NodeList nodes = doc.getElementsByTagName(parentTag);
+        if (nodes.getLength() != 1) {
+            log.warn("Not able or ambiguous to find element: " + parentTag);
+            return;
+        }
+
+        Element parentElement = (Element) nodes.item(0);
+        if (parentElement == null) {
+            log.warn("Not able to find element: " + parentTag);
+            return;
+        }
+
+        NodeList removeNodes = parentElement.getElementsByTagName(removeNode);
+        if (removeNodes.getLength() != 1) {
+            log.warn("Not able or ambiguous to find element: " + removeNode + " within node " + parentTag);
+            return;
+        }
+
+        Element removeElement = (Element) removeNodes.item(0);
+        if (removeElement == null) {
+            log.warn("Not able to find element: " + removeNode + " within node " + parentTag);
+            return;
+        }
+
+        parentElement.removeChild(removeElement);
+    }
+
+    public static String getElementTextContent(Document doc, String path) {
+        String[] pathSegments = path.split("/");
+
+        Element currentElement = (Element) doc.getElementsByTagName(pathSegments[0]).item(0);
+        if (currentElement == null) {
+            log.warn("Not able to find element: " + pathSegments[0] + " in document");
+            return null;
+        }
+
+        for (int i = 1; i < pathSegments.length; i++) {
+            currentElement = (Element) currentElement.getElementsByTagName(pathSegments[i]).item(0);
+
+            if (currentElement == null) {
+                log.warn("Not able to find element: " + pathSegments[i] + " in " + pathSegments[i - 1]);
+                return null;
+            }
+        }
+
+        return currentElement.getTextContent();
+    }
+
+    public static void appendChildInDocument(Document doc, String parentTag, Element node) {
+        NodeList nodes = doc.getElementsByTagName(parentTag);
+        if (nodes.getLength() != 1) {
+            log.warn("Not able or ambiguous to find element: " + parentTag);
+            return;
+        }
+
+        Element parentElement = (Element) nodes.item(0);
+        if (parentElement == null) {
+            log.warn("Not able to find element: " + parentTag);
+            return;
+        }
+
+        parentElement.appendChild(node);
+    }
+
     public static void execCommand(String command, File dir) throws IOException, InterruptedException {
         Process process = Runtime.getRuntime().exec(command, null, dir);
         if (process.waitFor(10, TimeUnit.SECONDS)) {
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java
index 586351d..2253828 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java
@@ -105,6 +105,10 @@ public class OAuthClient {
 
     private String nonce;
 
+    private String request;
+
+    private String requestUri;
+
     private Map<String, PublicKey> publicKeys = new HashMap<>();
 
     public void init(Keycloak adminClient, WebDriver driver) {
@@ -121,6 +125,9 @@ public class OAuthClient {
         clientSessionState = null;
         clientSessionHost = null;
         maxAge = null;
+        nonce = null;
+        request = null;
+        requestUri = null;
     }
 
     public AuthorizationEndpointResponse doLogin(String username, String password) {
@@ -536,6 +543,12 @@ public class OAuthClient {
         if (maxAge != null) {
             b.queryParam(OIDCLoginProtocol.MAX_AGE_PARAM, maxAge);
         }
+        if (request != null) {
+            b.queryParam(OIDCLoginProtocol.REQUEST_PARAM, request);
+        }
+        if (requestUri != null) {
+            b.queryParam(OIDCLoginProtocol.REQUEST_URI_PARAM, requestUri);
+        }
         return b.build(realm).toString();
     }
 
@@ -644,6 +657,16 @@ public class OAuthClient {
         return this;
     }
 
+    public OAuthClient request(String request) {
+        this.request = request;
+        return this;
+    }
+
+    public OAuthClient requestUri(String requestUri) {
+        this.requestUri = requestUri;
+        return this;
+    }
+
     public String getRealm() {
         return realm;
     }
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/UserInfoClientUtil.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/UserInfoClientUtil.java
index dd6a3db..b5af7c9 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/UserInfoClientUtil.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/UserInfoClientUtil.java
@@ -28,6 +28,7 @@ import javax.ws.rs.core.UriBuilder;
 import org.junit.Assert;
 import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
 import org.keycloak.representations.UserInfo;
+import org.keycloak.utils.MediaType;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -51,6 +52,7 @@ public class UserInfoClientUtil {
 
     public static void testSuccessfulUserInfoResponse(Response response, String expectedUsername, String expectedEmail) {
         Assert.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+        Assert.assertEquals(response.getHeaderString(HttpHeaders.CONTENT_TYPE), MediaType.APPLICATION_JSON);
 
         UserInfo userInfo = response.readEntity(UserInfo.class);
 
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractAdapterTest.java
index 0ef7215..63821b3 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractAdapterTest.java
@@ -31,6 +31,7 @@ import java.io.IOException;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 /**
  *
@@ -63,10 +64,12 @@ public abstract class AbstractAdapterTest extends AbstractAuthTest {
                 modifyClientUrls(tr, appServerContextRootPage.toString(), "");
                 modifyClientWebOrigins(tr, "8080", System.getProperty("auth.server.http.port", null));
                 modifySamlMasterURLs(tr, "/", "http://localhost:" + System.getProperty("auth.server.http.port", null) + "/");
+                modifySAMLClientsAttributes(tr, "8080", System.getProperty("auth.server.http.port", "8180"));
             } else {
                 modifyClientRedirectUris(tr, "^(/.*/\\*)", appServerContextRootPage.toString() + "$1");
                 modifyClientUrls(tr, "^(/.*)", appServerContextRootPage.toString() + "$1");
                 modifySamlMasterURLs(tr, "8080", System.getProperty("auth.server.http.port", null));
+                modifySAMLClientsAttributes(tr, "8080", System.getProperty("app.server.http.port", "8280"));
             }
             if ("true".equals(System.getProperty("auth.server.ssl.required"))) {
                 tr.setSslRequired("all");
@@ -125,6 +128,19 @@ public abstract class AbstractAdapterTest extends AbstractAuthTest {
         }
     }
 
+    protected void modifySAMLClientsAttributes(RealmRepresentation realm, String regex, String replacement) {
+        if (realm.getClients() != null) {
+            for (ClientRepresentation client : realm.getClients()) {
+                if (client.getProtocol() != null && client.getProtocol().equals("saml")) {
+                    log.info("Modifying attributes of SAML client: " + client.getClientId());
+                    for (Map.Entry<String, String> entry : client.getAttributes().entrySet()) {
+                        client.getAttributes().put(entry.getKey(), entry.getValue().replaceAll(regex, replacement));
+                    }
+                }
+            }
+        }
+    }
+
     protected void modifySamlMasterURLs(RealmRepresentation realm, String regex, String replacement) {
         if (realm.getClients() != null) {
             for (ClientRepresentation client : realm.getClients()) {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractServletsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractServletsAdapterTest.java
index badf51c..127c863 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractServletsAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractServletsAdapterTest.java
@@ -25,12 +25,11 @@ import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.testsuite.util.WaitUtils;
 import org.openqa.selenium.By;
 
+import javax.ws.rs.core.UriBuilder;
 import java.io.IOException;
 import java.net.URL;
 import java.util.List;
 
-import javax.ws.rs.core.UriBuilder;
-
 import static org.keycloak.testsuite.auth.page.AuthRealm.DEMO;
 import static org.keycloak.testsuite.util.IOUtil.loadRealm;
 
@@ -66,15 +65,15 @@ public abstract class AbstractServletsAdapterTest extends AbstractAdapterTest {
     }
 
     protected static WebArchive samlServletDeployment(String name, Class... servletClasses) {
-        return samlServletDeployment(name, "keycloak-saml.xml", servletClasses);
+        return samlServletDeployment(name, "web.xml", servletClasses);
     }
 
-    protected static WebArchive samlServletDeployment(String name, String adapterConfig ,Class... servletClasses) {
+    protected static WebArchive samlServletDeployment(String name, String webXMLPath, Class... servletClasses) {
         String baseSAMLPath = "/adapter-test/keycloak-saml/";
         String webInfPath = baseSAMLPath + name + "/WEB-INF/";
 
-        URL keycloakSAMLConfig = AbstractServletsAdapterTest.class.getResource(webInfPath + adapterConfig);
-        URL webXML = AbstractServletsAdapterTest.class.getResource(baseSAMLPath + "web.xml");
+        URL keycloakSAMLConfig = AbstractServletsAdapterTest.class.getResource(webInfPath + "keycloak-saml.xml");
+        URL webXML = AbstractServletsAdapterTest.class.getResource(baseSAMLPath + webXMLPath);
 
         WebArchive deployment = ShrinkWrap.create(WebArchive.class, name + ".war")
                 .addClasses(servletClasses)
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/EnforcerConfigTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/EnforcerConfigTest.java
new file mode 100644
index 0000000..4236b2e
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/EnforcerConfigTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.testsuite.adapter.example.authorization;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.adapters.KeycloakDeploymentBuilder;
+import org.keycloak.adapters.authorization.PolicyEnforcer;
+import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.AbstractKeycloakTest;
+
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.keycloak.testsuite.util.IOUtil.loadRealm;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class EnforcerConfigTest extends AbstractKeycloakTest {
+
+    @Override
+    public void addTestRealms(List<RealmRepresentation> testRealms) {
+        RealmRepresentation realm = loadRealm(getClass().getResourceAsStream("/authorization-test/test-authz-realm.json"));
+        testRealms.add(realm);
+    }
+
+    @Test
+    public void testMultiplePathsWithSameName() throws Exception{
+        KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(getClass().getResourceAsStream("/authorization-test/enforcer-config-paths-same-name.json"));
+        PolicyEnforcer policyEnforcer = deployment.getPolicyEnforcer();
+        List<PolicyEnforcerConfig.PathConfig> paths = policyEnforcer.getPaths();
+        assertEquals(1, paths.size());
+        assertEquals(4, paths.get(0).getMethods().size());
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLFilterServletAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLFilterServletAdapterTest.java
index 0df65a4..268285a 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLFilterServletAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLFilterServletAdapterTest.java
@@ -2,6 +2,8 @@ package org.keycloak.testsuite.adapter.servlet;
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
 import org.keycloak.testsuite.arquillian.annotation.UseServletFilter;
 
 /**
@@ -25,11 +27,12 @@ public abstract class AbstractSAMLFilterServletAdapterTest extends AbstractSAMLS
         salesPostSigEmailServletPage.checkRoles(true);
         salesPostSigPersistentServletPage.checkRoles(true);
         salesPostSigTransientServletPage.checkRoles(true);
-        employee2ServletPage.navigateTo();
+        salesPostAssertionAndResponseSigPage.checkRoles(true);
 
         //using endpoint instead of query param because we are not able to put query param to IDP initiated login
+        employee2ServletPage.navigateTo();
         testRealmLoginPage.form().login(bburkeUser);
-        employee2ServletPage.checkRolesEndPoint();
+        employee2ServletPage.checkRolesEndPoint(true);
         employee2ServletPage.logout();
 
         forbiddenIfNotAuthenticated = false;
@@ -51,4 +54,18 @@ public abstract class AbstractSAMLFilterServletAdapterTest extends AbstractSAMLS
         salesPostSigPersistentServletPage.checkRoles(false);
         salesPostSigTransientServletPage.checkRoles(false);
     }
+
+    @Test
+    @Override
+    @Ignore
+    public void testSavedPostRequest() {
+
+    }
+
+    @Test
+    @Override
+    @Ignore
+    public void testErrorHandling() {
+
+    }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLServletsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLServletsAdapterTest.java
index ddc23b7..2973caa 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLServletsAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLServletsAdapterTest.java
@@ -20,11 +20,19 @@ package org.keycloak.testsuite.adapter.servlet;
 import org.jboss.arquillian.container.test.api.Deployment;
 import org.jboss.arquillian.graphene.page.Page;
 import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Assert;
 import org.junit.Test;
 import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.admin.client.resource.ProtocolMappersResource;
+import org.keycloak.protocol.saml.mappers.AttributeStatementHelper;
+import org.keycloak.protocol.saml.mappers.RoleListMapper;
 import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.ProtocolMapperRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.saml.BaseSAML2BindingBuilder;
+import org.keycloak.saml.SAML2ErrorResponseBuilder;
+import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
 import org.keycloak.testsuite.adapter.AbstractServletsAdapterTest;
 import org.keycloak.testsuite.adapter.page.*;
 import org.keycloak.testsuite.admin.ApiUtil;
@@ -35,8 +43,15 @@ import org.keycloak.testsuite.util.IOUtil;
 import org.openqa.selenium.By;
 import org.w3c.dom.Document;
 
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Form;
 import javax.ws.rs.core.Response;
+import java.net.URI;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
@@ -71,6 +86,9 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
     protected SalesPostServlet salesPostServletPage;
 
     @Page
+    private SalesPost2Servlet salesPost2ServletPage;
+
+    @Page
     protected SalesPostEncServlet salesPostEncServletPage;
 
     @Page
@@ -93,6 +111,26 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
 
     protected boolean forbiddenIfNotAuthenticated = true;
 
+    @Page
+    protected SalesPostAssertionAndResponseSig salesPostAssertionAndResponseSigPage;
+
+    @Page
+    protected BadAssertionSalesPostSig badAssertionSalesPostSigPage;
+
+    @Page
+    protected MissingAssertionSig missingAssertionSigPage;
+
+    @Page
+    protected EmployeeServlet employeeServletPage;
+
+    @Page
+    private InputPortal inputPortalPage;
+
+    @Page
+    private SAMLIDPInitiatedLogin samlidpInitiatedLoginPage;
+
+    public static final String FORBIDDEN_TEXT = "HTTP status code: 403";
+
     @Deployment(name = BadClientSalesPostSigServlet.DEPLOYMENT_NAME)
     protected static WebArchive badClientSalesPostSig() {
         return samlServletDeployment(BadClientSalesPostSigServlet.DEPLOYMENT_NAME, SendUsernameServlet.class);
@@ -158,6 +196,36 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
         return samlServletDeployment(SalesPostSigTransientServlet.DEPLOYMENT_NAME, SendUsernameServlet.class);
     }
 
+    @Deployment(name = InputPortal.DEPLOYMENT_NAME)
+    protected static WebArchive inputPortal() {
+        return samlServletDeployment(InputPortal.DEPLOYMENT_NAME, "input-portal/WEB-INF/web.xml" , InputServlet.class);
+    }
+
+    @Deployment(name = SalesPost2Servlet.DEPLOYMENT_NAME)
+    protected static WebArchive salesPost2() {
+        return samlServletDeployment(SalesPost2Servlet.DEPLOYMENT_NAME, SendUsernameServlet.class);
+    }
+
+    @Deployment(name = SalesPostAssertionAndResponseSig.DEPLOYMENT_NAME)
+    protected static WebArchive salesPostAssertionAndResponseSig() {
+        return samlServletDeployment(SalesPostAssertionAndResponseSig.DEPLOYMENT_NAME, SendUsernameServlet.class);
+    }
+
+    @Deployment(name = BadAssertionSalesPostSig.DEPLOYMENT_NAME)
+    protected static WebArchive badAssertionSalesPostSig() {
+        return samlServletDeployment(BadAssertionSalesPostSig.DEPLOYMENT_NAME, SendUsernameServlet.class);
+    }
+
+    @Deployment(name = MissingAssertionSig.DEPLOYMENT_NAME)
+    protected static WebArchive missingAssertionSig() {
+        return samlServletDeployment(MissingAssertionSig.DEPLOYMENT_NAME, SendUsernameServlet.class);
+    }
+
+    @Deployment(name = EmployeeServlet.DEPLOYMENT_NAME)
+    protected static WebArchive employeeServlet() {
+        return samlServletDeployment(EmployeeServlet.DEPLOYMENT_NAME, "employee/WEB-INF/web.xml", SamlSPFacade.class);
+    }
+
     @Override
     public void addAdapterTestRealms(List<RealmRepresentation> testRealms) {
         testRealms.add(loadRealm("/adapter-test/keycloak-saml/testsaml.json"));
@@ -171,38 +239,54 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
         testRealmSAMLPostLoginPage.setAuthRealm(SAMLSERVLETDEMO);
     }
 
-    private void assertForbidden(AbstractPage page) {
+    private void assertForbidden(AbstractPage page, String expectedNotContains) {
         page.navigateTo();
-        waitUntilElement(By.xpath("//body")).text().not().contains("principal=");
-        assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains("Status 403"));
+        waitUntilElement(By.xpath("//body")).text().not().contains(expectedNotContains);
+        assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains(FORBIDDEN_TEXT));
     }
 
-    private void assertSuccessfullyLoggedIn(AbstractPage page) {
+    private void assertSuccessfullyLoggedIn(AbstractPage page, String expectedText) {
         page.navigateTo();
-        waitUntilElement(By.xpath("//body")).text().contains("principal=bburke");
+        waitUntilElement(By.xpath("//body")).text().contains(expectedText);
     }
 
-    private void assertForbiddenLogin(AbstractPage page, String username, String password, Login loginPage) {
+    private void assertForbiddenLogin(AbstractPage page, String username, String password, Login loginPage, String expectedNotContains) {
         page.navigateTo();
         assertCurrentUrlStartsWith(loginPage);
         loginPage.form().login(username, password);
-        waitUntilElement(By.xpath("//body")).text().not().contains("principal=");
+        waitUntilElement(By.xpath("//body")).text().not().contains(expectedNotContains);
         //Different 403 status page on EAP and Wildfly
-        assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains("Status 403"));
+        assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains(FORBIDDEN_TEXT));
     }
 
-    private void assertSuccessfulLogin(AbstractPage page, UserRepresentation user, Login loginPage) {
+    private void assertSuccessfulLogin(AbstractPage page, UserRepresentation user, Login loginPage, String expectedString) {
         page.navigateTo();
         assertCurrentUrlStartsWith(loginPage);
         loginPage.form().login(user);
-        waitUntilElement(By.xpath("//body")).text().contains("principal=bburke");
+        waitUntilElement(By.xpath("//body")).text().contains(expectedString);
     }
 
     private void testSuccessfulAndUnauthorizedLogin(SAMLServlet page, Login loginPage) {
-        assertSuccessfulLogin(page, bburkeUser, loginPage);
+        testSuccessfulAndUnauthorizedLogin(page, loginPage, "principal=bburke");
+    }
+
+    private void testSuccessfulAndUnauthorizedLogin(SAMLServlet page, Login loginPage, String expectedText) {
+        testSuccessfulAndUnauthorizedLogin(page, loginPage, expectedText, "principal=");
+    }
+
+    private void testSuccessfulAndUnauthorizedLogin(SAMLServlet page, Login loginPage, String expectedText, String expectedNotContains) {
+        assertSuccessfulLogin(page, bburkeUser, loginPage, expectedText);
         page.logout();
-        assertForbiddenLogin(page, "unauthorized", "password", loginPage);
+        checkLoggedOut(page, loginPage);
+        assertForbiddenLogin(page, "unauthorized", "password", loginPage, expectedNotContains);
         page.logout();
+        checkLoggedOut(page, loginPage);
+    }
+
+    private void checkLoggedOut(AbstractPage page, Login loginPage) {
+        page.navigateTo();
+        waitUntilElement(By.xpath("//body")).is().present();
+        assertCurrentUrlStartsWith(loginPage);
     }
 
     @Test
@@ -221,38 +305,35 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
 
     @Test
     public void unauthorizedSSOTest() {
-        assertForbiddenLogin(salesPostServletPage, "unauthorized", "password", testRealmSAMLPostLoginPage);
-        assertForbidden(employee2ServletPage);
-        assertForbidden(employeeSigFrontServletPage);
-        assertForbidden(salesPostSigPersistentServletPage);
+        assertForbiddenLogin(salesPostServletPage, "unauthorized", "password", testRealmSAMLPostLoginPage, "principal=");
+        assertForbidden(employee2ServletPage, "principal=");
+        assertForbidden(employeeSigFrontServletPage, "principal=");
+        assertForbidden(salesPostSigPersistentServletPage, "principal=");
         salesPostServletPage.logout();
+        checkLoggedOut(salesPostServletPage, testRealmSAMLPostLoginPage);
     }
 
     @Test
     public void singleLoginAndLogoutSAMLTest() {
-        assertSuccessfulLogin(salesPostServletPage, bburkeUser, testRealmSAMLPostLoginPage);
-        assertSuccessfullyLoggedIn(salesPostSigServletPage);
-        assertSuccessfullyLoggedIn(employee2ServletPage);
-        assertSuccessfullyLoggedIn(salesPostEncServletPage);
+        assertSuccessfulLogin(salesPostServletPage, bburkeUser, testRealmSAMLPostLoginPage, "principal=bburke");
+        assertSuccessfullyLoggedIn(salesPostSigServletPage, "principal=bburke");
+        assertSuccessfullyLoggedIn(employee2ServletPage, "principal=bburke");
+        assertSuccessfullyLoggedIn(salesPostEncServletPage, "principal=bburke");
 
         employeeSigFrontServletPage.logout();
 
-        employeeSigFrontServletPage.navigateTo();
-        assertCurrentUrlStartsWith(testRealmSAMLRedirectLoginPage);
-
-        employeeSigServletPage.navigateTo();
-        assertCurrentUrlStartsWith(testRealmSAMLRedirectLoginPage);
+        checkLoggedOut(employeeSigFrontServletPage, testRealmSAMLRedirectLoginPage);
+        checkLoggedOut(employeeSigServletPage, testRealmSAMLRedirectLoginPage);
 
         salesPostPassiveServletPage.navigateTo();
         if (forbiddenIfNotAuthenticated) {
             waitUntilElement(By.xpath("//body")).text().not().contains("principal=");
-            assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains("<body></body>") || driver.getPageSource().equals(""));
+            assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains(FORBIDDEN_TEXT));
         } else {
             waitUntilElement(By.xpath("//body")).text().contains("principal=null");
         }
 
-        salesPostSigEmailServletPage.navigateTo();
-        assertCurrentUrlStartsWith(testRealmSAMLPostLoginPage);
+        checkLoggedOut(salesPostSigEmailServletPage, testRealmSAMLPostLoginPage);
     }
 
     @Test
@@ -268,7 +349,7 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
 
         waitUntilElement(By.xpath("//body")).text().not().contains("principal=");
         //Different 403 status page on EAP and Wildfly
-        assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains("Status 403"));
+        assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains(FORBIDDEN_TEXT));
     }
 
     @Test
@@ -328,14 +409,14 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
         if (forbiddenIfNotAuthenticated) {
             waitUntilElement(By.xpath("//body")).text().not().contains("principal=");
             //Different 403 status page on EAP and Wildfly
-            assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains("<body></body>") || driver.getPageSource().equals(""));
+            assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains(FORBIDDEN_TEXT));
         } else {
             waitUntilElement(By.xpath("//body")).text().contains("principal=null");
         }
 
-        assertSuccessfulLogin(salesPostServletPage, bburkeUser, testRealmSAMLPostLoginPage);
+        assertSuccessfulLogin(salesPostServletPage, bburkeUser, testRealmSAMLPostLoginPage, "principal=bburke");
 
-        assertSuccessfullyLoggedIn(salesPostPassiveServletPage);
+        assertSuccessfullyLoggedIn(salesPostPassiveServletPage, "principal=bburke");
 
         salesPostPassiveServletPage.logout();
         salesPostPassiveServletPage.navigateTo();
@@ -343,12 +424,13 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
         if (forbiddenIfNotAuthenticated) {
             waitUntilElement(By.xpath("//body")).text().not().contains("principal=");
             //Different 403 status page on EAP and Wildfly
-            assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains("<body></body>") || driver.getPageSource().equals(""));
+            assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains(FORBIDDEN_TEXT));
         } else {
             waitUntilElement(By.xpath("//body")).text().contains("principal=null");
         }
-        assertForbiddenLogin(salesPostServletPage, "unauthorized", "password", testRealmSAMLPostLoginPage);
-        assertForbidden(salesPostPassiveServletPage);
+
+        assertForbiddenLogin(salesPostServletPage, "unauthorized", "password", testRealmSAMLPostLoginPage, "principal=");
+        assertForbidden(salesPostPassiveServletPage, "principal=");
 
         salesPostPassiveServletPage.logout();
     }
@@ -360,7 +442,7 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
 
     @Test
     public void salesPostSigEmailTest() {
-        testSuccessfulAndUnauthorizedLogin(salesPostSigEmailServletPage, testRealmSAMLPostLoginPage);
+        testSuccessfulAndUnauthorizedLogin(salesPostSigEmailServletPage, testRealmSAMLPostLoginPage, "principal=bburke@redhat.com");
     }
 
     @Test
@@ -371,9 +453,11 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
         waitUntilElement(By.xpath("//body")).text().contains("principal=G-");
 
         salesPostSigPersistentServletPage.logout();
+        checkLoggedOut(salesPostSigPersistentServletPage, testRealmSAMLPostLoginPage);
 
-        assertForbiddenLogin(salesPostSigPersistentServletPage, "unauthorized", "password", testRealmSAMLPostLoginPage);
+        assertForbiddenLogin(salesPostSigPersistentServletPage, "unauthorized", "password", testRealmSAMLPostLoginPage, "principal=");
         salesPostSigPersistentServletPage.logout();
+        checkLoggedOut(salesPostSigPersistentServletPage, testRealmSAMLPostLoginPage);
     }
 
     @Test
@@ -384,36 +468,260 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
         waitUntilElement(By.xpath("//body")).text().contains("principal=G-");
 
         salesPostSigTransientServletPage.logout();
+        checkLoggedOut(salesPostSigTransientServletPage, testRealmSAMLPostLoginPage);
 
-        assertForbiddenLogin(salesPostSigTransientServletPage, "unauthorized", "password", testRealmSAMLPostLoginPage);
+        assertForbiddenLogin(salesPostSigTransientServletPage, "unauthorized", "password", testRealmSAMLPostLoginPage, "principal=");
         salesPostSigTransientServletPage.logout();
+        checkLoggedOut(salesPostSigTransientServletPage, testRealmSAMLPostLoginPage);
     }
 
     @Test
     public void idpInitiatedLogin() {
-        samlidpInitiatedLogin.setAuthRealm(SAMLSERVLETDEMO);
-        samlidpInitiatedLogin.setUrlName("employee2");
-        samlidpInitiatedLogin.navigateTo();
-        samlidpInitiatedLogin.form().login(bburkeUser);
+        samlidpInitiatedLoginPage.setAuthRealm(SAMLSERVLETDEMO);
+        samlidpInitiatedLoginPage.setUrlName("employee2");
+        samlidpInitiatedLoginPage.navigateTo();
+        samlidpInitiatedLoginPage.form().login(bburkeUser);
 
         waitUntilElement(By.xpath("//body")).text().contains("principal=bburke");
 
-        assertSuccessfullyLoggedIn(salesPostSigServletPage);
+        assertSuccessfullyLoggedIn(salesPostSigServletPage, "principal=bburke");
 
         employee2ServletPage.logout();
+        checkLoggedOut(employee2ServletPage, testRealmSAMLPostLoginPage);
     }
 
     @Test
     public void idpInitiatedUnauthorizedLoginTest() {
-        samlidpInitiatedLogin.setAuthRealm(SAMLSERVLETDEMO);
-        samlidpInitiatedLogin.setUrlName("employee2");
-        samlidpInitiatedLogin.navigateTo();
-        samlidpInitiatedLogin.form().login("unauthorized", "password");
+        samlidpInitiatedLoginPage.setAuthRealm(SAMLSERVLETDEMO);
+        samlidpInitiatedLoginPage.setUrlName("employee2");
+        samlidpInitiatedLoginPage.navigateTo();
+        samlidpInitiatedLoginPage.form().login("unauthorized", "password");
 
         waitUntilElement(By.xpath("//body")).text().not().contains("bburke");
-        assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains("Status 403"));
+        assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains(FORBIDDEN_TEXT));
+
+        assertForbidden(employee2ServletPage, "principal=");
+        employee2ServletPage.logout();
+        checkLoggedOut(employee2ServletPage, testRealmSAMLPostLoginPage);
+    }
+
+    @Test
+    public void testSavedPostRequest() {
+        inputPortalPage.navigateTo();
+        assertCurrentUrlStartsWith(inputPortalPage);
+        inputPortalPage.execute("hello");
+
+        assertCurrentUrlStartsWith(testRealmSAMLPostLoginPage);
+        testRealmLoginPage.form().login("bburke@redhat.com", "password");
+        Assert.assertEquals(driver.getCurrentUrl(), inputPortalPage + "/secured/post");
+        waitUntilElement(By.xpath("//body")).text().contains("parameter=hello");
+
+        // test that user principal and KeycloakSecurityContext available
+        driver.navigate().to(inputPortalPage + "/insecure");
+        waitUntilElement(By.xpath("//body")).text().contains("Insecure Page");
+
+        if (System.getProperty("insecure.user.principal.unsupported") == null) waitUntilElement(By.xpath("//body")).text().contains("UserPrincipal");
+
+        // test logout
+
+        inputPortalPage.logout();
+
+        // test unsecured POST KEYCLOAK-901
 
-        assertForbidden(employee2ServletPage);
+        Client client = ClientBuilder.newClient();
+        Form form = new Form();
+        form.param("parameter", "hello");
+        String text = client.target(inputPortalPage + "/unsecured").request().post(Entity.form(form), String.class);
+        Assert.assertTrue(text.contains("parameter=hello"));
+        client.close();
+    }
+
+    @Test
+    public void testPostSimpleLoginLogoutIdpInitiatedRedirectTo() {
+        samlidpInitiatedLoginPage.setAuthRealm(SAMLSERVLETDEMO);
+        samlidpInitiatedLoginPage.setUrlName("sales-post2");
+        samlidpInitiatedLoginPage.navigateTo();
+
+        samlidpInitiatedLoginPage.form().login(bburkeUser);
+        assertCurrentUrlStartsWith(salesPost2ServletPage);
+        assertTrue(driver.getCurrentUrl().endsWith("/foo"));
+        waitUntilElement(By.xpath("//body")).text().contains("principal=bburke");
+        salesPost2ServletPage.logout();
+        checkLoggedOut(salesPost2ServletPage, testRealmSAMLPostLoginPage);
+    }
+
+    @Test
+    public void salesPostAssertionAndResponseSigTest() {
+        testSuccessfulAndUnauthorizedLogin(salesPostAssertionAndResponseSigPage, testRealmSAMLPostLoginPage);
+    }
+
+    @Test
+    public void testPostBadAssertionSignature() {
+        badAssertionSalesPostSigPage.navigateTo();
+        assertCurrentUrlStartsWith(testRealmSAMLPostLoginPage);
+        testRealmSAMLPostLoginPage.form().login("bburke", "password");
+
+        waitUntilElement(By.xpath("//body")).text().contains("Error info: SamlAuthenticationError [reason=INVALID_SIGNATURE, status=null]");
+        assertEquals(driver.getCurrentUrl(), badAssertionSalesPostSigPage + "/saml");
+    }
+
+    @Test
+    public void testMissingAssertionSignature() {
+        missingAssertionSigPage.navigateTo();
+        assertCurrentUrlStartsWith(testRealmSAMLPostLoginPage);
+        testRealmSAMLPostLoginPage.form().login("bburke", "password");
+
+        waitUntilElement(By.xpath("//body")).text().contains("Error info: SamlAuthenticationError [reason=INVALID_SIGNATURE, status=null]");
+        assertEquals(driver.getCurrentUrl(), missingAssertionSigPage + "/saml");
+    }
+
+    @Test
+    public void testErrorHandling() throws Exception {
+        Client client = ClientBuilder.newClient();
+        // make sure
+        Response response = client.target(employeeSigServletPage.toString()).request().get();
+        response.close();
+        SAML2ErrorResponseBuilder builder = new SAML2ErrorResponseBuilder()
+                .destination(employeeSigServletPage.toString() + "/saml")
+                .issuer("http://localhost:" + System.getProperty("auth.server.http.port", "8180") + "/realms/demo")
+                .status(JBossSAMLURIConstants.STATUS_REQUEST_DENIED.get());
+        BaseSAML2BindingBuilder binding = new BaseSAML2BindingBuilder()
+                .relayState(null);
+        Document document = builder.buildDocument();
+        URI uri = binding.redirectBinding(document).generateURI(employeeSigServletPage.toString() + "/saml", false);
+        response = client.target(uri).request().get();
+        String errorPage = response.readEntity(String.class);
+        response.close();
+        Assert.assertTrue(errorPage.contains("Error info: SamlAuthenticationError [reason=ERROR_STATUS"));
+        Assert.assertFalse(errorPage.contains("status=null"));
+        client.close();
+    }
+
+    @Test
+    public void testRelayStateEncoding() throws Exception {
+        // this test has a hardcoded SAMLRequest and we hack a SP face servlet to get the SAMLResponse so we can look
+        // at the relay state
+        employeeServletPage.navigateTo();
+        assertCurrentUrlStartsWith(testRealmSAMLPostLoginPage);
+        testRealmSAMLPostLoginPage.form().login("bburke", "password");
+        assertCurrentUrlStartsWith(employeeServletPage);
+        waitUntilElement(By.xpath("//body")).text().contains("Relay state: " + SamlSPFacade.RELAY_STATE);
+        waitUntilElement(By.xpath("//body")).text().not().contains("SAML response: null");
+    }
+
+    @Test
+    public void testAttributes() throws Exception {
+        ClientResource clientResource = ApiUtil.findClientResourceByClientId(testRealmResource(), "http://localhost:8081/employee2/");
+        ProtocolMappersResource protocolMappersResource = clientResource.getProtocolMappers();
+
+        Map<String, String> config = new LinkedHashMap<>();
+        config.put("attribute.nameformat", "Basic");
+        config.put("user.attribute", "topAttribute");
+        config.put("attribute.name", "topAttribute");
+        createProtocolMapper(protocolMappersResource, "topAttribute", "saml", "saml-user-attribute-mapper", config);
+
+        config = new LinkedHashMap<>();
+        config.put("attribute.nameformat", "Basic");
+        config.put("user.attribute", "level2Attribute");
+        config.put("attribute.name", "level2Attribute");
+        createProtocolMapper(protocolMappersResource, "level2Attribute", "saml", "saml-user-attribute-mapper", config);
+
+        config = new LinkedHashMap<>();
+        config.put("attribute.nameformat", "Basic");
+        config.put("single", "true");
+        config.put("attribute.name", "group");
+        createProtocolMapper(protocolMappersResource, "groups", "saml", "saml-group-membership-mapper", config);
+
+        setRolesToCheck("manager,user");
+
+        employee2ServletPage.navigateTo();
+        assertCurrentUrlStartsWith(testRealmSAMLPostLoginPage);
+        testRealmSAMLPostLoginPage.form().login("level2GroupUser", "password");
+
+        driver.navigate().to(employee2ServletPage.toString() + "/getAttributes");
+        waitUntilElement(By.xpath("//body")).text().contains("topAttribute: true");
+        waitUntilElement(By.xpath("//body")).text().contains("level2Attribute: true");
+        waitUntilElement(By.xpath("//body")).text().contains("attribute email: level2@redhat.com");
+        waitUntilElement(By.xpath("//body")).text().not().contains("group: []");
+        waitUntilElement(By.xpath("//body")).text().not().contains("group: null");
+        waitUntilElement(By.xpath("//body")).text().contains("group: [level2]");
+
+        employee2ServletPage.logout();
+        checkLoggedOut(employee2ServletPage, testRealmSAMLPostLoginPage);
+
+        setRolesToCheck("manager,employee,user");
+
+        employee2ServletPage.navigateTo();
+        assertCurrentUrlStartsWith(testRealmSAMLPostLoginPage);
+        testRealmSAMLPostLoginPage.form().login(bburkeUser);
+
+        driver.navigate().to(employee2ServletPage.toString() + "/getAttributes");
+        waitUntilElement(By.xpath("//body")).text().contains("attribute email: bburke@redhat.com");
+        waitUntilElement(By.xpath("//body")).text().contains("friendlyAttribute email: bburke@redhat.com");
+        waitUntilElement(By.xpath("//body")).text().contains("phone: 617");
+        waitUntilElement(By.xpath("//body")).text().contains("friendlyAttribute phone: null");
+
+        employee2ServletPage.logout();
+        checkLoggedOut(employee2ServletPage, testRealmSAMLPostLoginPage);
+
+        config = new LinkedHashMap<>();
+        config.put("attribute.value", "hard");
+        config.put("attribute.nameformat", "Basic");
+        config.put("attribute.name", "hardcoded-attribute");
+        createProtocolMapper(protocolMappersResource, "hardcoded-attribute", "saml", "saml-hardcode-attribute-mapper", config);
+
+        config = new LinkedHashMap<>();
+        config.put("role", "hardcoded-role");
+        createProtocolMapper(protocolMappersResource, "hardcoded-role", "saml", "saml-hardcode-role-mapper", config);
+
+        config = new LinkedHashMap<>();
+        config.put("new.role.name", "pee-on");
+        config.put("role", "http://localhost:8081/employee/.employee");
+        createProtocolMapper(protocolMappersResource, "renamed-employee-role", "saml", "saml-role-name-mapper", config);
+
+        for (ProtocolMapperRepresentation mapper : clientResource.toRepresentation().getProtocolMappers()) {
+            if (mapper.getName().equals("role-list")) {
+                protocolMappersResource.delete(mapper.getId());
+
+                mapper.setId(null);
+                mapper.getConfig().put(RoleListMapper.SINGLE_ROLE_ATTRIBUTE, "true");
+                mapper.getConfig().put(AttributeStatementHelper.SAML_ATTRIBUTE_NAME, "memberOf");
+                protocolMappersResource.createMapper(mapper);
+            }
+        }
+
+        setRolesToCheck("pee-on,el-jefe,manager,hardcoded-role");
+
+        config = new LinkedHashMap<>();
+        config.put("new.role.name", "el-jefe");
+        config.put("role", "user");
+        createProtocolMapper(protocolMappersResource, "renamed-role", "saml", "saml-role-name-mapper", config);
+
+        employee2ServletPage.navigateTo();
+        assertCurrentUrlStartsWith(testRealmSAMLPostLoginPage);
+        testRealmSAMLPostLoginPage.form().login(bburkeUser);
+
+        driver.navigate().to(employee2ServletPage.toString() + "/getAttributes");
+        waitUntilElement(By.xpath("//body")).text().contains("hardcoded-attribute: hard");
+        employee2ServletPage.checkRolesEndPoint(false);
+        employee2ServletPage.logout();
+        checkLoggedOut(employee2ServletPage, testRealmSAMLPostLoginPage);
+    }
+
+    private void createProtocolMapper(ProtocolMappersResource resource, String name, String protocol, String protocolMapper, Map<String, String> config) {
+        ProtocolMapperRepresentation representation = new ProtocolMapperRepresentation();
+        representation.setName(name);
+        representation.setProtocol(protocol);
+        representation.setProtocolMapper(protocolMapper);
+        representation.setConfig(config);
+        resource.createMapper(representation);
+    }
+
+    private void setRolesToCheck(String roles) {
+        employee2ServletPage.navigateTo();
+        assertCurrentUrlStartsWith(testRealmSAMLPostLoginPage);
+        testRealmSAMLPostLoginPage.form().login(bburkeUser);
+        driver.navigate().to(employee2ServletPage.toString() + "/setCheckRoles?roles=" + roles);
         employee2ServletPage.logout();
     }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java
index ee06750..b6f9bde 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java
@@ -33,16 +33,14 @@ import org.keycloak.representations.adapters.action.PushNotBeforeAction;
 import org.keycloak.representations.adapters.action.TestAvailabilityAction;
 import org.keycloak.representations.idm.*;
 
+import javax.ws.rs.BadRequestException;
 import javax.ws.rs.NotFoundException;
 import javax.ws.rs.core.Response;
 
 import java.io.IOException;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 
+import org.keycloak.services.ErrorResponseException;
 import org.keycloak.testsuite.Assert;
 import org.keycloak.testsuite.util.AdminEventPaths;
 import org.keycloak.testsuite.util.ClientBuilder;
@@ -212,6 +210,52 @@ public class ClientTest extends AbstractAdminTest {
         assertEquals("service-account-serviceclient", userRep.getUsername());
     }
 
+    // KEYCLOAK-3421
+    @Test
+    public void createClientWithFragments() {
+        ClientRepresentation client = ClientBuilder.create()
+                .clientId("client-with-fragment")
+                .rootUrl("http://localhost/base#someFragment")
+                .redirectUris("http://localhost/auth", "http://localhost/auth#fragment", "http://localhost/auth*", "/relative")
+                .build();
+
+        Response response = realm.clients().create(client);
+        assertUriFragmentError(response);
+    }
+
+    // KEYCLOAK-3421
+    @Test
+    public void updateClientWithFragments() {
+        ClientRepresentation client = ClientBuilder.create()
+                .clientId("client-with-fragment")
+                .redirectUris("http://localhost/auth", "http://localhost/auth*")
+                .build();
+        Response response = realm.clients().create(client);
+        ClientResource clientResource = realm.clients().get(ApiUtil.getCreatedId(response));
+
+        client = clientResource.toRepresentation();
+        client.setRootUrl("http://localhost/base#someFragment");
+        List<String> redirectUris = client.getRedirectUris();
+        redirectUris.add("http://localhost/auth#fragment");
+        redirectUris.add("/relative");
+        client.setRedirectUris(redirectUris);
+
+        try {
+            clientResource.update(client);
+            fail("Should fail");
+        }
+        catch (BadRequestException e) {
+            assertUriFragmentError(e.getResponse());
+        }
+    }
+
+    private void assertUriFragmentError(Response response) {
+        assertEquals(response.getStatus(), 400);
+        String error = response.readEntity(OAuth2ErrorRepresentation.class).getError();
+        assertTrue("Error response doesn't mention Redirect URIs fragments", error.contains("Redirect URIs must not contain an URI fragment"));
+        assertTrue("Error response doesn't mention Root URL fragments", error.contains("Root URL must not contain an URL fragment"));
+    }
+
     @Test
     public void pushRevocation() {
         testingClient.testApp().clearAdminActions();
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCClientRegistrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCClientRegistrationTest.java
index 5d7d2c1..19d6413 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCClientRegistrationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCClientRegistrationTest.java
@@ -35,24 +35,34 @@ import org.keycloak.common.util.CollectionUtil;
 import org.keycloak.common.util.KeycloakUriBuilder;
 import org.keycloak.constants.ServiceUrlConstants;
 import org.keycloak.jose.jwk.JSONWebKeySet;
+import org.keycloak.jose.jws.Algorithm;
 import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
 import org.keycloak.protocol.oidc.utils.OIDCResponseType;
 import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.JsonWebToken;
 import org.keycloak.representations.idm.ClientInitialAccessCreatePresentation;
 import org.keycloak.representations.idm.ClientInitialAccessPresentation;
 import org.keycloak.representations.idm.ClientRegistrationTrustedHostRepresentation;
+import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.representations.oidc.OIDCClientRepresentation;
 import org.keycloak.testsuite.Assert;
+import org.keycloak.testsuite.client.resources.TestApplicationResourceUrls;
+import org.keycloak.testsuite.client.resources.TestOIDCEndpointsApplicationResource;
+import org.keycloak.testsuite.rest.resource.TestingOIDCEndpointsApplicationResource;
 import org.keycloak.testsuite.util.OAuthClient;
 import java.security.PrivateKey;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 
 import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
 
 import static org.junit.Assert.*;
 import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
@@ -96,24 +106,34 @@ public class OIDCClientRegistrationTest extends AbstractClientRegistrationTest {
         return response;
     }
 
-    @Test
-    public void testCreateWithTrustedHost() throws Exception {
-        reg.auth(null);
-
-        OIDCClientRepresentation client = createRep();
+    private void assertCreateFail(OIDCClientRepresentation client, int expectedStatusCode) {
+        assertCreateFail(client, expectedStatusCode, null);
+    }
 
-        // Failed to create client
+    private void assertCreateFail(OIDCClientRepresentation client, int expectedStatusCode, String expectedErrorContains) {
         try {
             reg.oidc().create(client);
             Assert.fail("Not expected to successfuly register client");
         } catch (ClientRegistrationException expected) {
             HttpErrorException httpEx = (HttpErrorException) expected.getCause();
-            Assert.assertEquals(401, httpEx.getStatusLine().getStatusCode());
+            Assert.assertEquals(expectedStatusCode, httpEx.getStatusLine().getStatusCode());
+            if (expectedErrorContains != null) {
+                assertTrue("Error response doesn't contain expected text", httpEx.getErrorResponse().contains(expectedErrorContains));
+            }
         }
+    }
+
+    @Test
+    public void testCreateWithTrustedHost() throws Exception {
+        reg.auth(null);
+
+        OIDCClientRepresentation client = createRep();
+
+        // Failed to create client
+        assertCreateFail(client, 401);
 
         // Create trusted host entry
-        Response response = adminClient.realm(REALM_NAME).clientRegistrationTrustedHost().create(ClientRegistrationTrustedHostRepresentation.create("localhost", 2, 2));
-        Assert.assertEquals(201, response.getStatus());
+        createTrustedHost("localhost", 2);
 
         // Successfully register client
         reg.oidc().create(client);
@@ -126,13 +146,20 @@ public class OIDCClientRegistrationTest extends AbstractClientRegistrationTest {
         reg.oidc().create(client);
 
         // Failed to create 3rd client
-        try {
-            reg.oidc().create(client);
-            Assert.fail("Not expected to successfuly register client");
-        } catch (ClientRegistrationException expected) {
-            HttpErrorException httpEx = (HttpErrorException) expected.getCause();
-            Assert.assertEquals(401, httpEx.getStatusLine().getStatusCode());
-        }
+        assertCreateFail(client, 401);
+    }
+
+    // KEYCLOAK-3421
+    @Test
+    public void createClientWithUriFragment() {
+        reg.auth(null);
+
+        createTrustedHost("localhost", 1);
+
+        OIDCClientRepresentation client = createRep();
+        client.setRedirectUris(Arrays.asList("http://localhost/auth", "http://localhost/auth#fragment", "http://localhost/auth*"));
+
+        assertCreateFail(client, 400, "URI fragment");
     }
 
     @Test
@@ -152,6 +179,7 @@ public class OIDCClientRegistrationTest extends AbstractClientRegistrationTest {
         assertEquals(Arrays.asList("code", "none"), response.getResponseTypes());
         assertEquals(Arrays.asList(OAuth2Constants.AUTHORIZATION_CODE, OAuth2Constants.REFRESH_TOKEN), response.getGrantTypes());
         assertEquals(OIDCLoginProtocol.CLIENT_SECRET_BASIC, response.getTokenEndpointAuthMethod());
+        Assert.assertNull(response.getUserinfoSignedResponseAlg());
     }
 
     @Test
@@ -212,8 +240,11 @@ public class OIDCClientRegistrationTest extends AbstractClientRegistrationTest {
         clientRep.setGrantTypes(Collections.singletonList(OAuth2Constants.CLIENT_CREDENTIALS));
         clientRep.setTokenEndpointAuthMethod(OIDCLoginProtocol.PRIVATE_KEY_JWT);
 
-        // Corresponds to PRIVATE_KEY
-        JSONWebKeySet keySet = loadJson(getClass().getResourceAsStream("/clientreg-test/jwks.json"), JSONWebKeySet.class);
+        // Generate keys for client
+        TestOIDCEndpointsApplicationResource oidcClientEndpointsResource = testingClient.testApp().oidcClientEndpoints();
+        Map<String, String> generatedKeys = oidcClientEndpointsResource.generateKeys();
+
+        JSONWebKeySet keySet = oidcClientEndpointsResource.getJwks();
         clientRep.setJwks(keySet);
 
         OIDCClientRepresentation response = reg.oidc().create(clientRep);
@@ -222,7 +253,7 @@ public class OIDCClientRegistrationTest extends AbstractClientRegistrationTest {
         Assert.assertNull(response.getClientSecretExpiresAt());
 
         // Tries to authenticate client with privateKey JWT
-        String signedJwt = getClientSignedJWT(response.getClientId());
+        String signedJwt = getClientSignedJWT(response.getClientId(), generatedKeys.get(TestingOIDCEndpointsApplicationResource.PRIVATE_KEY));
         OAuthClient.AccessTokenResponse accessTokenResponse = doClientCredentialsGrantRequest(signedJwt);
         Assert.assertEquals(200, accessTokenResponse.getStatusCode());
         AccessToken accessToken = oauth.verifyToken(accessTokenResponse.getAccessToken());
@@ -236,8 +267,11 @@ public class OIDCClientRegistrationTest extends AbstractClientRegistrationTest {
         clientRep.setGrantTypes(Collections.singletonList(OAuth2Constants.CLIENT_CREDENTIALS));
         clientRep.setTokenEndpointAuthMethod(OIDCLoginProtocol.PRIVATE_KEY_JWT);
 
-        // Use the realmKey for client authentication too
-        clientRep.setJwksUri(oauth.getCertsUrl(REALM_NAME));
+        // Generate keys for client
+        TestOIDCEndpointsApplicationResource oidcClientEndpointsResource = testingClient.testApp().oidcClientEndpoints();
+        Map<String, String> generatedKeys = oidcClientEndpointsResource.generateKeys();
+
+        clientRep.setJwksUri(TestApplicationResourceUrls.clientJwksUri());
 
         OIDCClientRepresentation response = reg.oidc().create(clientRep);
         Assert.assertEquals(OIDCLoginProtocol.PRIVATE_KEY_JWT, response.getTokenEndpointAuthMethod());
@@ -245,22 +279,51 @@ public class OIDCClientRegistrationTest extends AbstractClientRegistrationTest {
         Assert.assertNull(response.getClientSecretExpiresAt());
 
         // Tries to authenticate client with privateKey JWT
-        String signedJwt = getClientSignedJWT(response.getClientId());
+        String signedJwt = getClientSignedJWT(response.getClientId(), generatedKeys.get(TestingOIDCEndpointsApplicationResource.PRIVATE_KEY));
         OAuthClient.AccessTokenResponse accessTokenResponse = doClientCredentialsGrantRequest(signedJwt);
         Assert.assertEquals(200, accessTokenResponse.getStatusCode());
         AccessToken accessToken = oauth.verifyToken(accessTokenResponse.getAccessToken());
         Assert.assertEquals(response.getClientId(), accessToken.getAudience()[0]);
     }
 
+    @Test
+    public void testSignaturesRequired() throws Exception {
+        OIDCClientRepresentation clientRep = createRep();
+        clientRep.setUserinfoSignedResponseAlg(Algorithm.RS256.toString());
+        clientRep.setRequestObjectSigningAlg(Algorithm.RS256.toString());
+
+        OIDCClientRepresentation response = reg.oidc().create(clientRep);
+        Assert.assertEquals(Algorithm.RS256.toString(), response.getUserinfoSignedResponseAlg());
+        Assert.assertEquals(Algorithm.RS256.toString(), response.getRequestObjectSigningAlg());
+        Assert.assertNotNull(response.getClientSecret());
+
+        // Test Keycloak representation
+        ClientRepresentation kcClient = getClient(response.getClientId());
+        OIDCAdvancedConfigWrapper config = OIDCAdvancedConfigWrapper.fromClientRepresentation(kcClient);
+        Assert.assertEquals(config.getUserInfoSignedResponseAlg(), Algorithm.RS256);
+        Assert.assertEquals(config.getRequestObjectSignatureAlg(), Algorithm.RS256);
+    }
+
 
     // Client auth with signedJWT - helper methods
 
-    private String getClientSignedJWT(String clientId) {
+    private String getClientSignedJWT(String clientId, String privateKeyPem) {
         String realmInfoUrl = KeycloakUriBuilder.fromUri(getAuthServerRoot()).path(ServiceUrlConstants.REALM_INFO_PATH).build(REALM_NAME).toString();
 
-        PrivateKey privateKey = KeycloakModelUtils.getPrivateKey(PRIVATE_KEY);
+        PrivateKey privateKey = KeycloakModelUtils.getPrivateKey(privateKeyPem);
 
-        JWTClientCredentialsProvider jwtProvider = new JWTClientCredentialsProvider();
+        // Use token-endpoint as audience as OIDC conformance testsuite is using it too.
+        JWTClientCredentialsProvider jwtProvider = new JWTClientCredentialsProvider() {
+
+            @Override
+            protected JsonWebToken createRequestToken(String clientId, String realmInfoUrl) {
+                JsonWebToken jwt = super.createRequestToken(clientId, realmInfoUrl);
+                String tokenEndpointUrl = OIDCLoginProtocolService.tokenUrl(UriBuilder.fromUri(getAuthServerRoot())).build(REALM_NAME).toString();
+                jwt.audience(tokenEndpointUrl);
+                return jwt;
+            }
+
+        };
         jwtProvider.setPrivateKey(privateKey);
         jwtProvider.setTokenTimeout(10);
         return jwtProvider.createSignedRequestToken(clientId, realmInfoUrl);
@@ -290,4 +353,9 @@ public class OIDCClientRegistrationTest extends AbstractClientRegistrationTest {
         }
     }
 
+    private void createTrustedHost(String name, int count) {
+        Response response = adminClient.realm(REALM_NAME).clientRegistrationTrustedHost().create(ClientRegistrationTrustedHostRepresentation.create(name, count, count));
+        Assert.assertEquals(201, response.getStatus());
+    }
+
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java
index 6abaf97..43c6fa9 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java
@@ -75,11 +75,11 @@ public class ExportImportTest extends AbstractExportImportTest {
 
     @Test
     public void testDirFullExportImport() throws Throwable {
-        testingClient.testing().setProvider(DirExportProviderFactory.PROVIDER_ID);
-        String targetDirPath = testingClient.testing().getExportImportTestDirectory()+ File.separator + "dirExport";
+        testingClient.testing().exportImport().setProvider(DirExportProviderFactory.PROVIDER_ID);
+        String targetDirPath = testingClient.testing().exportImport().getExportImportTestDirectory()+ File.separator + "dirExport";
         DirExportProvider.recursiveDeleteDir(new File(targetDirPath));
-        testingClient.testing().setDir(targetDirPath);
-        testingClient.testing().setUsersPerFile(ExportImportConfig.DEFAULT_USERS_PER_FILE);
+        testingClient.testing().exportImport().setDir(targetDirPath);
+        testingClient.testing().exportImport().setUsersPerFile(ExportImportConfig.DEFAULT_USERS_PER_FILE);
 
         testFullExportImport();
 
@@ -89,11 +89,13 @@ public class ExportImportTest extends AbstractExportImportTest {
 
     @Test
     public void testDirRealmExportImport() throws Throwable {
-        testingClient.testing().setProvider(DirExportProviderFactory.PROVIDER_ID);
-        String targetDirPath = testingClient.testing().getExportImportTestDirectory() + File.separator + "dirRealmExport";
+        testingClient.testing()
+                .exportImport()
+                .setProvider(DirExportProviderFactory.PROVIDER_ID);
+        String targetDirPath = testingClient.testing().exportImport().getExportImportTestDirectory() + File.separator + "dirRealmExport";
         DirExportProvider.recursiveDeleteDir(new File(targetDirPath));
-        testingClient.testing().setDir(targetDirPath);
-        testingClient.testing().setUsersPerFile(3);
+        testingClient.testing().exportImport().setDir(targetDirPath);
+        testingClient.testing().exportImport().setUsersPerFile(3);
 
         testRealmExportImport();
 
@@ -104,18 +106,18 @@ public class ExportImportTest extends AbstractExportImportTest {
 
     @Test
     public void testSingleFileFullExportImport() throws Throwable {
-        testingClient.testing().setProvider(SingleFileExportProviderFactory.PROVIDER_ID);
-        String targetFilePath = testingClient.testing().getExportImportTestDirectory() + File.separator + "singleFile-full.json";
-        testingClient.testing().setFile(targetFilePath);
+        testingClient.testing().exportImport().setProvider(SingleFileExportProviderFactory.PROVIDER_ID);
+        String targetFilePath = testingClient.testing().exportImport().getExportImportTestDirectory() + File.separator + "singleFile-full.json";
+        testingClient.testing().exportImport().setFile(targetFilePath);
 
         testFullExportImport();
     }
 
     @Test
     public void testSingleFileRealmExportImport() throws Throwable {
-        testingClient.testing().setProvider(SingleFileExportProviderFactory.PROVIDER_ID);
-        String targetFilePath = testingClient.testing().getExportImportTestDirectory() + File.separator + "singleFile-realm.json";
-        testingClient.testing().setFile(targetFilePath);
+        testingClient.testing().exportImport().setProvider(SingleFileExportProviderFactory.PROVIDER_ID);
+        String targetFilePath = testingClient.testing().exportImport().getExportImportTestDirectory() + File.separator + "singleFile-realm.json";
+        testingClient.testing().exportImport().setFile(targetFilePath);
 
         testRealmExportImport();
     }
@@ -126,14 +128,14 @@ public class ExportImportTest extends AbstractExportImportTest {
         removeRealm("test-realm");
 
         // Set the realm, which doesn't have builtin clients/roles inside JSON
-        testingClient.testing().setProvider(SingleFileExportProviderFactory.PROVIDER_ID);
+        testingClient.testing().exportImport().setProvider(SingleFileExportProviderFactory.PROVIDER_ID);
         URL url = ExportImportTest.class.getResource("/model/testrealm.json");
         String targetFilePath = new File(url.getFile()).getAbsolutePath();
-        testingClient.testing().setFile(targetFilePath);
+        testingClient.testing().exportImport().setFile(targetFilePath);
 
-        testingClient.testing().setAction(ExportImportConfig.ACTION_IMPORT);
+        testingClient.testing().exportImport().setAction(ExportImportConfig.ACTION_IMPORT);
 
-        testingClient.testing().runImport();
+        testingClient.testing().exportImport().runImport();
 
         RealmResource testRealmRealm = adminClient.realm("test-realm");
 
@@ -158,14 +160,14 @@ public class ExportImportTest extends AbstractExportImportTest {
         realm.components().add(component);
 
 
-        testingClient.testing().setProvider(SingleFileExportProviderFactory.PROVIDER_ID);
+        testingClient.testing().exportImport().setProvider(SingleFileExportProviderFactory.PROVIDER_ID);
 
-        String targetFilePath = testingClient.testing().getExportImportTestDirectory() + File.separator + "singleFile-realm.json";
-        testingClient.testing().setFile(targetFilePath);
-        testingClient.testing().setAction(ExportImportConfig.ACTION_EXPORT);
-        testingClient.testing().setRealmName("component-realm");
+        String targetFilePath = testingClient.testing().exportImport().getExportImportTestDirectory() + File.separator + "singleFile-realm.json";
+        testingClient.testing().exportImport().setFile(targetFilePath);
+        testingClient.testing().exportImport().setAction(ExportImportConfig.ACTION_EXPORT);
+        testingClient.testing().exportImport().setRealmName("component-realm");
 
-        testingClient.testing().runExport();
+        testingClient.testing().exportImport().runExport();
 
         // Delete some realm (and some data in admin realm)
         adminClient.realm("component-realm").remove();
@@ -173,9 +175,9 @@ public class ExportImportTest extends AbstractExportImportTest {
         Assert.assertEquals(3, adminClient.realms().findAll().size());
 
         // Configure import
-        testingClient.testing().setAction(ExportImportConfig.ACTION_IMPORT);
+        testingClient.testing().exportImport().setAction(ExportImportConfig.ACTION_IMPORT);
 
-        testingClient.testing().runImport();
+        testingClient.testing().exportImport().runImport();
 
         realmRep = realm.toRepresentation();
 
@@ -203,10 +205,10 @@ public class ExportImportTest extends AbstractExportImportTest {
     }
 
     private void testFullExportImport() throws LifecycleException {
-        testingClient.testing().setAction(ExportImportConfig.ACTION_EXPORT);
-        testingClient.testing().setRealmName("");
+        testingClient.testing().exportImport().setAction(ExportImportConfig.ACTION_EXPORT);
+        testingClient.testing().exportImport().setRealmName("");
 
-        testingClient.testing().runExport();
+        testingClient.testing().exportImport().runExport();
 
         removeRealm("test");
         removeRealm("test-realm");
@@ -218,9 +220,9 @@ public class ExportImportTest extends AbstractExportImportTest {
         assertNotAuthenticated("test", "user3", "password");
 
         // Configure import
-        testingClient.testing().setAction(ExportImportConfig.ACTION_IMPORT);
+        testingClient.testing().exportImport().setAction(ExportImportConfig.ACTION_IMPORT);
 
-        testingClient.testing().runImport();
+        testingClient.testing().exportImport().runImport();
 
         // Ensure data are imported back
         Assert.assertEquals(3, adminClient.realms().findAll().size());
@@ -232,10 +234,10 @@ public class ExportImportTest extends AbstractExportImportTest {
     }
 
     private void testRealmExportImport() throws LifecycleException {
-        testingClient.testing().setAction(ExportImportConfig.ACTION_EXPORT);
-        testingClient.testing().setRealmName("test");
+        testingClient.testing().exportImport().setAction(ExportImportConfig.ACTION_EXPORT);
+        testingClient.testing().exportImport().setRealmName("test");
 
-        testingClient.testing().runExport();
+        testingClient.testing().exportImport().runExport();
 
         // Delete some realm (and some data in admin realm)
         adminClient.realm("test").remove();
@@ -248,9 +250,9 @@ public class ExportImportTest extends AbstractExportImportTest {
         assertNotAuthenticated("test", "user3", "password");
 
         // Configure import
-        testingClient.testing().setAction(ExportImportConfig.ACTION_IMPORT);
+        testingClient.testing().exportImport().setAction(ExportImportConfig.ACTION_IMPORT);
 
-        testingClient.testing().runImport();
+        testingClient.testing().exportImport().runImport();
 
         // Ensure data are imported back, but just for "test" realm
         Assert.assertEquals(3, adminClient.realms().findAll().size());
@@ -273,27 +275,4 @@ public class ExportImportTest extends AbstractExportImportTest {
         Assert.assertEquals(expectedResult, testingClient.testing().validCredentials(realmName, username, password));
     }
 
-    private static String getExportImportTestDirectory() {
-        String dirPath = null;
-        String relativeDirExportImportPath = "testsuite" + File.separator +
-                                             "integration-arquillian" + File.separator +
-                                             "tests" + File.separator +
-                                             "base" + File.separator +
-                                             "target" + File.separator +
-                                             "export-import";
-
-        if (System.getProperties().containsKey("maven.home")) {
-            dirPath = System.getProperty("user.dir").replaceFirst("testsuite.integration.*", Matcher.quoteReplacement(relativeDirExportImportPath));
-        } else {
-            for (String c : System.getProperty("java.class.path").split(File.pathSeparator)) {
-                if (c.contains(File.separator + "testsuite" + File.separator + "integration-arquillian" + File.separator)) {
-                    dirPath = c.replaceFirst("testsuite.integration-arquillian.*", Matcher.quoteReplacement(relativeDirExportImportPath));
-                }
-            }
-        }
-
-        String absolutePath = new File(dirPath).getAbsolutePath();
-        return absolutePath;
-    }
-
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/ClientAuthSignedJWTTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/ClientAuthSignedJWTTest.java
index 087a1e6..c1cbdea 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/ClientAuthSignedJWTTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/ClientAuthSignedJWTTest.java
@@ -55,7 +55,9 @@ import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.EventRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.services.util.CertificateInfoHelper;
 import org.keycloak.testsuite.AbstractKeycloakTest;
+import org.keycloak.testsuite.Assert;
 import org.keycloak.testsuite.AssertEvents;
 import org.keycloak.testsuite.admin.ApiUtil;
 import org.keycloak.testsuite.auth.page.AuthRealm;
@@ -70,6 +72,7 @@ import java.net.URL;
 import java.nio.file.Files;
 import java.security.KeyStore;
 import java.security.PrivateKey;
+import java.security.PublicKey;
 import java.security.cert.X509Certificate;
 import java.util.*;
 
@@ -81,7 +84,6 @@ import static org.junit.Assert.assertNotEquals;
  * @author Vaclav Muzikar <vmuzikar@redhat.com>
  */
 public class ClientAuthSignedJWTTest extends AbstractKeycloakTest {
-    public static final String CERTIFICATE_PEM = "Certificate PEM";
 
     @Rule
     public AssertEvents events = new AssertEvents(this);
@@ -349,8 +351,18 @@ public class ClientAuthSignedJWTTest extends AbstractKeycloakTest {
     }
 
     @Test
-    public void testUploadPEM() throws Exception {
-        testUploadKeystore(CERTIFICATE_PEM, "client-auth-test/certificate.pem", "undefined", "undefined");
+    public void testUploadCertificatePEM() throws Exception {
+        testUploadKeystore(org.keycloak.services.resources.admin.ClientAttributeCertificateResource.CERTIFICATE_PEM, "client-auth-test/certificate.pem", "undefined", "undefined");
+    }
+
+    @Test
+    public void testUploadPublicKeyPEM() throws Exception {
+        testUploadKeystore(org.keycloak.services.resources.admin.ClientAttributeCertificateResource.PUBLIC_KEY_PEM, "client-auth-test/publickey.pem", "undefined", "undefined");
+    }
+
+    @Test
+    public void testUploadJWKS() throws Exception {
+        testUploadKeystore(org.keycloak.services.resources.admin.ClientAttributeCertificateResource.JSON_WEB_KEY_SET, "clientreg-test/jwks.json", "undefined", "undefined");
     }
 
     // We need to test this as a genuine REST API HTTP request
@@ -395,20 +407,27 @@ public class ClientAuthSignedJWTTest extends AbstractKeycloakTest {
         assertEquals(200, httpResponse.getStatusLine().getStatusCode());
 
         client = getClient(testRealm.getRealm(), client.getId()).toRepresentation();
-        String pem;
 
         // Assert the uploaded certificate
-        if (!keystoreFormat.equals(CERTIFICATE_PEM)) {
+        if (keystoreFormat.equals(org.keycloak.services.resources.admin.ClientAttributeCertificateResource.PUBLIC_KEY_PEM)) {
+            String pem = new String(Files.readAllBytes(keystoreFile.toPath()));
+            final String publicKeyNew = client.getAttributes().get(JWTClientAuthenticator.ATTR_PREFIX + "." + CertificateInfoHelper.PUBLIC_KEY);
+            assertEquals("Certificates don't match", pem, publicKeyNew);
+        } else if (keystoreFormat.equals(org.keycloak.services.resources.admin.ClientAttributeCertificateResource.JSON_WEB_KEY_SET)) {
+            final String publicKeyNew = client.getAttributes().get(JWTClientAuthenticator.ATTR_PREFIX + "." + CertificateInfoHelper.PUBLIC_KEY);
+            // Just assert it's valid public key
+            PublicKey pk = KeycloakModelUtils.getPublicKey(publicKeyNew);
+            Assert.assertNotNull(pk);
+        } else if (keystoreFormat.equals(org.keycloak.services.resources.admin.ClientAttributeCertificateResource.CERTIFICATE_PEM)) {
+            String pem = new String(Files.readAllBytes(keystoreFile.toPath()));
+            assertCertificate(client, certOld, pem);
+        } else {
             InputStream keystoreIs = new FileInputStream(keystoreFile);
             KeyStore keyStore = getKeystore(keystoreIs, storePassword, keystoreFormat);
             keystoreIs.close();
-            pem = KeycloakModelUtils.getPemFromCertificate((X509Certificate) keyStore.getCertificate(keyAlias));
+            String pem = KeycloakModelUtils.getPemFromCertificate((X509Certificate) keyStore.getCertificate(keyAlias));
+            assertCertificate(client, certOld, pem);
         }
-        else {
-            pem = new String(Files.readAllBytes(keystoreFile.toPath()));
-        }
-
-        assertCertificate(client, certOld, pem);
     }
 
     // TEST ERRORS
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OAuthRedirectUriTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OAuthRedirectUriTest.java
index f80e789..520f910 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OAuthRedirectUriTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OAuthRedirectUriTest.java
@@ -97,6 +97,11 @@ public class OAuthRedirectUriTest extends AbstractKeycloakTest {
                 .secret("password");
         realm.client(installedApp6);
 
+        ClientBuilder installedApp7 = ClientBuilder.create().id("test-query-component").name("test-query-component")
+                .redirectUris("http://localhost?foo=bar", "http://localhost?foo=bar*")
+                .secret("password");
+        realm.client(installedApp7);
+
         testRealms.add(realm.build());
     }
 
@@ -186,6 +191,26 @@ public class OAuthRedirectUriTest extends AbstractKeycloakTest {
     }
 
     @Test
+    public void testQueryComponents() throws IOException {
+        // KEYCLOAK-3420
+        oauth.clientId("test-query-component");
+        checkRedirectUri("http://localhost?foo=bar", true);
+        checkRedirectUri("http://localhost?foo=bara", false);
+        checkRedirectUri("http://localhost?foo=bar/", false);
+        checkRedirectUri("http://localhost?foo2=bar2&foo=bar", false);
+        checkRedirectUri("http://localhost?foo=b", false);
+        checkRedirectUri("http://localhost?foo", false);
+        checkRedirectUri("http://localhost?foo=bar&bar=foo", false);
+        checkRedirectUri("http://localhost?foo&bar=foo", false);
+        checkRedirectUri("http://localhost?foo&bar", false);
+        checkRedirectUri("http://localhost", false);
+
+        // KEYCLOAK-3418
+        oauth.clientId("test-installed");
+        checkRedirectUri("http://localhost?foo=bar", false);
+    }
+
+    @Test
     public void testWildcard() throws IOException {
         oauth.clientId("test-wildcard");
         checkRedirectUri("http://example.com", false);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCAdvancedRequestParamsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCAdvancedRequestParamsTest.java
index 191581e..e01057d 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCAdvancedRequestParamsTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCAdvancedRequestParamsTest.java
@@ -19,30 +19,42 @@ package org.keycloak.testsuite.oidc;
 
 import java.util.List;
 
-
 import org.jboss.arquillian.graphene.page.Page;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.keycloak.OAuthErrorException;
+import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator;
 import org.keycloak.common.util.Time;
 import org.keycloak.events.Details;
+import org.keycloak.jose.jws.Algorithm;
 import org.keycloak.models.Constants;
+import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
 import org.keycloak.representations.IDToken;
+import org.keycloak.representations.idm.CertificateRepresentation;
+import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.EventRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.services.util.CertificateInfoHelper;
 import org.keycloak.testsuite.Assert;
 import org.keycloak.testsuite.AssertEvents;
 import org.keycloak.testsuite.TestRealmKeycloakTest;
 import org.keycloak.testsuite.admin.AbstractAdminTest;
+import org.keycloak.testsuite.admin.ApiUtil;
+import org.keycloak.testsuite.client.resources.TestApplicationResourceUrls;
+import org.keycloak.testsuite.client.resources.TestOIDCEndpointsApplicationResource;
 import org.keycloak.testsuite.pages.AccountUpdateProfilePage;
 import org.keycloak.testsuite.pages.AppPage;
+import org.keycloak.testsuite.pages.ErrorPage;
 import org.keycloak.testsuite.pages.LoginPage;
 import org.keycloak.testsuite.pages.OAuthGrantPage;
+import org.keycloak.testsuite.rest.resource.TestingOIDCEndpointsApplicationResource;
 import org.keycloak.testsuite.util.ClientManager;
 import org.keycloak.testsuite.util.OAuthClient;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
@@ -68,6 +80,9 @@ public class OIDCAdvancedRequestParamsTest extends TestRealmKeycloakTest {
     @Page
     protected OAuthGrantPage grantPage;
 
+    @Page
+    protected ErrorPage errorPage;
+
 
     @Override
     public void configureTestRealm(RealmRepresentation testRealm) {
@@ -308,29 +323,98 @@ public class OIDCAdvancedRequestParamsTest extends TestRealmKeycloakTest {
     // REQUEST & REQUEST_URI
 
     @Test
-    public void requestParam() {
-        driver.navigate().to(oauth.getLoginFormUrl() + "&request=abc");
+    public void requestParamUnsigned() throws Exception {
+        String validRedirectUri = oauth.getRedirectUri();
+        TestOIDCEndpointsApplicationResource oidcClientEndpointsResource = testingClient.testApp().oidcClientEndpoints();
 
-        assertFalse(loginPage.isCurrent());
+        // Send request object with invalid redirect uri.
+        oidcClientEndpointsResource.setOIDCRequest("test", "test-app", "http://invalid", null, Algorithm.none.toString());
+        String requestStr = oidcClientEndpointsResource.getOIDCRequest();
+
+        oauth.request(requestStr);
+        oauth.openLoginForm();
+        Assert.assertTrue(errorPage.isCurrent());
+        assertEquals("Invalid parameter: redirect_uri", errorPage.getError());
+
+        // Assert the value from request object has bigger priority then from the query parameter.
+        oauth.redirectUri("http://invalid");
+        oidcClientEndpointsResource.setOIDCRequest("test", "test-app", validRedirectUri, "10", Algorithm.none.toString());
+        requestStr = oidcClientEndpointsResource.getOIDCRequest();
+
+        oauth.request(requestStr);
+        OAuthClient.AuthorizationEndpointResponse response = oauth.doLogin("test-user@localhost", "password");
+        Assert.assertNotNull(response.getCode());
+        Assert.assertEquals("mystate", response.getState());
         assertTrue(appPage.isCurrent());
+    }
 
-        // Assert error response was sent because not logged in
-        OAuthClient.AuthorizationEndpointResponse resp = new OAuthClient.AuthorizationEndpointResponse(oauth);
-        Assert.assertNull(resp.getCode());
-        Assert.assertEquals(OAuthErrorException.REQUEST_NOT_SUPPORTED, resp.getError());
+    @Test
+    public void requestUriParamUnsigned() throws Exception {
+        String validRedirectUri = oauth.getRedirectUri();
+        TestOIDCEndpointsApplicationResource oidcClientEndpointsResource = testingClient.testApp().oidcClientEndpoints();
+
+        // Send request object with invalid redirect uri.
+        oidcClientEndpointsResource.setOIDCRequest("test", "test-app", "http://invalid", null, Algorithm.none.toString());
+
+        oauth.requestUri(TestApplicationResourceUrls.clientRequestUri());
+        oauth.openLoginForm();
+        Assert.assertTrue(errorPage.isCurrent());
+        assertEquals("Invalid parameter: redirect_uri", errorPage.getError());
+
+        // Assert the value from request object has bigger priority then from the query parameter.
+        oauth.redirectUri("http://invalid");
+        oidcClientEndpointsResource.setOIDCRequest("test", "test-app", validRedirectUri, "10", Algorithm.none.toString());
+
+        OAuthClient.AuthorizationEndpointResponse response = oauth.doLogin("test-user@localhost", "password");
+        Assert.assertNotNull(response.getCode());
+        Assert.assertEquals("mystate", response.getState());
+        assertTrue(appPage.isCurrent());
     }
 
     @Test
-    public void requestUriParam() {
-        driver.navigate().to(oauth.getLoginFormUrl() + "&request_uri=https%3A%2F%2Flocalhost%3A60784%2Fexport%2FqzHTG11W48.jwt");
+    public void requestUriParamSigned() throws Exception {
+        String validRedirectUri = oauth.getRedirectUri();
+        TestOIDCEndpointsApplicationResource oidcClientEndpointsResource = testingClient.testApp().oidcClientEndpoints();
+
+        // Set required signature for request_uri
+        ClientResource clientResource = ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app");
+        ClientRepresentation clientRep = clientResource.toRepresentation();
+        OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setRequestObjectSignatureAlg(Algorithm.RS256);
+        clientResource.update(clientRep);
+
+        // Verify unsigned request_uri will fail
+        oidcClientEndpointsResource.setOIDCRequest("test", "test-app", validRedirectUri, "10", Algorithm.none.toString());
+        oauth.requestUri(TestApplicationResourceUrls.clientRequestUri());
+        oauth.openLoginForm();
+        Assert.assertTrue(errorPage.isCurrent());
+        assertEquals("Invalid Request", errorPage.getError());
 
-        assertFalse(loginPage.isCurrent());
+        // Generate keypair for client
+        String clientPublicKeyPem = oidcClientEndpointsResource.generateKeys().get(TestingOIDCEndpointsApplicationResource.PUBLIC_KEY);
+
+        // Verify signed request_uri will fail due to failed signature validation
+        oidcClientEndpointsResource.setOIDCRequest("test", "test-app", validRedirectUri, "10", Algorithm.RS256.toString());
+        oauth.openLoginForm();
+        Assert.assertTrue(errorPage.isCurrent());
+        assertEquals("Invalid Request", errorPage.getError());
+
+
+        // Update clientModel with publicKey for signing
+        clientRep = clientResource.toRepresentation();
+        CertificateRepresentation cert = new CertificateRepresentation();
+        cert.setPublicKey(clientPublicKeyPem);
+        CertificateInfoHelper.updateClientRepresentationCertificateInfo(clientRep, cert, JWTClientAuthenticator.ATTR_PREFIX);
+        clientResource.update(clientRep);
+
+        // Check signed request_uri will pass
+        OAuthClient.AuthorizationEndpointResponse response = oauth.doLogin("test-user@localhost", "password");
+        Assert.assertNotNull(response.getCode());
+        Assert.assertEquals("mystate", response.getState());
         assertTrue(appPage.isCurrent());
 
-        // Assert error response was sent because not logged in
-        OAuthClient.AuthorizationEndpointResponse resp = new OAuthClient.AuthorizationEndpointResponse(oauth);
-        Assert.assertNull(resp.getCode());
-        Assert.assertEquals(OAuthErrorException.REQUEST_URI_NOT_SUPPORTED, resp.getError());
+        // Revert requiring signature for client
+        OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setRequestObjectSignatureAlg(null);
+        clientResource.update(clientRep);
     }
 
     // LOGIN_HINT
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCWellKnownProviderTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCWellKnownProviderTest.java
index ab5c230..7fea657 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCWellKnownProviderTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCWellKnownProviderTest.java
@@ -86,6 +86,8 @@ public class OIDCWellKnownProviderTest extends AbstractKeycloakTest {
 
             Assert.assertNames(oidcConfig.getSubjectTypesSupported(), "public");
             Assert.assertNames(oidcConfig.getIdTokenSigningAlgValuesSupported(), Algorithm.RS256.toString());
+            Assert.assertNames(oidcConfig.getUserInfoSigningAlgValuesSupported(), Algorithm.RS256.toString());
+            Assert.assertNames(oidcConfig.getRequestObjectSigningAlgValuesSupported(), Algorithm.none.toString(), Algorithm.RS256.toString());
 
             // Client authentication
             Assert.assertNames(oidcConfig.getTokenEndpointAuthMethodsSupported(), "client_secret_basic", "client_secret_post", "private_key_jwt");
@@ -100,8 +102,8 @@ public class OIDCWellKnownProviderTest extends AbstractKeycloakTest {
             Assert.assertNames(oidcConfig.getScopesSupported(), OAuth2Constants.SCOPE_OPENID, OAuth2Constants.OFFLINE_ACCESS);
 
             // Request and Request_Uri
-            Assert.assertFalse(oidcConfig.getRequestParameterSupported());
-            Assert.assertFalse(oidcConfig.getRequestUriParameterSupported());
+            Assert.assertTrue(oidcConfig.getRequestParameterSupported());
+            Assert.assertTrue(oidcConfig.getRequestUriParameterSupported());
         } finally {
             client.close();
         }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/UserInfoTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/UserInfoTest.java
index 268333f..e0588e7 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/UserInfoTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/UserInfoTest.java
@@ -21,19 +21,31 @@ import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.keycloak.OAuth2Constants;
+import org.keycloak.admin.client.resource.ClientResource;
 import org.keycloak.events.Details;
 import org.keycloak.events.Errors;
 import org.keycloak.events.EventType;
+import org.keycloak.jose.jws.Algorithm;
+import org.keycloak.jose.jws.JWSInput;
+import org.keycloak.jose.jws.crypto.RSAProvider;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
 import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
 import org.keycloak.representations.AccessTokenResponse;
 import org.keycloak.representations.UserInfo;
+import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.services.Urls;
 import org.keycloak.testsuite.AbstractKeycloakTest;
+import org.keycloak.testsuite.Assert;
 import org.keycloak.testsuite.AssertEvents;
+import org.keycloak.testsuite.admin.ApiUtil;
 import org.keycloak.testsuite.util.ClientManager;
 import org.keycloak.testsuite.util.RealmBuilder;
 import org.keycloak.testsuite.util.UserInfoClientUtil;
 import org.keycloak.util.BasicAuthHelper;
+import org.keycloak.util.JsonSerialization;
+import org.keycloak.utils.MediaType;
 
 import javax.ws.rs.client.Client;
 import javax.ws.rs.client.ClientBuilder;
@@ -45,6 +57,7 @@ import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriBuilder;
 import java.net.URI;
+import java.security.PublicKey;
 import java.util.List;
 
 import static org.junit.Assert.assertEquals;
@@ -153,6 +166,62 @@ public class UserInfoTest extends AbstractKeycloakTest {
     }
 
     @Test
+    public void testSuccessSignedResponse() throws Exception {
+        // Require signed userInfo request
+        ClientResource clientResource = ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app");
+        ClientRepresentation clientRep = clientResource.toRepresentation();
+        OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setUserInfoSignedResponseAlg(Algorithm.RS256);
+        clientResource.update(clientRep);
+
+        // test signed response
+        Client client = ClientBuilder.newClient();
+
+        try {
+            AccessTokenResponse accessTokenResponse = executeGrantAccessTokenRequest(client);
+
+            Response response = UserInfoClientUtil.executeUserInfoRequest_getMethod(client, accessTokenResponse.getToken());
+
+            events.expect(EventType.USER_INFO_REQUEST)
+                    .session(Matchers.notNullValue(String.class))
+                    .detail(Details.AUTH_METHOD, Details.VALIDATE_ACCESS_TOKEN)
+                    .detail(Details.USERNAME, "test-user@localhost")
+                    .detail(Details.SIGNATURE_REQUIRED, "true")
+                    .detail(Details.SIGNATURE_ALGORITHM, Algorithm.RS256.toString())
+                    .assertEvent();
+
+            // Check signature and content
+            RealmRepresentation realmRep = adminClient.realm("test").toRepresentation();
+            PublicKey publicKey = KeycloakModelUtils.getPublicKey(realmRep.getPublicKey());
+
+            Assert.assertEquals(200, response.getStatus());
+            Assert.assertEquals(response.getHeaderString(HttpHeaders.CONTENT_TYPE), MediaType.APPLICATION_JWT);
+            String signedResponse = response.readEntity(String.class);
+            response.close();
+
+            JWSInput jwsInput = new JWSInput(signedResponse);
+            Assert.assertTrue(RSAProvider.verify(jwsInput, publicKey));
+
+            UserInfo userInfo = JsonSerialization.readValue(jwsInput.getContent(), UserInfo.class);
+
+            Assert.assertNotNull(userInfo);
+            Assert.assertNotNull(userInfo.getSubject());
+            Assert.assertEquals("test-user@localhost", userInfo.getEmail());
+            Assert.assertEquals("test-user@localhost", userInfo.getPreferredUsername());
+
+            Assert.assertTrue(userInfo.hasAudience("test-app"));
+            String expectedIssuer = Urls.realmIssuer(new URI(AUTH_SERVER_ROOT), "test");
+            Assert.assertEquals(expectedIssuer, userInfo.getIssuer());
+
+        } finally {
+            client.close();
+        }
+
+        // Revert signed userInfo request
+        OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setUserInfoSignedResponseAlg(null);
+        clientResource.update(clientRep);
+    }
+
+    @Test
     public void testSessionExpired() throws Exception {
         Client client = ClientBuilder.newClient();
 
@@ -235,6 +304,7 @@ public class UserInfoTest extends AbstractKeycloakTest {
                 .session(Matchers.notNullValue(String.class))
                 .detail(Details.AUTH_METHOD, Details.VALIDATE_ACCESS_TOKEN)
                 .detail(Details.USERNAME, "test-user@localhost")
+                .detail(Details.SIGNATURE_REQUIRED, "false")
                 .assertEvent();
         UserInfoClientUtil.testSuccessfulUserInfoResponse(response, "test-user@localhost", "test-user@localhost");
     }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/SslMailServer.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/SslMailServer.java
index 0088e42..0f87d49 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/SslMailServer.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/SslMailServer.java
@@ -91,7 +91,7 @@ public class SslMailServer {
             ksKeys.load(keyStoreIS, keyStorePassphrase);
 
             // KeyManager decides which key material to use.
-            KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+            KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
             kmf.init(ksKeys, keyStorePassphrase);
 
             // Trust store for client authentication.
@@ -101,7 +101,7 @@ public class SslMailServer {
             ksTrust.load(trustStoreIS, trustStorePassphrase);
 
             // TrustManager decides which certificate authorities to use.
-            TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
+            TrustManagerFactory tmf = TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
             tmf.init(ksTrust);
 
             final SSLContext sslContext = SSLContext.getInstance("TLS");
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/bad-assertion-sales-post-sig/WEB-INF/keycloak-saml.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/bad-assertion-sales-post-sig/WEB-INF/keycloak-saml.xml
new file mode 100755
index 0000000..2ea7ff7
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/bad-assertion-sales-post-sig/WEB-INF/keycloak-saml.xml
@@ -0,0 +1,62 @@
+<!--
+  ~ 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.
+  -->
+
+<keycloak-saml-adapter>
+    <SP entityID="http://localhost:8081/bad-assertion-sales-post-sig/"
+        sslPolicy="EXTERNAL"
+        nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
+        logoutPage="/logout.jsp"
+        forceAuthentication="false">
+        <Keys>
+            <Key signing="true" >
+                <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+                    <PrivateKey alias="http://localhost:8081/bad-realm-sales-post-sig/" password="test123"/>
+                    <Certificate alias="http://localhost:8081/bad-realm-sales-post-sig/"/>
+                </KeyStore>
+            </Key>
+        </Keys>
+        <PrincipalNameMapping policy="FROM_NAME_ID"/>
+        <RoleIdentifiers>
+            <Attribute name="Role"/>
+        </RoleIdentifiers>
+        <IDP entityID="idp">
+            <SingleSignOnService signRequest="true"
+                                 validateAssertionSignature="true"
+                                 requestBinding="POST"
+                                 bindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    />
+
+            <SingleLogoutService
+                    validateRequestSignature="true"
+                    validateResponseSignature="true"
+                    signRequest="true"
+                    signResponse="true"
+                    requestBinding="POST"
+                    responseBinding="POST"
+                    postBindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    redirectBindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    />
+            <Keys>
+                <Key signing="true">
+                    <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+                        <Certificate alias="demo"/>
+                    </KeyStore>
+                </Key>
+            </Keys>
+        </IDP>
+     </SP>
+</keycloak-saml-adapter>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/bad-assertion-sales-post-sig/WEB-INF/keystore.jks b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/bad-assertion-sales-post-sig/WEB-INF/keystore.jks
new file mode 100755
index 0000000..215384c
Binary files /dev/null and b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/bad-assertion-sales-post-sig/WEB-INF/keystore.jks differ
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee/WEB-INF/keycloak-saml.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee/WEB-INF/keycloak-saml.xml
new file mode 100755
index 0000000..96f0b86
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee/WEB-INF/keycloak-saml.xml
@@ -0,0 +1,42 @@
+<!--
+  ~ 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.
+  -->
+
+<keycloak-saml-adapter>
+    <SP entityID="http://localhost:8081/employee/"
+        sslPolicy="EXTERNAL"
+        nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
+        logoutPage="/logout.jsp"
+        forceAuthentication="false">
+        <PrincipalNameMapping policy="FROM_NAME_ID"/>
+        <RoleIdentifiers>
+            <Attribute name="memberOf"/>
+            <Attribute name="Role"/>
+        </RoleIdentifiers>
+        <IDP entityID="idp">
+            <SingleSignOnService requestBinding="POST"
+                                 bindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    />
+
+            <SingleLogoutService
+                    requestBinding="POST"
+                    responseBinding="POST"
+                    postBindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    redirectBindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    />
+        </IDP>
+     </SP>
+</keycloak-saml-adapter>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee/WEB-INF/web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee/WEB-INF/web.xml
new file mode 100644
index 0000000..0199588
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee/WEB-INF/web.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+         version="3.0">
+
+    <module-name>%CONTEXT_PATH%</module-name>
+
+    <servlet>
+        <servlet-name>Servlet</servlet-name>
+        <servlet-class>org.keycloak.testsuite.adapter.servlet.SamlSPFacade</servlet-class>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>Servlet</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>Users</web-resource-name>
+            <url-pattern>/*</url-pattern>
+        </web-resource-collection>
+        <auth-constraint>
+            <role-name>manager</role-name>
+        </auth-constraint>
+    </security-constraint>
+
+    <login-config>
+        <auth-method>KEYCLOAK-SAML</auth-method>
+        <realm-name>demo</realm-name>
+    </login-config>
+
+    <security-role>
+        <role-name>manager</role-name>
+    </security-role>
+</web-app>
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/input-portal/WEB-INF/keycloak-saml.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/input-portal/WEB-INF/keycloak-saml.xml
new file mode 100755
index 0000000..e1cd76a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/input-portal/WEB-INF/keycloak-saml.xml
@@ -0,0 +1,41 @@
+<!--
+  ~ 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.
+  -->
+
+<keycloak-saml-adapter>
+    <SP entityID="http://localhost:8081/input-portal/"
+        sslPolicy="EXTERNAL"
+        nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
+        logoutPage="/logout.jsp"
+        forceAuthentication="false">
+        <PrincipalNameMapping policy="FROM_NAME_ID"/>
+        <RoleIdentifiers>
+            <Attribute name="Role"/>
+        </RoleIdentifiers>
+        <IDP entityID="idp">
+            <SingleSignOnService requestBinding="POST"
+                                 bindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    />
+
+            <SingleLogoutService
+                    requestBinding="POST"
+                    responseBinding="POST"
+                    postBindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    redirectBindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    />
+        </IDP>
+     </SP>
+</keycloak-saml-adapter>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/input-portal/WEB-INF/web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/input-portal/WEB-INF/web.xml
new file mode 100644
index 0000000..865dc8a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/input-portal/WEB-INF/web.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+         version="3.0">
+
+    <module-name>%CONTEXT_PATH%</module-name>
+
+    <servlet>
+        <servlet-name>Servlet</servlet-name>
+        <servlet-class>org.keycloak.testsuite.adapter.servlet.InputServlet</servlet-class>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>Servlet</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>Users</web-resource-name>
+            <url-pattern>/secured/*</url-pattern>
+        </web-resource-collection>
+        <auth-constraint>
+            <role-name>manager</role-name>
+        </auth-constraint>
+    </security-constraint>
+
+    <login-config>
+        <auth-method>KEYCLOAK-SAML</auth-method>
+        <realm-name>demo</realm-name>
+    </login-config>
+
+    <security-role>
+        <role-name>manager</role-name>
+    </security-role>
+</web-app>
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/missing-assertion-sig/WEB-INF/keycloak-saml.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/missing-assertion-sig/WEB-INF/keycloak-saml.xml
new file mode 100755
index 0000000..936fb21
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/missing-assertion-sig/WEB-INF/keycloak-saml.xml
@@ -0,0 +1,60 @@
+<!--
+  ~ 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.
+  -->
+
+<keycloak-saml-adapter>
+    <SP entityID="http://localhost:8081/missing-assertion-sig/"
+        sslPolicy="EXTERNAL"
+        nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
+        logoutPage="/logout.jsp"
+        forceAuthentication="false">
+        <Keys>
+            <Key signing="true" >
+                <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+                    <PrivateKey alias="http://localhost:8080/sales-post-sig/" password="test123"/>
+                    <Certificate alias="http://localhost:8080/sales-post-sig/"/>
+                </KeyStore>
+            </Key>
+        </Keys>
+        <PrincipalNameMapping policy="FROM_NAME_ID"/>
+        <RoleIdentifiers>
+            <Attribute name="Role"/>
+        </RoleIdentifiers>
+        <IDP entityID="idp"
+             signaturesRequired="true">
+        <SingleSignOnService requestBinding="POST"
+                             bindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                             validateAssertionSignature="true"
+                             validateResponseSignature="false"
+                    />
+
+            <SingleLogoutService
+                    requestBinding="POST"
+                    responseBinding="POST"
+                    postBindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    redirectBindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    />
+            <Keys>
+                <Key signing="true">
+                    <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+                        <Certificate alias="demo"/>
+                    </KeyStore>
+                </Key>
+            </Keys>
+        </IDP>
+     </SP>
+</keycloak-saml-adapter>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/missing-assertion-sig/WEB-INF/keystore.jks b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/missing-assertion-sig/WEB-INF/keystore.jks
new file mode 100755
index 0000000..144830b
Binary files /dev/null and b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/missing-assertion-sig/WEB-INF/keystore.jks differ
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/sales-post2/WEB-INF/keycloak-saml.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/sales-post2/WEB-INF/keycloak-saml.xml
new file mode 100755
index 0000000..eae5b14
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/sales-post2/WEB-INF/keycloak-saml.xml
@@ -0,0 +1,41 @@
+<!--
+  ~ 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.
+  -->
+
+<keycloak-saml-adapter>
+    <SP entityID="http://localhost:8081/sales-post2/"
+        sslPolicy="EXTERNAL"
+        nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
+        logoutPage="/logout.jsp"
+        forceAuthentication="false">
+        <PrincipalNameMapping policy="FROM_NAME_ID"/>
+        <RoleIdentifiers>
+            <Attribute name="Role"/>
+        </RoleIdentifiers>
+        <IDP entityID="idp">
+            <SingleSignOnService requestBinding="POST"
+                                 bindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    />
+
+            <SingleLogoutService
+                    requestBinding="POST"
+                    responseBinding="POST"
+                    postBindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    redirectBindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    />
+        </IDP>
+     </SP>
+</keycloak-saml-adapter>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/sales-post-assertion-and-response-sig/WEB-INF/keycloak-saml.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/sales-post-assertion-and-response-sig/WEB-INF/keycloak-saml.xml
new file mode 100755
index 0000000..94c3f4d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/sales-post-assertion-and-response-sig/WEB-INF/keycloak-saml.xml
@@ -0,0 +1,60 @@
+<!--
+  ~ 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.
+  -->
+
+<keycloak-saml-adapter>
+    <SP entityID="http://localhost:8081/sales-post-assertion-and-response-sig/"
+        sslPolicy="EXTERNAL"
+        nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
+        logoutPage="/logout.jsp"
+        forceAuthentication="false">
+        <Keys>
+            <Key signing="true" >
+                <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+                    <PrivateKey alias="http://localhost:8080/sales-post-sig/" password="test123"/>
+                    <Certificate alias="http://localhost:8080/sales-post-sig/"/>
+                </KeyStore>
+            </Key>
+        </Keys>
+        <PrincipalNameMapping policy="FROM_NAME_ID"/>
+        <RoleIdentifiers>
+            <Attribute name="Role"/>
+        </RoleIdentifiers>
+        <IDP entityID="idp"
+             signaturesRequired="true">
+        <SingleSignOnService requestBinding="POST"
+                             bindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                             validateAssertionSignature="true"
+                             validateResponseSignature="true"
+                    />
+
+            <SingleLogoutService
+                    requestBinding="POST"
+                    responseBinding="POST"
+                    postBindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    redirectBindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    />
+            <Keys>
+                <Key signing="true">
+                    <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+                        <Certificate alias="demo"/>
+                    </KeyStore>
+                </Key>
+            </Keys>
+        </IDP>
+     </SP>
+</keycloak-saml-adapter>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/sales-post-assertion-and-response-sig/WEB-INF/keystore.jks b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/sales-post-assertion-and-response-sig/WEB-INF/keystore.jks
new file mode 100755
index 0000000..144830b
Binary files /dev/null and b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/sales-post-assertion-and-response-sig/WEB-INF/keystore.jks differ
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/testsaml.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/testsaml.json
index 5a1d2c6..0e25d89 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/testsaml.json
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/testsaml.json
@@ -68,6 +68,101 @@
     ],
     "clients": [
         {
+            "clientId": "http://localhost:8081/missing-assertion-sig/",
+            "enabled": true,
+            "protocol": "saml",
+            "fullScopeAllowed": true,
+            "baseUrl": "http://localhost:8080/missing-assertion-sig",
+            "redirectUris": [
+                "http://localhost:8080/missing-assertion-sig/*"
+            ],
+            "attributes": {
+                "saml_assertion_consumer_url_post": "http://localhost:8080/missing-assertion-sig/saml",
+                "saml_assertion_consumer_url_redirect": "http://localhost:8080/missing-assertion-sig/saml",
+                "saml_single_logout_service_url_post": "http://localhost:8080/missing-assertion-sig/saml",
+                "saml_single_logout_service_url_redirect": "http://localhost:8080/missing-assertion-sig/saml",
+                "saml.server.signature": "true",
+                "saml.assertion.signature": "false",
+                "saml.signature.algorithm": "RSA_SHA256",
+                "saml.client.signature": "true",
+                "saml.authnstatement": "true",
+                "saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
+            }
+        },
+        {
+            "clientId": "http://localhost:8081/bad-assertion-sales-post-sig/",
+            "enabled": true,
+            "protocol": "saml",
+            "fullScopeAllowed": true,
+            "baseUrl": "http://localhost:8080/bad-assertion-sales-post-sig/",
+            "adminUrl": "http://localhost:8080/bad-assertion-sales-post-sig/saml",
+            "redirectUris": [
+                "http://localhost:8080/bad-assertion-sales-post-sig/*"
+            ],
+            "attributes": {
+                "saml.assertion.signature": "true",
+                "saml.client.signature": "true",
+                "saml.authnstatement": "true",
+                "saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
+            }
+        },
+        {
+            "clientId": "http://localhost:8081/input-portal/",
+            "enabled": true,
+            "fullScopeAllowed": true,
+            "protocol": "saml",
+            "baseUrl": "http://localhost:8080/input-portal",
+            "redirectUris": [
+                "http://localhost:8080/input-portal/*"
+            ],
+            "attributes": {
+                "saml.authnstatement": "true",
+                "saml_assertion_consumer_url_post": "http://localhost:8080/input-portal/saml",
+                "saml_assertion_consumer_url_redirect": "http://localhost:8080/input-portal/saml",
+                "saml_single_logout_service_url_post": "http://localhost:8080/input-portal/saml",
+                "saml_single_logout_service_url_redirect": "http://localhost:8080/input-portal/saml"
+            }
+        },
+        {
+            "clientId": "http://localhost:8081/sales-post-assertion-and-response-sig/",
+            "enabled": true,
+            "protocol": "saml",
+            "fullScopeAllowed": true,
+            "baseUrl": "http://localhost:8080/sales-post-assertion-and-response-sig",
+            "redirectUris": [
+                "http://localhost:8080/sales-post-assertion-and-response-sig/*"
+            ],
+            "attributes": {
+                "saml_assertion_consumer_url_post": "http://localhost:8080/sales-post-assertion-and-response-sig/saml",
+                "saml_assertion_consumer_url_redirect": "http://localhost:8080/sales-post-assertion-and-response-sig/saml",
+                "saml_single_logout_service_url_post": "http://localhost:8080/sales-post-assertion-and-response-sig/saml",
+                "saml_single_logout_service_url_redirect": "http://localhost:8080/sales-post-assertion-and-response-sig/saml",
+                "saml.server.signature": "true",
+                "saml.assertion.signature": "true",
+                "saml.signature.algorithm": "RSA_SHA256",
+                "saml.client.signature": "true",
+                "saml.authnstatement": "true",
+                "saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
+            }
+        },
+        {
+            "clientId": "http://localhost:8081/sales-post2/",
+            "enabled": true,
+            "fullScopeAllowed": true,
+            "protocol": "saml",
+            "baseUrl": "http://localhost:8080/sales-post2",
+            "redirectUris": [
+                "http://localhost:8080/sales-post2/*"
+            ],
+            "attributes": {
+                "saml.authnstatement": "true",
+                "saml_assertion_consumer_url_post": "http://localhost:8080/sales-post2/saml",
+                "saml_single_logout_service_url_post": "http://localhost:8080/sales-post2/saml",
+                "saml_idp_initiated_sso_url_name": "sales-post2",
+                "saml_idp_initiated_sso_relay_state": "redirectTo=/foo"
+            }
+        },
+        {
             "clientId": "http://localhost:8081/sales-post/",
             "enabled": true,
             "fullScopeAllowed": true,
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/web.xml
index 4207f91..aa2c4b7 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/web.xml
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/web.xml
@@ -28,6 +28,10 @@
         <url-pattern>/*</url-pattern>
     </servlet-mapping>
 
+    <error-page>
+        <location>/error.html</location>
+    </error-page>
+
     <security-constraint>
         <web-resource-collection>
             <web-resource-name>Application</web-resource-name>
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/enforcer-config-paths-same-name.json b/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/enforcer-config-paths-same-name.json
new file mode 100644
index 0000000..19d8c55
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/enforcer-config-paths-same-name.json
@@ -0,0 +1,59 @@
+{
+  "realm": "test-realm-authz",
+  "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+  "auth-server-url": "http://localhost:8180/auth",
+  "ssl-required": "external",
+  "resource": "test-app-authz",
+  "bearer-only": true,
+  "credentials": {
+    "secret": "secret"
+  },
+  "policy-enforcer": {
+    "paths": [
+      {
+        "path": "/v1/product/*",
+        "methods": [
+          {
+            "method": "POST",
+            "scopes": [
+              "create"
+            ]
+          }
+        ]
+      },
+      {
+        "path": "/v1/product/*",
+        "methods": [
+          {
+            "method": "GET",
+            "scopes": [
+              "view"
+            ]
+          }
+        ]
+      },
+      {
+        "path": "/v1/product/*",
+        "methods": [
+          {
+            "method": "PUT",
+            "scopes": [
+              "update"
+            ]
+          }
+        ]
+      },
+      {
+        "path": "/v1/product/*",
+        "methods": [
+          {
+            "method": "DELETE",
+            "scopes": [
+              "delete"
+            ]
+          }
+        ]
+      }
+    ]
+  }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/test-authz-realm.json b/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/test-authz-realm.json
new file mode 100644
index 0000000..b1a6f5a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/test-authz-realm.json
@@ -0,0 +1,50 @@
+{
+  "id": "test-realm-authz",
+  "realm": "test-realm-authz",
+  "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": "service-account-test-app-authz",
+      "enabled": true,
+      "serviceAccountClientId": "test-app-authz",
+      "clientRoles": {
+        "test-app-authz" : ["uma_protection"]
+      }
+    }
+  ],
+  "clients": [
+    {
+      "clientId": "test-app-authz",
+      "enabled": true,
+      "baseUrl": "/test-app-authz",
+      "adminUrl": "/test-app-authz",
+      "bearerOnly": false,
+      "authorizationSettings": {
+        "allowRemoteResourceManagement": true,
+        "policyEnforcementMode": "ENFORCING",
+        "resources": [
+          {
+            "name": "Product Resource",
+            "uri": "/v1/product/*",
+            "scopes": [
+              {
+                "name": "view",
+                "name": "create",
+                "name": "delete",
+                "name": "update"
+              }
+            ]
+          }
+        ]
+      },
+      "redirectUris": [
+        "/test-app-authz/*"
+      ],
+      "secret": "secret"
+    }
+  ]
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/client-auth-test/certificate.pem b/testsuite/integration-arquillian/tests/base/src/test/resources/client-auth-test/certificate.pem
index 4c589e8..a526c93 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/client-auth-test/certificate.pem
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/client-auth-test/certificate.pem
@@ -1,13 +1,21 @@
-MIICWwIBAAKBgQCqXOsp76WfwXSNCU5AOix6SDhNR7MOdEtawJRmdUcBuJd/QeFh
-usLVOwm3a7s1134nlVKBEhOYBzKOY6LulvGDT4wHZUxE6cGJW+dEUd/6jrfZz9Ka
-XyATzLUn2BKBQZqRleQPKupwaOGQqQGQ4OmJrWKqG77Rb5O7GLNNDRCGpwIDAQAB
-AoGAA4sJLQcZ57erR9+gq/9Ju4VS/nB2Td+nTYzhyTu5LVT8eZ4SJOkwciTXL+Ri
-sVz497PIUnzgwXGs4H87/UBQZ4UJNz+kTC3L6nr4b5bWoIxoiY76R+gzlYiNEV9s
-QsX+K1FNKKMnEVa7t3DvbCFdlmt+4Agh/KQCU92Q13mj+WECQQDRF+BY1emtFfVC
-2miuSWorMLoaKv5At3BMgcs4KPzlfpMiEOMM/m1jmGrqUm40RZlnkhKEqQB0VeqF
-27gMGZuxAkEA0JTHqZDfcf5bBYhWzE3+Ec9A22/ZQgCiuV+DmWhCYxHLFsOYPkhT
-0jAibfJc8E8VchXMjeDktBAyotylQO1V1wJAcZisLPdCdeOpFMH9/zopcP+PC+px
-qY0/eIFHe+JgyvnCS86q3ANaQLKs21MfRkzNtAQbBXGfqpSGzmR5kDFVcQJAX1YR
-qMSKDS2IZw/5NGrVnUhQybvm3s8xkW9B1GWeKnAglqgSNy2R+nl70ys7Ja/QCpRC
-KftILbFVo7Eca3/PgwJAM5xMmg3Yx/WKkiCREvKXIGKOKN5rGWhHoLnYJHlsOi1r
-Z7Bef90rMp2kDrfLUAgrIJH06kxB+3oPRcmgjWlmDQ==
+-----BEGIN CERTIFICATE-----
+MIIDXTCCAkWgAwIBAgIJAIzE3vQp7EQWMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
+aWRnaXRzIFB0eSBMdGQwHhcNMTYwMjI5MDgzMDU0WhcNNDMwNzE2MDgzMDU0WjBF
+MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
+ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAp1+GzdEkt2FZbISXYO12503FL6Oh8s4+tJ2fE66N8IezhugP8xiySDfW
+TEMaO5Z2TaTnQQoF9SSZ9Edq1GPxpBX0cdkCOBopEGdlb3hUYDeMaDMs18KGemUc
+Fj+CWB5VVcbmWMJ36WCz7FC+Oe38tmujR1AJpJL3pwqazyWIZzPqX8rW+rrNPGKP
+C96oBPZMb4RJWivLBJi/o5MGSpo1sJNtxyF4zUUI00LX0wZAV1HH1XErd1Vz41on
+nmB+tj9nevVRR4rDV280IELp9Ud0PIb3w843uJtwfSAwVG0pT6hv1VBDrBxTS08N
+dPU8CtkQAXzCCr8nqfAbUFOhcWRQgQIDAQABo1AwTjAdBgNVHQ4EFgQUFE+uUZAI
+n57ArEylqhCmHkAenTEwHwYDVR0jBBgwFoAUFE+uUZAIn57ArEylqhCmHkAenTEw
+DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEApkgD3OCtw3+sk7GR1YaJ
+xNd8HT+fxXmnLqGnCQWX8lRIg5vj1PDMRev6vlIK3JfQV3zajcpKFfpy96klWsJy
+ZLYBVW2QOtMzDdQ9I8dS4Pn/SJ/Vo/M/ucfY4ttcuUL3oQCrI/c/u9tcamGMfbwd
+658MlXrUvt4B6qXY5AbgUvYR25P86uw7hSFMq5tQftNQsLbOh2FEeIiKhpgI7w8S
+SPajaWjUXsfHc5H7f9MciE2NS1Vd3AViGrVWP1rgQ1Iv0UyQVQrnjmIs12ENJmTd
+5lDqra5FJhaO7+RUG6er8n8HwXzhHkPmezGqtxWKikjitqvDY9prB3omJSa4Led+
+AQ==
+-----END CERTIFICATE-----
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/client-auth-test/publickey.pem b/testsuite/integration-arquillian/tests/base/src/test/resources/client-auth-test/publickey.pem
index 906ff55..3169a04 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/client-auth-test/publickey.pem
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/client-auth-test/publickey.pem
@@ -1 +1,9 @@
-MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApnZ/E2BUULHjsRiSnEgZ
+4vGe15BRqZPdkHR+NcvVYpThc7JqY6nZrdrwO9sOjlMC5e2Q18Fypi4KbJpGSe9r
+0DPgcbPsHSoe2xFO3M8XBE0DyoRblaQFhe6p/sj3ak32k2zn+fMZUmlx/MTNQh1I
+Cki7So0NDCBXt8XGZNnEyvKeXOUZP5qicP9KxVAQiWJvlkaTjc8rrRTmf+HWw/Qf
+gQC0tzBRpa7T+RpW9O+rnWfOaNfTkTb9itIc+ZOa2Z4iidZ7+ifMOp9cNT641Wb6
+iYqJ2ufqY+msxI54tYM1tPgGS7r4SnCwmnqTaO383wXUl8TQ7qStmAWIepV3nNyu
+AQIDAQAB
+-----END PUBLIC KEY-----
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json
index 99e8614..d8c4dc1 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json
@@ -90,7 +90,8 @@
             "driverDialect": "${keycloak.connectionsJpa.driverDialect:}",
             "user": "${keycloak.connectionsJpa.user:sa}",
             "password": "${keycloak.connectionsJpa.password:}",
-            "databaseSchema": "${keycloak.connectionsJpa.databaseSchema:update}",
+            "initializeEmpty": true,
+            "migrationStrategy": "update",
             "showSql": "${keycloak.connectionsJpa.showSql:false}",
             "formatSql": "${keycloak.connectionsJpa.formatSql:true}",
             "globalStatsInterval": "${keycloak.connectionsJpa.globalStatsInterval:-1}"
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/EAP6SAMLFilterAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/EAP6SAMLFilterAdapterTest.java
index 627e5f3..1dcf846 100644
--- a/testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/EAP6SAMLFilterAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/EAP6SAMLFilterAdapterTest.java
@@ -8,5 +8,5 @@ import org.keycloak.testsuite.arquillian.annotation.UseServletFilter;
  * @author mhajas
  */
 @AppServerContainer("app-server-eap6")
-public class EAPSAMLFilterAdapterTest extends AbstractSAMLFilterServletAdapterTest {
+public class EAP6SAMLFilterAdapterTest extends AbstractSAMLFilterServletAdapterTest {
 }
diff --git a/testsuite/integration-arquillian/tests/other/adapters/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/pom.xml
index 9c49390..15fc1c4 100644
--- a/testsuite/integration-arquillian/tests/other/adapters/pom.xml
+++ b/testsuite/integration-arquillian/tests/other/adapters/pom.xml
@@ -172,6 +172,18 @@
                             </systemPropertyVariables>
                         </configuration>
                     </plugin>
+                    <plugin>
+                        <groupId>org.jboss.shrinkwrap.resolver</groupId>
+                        <artifactId>shrinkwrap-resolver-maven-plugin</artifactId>
+                        <version>${version.shrinkwrap.resolvers}</version>
+                        <executions>
+                            <execution>
+                                <goals>
+                                    <goal>propagate-execution-context</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
                 </plugins>
             </build>
         </profile>
diff --git a/testsuite/integration-arquillian/tests/other/console/pom.xml b/testsuite/integration-arquillian/tests/other/console/pom.xml
index d9232e8..2459c6d 100644
--- a/testsuite/integration-arquillian/tests/other/console/pom.xml
+++ b/testsuite/integration-arquillian/tests/other/console/pom.xml
@@ -36,6 +36,26 @@
     </properties>
 
     <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>properties-maven-plugin</artifactId>
+                <version>1.0.0</version>
+                <executions>
+                    <execution>
+                        <phase>process-resources</phase>
+                        <goals>
+                            <goal>read-project-properties</goal>
+                        </goals>
+                        <configuration>
+                            <files>
+                                <file>${testsuite.constants}</file>
+                            </files>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
         <pluginManagement>
             <plugins>
                 <plugin>
@@ -48,10 +68,11 @@
                                 <goal>copy-resources</goal>
                             </goals>
                             <configuration>
-                                <outputDirectory>${auth.server.home}/themes</outputDirectory>
+                                <outputDirectory>${keycloak.theme.dir}</outputDirectory>
                                 <resources>
                                     <resource>
                                         <directory>src/main/resources/themes</directory>
+                                        <filtering>true</filtering>
                                     </resource>
                                 </resources>
                             </configuration>
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/federation/KerberosUserProviderForm.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/federation/KerberosUserProviderForm.java
index 41e62c9..d19bae7 100644
--- a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/federation/KerberosUserProviderForm.java
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/federation/KerberosUserProviderForm.java
@@ -45,6 +45,10 @@ public class KerberosUserProviderForm extends Form {
         setInputValue(consoleDisplayNameInput, name);
     }
 
+    public String getConsoleDisplayNameInput() {
+        return getInputValue(consoleDisplayNameInput);
+    }
+
     public void setPriorityInput(Integer priority) {
         setInputValue(priorityInput, String.valueOf(priority));
     }
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/federation/UserFederation.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/federation/UserFederation.java
index c0dd078..06405ee 100644
--- a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/federation/UserFederation.java
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/federation/UserFederation.java
@@ -2,13 +2,18 @@ package org.keycloak.testsuite.console.page.federation;
 
 import org.jboss.arquillian.graphene.findby.FindByJQuery;
 import org.keycloak.testsuite.console.page.AdminConsoleRealm;
+import org.keycloak.testsuite.console.page.fragment.DataTable;
+import org.keycloak.testsuite.console.page.fragment.ModalDialog;
+import org.keycloak.testsuite.util.UIUtils;
 import org.openqa.selenium.By;
+import org.openqa.selenium.support.FindBy;
 import org.openqa.selenium.support.ui.Select;
 
 import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
 
 /**
- * Created by fkiss.
+ * @author fkiss
+ * @author Vaclav Muzikar <vmuzikar@redhat.com>
  */
 public class UserFederation extends AdminConsoleRealm {
 
@@ -20,9 +25,37 @@ public class UserFederation extends AdminConsoleRealm {
     @FindByJQuery("select[ng-model*='selectedProvider']")
     private Select addProviderSelect;
 
-    public void addProvider(String provider) {
-        waitUntilElement(By.cssSelector("select[ng-model*='selectedProvider']")).is().present();
+    @FindBy(xpath = "//div[./h1/span[text()='User Federation']]/table")
+    private FederationsTable federationsTable;
+
+    public FederationsTable table() {
+        return federationsTable;
+    }
+
+    public void addFederation(String provider) {
         addProviderSelect.selectByVisibleText(provider);
     }
 
+    public boolean hasProvider(String provider) {
+        return UIUtils.selectContainsOption(addProviderSelect, provider);
+    }
+
+    public class FederationsTable extends DataTable {
+        @FindBy(xpath = "//div[@class='modal-dialog']")
+        private ModalDialog modalDialog;
+
+        public void editFederation(String federation) {
+            clickRowActionButton(getRowByLinkText(federation), "Edit");
+        }
+
+        public void removeFederation(String federation) {
+            clickRowActionButton(getRowByLinkText(federation), "Delete");
+            modalDialog.confirmDeletion();
+        }
+
+        public int getFederationsCount() {
+            return rows().size();
+        }
+    }
+
 }
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/resources/themes/internat-test/account/messages/messages_cs.properties b/testsuite/integration-arquillian/tests/other/console/src/main/resources/themes/internat-test/account/messages/messages_cs.properties
new file mode 100644
index 0000000..f2fd7e0
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/resources/themes/internat-test/account/messages/messages_cs.properties
@@ -0,0 +1,5 @@
+#encoding: utf-8
+doSave=Uložit
+doCancel=Zrušit
+editAccountHtmlTitle=Upravit účet
+locale_cs=Čeština
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/resources/themes/internat-test/account/messages/messages_en.properties b/testsuite/integration-arquillian/tests/other/console/src/main/resources/themes/internat-test/account/messages/messages_en.properties
new file mode 100644
index 0000000..cc26e68
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/resources/themes/internat-test/account/messages/messages_en.properties
@@ -0,0 +1,2 @@
+#encoding: utf-8
+locale_cs=Čeština
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/resources/themes/internat-test/account/theme.properties b/testsuite/integration-arquillian/tests/other/console/src/main/resources/themes/internat-test/account/theme.properties
new file mode 100644
index 0000000..7bdb8fe
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/resources/themes/internat-test/account/theme.properties
@@ -0,0 +1,2 @@
+parent=${theme-default-name}
+locales=en,cs
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/resources/themes/internat-test/admin/messages/admin-messages_cs.properties b/testsuite/integration-arquillian/tests/other/console/src/main/resources/themes/internat-test/admin/messages/admin-messages_cs.properties
new file mode 100644
index 0000000..9a9ef0e
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/resources/themes/internat-test/admin/messages/admin-messages_cs.properties
@@ -0,0 +1,7 @@
+#encoding: utf-8
+enabled=Povolit
+save=Uložit
+cancel=Zrušit
+onText=ZAP
+offText=VYP
+realm-settings=Nastavení Realmu
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/resources/themes/internat-test/admin/messages/messages_cs.properties b/testsuite/integration-arquillian/tests/other/console/src/main/resources/themes/internat-test/admin/messages/messages_cs.properties
new file mode 100644
index 0000000..e032076
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/resources/themes/internat-test/admin/messages/messages_cs.properties
@@ -0,0 +1,2 @@
+#encoding: utf-8
+invalidPasswordMinLengthMessage=Neplatné heslo: musí mít alespoň {0} znaků.
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/resources/themes/internat-test/admin/theme.properties b/testsuite/integration-arquillian/tests/other/console/src/main/resources/themes/internat-test/admin/theme.properties
new file mode 100644
index 0000000..7bdb8fe
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/resources/themes/internat-test/admin/theme.properties
@@ -0,0 +1,2 @@
+parent=${theme-default-name}
+locales=en,cs
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/resources/themes/internat-test/email/theme.properties b/testsuite/integration-arquillian/tests/other/console/src/main/resources/themes/internat-test/email/theme.properties
new file mode 100644
index 0000000..7bdb8fe
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/resources/themes/internat-test/email/theme.properties
@@ -0,0 +1,2 @@
+parent=${theme-default-name}
+locales=en,cs
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/resources/themes/internat-test/login/messages/messages_cs.properties b/testsuite/integration-arquillian/tests/other/console/src/main/resources/themes/internat-test/login/messages/messages_cs.properties
new file mode 100644
index 0000000..3c43a7b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/resources/themes/internat-test/login/messages/messages_cs.properties
@@ -0,0 +1,11 @@
+#encoding: utf-8
+doLogIn=Přihlásit
+doRegister=Registrovat se
+doCancel=Zrušit
+doSubmit=Odeslat
+doForgotPassword=Zapomněli jste heslo?
+locale_cs=Čeština
+username=Uživatelské jméno
+usernameOrEmail=Uživatelské jméno nebo email
+email=Email
+password=Heslo
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/resources/themes/internat-test/login/messages/messages_en.properties b/testsuite/integration-arquillian/tests/other/console/src/main/resources/themes/internat-test/login/messages/messages_en.properties
new file mode 100644
index 0000000..cc26e68
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/resources/themes/internat-test/login/messages/messages_en.properties
@@ -0,0 +1,2 @@
+#encoding: utf-8
+locale_cs=Čeština
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/resources/themes/internat-test/login/theme.properties b/testsuite/integration-arquillian/tests/other/console/src/main/resources/themes/internat-test/login/theme.properties
new file mode 100644
index 0000000..7bdb8fe
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/resources/themes/internat-test/login/theme.properties
@@ -0,0 +1,2 @@
+parent=${theme-default-name}
+locales=en,cs
diff --git a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/federation/UserFederationTest.java b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/federation/UserFederationTest.java
new file mode 100644
index 0000000..d136a0f
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/federation/UserFederationTest.java
@@ -0,0 +1,58 @@
+package org.keycloak.testsuite.console.federation;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.testsuite.console.AbstractConsoleTest;
+import org.keycloak.testsuite.console.page.federation.CreateKerberosUserProvider;
+import org.keycloak.testsuite.console.page.federation.UserFederation;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Vaclav Muzikar <vmuzikar@redhat.com>
+ */
+public class UserFederationTest extends AbstractConsoleTest {
+    @Page
+    private UserFederation userFederationPage;
+
+    @Page
+    private CreateKerberosUserProvider createKerberosUserProviderPage;
+
+    @Before
+    public void beforeUserFederationTest() {
+        userFederationPage.navigateTo();
+    }
+
+    @Test
+    public void availableProviders() {
+        assertTrue(userFederationPage.hasProvider("ldap"));
+        assertTrue(userFederationPage.hasProvider("kerberos"));
+    }
+
+    @Test
+    public void addRemoveFederation() {
+        final String federationName = "KerberosProvider";
+
+        userFederationPage.addFederation("kerberos");
+        assertTrue(createKerberosUserProviderPage.isCurrent());
+
+        createKerberosUserProviderPage.form().setConsoleDisplayNameInput(federationName);
+        createKerberosUserProviderPage.form().setKerberosRealmInput("KEYCLOAK.ORG");
+        createKerberosUserProviderPage.form().setServerPrincipalInput("HTTP/localhost@KEYCLOAK.ORG");
+        createKerberosUserProviderPage.form().setKeyTabInput("http.keytab");
+        createKerberosUserProviderPage.form().save();
+        assertAlertSuccess();
+        userFederationPage.navigateTo();
+
+        assertEquals(1, userFederationPage.table().getFederationsCount());
+
+        userFederationPage.table().editFederation(federationName);
+        assertEquals(federationName, createKerberosUserProviderPage.form().getConsoleDisplayNameInput());
+        userFederationPage.navigateTo();
+
+        userFederationPage.table().removeFederation(federationName);
+        assertEquals(0, userFederationPage.table().getFederationsCount());
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/realm/InternationalizationTest.java b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/realm/InternationalizationTest.java
index 090288b..97cb308 100644
--- a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/realm/InternationalizationTest.java
+++ b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/realm/InternationalizationTest.java
@@ -4,6 +4,7 @@ import org.jboss.arquillian.graphene.page.Page;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.testsuite.auth.page.AuthRealm;
 import org.keycloak.testsuite.console.page.fragment.Dropdown;
 import org.keycloak.testsuite.console.page.realm.ThemeSettings;
@@ -17,6 +18,13 @@ import static org.keycloak.testsuite.util.URLAssert.*;
  * @author Vaclav Muzikar <vmuzikar@redhat.com>
  */
 public class InternationalizationTest extends AbstractRealmTest {
+    private static final String THEME_NAME = "internat-test";
+    private static final String LOCALE_CS_NAME = "Čeština";
+
+    private static final String LABEL_CS_PASSWORD = "Heslo";
+    private static final String LABEL_CS_REALM_SETTINGS = "Nastavení Realmu";
+    private static final String LABEL_CS_EDIT_ACCOUNT = "Upravit účet";
+
     @Page
     private ThemeSettings themeSettingsPage;
 
@@ -25,6 +33,13 @@ public class InternationalizationTest extends AbstractRealmTest {
 
     @Before
     public void beforeInternationalizationTest() {
+        RealmRepresentation realmRepresentation = testRealmResource().toRepresentation();
+        realmRepresentation.setAccountTheme(THEME_NAME);
+        realmRepresentation.setAdminTheme(THEME_NAME);
+        realmRepresentation.setEmailTheme(THEME_NAME);
+        realmRepresentation.setLoginTheme(THEME_NAME);
+        testRealmResource().update(realmRepresentation);
+
         realmSettingsPage.navigateTo();
         tabs().themes();
         themeSettingsPage.setInternatEnabled(true);
@@ -47,14 +62,14 @@ public class InternationalizationTest extends AbstractRealmTest {
     public void loginInternationalization() {
         testRealmAdminConsolePage.navigateTo();
 
-        localeDropdown.selectByText("Español");
-        assertLocale(".//label[@for='password']", "Contraseña"); // Password
+        localeDropdown.selectByText(LOCALE_CS_NAME);
+        assertLocale(".//label[@for='password']", LABEL_CS_PASSWORD);
 
         loginToTestRealmConsoleAs(testUser);
-        assertConsoleLocale("Temas");
+        assertConsoleLocale(LABEL_CS_REALM_SETTINGS);
 
         testRealmAccountPage.navigateTo();
-        assertAccountLocale("Cuenta");
+        assertAccountLocale(LABEL_CS_EDIT_ACCOUNT);
     }
 
     /**
@@ -65,24 +80,24 @@ public class InternationalizationTest extends AbstractRealmTest {
         testRealmAccountPage.navigateTo();
         loginPage.form().login(testUser);
 
-        localeDropdown.selectByText("Français");
+        localeDropdown.selectByText(LOCALE_CS_NAME);
         testRealmAccountPage.navigateTo();
-        assertAccountLocale("Compte");
+        assertAccountLocale(LABEL_CS_EDIT_ACCOUNT);
 
         deleteAllCookiesForTestRealm();
 
         loginToTestRealmConsoleAs(testUser);
-        assertConsoleLocale("Thèmes");
+        assertConsoleLocale(LABEL_CS_REALM_SETTINGS);
     }
 
     private void assertConsoleLocale(String expected) {
         assertCurrentUrlEquals(realmSettingsPage);
-        assertLocale(".//a[contains(@href,'/theme-settings')]", expected); // Themes
+        assertLocale(".//div[@class='nav-category'][1]/ul/li[1]//a", expected); // Realm Settings
     }
 
     private void assertAccountLocale(String expected) {
         assertCurrentUrlEquals(testRealmAccountPage);
-        assertLocale(".//div[contains(@class,'bs-sidebar')]/ul/li", expected); // Account
+        assertLocale(".//div[contains(@class,'content-area')]/div[@class='row']/div/h2", expected); // Edit Account
     }
 
     private void assertLocale(String xpathSelector, String expected) {
diff --git a/testsuite/integration-arquillian/tests/other/pom.xml b/testsuite/integration-arquillian/tests/other/pom.xml
index bb238bf..6b0f83c 100644
--- a/testsuite/integration-arquillian/tests/other/pom.xml
+++ b/testsuite/integration-arquillian/tests/other/pom.xml
@@ -59,7 +59,7 @@
                                         <artifactId>integration-arquillian-tests-base</artifactId>
                                         <version>${project.version}</version>
                                         <type>test-jar</type>
-                                        <includes>arquillian.xml,keycloak-add-user.json,kerberos/*</includes>
+                                        <includes>arquillian.xml,keycloak-add-user.json,test-constants.properties,kerberos/*</includes>
                                     </artifactItem>
                                 </artifactItems>
                             </configuration>
diff --git a/testsuite/integration-arquillian/tests/pom.xml b/testsuite/integration-arquillian/tests/pom.xml
index 6c25059..b2ee848 100755
--- a/testsuite/integration-arquillian/tests/pom.xml
+++ b/testsuite/integration-arquillian/tests/pom.xml
@@ -74,7 +74,7 @@
         <frontend.console.output>true</frontend.console.output>
         <backends.console.output>true</backends.console.output>
 
-        <testsuite.constants>test-constants.properties</testsuite.constants>
+        <testsuite.constants>${project.build.directory}/dependency/test-constants.properties</testsuite.constants>
 
     </properties>
 
@@ -780,6 +780,14 @@
                     <groupId>org.keycloak</groupId>
                     <artifactId>keycloak-adapter-spi</artifactId>
                 </dependency>
+                <dependency>
+                    <groupId>org.keycloak</groupId>
+                    <artifactId>keycloak-saml-adapter-api-public</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.keycloak</groupId>
+                    <artifactId>keycloak-authz-client</artifactId>
+                </dependency>
 
                 <!--UNDERTOW-->
 
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 0bb466d..7e1715d 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
@@ -157,6 +157,7 @@ locale_en=English
 locale_es=Espa\u00F1ol
 locale_fr=Fran\u00e7ais
 locale_it=Italian
+locale_ja=\u65E5\u672C\u8A9E
 locale_lt=Lietuvi\u0173
 locale_pt-BR=Portugu\u00EAs (Brasil)
 locale_ru=\u0420\u0443\u0441\u0441\u043A\u0438\u0439
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/account/messages/messages_ja.properties b/themes/src/main/resources/theme/base/account/messages/messages_ja.properties
new file mode 100644
index 0000000..82c9cb5
--- /dev/null
+++ b/themes/src/main/resources/theme/base/account/messages/messages_ja.properties
@@ -0,0 +1,153 @@
+# encoding: utf-8
+doSave=保存
+doCancel=キャンセル
+doLogOutAllSessions=全セッションからログアウト
+doRemove=削除
+doAdd=追加
+doSignOut=サインアウト
+
+editAccountHtmlTitle=アカウントの編集
+federatedIdentitiesHtmlTitle=Federated Identities
+accountLogHtmlTitle=アカウントログ
+changePasswordHtmlTitle=パスワード変更
+sessionsHtmlTitle=セッション
+accountManagementTitle=Keycloak アカウント管理
+authenticatorTitle=Authenticator
+applicationsHtmlTitle=アプリケーション
+
+authenticatorCode=ワンタイムコード
+email=Eメール
+firstName=名
+givenName=名
+fullName=氏名
+lastName=姓
+familyName=姓
+password=パスワード
+passwordConfirm=新しいパスワード (確認)
+passwordNew=新しいパスワード
+username=ユーザー名
+address=住所
+street=番地
+locality=市区町村
+region=都道府県
+postal_code=郵便番号
+country=国
+emailVerified=確認済みEメール
+gssDelegationCredential=GSS 代行クレデンシャル
+
+role_admin=管理者
+role_realm-admin=レルムの管理
+role_create-realm=レルムの作成
+role_view-realm=レルムの参照
+role_view-users=ユーザーの参照
+role_view-applications=アプリケーションの参照
+role_view-clients=クライアントの参照
+role_view-events=イベントの参照
+role_view-identity-providers=アイデンティティ プロバイダーの参照
+role_manage-realm=レルムの管理
+role_manage-users=ユーザーの管理
+role_manage-applications=アプリケーションの管理
+role_manage-identity-providers=アイデンティティ プロバイダーの管理
+role_manage-clients=クライアントの管理
+role_manage-events=イベントの管理
+role_view-profile=プロフィールの参照
+role_manage-account=アカウントの管理
+role_read-token=トークンの参照
+role_offline-access=オフラインアクセス
+role_uma_authorization=アクセス権の取得
+client_account=アカウント
+client_security-admin-console=セキュリティ管理コンソール
+client_admin-cli=管理 CLI
+client_realm-management=レルム管理
+client_broker=ブローカー
+
+
+requiredFields=必須
+allFieldsRequired=全ての入力項目が必須
+
+backToApplication=&laquo; アプリケーションに戻る
+backTo={0} に戻る
+
+date=日付
+event=イベント
+ip=IP
+client=クライアント
+clients=クライアント
+details=詳細
+started=開始
+lastAccess=最終アクセス
+expires=有効期限
+applications=アプリケーション
+
+account=アカウント
+federatedIdentity=Federated Identity
+authenticator=Authenticator
+sessions=セッション
+log=ログ
+
+application=アプリケーション
+availablePermissions=使用可能なアクセス権
+grantedPermissions=許可されたアクセス権
+grantedPersonalInfo=許可された個人情報
+additionalGrants=追加の許可
+action=アクション
+inResource=in
+fullAccess=フルアクセス
+offlineToken=オフライントークン
+revoke=許可の取り消し
+
+configureAuthenticators=設定済みの Authenticator
+mobile=モバイル
+totpStep1=<a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> または Google Authenticator (Google認証システム) をご自身のデバイスにインストールしてください。これらのアプリケーションは <a href="https://play.google.com">Google Play</a> と Apple App Store で入手できます。
+totpStep2=アプリケーションを開きバーコードをスキャンするかキーを入力してください。
+totpStep3=アプリケーションで提供されたワンタイムコードを入力して保存をクリックし、セットアップを完了してください。
+
+missingUsernameMessage=ユーザー名を入力してください。
+missingFirstNameMessage=名を入力してください。
+invalidEmailMessage=無効なメールアドレスです。
+missingLastNameMessage=姓を入力してください。
+missingEmailMessage=Eメールを入力してください。
+missingPasswordMessage=パスワードを入力してください。
+notMatchPasswordMessage=パスワードが一致していません。
+
+missingTotpMessage=Authenticator コードを入力してください。
+invalidPasswordExistingMessage=無効な既存のパスワードです。
+invalidPasswordConfirmMessage=新しいパスワード (確認) と一致していません。
+invalidTotpMessage=無効な Authenticator コードです。
+
+usernameExistsMessage=既に存在するユーザー名です。
+emailExistsMessage=既に存在するEメールです。
+
+readOnlyUserMessage=リードオンリーのためアカウントを更新することはできません。
+readOnlyPasswordMessage=リードオンリーのためパスワードを更新することはできません。
+
+successTotpMessage=モバイル Authenticator が設定されました。
+successTotpRemovedMessage=モバイル Authenticator が削除されました。
+
+successGrantRevokedMessage=許可が正常に取り消しされました。
+
+accountUpdatedMessage=アカウントが更新されました。
+accountPasswordUpdatedMessage=パスワードが更新されました。
+
+missingIdentityProviderMessage=アイデンティティ プロバイダーが指定されていません。
+invalidFederatedIdentityActionMessage=無効または存在しないアクションです。
+identityProviderNotFoundMessage=指定されたアイデンティティ プロバイダーが見つかりません。
+federatedIdentityLinkNotActiveMessage=このアイデンティティは有効ではありません。
+federatedIdentityRemovingLastProviderMessage=パスワードがないため最後の Federated Identity を削除できません。
+identityProviderRedirectErrorMessage=アイデンティティ プロバイダーへのリダイレクトに失敗しました。
+identityProviderRemovedMessage=アイデンティティ プロバイダーが正常に削除されました。
+identityProviderAlreadyLinkedMessage={0}から返された Federated Identity は既に他のユーザーに関連付けされています。
+staleCodeAccountMessage=有効期限切れです。再度お試しください。
+consentDenied=同意が拒否されました。
+
+accountDisabledMessage=アカウントが無効です。管理者に連絡してください。
+
+accountTemporarilyDisabledMessage=アカウントが一時的に無効です。管理者に連絡、またはしばらく時間をおいてから再度お試しください。
+invalidPasswordMinLengthMessage=無効なパスワード: 最小 {0} の長さが必要です。
+invalidPasswordMinLowerCaseCharsMessage=無効なパスワード: 少なくとも {0} 文字の小文字を含む必要があります。
+invalidPasswordMinDigitsMessage=無効なパスワード: 少なくとも {0} 文字の数字を含む必要があります。
+invalidPasswordMinUpperCaseCharsMessage=無効なパスワード: 少なくとも {0} 文字の大文字を含む必要があります。
+invalidPasswordMinSpecialCharsMessage=無効なパスワード:  少なくとも {0} 文字の特殊文字を含む必要があります。
+invalidPasswordNotUsernameMessage=無効なパスワード: ユーザー名と同じパスワードは禁止されています。
+invalidPasswordRegexPatternMessage=無効なパスワード: 正規表現パターンと一致しません。
+invalidPasswordHistoryMessage=無効なパスワード: 最近の {0} パスワードのいずれかと同じパスワードは禁止されています。
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/account/theme.properties b/themes/src/main/resources/theme/base/account/theme.properties
index db32af8..ff4018c 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,lt,pt-BR,ru
\ No newline at end of file
+locales=ca,de,en,es,fr,it,ja,lt,pt-BR,ru
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_ja.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_ja.properties
new file mode 100644
index 0000000..f99e168
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_ja.properties
@@ -0,0 +1,1121 @@
+# encoding: utf-8
+consoleTitle=Keycloak 管理コンソール
+
+# Common messages
+enabled=有効
+name=名前
+displayName=表示名
+displayNameHtml=HTML 表示名
+save=保存
+cancel=キャンセル
+onText=オン
+offText=オフ
+client=クライアント
+clients=クライアント
+clear=クリア
+selectOne=1つ選択...
+
+true=はい
+false=いいえ
+
+endpoints=エンドポイント
+
+# Realm settings
+realm-detail.enabled.tooltip=有効の場合は、ユーザーとクライアントはこのレルムのみアクセス可能になります
+realm-detail.oidc-endpoints.tooltip=OpenID Connect エンドポイントの設定を表示します。
+registrationAllowed=ユーザー登録
+registrationAllowed.tooltip=登録ページの有効/無効。ログインページに登録のリンクも表示されるようになります。
+registrationEmailAsUsername=Eメールをユーザー名とする
+registrationEmailAsUsername.tooltip=有効の場合は、登録フォームにおいてユーザー名フィールドが非表示なり、Eメールが新規ユーザーのユーザー名として使われます。
+editUsernameAllowed=ユーザー名の編集
+editUsernameAllowed.tooltip=有効の場合はユーザー名フィールドが編集可能になり、そうでない場合は読み取り専用になります。
+resetPasswordAllowed=パスワード忘れ
+resetPasswordAllowed.tooltip=クレデンシャルを忘れてしまった場合にクリックするリンクをログインページに表示します。
+rememberMe=ログイン状態の保存
+rememberMe.tooltip=セッションの有効期限が切れるまではブラウザーの再起動でもログイン状態を保存するチェックボックスをログインページに表示します。
+verifyEmail=Eメールの確認
+verifyEmail.tooltip=初回ログイン時にメールアドレスの確認をユーザーに要求します。
+sslRequired=SSL の要求
+sslRequired.option.all=全てのリクエスト
+sslRequired.option.external=外部リクエスト
+sslRequired.option.none=なし
+sslRequired.tooltip=HTTPS は必須ですか? 「なし」 は HTTPS がどのクライアント IP アドレスにも要求されないことを意味します。 「外部リクエスト」 はローカルホストとプライベート IP アドレスは HTTPS なしでアクセスできることを意味します。 「すべてのリクエスト」 は HTTPS がすべての IP アドレスに要求されることを意味します。
+publicKey=公開鍵
+privateKey=秘密鍵
+gen-new-keys=新しい鍵を生成する
+certificate=証明書
+host=ホスト
+smtp-host=SMTP ホスト
+port=ポート
+smtp-port=SMTP ポート (デフォルトは25)
+from=差出人
+sender-email-addr=送信者のメールアドレス
+enable-ssl=SSL の有効
+enable-start-tls=StartTLS の有効
+enable-auth=認証の有効
+username=ユーザー名
+login-username=ログインユーザー名
+password=パスワード
+login-password=ログインパスワード
+login-theme=ログインテーマ
+login-theme.tooltip=ログイン、TOTP、グラント、登録、パスワード忘れ画面のテーマを選択します。
+account-theme=アカウントテーマ
+account-theme.tooltip=ユーザーアカウント管理画面のテーマを選択します。
+admin-console-theme=管理コンソールテーマ
+select-theme-admin-console=管理コンソールのテーマを選択します。
+email-theme=Eメールテーマ
+select-theme-email=サーバーから送信されるEメールのテーマを選択します。
+i18n-enabled=国際化の有効
+supported-locales=サポートされるロケール
+supported-locales.placeholder=ロケールを入力し Enter キーを押してください
+default-locale=デフォルトロケール
+realm-cache-clear=レルムキャッシュ
+realm-cache-clear.tooltip=レルムキャッシュからすべてのエントリをクリアする (すべてのレルムのエントリをクリアします)
+user-cache-clear=ユーザーキャッシュ
+user-cache-clear.tooltip=ユーザーキャッシュからすべてのエントリを削除します (すべてのレルムのエントリをクリアします)
+revoke-refresh-token=リフレッシュトークンの取り消し
+revoke-refresh-token.tooltip=有効の場合は、リフレッシュトークンは1回のみ使用可能になります。無効の場合は、リフレッシュトークンは使用されても取り消しされず、何度でも使用可能になります。
+sso-session-idle=SSO セッションアイドル
+seconds=秒
+minutes=分
+hours=時
+days=日
+sso-session-max=SSO セッション最大
+sso-session-idle.tooltip=セッションの有効期限が切れるまでのアイドル時間です。セッションが有効期限切れの際はトークンとブラウザーセッションは無効化されます。
+sso-session-max.tooltip=セッションの有効期限が切れるまでの最大時間です。セッションが有効期限切れの際はトークンとブラウザーセッションは無効化されます。
+offline-session-idle=オフラインセッションアイドル
+offline-session-idle.tooltip=セッションの有効期限が切れるまでのオフライン時間です。この期限内に少なくとも1回はオフライントークンを使用してリフレッシュしないと、オフラインセッションは有効期限切れとなります。
+access-token-lifespan=アクセストークン生存期間
+access-token-lifespan.tooltip=アクセストークンが有効期限切れとなる最大時間です。この値は SSO タイムアウトと比べて短くすることをお勧めします。
+access-token-lifespan-for-implicit-flow=Implicit Flow におけるアクセストークン生存期間
+access-token-lifespan-for-implicit-flow.tooltip=OpenID Connect Implicit Flow で発行されたアクセストークンが有効期限切れとなる最大時間です。この値は SSO タイムアウトより短くすることをお勧めします。 Implicit Flow ではトークンを更新することはありませんので、 「アクセストークン生存期間」 とは異なる別々のタイムアウト設定を設けています。
+client-login-timeout=クライアントのログインタイムアウト
+client-login-timeout.tooltip=クライアントがアクセストークンプロトコルを完了するまでの最大時間です。これは通常は1分にすべきです。
+login-timeout=ログインタイムアウト
+login-timeout.tooltip=ユーザーがログインを完了するまでの最大時間です。これは30分以上と比較的長くすることをお勧めします。
+login-action-timeout=ログインアクションタイムアウト
+login-action-timeout.tooltip=ユーザーがパスワードの更新や TOTP の設定のようなログインに関係するアクションを完了するまでの最大時間です。これは5分以上と比較的長くすることをお勧めします。
+headers=ヘッダー
+brute-force-detection=ブルートフォースの検出
+x-frame-options=X-Frame-Options
+x-frame-options-tooltip=デフォルト値では別のオリジンの IFrame からの読み込みを防ぎます (詳細はラベルをクリックしてください)
+content-sec-policy=Content-Security-Policy
+content-sec-policy-tooltip=デフォルト値では別のオリジンの IFrame からの読み込みを防ぎます (詳細はラベルをクリックしてください)
+content-type-options=X-Content-Type-Options
+content-type-options-tooltip=デフォルト値では Internet Explorer と Google Chrome に対して、宣言された content-type を避けてレスポンスの MIME-sniffing を行うことを防ぎます (詳細はラベルをクリックしてください) 
+max-login-failures=最大ログイン失敗回数
+max-login-failures.tooltip=検出するまでの失敗回数です。
+wait-increment=連続失敗時の待機時間
+wait-increment.tooltip=失敗回数が閾値に達した場合、どれくらいの時間ユーザーはロックアウトされるか設定します。
+quick-login-check-millis=クイックログイン試行間のミリ秒数チェック
+quick-login-check-millis.tooltip=クイックログイン失敗があまりにも頻繁に発生した場合は、ユーザーをロックアウトします。
+min-quick-login-wait=クイックログイン失敗時の最小待機時間
+min-quick-login-wait.tooltip=クイックログイン失敗後にどれくらいの時間待機するか設定します。
+max-wait=最大待機時間
+max-wait.tooltip=ユーザーがロックアウトされる最大待機時間を設定します。
+failure-reset-time=ログイン失敗回数のリセット時間
+failure-reset-time.tooltip=いつ失敗回数がリセットされるか設定します。
+realm-tab-login=ログイン
+realm-tab-keys=鍵
+realm-tab-email=Eメール
+realm-tab-themes=テーマ
+realm-tab-cache=キャッシュ
+realm-tab-tokens=トークン
+realm-tab-client-initial-access=初期アクセストークン
+realm-tab-security-defenses=セキュリティ防御
+realm-tab-general=一般
+add-realm=レルムの追加
+
+#Session settings
+realm-sessions=レルムセッション
+revocation=取り消し
+logout-all=すべてログアウトする
+active-sessions=有効なセッション
+sessions=セッション
+not-before=この日時より前
+not-before.tooltip=この日時より前に発行されたトークンを取り消します。
+set-to-now=現在日時を設定
+push=プッシュ
+push.tooltip=管理URLを持つすべてのクライアントに対して、新しい取り消しポリシーを通知します。
+
+#Protocol Mapper
+usermodel.prop.label=プロパティ
+usermodel.prop.tooltip=UserModel インタフェースのプロパティメソッドの名前です。例えば、 「email」 の値は UserModel.getEmail() メソッドを参照しています。
+usermodel.attr.label=ユーザー属性
+usermodel.attr.tooltip=格納されるユーザー属性名、UserMode.attribute マップ内の属性名です。
+userSession.modelNote.label=ユーザーセッションノート
+userSession.modelNote.tooltip=UserSessionModel.note マップ内のユーザーセッションノート名です。
+multivalued.label=マルチバリュー
+multivalued.tooltip=属性がマルチバリューをサポートしているかどうかを示します。サポートしている場合は、この属性のすべての値リストがクレームとして設定されます。サポートしていない場合は、最初の値だけがクレームとして設定されます。
+selectRole.label=ロールの選択
+selectRole.tooltip=左側にあるテキストボックスにロールを入力するか、ブラウズして必要なロールを選択するためにこのボタンをクリックしてください。
+tokenClaimName.label=トークンクレーム名
+tokenClaimName.tooltip=トークン内に挿入するクレームの名前を設定します。 「address.street」 のように完全修飾名で設定します。この場合、ネストされた JSON オブジェクトが作成されます。
+jsonType.label=クレーム JSON タイプ
+jsonType.tooltip=トークンへの JSON クレーム の追加で使用される JSON タイプを設定します。 long、int、boolean、String が有効な値です。
+includeInIdToken.label=ID トークンに追加
+includeInIdToken.tooltip=クレームを ID トークンに追加すべきかどうかを設定します。
+includeInAccessToken.label=アクセストークンに追加
+includeInAccessToken.tooltip=クレームをアクセストークンに追加すべきかどうかを設定します。
+includeInUserInfo.label=UserInfo に追加
+includeInUserInfo.tooltip=クレームを UserInfo に追加すべきかどうかを設定します。
+usermodel.clientRoleMapping.clientId.label=クライアント ID
+usermodel.clientRoleMapping.clientId.tooltip=ロールマッピング用のクライアント ID
+usermodel.clientRoleMapping.rolePrefix.label=クライアントロールのプレフィックス
+usermodel.clientRoleMapping.rolePrefix.tooltip=各クライアントロールのプレフィックスを設定します (オプション)。
+usermodel.realmRoleMapping.rolePrefix.label=レルムロールのプレフィックス
+usermodel.realmRoleMapping.rolePrefix.tooltip=各レルムロールのプレフィックスを設定します (オプション)。
+
+# client details
+clients.tooltip=クライアントとはレルム内の信頼されたブラウザーアプリケーションや Web サービスです。これらのクライアントはログインを要求することができます。また、クライアント固有のロールを定義することができます。
+search.placeholder=検索...
+create=作成
+import=インポート
+client-id=クライアント ID
+base-url=ベース URL
+actions=アクション
+not-defined=未定義
+edit=編集
+delete=削除
+no-results=結果がありません
+no-clients-available=使用可能なクライアントはありません
+add-client=クライアントの追加
+select-file=ファイルを選択
+view-details=詳細を参照
+clear-import=インポートをクリア
+client-id.tooltip=URI とトークンで参照される ID を指定します。例えば 「my-client」 です。 SAML においては 期待される AuthnRequest の Issuer の値になります。
+client.name.tooltip=クライアントの表示名を指定します。例えば、 「My Client」 です。ローカライズ用のキーもサポートしています。例\: ${my_client}
+client.enabled.tooltip=無効なクライアントはログインを開始したり、アクセストークンを取得したりすることはできません。
+consent-required=同意が必要
+consent-required.tooltip=有効の場合は、ユーザーはクライアントアクセスに同意する必要があります。
+client-protocol=クライアントプロトコル
+client-protocol.tooltip=「OpenID Connect」 は、認可サーバーによって実行される認証を元にして、エンドユーザーのアイデンティティを確認するクライアントです。 「SAML」 は、クロスドメインのシングルサインオン (SSO) を含むWeb ベースの認証と認可シナリオを可能にし、情報を渡すためにアサーションを含むセキュリティトークンを使用します。
+access-type=アクセスタイプ
+access-type.tooltip=「Confidential」 クライアントはログインプロトコルの開始するためにシークレットを必要とします。 「Public」 クライアントはシークレットを必要としません。 「Bearer-only」 クライアントはログインを開始することはない Web サービスです。
+standard-flow-enabled=Standard Flow の有効
+standard-flow-enabled.tooltip=OpenID Connect の標準的な、認可コードによるリダイレクトベースの認証を有効にします。 OpenID Connect または OAuth2 の仕様における 「Authorization Code Flow」 のサポートを有効にします。
+implicit-flow-enabled=Implicit Flow の有効
+implicit-flow-enabled.tooltip=OpenID Connect の認可コードなしのリダイレクトベース認証のサポートを有効にします。OpenID Connect または OAuth2 の仕様における 「Implicit Flow」 のサポートを有効にします。
+direct-access-grants-enabled=ダイレクトアクセスグラントの有効
+direct-access-grants-enabled.tooltip=ダイレクトアクセスグラントのサポートを有効にします。これは、アクセストークンの取得のために Keycloak サーバーとユーザーのユーザー名/パスワードで直接アクセスを行います。OAuth2 の仕様における 「リソースオーナーパスワードクレデンシャルグラント」 のサポートを有効にします。
+service-accounts-enabled=サービスアカウントの有効
+service-accounts-enabled.tooltip=Keycloak にこのクライアントを認証し、このクライアント専用のアクセストークンの取得ができるようになります。OAuth2 の仕様における 「クライアントクレデンシャルグラント」 のサポートを有効にします。
+include-authnstatement=AuthnStatement を含める
+include-authnstatement.tooltip=認証方式とタイムスタンプを含めたステートメントをログインレスポンスに含めるべきか設定します。
+sign-documents=ドキュメントを署名する
+sign-documents.tooltip=SAML ドキュメントをレルムで署名すべきか設定します。
+sign-assertions=アサーションを署名する
+sign-assertions.tooltip=SAML ドキュメント内のアサーションを署名すべきか設定します。もしドキュメントが既に署名済みの場合は、この設定は不要です。
+signature-algorithm=署名アルゴリズム
+signature-algorithm.tooltip=ドキュメントの署名に使用する署名アルゴリズムです。
+canonicalization-method=正規化方式
+canonicalization-method.tooltip=XML署名の正規化方式 (Canonicalization Method) を設定します。
+encrypt-assertions=アサーションを暗号化する
+encrypt-assertions.tooltip=SAML アサーションをクライアントの公開鍵で AES を使い暗号化すべきか設定します。
+client-signature-required=クライアント署名が必須
+client-signature-required.tooltip=クライアントは SAML リクエストとレスポンスを署名しますか? それらを検証すべきか設定します。
+force-post-binding=POST Binding を強制
+force-post-binding.tooltip=レスポンスに常に POST Binding を使用します。
+front-channel-logout=フロントチャンネルログアウト
+front-channel-logout.tooltip=有効の場合は、ログアウトはクライアントへのブラウザーリダイレクトが必要になります。無効の場合は、サーバーはログアウトのバックグラウンド呼び出しを行います。
+force-name-id-format=Name ID フォーマットを強制
+force-name-id-format.tooltip=要求された NameID サブジェクトフォーマットを無視し、管理コンソールで設定された物を使用します。
+name-id-format=Name ID フォーマット
+name-id-format.tooltip=サブジェクトに使用する Name ID フォーマットを設定します。
+root-url=ルート URL
+root-url.tooltip=相対 URL に追加するルート URL を設定します。
+valid-redirect-uris=有効なリダイレクト URI
+valid-redirect-uris.tooltip=ログインまたはログインの成功後にブラウザーがリダイレクト可能とする、有効な URI パターンを設定します。単純なワイルドカード、つまり 「http://example.com/*」 が使用可能です。相対パス、つまり 「/my/relative/path/*」 も指定可能です。相対パスはクライアントのルート URL を基準とします。または、未指定の場合は認証サーバーのルート URL が使用されます。SAML では、ログインリクエストに埋め込まれたコンシューマサービスの URL に依存している場合は、有効な URI パターンを設定する必要があります。
+base-url.tooltip=認証サーバーがクライアントへのリダイレクトまたは戻るリンクを必要とする際に使用するデフォルト URL を設定します。
+admin-url=管理 URL
+admin-url.tooltip=クライアントの管理インタフェースの URL を設定します。クライアントがアダプターの REST API をサポートしている場合に設定してください。この REST API により、認証サーバーは取り消しポリシーや他の管理タスクをプッシュすることができます。通常、クライアントのベース URL を設定します。
+master-saml-processing-url=SAML を処理するマスター URL
+master-saml-processing-url.tooltip=設定された場合は、この URL が SP のアサーションコンシューマおよびシングルログアウトサービスの両方の Binding に使われます。これは、SAML エンドポイントの詳細設定にある各 Binding やサービスの設定にて個別に上書きすることができます。
+idp-sso-url-ref=IDP Initiated SSO の URL Name
+idp-sso-url-ref.tooltip=IDP Initiated SSO を行う際にクライアントを参照するための URL フラグメント名を設定します。空にすると IDP Initiated SSO は無効になります。ブラウザーから参照する URL は 「{server-root}/realms/{realm}/protocol/saml/clients/{client-url-name}」 になります。
+idp-sso-relay-state=IDP Initiated SSO の RelayState
+idp-sso-relay-state.tooltip=IDP Initiated SSO を行う際の SAML リクエストで送信したい RelayState を設定します。
+web-origins=Web オリジン
+web-origins.tooltip=許可される CORS オリジンを設定します。有効なリダイレクト URI のすべてのオリジンを許可するには 「+」 を追加してください。すべてのオリジンを許可するには 「*」 を追加してください。
+fine-saml-endpoint-conf=SAML エンドポイントの詳細設定
+fine-saml-endpoint-conf.tooltip=アサーションコンシューマおよびシングルログアウトサービスの正確な URL を設定するにはこのセクションを開きます。
+assertion-consumer-post-binding-url=アサーションコンシューマサービスの POST Binding URL
+assertion-consumer-post-binding-url.tooltip=アサーションコンシューマサービス (ログインレスポンス) の SAML POST Binding URL を設定します。この Binding のための URL がない場合は空でよいです。
+assertion-consumer-redirect-binding-url=アサーションコンシューマサービスの Redirect Binding URL
+assertion-consumer-redirect-binding-url.tooltip=アサーションコンシューマサービス (ログインレスポンス) の SAML Redirect Binding URL を設定します。この Binding のための URL がない場合は空でよいです。
+logout-service-post-binding-url=ログアウトサービスの POST Binding URL
+logout-service-post-binding-url.tooltip=シングルログアウトサービスの SAML POST Binding URLを設定します。異なる Binding を使用している場合は空でよいです。
+logout-service-redir-binding-url=ログアウトサービスの Redirect Binding URL
+logout-service-redir-binding-url.tooltip=シングルログアウトサービスの SAML Redirect Binding URLを設定します。異なる Binding を使用している場合は空でよいです。
+
+# client import
+import-client=クライアントのインポート
+format-option=フォーマットオプション
+select-format=フォーマットを選択
+import-file=ファイルをインポート
+
+# client tabs
+settings=設定
+credentials=クレデンシャル
+saml-keys=SAML 鍵
+roles=ロール
+mappers=マッパー
+mappers.tooltip=プロトコルマッパーはトークンやドキュメントの変換を行います。ユーザーデータをプロトコルのクレームにマッピングしたり、クライアントと認証サーバー間の任意のリクエストを単に変換したりすることができます。
+scope=スコープ
+scope.tooltip=スコープマッピングはクライアントに要求されたアクセストークン内に含まれるユーザーのロールマッピングを制限することができます。
+sessions.tooltip=このクライアントの有効なセッションを参照します。どのユーザーがアクティブでいつログインしたかを見ることができます。
+offline-access=オフラインアクセス
+offline-access.tooltip=このクライアントのオフラインセッションを参照します。オフライントークンをどのユーザーがいつ取得したかを見ることができます。このクライアントのすべてのトークンを取り消すには、取り消しタブを開き、 「この日時より前」 に現在日時を設定してください。
+clustering=クラスタリング
+installation=インストール
+installation.tooltip=様々なクライアントアダプターの設定フォーマットを生成するヘルパーユーティリティです。生成したものをダウンロードまたはカットアンドペーストしてクライアントに設定することができます。
+service-account-roles=サービスアカウントロール
+service-account-roles.tooltip=このクライアント専用のサービスアカウントのロールマッピングを認証できるようにします。
+
+# client credentials
+client-authenticator=クライアント認証
+client-authenticator.tooltip=Keycloak サーバーに対してこのクライアントの認証に使用するクライアント認証方式を設定します。
+certificate.tooltip=クライアントで発行され、キーストアの秘密鍵で署名された JWT を検証するためのクライアント証明書です。
+publicKey.tooltip=クライアントで発行され、秘密鍵で署名された JWT を検証するための公開鍵です。
+no-client-certificate-configured=クライアント証明書が設定されていません
+gen-new-keys-and-cert=新しい鍵と証明書を生成
+import-certificate=証明書をインポート
+gen-client-private-key=クライアントの秘密鍵の生成
+generate-private-key=秘密鍵の生成
+archive-format=アーカイブ形式
+archive-format.tooltip=Java キーストアまたは PKCS12 アーカイブ形式
+key-alias=キーエイリアス
+key-alias.tooltip=秘密鍵と証明書のアーカイブエイリアスです。
+key-password=鍵のパスワード
+key-password.tooltip=アーカイブ内の秘密鍵にアクセスするためのパスワード
+store-password=ストアのパスワード
+store-password.tooltip=アーカイブ自身にアクセスするためのパスワード
+generate-and-download=生成 & ダウンロード
+client-certificate-import=クライアント証明書のインポート
+import-client-certificate=クライアント証明書のインポート
+jwt-import.key-alias.tooltip=証明書のアーカイブエイリアスです。
+secret=シークレット
+regenerate-secret=シークレットの再生成
+registrationAccessToken=登録用アクセストークン
+registrationAccessToken.regenerate=登録用アクセストークンの再生成
+registrationAccessToken.tooltip=登録用アクセストークンにより、クライアントはクライアント登録サービスにアクセスできます。
+add-role=ロールの追加
+role-name=ロール名
+composite=複合
+description=説明
+no-client-roles-available=使用可能なクライアントロールはありません。
+scope-param-required=スコープパラメータが必要
+scope-param-required.tooltip=認可/トークンリクエストの間にロール名のスコープパラメータが使用された場合のみ与えられます。
+composite-roles=複合ロール
+composite-roles.tooltip=このロールがユーザーにアサイン(アサイン解除)された際に、関連するロールが暗黙的にアサイン(アサイン解除)されます。
+realm-roles=レルムロール
+available-roles=使用可能なロール
+add-selected=選択したものを追加
+associated-roles=関連ロール
+composite.associated-realm-roles.tooltip=この複合ロールに関連付けされているレルムレベルのロールです。
+composite.available-realm-roles.tooltip=この複合ロールに関連付け可能なレルムレベルのロールです。
+remove-selected=選択されたものを削除
+client-roles=クライアントロール
+select-client-to-view-roles=ロールを参照するにはクライアントを選択してください
+available-roles.tooltip=この複合ロール関連付け可能なこのクライアントのロールです。
+client.associated-roles.tooltip=この複合ロールに関連付けされているクライアントロールです。
+add-builtin=ビルトインを追加
+category=カテゴリ
+type=タイプ
+no-mappers-available=使用可能なマッパーはありません
+add-builtin-protocol-mappers=ビルトインプロトコルマッパーを追加
+add-builtin-protocol-mapper=ビルトインプロトコルマッパーを追加
+scope-mappings=スコープマッピング
+full-scope-allowed=フルスコープを許可
+full-scope-allowed.tooltip=全ての制限の無効を許可します。
+scope.available-roles.tooltip=スコープにアサイン可能なレルムレベルのロールです。
+assigned-roles=アサイン済みロール
+assigned-roles.tooltip=スコープにアサイン済みのレルムレベルのロールです。
+effective-roles=有効なロール
+realm.effective-roles.tooltip=複合ロールの継承も含めたアサイン済みのレルムレベルのロールです。
+select-client-roles.tooltip=ロールを参照するにはクライアントを選択してください
+assign.available-roles.tooltip=アサイン可能なクライアントロールです。
+client.assigned-roles.tooltip=アサイン済みクライアントロールです。
+client.effective-roles.tooltip=複合ロールより引き継いでいるロールも含めたアサイン済みのクライアントロールです。
+basic-configuration=基本設定
+node-reregistration-timeout=ノード再登録のタイムアウト
+node-reregistration-timeout.tooltip=登録されたクライアントをクラスターノードへ再登録する際の最大時間間隔を設定します。クラスターノードがこの時間内に Keycloak に再登録リクエストを送信しない場合は、Keycloak から登録解除されます。
+registered-cluster-nodes=登録済みクラスターノード
+register-node-manually=ノードを手動で登録
+test-cluster-availability=クラスターの可用性をテスト
+last-registration=最終登録
+node-host=ノードホスト
+no-registered-cluster-nodes=使用可能な登録済みクラスターノードがありません
+cluster-nodes=クラスターノード
+add-node=ノードを追加
+active-sessions.tooltip=このクライントの有効なユーザーセッションの合計数です。
+show-sessions=セッションを表示
+show-sessions.tooltip=有効なセッション数に応じて高負荷なオペレーションになる恐れがありますので注意してください。
+user=ユーザー
+from-ip=From IP
+session-start=セッション開始
+first-page=最初のページ
+previous-page=前のページ
+next-page=次のページ
+client-revoke.not-before.tooltip=この日時より前に発行されたこのクライアント用のトークンを取り消します。
+client-revoke.push.tooltip=管理 URL がこのクライアントに設定されている場合は、クライアントにポリシーをプッシュします。
+select-a-format=フォーマットを選択
+download=ダウンロード
+offline-tokens=オフライントークン
+offline-tokens.tooltip=このクライアントのオフライントークンの合計数です。
+show-offline-tokens=オフライントークンを表示
+show-offline-tokens.tooltip=オフライントークン数に応じて高負荷なオペレーションになる恐れがありますので注意してください。
+token-issued=発行済みトークン
+last-access=最終アクセス
+last-refresh=最終リフレッシュ
+key-export=鍵をエクスポート
+key-import=鍵をインポート
+export-saml-key=SAML 鍵をエクスポート
+import-saml-key=SAML 鍵をインポート
+realm-certificate-alias=レルム証明書エイリアス
+realm-certificate-alias.tooltip=レルム証明書もアーカイブに格納されます。これはそのエイリアスとなります。
+signing-key=署名鍵
+saml-signing-key=SAML 署名鍵です。
+private-key=秘密鍵
+generate-new-keys=新しい鍵を生成
+export=エクスポート
+encryption-key=暗号化鍵
+saml-encryption-key.tooltip=SAML 暗号化鍵です。
+service-accounts=サービスアカウント
+service-account.available-roles.tooltip=サービスアカウントにアサイン可能なレルムレベルのロールです。
+service-account.assigned-roles.tooltip=サービスアカウントにアサイン済みのレルムレベルのロールです。
+service-account-is-not-enabled-for={{client}} のサービスアカウントは有効ではありません
+create-protocol-mappers=プロトコルマッパーを作成
+create-protocol-mapper=プロトコルマッパーを作成
+protocol=プロトコル
+protocol.tooltip=プロトコルです。
+id=ID
+mapper.name.tooltip=マッパーの名前です。
+mapper.consent-required.tooltip=一時的なアクセスを許可する際に、クライアントへの提供データにユーザーの同意を必要とするか設定します。
+consent-text=同意のテキスト
+consent-text.tooltip=同意ページに表示するテキストです。
+mapper-type=マッパータイプ
+mapper-type.tooltip=マッパーのタイプです。
+# realm identity providers
+identity-providers=アイデンティティ プロバイダー
+table-of-identity-providers=アイデンティティ プロバイダーの一覧表
+add-provider.placeholder=プロバイダーを追加...
+provider=プロバイダー
+gui-order=GUI 順序
+first-broker-login-flow=初回ログインフロー
+post-broker-login-flow=ログイン後のフロー
+redirect-uri=リダイレクト URI
+redirect-uri.tooltip=アイデンティティ プロバイダーの設定で使用するリダイレクト URIです。
+alias=エイリアス
+identity-provider.alias.tooltip=エイリアスは一意にアイデンティティ プロバイダーを識別するもので、リダイレクト URI の構築にも使用されます。
+identity-provider.enabled.tooltip=このアイデンティティ プロバイダーの有効/無効を設定します。
+authenticate-by-default=デフォルトで認証
+identity-provider.authenticate-by-default.tooltip=ログイン画面の表示前に、このプロバイダーでデフォルトで認証試行すべきかどうかを示しています。
+store-tokens=トークンの格納
+identity-provider.store-tokens.tooltip=ユーザー認証後のトークン格納の有効/無効を設定します。
+stored-tokens-readable=読み取り可能なトークンを格納
+identity-provider.stored-tokens-readable.tooltip=新しいユーザーが格納されたトークンを読み取り可能かどうかの有効/無効設定です。broker.read-token ロールをアサインします。
+update-profile-on-first-login=初回ログイン時にプロフィールを更新
+on=オン
+on-missing-info=情報不足の場合
+off=オフ
+update-profile-on-first-login.tooltip=初回ログイン時にどのユーザーがプロフィールの更新を必要とするか条件を定義します。
+trust-email=Eメールを信頼
+trust-email.tooltip=有効とした場合は、このレルムでEメールの確認が有効となっている場合でも、このプロバイダーが提供するEメールは確認されなくなります。
+gui-order.tooltip=GUI (例. ログインページ上) でのプロバイダーの表示順序を決める番号を設定します。
+first-broker-login-flow.tooltip=このアイデンティティ プロバイダーでの初回ログイン後に起動させる認証フローのエイリアスです。 「初回ログイン」 という用語は、認証したアイデンティティ プロバイダーアカウントに関連付けられた Keycloak アカウントがまだ存在しない状態であることを意味します。
+post-broker-login-flow.tooltip=このアイデンティティ プロバイダーでの各ログイン後に起動させる認証フローのエイリアスです。このアイデンティティ プロバイダーで認証された各ユーザーの追加の確認 (例えば OTP) を行いたい場合に便利です。このアイデンティティ プロバイダーによるログイン後に追加の Authenticator の起動を行いたくない場合は、空のままとしてください。また、Authenticator の実装では、ClientSession にはアイデンティティ プロバイダーによりユーザーが既に設定されていることに注意してください。
+openid-connect-config=OpenID Connect の設定
+openid-connect-config.tooltip=OIDC SP と 外部 IDP の設定です。
+authorization-url=認可 URL
+authorization-url.tooltip=認可 URL を設定します。
+token-url=トークン URL
+token-url.tooltip=トークン URL を設定します。
+logout-url=ログアウト URL
+identity-provider.logout-url.tooltip=外部 IDP からユーザーのログアウトに使用するセッション終了エンドポイントを設定します。
+backchannel-logout=バックチャンネルログアウト
+backchannel-logout.tooltip=外部 IDP がバックチャンネルログアウトをサポートするどうかを設定します。
+user-info-url=UserInfo URL
+user-info-url.tooltip=UserInfo の URL を設定します。これはオプションです。
+identity-provider.client-id.tooltip=アイデンティティ プロバイダーで登録されているクライアントまたはクライアント識別子を設定します。
+client-secret=クライアントシークレット
+show-secret=シークレットを表示する
+hide-secret=シークレットを隠す
+client-secret.tooltip=アイデンティティ プロバイダーで登録されているクライアントまたはクライアントシークレットを設定します。
+issuer=発行者 (Issuer)
+issuer.tooltip=レスポンス内の発行者の識別子 (Issuer Identifier) を設定します。未設定の場合は、検証は実行されません。
+default-scopes=デフォルトスコープ
+identity-provider.default-scopes.tooltip=認可要求の際に送信されるスコープです。スペース区切りでスコープのリストを設定します。デフォルトは 「openid」 です。
+prompt=プロンプト (prompt)
+unspecified.option=未定義
+none.option=none
+consent.option=consent
+login.option=login
+select-account.option=select_account
+prompt.tooltip=認証サーバーは再認証や同意をエンドユーザーに促すかどうかを指定します。
+validate-signatures=署名検証
+identity-provider.validate-signatures.tooltip=外部 IDP の署名検証の有効/無効を設定します。
+validating-public-key=検証用の公開鍵
+identity-provider.validating-public-key.tooltip=外部 IDP の署名検証に使用する PEM 形式の公開鍵を設定します。
+import-external-idp-config=外部 IDP 設定のインポート
+import-external-idp-config.tooltip=外部 IDP メタデータを設定ファイルよりロード、または URL よりダウンロードして設定します。
+import-from-url=URL よりインポート
+identity-provider.import-from-url.tooltip=リモート IDP ディスカバリディスクリプタよりメタデータをインポートします。
+import-from-file=ファイルよりインポート
+identity-provider.import-from-file.tooltip=ダウンロードした IDP ディスカバリディスクリプタよりメタデータをインポートします。
+saml-config=SAML 設定
+identity-provider.saml-config.tooltip=SAML SP と 外部 IDP の設定です。
+single-signon-service-url=シングルサインオンサービスの URL
+saml.single-signon-service-url.tooltip=認証リクエスト (SAML AuthnRequest) の送信に使用する URL を設定します。
+single-logout-service-url=シングルログアウトサービスの URL
+saml.single-logout-service-url.tooltip=ログアウトリクエストの送信に使用する URL を設定します。
+nameid-policy-format=Name ID ポリシーフォーマット
+nameid-policy-format.tooltip=Name ID フォーマットに対応する URI リファレンスを指定します。デフォルトは urn:oasis:names:tc:SAML:2.0:nameid-format:persistent になります。
+http-post-binding-response=HTTP-POST Binding レスポンス
+http-post-binding-response.tooltip=HTTP-POST Binding を使用してリクエストに応答するかどうかを設定します。オフの場合は、HTTP-REDIRECT Binding が使用されます。
+http-post-binding-for-authn-request=AuthnRequest の HTTP-POST Binding
+http-post-binding-for-authn-request.tooltip=HTTP-POST Binding を使用して AuthnRequest を送信するかどうかを設定します。オフの場合は、HTTP-REDIRECT Binding が使用されます。
+want-authn-requests-signed=AuthnRequest の署名が必要
+want-authn-requests-signed.tooltip=アイデンティティ プロバイダーが署名された AuthnRequest を必要とするかどうかを設定します。
+force-authentication=認証を強制
+identity-provider.force-authentication.tooltip=アイデンティティ プロバイダーが以前のセキュリティコンテキストに頼るのではなく、プレゼンターを直接認証すべきかどうかを設定します。
+validate-signature=署名検証
+saml.validate-signature.tooltip=SAML レスポンスの署名検証の有効/無効を設定します。
+validating-x509-certificate=検証用の X509 証明書
+validating-x509-certificate.tooltip=署名の確認に使用する PEM 形式の証明書を設定します。
+saml.import-from-url.tooltip=リモート IDP の SAML エンティティディスクリプタからメタデータをインポートします。
+social.client-id.tooltip=アイデンティティ プロバイダーで登録されているクライアント識別子を設定します。
+social.client-secret.tooltip=アイデンティティ プロバイダーで登録されているクライアントシークレットを設定します。
+social.default-scopes.tooltip=認可要求の際に送信されるスコープを設定します。設定可能な値、区切り文字、デフォルト値はドキュメントを参照してください。
+key=Key
+stackoverflow.key.tooltip=Stack Overflow のクライアント登録で取得した Key を設定します。
+
+# User federation
+sync-ldap-roles-to-keycloak=LDAP ロールを Keycloak に同期
+sync-keycloak-roles-to-ldap=Keycloak ロールを LDAP に同期
+sync-ldap-groups-to-keycloak=LDAP グループを Keycloak に同期
+sync-keycloak-groups-to-ldap=Keycloak グループを LDAP に同期
+
+realms=レルム
+realm=レルム
+
+identity-provider-mappers=アイデンティティ プロバイダー マッパー
+create-identity-provider-mapper=アイデンティティ プロバイダー マッパーを作成
+add-identity-provider-mapper=アイデンティティ プロバイダー マッパーを追加
+client.description.tooltip=クライアントの説明を指定します。例えば 「タイムシート用のクライアント」 です。ローカライズ用のキーもサポートしています。例\: ${my_client_description}
+
+expires=有効期限
+expiration=有効期限
+expiration.tooltip=トークンの有効期間を指定します。
+count=カウント
+count.tooltip=このトークンを利用してクライアントをいくつ作成可能か指定します。
+remainingCount=残りのカウント
+created=作成日時
+back=戻る
+initial-access-tokens=初期アクセストークン
+initial-access-tokens.tooltip=動的クライアント登録用の初期アクセストークンです。これらのトークン付きのリクエストは任意のホストから送信することができます。
+add-initial-access-tokens=初期アクセストークンを追加
+initial-access-token=初期アクセストークン
+initial-access.copyPaste.tooltip=後からは取得することはできませんので、このページから離れる前に初期アクセストークンをコピー/ペーストしてください。
+continue=続ける
+initial-access-token.confirm.title=初期アクセストークンのコピー
+initial-access-token.confirm.text=後からは取得することはできませんので、初期アクセストークンのコピー & ペーストを行ってください
+no-initial-access-available=使用可能な初期アクセストークンはありません
+
+trusted-hosts-legend=クライアント登録用の信頼されたホスト
+trusted-hosts-legend.tooltip=クライアント登録用に信頼されたホストです。これらのホストからのクライアント登録のリクエストは、初期アクセストークンなしで送信することができます。特定ホストからのクライアント登録数は指定された数に制限することができます。
+no-client-trusted-hosts-available=使用可能な信頼されたホストはありません
+add-client-reg-trusted-host=信頼されたホストの追加
+hostname=ホスト名
+client-reg-hostname.tooltip=完全修飾のホスト名またはIPアドレスです。このホスト/アドレスからのクライアント登録のリクエストは信頼され、新しいクライアント登録が許可されます。
+client-reg-count.tooltip=特定ホストからのクライアント登録リクエストを許可する数です。このリミットに達した場合はリスタートする必要があります。
+client-reg-remainingCount.tooltip=このホストからのクライアント登録リクエストの残りのカウントです。このリミットに達した場合はリスタートする必要があります。
+reset-remaining-count=残りのカウントをリセット
+
+
+client-templates=クライアントテンプレート
+client-templates.tooltip=クライアントテンプレートでは、複数のクライアントで共有する共通設定を定義することができます。
+
+groups=グループ
+
+group.add-selected.tooltip=グループにアサイン可能なレルムロールです。
+group.assigned-roles.tooltip=グループにマッピングされたレルムロールです。
+group.effective-roles.tooltip=マッピングされているすべてのレルムロールです。複合ロールより引き継いでいるロールも含みます。
+group.available-roles.tooltip=このクライアントよりアサイン可能なロールです。
+group.assigned-roles-client.tooltip=マッピングされたこのクライアントのロールです。
+group.effective-roles-client.tooltip=マッピングされたこのクライアントのロールです。複合ロールより引き継いでいるロールも含みます。
+
+default-roles=デフォルトロール
+no-realm-roles-available=使用可能なレルムロールはありません
+
+users=ユーザー
+user.add-selected.tooltip=ユーザーにアサイン可能なレルムロールです。
+user.assigned-roles.tooltip=ユーザーにマッピングされたレルムロールです。
+user.effective-roles.tooltip=マッピングされているすべてのレルムロールです。複合ロールより引き継いでいるロールも含みます。
+user.available-roles.tooltip=このクライアントよりアサイン可能なロールです。
+user.assigned-roles-client.tooltip=マッピングされたこのクライアントのロールです。
+user.effective-roles-client.tooltip=マッピングされたこのクライアントのロールです。複合ロールより引き継いでいるロールも含みます。
+default.available-roles.tooltip=アサイン可能なレルムレベルのロールです。
+realm-default-roles=レルムのデフォルトロール
+realm-default-roles.tooltip=ユーザーにアサインされたレルムレベルのロールです。
+default.available-roles-client.tooltip=デフォルトでアサイン可能なこのクライアントのロールです。
+client-default-roles=クライアントのデフォルトロール
+client-default-roles.tooltip=デフォルトロールとしてアサインされたこのクライアントのロールです。
+composite.available-roles.tooltip=この複合ロールに関連付け可能なレルムレベルのロールです。
+composite.associated-roles.tooltip=この複合ロールに関連付けされているレルムレベルのロールです。
+composite.available-roles-client.tooltip=この複合ロールに関連付け可能なこのクライアントのロールです。
+composite.associated-roles-client.tooltip=この複合ロールに関連付けされているクライアントロールです。
+partial-import=部分インポート
+partial-import.tooltip=部分インポートでは、以前にエクスポートした JSON ファイルよりユーザー、クライアント、およびその他のリソースをインポートすることができます。
+
+file=ファイル
+exported-json-file=エクスポートされた JSON ファイル
+import-from-realm=レルムからインポート
+import-users=ユーザーをインポート
+import-groups=グループをインポート
+import-clients=クライアントをインポート
+import-identity-providers=アイデンティティ プロバイダーをインポート
+import-realm-roles=レルムロールをインポート
+import-client-roles=クライアントロールをインポート
+if-resource-exists=リソースが存在する場合
+fail=失敗
+skip=スキップ
+overwrite=上書き
+if-resource-exists.tooltip=既に存在するリソースをインポートしようとした場合にどうすべきかを指定します。
+
+action=アクション
+role-selector=ロールの選択
+realm-roles.tooltip=選択可能なレルムロールです。
+
+select-a-role=ロールを選択してください
+select-realm-role=レルムロールを選択
+client-roles.tooltip=選択可能なクライアントロールです。
+select-client-role=クライアントロールを選択
+
+client-template=クライアントテンプレート
+client-template.tooltip=設定を引き継ぐクライアントテンプレートを選択します。
+client-saml-endpoint=クライアント SAML エンドポイント
+add-client-template=クライアントテンプレートを追加
+
+manage=管理
+authentication=認証
+user-federation=ユーザーフェデレーション
+user-storage=ユーザーストレージ
+events=イベント
+realm-settings=レルムの設定
+configure=設定
+select-realm=レルムの選択
+add=追加
+
+client-template.name.tooltip=クライアントテンプレートの名前です。レルム内でユニークにする必要があります。
+client-template.description.tooltip=クライアントテンプレートの説明です。
+client-template.protocol.tooltip=このクライアントテンプレートにより、どの SSO プロトコルが提供されるか設定します。
+
+add-user-federation-provider=ユーザー フェデレーション プロバイダーの追加
+add-user-storage-provider=ユーザー ストレージ プロバイダーの追加
+required-settings=必要な設定
+provider-id=プロバイダー ID
+console-display-name=コンソール表示名
+console-display-name.tooltip=管理コンソール内でのリンク表示名を設定します。
+priority=優先度
+priority.tooltip=ユーザーを検索する際のプロバイダーの優先度を設定します。低い順となります。
+sync-settings=同期の設定
+periodic-full-sync=定期的なフル同期
+periodic-full-sync.tooltip=プロバイダーユーザーの Keycloak への定期的なフル同期を有効または無効とすべきかを設定します。
+full-sync-period=フル同期の周期
+full-sync-period.tooltip=フル同期の周期を秒で設定します。
+periodic-changed-users-sync=定期的な変更ユーザーの同期
+periodic-changed-users-sync.tooltip=変更または新規作成されたプロバイダーユーザーの Keycloak への定期的な同期を有効または無効とすべきか設定します。
+changed-users-sync-period=変更ユーザーの同期周期
+changed-users-sync-period.tooltip=変更または新規作成されたプロバイダーユーザーの同期周期を秒で設定します。
+synchronize-changed-users=変更ユーザーを同期
+synchronize-all-users=すべてのユーザーを同期
+kerberos-realm=Kerberos レルム
+kerberos-realm.tooltip=Kerberos レルムの名前を設定します。例えば、 FOO.ORG です。
+server-principal=サーバープリンシパル
+server-principal.tooltip=サーバー、ドメイン名を含む HTTP サービスのサービスプリンシパルのフルネームを設定します。例えば、 HTTP/host.foo.org@FOO.ORG です。
+keytab=KeyTab
+keytab.tooltip=サーバープリンシパルのクレデンシャルを含む Kerberos の KeyTab ファイルを設定します。例えば、/etc/krb5.keytab です。
+debug=デバッグ
+debug.tooltip=Krb5LoginModule の標準出力へのデバッグロギングの有効/無効を設定します。
+allow-password-authentication=パスワード認証を許可
+allow-password-authentication.tooltip=Kerberos データベースに対するユーザー名/パスワード認証の有効/無効を設定します。
+edit-mode=編集モード
+edit-mode.tooltip=READ_ONLY は、パスワード更新は許可されずユーザーは常に Kerberos のパスワードで認証することを意味します。UNSYNCED は、ユーザーは Keycloak データベース内の自分のパスワードを変更可能であり、Kerberos のパスワードの代わりに使用されることを意味します。
+ldap.edit-mode.tooltip=READ_ONLY は、読み取り専用の LDAP ストアです。WRITABLE は、データがオンデマンドで LDAP に書き戻しされることを意味します。UNSYNCEDは、ユーザーデータはインポートされますが、LDAP に書き戻しはされないことを意味します。
+update-profile-first-login=初回ログイン時にプロフィールを更新
+update-profile-first-login.tooltip=初回ログイン時のプロフィール更新の有効/無効を設定します。
+sync-registrations=登録の同期
+ldap.sync-registrations.tooltip=LDAP ストア内に新規作成ユーザーを作成すべきかどうかを設定します。どのプロバイダーが新しいユーザーの同期先に選択されるかは、優先度が影響します。
+vendor=ベンダー
+ldap.vendor.tooltip=LDAP ベンダー (プロバイダー)
+username-ldap-attribute=ユーザー名の LDAP 属性
+ldap-attribute-name-for-username=ユーザー名の LDAP 属性名
+username-ldap-attribute.tooltip=Keycloak ユーザー名にマッピングされる LDAP 属性名を設定します。多くの LDAP サーバーベンダーでは 「uid」 となります。Active Directory では 「sAMAccountName」 または 「cn」 となります。LDAP から Keycloak にインポートするすべての LDAP ユーザーのレコードで、属性は入力されているはずです。
+rdn-ldap-attribute=RDN LDAP 属性
+ldap-attribute-name-for-user-rdn=ユーザー RDN の LDAP 属性名
+rdn-ldap-attribute.tooltip=一般的なユーザー DN の RDN (top 属性) として使用される LDAP 属性名を設定します。通常は、ユーザー名の LDAP 属性 と同じですが、必須ではありません。例えば Active Directory では、ユーザー名が 「sAMAccountName」 だと RDN 属性として 「cn」 を使用するのが一般的です。
+uuid-ldap-attribute=UUID LDAP 属性
+ldap-attribute-name-for-uuid=UUID の LDAP 属性名
+uuid-ldap-attribute.tooltip=LDAP 内でオブジェクトのユニークなオブジェクト識別子 (UUID) として使用される LDAP 属性名を設定します。多くの LDAP サーバーベンダーでは 「entryUUID」 となりますが、異なる場合もあります。例えば Active Directory では、 「objectGUID」 となります。お使いの LDAP サーバーが UUID をサポートしていない場合は、ツリー内の LDAP ユーザーの中でユニークとなる他の属性を使用することができます。例えば、 「uid」 や 「entryDN」 です。
+user-object-classes=ユーザーオブジェクトクラス
+ldap-user-object-classes.placeholder=LDAP のユーザーオブジェクトクラス (カンマ区切り)
+
+ldap-connection-url=LDAP 接続 URL
+ldap-users-dn=LDAP ユーザー DN
+ldap-bind-dn=LDAP Bind DN
+ldap-bind-credentials=LDAP Bind のクレデンシャル
+ldap-filter=LDAP フィルター
+ldap.user-object-classes.tooltip=LDAP ユーザー用の すべての LDAP オブジェクトクラスをカンマ区切りで設定します。例: 「inetOrgPerson, organizationalPerson」 。新規作成された Keycloak ユーザーは、これらすべてのオブジェクトクラスを使用して LDAP に書き込まれます。また、既存の LDAP ユーザーのレコードは、これらすべてのオブジェクトクラスを含む場合だけ発見されます。
+
+connection-url=接続 URL
+ldap.connection-url.tooltip=お使いの LDAP サーバーへの接続 URL
+test-connection=接続テスト
+users-dn=ユーザー DN
+ldap.users-dn.tooltip=ユーザーがいる LDAP ツリーの完全 DN を設定します。この DN は LDAP ユーザーの親になります。例えば、典型的なユーザーは 「uid=john,ou=users,dc=example,dc=com」 「ou=users,dc=example,dc=com」 のような DN となりますが、この場合は 「ou=users,dc=example,dc=com」 となります。
+authentication-type=認証タイプ
+ldap.authentication-type.tooltip=LDAP 認証タイプを設定します。今のところ、 「none」 (Anonymous LDAP 認証) または 「simple」 (クレデンシャルによる Bind + パスワード認証による Bind) メカニズムが用意されています。
+bind-dn=Bind DN
+ldap.bind-dn.tooltip=Keycloak が LDAP サーバーにアクセスするために使用する LDAP 管理者の DN を設定します。
+bind-credential=Bind のクレデンシャル
+ldap.bind-credential.tooltip=LDAP 管理者のパスワードを設定します。
+test-authentication=認証テスト
+custom-user-ldap-filter=カスタムユーザー LDAP フィルター
+ldap.custom-user-ldap-filter.tooltip=ユーザー検索のフィルタリングを行う LDAP フィルターを設定します。追加のフィルターが必要ない場合は空のままにしてください。設定は、 「(」 から始まり 「)」 で終わることを確認してください。
+search-scope=検索スコープ
+ldap.search-scope.tooltip=One Level では、ユーザー DN で指定された DN 内のユーザーのみを検索します。subtree では、サブツリー全体を検索します。より詳細については LDAP のドキュメントを参照してください。
+use-truststore-spi=トラストストア SPI を使用
+ldap.use-truststore-spi.tooltip=LDAP 接続で、keycloak-server.json で設定されたトラストストアの トラストストア SPI を使用するかどうかを指定します。 「Always」 は常に使用することを意味します。 「Never」 は使用しないことを意味します。 「Only for ldaps」 は、接続 URL が ldaps の場合に使用することを意味します。keycloak-server.json で設定されていない場合でも、デフォルトの Java CA 証明書 (cacerts) や 「javax.net.ssl.trustStore」 プロパティで指定された証明書が使用される点に注意してください。
+connection-pooling=接続プーリング
+ldap.connection-pooling.tooltip=Keycloak は LDAP サーバーへのアクセスで接続プールを使用するかどうかを設定します。
+ldap.pagination.tooltip=LDAP サーバーはページネーションをサポートするかどうかを設定します。
+kerberos-integration=Kerberos と統合
+allow-kerberos-authentication=Kerberos 認証を許可
+ldap.allow-kerberos-authentication.tooltip=SPNEGO/Kerberos のトークンを持つユーザーの HTTP 認証を有効/無効にします。認証されたユーザーに関するデータはこの LDAP サーバーよりプロビジョニングされます。
+use-kerberos-for-password-authentication=パスワード認証に Kerberos を使用
+ldap.use-kerberos-for-password-authentication.tooltip=LDAP サーバーに対してディレクトリサービスの API で認証する代わりに、Kerberos に対して ユーザー名/パスワード認証する Kerberos ログインモジュールを使用します。
+batch-size=バッチサイズ
+ldap.batch-size.tooltip=1トランザクションで LDAP から Keycloak にインポートされる LDAP ユーザー数を設定します。
+ldap.periodic-full-sync.tooltip=Keycloak への LDAP ユーザーの定期的なフル同期を有効/無効にします。
+ldap.periodic-changed-users-sync.tooltip=Keycloak への 変更または新規作成された LDAP ユーザーの定期的な同期を有効/無効にします。
+ldap.changed-users-sync-period.tooltip=変更または新規作成された LDAP ユーザーの同期周期を秒で設定します。
+user-federation-mappers=ユーザーフェデレーションのマッパー
+create-user-federation-mapper=ユーザーフェデレーション マッパーの作成
+add-user-federation-mapper=ユーザーフェデレーション マッパーの追加
+provider-name=プロバイダー名
+no-user-federation-providers-configured=設定されているユーザーフェデレーション プロバイダーはありません
+no-user-storage-providers-configured=設定されているユーザーストレージ プロバイダーはありません
+add-identity-provider=アイデンティティ プロバイダーの登録
+add-identity-provider-link=アイデンティティ プロバイダーのリンク登録
+identity-provider=アイデンティティ プロバイダー
+identity-provider-user-id=アイデンティティ プロバイダーのユーザー ID
+identity-provider-user-id.tooltip=アイデンティティ プロバイダー側のユーザーのユニーク ID です。
+identity-provider-username=アイデンティティ プロバイダーのユーザー名
+identity-provider-username.tooltip=アイデンティティ プロバイダー側のユーザー名です。
+pagination=ページネーション
+
+browser-flow=ブラウザーフロー
+browser-flow.tooltip=ブラウザー認証で使用したいフローを選択してください。
+registration-flow=登録フロー
+registration-flow.tooltip=登録で使用したいフローを選択してください。
+direct-grant-flow=ダイレクトグラントフロー
+direct-grant-flow.tooltip=ダイレクトグラント認証で使用したいフローを選択してください。
+reset-credentials=クレデンシャルのリセット
+reset-credentials.tooltip=ユーザーがクレデンシャルを忘れた際に使用したいフローを選択してください。
+client-authentication=クライアント認証
+client-authentication.tooltip=クライアント認証で使用したいフローを選択してください。
+new=新規作成
+copy=コピー
+add-execution=Execution を追加
+add-flow=フローを追加
+auth-type=認証タイプ
+requirement=必要条件
+config=設定
+no-executions-available=使用可能な Execution がありません
+authentication-flows=認証フロー
+create-authenticator-config=認証設定の作成
+authenticator.alias.tooltip=この設定の名前を設定します。
+otp-type=OTP タイプ
+time-based=タイムベース
+counter-based=カウンターベース
+otp-type.tooltip=TOTP はタイムベースのワンタイムパスワードです。 「hotp」 は、サーバーでハッシュに対してカウンターを保持するカウンターベースのワンタイムパスワードです。
+otp-hash-algorithm=OTP ハッシュアルゴリズム
+otp-hash-algorithm.tooltip=OTP を生成するのにどのハッシュアルゴリズムを使用するか設定します。
+number-of-digits=桁数
+otp.number-of-digits.tooltip=OTP の桁数を設定します。
+look-ahead-window=先読みウィンドウ
+otp.look-ahead-window.tooltip=トークンジェネレーターとサーバーが時刻同期またはカウンター同期していないことを考慮してどれくらい先読みを行うか設定します。
+initial-counter=初期カウンター
+otp.initial-counter.tooltip=初期カウンターの値は何とするか設定します。
+otp-token-period=OTP トークンの期間
+otp-token-period.tooltip=OTP トークンは何秒有効とするか設定します。デフォルトは30秒です。
+table-of-password-policies=パスワードポリシーの一覧表
+add-policy.placeholder=ポリシーを追加...
+policy-type=ポリシーのタイプ
+policy-value=ポリシーの値
+admin-events=管理イベント
+admin-events.tooltip=保存されたレルムの管理イベントを表示します。管理イベントは、アカウント管理に関連したイベント、例えばレルムの作成などです。イベントの保存を有効にするには設定へ移動してください。
+login-events=ログインイベント
+filter=フィルター
+update=更新
+reset=リセット
+operation-types=操作タイプ
+resource-types=リソースタイプ
+select-operations.placeholder=操作を選択...
+select-resource-types.placeholder=リソースタイプを選択...
+resource-path=リソースパス
+resource-path.tooltip=リソースパスでフィルターします。ワイルドカード 「*」 はパスの単一部分と一致し、 「**」 は複数部分と一致します。例えば、 「realms/*/clients/asbc」 はすべてのレルム内の ID asbc を持つクライアントとマッチし、 「realms/master/**」 は master レルム内のすべてにマッチします。
+date-(from)=日付 (From)
+date-(to)=日付 (To)
+authentication-details=認証の詳細
+ip-address=IP アドレス
+time=日時
+operation-type=操作タイプ
+resource-type=リソースタイプ
+auth=認証
+representation=Representation
+register=登録
+required-action=Required アクション
+default-action=デフォルトアクション
+auth.default-action.tooltip=有効の場合は、新規ユーザーにはこの Required アクションがアサインされます。
+no-required-actions-configured=設定された Required アクションはありません
+defaults-to-id=ID がデフォルトになります
+flows=フロー
+bindings=バインディング
+required-actions=Required アクション
+password-policy=パスワードポリシー
+otp-policy=OTP ポリシー
+user-groups=ユーザーグループ
+default-groups=デフォルトグループ
+groups.default-groups.tooltip=新規ユーザーが自動的に参加するグループのセットを設定します。
+cut=カット
+paste=ペースト
+
+create-group=グループの作成
+create-authenticator-execution=認証 Execution の作成
+create-form-action-execution=フォームアクション Execution の作成
+create-top-level-form=トップレベルフォームの作成
+flow.alias.tooltip=フローの表示名を指定します。
+top-level-flow-type=トップレベルフロータイプ
+flow.generic=generic
+flow.client=client
+top-level-flow-type.tooltip=どの種類のトップレベルフローを作成しますか? 「client」 タイプは、クライアント (アプリケーション) の認証で使用します。 「generic」 はユーザーと他のすべてで使用します。
+create-execution-flow=Execution フローの作成
+flow-type=フロータイプ
+flow.form.type=form
+flow.generic.type=generic
+flow-type.tooltip=どの種類のフォームかを設定します。
+form-provider=フォームプロバイダー
+default-groups.tooltip=新規作成または登録されたユーザーは自動的にこれらのグループに追加されます。
+select-a-type.placeholder=タイプを選択してください
+available-groups=使用可能なグループ
+available-groups.tooltip=デフォルトとして追加したいグループを選択してください。
+value=値
+table-of-group-members=グループメンバーの一覧表
+last-name=姓
+first-name=名
+email=Eメール
+toggle-navigation=ナビゲーションの切り替え
+manage-account=アカウントの管理
+sign-out=サインアウト
+server-info=サーバー情報
+resource-not-found=リソースが<strong>見つかりません</strong>...
+resource-not-found.instruction=お探しのリソースが見つかりませんでした。入力された URL が正しいかご確認ください。
+go-to-the-home-page=ホームページへ移動 &raquo;
+page-not-found=ページが<strong>見つかりません</strong>...
+page-not-found.instruction=お探しのページが見つかりませんでした。入力された URL が正しいかご確認ください。
+events.tooltip=保存されたレルムのイベントを表示します。イベントは、ユーザーアカウントに関連したイベント、例えばログインなどです。イベントの保存を有効にするには設定へ移動してください。
+select-event-types.placeholder=イベントタイプを選択...
+events-config.tooltip=ユーザーと管理イベントの保存を有効にする設定オプションを表示します。
+select-an-action.placeholder=アクションを選択...
+event-listeners.tooltip=どのリスナーがレルムのイベントを受け取るか設定します。
+login.save-events.tooltip=有効の場合は、ログインイベントがデータベースに保存され、管理コンソールとアカウント管理で使用することができます。
+clear-events.tooltip=データベース内のすべてのイベントを削除します。
+events.expiration.tooltip=イベントの有効期限を設定します。期限切れのイベントはデータベースから定期的に削除されます。
+admin-events-settings=管理イベントの設定
+save-events=イベントの保存
+admin.save-events.tooltip=有効の場合は、管理イベントがデータベースに保存され、管理コンソールで使用可能になります。
+saved-types.tooltip=どのイベントタイプが保存されるかを設定します。
+include-representation=Representation を含める
+include-representation.tooltip=作成または更新リクエストの JSON Representation を含めるかどうかを設定します。
+clear-admin-events.tooltip=データベース内のすべての管理イベントを削除します。
+server-version=サーバーのバージョン
+info=情報
+providers=プロバイダー
+server-time=サーバーの時刻
+server-uptime=サーバーの稼働時間
+memory=メモリー
+total-memory=メモリーの総容量
+free-memory=空きメモリー
+used-memory=使用メモリー
+system=システム
+current-working-directory=現在の作業ディレクトリ
+java-version=Java バージョン
+java-vendor=Java ベンダー
+java-runtime=Java ランタイム
+java-vm=Java VM
+java-vm-version=Java VM バージョン
+java-home=Java ホーム
+user-name=ユーザー名
+user-timezone=ユーザータイムゾーン
+user-locale=ユーザーロケール
+system-encoding=システムエンコーディング
+operating-system=オペレーションシステム
+os-architecture=OS アーキテクチャ
+spi=SPI
+granted-roles=許可されたロール
+granted-protocol-mappers=許可されたプロトコルマッパー
+additional-grants=追加の許可
+revoke=取り消し
+new-password=新しいパスワード
+password-confirmation=新しいパスワード (確認)
+reset-password=パスワードをリセット
+credentials.temporary.tooltip=有効の場合は、ユーザーは次のログイン時にパスワードの変更が必要となります。
+remove-totp=TOTP の削除
+credentials.remove-totp.tooltip=ユーザーのワンタイムパスワードジェネレーターを削除します。
+reset-actions=リセットアクション
+credentials.reset-actions.tooltip=ユーザーにリセットアクションEメールを送信する際に実行するアクションを設定します。 「Verify email」 は、メールアドレスの確認のためにユーザーにEメールを送信します。 「Update profile」 は、新しい個人情報の入力を必要とします。 「Update password」 は、新しいパスワードの入力を必要とします。 「Configure TOTP」 は、モバイルのパスワードジェネレーターのセットアップを必要とします。
+reset-actions-email=リセットアクションEメール
+send-email=Eメールを送信
+credentials.reset-actions-email.tooltip=リンクを記載したEメールをユーザーに送信します。リンクをクリックすることで、ユーザーはリセットアクションを実行できます。ユーザーはリセットの前にログインする必要はありません。例えば、 「Update Password」 アクションを設定してこのボタンをクリックすると、ユーザーはログインなしにパスワードの変更が可能になります。
+add-user=ユーザーの追加
+created-at=作成日
+user-enabled=ユーザーの有効
+user-enabled.tooltip=無効ユーザーはログインすることができません。
+user-temporarily-locked=ユーザーの一時的なロック
+user-temporarily-locked.tooltip=ユーザーは何度もログインに失敗してロックされている可能性があります。
+unlock-user=ユーザーをアンロック
+federation-link=フェデレーション リンク
+email-verified=確認済みのEメール
+email-verified.tooltip=ユーザーのEメールは確認済みかどうか設定します。
+required-user-actions=必要なユーザーアクション
+required-user-actions.tooltip=ユーザーがログイン時に必要となるアクションを設定します。 「Verify email」 は、メールアドレスの確認のためにユーザーにEメールを送信します。 「Update profile」 は、新しい個人情報の入力を必要とします。 「Update password」 は、新しいパスワードの入力を必要とします。 「Configure TOTP」 は、モバイルのパスワードジェネレーターのセットアップを必要とします。
+locale=ロケール
+select-one.placeholder=1つ選択...
+impersonate=代理ログイン
+impersonate-user=ユーザーの代理
+impersonate-user.tooltip=このユーザーとしてログインします。同じレルム内のユーザーの場合は、このユーザーでログインする前に、現在のログインセッションがログアウトされます。
+identity-provider-alias=アイデンティティ プロバイダーのエイリアス
+provider-user-id=プロバイダーのユーザー ID
+provider-username=プロバイダーのユーザー名
+no-identity-provider-links-available=使用可能なアイデンティティ プロバイダーのリンクはありません
+group-membership=グループメンバーシップ
+leave=外す
+group-membership.tooltip=メンバーであるグループです。グループから外すには、リストのグループを選択して 「外す」 ボタンをクリックしてください。
+membership.available-groups.tooltip=ユーザーが参加可能なグループです。グループを選択して 「参加」 ボタンをクリックしてください。
+table-of-realm-users=レルムユーザーの一覧表
+view-all-users=すべてのユーザーを参照
+unlock-users=ユーザーのアンロック
+no-users-available=使用可能なユーザーはおりません
+users.instruction=検索を入力するか、 「すべてのユーザーを参照」 をクリックしてください
+consents=同意
+started=開始
+logout-all-sessions=すべてのセッションをログアウト
+logout=ログアウト
+new-name=新しい名前
+ok=OK
+attributes=属性
+role-mappings=ロールマッピング
+members=メンバー
+details=詳細
+identity-provider-links=アイデンティティ プロバイダーのリンク
+register-required-action=Required アクションの登録
+gender=性別
+address=住所
+phone=電話番号
+profile-url=プロフィール URL
+picture-url=画像 URL
+website=Web サイト
+import-keys-and-cert=鍵と証明書をインポート
+import-keys-and-cert.tooltip=クライアントの鍵ペアと証明書をアップロードします。
+upload-keys=鍵をアップロード
+download-keys-and-cert=鍵と証明書をダウンロード
+no-value-assigned.placeholder=アサイン済みの値はありません
+remove=削除
+no-group-members=グループメンバーはおりません
+temporary=一時的
+join=参加
+event-type=イベントタイプ
+events-config=イベント設定
+event-listeners=イベントリスナー
+login-events-settings=ログインイベントの設定
+clear-events=イベントのクリア
+saved-types=保存タイプ
+clear-admin-events=管理イベントのクリア
+clear-changes=変更をクリア
+error=エラー
+
+# Authz
+# Authz Common
+authz-authorization=認可
+authz-owner=オーナー
+authz-uri=URI
+authz-scopes=スコープ
+authz-resource=リソース
+authz-resource-type=リソースタイプ
+authz-resources=リソース
+authz-scope=スコープ
+authz-authz-scopes=認可スコープ
+authz-policies=ポリシー
+authz-permissions=アクセス権
+authz-evaluate=評価
+authz-icon-uri=アイコン URI
+authz-icon-uri.tooltip=アイコンを指す URI を設定します。
+authz-select-scope=スコープを選択
+authz-select-resource=リソースを選択
+authz-associated-policies=関連ポリシー
+authz-any-resource=任意のリソース
+authz-any-scope=任意のスコープ
+authz-any-role=任意のロール
+authz-policy-evaluation=ポリシー評価
+authz-select-client=クライアントを選択
+authz-select-user=ユーザーを選択
+authz-entitlements=エンタイトルメント
+authz-no-resources=リソースはありません
+authz-result=結果
+authz-authorization-services-enabled=認可の有効
+authz-authorization-services-enabled.tooltip=きめ細かい認可のサポートを有効/無効にします。
+authz-required=必須
+
+# Authz Settings
+authz-import-config.tooltip=リソースサーバーの認可設定を含む JSON ファイルをインポートします。
+
+authz-policy-enforcement-mode=ポリシー施行モード
+authz-policy-enforcement-mode.tooltip=ポリシー施行モードは、認可リクエストを評価する際に適用される方法を決定します。 「Enforcing」 は、与えられたリソースに関連するポリシーが存在しない場合でも、リクエストはデフォルトで拒否されることを意味します。 「Permissive」 は、与えられたリソースに関連するポリシーが存在しない場合でも、リクエストは許可されることを意味します。 「Disabled」 は、完全にポリシーの評価を無効にし、任意のリソースへのアクセスを許可します。
+authz-policy-enforcement-mode-enforcing=Enforcing
+authz-policy-enforcement-mode-permissive=Permissive
+authz-policy-enforcement-mode-disabled=Disabled
+
+authz-remote-resource-management=リモートリソース管理
+authz-remote-resource-management.tooltip=リソースは、リソースサーバーによりリモートで管理すべきかどうかを設定します。 オフの場合は、リソースはこの管理コンソールだけで管理されます。
+
+authz-export-settings=エクスポート設定
+authz-export-settings.tooltip=このリソースサーバーのすべての認可設定をエクスポートしダウンロードします。
+
+# Authz Resource List
+authz-no-resources-available=使用可能なリソースはありません。
+authz-no-scopes-assigned=アサイン済みのスコープはありません。
+authz-no-type-defined=定義されたタイプはありません。
+authz-no-permission-assigned=アサイン済みのアクセス権はありません。
+authz-no-policy-assigned=アサイン済みのポリシーはありません。
+authz-create-permission=アクセス権を作成
+
+# Authz Resource Detail
+authz-add-resource=リソースの追加
+authz-resource-name.tooltip=このリソースのユニークな名前を設定します。名前はリソースの一意な識別に使用され、特定のリソースを照会する際に使用することができます。
+authz-resource-owner.tooltip=このリソースのオーナーです。
+authz-resource-type.tooltip=このリソースのタイプを設定します。異なるリソースインスタンスを同じタイプにグルーピングすることができます。
+authz-resource-uri.tooltip=このリソースを一意に識別する URI を設定します。
+authz-resource-scopes.tooltip=このリソースに関連付けるスコープを設定します。
+
+# Authz Scope List
+authz-add-scope=スコープの追加
+authz-no-scopes-available=使用可能なスコープはありません。
+
+# Authz Scope Detail
+authz-scope-name.tooltip=このスコープのユニークな名前を設定します。名前はスコープの一意な識別に使用され、特定のスコープを照会する際に使用することができます。
+
+# Authz Policy List
+authz-all-types=すべてのタイプ
+authz-create-policy=ポリシーを作成
+authz-no-policies-available=使用可能なポリシーはありません。
+
+# Authz Policy Detail
+authz-policy-name.tooltip=このポリシーの名前を設定します。
+authz-policy-description.tooltip=このポリシーの説明を設定します。
+authz-policy-logic=ロジック
+authz-policy-logic-positive=Positive
+authz-policy-logic-negative=Negative
+authz-policy-logic.tooltip=ロジックは、ポリシーの判定方法を決定します。 「Positive」 の場合は、このポリシーの評価中に得られた結果 (許可または拒否) が判定の実行に使用されます。 「Negative」 の場合は、結果は反転されます。言い換えれば、許可は拒否とその反対になります。
+authz-policy-apply-policy=ポリシーの適用
+authz-policy-apply-policy.tooltip=このポリシーやアクセス権で定義されたスコープに適用するすべてのポリシーを設定します。
+authz-policy-decision-strategy=判定戦略
+authz-policy-decision-strategy.tooltip=判定戦略は、ポリシーの評価方法と最終的な判定方法を決定します。 「Affirmative」 は、全体の判定が positive となるためには、少なくとも1つのポリシーが positive と評価する必要がある、ということを意味します。 「Unanimous」 は、全体の判定が positive となるためには、すべてのポリシーが positive と評価する必要がある、ということを意味します。 「Consensus」 は、positive の数が nagative の数より多くなければならないことを意味します。positive と negative の数が同じ場合は、最終的な判定は negative になります。
+authz-policy-decision-strategy-affirmative=Affirmative
+authz-policy-decision-strategy-unanimous=Unanimous
+authz-policy-decision-strategy-consensus=Consensus
+authz-select-a-policy=ポリシーを選択
+
+# Authz Role Policy Detail
+authz-add-role-policy=ロールポリシーの追加
+authz-no-roles-assigned=アサイン済みのロールはありません。
+authz-policy-role-realm-roles.tooltip=このポリシーで許可されるレルムロールを指定してください。
+authz-policy-role-clients.tooltip=このポリシーに適用されるクライアントロールをフィルタリングするために、クライアントを選択してください。
+authz-policy-role-client-roles.tooltip=このポリシーで許可されるクライアントロールを指定してください。
+
+# Authz User Policy Detail
+authz-add-user-policy=ユーザーポリシーの追加
+authz-no-users-assigned=アサイン済みのユーザーはおりません。
+authz-policy-user-users.tooltip=どのユーザーがこのポリシーで許可されるか指定してください。
+
+# Authz Time Policy Detail
+authz-add-time-policy=タイムポリシーの追加
+authz-policy-time-not-before.tooltip=ポリシーを許可しない日時を定義します。現在日時がこの値より後か、等しい場合にのみ許可されます。
+authz-policy-time-not-on-after=この日時より後
+authz-policy-time-not-on-after.tooltip=ポリシーを許可しない日時を定義します。現在日時がこの値より前か、等しい場合にのみ許可されます。
+
+# Authz Drools Policy Detail
+authz-add-drools-policy=Drools ポリシーの追加
+authz-policy-drools-maven-artifact-resolve=解決
+authz-policy-drools-maven-artifact=ポリシー Maven アーティファクト
+authz-policy-drools-maven-artifact.tooltip=ルールの読み込む先となるアーティファクトを示す Maven GAV を設定します。GAV を提供し 「解決」 をクリックすることで、 「モジュール」 と 「セッション」 フィールドを読み込みます。
+authz-policy-drools-module=モジュール
+authz-policy-drools-module.tooltip=このポリシーで使用されるモジュールです。ルールの読み込み先から特定のセッションを選択するにはモジュールを提供する必要があります。
+authz-policy-drools-session=セッション
+authz-policy-drools-session.tooltip=このポリシーで使用されるセッションです。セッションは、ポリシーを処理する際に評価するすべてのルールを提供します。
+authz-policy-drools-update-period=更新周期
+authz-policy-drools-update-period.tooltip=アーティファクトの更新をスキャンする間隔を指定します。
+
+# Authz JS Policy Detail
+authz-add-js-policy=JavaScript ポリシーの追加
+authz-policy-js-code=コード
+authz-policy-js-code.tooltip=このポリシーの条件を提供する JavaScript コードを設定します。
+
+
+# Authz Aggregated Policy Detail
+authz-aggregated=集約
+authz-add-aggregated-policy=集約ポリシーの追加
+
+# Authz Permission List
+authz-no-permissions-available=使用可能なアクセス権はありません。
+
+# Authz Permission Detail
+authz-permission-name.tooltip=このアクセス権の名前を設定します。
+authz-permission-description.tooltip=このアクセス権の説明を設定します。
+
+# Authz Resource Permission Detail
+authz-add-resource-permission=リソースアクセス権の追加
+authz-permission-resource-apply-to-resource-type=リソースタイプに適用
+authz-permission-resource-apply-to-resource-type.tooltip=このアクセス権が、特定タイプの全リソースに適用されるかどうかを指定します。この場合、アクセス権は特定リソースタイプの全インスタンスに対して評価されます。
+authz-permission-resource-resource.tooltip=このアクセス権が適用されるリソースインスタンスを指定します。
+authz-permission-resource-type.tooltip=このアクセス権が適用されるリソースタイプを指定します。
+
+# Authz Scope Permission Detail
+authz-add-scope-permission=スコープアクセス権の追加
+authz-permission-scope-resource.tooltip=選択されたリソースに関連するスコープに制限します。選択されていない場合は、すべてのスコープが使用可能になります。
+authz-permission-scope-scope.tooltip=このアクセス権は1つまたは複数のスコープに適用されるように指定してください。
+
+# Authz Evaluation
+authz-evaluation-identity-information=アイデンティティ情報
+authz-evaluation-identity-information.tooltip=ポリシーの評価の際に使用されるアイデンティティ情報の設定オプションです。
+authz-evaluation-client.tooltip=認可リクエストを作成するクライアントを選択してください。提供されない場合は、認可リクエストは今いるページのクライアントで行われることになります。
+authz-evaluation-user.tooltip=アクセス権を照会するために使用するユーザーのアイデンティティを選択してください。
+authz-evaluation-role.tooltip=選択されたユーザーに関連付けたいロールを選択してください。
+authz-evaluation-new=新規に評価
+authz-evaluation-re-evaluate=再評価
+authz-evaluation-previous=前の評価
+authz-evaluation-contextual-info=コンテキスト情報
+authz-evaluation-contextual-info.tooltip=ポリシーの評価の際に使用されるコンテキスト情報の設定オプションです。
+authz-evaluation-contextual-attributes=コンテキスト属性
+authz-evaluation-contextual-attributes.tooltip=実行環境や実行コンテキストによって提供される任意の属性を設定します。
+authz-evaluation-permissions.tooltip=ポリシーが適用されるようにアクセス権を設定するオプションです。
+authz-evaluation-evaluate=評価
+authz-evaluation-any-resource-with-scopes=スコープを持つ任意のリソース
+authz-evaluation-no-result=認可リクエストから結果を得ることができませんでした。提供されたリソースまたはスコープが、ポリシーと関連付けられているかどうかを確認してください。
+authz-evaluation-no-policies-resource=このリソースのポリシーが見つかりませんでした。
+authz-evaluation-result.tooltip=このアクセス権のリクエストの全体的な結果です。
+authz-evaluation-scopes.tooltip=許可されたスコープリストです。
+authz-evaluation-policies.tooltip=どのポリシーが評価され判定されたか詳細を表示しています。
+authz-evaluation-authorization-data=レスポンス
+authz-evaluation-authorization-data.tooltip=認可リクエストの処理の結果として送信された認可データのトークンを表示します。これは、許可を求めたクライアントに対して Keycloak が発行する基本的なものです。現在の認可リクエストで付与されたアクセス権については 「authorization」 クレームを確認してください。
+authz-show-authorization-data=認可データを表示
diff --git a/themes/src/main/resources/theme/base/admin/messages/messages_ja.properties b/themes/src/main/resources/theme/base/admin/messages/messages_ja.properties
new file mode 100644
index 0000000..9a8d26d
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/messages/messages_ja.properties
@@ -0,0 +1,15 @@
+# encoding: utf-8
+invalidPasswordMinLengthMessage=無効なパスワード: 最小 {0} の長さが必要です。
+invalidPasswordMinLowerCaseCharsMessage=無効なパスワード: 少なくとも {0} 文字の小文字を含む必要があります。
+invalidPasswordMinDigitsMessage=無効なパスワード: 少なくとも {0} 文字の数字を含む必要があります。
+invalidPasswordMinUpperCaseCharsMessage=無効なパスワード: 少なくとも {0} 文字の大文字を含む必要があります。
+invalidPasswordMinSpecialCharsMessage=無効なパスワード:  少なくとも {0} 文字の特殊文字を含む必要があります。
+invalidPasswordNotUsernameMessage=無効なパスワード: ユーザー名と同じパスワードは禁止されています。
+invalidPasswordRegexPatternMessage=無効なパスワード: 正規表現パターンと一致しません。
+invalidPasswordHistoryMessage=無効なパスワード: 最近の {0} パスワードのいずれかと同じパスワードは禁止されています。
+
+ldapErrorInvalidCustomFilter=LDAP フィルターのカスタム設定が、 「(」 から開始または 「)」 で終了となっていません。
+ldapErrorMissingClientId=レルムロールマッピングを使用しない場合は、クライアント ID は設定内で提供される必要があります。
+ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType=グループ継承と UID メンバーシップタイプを一緒に保存することはできません。
+ldapErrorCantWriteOnlyForReadOnlyLdap=LDAP プロバイダーモードが WRITABLE ではない場合は、write only を設定することはできません。
+ldapErrorCantWriteOnlyAndReadOnly=write-only と read-only を一緒に設定することはできません。
\ No newline at end of file
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 9c67740..72b6c43 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
@@ -1090,7 +1090,36 @@ module.controller('ResourceServerPolicyTimeDetailCtrl', function($scope, $route,
         },
 
         onInitUpdate : function(policy) {
-
+            if (policy.config.dayMonth) {
+                policy.config.dayMonth = parseInt(policy.config.dayMonth);
+            }
+            if (policy.config.dayMonthEnd) {
+                policy.config.dayMonthEnd = parseInt(policy.config.dayMonthEnd);
+            }
+            if (policy.config.month) {
+                policy.config.month = parseInt(policy.config.month);
+            }
+            if (policy.config.monthEnd) {
+                policy.config.monthEnd = parseInt(policy.config.monthEnd);
+            }
+            if (policy.config.year) {
+                policy.config.year = parseInt(policy.config.year);
+            }
+            if (policy.config.yearEnd) {
+                policy.config.yearEnd = parseInt(policy.config.yearEnd);
+            }
+            if (policy.config.hour) {
+                policy.config.hour = parseInt(policy.config.hour);
+            }
+            if (policy.config.hourEnd) {
+                policy.config.hourEnd = parseInt(policy.config.hourEnd);
+            }
+            if (policy.config.minute) {
+                policy.config.minute = parseInt(policy.config.minute);
+            }
+            if (policy.config.minuteEnd) {
+                policy.config.minuteEnd = parseInt(policy.config.minuteEnd);
+            }
         },
 
         onUpdate : function() {
@@ -1106,6 +1135,19 @@ module.controller('ResourceServerPolicyTimeDetailCtrl', function($scope, $route,
 
         }
     }, realm, client, $scope);
+
+    $scope.isRequired = function () {
+        var policy = $scope.policy;
+        if (policy.config.noa || policy.config.nbf
+            || policy.config.dayMonth
+            || policy.config.month
+            || policy.config.year
+            || policy.config.hour
+            || policy.config.minute) {
+            return false;
+        }
+        return true;
+    }
 });
 
 module.controller('ResourceServerPolicyAggregateDetailCtrl', function($scope, $route, $location, realm, PolicyController, ResourceServerPolicy, client) {
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 d1041c7..3bbd471 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
@@ -365,11 +365,12 @@ module.controller('ClientCertificateImportCtrl', function($scope, $location, $ht
     ];
 
     if (callingContext == 'jwt-credentials') {
-        $scope.keyFormats.push('JSON Web Key Set (JWK)');
+        $scope.keyFormats.push('Public Key PEM');
+        $scope.keyFormats.push('JSON Web Key Set');
     }
 
     $scope.hideKeystoreSettings = function() {
-        return $scope.uploadKeyFormat == 'Certificate PEM' || $scope.uploadKeyFormat == 'JSON Web Key Set (JWK)';
+        return $scope.uploadKeyFormat == 'Certificate PEM' || $scope.uploadKeyFormat == 'Public Key PEM' || $scope.uploadKeyFormat == 'JSON Web Key Set';
     }
 
     $scope.uploadKeyFormat = $scope.keyFormats[0];
@@ -791,6 +792,17 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates,
         {name: "INCLUSIVE_WITH_COMMENTS", value: "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"}
     ];
 
+    $scope.oidcSignatureAlgorithms = [
+        "unsigned",
+        "RS256"
+    ];
+
+    $scope.requestObjectSignatureAlgorithms = [
+        "any",
+        "none",
+        "RS256"
+    ];
+
     $scope.realm = realm;
     $scope.samlAuthnStatement = false;
     $scope.samlMultiValuedRoles = false;
@@ -891,6 +903,12 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates,
                 $scope.samlForcePostBinding = false;
             }
         }
+
+        var attrVal1 = $scope.client.attributes['user.info.response.signature.alg'];
+        $scope.userInfoSignedResponseAlg = attrVal1==null ? 'unsigned' : attrVal1;
+
+        var attrVal2 = $scope.client.attributes['request.object.signature.alg'];
+         $scope.requestObjectSignatureAlg = attrVal2==null ? 'any' : attrVal2;
     }
 
     if (!$scope.create) {
@@ -955,6 +973,22 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates,
         $scope.client.attributes['saml_name_id_format'] = $scope.nameIdFormat;
     };
 
+    $scope.changeUserInfoSignedResponseAlg = function() {
+        if ($scope.userInfoSignedResponseAlg === 'unsigned') {
+            $scope.client.attributes['user.info.response.signature.alg'] = null;
+        } else {
+            $scope.client.attributes['user.info.response.signature.alg'] = $scope.userInfoSignedResponseAlg;
+        }
+    };
+
+    $scope.changeRequestObjectSignatureAlg = function() {
+        if ($scope.requestObjectSignatureAlg === 'any') {
+            $scope.client.attributes['request.object.signature.alg'] = null;
+        } else {
+            $scope.client.attributes['request.object.signature.alg'] = $scope.requestObjectSignatureAlg;
+        }
+    };
+
     $scope.$watch(function() {
         return $location.path();
     }, function() {
@@ -1084,6 +1118,12 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates,
             }, $scope.client, function() {
                 $route.reload();
                 Notifications.success("Your changes have been saved to the client.");
+            }, function(error) {
+                if (error.status == 400 && error.data.error_description) {
+                    Notifications.error(error.data.error_description);
+                } else {
+                    Notifications.error('Unexpected error when updating client');
+                }
             });
         }
     };
@@ -1198,6 +1238,12 @@ module.controller('CreateClientCtrl', function($scope, realm, client, templates,
             var id = l.substring(l.lastIndexOf("/") + 1);
             $location.url("/realms/" + realm.realm + "/clients/" + id);
             Notifications.success("The client has been created.");
+        }, function(error) {
+            if (error.status == 400 && error.data.error_description) {
+                Notifications.error(error.data.error_description);
+            } else {
+                Notifications.error('Unexpected error when creating client');
+            }
         });
     };
 
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-time-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-time-detail.html
index fc4af74..5177734 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-time-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/provider/resource-server-policy-time-detail.html
@@ -37,7 +37,7 @@
                 <label class="col-md-2 control-label" for="policy.config.nbf">{{:: 'not-before' | translate}}</label>
 
                 <div class="col-md-6 time-selector">
-                    <input class="form-control" style="width: 150px" type="text" id="policy.config.nbf" name="notBefore" data-ng-model="policy.config.nbf" placeholder="yyyy-MM-dd hh:mm:ss">
+                    <input class="form-control" style="width: 150px" type="text" id="policy.config.nbf" name="notBefore" data-ng-model="policy.config.nbf" placeholder="yyyy-MM-dd hh:mm:ss" data-ng-required="isRequired()">
                 </div>
                 <kc-tooltip>{{:: 'authz-policy-time-not-before.tooltip' | translate}}</kc-tooltip>
             </div>
@@ -45,10 +45,50 @@
                 <label class="col-md-2 control-label" for="policy.config.noa">{{:: 'authz-policy-time-not-on-after' | translate}}</label>
 
                 <div class="col-md-6 time-selector">
-                    <input class="form-control" style="width: 150px" type="text" id="policy.config.noa" name="policy.config.noa" data-ng-model="policy.config.noa" placeholder="yyyy-MM-dd hh:mm:ss">
+                    <input class="form-control" style="width: 150px" type="text" id="policy.config.noa" name="policy.config.noa" data-ng-model="policy.config.noa" placeholder="yyyy-MM-dd hh:mm:ss" data-ng-required="isRequired()">
                 </div>
                 <kc-tooltip>{{:: 'authz-policy-time-not-on-after.tooltip' | translate}}</kc-tooltip>
             </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="policy.config.noa">{{:: 'authz-policy-time-day-month' | translate}}</label>
+
+                <div class="col-md-6 time-selector">
+                    <input class="form-control" type="number" min="1" max="31" data-ng-model="policy.config.dayMonth" id="dayMonth" name="dayMonth" data-ng-required="isRequired()"/>&nbsp;&nbsp;to&nbsp;&nbsp;<input class="form-control" type="number" min="{{policy.config.dayMonth}}" max="31" data-ng-model="policy.config.dayMonthEnd" id="dayMonthEnd" name="dayMonthEnd"/>
+                </div>
+                <kc-tooltip>{{:: 'authz-policy-time-day-month.tooltip' | translate}}</kc-tooltip>
+            </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="policy.config.noa">{{:: 'authz-policy-time-month' | translate}}</label>
+
+                <div class="col-md-6 time-selector">
+                    <input class="form-control" type="number" min="1" max="12" data-ng-model="policy.config.month" id="month" name="month" data-ng-required="isRequired()"/>&nbsp;&nbsp;to&nbsp;&nbsp;<input class="form-control" type="number" min="{{policy.config.month}}" max="12" data-ng-model="policy.config.monthEnd" id="monthEnd" name="monthEnd"/>
+                </div>
+                <kc-tooltip>{{:: 'authz-policy-time-month.tooltip' | translate}}</kc-tooltip>
+            </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="policy.config.noa">{{:: 'authz-policy-time-year' | translate}}</label>
+
+                <div class="col-md-6 time-selector">
+                    <input class="form-control" type="number" min="2016" max="2050" data-ng-model="policy.config.year" id="year" name="year" data-ng-required="isRequired()"/>&nbsp;&nbsp;to&nbsp;&nbsp;<input class="form-control" type="number" min="{{policy.config.year}}" max="2050" data-ng-model="policy.config.yearEnd" id="yearEnd" name="yearEnd"/>
+                </div>
+                <kc-tooltip>{{:: 'authz-policy-time-year.tooltip' | translate}}</kc-tooltip>
+            </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="policy.config.noa">{{:: 'authz-policy-time-hour' | translate}}</label>
+
+                <div class="col-md-6 time-selector">
+                    <input class="form-control" type="number" min="0" max="23" data-ng-model="policy.config.hour" id="hour" name="hour" data-ng-required="isRequired()"/>&nbsp;&nbsp;to&nbsp;&nbsp;<input class="form-control" type="number" min="{{policy.config.hour}}" max="23" data-ng-model="policy.config.hourEnd" id="hourEnd" name="hourEnd"/>
+                </div>
+                <kc-tooltip>{{:: 'authz-policy-time-hour.tooltip' | translate}}</kc-tooltip>
+            </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="policy.config.noa">{{:: 'authz-policy-time-minute' | translate}}</label>
+
+                <div class="col-md-6 time-selector">
+                    <input class="form-control" type="number" min="0" max="59" data-ng-model="policy.config.minute" id="minute" name="minute" data-ng-required="isRequired()"/>&nbsp;&nbsp;to&nbsp;&nbsp;<input class="form-control" type="number" min="{{policy.config.minute}}" max="59" data-ng-model="policy.config.minuteEnd" id="minuteEnd" name="minuteEnd"/>
+                </div>
+                <kc-tooltip>{{:: 'authz-policy-time-minute.tooltip' | translate}}</kc-tooltip>
+            </div>
             <div class="form-group clearfix">
                 <label class="col-md-2 control-label" for="policy.logic">{{:: 'authz-policy-logic' | translate}}</label>
 
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
index 1378f78..d8f0d24 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
@@ -333,6 +333,36 @@
             </div>
         </fieldset>
 
+        <fieldset data-ng-show="protocol == 'openid-connect'">
+            <legend collapsed><span class="text">{{:: 'fine-oidc-endpoint-conf' | translate}}</span>  <kc-tooltip>{{:: 'fine-oidc-endpoint-conf.tooltip' | translate}}</kc-tooltip></legend>
+            <div class="form-group clearfix block" data-ng-show="protocol == 'openid-connect'">
+                <label class="col-md-2 control-label" for="userInfoSignedResponseAlg">{{:: 'user-info-signed-response-alg' | translate}}</label>
+                <div class="col-sm-6">
+                    <div>
+                        <select class="form-control" id="userInfoSignedResponseAlg"
+                                ng-change="changeUserInfoSignedResponseAlg()"
+                                ng-model="userInfoSignedResponseAlg"
+                                ng-options="sig for sig in oidcSignatureAlgorithms">
+                        </select>
+                    </div>
+                </div>
+                <kc-tooltip>{{:: 'user-info-signed-response-alg.tooltip' | translate}}</kc-tooltip>
+            </div>
+            <div class="form-group clearfix block" data-ng-show="protocol == 'openid-connect'">
+                <label class="col-md-2 control-label" for="requestObjectSignatureAlg">{{:: 'request-object-signature-alg' | translate}}</label>
+                <div class="col-sm-6">
+                    <div>
+                        <select class="form-control" id="requestObjectSignatureAlg"
+                                ng-change="changeRequestObjectSignatureAlg()"
+                                ng-model="requestObjectSignatureAlg"
+                                ng-options="sig for sig in requestObjectSignatureAlgorithms">
+                        </select>
+                    </div>
+                </div>
+                <kc-tooltip>{{:: 'request-object-signature-alg.tooltip' | translate}}</kc-tooltip>
+            </div>
+        </fieldset>
+
         <div class="form-group">
             <div class="col-md-10 col-md-offset-2" data-ng-show="access.manageClients">
                 <button kc-save  data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
diff --git a/themes/src/main/resources/theme/base/admin/theme.properties b/themes/src/main/resources/theme/base/admin/theme.properties
index 65bc856..b292e39 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,de,en,es,fr,it,lt,pt-BR,ru
\ No newline at end of file
+locales=ca,de,en,es,fr,it,ja,lt,pt-BR,ru
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/email/messages/messages_ja.properties b/themes/src/main/resources/theme/base/email/messages/messages_ja.properties
new file mode 100644
index 0000000..087170a
--- /dev/null
+++ b/themes/src/main/resources/theme/base/email/messages/messages_ja.properties
@@ -0,0 +1,25 @@
+# encoding: utf-8
+emailVerificationSubject=Eメールの確認
+emailVerificationBody=このメールアドレスで {2} アカウントが作成されたました。以下のリンクをクリックしてメールアドレスの確認を完了してください。\n\n{0}\n\nこのリンクは {1} 分間だけ有効です。\n\nもしこのアカウントの作成に心当たりがない場合は、このメールを無視してください。
+emailVerificationBodyHtml=<p>このメールアドレスで {2} アカウントが作成されました。以下のリンクをクリックしてメールアドレスの確認を完了してください。</p><p><a href="{0}">{0}</a></p><p>このリンクは {1} 分間だけ有効です。</p><p>もしこのアカウントの作成に心当たりがない場合は、このメールを無視してください。</p>
+identityProviderLinkSubject=リンク {0}
+identityProviderLinkBody=あなたの "{1}" アカウントと {2} ユーザーの "{0}" アカウントのリンクが要求されました。以下のリンクをクリックしてアカウントのリンクを行ってください。\n\n{3}\n\nこのリンクは {4} 分間だけ有効です。\n\nもしアカウントのリンクを行わない場合は、このメッセージを無視してください。アカウントのリンクを行うことで、{0} 経由で {1} にログインすることができるようになります。
+identityProviderLinkBodyHtml=<p>あなたの <b>{1}</b> アカウントと {2} ユーザーの <b>{0}</b> アカウントのリンクが要求されました。以下のリンクをクリックしてアカウントのリンクを行ってください。</p><p><a href="{3}">{3}</a></p><p>このリンクは {4} 分間だけ有効です。</p><p>もしアカウントのリンクを行わない場合は、このメッセージを無視してください。アカウントのリンクを行うことで、{0} 経由で {1} にログインすることができるようになります。</p>
+passwordResetSubject=パスワードのリセット
+passwordResetBody=あなたの {2} アカウントのクレデンシャルの変更が要求されています。以下のリンクをクリックしてクレデンシャルのリセットを行ってください。\n\n{0}\n\nこのリンクとコードは {1} 分間だけ有効です。\n\nもしクレデンシャルのリセットを行わない場合は、このメッセージを無視してください。何も変更されません。
+passwordResetBodyHtml=<p>あなたの {2} アカウントのクレデンシャルの変更が要求されています。以下のリンクをクリックしてクレデンシャルのリセットを行ってください。</p><p><a href="{0}">{0}</a></p><p>このリンクとコードは {1} 分間だけ有効です。</p><p>もしクレデンシャルのリセットを行わない場合は、このメッセージを無視してください。何も変更されません。</p>
+executeActionsSubject=アカウントの更新
+executeActionsBody=管理者よりあなたの {2} アカウントの更新が要求されています。以下のリンクをクリックしてこのプロセスを開始してください。\n\n{0}\n\nこのリンクは {1} 分間だけ有効です。\n\n管理者からのこの変更要求についてご存知ない場合は、このメッセージを無視してください。何も変更されません。
+executeActionsBodyHtml=<p>管理者よりあなたの {2} アカウントの更新が要求されています。以下のリンクをクリックしてこのプロセスを開始してください。</p><p><a href="{0}">{0}</a></p><p>このリンクは {1} 分間だけ有効です。</p><p>管理者からのこの変更要求についてご存知ない場合は、このメッセージを無視してください。何も変更されません。</p>
+eventLoginErrorSubject=ログインエラー
+eventLoginErrorBody={0} に {1} からのログイン失敗があなたのアカウントで検出されました。心当たりがない場合は、管理者に連絡してください。
+eventLoginErrorBodyHtml=<p>{0} に {1} からのログイン失敗があなたのアカウントで検出されました。心当たりがない場合は管理者に連絡してください。</p>
+eventRemoveTotpSubject=TOTPの削除
+eventRemoveTotpBody={0} に {1} からの操作で TOTP が削除されました。心当たりがない場合は、管理者に連絡してください。
+eventRemoveTotpBodyHtml=<p>{0} に {1} からの操作で TOTP が削除されました。心当たりがない場合は、管理者に連絡してください。</p>
+eventUpdatePasswordSubject=パスワードの更新
+eventUpdatePasswordBody={0} に {1} からの操作であなたのパスワードが変更されました。心当たりがない場合は、管理者に連絡してください。
+eventUpdatePasswordBodyHtml=<p>{0} に {1} からの操作であなたのパスワードが変更されました。心当たりがない場合は、管理者に連絡してください。</p>
+eventUpdateTotpSubject=TOTPの更新
+eventUpdateTotpBody={0} に {1} からの操作で TOTP が更新されました。心当たりがない場合は、管理者に連絡してください。
+eventUpdateTotpBodyHtml=<p>{0} に {1} からの操作で TOTP が更新されました。心当たりがない場合は、管理者に連絡してください。</p>
diff --git a/themes/src/main/resources/theme/base/email/theme.properties b/themes/src/main/resources/theme/base/email/theme.properties
index db32af8..ff4018c 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,lt,pt-BR,ru
\ No newline at end of file
+locales=ca,de,en,es,fr,it,ja,lt,pt-BR,ru
\ No newline at end of file
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 45305c7..17e1b74 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
@@ -214,6 +214,7 @@ locale_en=English
 locale_es=Espa\u00F1ol
 locale_fr=Fran\u00e7ais
 locale_it=Italian
+locale_ja=\u65E5\u672C\u8A9E
 locale_pt_BR=Portugu\u00EAs (Brasil)
 locale_pt-BR=Portugu\u00EAs (Brasil)
 locale_ru=\u0420\u0443\u0441\u0441\u043A\u0438\u0439
diff --git a/themes/src/main/resources/theme/base/login/messages/messages_ja.properties b/themes/src/main/resources/theme/base/login/messages/messages_ja.properties
new file mode 100644
index 0000000..e0d1fd8
--- /dev/null
+++ b/themes/src/main/resources/theme/base/login/messages/messages_ja.properties
@@ -0,0 +1,218 @@
+# encoding: utf-8
+doLogIn=ログイン
+doRegister=登録
+doCancel=キャンセル
+doSubmit=送信
+doYes=はい
+doNo=いいえ
+doContinue=続ける
+doAccept=承諾
+doDecline=却下
+doForgotPassword=パスワードをお忘れですか?
+doClickHere=クリックしてください
+doImpersonate=代理ログイン
+kerberosNotConfigured=Kerberos は設定されていません
+kerberosNotConfiguredTitle=Kerberos は設定されていません
+bypassKerberosDetail=Kerberos 経由でログインしていないか、ブラウザーで Kerberos ログインの設定がされていません。他の手段でログインするには 「続ける」 をクリックしてください。
+kerberosNotSetUp=Kerberos が設定されていません。ログインできません。
+registerWithTitle={0} で登録
+registerWithTitleHtml={0}
+loginTitle={0} にログイン
+loginTitleHtml={0}
+impersonateTitle={0} ユーザーの代理
+impersonateTitleHtml=<strong>{0}</strong> ユーザーの代理</strong>
+realmChoice=レルム
+unknownUser=不明なユーザー
+loginTotpTitle=モバイル Authenticator セットアップ
+loginProfileTitle=アカウント情報の更新
+loginTimeout=ログインに時間がかかりすぎています。最初からログイン処理を開始します。
+oauthGrantTitle=Grant アクセス
+oauthGrantTitleHtml={0}
+errorTitle=申し訳ございません
+errorTitleHtml=<strong>申し訳ございません</strong>
+emailVerifyTitle=Eメール確認
+emailForgotTitle=パスワードをお忘れですか?
+updatePasswordTitle=パスワード更新
+codeSuccessTitle=成功コード
+codeErrorTitle=エラーコード\: {0}
+
+termsTitle=利用規約
+termsTitleHtml=利用規約
+termsText=<p>利用規約はここで設定する必要があります</p>
+
+recaptchaFailed=無効な reCAPTCHA
+recaptchaNotConfigured=reCAPTCHA が必須ですが、設定されていません
+consentDenied=同意は拒否されました。
+
+noAccount=新規ユーザーですか?
+username=ユーザー名
+usernameOrEmail=ユーザー名 または メールアドレス
+firstName=名
+givenName=名
+fullName=氏名
+lastName=姓
+familyName=姓
+email=Eメール
+password=パスワード
+passwordConfirm=パスワード (確認)
+passwordNew=新しいパスワード
+passwordNewConfirm=新しいパスワード (確認)
+rememberMe=ログイン状態の保存
+authenticatorCode=ワンタイムコード
+address=住所
+street=番地
+locality=市区町村
+region=都道府県
+postal_code=郵便番号
+country=国
+emailVerified=確認済みEメール
+gssDelegationCredential=GSS 代行クレデンシャル
+
+loginTotpStep1=<a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> または Google Authenticator (Google認証システム) をご自身のデバイスにインストールしてください。これらのアプリケーションは <a href="https://play.google.com">Google Play</a> と Apple App Store で入手できます。
+loginTotpStep2=アプリケーションを開きバーコードをスキャンするかキーを入力してください。
+loginTotpStep3=アプリケーションで提供されたワンタイムコードを入力して送信をクリックし、セットアップを完了してください。
+loginTotpOneTime=ワンタイムコード
+
+oauthGrantRequest=アクセス権を許可してよろしいでしょうか?
+inResource=in
+
+emailVerifyInstruction1=メールアドレスを確認する手順を記載したEメールを送信しました。
+emailVerifyInstruction2=Eメールで確認コードを受け取っていませんか?
+emailVerifyInstruction3=Eメールを再送信します。
+
+emailLinkIdpTitle=リンク {0}
+emailLinkIdp1={0} の {1} アカウントを あなたの {2} アカウントとリンクするための手順を記載したEメールを送信しました。
+emailLinkIdp2=Eメールで確認コードを受け取っていませんか?
+emailLinkIdp3=Eメールを再送信します。
+
+backToLogin=&laquo; ログインに戻る
+
+emailInstruction=ユーザー名またメールアドレスを入力してください。新しいパスワードの設定方法をご案内いたします。
+
+copyCodeInstruction=このコードをコピーし、あなたのアプリケーションにペーストしてください:
+
+personalInfo=個人情報:
+role_admin=管理者
+role_realm-admin=レルム管理者
+role_create-realm=レルムの作成
+role_create-client=クライアントの作成
+role_view-realm=レルムの参照
+role_view-users=ユーザーの参照
+role_view-applications=アプリケーションの参照
+role_view-clients=クライアントの参照
+role_view-events=イベントの参照
+role_view-identity-providers=アイデンティティ プロバイダーの参照
+role_manage-realm=レルムの管理
+role_manage-users=ユーザーの管理
+role_manage-applications=アプリケーションの管理
+role_manage-identity-providers=アイデンティティ プロバイダーの管理
+role_manage-clients=クライアントの管理
+role_manage-events=イベントの管理
+role_view-profile=プロフィールの参照
+role_manage-account=アカウントの管理
+role_read-token=トークンの読み取り
+role_offline-access=オフラインアクセス
+client_account=アカウント
+client_security-admin-console=セキュリティ管理コンソール
+client_admin-cli=管理 CLI
+client_realm-management=レルム管理
+client_broker=ブローカー
+
+invalidUserMessage=無効なユーザー名またはパスワードです。
+invalidEmailMessage=無効なメールアドレスです。
+accountDisabledMessage=アカウントは無効です。管理者に連絡してください。
+accountTemporarilyDisabledMessage=アカウントは一時的に無効です。管理者に連絡、またはしばらく時間をおいてから再度お試しください。
+expiredCodeMessage=ログインタイムアウトが発生しました。再度ログインしてください。
+
+missingFirstNameMessage=名を指定してください。
+missingLastNameMessage=姓を指定してください。
+missingEmailMessage=Eメールを指定してください。
+missingUsernameMessage=ユーザー名を指定してください。
+missingPasswordMessage=パスワードを指定してください。
+missingTotpMessage=Authenticator コードを指定してください。
+notMatchPasswordMessage=パスワードが一致していません。
+
+invalidPasswordExistingMessage=間違った既存のパスワードです。
+invalidPasswordConfirmMessage=パスワード確認が一致していません。
+invalidTotpMessage=間違った Authenticator コードです。
+
+usernameExistsMessage=既に存在するユーザー名です。
+emailExistsMessage=既に存在するEメールです。
+
+federatedIdentityExistsMessage={0} {1} のユーザーは既に存在します。そのアカウントををリンクするにはアカウント管理にログインしてください。
+
+confirmLinkIdpTitle=既に存在するアカウントです。
+federatedIdentityConfirmLinkMessage={0} {1} のユーザーは既に存在します。継続しますか?
+federatedIdentityConfirmReauthenticateMessage={1} でアカウントをリンクするために {0} として認証します
+confirmLinkIdpReviewProfile=プロフィールの確認
+confirmLinkIdpContinue=既存のアカウントに追加する
+
+configureTotpMessage=アカウントを有効にするにはモバイル Authenticator のセットアップが必要です。
+updateProfileMessage=アカウントを有効にするにはユーザープロフィールの更新が必要です。
+updatePasswordMessage=アカウントを有効にするにはパスワードの更新が必要です。
+verifyEmailMessage=アカウントを有効にするにはメールアドレスの確認が必要です。
+linkIdpMessage=アカウントを {0} とリンクするにはメールアドレスの確認が必要です。
+
+emailSentMessage=詳細な手順を記載したEメールをすぐに受信してください。
+emailSendErrorMessage=Eメールの送信に失敗しました。しばらく時間をおいてから再度お試しください。
+
+accountUpdatedMessage=アカウントが更新されました。
+accountPasswordUpdatedMessage=パスワードが更新されました。
+
+noAccessMessage=アクセスがありません
+
+invalidPasswordMinLengthMessage=無効なパスワード: 最小 {0} の長さが必要です。
+invalidPasswordMinDigitsMessage=無効なパスワード: 少なくとも {0} 文字の数字を含む必要があります。
+invalidPasswordMinLowerCaseCharsMessage=無効なパスワード: 少なくとも {0} 文字の小文字を含む必要があります。
+invalidPasswordMinUpperCaseCharsMessage=無効なパスワード: 少なくとも {0} 文字の大文字を含む必要があります。
+invalidPasswordMinSpecialCharsMessage=無効なパスワード:  少なくとも {0} 文字の特殊文字を含む必要があります。
+invalidPasswordNotUsernameMessage=無効なパスワード: ユーザー名と同じパスワードは禁止されています。
+invalidPasswordRegexPatternMessage=無効なパスワード: 正規表現パターンと一致しません。
+invalidPasswordHistoryMessage=無効なパスワード: 最近の {0} パスワードのいずれかと同じパスワードは禁止されています。
+
+failedToProcessResponseMessage=応答を処理できませんでした
+httpsRequiredMessage=HTTPS が必須です
+realmNotEnabledMessage=レルムが有効ではありません
+invalidRequestMessage=無効なリクエストです
+failedLogout=ログアウトに失敗しました
+unknownLoginRequesterMessage=不明なログイン要求元です
+loginRequesterNotEnabledMessage=ログイン要求元は有効ではありません
+bearerOnlyMessage=bearer-only のアプリケーションはブラウザーログインを開始することが許可されていません
+standardFlowDisabledMessage=与えられた response_type でクライアントはブラウザーログインを開始することが許可されていません。 Standard Flow は無効です。
+implicitFlowDisabledMessage=与えられた response_type でクライアントはブラウザーログインを開始することが許可されていません。 Implicit Flow は無効です。
+invalidRedirectUriMessage=無効なリダイレクト URI です
+unsupportedNameIdFormatMessage=サポートされていない NameID Format です
+invalidRequesterMessage=無効な要求元です
+registrationNotAllowedMessage=登録は許可されていません
+resetCredentialNotAllowedMessage=クレデンシャルのリセットは許可されていません
+
+permissionNotApprovedMessage=アクセス権は承認されていません。
+noRelayStateInResponseMessage=アイデンティティ プロバイダーからの応答に RelayState がありません。
+insufficientPermissionMessage=アイデンティティにリンクするには不十分なアクセス権です。
+couldNotProceedWithAuthenticationRequestMessage=アイデンティティ プロバイダーに認証要求を続行できませんでした。
+couldNotObtainTokenMessage=アイデンティティ プロバイダーからトークンを取得できませんでした。
+unexpectedErrorRetrievingTokenMessage=アイデンティティ プロバイダーからのトークン取得で予期せぬエラーが発生しました。
+unexpectedErrorHandlingResponseMessage=アイデンティティ プロバイダーからの応答を処理する際に予期せぬエラーが発生しました。
+identityProviderAuthenticationFailedMessage=認証に失敗しました。アイデンティティ プロバイダーを使用して認証できませんでした。
+identityProviderDifferentUserMessage={1} として認証されることを期待していましたが、{0} として認証されました
+couldNotSendAuthenticationRequestMessage=アイデンティティ プロバイダーに認証要求を送信することができませんでした。
+unexpectedErrorHandlingRequestMessage=アイデンティティ プロバイダーへの認証要求を処理する際に予期せぬエラーが発生しました。
+invalidAccessCodeMessage=無効なアクセスコードです。
+sessionNotActiveMessage=セッションが有効ではありません。
+invalidCodeMessage=エラーが発生しました。アプリケーションを介して再度ログインしてください。
+identityProviderUnexpectedErrorMessage=アイデンティティ プロバイダーによる認証の再に予期せぬエラーが発生しました
+identityProviderNotFoundMessage=該当の識別子を持つアイデンティティ プロバイダーが見つかりませんでした。
+identityProviderLinkSuccess=アカウントが正常に {0} の {1} アカウントにリンクされました。
+staleCodeMessage=このページはもはや有効ではありませんので、アプリケーションに戻り再度ログインしてください
+realmSupportsNoCredentialsMessage=レルムはクレデンシャルタイプをサポートしていません。
+identityProviderNotUniqueMessage=レルムは複数のアイデンティティ プロバイダーをサポートしています。どのアイデンティティ プロバイダーが認証に使用されるべきか判断できませんでした。
+emailVerifiedMessage=メールアドレスが確認できました。
+staleEmailVerificationLink=クリックされたリンクは古いリンクでありもはや有効ではありません。おそらくEメールは既に確認済みではないでしょうか?
+
+backToApplication=&laquo; アプリケーションに戻る
+missingParameterMessage=不足パラメータ\: {0}
+clientNotFoundMessage=クライアントが見つかりません。
+clientDisabledMessage=クライアントが無効になっています。
+invalidParameterMessage=無効なパラメータ\: {0}
+alreadyLoggedIn=既にログインしています。
+
diff --git a/themes/src/main/resources/theme/base/login/theme.properties b/themes/src/main/resources/theme/base/login/theme.properties
index db32af8..ff4018c 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,lt,pt-BR,ru
\ No newline at end of file
+locales=ca,de,en,es,fr,it,ja,lt,pt-BR,ru
\ No newline at end of file
diff --git a/wildfly/server-subsystem/pom.xml b/wildfly/server-subsystem/pom.xml
index 5601739..05b0fc0 100755
--- a/wildfly/server-subsystem/pom.xml
+++ b/wildfly/server-subsystem/pom.xml
@@ -47,12 +47,35 @@
                         <include>**/*TestCase.java</include>
                     </includes>
                 </configuration>
-            </plugin>            
+            </plugin>        
         </plugins>
+        <filters>
+            <filter>src/main/config/default-server-subsys-config.properties</filter>
+        </filters>
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+                <filtering>true</filtering>
+            </resource>
+        </resources>
+        <testResources>
+            <testResource>
+                <directory>src/test/resources</directory>
+                <filtering>true</filtering>
+            </testResource>
+        </testResources>
     </build>
 
     <dependencies>
         <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.wildfly.core</groupId>
             <artifactId>wildfly-controller</artifactId>
             <scope>provided</scope>
diff --git a/wildfly/server-subsystem/src/main/config/default-server-subsys-config.properties b/wildfly/server-subsystem/src/main/config/default-server-subsys-config.properties
new file mode 100644
index 0000000..2355ab0
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/config/default-server-subsys-config.properties
@@ -0,0 +1,73 @@
+# IMPORTANT: If you change this file you should also make equivalent changes
+# to src/main/resources/cli/default-keycloak-subsys-config.cli
+# The CLI file is packaged with the subsystem and extracted by the overlay distribution.
+
+keycloak.server.subsys.default.config=\
+<subsystem xmlns="urn:jboss:domain:keycloak-server:1.1">\
+   <web-context>auth</web-context>\
+    <providers>\
+        <provider>classpath:${jboss.home.dir}/providers/*</provider>\
+    </providers>\
+    <master-realm-name>master</master-realm-name>\
+    <scheduled-task-interval>900</scheduled-task-interval>\
+    <theme>\
+        <staticMaxAge>2592000</staticMaxAge>\
+        <cacheThemes>true</cacheThemes>\
+        <cacheTemplates>true</cacheTemplates>\
+        <dir>${jboss.home.dir}/themes</dir>\
+    </theme>\
+    <spi name="eventsStore">\
+        <default-provider>jpa</default-provider>\
+        <provider name="jpa" enabled="true">\
+            <properties>\
+                <property name="exclude-events" value="[&quot;REFRESH_TOKEN&quot;]"/>\
+            </properties>\
+        </provider>\
+    </spi>\
+    <spi name="realm">\
+        <default-provider>jpa</default-provider>\
+    </spi>\
+    <spi name="user">\
+        <default-provider>jpa</default-provider>\
+    </spi>\
+    <spi name="userCache">\
+        <provider name="default" enabled="true"/>\
+    </spi>\
+    <spi name="userSessionPersister">\
+        <default-provider>jpa</default-provider>\
+    </spi>\
+    <spi name="authorizationPersister">\
+        <default-provider>jpa</default-provider>\
+    </spi>\
+    <spi name="timer">\
+        <default-provider>basic</default-provider>\
+    </spi>\
+    <spi name="connectionsHttpClient">\
+        <provider name="default" enabled="true"/>\
+    </spi>\
+    <spi name="connectionsJpa">\
+        <provider name="default" enabled="true">\
+            <properties>\
+                <property name="dataSource" value="java:jboss/datasources/KeycloakDS"/>\
+                <property name="initializeEmpty" value="true"/>\
+                <property name="migrationStrategy" value="update"/>\
+                <property name="migrationExport" value="${jboss.home.dir}/keycloak-database-update.sql"/>\
+            </properties>\
+        </provider>\
+    </spi>\
+    <spi name="realmCache">\
+        <provider name="default" enabled="true"/>\
+    </spi>\
+    <spi name="connectionsInfinispan">\
+        <default-provider>default</default-provider>\
+        <provider name="default" enabled="true">\
+            <properties>\
+                <property name="cacheContainer" value="java:comp/env/infinispan/Keycloak"/>\
+            </properties>\
+        </provider>\
+    </spi>\
+    <spi name="jta-lookup">\
+        <default-provider>${keycloak.jta.lookup.provider:jboss}</default-provider>\
+        <provider name="jboss" enabled="true"/>\
+    </spi>\
+</subsystem>\
\ No newline at end of file
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/attributes/ModulesListAttributeBuilder.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/attributes/ModulesListAttributeBuilder.java
new file mode 100644
index 0000000..d47ea10
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/attributes/ModulesListAttributeBuilder.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.server.attributes;
+
+import org.jboss.as.controller.StringListAttributeDefinition;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class ModulesListAttributeBuilder extends StringListAttributeDefinition.Builder {
+    public ModulesListAttributeBuilder() {
+        super("modules");
+        setAllowExpression(true);
+        setAllowNull(true);
+    }
+}
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/attributes/ProvidersListAttributeBuilder.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/attributes/ProvidersListAttributeBuilder.java
new file mode 100644
index 0000000..2b4952e
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/attributes/ProvidersListAttributeBuilder.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.server.attributes;
+
+import org.jboss.as.controller.StringListAttributeDefinition;
+import org.jboss.dmr.ModelNode;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class ProvidersListAttributeBuilder extends StringListAttributeDefinition.Builder {
+    public ProvidersListAttributeBuilder() {
+        super("providers");
+        ModelNode provider = new ModelNode();
+        provider.add("classpath:${jboss.home.dir}/providers/*");
+        this.defaultValue = provider;
+        setAllowExpression(true);
+        setAllowNull(true);
+    }
+    
+}
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/JsonConfigConverter.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/JsonConfigConverter.java
new file mode 100644
index 0000000..aa4d3f5
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/JsonConfigConverter.java
@@ -0,0 +1,262 @@
+/*
+ * 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.server.extension;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import org.jboss.as.controller.PathAddress;
+import org.jboss.as.controller.operations.common.Util;
+import org.jboss.dmr.ModelNode;
+
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.WRITE_ATTRIBUTE_OPERATION;
+
+import static org.keycloak.subsystem.server.extension.KeycloakSubsystemDefinition.PROVIDERS;
+import static org.keycloak.subsystem.server.extension.KeycloakSubsystemDefinition.MASTER_REALM_NAME;
+import static org.keycloak.subsystem.server.extension.KeycloakSubsystemDefinition.SCHEDULED_TASK_INTERVAL;
+import static org.keycloak.subsystem.server.extension.ThemeResourceDefinition.CACHE_TEMPLATES;
+import static org.keycloak.subsystem.server.extension.ThemeResourceDefinition.CACHE_THEMES;
+import static org.keycloak.subsystem.server.extension.ThemeResourceDefinition.DEFAULT;
+import static org.keycloak.subsystem.server.extension.ThemeResourceDefinition.DIR;
+import static org.keycloak.subsystem.server.extension.ThemeResourceDefinition.MODULES;
+import static org.keycloak.subsystem.server.extension.ThemeResourceDefinition.STATIC_MAX_AGE;
+import static org.keycloak.subsystem.server.extension.ThemeResourceDefinition.WELCOME_THEME;
+
+/**
+ * Converts json representation of Keycloak config to DMR operations.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class JsonConfigConverter {
+
+    private static final List<String> NON_SPI_LIST = new ArrayList<>();
+    
+    static {
+        NON_SPI_LIST.add("providers");
+        NON_SPI_LIST.add("admin");
+        NON_SPI_LIST.add("theme");
+        NON_SPI_LIST.add("scheduled");
+    }
+
+    /**
+     * Convert keycloak-server.json to DMR operations that write to standalone.xml
+     * or domain.xml.
+     * 
+     * @param json The json representation of the config.
+     * @param subsysAddress The management model address of the keycloak-server subsystem.
+     * @return A list of DMR operations.
+     * @throws IOException If the json can not be parsed.
+     */
+    public static List<ModelNode> convertJsonConfig(String json, PathAddress subsysAddress) throws IOException {
+        List<ModelNode> list = new ArrayList<>();
+
+        JsonNode root = new ObjectMapper().readTree(json);
+
+        list.add(masterRealmName(root, subsysAddress));
+        list.add(scheduledTaskInterval(root, subsysAddress));
+        list.add(providers(root, subsysAddress));
+        list.add(theme(root, subsysAddress.append(ThemeResourceDefinition.TAG_NAME, 
+                                                ThemeResourceDefinition.RESOURCE_NAME)));
+        list.addAll(spis(root, subsysAddress));
+
+        return list;
+    }
+
+    private static ModelNode masterRealmName(JsonNode root, PathAddress addr) {
+        JsonNode targetNode = getNode(root, "admin", "realm");
+        String value = MASTER_REALM_NAME.getDefaultValue().asString();
+        if (targetNode != null) value = targetNode.asText(value);
+        
+        ModelNode op = Util.createOperation(WRITE_ATTRIBUTE_OPERATION, addr);
+        op.get("name").set(MASTER_REALM_NAME.getName());
+        op.get("value").set(value);
+        return op;
+    }
+    
+    private static ModelNode scheduledTaskInterval(JsonNode root, PathAddress addr) {
+        JsonNode targetNode = getNode(root, "scheduled", "interval");
+        Long value = SCHEDULED_TASK_INTERVAL.getDefaultValue().asLong();
+        if (targetNode != null) value = targetNode.asLong(value);
+        
+        ModelNode op = Util.createOperation(WRITE_ATTRIBUTE_OPERATION, addr);
+        op.get("name").set(SCHEDULED_TASK_INTERVAL.getName());
+        op.get("value").set(value);
+        return op;
+    }
+    
+    private static ModelNode providers(JsonNode root, PathAddress addr) {
+        JsonNode targetNode = getNode(root, "providers");
+        ModelNode value = PROVIDERS.getDefaultValue();
+        if (targetNode != null && targetNode.isArray()) {
+            value = new ModelNode();
+            for (JsonNode node : targetNode) {
+                value.add(node.asText());
+            }
+        }
+        
+        ModelNode op = Util.createOperation(WRITE_ATTRIBUTE_OPERATION, addr);
+        op.get("name").set(PROVIDERS.getName());
+        op.get("value").set(value);
+        return op;
+    }
+    
+    private static ModelNode theme(JsonNode root, PathAddress addr) {
+        JsonNode themeNode = getNode(root, "theme");
+        ModelNode op = Util.createAddOperation(addr);
+        
+        JsonNode targetNode = getNode(themeNode, "staticMaxAge");
+        Long lValue = STATIC_MAX_AGE.getDefaultValue().asLong();
+        if (targetNode != null) lValue = targetNode.asLong(lValue);
+        op.get(STATIC_MAX_AGE.getName()).set(lValue);
+
+        targetNode = getNode(themeNode, "cacheTemplates");
+        Boolean bValue = CACHE_TEMPLATES.getDefaultValue().asBoolean();
+        if (targetNode != null) bValue = targetNode.asBoolean(bValue);
+        op.get(CACHE_TEMPLATES.getName()).set(bValue);
+        
+        targetNode = getNode(themeNode, "cacheThemes");
+        bValue = CACHE_THEMES.getDefaultValue().asBoolean();
+        if (targetNode != null) bValue = targetNode.asBoolean(bValue);
+        op.get(CACHE_THEMES.getName()).set(bValue);
+        
+        targetNode = getNode(themeNode, "folder", "dir");
+        String sValue = DIR.getDefaultValue().asString();
+        if (targetNode != null) sValue = targetNode.asText(sValue);
+        op.get(DIR.getName()).set(sValue);
+        
+        targetNode = getNode(themeNode, "welcomeTheme");
+        if (targetNode != null) op.get(WELCOME_THEME.getName()).set(targetNode.asText());
+        
+        targetNode = getNode(themeNode, "default");
+        if (targetNode != null) op.get(DEFAULT.getName()).set(targetNode.asText());
+        
+        targetNode = getNode(themeNode, "module", "modules");
+        if (targetNode != null && targetNode.isArray()) {
+            op.get(MODULES.getName()).set(themeModules(targetNode));
+        }
+        
+        return op;
+    }
+    
+    private static ModelNode themeModules(JsonNode modulesNode) {
+        ModelNode modules = new ModelNode();
+        for (JsonNode node : modulesNode) {
+            modules.add(node.asText());
+        }
+        return modules;
+    }
+    
+    private static Collection<ModelNode> spis(JsonNode root, PathAddress addr) {
+        List<ModelNode> spis = new ArrayList<>();
+        
+        Iterator<String> spiIterator = root.fieldNames();
+        while (spiIterator.hasNext()) {
+            String spiName = spiIterator.next();
+            if (NON_SPI_LIST.contains(spiName)) continue;
+            
+            PathAddress spiAddr = addr.append("spi", spiName);
+            spis.addAll(spi(root, spiAddr, spiName));
+        }
+        
+        return spis;
+    }
+    
+    private static List<ModelNode> spi(JsonNode root, PathAddress spiAddr, String spiName) {
+        List<ModelNode> spiAndProviders = new ArrayList<>();
+        ModelNode op = Util.createAddOperation(spiAddr);
+        spiAndProviders.add(op);
+        
+        Iterator<String> providerIterator = root.get(spiName).fieldNames();
+        while (providerIterator.hasNext()) {
+            String providerName = providerIterator.next();
+            if ("provider".equals(providerName)) {
+                op.get(SpiResourceDefinition.DEFAULT_PROVIDER.getName()).set(getNode(root, spiName, "provider").asText());
+            } else {
+                PathAddress providerAddr = spiAddr.append("provider", providerName);
+                spiAndProviders.add(spiProvider(getNode(root, spiName, providerName), providerAddr));
+            }
+        }
+        
+        return spiAndProviders;
+    }
+    
+    private static ModelNode spiProvider(JsonNode providerNode, PathAddress providerAddr) {
+        ModelNode op = Util.createAddOperation(providerAddr);
+        
+        ModelNode properties = new ModelNode();
+        
+        Iterator<String> propNames = providerNode.fieldNames();
+        while (propNames.hasNext()) {
+            String propName = propNames.next();
+            
+            if ("enabled".equals(propName)) {
+                op.get(ProviderResourceDefinition.ENABLED.getName()).set(providerNode.get(propName).asBoolean());
+            } else {
+                if (providerNode.get(propName).isArray()) {
+                    properties.get(propName).set(makeArrayText(providerNode.get(propName)));
+                } else {
+                    properties.get(propName).set(providerNode.get(propName).asText());
+                }
+            }
+        }
+        
+        if (properties.isDefined() && !properties.asPropertyList().isEmpty()) {
+            op.get("properties").set(properties);
+        }
+        
+        if (!op.hasDefined(ProviderResourceDefinition.ENABLED.getName())) {
+            op.get(ProviderResourceDefinition.ENABLED.getName()).set(ProviderResourceDefinition.ENABLED.getDefaultValue());
+        }
+        
+        return op;
+    }
+    
+    private static String makeArrayText(JsonNode arrayNode) {
+        StringBuilder builder = new StringBuilder("[");
+        
+        Iterator<JsonNode> nodes = arrayNode.iterator();
+        while (nodes.hasNext()) {
+            JsonNode node = nodes.next();
+            builder.append("\"");
+            builder.append(node.asText());
+            builder.append("\"");
+            if (nodes.hasNext()) builder.append(",");
+        }
+        builder.append("]");
+        
+        return builder.toString();
+    }
+    
+    private static JsonNode getNode(JsonNode root, String... path) {
+        if (root == null) {
+            return null;
+        }
+        JsonNode n = root;
+        for (String p : path) {
+            n = n.get(p);
+            if (n == null) {
+                return null;
+            }
+        }
+        return n;
+    }
+
+}
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakAdapterConfigService.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakAdapterConfigService.java
index 03a2fe2..0eec2ee 100755
--- a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakAdapterConfigService.java
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakAdapterConfigService.java
@@ -16,9 +16,15 @@
  */
 package org.keycloak.subsystem.server.extension;
 
+import org.jboss.as.controller.PathAddress;
+import org.jboss.as.controller.PathElement;
+import org.jboss.dmr.ModelNode;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADDRESS;
+import org.jboss.dmr.Property;
+
 /**
  * This service keeps track of the entire Keycloak management model so as to provide
- * adapter configuration to each deployment at deploy time.
+ * configuration to the Keycloak Server.
  *
  * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
  */
@@ -27,6 +33,8 @@ public final class KeycloakAdapterConfigService {
     static final KeycloakAdapterConfigService INSTANCE = new KeycloakAdapterConfigService();
 
     static final String DEPLOYMENT_NAME = "keycloak-server.war";
+    
+    static ModelNode fullConfig = new ModelNode();
 
     private String webContext;
 
@@ -34,6 +42,127 @@ public final class KeycloakAdapterConfigService {
     private KeycloakAdapterConfigService() {
     }
 
+    void updateConfig(ModelNode operation, ModelNode config) {
+        PathAddress address = PathAddress.pathAddress(operation.get(ADDRESS));
+        address = address.subAddress(1); // remove root (subsystem=keycloak-server)
+        
+        ModelNode newConfig = fullConfig.clone();
+        ModelNode subNode = newConfig;
+        for (PathElement pathElement : address) {
+            subNode = subNode.get(pathElement.getKey(), pathElement.getValue());
+        }
+        
+        subNode.set(config.clone());
+        
+        // remove undefined properties
+        for (Property prop : subNode.asPropertyList()) {
+            if (!prop.getValue().isDefined()) {
+                subNode.remove(prop.getName());
+            }
+        }
+        
+        fullConfig = newConfig;
+    }
+    
+    ModelNode getConfig() {
+        ModelNode copy = fullConfig.clone();
+        //System.out.println("******** BEFORE *************");
+        //System.out.println(copy);
+        //System.out.println("*****************************");
+        copy.remove("web-context");
+        massageScheduledTaskInterval(copy);
+        massageMasterRealm(copy);
+        massageTheme(copy);
+        massageSpis(copy);
+        //System.out.println("******** JSON *************");
+        //System.out.println(copy.resolve().toJSONString(false));
+        //System.out.println("**********************");
+        return copy;
+    }
+    
+    // The "massage" methods rearrange the model so that everything will
+    // be where the Keycloak server's Config interface expects it to be.
+    
+    private void massageScheduledTaskInterval(ModelNode copy) {
+        if (!copy.hasDefined("scheduled-task-intervale")) return;
+        ModelNode taskInterval = copy.remove("scheduled-task-interval");
+        copy.get("scheduled", "interval").set(taskInterval);
+    }
+    
+    private void massageMasterRealm(ModelNode copy) {
+        if (!copy.hasDefined("master-realm-name")) return;
+        ModelNode master = copy.remove("master-realm-name");
+        copy.get("admin", "realm").set(master);
+    }
+    
+    private void massageTheme(ModelNode copy) {
+        if (!copy.hasDefined("theme")) return;
+        if (!copy.get("theme").hasDefined("defaults")) return;
+        
+        ModelNode themeDefaults = copy.get("theme", "defaults");
+        copy.get("theme").set(themeDefaults);
+        
+        if (copy.has("theme", "dir")) {
+            ModelNode dir = copy.get("theme", "dir");
+            copy.get("theme", "folder", "dir").set(dir);
+            copy.get("theme").remove("dir");
+        }
+        
+        if (copy.has("theme", "modules")) {
+            ModelNode modules = copy.get("theme").remove("modules");
+            copy.get("theme", "module", "modules").set(modules);
+        }
+    }
+    
+    private void massageSpis(ModelNode copy) {
+        if (!copy.hasDefined("spi")) return;
+        ModelNode spis = copy.remove("spi");
+        
+        for (Property prop : spis.asPropertyList()) {
+            ModelNode spi = prop.getValue();
+            
+            if (spi.has("provider")) {
+                massageProviders(spi);
+            }
+            
+            if (spi.has("default-provider")) {
+                ModelNode defaultProvider = spi.remove("default-provider");
+                spi.get("provider").set(defaultProvider);
+            }
+            
+            copy.get(prop.getName()).set(spi);
+        }
+    }
+    
+    private void massageProviders(ModelNode spi) {
+        if (!spi.hasDefined("provider")) return;
+        ModelNode providers = spi.remove("provider");
+        for (Property prop : providers.asPropertyList()) {
+            ModelNode provider = prop.getValue();
+            if (provider.has("properties")) {
+                massageProviderProps(provider);
+            }
+            spi.get(prop.getName()).set(provider);
+        }
+    }
+    
+    private void massageProviderProps(ModelNode provider) {
+        if (!provider.hasDefined("properties")) return;
+        ModelNode providerProps = provider.remove("properties");
+        for (Property prop : providerProps.asPropertyList()) {
+            ModelNode value = prop.getValue();
+            if (isArray(value.asString().trim())) {
+                provider.get(prop.getName()).set(ModelNode.fromString(value.asString()).asList());
+            } else {
+                provider.get(prop.getName()).set(value);
+            }
+        }
+    }
+    
+    private boolean isArray(String value) {
+        return value.startsWith("[") && value.endsWith("]");
+    }
+    
     void setWebContext(String webContext) {
         this.webContext = webContext;
     }
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakExtension.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakExtension.java
index c6a3cdb..bdfe47c 100755
--- a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakExtension.java
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakExtension.java
@@ -26,6 +26,7 @@ import org.jboss.as.controller.descriptions.StandardResourceDescriptionResolver;
 import org.jboss.as.controller.parsing.ExtensionParsingContext;
 
 import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM;
+import org.jboss.as.controller.registry.ManagementResourceRegistration;
 import static org.keycloak.subsystem.server.logging.KeycloakLogger.ROOT_LOGGER;
 
 
@@ -44,6 +45,10 @@ public class KeycloakExtension implements Extension {
     private static final ResourceDefinition KEYCLOAK_SUBSYSTEM_RESOURCE = new KeycloakSubsystemDefinition();
     private static final KeycloakSubsystemParser PARSER = new KeycloakSubsystemParser();
     private static final ModelVersion MGMT_API_VERSION = ModelVersion.create(1,1,0);
+    
+    static final ThemeResourceDefinition THEME_DEFINITION = new ThemeResourceDefinition();
+    static final SpiResourceDefinition SPI_DEFINITION = new SpiResourceDefinition();
+    static final ProviderResourceDefinition PROVIDER_DEFINITION = new ProviderResourceDefinition();
 
     static StandardResourceDescriptionResolver getResourceDescriptionResolver(final String... keyPrefix) {
         StringBuilder prefix = new StringBuilder(SUBSYSTEM_NAME);
@@ -69,7 +74,11 @@ public class KeycloakExtension implements Extension {
         ROOT_LOGGER.debug("Activating Keycloak Extension");
         final SubsystemRegistration subsystem = context.registerSubsystem(SUBSYSTEM_NAME, MGMT_API_VERSION);
 
-        subsystem.registerSubsystemModel(KEYCLOAK_SUBSYSTEM_RESOURCE);
+        ManagementResourceRegistration subsystemRegistration = subsystem.registerSubsystemModel(KEYCLOAK_SUBSYSTEM_RESOURCE);
+        subsystemRegistration.registerSubModel(THEME_DEFINITION);
+        ManagementResourceRegistration spiRegistration = subsystemRegistration.registerSubModel(SPI_DEFINITION);
+        spiRegistration.registerSubModel(PROVIDER_DEFINITION);
+        
         subsystem.registerXMLElementWriter(PARSER);
     }
 }
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakServerDeploymentProcessor.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakServerDeploymentProcessor.java
index f99cab3..1bc2211 100755
--- a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakServerDeploymentProcessor.java
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakServerDeploymentProcessor.java
@@ -16,41 +16,78 @@
  */
 package org.keycloak.subsystem.server.extension;
 
+import java.util.ArrayList;
+import java.util.List;
 import org.jboss.as.ee.component.EEModuleDescription;
 import org.jboss.as.server.deployment.DeploymentPhaseContext;
 import org.jboss.as.server.deployment.DeploymentUnit;
 import org.jboss.as.server.deployment.DeploymentUnitProcessingException;
 import org.jboss.as.server.deployment.DeploymentUnitProcessor;
+import org.jboss.as.web.common.WarMetaData;
+import org.jboss.metadata.javaee.spec.ParamValueMetaData;
+import org.jboss.metadata.web.jboss.JBossWebMetaData;
 import org.jboss.msc.service.ServiceName;
 import org.jboss.msc.service.ServiceTarget;
 
 /**
- * DUP responsible for setting the web context of a Keycloak auth server.
+ * DUP responsible for setting the web context of a Keycloak auth server and
+ * passing the Keycloak configuration to the Keycloak server.
  *
  * @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
  */
 public class KeycloakServerDeploymentProcessor implements DeploymentUnitProcessor {
 
+    // This param name is defined again in Keycloak Services class
+    // org.keycloak.services.resources.KeycloakApplication.  We have this value in
+    // two places to avoid dependency between Keycloak Subsystem and Keyclaok Services module.
+    public static final String KEYCLOAK_CONFIG_PARAM_NAME = "org.keycloak.server-subsystem.Config";
+    
     private static final ServiceName cacheContainerService = ServiceName.of("jboss", "infinispan", "keycloak");
-
+    
     @Override
     public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
         DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
-        KeycloakAdapterConfigService config = KeycloakAdapterConfigService.INSTANCE;
+        KeycloakAdapterConfigService configService = KeycloakAdapterConfigService.INSTANCE;
         String deploymentName = deploymentUnit.getName();
 
-        if (!config.isKeycloakServerDeployment(deploymentName)) {
+        if (!configService.isKeycloakServerDeployment(deploymentName)) {
             return;
         }
 
         final EEModuleDescription description = deploymentUnit.getAttachment(org.jboss.as.ee.component.Attachments.EE_MODULE_DESCRIPTION);
-        String webContext = config.getWebContext();
+        String webContext = configService.getWebContext();
         if (webContext == null) {
             throw new DeploymentUnitProcessingException("Can't determine web context/module for Keycloak Server");
         }
         description.setModuleName(webContext);
 
         addInfinispanCaches(phaseContext);
+        addConfiguration(deploymentUnit, configService);
+    }
+    
+    private void addConfiguration(DeploymentUnit deploymentUnit, KeycloakAdapterConfigService configService) throws DeploymentUnitProcessingException {
+        WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
+        if (warMetaData == null) {
+            throw new DeploymentUnitProcessingException("WarMetaData not found for KeycloakServer.");
+        }
+        
+        JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData();
+        if (webMetaData == null) {
+            webMetaData = new JBossWebMetaData();
+            warMetaData.setMergedJBossWebMetaData(webMetaData);
+        }
+
+        List<ParamValueMetaData> contextParams = webMetaData.getContextParams();
+        if (contextParams == null) {
+            contextParams = new ArrayList<ParamValueMetaData>();
+        }
+
+        ParamValueMetaData param = new ParamValueMetaData();
+        param.setParamName(KEYCLOAK_CONFIG_PARAM_NAME);
+        param.setParamValue(configService.getConfig().toString());
+        contextParams.add(param);
+
+        webMetaData.setContextParams(contextParams);
     }
 
     private void addInfinispanCaches(DeploymentPhaseContext context) {
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemAdd.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemAdd.java
index 3c6dc34..6d75e28 100755
--- a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemAdd.java
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemAdd.java
@@ -62,9 +62,10 @@ class KeycloakSubsystemAdd extends AbstractBoottimeAddStepHandler {
         }, OperationContext.Stage.RUNTIME);
     }
 
-    protected void populateModel(final OperationContext context, final ModelNode operation, final Resource resource) throws  OperationFailedException {
+    @Override
+    protected void populateModel(final OperationContext context, final ModelNode operation, final Resource resource) throws OperationFailedException {
         ModelNode model = resource.getModel();
-
+        
         // set attribute values from parsed model
         for (AttributeDefinition attrDef : ALL_ATTRIBUTES) {
             attrDef.validateAndSet(operation, model);
@@ -89,5 +90,7 @@ class KeycloakSubsystemAdd extends AbstractBoottimeAddStepHandler {
         ServerUtil serverUtil = new ServerUtil(operation);
         serverUtil.addStepToUploadServerWar(context);
         KeycloakAdapterConfigService.INSTANCE.setWebContext(webContext);
+        
+        KeycloakAdapterConfigService.INSTANCE.updateConfig(operation, model);
     }
 }
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemDefinition.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemDefinition.java
index 6fcee7b..3dc4917 100644
--- a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemDefinition.java
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemDefinition.java
@@ -29,6 +29,8 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import org.jboss.as.controller.StringListAttributeDefinition;
+import org.keycloak.subsystem.server.attributes.ProvidersListAttributeBuilder;
 
 /**
  * Definition of subsystem=keycloak-server.
@@ -44,15 +46,34 @@ public class KeycloakSubsystemDefinition extends SimpleResourceDefinition {
             .setRestartAllServices()
             .build();
 
-    static final List<SimpleAttributeDefinition> ALL_ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
+    static final StringListAttributeDefinition PROVIDERS = new ProvidersListAttributeBuilder().build();
+    
+    static final SimpleAttributeDefinition MASTER_REALM_NAME =
+        new SimpleAttributeDefinitionBuilder("master-realm-name", ModelType.STRING, true)
+            .setAllowExpression(true)
+            .setDefaultValue(new ModelNode("master"))
+            .setRestartAllServices()
+            .build();
+    
+    static final SimpleAttributeDefinition SCHEDULED_TASK_INTERVAL =
+        new SimpleAttributeDefinitionBuilder("scheduled-task-interval", ModelType.LONG, true)
+            .setAllowExpression(true)
+            .setDefaultValue(new ModelNode("900"))
+            .setRestartAllServices()
+            .build();
+    
+    static final List<AttributeDefinition> ALL_ATTRIBUTES = new ArrayList<AttributeDefinition>();
 
     static {
         ALL_ATTRIBUTES.add(WEB_CONTEXT);
+        ALL_ATTRIBUTES.add(PROVIDERS);
+        ALL_ATTRIBUTES.add(MASTER_REALM_NAME);
+        ALL_ATTRIBUTES.add(SCHEDULED_TASK_INTERVAL);
     }
 
-    private static final Map<String, SimpleAttributeDefinition> DEFINITION_LOOKUP = new HashMap<String, SimpleAttributeDefinition>();
+    private static final Map<String, AttributeDefinition> DEFINITION_LOOKUP = new HashMap<String, AttributeDefinition>();
     static {
-        for (SimpleAttributeDefinition def : ALL_ATTRIBUTES) {
+        for (AttributeDefinition def : ALL_ATTRIBUTES) {
             DEFINITION_LOOKUP.put(def.getXmlName(), def);
         }
     }
@@ -71,6 +92,7 @@ public class KeycloakSubsystemDefinition extends SimpleResourceDefinition {
     public void registerOperations(ManagementResourceRegistration resourceRegistration) {
         super.registerOperations(resourceRegistration);
         resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);
+        resourceRegistration.registerOperationHandler(MigrateJsonOperation.DEFINITION, new MigrateJsonOperation());
     }
 
     @Override
@@ -81,7 +103,7 @@ public class KeycloakSubsystemDefinition extends SimpleResourceDefinition {
         }
     }
 
-    public static SimpleAttributeDefinition lookup(String name) {
+    public static AttributeDefinition lookup(String name) {
         return DEFINITION_LOOKUP.get(name);
     }
 }
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemParser.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemParser.java
index 6db6e57..0f4ea8c 100755
--- a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemParser.java
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemParser.java
@@ -29,9 +29,26 @@ import org.jboss.staxmapper.XMLExtendedStreamWriter;
 import javax.xml.stream.XMLStreamConstants;
 import javax.xml.stream.XMLStreamException;
 import java.util.List;
+import static javax.xml.stream.XMLStreamConstants.END_ELEMENT;
+import org.jboss.as.controller.AttributeDefinition;
+import org.jboss.as.controller.PathElement;
+import org.jboss.as.controller.PropertiesAttributeDefinition;
+import org.jboss.as.controller.SimpleAttributeDefinition;
+import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
+import org.jboss.dmr.Property;
 
 import static org.keycloak.subsystem.server.extension.KeycloakExtension.PATH_SUBSYSTEM;
 import static org.keycloak.subsystem.server.extension.KeycloakSubsystemDefinition.WEB_CONTEXT;
+import static org.keycloak.subsystem.server.extension.KeycloakSubsystemDefinition.PROVIDERS;
+import static org.keycloak.subsystem.server.extension.KeycloakSubsystemDefinition.MASTER_REALM_NAME;
+import static org.keycloak.subsystem.server.extension.KeycloakSubsystemDefinition.SCHEDULED_TASK_INTERVAL;
+
+import static org.keycloak.subsystem.server.extension.ThemeResourceDefinition.MODULES;
+
+import static org.keycloak.subsystem.server.extension.SpiResourceDefinition.DEFAULT_PROVIDER;
+
+import static org.keycloak.subsystem.server.extension.ProviderResourceDefinition.ENABLED;
+import static org.keycloak.subsystem.server.extension.ProviderResourceDefinition.PROPERTIES;
 
 /**
  * The subsystem parser, which uses stax to read and write to and from xml
@@ -51,12 +68,116 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
         while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
             if (reader.getLocalName().equals(WEB_CONTEXT.getXmlName())) {
                 WEB_CONTEXT.parseAndSetParameter(reader.getElementText(), addKeycloakSub, reader);
+            } else if (reader.getLocalName().equals(PROVIDERS.getXmlName())) {
+                readProviders(reader, addKeycloakSub);
+            } else if (reader.getLocalName().equals(MASTER_REALM_NAME.getXmlName())) {
+                MASTER_REALM_NAME.parseAndSetParameter(reader.getElementText(), addKeycloakSub, reader);
+            } else if (reader.getLocalName().equals(SCHEDULED_TASK_INTERVAL.getXmlName())) {
+                SCHEDULED_TASK_INTERVAL.parseAndSetParameter(reader.getElementText(), addKeycloakSub, reader);
+            } else if (reader.getLocalName().equals(ThemeResourceDefinition.TAG_NAME)) {
+                readTheme(list, reader);
+            } else if (reader.getLocalName().equals(SpiResourceDefinition.TAG_NAME)) {
+                readSpi(list, reader);
             } else {
                 throw new XMLStreamException("Unknown keycloak-server subsystem tag: " + reader.getLocalName());
             }
         }
     }
-
+    
+    private void readProviders(final XMLExtendedStreamReader reader, ModelNode addKeycloakSub) throws XMLStreamException {
+        while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
+            PROVIDERS.parseAndAddParameterElement(reader.getElementText(),addKeycloakSub, reader);
+        }
+    }
+    
+    private void readTheme(final List<ModelNode> list, final XMLExtendedStreamReader reader) throws XMLStreamException {
+        ModelNode addThemeDefaults = new ModelNode();
+        addThemeDefaults.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.ADD);
+        PathAddress addr = PathAddress.pathAddress(PathElement.pathElement(ModelDescriptionConstants.SUBSYSTEM, KeycloakExtension.SUBSYSTEM_NAME),
+                                                   PathElement.pathElement(ThemeResourceDefinition.TAG_NAME, ThemeResourceDefinition.RESOURCE_NAME));
+        addThemeDefaults.get(ModelDescriptionConstants.OP_ADDR).set(addr.toModelNode());
+        list.add(addThemeDefaults);
+        
+        while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
+            String tagName = reader.getLocalName();
+            if (MODULES.getName().equals(tagName)) {
+                readModules(reader, addThemeDefaults);
+                continue;
+            }
+            
+            SimpleAttributeDefinition def = KeycloakExtension.THEME_DEFINITION.lookup(tagName);
+            if (def == null) throw new XMLStreamException("Unknown theme tag " + tagName);
+            def.parseAndSetParameter(reader.getElementText(), addThemeDefaults, reader);
+        }
+    }
+    
+    private void readModules(final XMLExtendedStreamReader reader, ModelNode addThemeDefaults) throws XMLStreamException {
+        while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
+            MODULES.parseAndAddParameterElement(reader.getElementText(),addThemeDefaults, reader);
+        }
+    }
+    
+    private void readSpi(final List<ModelNode> list, final XMLExtendedStreamReader reader) throws XMLStreamException {
+        String spiName = ParseUtils.requireAttributes(reader, "name")[0];
+        ModelNode addSpi = new ModelNode();
+        addSpi.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.ADD);
+        PathAddress addr = PathAddress.pathAddress(PathElement.pathElement(ModelDescriptionConstants.SUBSYSTEM, KeycloakExtension.SUBSYSTEM_NAME),
+                                                   PathElement.pathElement(SpiResourceDefinition.TAG_NAME, spiName));
+        addSpi.get(ModelDescriptionConstants.OP_ADDR).set(addr.toModelNode());
+        list.add(addSpi);
+        
+        while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
+            if (reader.getLocalName().equals(DEFAULT_PROVIDER.getXmlName())) {
+                DEFAULT_PROVIDER.parseAndSetParameter(reader.getElementText(), addSpi, reader);
+            } else if (reader.getLocalName().equals(ProviderResourceDefinition.TAG_NAME)) {
+                readProvider(list, spiName, reader);
+            }
+        }
+    }
+    
+    private void readProvider(final List<ModelNode> list, String spiName, final XMLExtendedStreamReader reader) throws XMLStreamException {
+        String[] attributes = ParseUtils.requireAttributes(reader, "name", ENABLED.getXmlName());
+        String providerName = attributes[0];
+        String enabled = attributes[1];
+        
+        ModelNode addProvider = new ModelNode();
+        addProvider.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.ADD);
+        PathAddress addr = PathAddress.pathAddress(PathElement.pathElement(ModelDescriptionConstants.SUBSYSTEM, KeycloakExtension.SUBSYSTEM_NAME),
+                                                   PathElement.pathElement(SpiResourceDefinition.TAG_NAME, spiName),
+                                                   PathElement.pathElement(ProviderResourceDefinition.TAG_NAME, providerName));
+        addProvider.get(ModelDescriptionConstants.OP_ADDR).set(addr.toModelNode());
+        addProvider.get(ENABLED.getName()).set(Boolean.valueOf(enabled));
+        list.add(addProvider);
+        
+        while (nextTag(reader) != END_ELEMENT) {
+            if (reader.getLocalName().equals(PROPERTIES.getXmlName())) {
+                readProperties(PROPERTIES, addProvider, reader);
+            }
+        }
+    }
+    
+    private void readProperties(final PropertiesAttributeDefinition attrDef, ModelNode addOp, final XMLExtendedStreamReader reader) throws XMLStreamException {
+        while (nextTag(reader) != END_ELEMENT) {
+        int attrCount = reader.getAttributeCount();
+            if (attrCount != 2) throw new XMLStreamException("Property must have only two attributes");
+            String name = "";
+            String value = "";
+            for (int i=0 ; i < 2; i++) {
+                String attrName = reader.getAttributeLocalName(i);
+                String attrValue = reader.getAttributeValue(i);
+                if (attrName.equals("name")) {
+                    name = attrValue;
+                } else if (attrName.equals("value")) {
+                    value = attrValue;
+                } else {
+                    throw new XMLStreamException("Property can only have attributes named 'name' and 'value'");
+                }
+            }
+            attrDef.parseAndAddParameterElement(name, value, addOp, reader);
+        nextTag(reader);
+        }
+            }
+        
     // used for debugging
     private int nextTag(XMLExtendedStreamReader reader) throws XMLStreamException {
         return reader.nextTag();
@@ -69,9 +190,64 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
     public void writeContent(final XMLExtendedStreamWriter writer, final SubsystemMarshallingContext context) throws XMLStreamException {
         context.startSubsystemElement(KeycloakExtension.NAMESPACE, false);
         writeWebContext(writer, context);
+        writeList(writer, context.getModelNode(), PROVIDERS, "provider");
+        writeAdmin(writer, context);
+        writeScheduledTaskInterval(writer, context);
+        writeThemeDefaults(writer, context);
+        writeSpis(writer, context);
+        writer.writeEndElement();
+    }
+    
+    private void writeThemeDefaults(XMLExtendedStreamWriter writer, SubsystemMarshallingContext context) throws XMLStreamException {
+        if (!context.getModelNode().get(ThemeResourceDefinition.TAG_NAME).isDefined()) {
+            return;
+        }
+        
+        writer.writeStartElement(ThemeResourceDefinition.TAG_NAME);
+        ModelNode themeElements = context.getModelNode().get(ThemeResourceDefinition.TAG_NAME, ThemeResourceDefinition.RESOURCE_NAME);
+        for (AttributeDefinition def : ThemeResourceDefinition.ALL_ATTRIBUTES) {
+            if (themeElements.hasDefined(def.getName())) {
+                if (def == MODULES) {
+                    ModelNode themeContext = context.getModelNode().get("theme", "defaults");
+                    writeList(writer, themeContext, def, "module");
+                } else {
+                    def.marshallAsElement(themeElements, writer);
+                }
+            }
+        }
         writer.writeEndElement();
     }
 
+    private void writeSpis(XMLExtendedStreamWriter writer, SubsystemMarshallingContext context) throws XMLStreamException {
+        if (!context.getModelNode().get(SpiResourceDefinition.TAG_NAME).isDefined()) {
+            return;
+        }
+        
+        for (Property spi : context.getModelNode().get(SpiResourceDefinition.TAG_NAME).asPropertyList()) {
+            writer.writeStartElement(SpiResourceDefinition.TAG_NAME);
+            writer.writeAttribute("name", spi.getName());
+            ModelNode spiElements = spi.getValue();
+            DEFAULT_PROVIDER.marshallAsElement(spiElements, writer);
+            writeProviders(writer, spiElements);
+            writer.writeEndElement();
+        }
+    }
+    
+    private void writeProviders(XMLExtendedStreamWriter writer, ModelNode spiElements) throws XMLStreamException {
+        if (!spiElements.get(ProviderResourceDefinition.TAG_NAME).isDefined()) {
+            return;
+        }
+        
+        for (Property provider : spiElements.get(ProviderResourceDefinition.TAG_NAME).asPropertyList()) {
+            writer.writeStartElement(ProviderResourceDefinition.TAG_NAME);
+            writer.writeAttribute("name", provider.getName());
+            ModelNode providerElements = provider.getValue();
+            ENABLED.marshallAsAttribute(providerElements, writer);
+            PROPERTIES.marshallAsElement(providerElements, writer);
+            writer.writeEndElement();
+        }
+    }
+    
     private void writeWebContext(XMLExtendedStreamWriter writer, SubsystemMarshallingContext context) throws XMLStreamException {
         if (!context.getModelNode().get(WEB_CONTEXT.getName()).isDefined()) {
             return;
@@ -79,4 +255,36 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
 
         WEB_CONTEXT.marshallAsElement(context.getModelNode(), writer);
     }
-}
+    
+    private void writeAdmin(XMLExtendedStreamWriter writer, SubsystemMarshallingContext context) throws XMLStreamException {
+        if (!context.getModelNode().get(MASTER_REALM_NAME.getName()).isDefined()) {
+            return;
+        }
+
+        MASTER_REALM_NAME.marshallAsElement(context.getModelNode(), writer);
+    }
+    
+    private void writeScheduledTaskInterval(XMLExtendedStreamWriter writer, SubsystemMarshallingContext context) throws XMLStreamException {
+        if (!context.getModelNode().get(SCHEDULED_TASK_INTERVAL.getName()).isDefined()) {
+            return;
+        }
+
+        SCHEDULED_TASK_INTERVAL.marshallAsElement(context.getModelNode(), writer);
+    }
+    
+    private void writeList(XMLExtendedStreamWriter writer, ModelNode context, AttributeDefinition def, String elementName) throws XMLStreamException {
+        if (!context.get(def.getName()).isDefined()) {
+            return;
+        }
+
+        writer.writeStartElement(def.getXmlName());
+        ModelNode modules = context.get(def.getName());
+        for (ModelNode module : modules.asList()) {
+            writer.writeStartElement(elementName);
+            writer.writeCharacters(module.asString());
+            writer.writeEndElement();
+        }
+        writer.writeEndElement();
+    }
+    
+}
\ No newline at end of file
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemWriteAttributeHandler.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemWriteAttributeHandler.java
index 8469198..3304616 100755
--- a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemWriteAttributeHandler.java
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemWriteAttributeHandler.java
@@ -17,7 +17,6 @@
 package org.keycloak.subsystem.server.extension;
 
 import org.jboss.as.controller.AttributeDefinition;
-import org.jboss.as.controller.SimpleAttributeDefinition;
 
 import java.util.List;
 import org.jboss.as.controller.ModelOnlyWriteAttributeHandler;
@@ -33,7 +32,7 @@ import org.jboss.dmr.ModelNode;
  */
 public class KeycloakSubsystemWriteAttributeHandler extends ModelOnlyWriteAttributeHandler { //extends ReloadRequiredWriteAttributeHandler {
 
-    public KeycloakSubsystemWriteAttributeHandler(List<SimpleAttributeDefinition> definitions) {
+    public KeycloakSubsystemWriteAttributeHandler(List<AttributeDefinition> definitions) {
         this(definitions.toArray(new AttributeDefinition[definitions.size()]));
     }
 
@@ -59,7 +58,7 @@ public class KeycloakSubsystemWriteAttributeHandler extends ModelOnlyWriteAttrib
     }
 
     private boolean attribNotChanging(String attributeName, ModelNode newValue, ModelNode oldValue) {
-        SimpleAttributeDefinition attribDef = KeycloakSubsystemDefinition.lookup(attributeName);
+        AttributeDefinition attribDef = KeycloakSubsystemDefinition.lookup(attributeName);
         if (!oldValue.isDefined()) {
             oldValue = attribDef.getDefaultValue();
         }
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/MigrateJsonOperation.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/MigrateJsonOperation.java
new file mode 100644
index 0000000..92c229d
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/MigrateJsonOperation.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.server.extension;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import org.jboss.as.controller.AttributeDefinition;
+import org.jboss.as.controller.OperationContext;
+import org.jboss.as.controller.OperationDefinition;
+import org.jboss.as.controller.OperationFailedException;
+import org.jboss.as.controller.OperationStepHandler;
+import org.jboss.as.controller.PathAddress;
+import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
+import org.jboss.as.controller.SimpleOperationDefinitionBuilder;
+import org.jboss.dmr.ModelNode;
+import org.jboss.dmr.ModelType;
+
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADDRESS;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
+
+/**
+ * This operation provides a migration path from keycloak-server.json to
+ * standalone.xml or domain.xml.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class MigrateJsonOperation implements OperationStepHandler {
+    public static final String OPERATION_NAME = "migrate-json";
+    
+    private static final String CONFIG_DIR = System.getProperty("jboss.server.config.dir");
+    private static final Path DEFAULT_CONFIG_FILE = Paths.get(CONFIG_DIR, "keycloak-server.json");
+
+    private static final AttributeDefinition FILE_ATTRIBUTE = SimpleAttributeDefinitionBuilder.create("file", ModelType.BYTES, true).build();
+    public static final OperationDefinition DEFINITION = new SimpleOperationDefinitionBuilder(OPERATION_NAME, KeycloakExtension.getResourceDescriptionResolver())
+            .setRuntimeOnly()
+            .setReadOnly()
+            .setReplyType(ModelType.STRING)
+            .setParameters(FILE_ATTRIBUTE)
+            .build();
+
+    private String localConfig() throws IOException {
+        if (Files.notExists(DEFAULT_CONFIG_FILE)) return null;
+        return new String(Files.readAllBytes(DEFAULT_CONFIG_FILE));
+    }
+    
+    private String readConfig(ModelNode operation) throws IOException {
+        ModelNode file = operation.get(FILE_ATTRIBUTE.getName());
+        if (file.isDefined() && file.asBytes().length > 0) {
+            return new String(file.asBytes());
+        }
+        
+        String localConfig = localConfig();
+        if (localConfig != null) return localConfig;
+        
+        throw new IOException("Can not find json file to migrate");
+    }
+    
+    @Override
+    public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
+        List<ModelNode> ops = null;
+        try {
+            PathAddress currentAddr = context.getCurrentAddress();
+            ops = JsonConfigConverter.convertJsonConfig(readConfig(operation), currentAddr);
+        } catch (IOException ioe) {
+            throw new OperationFailedException(ioe);
+        }
+         
+        for (ModelNode op : ops) {
+            PathAddress addr = PathAddress.pathAddress(op.get(ADDRESS));
+            String opName = op.get(OP).asString();
+            context.addStep(op, 
+                            context.getRootResourceRegistration().getOperationHandler(addr, opName), 
+                            OperationContext.Stage.MODEL);
+        }
+        
+        context.completeStep(OperationContext.RollbackHandler.NOOP_ROLLBACK_HANDLER);
+    }
+    
+}
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ProviderResourceAddHandler.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ProviderResourceAddHandler.java
new file mode 100644
index 0000000..7db883c
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ProviderResourceAddHandler.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.server.extension;
+
+import org.jboss.as.controller.AbstractAddStepHandler;
+import org.jboss.as.controller.OperationFailedException;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
+import org.jboss.dmr.ModelNode;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class ProviderResourceAddHandler extends AbstractAddStepHandler {
+
+    public static ProviderResourceAddHandler INSTANCE = new ProviderResourceAddHandler();
+
+    private ProviderResourceAddHandler() {
+    }
+
+    @Override
+    protected void populateModel(ModelNode operation, ModelNode model) throws OperationFailedException {
+        // TODO: localize exception. get id number
+        if (!operation.get(OP).asString().equals(ADD)) {
+            throw new OperationFailedException("Unexpected operation for add SPI. operation=" + operation.toString());
+        }
+
+        ProviderResourceDefinition.ENABLED.validateAndSet(operation, model);
+        ProviderResourceDefinition.PROPERTIES.validateAndSet(operation, model);
+        
+        KeycloakAdapterConfigService.INSTANCE.updateConfig(operation, model);
+    }
+}
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ProviderResourceDefinition.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ProviderResourceDefinition.java
new file mode 100644
index 0000000..68ba9bb
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ProviderResourceDefinition.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.server.extension;
+
+import org.jboss.as.controller.PathElement;
+import org.jboss.as.controller.PropertiesAttributeDefinition;
+import org.jboss.as.controller.ReloadRequiredWriteAttributeHandler;
+import org.jboss.as.controller.SimpleAttributeDefinition;
+import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
+import org.jboss.as.controller.SimpleResourceDefinition;
+import org.jboss.as.controller.registry.ManagementResourceRegistration;
+import org.jboss.dmr.ModelNode;
+import org.jboss.dmr.ModelType;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class ProviderResourceDefinition extends SimpleResourceDefinition {
+
+    public static final String TAG_NAME = "provider";
+    
+    protected static final SimpleAttributeDefinition ENABLED =
+            new SimpleAttributeDefinitionBuilder("enabled", ModelType.BOOLEAN, true)
+                    .setAllowExpression(true)
+                    .setDefaultValue(new ModelNode(true))
+                    .setAllowNull(false)
+                    .setRestartAllServices()
+                    .build();
+    
+    static final PropertiesAttributeDefinition PROPERTIES =
+            new PropertiesAttributeDefinition.Builder("properties", true)
+            .setRestartAllServices()
+            .setAllowExpression(true)
+            .build();
+    
+    protected static final ReloadRequiredWriteAttributeHandler WRITE_ATTR_HANDLER = new ReloadRequiredWriteAttributeHandler(ENABLED);
+    
+    protected ProviderResourceDefinition() {
+        super(PathElement.pathElement(TAG_NAME),
+            KeycloakExtension.getResourceDescriptionResolver(TAG_NAME),
+            ProviderResourceAddHandler.INSTANCE,
+            ProviderResourceRemoveHandler.INSTANCE
+        );
+    }
+
+    @Override
+    public void registerAttributes(ManagementResourceRegistration resourceRegistration) {
+        super.registerAttributes(resourceRegistration);
+        resourceRegistration.registerReadWriteAttribute(ENABLED, null, WRITE_ATTR_HANDLER);
+        resourceRegistration.registerReadWriteAttribute(PROPERTIES, null, WRITE_ATTR_HANDLER);
+    }
+    
+    
+}
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ProviderResourceRemoveHandler.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ProviderResourceRemoveHandler.java
new file mode 100644
index 0000000..21d8217
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ProviderResourceRemoveHandler.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.server.extension;
+
+import org.jboss.as.controller.AbstractRemoveStepHandler;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class ProviderResourceRemoveHandler extends AbstractRemoveStepHandler {
+    
+    public static ProviderResourceRemoveHandler INSTANCE = new ProviderResourceRemoveHandler();
+    
+    private ProviderResourceRemoveHandler() {}
+    
+}
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/SpiResourceAddHandler.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/SpiResourceAddHandler.java
new file mode 100644
index 0000000..3d41791
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/SpiResourceAddHandler.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.server.extension;
+
+import org.jboss.as.controller.AbstractAddStepHandler;
+import org.jboss.as.controller.OperationFailedException;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
+import org.jboss.dmr.ModelNode;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class SpiResourceAddHandler extends AbstractAddStepHandler {
+
+    public static SpiResourceAddHandler INSTANCE = new SpiResourceAddHandler();
+    
+    private SpiResourceAddHandler() {}
+    
+    @Override
+    protected void populateModel(ModelNode operation, ModelNode model) throws OperationFailedException {
+        // TODO: localize exception. get id number
+        if (!operation.get(OP).asString().equals(ADD)) {
+            throw new OperationFailedException("Unexpected operation for add SPI. operation=" + operation.toString());
+        }
+
+        SpiResourceDefinition.DEFAULT_PROVIDER.validateAndSet(operation, model);
+        
+        KeycloakAdapterConfigService.INSTANCE.updateConfig(operation, model);
+    }
+}
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/SpiResourceDefinition.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/SpiResourceDefinition.java
new file mode 100644
index 0000000..db15ba4
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/SpiResourceDefinition.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.server.extension;
+
+import org.jboss.as.controller.PathElement;
+import org.jboss.as.controller.ReloadRequiredWriteAttributeHandler;
+import org.jboss.as.controller.SimpleAttributeDefinition;
+import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
+import org.jboss.as.controller.SimpleResourceDefinition;
+import org.jboss.as.controller.registry.ManagementResourceRegistration;
+import org.jboss.dmr.ModelType;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class SpiResourceDefinition extends SimpleResourceDefinition {
+
+    public static final String TAG_NAME = "spi";
+    
+    protected static final SimpleAttributeDefinition DEFAULT_PROVIDER =
+            new SimpleAttributeDefinitionBuilder("default-provider", ModelType.STRING, true)
+                    .setAllowExpression(true)
+                    .setRestartAllServices()
+                    .build();
+    
+    protected static final ReloadRequiredWriteAttributeHandler WRITE_ATTR_HANDLER = new ReloadRequiredWriteAttributeHandler(DEFAULT_PROVIDER);
+    
+    protected SpiResourceDefinition() {
+        super(PathElement.pathElement(TAG_NAME),
+            KeycloakExtension.getResourceDescriptionResolver(TAG_NAME),
+            SpiResourceAddHandler.INSTANCE,
+            SpiResourceRemoveHandler.INSTANCE
+        );
+    }
+
+    @Override
+    public void registerAttributes(ManagementResourceRegistration resourceRegistration) {
+        super.registerAttributes(resourceRegistration);
+        resourceRegistration.registerReadWriteAttribute(DEFAULT_PROVIDER, null, WRITE_ATTR_HANDLER);
+    }
+    
+    
+}
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/SpiResourceRemoveHandler.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/SpiResourceRemoveHandler.java
new file mode 100644
index 0000000..468a40a
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/SpiResourceRemoveHandler.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.server.extension;
+
+import org.jboss.as.controller.AbstractRemoveStepHandler;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class SpiResourceRemoveHandler extends AbstractRemoveStepHandler {
+    
+    public static SpiResourceRemoveHandler INSTANCE = new SpiResourceRemoveHandler();
+    
+    private SpiResourceRemoveHandler() {}
+    
+}
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ThemeResourceAddHandler.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ThemeResourceAddHandler.java
new file mode 100644
index 0000000..b955101
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ThemeResourceAddHandler.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.server.extension;
+
+import org.jboss.as.controller.AbstractAddStepHandler;
+import org.jboss.as.controller.AttributeDefinition;
+import org.jboss.as.controller.OperationFailedException;
+import org.jboss.as.controller.PathAddress;
+import org.jboss.as.controller.PathElement;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADDRESS;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
+import org.jboss.dmr.ModelNode;
+import static org.keycloak.subsystem.server.extension.ThemeResourceDefinition.ALL_ATTRIBUTES;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class ThemeResourceAddHandler extends AbstractAddStepHandler {
+
+    public static ThemeResourceAddHandler INSTANCE = new ThemeResourceAddHandler();
+    
+    private ThemeResourceAddHandler() {}
+    
+    @Override
+    protected void populateModel(ModelNode operation, ModelNode model) throws OperationFailedException {
+        // TODO: localize exception. get id number
+        if (!operation.get(OP).asString().equals(ADD)) {
+            throw new OperationFailedException("Unexpected operation for add Theme. operation=" + operation.toString());
+        }
+        
+        PathAddress address = PathAddress.pathAddress(operation.get(ADDRESS));
+        PathElement last = address.getLastElement();
+        if (!last.getValue().equals(ThemeResourceDefinition.RESOURCE_NAME)) {
+            throw new OperationFailedException("Theme resource with name " + last.getValue() + " not allowed.");
+        }
+
+        for (AttributeDefinition def : ALL_ATTRIBUTES) {
+            def.validateAndSet(operation, model);
+        }
+        
+        KeycloakAdapterConfigService.INSTANCE.updateConfig(operation, model);
+    }
+}
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ThemeResourceDefinition.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ThemeResourceDefinition.java
new file mode 100644
index 0000000..94dca67
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ThemeResourceDefinition.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.server.extension;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.jboss.as.controller.AttributeDefinition;
+import org.jboss.as.controller.PathElement;
+import org.jboss.as.controller.ReloadRequiredWriteAttributeHandler;
+import org.jboss.as.controller.SimpleAttributeDefinition;
+import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
+import org.jboss.as.controller.SimpleResourceDefinition;
+import org.jboss.as.controller.StringListAttributeDefinition;
+import org.jboss.as.controller.registry.ManagementResourceRegistration;
+import org.jboss.dmr.ModelNode;
+import org.jboss.dmr.ModelType;
+import org.keycloak.subsystem.server.attributes.ModulesListAttributeBuilder;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class ThemeResourceDefinition extends SimpleResourceDefinition {
+
+    public static final String TAG_NAME = "theme";
+    
+    // This is the internal name of the singleton resource
+    public static final String RESOURCE_NAME = "defaults";
+    
+    // NOTE: All attributes must be SimpleAttributeDefinition.  If that needs to
+    //       change then refactor starting with lookup() method below.
+    static final SimpleAttributeDefinition STATIC_MAX_AGE =
+        new SimpleAttributeDefinitionBuilder("staticMaxAge", ModelType.LONG, true)
+            .setAllowExpression(true)
+            .setDefaultValue(new ModelNode("2592000"))
+            .setRestartAllServices()
+            .build();
+    
+    static final SimpleAttributeDefinition CACHE_THEMES =
+        new SimpleAttributeDefinitionBuilder("cacheThemes", ModelType.BOOLEAN, true)
+                .setAllowExpression(true)
+                .setDefaultValue(new ModelNode(true))
+                .setAllowNull(false)
+                .setRestartAllServices()
+                .build();
+    
+    static final SimpleAttributeDefinition CACHE_TEMPLATES =
+        new SimpleAttributeDefinitionBuilder("cacheTemplates", ModelType.BOOLEAN, true)
+                .setAllowExpression(true)
+                .setDefaultValue(new ModelNode(true))
+                .setAllowNull(false)
+                .setRestartAllServices()
+                .build();
+    
+    static final SimpleAttributeDefinition WELCOME_THEME =
+        new SimpleAttributeDefinitionBuilder("welcomeTheme", ModelType.STRING, true)
+            .setAllowExpression(true)
+            .setRestartAllServices()
+            .build();
+    
+    static final SimpleAttributeDefinition DEFAULT =
+        new SimpleAttributeDefinitionBuilder("default", ModelType.STRING, true)
+            .setAllowExpression(true)
+            .setRestartAllServices()
+            .build();
+    
+    static final SimpleAttributeDefinition DIR =
+        new SimpleAttributeDefinitionBuilder("dir", ModelType.STRING, true)
+            .setAllowExpression(true)
+            .setDefaultValue(new ModelNode("${jboss.home.dir}/themes"))
+            .setRestartAllServices()
+            .build();
+    
+    static final StringListAttributeDefinition MODULES = new ModulesListAttributeBuilder().build();
+    
+    static final List<AttributeDefinition> ALL_ATTRIBUTES = new ArrayList<>();
+
+    static {
+        ALL_ATTRIBUTES.add(STATIC_MAX_AGE);
+        ALL_ATTRIBUTES.add(CACHE_THEMES);
+        ALL_ATTRIBUTES.add(CACHE_TEMPLATES);
+        ALL_ATTRIBUTES.add(WELCOME_THEME);
+        ALL_ATTRIBUTES.add(DEFAULT);
+        ALL_ATTRIBUTES.add(DIR);
+        ALL_ATTRIBUTES.add(MODULES);
+    }
+
+    private static final Map<String, AttributeDefinition> DEFINITION_LOOKUP = new HashMap<>();
+    static {
+        for (AttributeDefinition def : ALL_ATTRIBUTES) {
+            DEFINITION_LOOKUP.put(def.getXmlName(), def);
+        }
+    }
+    
+    protected static final ReloadRequiredWriteAttributeHandler WRITE_ATTR_HANDLER = new ReloadRequiredWriteAttributeHandler(ALL_ATTRIBUTES);
+    
+    protected ThemeResourceDefinition() {
+        super(PathElement.pathElement(TAG_NAME),
+            KeycloakExtension.getResourceDescriptionResolver(TAG_NAME),
+            ThemeResourceAddHandler.INSTANCE,
+            ThemeResourceRemoveHandler.INSTANCE
+        );
+    }
+
+    @Override
+    public void registerAttributes(ManagementResourceRegistration resourceRegistration) {
+        super.registerAttributes(resourceRegistration);
+        
+        for (AttributeDefinition def : ALL_ATTRIBUTES) {
+            resourceRegistration.registerReadWriteAttribute(def, null, WRITE_ATTR_HANDLER);
+        }
+    }
+
+    public static SimpleAttributeDefinition lookup(String name) {
+        return (SimpleAttributeDefinition)DEFINITION_LOOKUP.get(name);
+    }
+    
+}
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ThemeResourceRemoveHandler.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ThemeResourceRemoveHandler.java
new file mode 100644
index 0000000..797c564
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ThemeResourceRemoveHandler.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.server.extension;
+
+import org.jboss.as.controller.AbstractRemoveStepHandler;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class ThemeResourceRemoveHandler extends AbstractRemoveStepHandler {
+    
+    public static ThemeResourceRemoveHandler INSTANCE = new ThemeResourceRemoveHandler();
+    
+    private ThemeResourceRemoveHandler() {}
+
+}
diff --git a/wildfly/server-subsystem/src/main/resources/cli/default-keycloak-subsys-config.cli b/wildfly/server-subsystem/src/main/resources/cli/default-keycloak-subsys-config.cli
new file mode 100644
index 0000000..6adef01
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/resources/cli/default-keycloak-subsys-config.cli
@@ -0,0 +1,21 @@
+/subsystem=keycloak-server:add(web-context=auth,master-realm-name=master,scheduled-task-interval=900,providers=[classpath:${jboss.home.dir}/providers/*])
+/subsystem=keycloak-server/theme=defaults/:add(dir=${jboss.home.dir}/themes,staticMaxAge=2592000,cacheTemplates=true,cacheThemes=true)
+/subsystem=keycloak-server/spi=eventsStore/:add(default-provider=jpa)
+/subsystem=keycloak-server/spi=eventsStore/provider=jpa/:add(properties={exclude-events => "[\"REFRESH_TOKEN\"]"},enabled=true)
+/subsystem=keycloak-server/spi=realm/:add(default-provider=jpa)
+/subsystem=keycloak-server/spi=user/:add(default-provider=jpa)
+/subsystem=keycloak-server/spi=userCache/:add
+/subsystem=keycloak-server/spi=userCache/provider=default/:add(enabled=true)
+/subsystem=keycloak-server/spi=userSessionPersister/:add(default-provider=jpa)
+/subsystem=keycloak-server/spi=authorizationPersister/:add(default-provider=jpa)
+/subsystem=keycloak-server/spi=timer/:add(default-provider=basic)
+/subsystem=keycloak-server/spi=connectionsHttpClient/:add
+/subsystem=keycloak-server/spi=connectionsHttpClient/provider=default/:add(enabled=true)
+/subsystem=keycloak-server/spi=connectionsJpa/:add
+/subsystem=keycloak-server/spi=connectionsJpa/provider=default/:add(properties={dataSource => "java:jboss/datasources/KeycloakDS",initializeEmpty => "true",migrationStrategy => "update",migrationExport => "${jboss.home.dir}/keycloak-database-update.sql"},enabled=true)
+/subsystem=keycloak-server/spi=realmCache/:add
+/subsystem=keycloak-server/spi=realmCache/provider=default/:add(enabled=true)
+/subsystem=keycloak-server/spi=connectionsInfinispan/:add(default-provider=default)
+/subsystem=keycloak-server/spi=connectionsInfinispan/provider=default/:add(properties={cacheContainer => "java:comp/env/infinispan/Keycloak"},enabled=true)
+/subsystem=keycloak-server/spi=jta-lookup/:add(default-provider=${keycloak.jta.lookup.provider:jboss})
+/subsystem=keycloak-server/spi=jta-lookup/provider=jboss/:add(enabled=true)
\ No newline at end of file
diff --git a/wildfly/server-subsystem/src/main/resources/default-config/keycloak-server-default-config.xml b/wildfly/server-subsystem/src/main/resources/default-config/keycloak-server-default-config.xml
new file mode 100644
index 0000000..e520611
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/resources/default-config/keycloak-server-default-config.xml
@@ -0,0 +1 @@
+${keycloak.server.subsys.default.config}
\ No newline at end of file
diff --git a/wildfly/server-subsystem/src/main/resources/org/keycloak/subsystem/server/extension/LocalDescriptions.properties b/wildfly/server-subsystem/src/main/resources/org/keycloak/subsystem/server/extension/LocalDescriptions.properties
index bf29d06..8d5c5a7 100755
--- a/wildfly/server-subsystem/src/main/resources/org/keycloak/subsystem/server/extension/LocalDescriptions.properties
+++ b/wildfly/server-subsystem/src/main/resources/org/keycloak/subsystem/server/extension/LocalDescriptions.properties
@@ -15,7 +15,38 @@
 # limitations under the License.
 #
 
+keycloak-server.migrate-json=Migrate keycloak-server.json to standalone.xml or domain.xml
+keycloak-server.migrate-json.file=Optional local path to keycloak-server.json
+
 keycloak-server.subsystem=Keycloak subsystem
 keycloak-server.subsystem.add=Operation Adds Keycloak subsystem
 keycloak-server.subsystem.remove=Operation removes Keycloak subsystem
 keycloak-server.subsystem.web-context=Web context where Keycloak server is bound. Default value is 'auth'.
+keycloak-server.subsystem.providers=Paths to search for Keycloak provider jars.
+keycloak-server.subsystem.master-realm-name=The name of the master admin realm.
+keycloak-server.subsystem.scheduled-task-interval=The interval (in seconds) to run scheduled tasks.
+keycloak-server.subsystem.spi=A Service Provider type.
+keycloak-server.subsystem.theme=Theme configuration properties.
+
+keycloak-server.theme=Theme configuration properties.
+keycloak-server.theme.add=Add the theme config properties.
+keycloak-server.theme.remove=Remove the theme config properties.
+keycloak-server.theme.staticMaxAge=Maximum time the browser should cache theme resources. A value of -1 will disable caching.
+keycloak-server.theme.cacheThemes=If true, themes are cached.
+keycloak-server.theme.cacheTemplates=If true, theme templates are cached.
+keycloak-server.theme.welcomeTheme=The welcome theme.
+keycloak-server.theme.default=The default theme to use if no theme is specified for a realm.
+keycloak-server.theme.dir=Directory where themes can be located.
+keycloak-server.theme.modules=List of modules containing themes.
+
+keycloak-server.spi=A Service Provider type.
+keycloak-server.spi.add=Add an spi.
+keycloak-server.spi.remove=Remove an spi.
+keycloak-server.spi.default-provider=The default provider for the spi.
+keycloak-server.spi.provider=A provider for the spi.
+
+keycloak-server.provider=A provider for the spi.
+keycloak-server.provider.add=Add a provider.
+keycloak-server.provider.remove=Remove a provider.
+keycloak-server.provider.enabled=Enable or disable the provider.
+keycloak-server.provider.properties=The properties for the provider.
diff --git a/wildfly/server-subsystem/src/main/resources/schema/wildfly-keycloak-server_1_1.xsd b/wildfly/server-subsystem/src/main/resources/schema/wildfly-keycloak-server_1_1.xsd
index df02124..fa9f9e0 100755
--- a/wildfly/server-subsystem/src/main/resources/schema/wildfly-keycloak-server_1_1.xsd
+++ b/wildfly/server-subsystem/src/main/resources/schema/wildfly-keycloak-server_1_1.xsd
@@ -1,21 +1,21 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
 <!--
-  ~ 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.
-  -->
+~ 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.
+-->
 
 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
            targetNamespace="urn:jboss:domain:keycloak-server:1.1"
@@ -35,8 +35,73 @@
                 ]]>
             </xs:documentation>
         </xs:annotation>
-        <xs:choice minOccurs="0" maxOccurs="1">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
             <xs:element name="web-context" type="xs:string" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="providers" type="providersLocationType" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="master-realm-name" type="xs:string" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="scheduled-task-interval" type="xs:long" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="theme" type="themeDefaultsType" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="spi" type="spiType" minOccurs="0" maxOccurs="unbounded"/>
         </xs:choice>
+        
+    </xs:complexType>
+    
+    <xs:complexType name="providersLocationType">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+            <xs:element name="provider" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
+        </xs:choice>
+    </xs:complexType>
+    
+    <xs:complexType name="themeDefaultsType">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+            <xs:element name="staticMaxAge" type="xs:long" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="cacheThemes" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="cacheTemplates" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="welcomeTheme" type="xs:string" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="default" type="xs:string" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="dir" type="xs:string" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="modules" type="modulesType" minOccurs="0" maxOccurs="1"/>
+        </xs:choice>
+    </xs:complexType>
+    
+    <xs:complexType name="modulesType">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+            <xs:element name="module" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
+        </xs:choice>
+    </xs:complexType>
+    
+    <xs:complexType name="spiType">
+        <xs:sequence>
+            <xs:element name="default-provider" type="xs:string" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="provider" type="providerType" minOccurs="0" maxOccurs="unbounded"/>
+        </xs:sequence>
+        <xs:attribute name="name" use="required"/>
+    </xs:complexType>
+    
+    <xs:complexType name="providerType">
+        <xs:sequence>
+            <xs:element name="properties" type="properties" minOccurs="0" maxOccurs="1"/>
+        </xs:sequence>
+        <xs:attribute name="name" use="required"/>
+        <xs:attribute name="enabled" use="required"/>
+    </xs:complexType>
+    
+    <xs:complexType name="properties">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+            <xs:element name="property" type="propertyType" maxOccurs="unbounded"/>
+            <xs:element name="list" type="listType" maxOccurs="unbounded"/>
+        </xs:choice>
+    </xs:complexType>
+
+    <xs:complexType name="propertyType">
+        <xs:attribute name="name" use="required"/>
+        <xs:attribute name="value" use="optional"/>
+    </xs:complexType>
+    
+    <xs:complexType name="listType">
+        <xs:sequence>
+            <xs:element name="value" minOccurs="0" maxOccurs="unbounded"/>
+        </xs:sequence>
+        <xs:attribute name="name" use="required"/>
     </xs:complexType>
 </xs:schema>
diff --git a/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-server.xml b/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-server.xml
index c269c31..67bf03e 100644
--- a/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-server.xml
+++ b/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-server.xml
@@ -1,25 +1,23 @@
 <?xml version='1.0' encoding='UTF-8'?>
 <!--
-  ~ 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.
-  -->
+~ 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.
+-->
 
 <!--  Template used by WildFly build when directed to include Keycloak subsystem in a configuration. -->
 <config>
-   <extension-module>org.keycloak.keycloak-server-subsystem</extension-module>
-   <subsystem xmlns="urn:jboss:domain:keycloak-server:1.1">
-      <web-context>auth</web-context>
-   </subsystem>
+    <extension-module>org.keycloak.keycloak-server-subsystem</extension-module>
+    ${keycloak.server.subsys.default.config}
 </config>
diff --git a/wildfly/server-subsystem/src/test/java/org/keycloak/subsystem/server/extension/JsonConfigConverterTestCase.java b/wildfly/server-subsystem/src/test/java/org/keycloak/subsystem/server/extension/JsonConfigConverterTestCase.java
new file mode 100644
index 0000000..4c6bf04
--- /dev/null
+++ b/wildfly/server-subsystem/src/test/java/org/keycloak/subsystem/server/extension/JsonConfigConverterTestCase.java
@@ -0,0 +1,446 @@
+/*
+ * 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.server.extension;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.jboss.as.controller.PathAddress;
+import org.jboss.as.controller.PathElement;
+import org.jboss.dmr.ModelNode;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADDRESS;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class JsonConfigConverterTestCase {
+
+    private final PathElement domainRoot = PathElement.pathElement("profile", "auth-server-clustered");
+    private final PathAddress domainAddress = PathAddress.pathAddress(domainRoot)
+                                                         .append(KeycloakExtension.PATH_SUBSYSTEM);
+    private final PathAddress standaloneAddress = PathAddress.pathAddress(KeycloakExtension.PATH_SUBSYSTEM);
+
+    @Test
+    public void testConvertJsonStandaloneWithModules() throws Exception {
+        String json = basicJsonConfig(true);
+        List<ModelNode> expResult = expectedOperations(true, false);
+        
+        List<ModelNode> result = JsonConfigConverter.convertJsonConfig(json, standaloneAddress);
+        assertEquals(expResult, result);
+    }
+    
+    @Test
+    public void testConvertJsonStandaloneWithoutModules() throws Exception {
+        String json = basicJsonConfig(false);
+        List<ModelNode> expResult = expectedOperations(false, false);
+        
+        List<ModelNode> result = JsonConfigConverter.convertJsonConfig(json, standaloneAddress);
+        assertEquals(expResult, result);
+    }
+    
+    @Test
+    public void testConvertJsonDomainWithModules() throws Exception {
+        String json = basicJsonConfig(true);
+        List<ModelNode> expResult = expectedOperations(true, true);
+        
+        List<ModelNode> result = JsonConfigConverter.convertJsonConfig(json, domainAddress);
+        assertEquals(expResult, result);
+    }
+    
+    @Test
+    public void testConvertJsonDomainWithoutModules() throws Exception {
+        String json = basicJsonConfig(false);
+        List<ModelNode> expResult = expectedOperations(false, true);
+        
+        List<ModelNode> result = JsonConfigConverter.convertJsonConfig(json, domainAddress);
+        assertEquals(expResult, result);
+    }
+    
+    private String basicJsonConfig(boolean includeModules) {
+        String basicConfig = 
+              "{\n"
+            + "    \"providers\": [\n"
+            + "        \"classpath:${jboss.home.dir}/providers/*\"\n"
+            + "    ],\n"
+            + "\n"
+            + "    \"admin\": {\n"
+            + "        \"realm\": \"master\"\n"
+            + "    },\n"
+            + "\n"
+            + "    \"eventsStore\": {\n"
+            + "        \"provider\": \"jpa\",\n"
+            + "        \"jpa\": {\n"
+            + "            \"exclude-events\": [ \"REFRESH_TOKEN\" ]\n"
+            + "        }\n"
+            + "    },\n"
+            + "\n"
+            + "    \"realm\": {\n"
+            + "        \"provider\": \"jpa\"\n"
+            + "    },\n"
+            + "\n"
+            + "    \"user\": {\n"
+            + "        \"provider\": \"jpa\"\n"
+            + "    },\n"
+            + "\n"
+            + "    \"userCache\": {\n"
+            + "        \"default\" : {\n"
+            + "            \"enabled\": true\n"
+            + "        }\n"
+            + "    },\n"
+            + "\n"
+            + "    \"userSessionPersister\": {\n"
+            + "        \"provider\": \"jpa\"\n"
+            + "    },\n"
+            + "\n"
+            + "    \"authorizationPersister\": {\n"
+            + "        \"provider\": \"jpa\"\n"
+            + "    },\n"
+            + "\n"
+            + "    \"timer\": {\n"
+            + "        \"provider\": \"basic\"\n"
+            + "    },\n"
+            + "\n"
+            + "    \"theme\": {\n"
+            + "        \"staticMaxAge\": 2592001,\n"
+            + "        \"cacheTemplates\": false,\n"
+            + "        \"cacheThemes\": false,\n"
+            + "        \"welcomeTheme\": \"welcome\",\n"
+            + "        \"default\": \"default\",\n"
+            + "        \"folder\": {\n"
+            + "          \"dir\": \"${jboss.home.dir}/themes\"\n";
+            
+        
+        if (includeModules) {
+            basicConfig +=
+              "        },\n"
+            + "        \"module\": {\n"
+            + "          \"modules\": [ \"org.keycloak.example.themes\" ]\n"
+            + "         }\n";
+        } else {
+            basicConfig +=
+              "        }\n";
+        }
+        
+        basicConfig +=
+              "     },\n"
+            + "\n"
+            + "    \"scheduled\": {\n"
+            + "        \"interval\": 900\n"
+            + "    },\n"
+            + "\n"
+            + "    \"connectionsHttpClient\": {\n"
+            + "        \"default\": {}\n"
+            + "    },\n"
+            + "\n"
+            + "    \"connectionsJpa\": {\n"
+            + "        \"default\": {\n"
+            + "            \"dataSource\": \"java:jboss/datasources/KeycloakDS\",\n"
+            + "            \"databaseSchema\": \"update\"\n"
+            + "        }\n"
+            + "    },\n"
+            + "\n"
+            + "    \"realmCache\": {\n"
+            + "        \"default\" : {\n"
+            + "            \"enabled\": true\n"
+            + "        }\n"
+            + "    },\n"
+            + "\n"
+            + "    \"connectionsInfinispan\": {\n"
+            + "        \"provider\": \"default\",\n"
+            + "        \"default\": {\n"
+            + "            \"cacheContainer\" : \"java:comp/env/infinispan/Keycloak\"\n"
+            + "        }\n"
+            + "    }\n"
+            + "}";
+        
+        return basicConfig;
+    }
+    
+    private List<ModelNode> expectedOperations(boolean includeModules, boolean isDomain) {
+        List<ModelNode> ops = new ArrayList<>();
+        
+        ops.add(ModelNode.fromString(
+            "{\n" +
+            "    \"operation\" => \"write-attribute\",\n" +
+            "    \"address\" => [(\"subsystem\" => \"keycloak-server\")],\n" +
+            "    \"name\" => \"master-realm-name\",\n" +
+            "    \"value\" => \"master\"\n" +
+            "}"
+        ));
+        
+        ops.add(ModelNode.fromString(
+            "{\n" +
+            "    \"operation\" => \"write-attribute\",\n" +
+            "    \"address\" => [(\"subsystem\" => \"keycloak-server\")],\n" +
+            "    \"name\" => \"scheduled-task-interval\",\n" +
+            "    \"value\" => 900L\n" +
+            "}"
+        ));
+        
+        ops.add(ModelNode.fromString(
+            "{\n" +
+            "    \"operation\" => \"write-attribute\",\n" +
+            "    \"address\" => [(\"subsystem\" => \"keycloak-server\")],\n" +
+            "    \"name\" => \"providers\",\n" +
+            "    \"value\" => [\"classpath:${jboss.home.dir}/providers/*\"]\n" +
+            "}"
+        ));
+        
+        if (includeModules) {
+            ops.add(ModelNode.fromString(
+                "{\n" +
+                "    \"operation\" => \"add\",\n" +
+                "    \"address\" => [\n" +
+                "        (\"subsystem\" => \"keycloak-server\"),\n" +
+                "        (\"theme\" => \"defaults\")\n" +
+                "    ],\n" +
+                "    \"staticMaxAge\" => 2592001L,\n" +
+                "    \"cacheTemplates\" => false,\n" +
+                "    \"cacheThemes\" => false,\n" +
+                "    \"dir\" => \"${jboss.home.dir}/themes\",\n" +
+                "    \"welcomeTheme\" => \"welcome\",\n" +
+                "    \"default\" => \"default\",\n" +
+                "    \"modules\" => [\"org.keycloak.example.themes\"]\n" +
+                "}"
+            ));
+        } else {
+            ops.add(ModelNode.fromString(
+                "{\n" +
+                "    \"operation\" => \"add\",\n" +
+                "    \"address\" => [\n" +
+                "        (\"subsystem\" => \"keycloak-server\"),\n" +
+                "        (\"theme\" => \"defaults\")\n" +
+                "    ],\n" +
+                "    \"staticMaxAge\" => 2592001L,\n" +
+                "    \"cacheTemplates\" => false,\n" +
+                "    \"cacheThemes\" => false,\n" +
+                "    \"dir\" => \"${jboss.home.dir}/themes\",\n" +
+                "    \"welcomeTheme\" => \"welcome\",\n" +
+                "    \"default\" => \"default\",\n" +
+                "}"
+            ));
+        }
+        
+        ops.add(ModelNode.fromString(
+            "{\n" +
+            "    \"operation\" => \"add\",\n" +
+            "    \"address\" => [\n" +
+            "        (\"subsystem\" => \"keycloak-server\"),\n" +
+            "        (\"spi\" => \"eventsStore\")\n" +
+            "    ],\n" +
+            "    \"default-provider\" => \"jpa\"\n" +
+            "}"
+        ));
+        
+        ops.add(ModelNode.fromString(
+            "{\n" +
+            "    \"operation\" => \"add\",\n" +
+            "    \"address\" => [\n" +
+            "        (\"subsystem\" => \"keycloak-server\"),\n" +
+            "        (\"spi\" => \"eventsStore\"),\n" +
+            "        (\"provider\" => \"jpa\")\n" +
+            "    ],\n" +
+            "    \"properties\" => {\"exclude-events\" => \"[\\\"REFRESH_TOKEN\\\"]\"},\n" +
+            "    \"enabled\" => true\n" +
+            "}"
+        ));
+        
+        ops.add(ModelNode.fromString(
+            "{\n" +
+            "    \"operation\" => \"add\",\n" +
+            "    \"address\" => [\n" +
+            "        (\"subsystem\" => \"keycloak-server\"),\n" +
+            "        (\"spi\" => \"realm\")\n" +
+            "    ],\n" +
+            "    \"default-provider\" => \"jpa\"\n" +
+            "}"
+        ));
+        
+        ops.add(ModelNode.fromString(
+            "{\n" +
+            "    \"operation\" => \"add\",\n" +
+            "    \"address\" => [\n" +
+            "        (\"subsystem\" => \"keycloak-server\"),\n" +
+            "        (\"spi\" => \"user\")\n" +
+            "    ],\n" +
+            "    \"default-provider\" => \"jpa\"\n" +
+            "}"
+        ));
+        
+        ops.add(ModelNode.fromString(
+            "{\n" +
+            "    \"operation\" => \"add\",\n" +
+            "    \"address\" => [\n" +
+            "        (\"subsystem\" => \"keycloak-server\"),\n" +
+            "        (\"spi\" => \"userCache\")\n" +
+            "    ]\n" +
+            "}"
+        ));
+        
+        ops.add(ModelNode.fromString(
+            "{\n" +
+            "    \"operation\" => \"add\",\n" +
+            "    \"address\" => [\n" +
+            "        (\"subsystem\" => \"keycloak-server\"),\n" +
+            "        (\"spi\" => \"userCache\"),\n" +
+            "        (\"provider\" => \"default\")\n" +
+            "    ],\n" +
+            "    \"enabled\" => true\n" +
+            "}"
+        ));
+        
+        ops.add(ModelNode.fromString(
+            "{\n" +
+            "    \"operation\" => \"add\",\n" +
+            "    \"address\" => [\n" +
+            "        (\"subsystem\" => \"keycloak-server\"),\n" +
+            "        (\"spi\" => \"userSessionPersister\")\n" +
+            "    ],\n" +
+            "    \"default-provider\" => \"jpa\"\n" +
+            "}"
+        ));
+        
+        ops.add(ModelNode.fromString(
+            "{\n" +
+            "    \"operation\" => \"add\",\n" +
+            "    \"address\" => [\n" +
+            "        (\"subsystem\" => \"keycloak-server\"),\n" +
+            "        (\"spi\" => \"authorizationPersister\")\n" +
+            "    ],\n" +
+            "    \"default-provider\" => \"jpa\"\n" +
+            "}"
+        ));
+        
+        ops.add(ModelNode.fromString(
+            "{\n" +
+            "    \"operation\" => \"add\",\n" +
+            "    \"address\" => [\n" +
+            "        (\"subsystem\" => \"keycloak-server\"),\n" +
+            "        (\"spi\" => \"timer\")\n" +
+            "    ],\n" +
+            "    \"default-provider\" => \"basic\"\n" +
+            "}"
+        ));
+        
+        ops.add(ModelNode.fromString(
+            "{\n" +
+            "    \"operation\" => \"add\",\n" +
+            "    \"address\" => [\n" +
+            "        (\"subsystem\" => \"keycloak-server\"),\n" +
+            "        (\"spi\" => \"connectionsHttpClient\")\n" +
+            "    ]\n" +
+            "}"
+        ));
+        
+        ops.add(ModelNode.fromString(
+            "{\n" +
+            "    \"operation\" => \"add\",\n" +
+            "    \"address\" => [\n" +
+            "        (\"subsystem\" => \"keycloak-server\"),\n" +
+            "        (\"spi\" => \"connectionsHttpClient\"),\n" +
+            "        (\"provider\" => \"default\")\n" +
+            "    ],\n" +
+            "    \"enabled\" => true\n" +
+            "}"
+        ));
+        
+        ops.add(ModelNode.fromString(
+            "{\n" +
+            "    \"operation\" => \"add\",\n" +
+            "    \"address\" => [\n" +
+            "        (\"subsystem\" => \"keycloak-server\"),\n" +
+            "        (\"spi\" => \"connectionsJpa\")\n" +
+            "    ]\n" +
+            "}"
+        ));
+        
+        ops.add(ModelNode.fromString(
+            "{\n" +
+            "    \"operation\" => \"add\",\n" +
+            "    \"address\" => [\n" +
+            "        (\"subsystem\" => \"keycloak-server\"),\n" +
+            "        (\"spi\" => \"connectionsJpa\"),\n" +
+            "        (\"provider\" => \"default\")\n" +
+            "    ],\n" +
+            "    \"properties\" => {\n" +
+            "        \"dataSource\" => \"java:jboss/datasources/KeycloakDS\",\n" +
+            "        \"databaseSchema\" => \"update\"\n" +
+            "    },\n" +
+            "    \"enabled\" => true\n" +
+            "}"
+        ));
+        
+        ops.add(ModelNode.fromString(
+            "{\n" +
+            "    \"operation\" => \"add\",\n" +
+            "    \"address\" => [\n" +
+            "        (\"subsystem\" => \"keycloak-server\"),\n" +
+            "        (\"spi\" => \"realmCache\")\n" +
+            "    ]\n" +
+            "}"
+        ));
+        
+        ops.add(ModelNode.fromString(
+            "{\n" +
+            "    \"operation\" => \"add\",\n" +
+            "    \"address\" => [\n" +
+            "        (\"subsystem\" => \"keycloak-server\"),\n" +
+            "        (\"spi\" => \"realmCache\"),\n" +
+            "        (\"provider\" => \"default\")\n" +
+            "    ],\n" +
+            "    \"enabled\" => true\n" +
+            "}"
+        ));
+        
+        ops.add(ModelNode.fromString(
+            "{\n" +
+            "    \"operation\" => \"add\",\n" +
+            "    \"address\" => [\n" +
+            "        (\"subsystem\" => \"keycloak-server\"),\n" +
+            "        (\"spi\" => \"connectionsInfinispan\")\n" +
+            "    ],\n" +
+            "    \"default-provider\" => \"default\"\n" +
+            "}"
+        ));
+        
+        ops.add(ModelNode.fromString(
+            "{\n" +
+            "    \"operation\" => \"add\",\n" +
+            "    \"address\" => [\n" +
+            "        (\"subsystem\" => \"keycloak-server\"),\n" +
+            "        (\"spi\" => \"connectionsInfinispan\"),\n" +
+            "        (\"provider\" => \"default\")\n" +
+            "    ],\n" +
+            "    \"properties\" => {\"cacheContainer\" => \"java:comp/env/infinispan/Keycloak\"},\n" +
+            "    \"enabled\" => true\n" +
+            "}"
+        ));
+
+        if (isDomain) { // prepend the domain root
+            for (ModelNode op : ops) {
+                PathAddress addr = PathAddress.pathAddress(op.get(ADDRESS));
+                PathAddress domainAddr = PathAddress.pathAddress(domainRoot).append(addr);
+                op.get(ADDRESS).set(domainAddr.toModelNode());
+            }
+        }
+        
+        return ops;
+    }
+}
\ No newline at end of file
diff --git a/wildfly/server-subsystem/src/test/java/org/keycloak/subsystem/server/extension/SubsystemParsingTestCase.java b/wildfly/server-subsystem/src/test/java/org/keycloak/subsystem/server/extension/SubsystemParsingTestCase.java
index c84acc2..0bc4110 100755
--- a/wildfly/server-subsystem/src/test/java/org/keycloak/subsystem/server/extension/SubsystemParsingTestCase.java
+++ b/wildfly/server-subsystem/src/test/java/org/keycloak/subsystem/server/extension/SubsystemParsingTestCase.java
@@ -17,10 +17,9 @@
 package org.keycloak.subsystem.server.extension;
 
 import java.io.IOException;
+import java.util.Properties;
 
 import org.jboss.as.subsystem.test.AbstractSubsystemBaseTest;
-import org.jboss.dmr.ModelNode;
-import org.junit.Test;
 
 /**
  * Tests all management expects for subsystem, parsing, marshaling, model definition and other
@@ -38,14 +37,14 @@ public class SubsystemParsingTestCase extends AbstractSubsystemBaseTest {
         super(KeycloakExtension.SUBSYSTEM_NAME, new KeycloakExtension());
     }
 
-    @Test
-    public void testJson() throws Exception {
-        ModelNode node = new ModelNode();
-        node.get("web-context").set("auth");
-
-        System.out.println("json=" + node.toJSONString(false));
+    @Override
+    protected Properties getResolvedProperties() {
+        Properties properties = new Properties();
+        properties.put("jboss.home.dir", System.getProperty("java.io.tmpdir"));
+        properties.put("keycloak.jta.lookup.provider", "jboss");
+        return properties;
     }
-
+    
     @Override
     protected String getSubsystemXml() throws IOException {
         return readResource("keycloak-server-1.1.xml");
diff --git a/wildfly/server-subsystem/src/test/resources/org/keycloak/subsystem/server/extension/keycloak-server-1.1.xml b/wildfly/server-subsystem/src/test/resources/org/keycloak/subsystem/server/extension/keycloak-server-1.1.xml
index 103e162..e520611 100644
--- a/wildfly/server-subsystem/src/test/resources/org/keycloak/subsystem/server/extension/keycloak-server-1.1.xml
+++ b/wildfly/server-subsystem/src/test/resources/org/keycloak/subsystem/server/extension/keycloak-server-1.1.xml
@@ -1,20 +1 @@
-<!--
-  ~ 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.
-  -->
-
-<subsystem xmlns="urn:jboss:domain:keycloak-server:1.1">
-    <web-context>auth</web-context>
-</subsystem>
\ No newline at end of file
+${keycloak.server.subsys.default.config}
\ No newline at end of file