keycloak-aplcache

Changes

.travis.yml 2(+1 -1)

pom.xml 22(+14 -8)

README.md 6(+3 -3)

Details

.travis.yml 2(+1 -1)

diff --git a/.travis.yml b/.travis.yml
index 57147b0..ec6ed80 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,7 +25,7 @@ before_script:
   - export MAVEN_SKIP_RC=true
 
 install: 
-  - travis_wait 60 mvn install --no-snapshot-updates -Pdistribution -DskipTests=true -B -V -q
+  - travis_wait 60 mvn install --no-snapshot-updates -Pdistribution -DskipTestsuite -B -V -q
 
 script:
   - ./travis-run-tests.sh $TESTS
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/AuthenticatedActionsHandler.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/AuthenticatedActionsHandler.java
index 472afb7..7f86ba1 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/AuthenticatedActionsHandler.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/AuthenticatedActionsHandler.java
@@ -101,6 +101,7 @@ public class AuthenticatedActionsHandler {
         if (!deployment.isCors()) return false;
         KeycloakSecurityContext securityContext = facade.getSecurityContext();
         String origin = facade.getRequest().getHeader(CorsHeaders.ORIGIN);
+        String exposeHeaders = deployment.getCorsExposedHeaders();
         String requestOrigin = UriUtils.getOrigin(facade.getRequest().getURI());
         log.debugv("Origin: {0} uri: {1}", origin, facade.getRequest().getURI());
         if (securityContext != null && origin != null && !origin.equals(requestOrigin)) {
@@ -124,6 +125,9 @@ public class AuthenticatedActionsHandler {
             facade.getResponse().setStatus(200);
             facade.getResponse().setHeader(CorsHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, origin);
             facade.getResponse().setHeader(CorsHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
+            if (exposeHeaders != null) {
+                facade.getResponse().setHeader(CorsHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, exposeHeaders);
+            }
         } else {
             log.debugv("cors validation not needed as we're not a secure session or origin header was null: {0}", facade.getRequest().getURI());
         }
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/CorsHeaders.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/CorsHeaders.java
index 715af3d..416c392 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/CorsHeaders.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/CorsHeaders.java
@@ -30,4 +30,5 @@ public interface CorsHeaders {
     String ORIGIN = "Origin";
     String ACCESS_CONTROL_REQUEST_METHOD = "Access-Control-Request-Method";
     String ACCESS_CONTROL_REQUEST_HEADERS = "Access-Control-Request-Headers";
+    String ACCESS_CONTROL_EXPOSE_HEADERS = "Access-Control-Expose-Headers";
 }
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java
index ba7bc5d..31f842c 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java
@@ -73,6 +73,7 @@ public class KeycloakDeployment {
     protected int corsMaxAge = -1;
     protected String corsAllowedHeaders;
     protected String corsAllowedMethods;
+    protected String corsExposedHeaders;
     protected boolean exposeToken;
     protected boolean alwaysRefreshToken;
     protected boolean registerNodeAtStartup;
@@ -325,6 +326,14 @@ public class KeycloakDeployment {
         this.corsAllowedMethods = corsAllowedMethods;
     }
 
+    public String getCorsExposedHeaders() {
+        return corsExposedHeaders;
+    }
+
+    public void setCorsExposedHeaders(String corsExposedHeaders) {
+        this.corsExposedHeaders = corsExposedHeaders;
+    }
+
     public boolean isExposeToken() {
         return exposeToken;
     }
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java
index 2fd9276..a651753 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java
@@ -96,6 +96,7 @@ public class KeycloakDeploymentBuilder {
             deployment.setCorsMaxAge(adapterConfig.getCorsMaxAge());
             deployment.setCorsAllowedHeaders(adapterConfig.getCorsAllowedHeaders());
             deployment.setCorsAllowedMethods(adapterConfig.getCorsAllowedMethods());
+            deployment.setCorsExposedHeaders(adapterConfig.getCorsExposedHeaders());
         }
 
         // https://tools.ietf.org/html/rfc7636
diff --git a/adapters/oidc/adapter-core/src/test/java/org/keycloak/adapters/KeycloakDeploymentBuilderTest.java b/adapters/oidc/adapter-core/src/test/java/org/keycloak/adapters/KeycloakDeploymentBuilderTest.java
index 233c1ed..a4f04ec 100644
--- a/adapters/oidc/adapter-core/src/test/java/org/keycloak/adapters/KeycloakDeploymentBuilderTest.java
+++ b/adapters/oidc/adapter-core/src/test/java/org/keycloak/adapters/KeycloakDeploymentBuilderTest.java
@@ -53,6 +53,7 @@ public class KeycloakDeploymentBuilderTest {
         assertEquals(1000, deployment.getCorsMaxAge());
         assertEquals("POST, PUT, DELETE, GET", deployment.getCorsAllowedMethods());
         assertEquals("X-Custom, X-Custom2", deployment.getCorsAllowedHeaders());
+        assertEquals("X-Custom3, X-Custom4", deployment.getCorsExposedHeaders());
         assertTrue(deployment.isBearerOnly());
         assertTrue(deployment.isPublicClient());
         assertTrue(deployment.isEnableBasicAuth());
diff --git a/adapters/oidc/adapter-core/src/test/resources/keycloak.json b/adapters/oidc/adapter-core/src/test/resources/keycloak.json
index 9f0a204..f53432f 100644
--- a/adapters/oidc/adapter-core/src/test/resources/keycloak.json
+++ b/adapters/oidc/adapter-core/src/test/resources/keycloak.json
@@ -9,6 +9,7 @@
     "cors-max-age": 1000,
     "cors-allowed-methods": "POST, PUT, DELETE, GET",
     "cors-allowed-headers": "X-Custom, X-Custom2",
+    "cors-exposed-headers": "X-Custom3, X-Custom4",
     "bearer-only": true,
     "public-client": true,
     "enable-basic-auth": true,
diff --git a/adapters/oidc/js/src/main/resources/keycloak.js b/adapters/oidc/js/src/main/resources/keycloak.js
index 3f09229..89b15b8 100755
--- a/adapters/oidc/js/src/main/resources/keycloak.js
+++ b/adapters/oidc/js/src/main/resources/keycloak.js
@@ -587,7 +587,7 @@
 
                 req.onreadystatechange = function () {
                     if (req.readyState == 4) {
-                        if (req.status == 200) {
+                        if (req.status == 200 || fileLoaded(req)) {
                             var config = JSON.parse(req.responseText);
 
                             kc.authServerUrl = config['auth-server-url'];
@@ -633,6 +633,10 @@
             return promise.promise;
         }
 
+        function fileLoaded(xhr) {
+            return xhr.status == 0 && xhr.responseText && xhr.responseURL.startsWith('file:');
+        }
+
         function setToken(token, refreshToken, idToken, timeLocal) {
             if (kc.tokenTimeoutHandle) {
                 clearTimeout(kc.tokenTimeoutHandle);
diff --git a/adapters/oidc/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/SharedAttributeDefinitons.java b/adapters/oidc/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/SharedAttributeDefinitons.java
index e4ff988..a7676ea 100755
--- a/adapters/oidc/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/SharedAttributeDefinitons.java
+++ b/adapters/oidc/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/SharedAttributeDefinitons.java
@@ -124,6 +124,12 @@ public class SharedAttributeDefinitons {
             .setAllowExpression(true)
             .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
             .build();
+    protected static final SimpleAttributeDefinition CORS_EXPOSED_HEADERS =
+            new SimpleAttributeDefinitionBuilder("cors-exposed-headers", ModelType.STRING, true)
+            .setXmlName("cors-exposed-headers")
+            .setAllowExpression(true)
+            .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
+            .build();
     protected static final SimpleAttributeDefinition EXPOSE_TOKEN =
             new SimpleAttributeDefinitionBuilder("expose-token", ModelType.BOOLEAN, true)
                     .setXmlName("expose-token")
@@ -191,6 +197,7 @@ public class SharedAttributeDefinitons {
         ATTRIBUTES.add(CORS_MAX_AGE);
         ATTRIBUTES.add(CORS_ALLOWED_HEADERS);
         ATTRIBUTES.add(CORS_ALLOWED_METHODS);
+        ATTRIBUTES.add(CORS_EXPOSED_HEADERS);
         ATTRIBUTES.add(EXPOSE_TOKEN);
         ATTRIBUTES.add(AUTH_SERVER_URL_FOR_BACKEND_REQUESTS);
         ATTRIBUTES.add(ALWAYS_REFRESH_TOKEN);
diff --git a/adapters/oidc/wildfly/wf8-subsystem/src/main/resources/org/keycloak/subsystem/wf8/extension/LocalDescriptions.properties b/adapters/oidc/wildfly/wf8-subsystem/src/main/resources/org/keycloak/subsystem/wf8/extension/LocalDescriptions.properties
index 6244b0c..71101a1 100755
--- a/adapters/oidc/wildfly/wf8-subsystem/src/main/resources/org/keycloak/subsystem/wf8/extension/LocalDescriptions.properties
+++ b/adapters/oidc/wildfly/wf8-subsystem/src/main/resources/org/keycloak/subsystem/wf8/extension/LocalDescriptions.properties
@@ -39,6 +39,7 @@ keycloak.realm.client-key-password=n/a
 keycloak.realm.cors-max-age=CORS max-age header
 keycloak.realm.cors-allowed-headers=CORS allowed headers
 keycloak.realm.cors-allowed-methods=CORS allowed methods
+keycloak.realm.cors-exposed-headers=CORS exposed headers
 keycloak.realm.expose-token=Enable secure URL that exposes access token
 keycloak.realm.auth-server-url-for-backend-requests=URL to use to make background calls to auth server
 keycloak.realm.always-refresh-token=Refresh token on every single web request
@@ -73,6 +74,7 @@ keycloak.secure-deployment.client-key-password=n/a
 keycloak.secure-deployment.cors-max-age=CORS max-age header
 keycloak.secure-deployment.cors-allowed-headers=CORS allowed headers
 keycloak.secure-deployment.cors-allowed-methods=CORS allowed methods
+keycloak.secure-deployment.cors-exposed-headers=CORS exposed headers
 keycloak.secure-deployment.expose-token=Enable secure URL that exposes access token
 keycloak.secure-deployment.auth-server-url-for-backend-requests=URL to use to make background calls to auth server
 keycloak.secure-deployment.always-refresh-token=Refresh token on every single web request
diff --git a/adapters/oidc/wildfly/wf8-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd b/adapters/oidc/wildfly/wf8-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd
index e9839bc..cc51ec4 100755
--- a/adapters/oidc/wildfly/wf8-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd
+++ b/adapters/oidc/wildfly/wf8-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd
@@ -58,6 +58,7 @@
             <xs:element name="disable-trust-manager" type="xs:boolean" minOccurs="0" maxOccurs="1" />
             <xs:element name="ssl-required" type="xs:string" minOccurs="0" maxOccurs="1" />
             <xs:element name="cors-allowed-methods" type="xs:string" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="cors-exposed-headers" type="xs:string" minOccurs="0" maxOccurs="1"/>
             <xs:element name="realm-public-key" type="xs:string" minOccurs="1" maxOccurs="1"/>
             <xs:element name="auth-server-url-for-backend-requests" type="xs:string" minOccurs="0" maxOccurs="1"/>
             <xs:element name="always-refresh-token" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
@@ -88,6 +89,7 @@
             <xs:element name="cors-allowed-methods" type="xs:string" minOccurs="0" maxOccurs="1"/>
             <xs:element name="bearer-only" type="xs:boolean" minOccurs="0" maxOccurs="1" />
             <xs:element name="cors-allowed-headers" type="xs:string" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="cors-exposed-headers" type="xs:string" minOccurs="0" maxOccurs="1"/>
             <xs:element name="resource" type="xs:string" minOccurs="0" maxOccurs="1" />
             <xs:element name="truststore" type="xs:string" minOccurs="0" maxOccurs="1"/>
             <xs:element name="truststore-password" type="xs:string" minOccurs="0" maxOccurs="1"/>
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/SharedAttributeDefinitons.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/SharedAttributeDefinitons.java
index 02d14e5..fafed42 100755
--- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/SharedAttributeDefinitons.java
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/SharedAttributeDefinitons.java
@@ -124,6 +124,12 @@ public class SharedAttributeDefinitons {
             .setAllowExpression(true)
             .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
             .build();
+    protected static final SimpleAttributeDefinition CORS_EXPOSED_HEADERS =
+            new SimpleAttributeDefinitionBuilder("cors-exposed-headers", ModelType.STRING, true)
+            .setXmlName("cors-exposed-headers")
+            .setAllowExpression(true)
+            .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
+            .build();
     protected static final SimpleAttributeDefinition EXPOSE_TOKEN =
             new SimpleAttributeDefinitionBuilder("expose-token", ModelType.BOOLEAN, true)
                     .setXmlName("expose-token")
@@ -175,6 +181,8 @@ public class SharedAttributeDefinitons {
 
 
 
+
+
     protected static final List<SimpleAttributeDefinition> ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
     static {
         ATTRIBUTES.add(REALM_PUBLIC_KEY);
@@ -192,6 +200,7 @@ public class SharedAttributeDefinitons {
         ATTRIBUTES.add(CORS_MAX_AGE);
         ATTRIBUTES.add(CORS_ALLOWED_HEADERS);
         ATTRIBUTES.add(CORS_ALLOWED_METHODS);
+        ATTRIBUTES.add(CORS_EXPOSED_HEADERS);
         ATTRIBUTES.add(EXPOSE_TOKEN);
         ATTRIBUTES.add(AUTH_SERVER_URL_FOR_BACKEND_REQUESTS);
         ATTRIBUTES.add(ALWAYS_REFRESH_TOKEN);
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/extension/LocalDescriptions.properties b/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/extension/LocalDescriptions.properties
index c0ca52e..a297c1d 100755
--- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/extension/LocalDescriptions.properties
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/extension/LocalDescriptions.properties
@@ -39,6 +39,7 @@ keycloak.realm.client-key-password=n/a
 keycloak.realm.cors-max-age=CORS max-age header
 keycloak.realm.cors-allowed-headers=CORS allowed headers
 keycloak.realm.cors-allowed-methods=CORS allowed methods
+keycloak.realm.cors-exposed-headers=CORS exposed headers
 keycloak.realm.expose-token=Enable secure URL that exposes access token
 keycloak.realm.auth-server-url-for-backend-requests=URL to use to make background calls to auth server
 keycloak.realm.always-refresh-token=Refresh token on every single web request
@@ -74,6 +75,7 @@ keycloak.secure-deployment.client-key-password=n/a
 keycloak.secure-deployment.cors-max-age=CORS max-age header
 keycloak.secure-deployment.cors-allowed-headers=CORS allowed headers
 keycloak.secure-deployment.cors-allowed-methods=CORS allowed methods
+keycloak.secure-deployment.cors-exposed-headers=CORS exposed headers
 keycloak.secure-deployment.expose-token=Enable secure URL that exposes access token
 keycloak.secure-deployment.auth-server-url-for-backend-requests=URL to use to make background calls to auth server
 keycloak.secure-deployment.always-refresh-token=Refresh token on every single web request
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd b/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd
index 84399a3..8118209 100755
--- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd
@@ -58,6 +58,7 @@
             <xs:element name="disable-trust-manager" type="xs:boolean" minOccurs="0" maxOccurs="1" />
             <xs:element name="ssl-required" type="xs:string" minOccurs="0" maxOccurs="1" />
             <xs:element name="cors-allowed-methods" type="xs:string" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="cors-exposed-headers" type="xs:string" minOccurs="0" maxOccurs="1"/>
             <xs:element name="realm-public-key" type="xs:string" minOccurs="1" maxOccurs="1"/>
             <xs:element name="auth-server-url-for-backend-requests" type="xs:string" minOccurs="0" maxOccurs="1"/>
             <xs:element name="always-refresh-token" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
@@ -88,6 +89,7 @@
             <xs:element name="cors-allowed-methods" type="xs:string" minOccurs="0" maxOccurs="1"/>
             <xs:element name="bearer-only" type="xs:boolean" minOccurs="0" maxOccurs="1" />
             <xs:element name="cors-allowed-headers" type="xs:string" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="cors-exposed-headers" type="xs:string" minOccurs="0" maxOccurs="1"/>
             <xs:element name="resource" type="xs:string" minOccurs="0" maxOccurs="1" />
             <xs:element name="truststore" type="xs:string" minOccurs="0" maxOccurs="1"/>
             <xs:element name="truststore-password" type="xs:string" minOccurs="0" maxOccurs="1"/>
diff --git a/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java b/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java
index f063962..ddd525b 100755
--- a/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java
+++ b/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java
@@ -29,7 +29,7 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder;
 @JsonPropertyOrder({"realm", "realm-public-key", "auth-server-url", "ssl-required",
         "resource", "public-client", "credentials",
         "use-resource-role-mappings",
-        "enable-cors", "cors-max-age", "cors-allowed-methods",
+        "enable-cors", "cors-max-age", "cors-allowed-methods", "cors-exposed-headers",
         "expose-token", "bearer-only", "autodetect-bearer-only",
         "connection-pool-size",
         "allow-any-hostname", "disable-trust-manager", "truststore", "truststore-password",
diff --git a/core/src/main/java/org/keycloak/representations/adapters/config/BaseAdapterConfig.java b/core/src/main/java/org/keycloak/representations/adapters/config/BaseAdapterConfig.java
index dfe4ae6..4a2b7e2 100755
--- a/core/src/main/java/org/keycloak/representations/adapters/config/BaseAdapterConfig.java
+++ b/core/src/main/java/org/keycloak/representations/adapters/config/BaseAdapterConfig.java
@@ -33,7 +33,7 @@ import java.util.TreeMap;
 @JsonPropertyOrder({"realm", "realm-public-key", "auth-server-url", "ssl-required",
         "resource", "public-client", "credentials",
         "use-resource-role-mappings",
-        "enable-cors", "cors-max-age", "cors-allowed-methods",
+        "enable-cors", "cors-max-age", "cors-allowed-methods", "cors-exposed-headers",
         "expose-token", "bearer-only", "autodetect-bearer-only", "enable-basic-auth"})
 public class BaseAdapterConfig extends BaseRealmConfig {
     @JsonProperty("resource")
@@ -48,6 +48,8 @@ public class BaseAdapterConfig extends BaseRealmConfig {
     protected String corsAllowedHeaders;
     @JsonProperty("cors-allowed-methods")
     protected String corsAllowedMethods;
+    @JsonProperty("cors-exposed-headers")
+    protected String corsExposedHeaders;
     @JsonProperty("expose-token")
     protected boolean exposeToken;
     @JsonProperty("bearer-only")
@@ -110,6 +112,14 @@ public class BaseAdapterConfig extends BaseRealmConfig {
          this.corsAllowedMethods = corsAllowedMethods;
      }
 
+    public String getCorsExposedHeaders() {
+        return corsExposedHeaders;
+    }
+
+    public void setCorsExposedHeaders(String corsExposedHeaders) {
+        this.corsExposedHeaders = corsExposedHeaders;
+    }
+
     public boolean isExposeToken() {
          return exposeToken;
      }
diff --git a/distribution/feature-packs/adapter-feature-pack/feature-pack-build.xml b/distribution/feature-packs/adapter-feature-pack/feature-pack-build.xml
index e249722..50ea3d7 100644
--- a/distribution/feature-packs/adapter-feature-pack/feature-pack-build.xml
+++ b/distribution/feature-packs/adapter-feature-pack/feature-pack-build.xml
@@ -17,7 +17,7 @@
 
 <build xmlns="urn:wildfly:feature-pack-build:1.0">
     <dependencies>
-        <artifact name="org.wildfly:wildfly-feature-pack" />
+        <artifact name="${feature.parent}" />
     </dependencies>
     <config>
         <standalone template="configuration/standalone/template.xml" subsystems="configuration/standalone/subsystems.xml" output-file="standalone/configuration/standalone.xml" />
diff --git a/distribution/feature-packs/adapter-feature-pack/pom.xml b/distribution/feature-packs/adapter-feature-pack/pom.xml
index e51f220..be9c989 100755
--- a/distribution/feature-packs/adapter-feature-pack/pom.xml
+++ b/distribution/feature-packs/adapter-feature-pack/pom.xml
@@ -61,12 +61,6 @@
             <groupId>org.keycloak</groupId>
             <artifactId>keycloak-authz-client</artifactId>
         </dependency>
-
-        <dependency>
-            <groupId>org.wildfly</groupId>
-            <artifactId>wildfly-feature-pack</artifactId>
-            <type>zip</type>
-        </dependency>
     </dependencies>
 
     <build>
@@ -119,4 +113,52 @@
         </plugins>
     </build>
 
+
+    <profiles>
+        <profile>
+            <id>community</id>
+            <activation>
+                <property>
+                    <name>!product</name>
+                </property>
+            </activation>
+
+            <properties>
+                <build-tools.version>${wildfly.build-tools.version}</build-tools.version>
+                <feature.parent>org.wildfly:wildfly-feature-pack</feature.parent>
+            </properties>
+
+            <dependencies>
+                <dependency>
+                    <groupId>org.wildfly</groupId>
+                    <artifactId>wildfly-feature-pack</artifactId>
+                    <type>zip</type>
+                </dependency>
+            </dependencies>
+        </profile>
+
+        <profile>
+            <id>product</id>
+            <activation>
+                <property>
+                    <name>product</name>
+                </property>
+            </activation>
+
+            <properties>
+                <build-tools.version>${eap.build-tools.version}</build-tools.version>
+                <feature.parent>org.jboss.eap:wildfly-feature-pack</feature.parent>
+            </properties>
+
+            <dependencies>
+                <dependency>
+                    <groupId>org.jboss.eap</groupId>
+                    <artifactId>wildfly-feature-pack</artifactId>
+                    <version>${eap.version}</version>
+                    <type>zip</type>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+
 </project>
diff --git a/examples/cordova/www/index.html b/examples/cordova/www/index.html
index 296986d..78646df 100644
--- a/examples/cordova/www/index.html
+++ b/examples/cordova/www/index.html
@@ -20,7 +20,7 @@
 <head>
     <title>Authentication Example</title>
 
-    <meta http-equiv="Content-Security-Policy" content="default-src *; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'unsafe-eval'">
+    <meta http-equiv="Content-Security-Policy" content="default-src * gap://ready; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'unsafe-eval'">
 
     <script type="text/javascript" charset="utf-8" src="cordova.js"></script>
     <script type="text/javascript" charset="utf-8" src="keycloak.js"></script>
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserCredentialStore.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserCredentialStore.java
index d3539fe..a1a0cdb 100644
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserCredentialStore.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserCredentialStore.java
@@ -119,7 +119,7 @@ public class JpaUserCredentialStore implements UserCredentialStore {
         entity.setUser(userRef);
         em.persist(entity);
         MultivaluedHashMap<String, String> config = cred.getConfig();
-        if (config != null || !config.isEmpty()) {
+        if (config != null && !config.isEmpty()) {
 
             for (String key : config.keySet()) {
                 List<String> values = config.getList(key);

pom.xml 22(+14 -8)

diff --git a/pom.xml b/pom.xml
index b14081a..4f128e2 100755
--- a/pom.xml
+++ b/pom.xml
@@ -45,7 +45,7 @@
         <wildfly.build-tools.version>1.1.3.Final</wildfly.build-tools.version>
         <wildfly11.version>11.0.0.Alpha1</wildfly11.version> <!-- for testing with wf11 pre-releases -->
         <wildfly11.build-tools.version>1.1.8.Final</wildfly11.build-tools.version>
-        <eap.version>7.1.0.Alpha1-redhat-16</eap.version>
+        <eap.version>7.1.0.Beta1-redhat-2</eap.version>
         <eap.build-tools.version>1.1.8.Final</eap.build-tools.version>
         <wildfly.core.version>2.0.10.Final</wildfly.core.version>
 
@@ -192,7 +192,6 @@
         <module>adapters</module>
         <module>authz</module>
         <module>examples</module>
-        <module>testsuite</module>
         <module>misc</module>
     </modules>
 
@@ -279,11 +278,6 @@
                 <version>${resteasy.version}</version>
             </dependency>
             <dependency>
-                <groupId>org.jboss.resteasy</groupId>
-                <artifactId>async-http-servlet-3.0</artifactId>
-                <version>${resteasy.version}</version>
-            </dependency>
-            <dependency>
                 <groupId>org.jboss.spec.javax.xml.bind</groupId>
                 <artifactId>jboss-jaxb-api_2.2_spec</artifactId>
                 <version>${jboss.spec.javax.xml.bind.jboss-jaxb-api_2.2_spec.version}</version>
@@ -1541,11 +1535,23 @@
                 <product.name-html>\u003Cstrong\u003ERed Hat\u003C\u002Fstrong\u003E\u003Csup\u003E\u00AE\u003C\u002Fsup\u003E Single Sign On</product.name-html>
                 <product.version>${project.version}</product.version>
                 <product.default-profile>product</product.default-profile>
-                <product.filename.version>7.1</product.filename.version>
+                <product.filename.version>7.2</product.filename.version>
             </properties>
         </profile>
 
         <profile>
+            <id>testsuite</id>
+            <activation>
+                <property>
+                    <name>!skipTestsuite</name>
+                </property>
+            </activation>
+            <modules>
+                <module>testsuite</module>
+            </modules>
+        </profile>
+
+        <profile>
             <id>distribution</id>
             <modules>
                 <module>distribution</module>

README.md 6(+3 -3)

diff --git a/README.md b/README.md
index a4454a0..c2efec5 100755
--- a/README.md
+++ b/README.md
@@ -41,10 +41,10 @@ To start Keycloak during development first build as specified above, then run:
     mvn -f testsuite/integration/pom.xml exec:java -Pkeycloak-server 
 
 
-To start Keycloak from the appliance distribution first build the distribution it as specified above, then run:
+To start Keycloak from the server distribution first build the distribution it as specified above, then run:
 
-    tar xfz distribution/appliance-dist/target/keycloak-appliance-dist-all-<VERSION>.tar.gz
-    cd keycloak-appliance-dist-all-<VERSION>/keycloak
+    tar xfz distribution/server-dist/target/keycloak-<VERSION>.tar.gz
+    cd keycloak-<VERSION>
     bin/standalone.sh
     
 To stop the server press `Ctrl + C`.
diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtil.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtil.java
index 714ee3f..bb15d23 100755
--- a/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtil.java
+++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtil.java
@@ -32,6 +32,7 @@ import org.keycloak.dom.saml.v2.assertion.StatementAbstractType;
 import org.keycloak.dom.saml.v2.assertion.SubjectType;
 import org.keycloak.dom.saml.v2.assertion.SubjectType.STSubType;
 import org.keycloak.dom.saml.v2.protocol.ResponseType;
+import org.keycloak.rotation.KeyLocator;
 import org.keycloak.saml.common.ErrorCodes;
 import org.keycloak.saml.common.PicketLinkLogger;
 import org.keycloak.saml.common.PicketLinkLoggerFactory;
@@ -287,6 +288,22 @@ public class AssertionUtil {
     }
 
     /**
+     * Given an assertion element, validate the signature.
+     */
+    public static boolean isSignatureValid(Element assertionElement, KeyLocator keyLocator) {
+        try {
+            Document doc = DocumentUtil.createDocument();
+            Node n = doc.importNode(assertionElement, true);
+            doc.appendChild(n);
+
+            return new SAML2Signature().validate(doc, keyLocator);
+        } catch (Exception e) {
+            logger.signatureAssertionValidationError(e);
+        }
+        return false;
+    }
+
+    /**
      * Check whether the assertion has expired
      *
      * @param assertion
@@ -540,7 +557,23 @@ public class AssertionUtil {
         return responseType.getAssertions().get(0).getAssertion();
     }
 
-    public static ResponseType decryptAssertion(ResponseType responseType, PrivateKey privateKey) throws ParsingException, ProcessingException, ConfigurationException {
+    public static boolean isAssertionEncrypted(ResponseType responseType) throws ProcessingException {
+        List<ResponseType.RTChoiceType> assertions = responseType.getAssertions();
+
+        if (assertions.isEmpty()) {
+            throw new ProcessingException("No assertion from response.");
+        }
+
+        ResponseType.RTChoiceType rtChoiceType = assertions.get(0);
+        return rtChoiceType.getEncryptedAssertion() != null;
+    }
+
+    /**
+     * This method modifies the given responseType, and replaces the encrypted assertion with a decrypted version.
+     *
+     * It returns the assertion element as it was decrypted. This can be used in sginature verification.
+     */
+    public static Element decryptAssertion(ResponseType responseType, PrivateKey privateKey) throws ParsingException, ProcessingException, ConfigurationException {
         SAML2Response saml2Response = new SAML2Response();
 
         Document doc = saml2Response.convert(responseType);
@@ -564,6 +597,6 @@ public class AssertionUtil {
 
         responseType.replaceAssertion(oldID, new ResponseType.RTChoiceType(assertion));
 
-        return responseType;
+        return decryptedDocumentElement;
     }
 }
\ No newline at end of file
diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/XMLTimeUtil.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/XMLTimeUtil.java
index 31916ff..8510d47 100755
--- a/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/XMLTimeUtil.java
+++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/XMLTimeUtil.java
@@ -130,7 +130,7 @@ public class XMLTimeUtil {
      * @return
      */
     public static long inMilis(int valueInMins) {
-        return valueInMins * 60 * 1000;
+        return (long) valueInMins * 60 * 1000;
     }
 
     /**
@@ -241,4 +241,4 @@ public class XMLTimeUtil {
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/saml-core/src/main/java/org/keycloak/saml/SPMetadataDescriptor.java b/saml-core/src/main/java/org/keycloak/saml/SPMetadataDescriptor.java
index 9a28137..5feda2b 100755
--- a/saml-core/src/main/java/org/keycloak/saml/SPMetadataDescriptor.java
+++ b/saml-core/src/main/java/org/keycloak/saml/SPMetadataDescriptor.java
@@ -23,10 +23,10 @@ package org.keycloak.saml;
  */
 public class SPMetadataDescriptor {
 
-    public static String getSPDescriptor(String binding, String assertionEndpoint, String logoutEndpoint, boolean wantAuthnRequestsSigned, String entityId, String nameIDPolicyFormat, String signingCerts) {
+    public static String getSPDescriptor(String binding, String assertionEndpoint, String logoutEndpoint, boolean wantAuthnRequestsSigned, boolean wantAssertionsSigned, String entityId, String nameIDPolicyFormat, String signingCerts) {
         String descriptor =
                 "<EntityDescriptor xmlns=\"urn:oasis:names:tc:SAML:2.0:metadata\" entityID=\"" + entityId + "\">\n" +
-                "    <SPSSODescriptor AuthnRequestsSigned=\"" + wantAuthnRequestsSigned + "\"\n" +
+                "    <SPSSODescriptor AuthnRequestsSigned=\"" + wantAuthnRequestsSigned + "\" WantAssertionsSigned=\"" + wantAssertionsSigned + "\"\n" +
                 "            protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol urn:oasis:names:tc:SAML:1.1:protocol http://schemas.xmlsoap.org/ws/2003/07/secext\">\n";
         if (wantAuthnRequestsSigned  && signingCerts != null) {
             descriptor += signingCerts;
diff --git a/saml-core/src/test/java/org/keycloak/saml/processing/core/parsers/saml/SAMLParserTest.java b/saml-core/src/test/java/org/keycloak/saml/processing/core/parsers/saml/SAMLParserTest.java
index 52dd7ba..713a5bd 100644
--- a/saml-core/src/test/java/org/keycloak/saml/processing/core/parsers/saml/SAMLParserTest.java
+++ b/saml-core/src/test/java/org/keycloak/saml/processing/core/parsers/saml/SAMLParserTest.java
@@ -103,9 +103,9 @@ public class SAMLParserTest {
             assertNotNull(rtChoiceType.getEncryptedAssertion());
 
             PrivateKey privateKey = DerUtils.decodePrivateKey(Base64.decode(PRIVATE_KEY));
-            ResponseType rtWithDecryptedAssertion = AssertionUtil.decryptAssertion(resp, privateKey);
+            AssertionUtil.decryptAssertion(resp, privateKey);
 
-            rtChoiceType = rtWithDecryptedAssertion.getAssertions().get(0);
+            rtChoiceType = resp.getAssertions().get(0);
             assertNotNull(rtChoiceType.getAssertion());
             assertNull(rtChoiceType.getEncryptedAssertion());
         }
diff --git a/services/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java b/services/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java
index 72d84ee..5825f60 100755
--- a/services/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java
+++ b/services/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java
@@ -49,9 +49,12 @@ import org.keycloak.protocol.saml.SamlProtocolUtils;
 import org.keycloak.saml.SAML2LogoutResponseBuilder;
 import org.keycloak.saml.SAMLRequestParser;
 import org.keycloak.saml.common.constants.GeneralConstants;
+import org.keycloak.saml.common.constants.JBossSAMLConstants;
 import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
 import org.keycloak.saml.common.exceptions.ConfigurationException;
 import org.keycloak.saml.common.exceptions.ProcessingException;
+import org.keycloak.saml.common.util.DocumentUtil;
+import org.keycloak.saml.processing.api.saml.v2.sig.SAML2Signature;
 import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
 import org.keycloak.saml.processing.core.saml.v2.constants.X500SAMLProfileConstants;
 import org.keycloak.saml.processing.core.saml.v2.util.AssertionUtil;
@@ -74,6 +77,7 @@ import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
+import javax.xml.namespace.QName;
 import java.io.IOException;
 import java.security.Key;
 import java.security.cert.X509Certificate;
@@ -83,6 +87,8 @@ import java.util.List;
 import org.keycloak.rotation.HardcodedKeyLocator;
 import org.keycloak.rotation.KeyLocator;
 import org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
 
 import java.util.*;
 
@@ -344,7 +350,38 @@ public class SAMLEndpoint {
                 if (responseType.getAssertions() == null || responseType.getAssertions().isEmpty()) {
                     return callback.error(relayState, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
                 }
-                AssertionType assertion = AssertionUtil.getAssertion(responseType, keys.getPrivateKey());
+
+                boolean assertionIsEncrypted = AssertionUtil.isAssertionEncrypted(responseType);
+
+                if (config.isWantAssertionsEncrypted() && !assertionIsEncrypted) {
+                    logger.error("The assertion is not encrypted, which is required.");
+                    event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
+                    event.error(Errors.INVALID_SAML_RESPONSE);
+                    return ErrorPage.error(session, Messages.INVALID_REQUESTER);
+                }
+
+                Element assertionElement;
+
+                if (assertionIsEncrypted) {
+                    // This methods writes the parsed and decrypted assertion back on the responseType parameter:
+                    assertionElement = AssertionUtil.decryptAssertion(responseType, keys.getPrivateKey());
+                } else {
+                    /* We verify the assertion using original document to handle cases where the IdP
+                    includes whitespace and/or newlines inside tags. */
+                    assertionElement = DocumentUtil.getElement(holder.getSamlDocument(), new QName(JBossSAMLConstants.ASSERTION.get()));
+                }
+
+                if (config.isWantAssertionsSigned() && config.isValidateSignature()) {
+                    if (!AssertionUtil.isSignatureValid(assertionElement, getIDPKeyLocator())) {
+                        logger.error("validation failed");
+                        event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
+                        event.error(Errors.INVALID_SIGNATURE);
+                        return ErrorPage.error(session, Messages.INVALID_REQUESTER);
+                    }
+                }
+
+                AssertionType assertion = responseType.getAssertions().get(0).getAssertion();
+
                 SubjectType subject = assertion.getSubject();
                 SubjectType.STSubType subType = subject.getSubType();
                 NameIDType subjectNameID = (NameIDType) subType.getBaseID();
diff --git a/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java b/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
index 6a453b9..c28cda8 100755
--- a/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
+++ b/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
@@ -236,6 +236,7 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
 
 
         boolean wantAuthnRequestsSigned = getConfig().isWantAuthnRequestsSigned();
+        boolean wantAssertionsSigned = getConfig().isWantAssertionsSigned();
         String entityId = getEntityId(uriInfo, realm);
         String nameIDPolicyFormat = getConfig().getNameIDPolicyFormat();
 
@@ -247,7 +248,7 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
         for (RsaKeyMetadata key : keys) {
             addKeyInfo(keysString, key, KeyTypes.SIGNING.value());
         }
-        String descriptor = SPMetadataDescriptor.getSPDescriptor(authnBinding, endpoint, endpoint, wantAuthnRequestsSigned, entityId, nameIDPolicyFormat, keysString.toString());
+        String descriptor = SPMetadataDescriptor.getSPDescriptor(authnBinding, endpoint, endpoint, wantAuthnRequestsSigned, wantAssertionsSigned, entityId, nameIDPolicyFormat, keysString.toString());
         return Response.ok(descriptor, MediaType.APPLICATION_XML_TYPE).build();
     }
 
diff --git a/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderConfig.java b/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderConfig.java
index d2a410b..4d200a0 100755
--- a/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderConfig.java
+++ b/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderConfig.java
@@ -121,6 +121,22 @@ public class SAMLIdentityProviderConfig extends IdentityProviderModel {
         getConfig().put("wantAuthnRequestsSigned", String.valueOf(wantAuthnRequestsSigned));
     }
 
+    public boolean isWantAssertionsSigned() {
+        return Boolean.valueOf(getConfig().get("wantAssertionsSigned"));
+    }
+
+    public void setWantAssertionsSigned(boolean wantAssertionsSigned) {
+        getConfig().put("wantAssertionsSigned", String.valueOf(wantAssertionsSigned));
+    }
+
+    public boolean isWantAssertionsEncrypted() {
+        return Boolean.valueOf(getConfig().get("wantAssertionsEncrypted"));
+    }
+
+    public void setWantAssertionsEncrypted(boolean wantAssertionsEncrypted) {
+        getConfig().put("wantAssertionsEncrypted", String.valueOf(wantAssertionsEncrypted));
+    }
+
     public boolean isAddExtensionsElementWithKeyInfo() {
         return Boolean.valueOf(getConfig().get("addExtensionsElementWithKeyInfo"));
     }
diff --git a/services/src/main/java/org/keycloak/forms/login/freemarker/model/ClientBean.java b/services/src/main/java/org/keycloak/forms/login/freemarker/model/ClientBean.java
index d342f16..43dbdc0 100755
--- a/services/src/main/java/org/keycloak/forms/login/freemarker/model/ClientBean.java
+++ b/services/src/main/java/org/keycloak/forms/login/freemarker/model/ClientBean.java
@@ -46,6 +46,10 @@ public class ClientBean {
         return client.getName();
     }
 
+    public String getDescription() {
+        return client.getDescription();
+    }
+
     public String getBaseUrl() {
         return ResolveRelative.resolveRelativeUri(requestUri, client.getRootUrl(), client.getBaseUrl());
     }
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 1014c0b..8984a4d 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
@@ -141,6 +141,15 @@ public class UserInfoEndpoint {
 
         UserSessionModel userSession = session.sessions().getUserSession(realm, token.getSessionState());
         ClientSessionModel clientSession = session.sessions().getClientSession(token.getClientSession());
+        if( userSession == null ) {
+            userSession = session.sessions().getOfflineUserSession(realm, token.getSessionState());
+            if( AuthenticationManager.isOfflineSessionValid(realm, userSession)) {
+                clientSession = session.sessions().getOfflineClientSession(realm, token.getClientSession());
+            } else {
+                userSession = null;
+                clientSession = null;
+            }
+        }
 
         if (userSession == null) {
             event.error(Errors.USER_SESSION_NOT_FOUND);
diff --git a/services/src/main/java/org/keycloak/protocol/saml/installation/SamlSPDescriptorClientInstallation.java b/services/src/main/java/org/keycloak/protocol/saml/installation/SamlSPDescriptorClientInstallation.java
index 6349953..bd109e6 100755
--- a/services/src/main/java/org/keycloak/protocol/saml/installation/SamlSPDescriptorClientInstallation.java
+++ b/services/src/main/java/org/keycloak/protocol/saml/installation/SamlSPDescriptorClientInstallation.java
@@ -47,7 +47,8 @@ public class SamlSPDescriptorClientInstallation implements ClientInstallationPro
         String nameIdFormat = samlClient.getNameIDFormat();
         if (nameIdFormat == null) nameIdFormat = SamlProtocol.SAML_DEFAULT_NAMEID_FORMAT;
         String spCertificate = SPMetadataDescriptor.xmlKeyInfo("        ", null, samlClient.getClientSigningCertificate(), KeyTypes.SIGNING.value(), true);
-        return SPMetadataDescriptor.getSPDescriptor(JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get(), assertionUrl, logoutUrl, samlClient.requiresClientSignature(), client.getClientId(), nameIdFormat, spCertificate);
+        return SPMetadataDescriptor.getSPDescriptor(JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get(), assertionUrl, logoutUrl,
+                samlClient.requiresClientSignature(), samlClient.requiresAssertionSignature(), client.getClientId(), nameIdFormat, spCertificate);
     }
 
     @Override
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
index 4585e16..12053e0 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
@@ -590,7 +590,7 @@ public class RealmAdminResource {
             query.client(client);
         }
 
-        if (types != null & !types.isEmpty()) {
+        if (types != null && !types.isEmpty()) {
             EventType[] t = new EventType[types.size()];
             for (int i = 0; i < t.length; i++) {
                 t[i] = EventType.valueOf(types.get(i));
diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml
index 4542557..cda3bbb 100755
--- a/testsuite/integration/pom.xml
+++ b/testsuite/integration/pom.xml
@@ -74,10 +74,6 @@
         </dependency>
         <dependency>
             <groupId>org.jboss.resteasy</groupId>
-            <artifactId>async-http-servlet-3.0</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.jboss.resteasy</groupId>
             <artifactId>resteasy-jaxrs</artifactId>
             <exclusions>
                 <exclusion>
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/ValidationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/ValidationTest.java
index aa4ca66..6833b34 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/ValidationTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/ValidationTest.java
@@ -78,7 +78,7 @@ public class ValidationTest {
     public void testBrokerExportDescriptor() throws Exception {
         URL schemaFile = getClass().getResource("/schema/saml/v2/saml-schema-metadata-2.0.xsd");
         Source xmlFile = new StreamSource(new ByteArrayInputStream(SPMetadataDescriptor.getSPDescriptor(
-                "POST", "http://realm/assertion", "http://realm/logout", true, "test", SamlProtocol.SAML_DEFAULT_NAMEID_FORMAT, KeycloakModelUtils.generateKeyPairCertificate("test").getCertificate()
+                "POST", "http://realm/assertion", "http://realm/logout", true, false, "test", SamlProtocol.SAML_DEFAULT_NAMEID_FORMAT, KeycloakModelUtils.generateKeyPairCertificate("test").getCertificate()
         ).getBytes()), "SP Descriptor");
         SchemaFactory schemaFactory = SchemaFactory
                 .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/ModalDialog.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/ModalDialog.java
index 2796fc4..9639749 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/ModalDialog.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/ModalDialog.java
@@ -48,6 +48,9 @@ public class ModalDialog {
     @FindBy(id = "name")
     private WebElement nameInput;
 
+    @FindBy(className = "modal-body")
+    private WebElement message;
+
     public void ok() {
         waitForModalFadeIn(driver);
         okButton.click();
@@ -70,4 +73,8 @@ public class ModalDialog {
         nameInput.clear();
         nameInput.sendKeys(name);
     }
+
+    public WebElement getMessage() {
+        return message;
+    }
 }
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractJSConsoleExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractJSConsoleExampleAdapterTest.java
index 37941b9..a71ef13 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractJSConsoleExampleAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractJSConsoleExampleAdapterTest.java
@@ -404,7 +404,7 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
 
         int timeSkew = Integer.parseInt(jsConsoleTestAppPage.getTimeSkewValue().getText());
         assertTrue("TimeSkew was: " + timeSkew + ", but should be ~0", timeSkew >= 0 - TIME_SKEW_TOLERANCE);
-        assertTrue("TimeSkew was: " + timeSkew + ", but should be ~0", timeSkew  <= TIME_SKEW_TOLERANCE);
+        assertTrue("TimeSkew was: " + timeSkew + ", but should be ~0", timeSkew <= TIME_SKEW_TOLERANCE);
 
         setTimeOffset(40);
         jsConsoleTestAppPage.refreshToken();
@@ -414,7 +414,7 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
 
         timeSkew = Integer.parseInt(jsConsoleTestAppPage.getTimeSkewValue().getText());
         assertTrue("TimeSkew was: " + timeSkew + ", but should be ~-40", timeSkew + 40 >= 0 - TIME_SKEW_TOLERANCE);
-        assertTrue("TimeSkew was: " + timeSkew + ", but should be ~-40", timeSkew + 40  <= TIME_SKEW_TOLERANCE);
+        assertTrue("TimeSkew was: " + timeSkew + ", but should be ~-40", timeSkew + 40 <= TIME_SKEW_TOLERANCE);
     }
 
     // KEYCLOAK-4179
@@ -526,6 +526,27 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
     }
 
     @Test
+    // KEYCLOAK-4503
+    public void initializeWithRefreshToken() {
+        oauth.realm(EXAMPLE);
+        oauth.clientId("js-console");
+        oauth.redirectUri("http://localhost:8280/js-console");
+        oauth.doLogin("user", "password");
+
+        String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
+        String token = oauth.doAccessTokenRequest(code, "password").getAccessToken();
+        String refreshToken = oauth.doRefreshTokenRequest(token, "password").getRefreshToken();
+
+        jsConsoleTestAppPage.navigateTo();
+        jsConsoleTestAppPage.setInput2(refreshToken);
+
+        jsConsoleTestAppPage.initWithRefreshToken();
+
+        waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Init Success (Not Authenticated)");
+        waitUntilElement(jsConsoleTestAppPage.getEventsElement()).text().not().contains("Auth Success");
+    }
+
+    @Test
     public void reentrancyCallbackTest() {
         logInAndInit("standard");
 
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 e6ec392..c57b64e 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
@@ -250,6 +250,24 @@ public class UserInfoTest extends AbstractKeycloakTest {
     }
 
     @Test
+    public void testSessionExpiredOfflineAccess() throws Exception {
+        Client client = ClientBuilder.newClient();
+
+        try {
+            AccessTokenResponse accessTokenResponse = executeGrantAccessTokenRequest(client, true);
+
+            testingClient.testing().removeUserSessions("test");
+
+            Response response = UserInfoClientUtil.executeUserInfoRequest_getMethod(client, accessTokenResponse.getToken());
+
+            testSuccessfulUserInfoResponse(response);
+            response.close();
+        } finally {
+            client.close();
+        }
+    }
+
+    @Test
     public void testUnsuccessfulUserInfoRequest() throws Exception {
         Client client = ClientBuilder.newClient();
 
@@ -274,8 +292,12 @@ public class UserInfoTest extends AbstractKeycloakTest {
     }
 
     private AccessTokenResponse executeGrantAccessTokenRequest(Client client) {
+        return executeGrantAccessTokenRequest(client, false);
+    }
+
+    private AccessTokenResponse executeGrantAccessTokenRequest(Client client, boolean requestOfflineToken) {
         UriBuilder builder = UriBuilder.fromUri(AUTH_SERVER_ROOT);
-        URI grantUri = OIDCLoginProtocolService.tokenUrl(builder).build("test");
+            URI grantUri = OIDCLoginProtocolService.tokenUrl(builder).build("test");
         WebTarget grantTarget = client.target(grantUri);
 
         String header = BasicAuthHelper.createHeader("test-app", "password");
@@ -283,6 +305,9 @@ public class UserInfoTest extends AbstractKeycloakTest {
         form.param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.PASSWORD)
                 .param("username", "test-user@localhost")
                 .param("password", "password");
+        if( requestOfflineToken) {
+            form.param("scope", "offline_access");
+        }
 
         Response response = grantTarget.request()
                 .header(HttpHeaders.AUTHORIZATION, header)
diff --git a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/DisableAuthorizationSettingsTest.java b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/DisableAuthorizationSettingsTest.java
index 3e3359e..0c60f0f 100644
--- a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/DisableAuthorizationSettingsTest.java
+++ b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/DisableAuthorizationSettingsTest.java
@@ -17,6 +17,8 @@
 package org.keycloak.testsuite.console.authorization;
 
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
 
 import org.junit.Test;
 
@@ -25,10 +27,13 @@ import org.junit.Test;
  */
 public class DisableAuthorizationSettingsTest extends AbstractAuthorizationSettingsTest {
 
+    public static final String WARNING_MESSAGE = "Are you sure you want to disable authorization ? Once you save your changes, all authorization settings associated with this client will be removed. This operation can not be reverted.";
+
     @Test
     public void testDisableAuthorization() throws InterruptedException {
         clientSettingsPage.navigateTo();
         clientSettingsPage.form().setAuthorizationSettingsEnabled(false);
+        waitUntilElement(modalDialog.getMessage()).text().contains(WARNING_MESSAGE);
         clientSettingsPage.form().confirmDisableAuthorizationSettings();
         Thread.sleep(1000);
         clientSettingsPage.form().save();
@@ -37,4 +42,14 @@ public class DisableAuthorizationSettingsTest extends AbstractAuthorizationSetti
         clientSettingsPage.navigateTo();
         assertFalse(clientSettingsPage.form().isAuthorizationSettingsEnabled());
     }
+
+    @Test
+    public void testCancelDisablingAuthorization() throws InterruptedException {
+        clientSettingsPage.navigateTo();
+        clientSettingsPage.form().setAuthorizationSettingsEnabled(false);
+        waitUntilElement(modalDialog.getMessage()).text().contains(WARNING_MESSAGE);
+        modalDialog.cancel();
+        Thread.sleep(1000);
+        assertTrue(clientSettingsPage.form().isAuthorizationSettingsEnabled());
+    }
 }
diff --git a/testsuite/integration-arquillian/tests/pom.xml b/testsuite/integration-arquillian/tests/pom.xml
index f0d569c..4e29ec3 100755
--- a/testsuite/integration-arquillian/tests/pom.xml
+++ b/testsuite/integration-arquillian/tests/pom.xml
@@ -894,10 +894,6 @@
 
                 <dependency>
                     <groupId>org.jboss.resteasy</groupId>
-                    <artifactId>async-http-servlet-3.0</artifactId>
-                </dependency>
-                <dependency>
-                    <groupId>org.jboss.resteasy</groupId>
                     <artifactId>resteasy-client</artifactId>
                 </dependency>
                 <dependency>
diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
index 175e39e..db61ff7 100644
--- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
+++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
@@ -551,7 +551,11 @@ http-post-binding-for-authn-request.tooltip=Indicates whether the AuthnRequest m
 http-post-binding-logout=HTTP-POST Binding Logout
 http-post-binding-logout.tooltip=Indicates whether to respond to requests using HTTP-POST binding. If false, HTTP-REDIRECT binding will be used.
 want-authn-requests-signed=Want AuthnRequests Signed
-want-authn-requests-signed.tooltip=Indicates whether the identity provider expects signed a AuthnRequest.
+want-authn-requests-signed.tooltip=Indicates whether the identity provider expects a signed AuthnRequest.
+want-assertions-signed=Want Assertions Signed
+want-assertions-signed.tooltip=Indicates whether this service provider expects a signed Assertion.
+want-assertions-encrypted=Want Assertions Encrypted
+want-assertions-encrypted.tooltip=Indicates whether this service provider expects an encrypted Assertion.
 force-authentication=Force Authentication
 identity-provider.force-authentication.tooltip=Indicates whether the identity provider must authenticate the presenter directly rather than rely on a previous security context.
 validate-signature=Validate Signature
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/app.js b/themes/src/main/resources/theme/base/admin/resources/js/app.js
index 58db5a0..734040e 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/app.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/app.js
@@ -1943,9 +1943,13 @@ module.factory('errorInterceptor', function($q, $window, $rootScope, $location, 
             } else if (response.status) {
                 if (response.data && response.data.errorMessage) {
                     Notifications.error(response.data.errorMessage);
+                } else if (response.data && response.data.error_description) {
+                    Notifications.error(response.data.error_description);
                 } else {
                     Notifications.error("An unexpected server error has occurred");
                 }
+            } else {
+                Notifications.error("No response from server.");
             }
             return $q.reject(response);
         }
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 a2cbea4..315b8a5 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
@@ -437,9 +437,6 @@ module.controller('ClientCertificateImportCtrl', function($scope, $location, $ht
             }).success(function(data, status, headers) {
                 Notifications.success("Keystore uploaded successfully.");
                 $location.url(redirectLocation);
-            }).error(function(data) {
-                var errorMsg = data['error_description'] ? data['error_description'] : 'The key store can not be uploaded. Please verify the file.';
-                Notifications.error(errorMsg);
             });
             //.then(success, error, progress);
         }
@@ -1229,12 +1226,6 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates,
             }, $scope.clientEdit, 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');
-                }
             });
         }
     };
@@ -1348,12 +1339,6 @@ 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');
-            }
         });
     };
 
@@ -1818,12 +1803,6 @@ module.controller('ClientProtocolMapperCtrl', function($scope, realm, serverInfo
             mapper = angular.copy($scope.mapper);
             $location.url("/realms/" + realm.realm + '/clients/' + client.id + "/mappers/" + $scope.model.mapper.id);
             Notifications.success("Your changes have been saved.");
-        }, function(error) {
-            if (error.status == 400 && error.data.error_description) {
-                Notifications.error(error.data.error_description);
-            } else {
-                Notifications.error('Unexpected error when updating protocol mapper');
-            }
         });
     };
 
@@ -1890,14 +1869,6 @@ module.controller('ClientProtocolMapperCreateCtrl', function($scope, realm, serv
             var id = l.substring(l.lastIndexOf("/") + 1);
             $location.url("/realms/" + realm.realm + '/clients/' + client.id + "/mappers/" + id);
             Notifications.success("Mapper has been created.");
-        }, function(error) {
-            if (error.status == 400 && error.data.error_description) {
-                Notifications.error(error.data.error_description);
-            } else if (error.status == 409 && error.data.errorMessage) {
-                Notifications.error(error.data.errorMessage);
-            } else {
-                Notifications.error('Unexpected error when updating protocol mapper');
-            }
         });
     };
 
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
index 4f2a62f..d5b63c3 100644
--- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
@@ -1220,10 +1220,6 @@ module.controller('GenericKeystoreCtrl', function($scope, $location, Notificatio
 
                 $location.url("/realms/" + realm.realm + "/keys/providers/" + $scope.instance.providerId + "/" + id);
                 Notifications.success("The provider has been created.");
-            }, function (errorResponse) {
-                if (errorResponse.data && errorResponse.data['error_description']) {
-                    Notifications.error(errorResponse.data['error_description']);
-                }
             });
         } else {
             Components.update({realm: realm.realm,
@@ -1232,10 +1228,6 @@ module.controller('GenericKeystoreCtrl', function($scope, $location, Notificatio
                 $scope.instance,  function () {
                     $route.reload();
                     Notifications.success("The provider has been updated.");
-                }, function (errorResponse) {
-                    if (errorResponse.data && errorResponse.data['error_description']) {
-                        Notifications.error(errorResponse.data['error_description']);
-                    }
                 });
         }
     };
@@ -2478,10 +2470,6 @@ module.controller('ClientRegPolicyDetailCtrl', function($scope, realm, clientReg
                 var id = l.substring(l.lastIndexOf("/") + 1);
                 $location.url("/realms/" + realm.realm + "/client-registration/client-reg-policies/" + $scope.instance.providerId + "/" + id);
                 Notifications.success("The policy has been created.");
-            }, function (errorResponse) {
-                if (errorResponse.data && errorResponse.data['error_description']) {
-                    Notifications.error(errorResponse.data['error_description']);
-                }
             });
         } else {
             Components.update({realm: realm.realm,
@@ -2490,10 +2478,6 @@ module.controller('ClientRegPolicyDetailCtrl', function($scope, realm, clientReg
                 $scope.instance,  function () {
                     $route.reload();
                     Notifications.success("The policy has been updated.");
-                }, function (errorResponse) {
-                    if (errorResponse.data && errorResponse.data['error_description']) {
-                        Notifications.error(errorResponse.data['error_description']);
-                    }
                 });
         }
     };
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
index 8f42618..13d8343 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
@@ -59,12 +59,6 @@ module.controller('UserRoleMappingCtrl', function($scope, $http, realm, user, cl
                     $scope.selectedClientMappings = [];
                 }
                 Notifications.success("Role mappings updated.");
-            }).error(function(response) {
-                if (response && response['error_description']) {
-                    Notifications.error(response['error_description']);
-                } else {
-                    Notifications.error("Failed to remove role mapping");
-                }
             });
     };
 
@@ -93,12 +87,6 @@ module.controller('UserRoleMappingCtrl', function($scope, $http, realm, user, cl
                 $scope.realmComposite = CompositeRealmRoleMapping.query({realm : realm.realm, userId : user.id});
                 $scope.realmRoles = AvailableRealmRoleMapping.query({realm : realm.realm, userId : user.id});
                 Notifications.success("Role mappings updated.");
-            }).error(function(response) {
-                if (response && response['error_description']) {
-                    Notifications.error(response['error_description']);
-                } else {
-                    Notifications.error("Failed to remove role mapping");
-                }
             });
     };
 
@@ -537,12 +525,6 @@ module.controller('UserCredentialsCtrl', function($scope, realm, user, $route, R
                 Notifications.success("The password has been reset");
                 $scope.password = null;
                 $scope.confirmPassword = null;
-            }, function(response) {
-                if (response.data && response.data['error_description']) {
-                    Notifications.error(response.data['error_description']);
-                } else {
-                    Notifications.error("Failed to reset user password");
-                }
             });
         }, function() {
             $scope.password = null;
@@ -822,10 +804,6 @@ module.controller('GenericUserStorageCtrl', function($scope, $location, Notifica
 
                 $location.url("/realms/" + realm.realm + "/user-storage/providers/" + $scope.instance.providerId + "/" + id);
                 Notifications.success("The provider has been created.");
-            }, function (errorResponse) {
-                if (errorResponse.data && errorResponse.data['error_description']) {
-                    Notifications.error(errorResponse.data['error_description']);
-                }
             });
         } else {
             console.log('update existing provider');
@@ -835,10 +813,6 @@ module.controller('GenericUserStorageCtrl', function($scope, $location, Notifica
                 $scope.instance,  function () {
                     $route.reload();
                     Notifications.success("The provider has been updated.");
-                }, function (errorResponse) {
-                    if (errorResponse.data && errorResponse.data['error_description']) {
-                        Notifications.error(errorResponse.data['error_description']);
-                    }
                 });
         }
     };
@@ -950,12 +924,6 @@ module.controller('UserGroupMembershipCtrl', function($scope, $route, realm, gro
         UserGroupMapping.remove({realm: realm.realm, userId: user.id, groupId: $scope.selectedGroup.id}, function() {
             Notifications.success('Removed group membership');
             $route.reload();
-        }, function(response) {
-            if (response.data && response.data['error_description']) {
-                Notifications.error(response.data['error_description']);
-            } else {
-                Notifications.error("Failed to leave group");
-            }
         });
 
     };
@@ -1231,10 +1199,6 @@ module.controller('LDAPUserStorageCtrl', function($scope, $location, Notificatio
 
                 $location.url("/realms/" + realm.realm + "/user-storage/providers/" + $scope.instance.providerId + "/" + id);
                 Notifications.success("The provider has been created.");
-            }, function (errorResponse) {
-                if (errorResponse.data && errorResponse.data['error_description']) {
-                    Notifications.error(errorResponse.data['error_description']);
-                }
             });
         } else {
             Components.update({realm: realm.realm,
@@ -1243,10 +1207,6 @@ module.controller('LDAPUserStorageCtrl', function($scope, $location, Notificatio
                 $scope.instance,  function () {
                     $route.reload();
                     Notifications.success("The provider has been updated.");
-                }, function (errorResponse) {
-                    if (errorResponse.data && errorResponse.data['error_description']) {
-                        Notifications.error(errorResponse.data['error_description']);
-                    }
                 });
         }
     };
@@ -1409,10 +1369,6 @@ module.controller('LDAPMapperCtrl', function($scope, $route, realm,  provider, m
             $scope.mapper,  function () {
                 $route.reload();
                 Notifications.success("The mapper has been updated.");
-            }, function (errorResponse) {
-                if (errorResponse.data && errorResponse.data['error_description']) {
-                    Notifications.error(errorResponse.data['error_description']);
-                }
             });
     };
 
@@ -1498,10 +1454,6 @@ module.controller('LDAPMapperCreateCtrl', function($scope, realm, provider, mapp
 
             $location.url("/realms/" + realm.realm + "/ldap-mappers/" + $scope.mapper.parentId + "/mappers/" + id);
             Notifications.success("The mapper has been created.");
-        }, function (errorResponse) {
-            if (errorResponse.data && errorResponse.data['error_description']) {
-                Notifications.error(errorResponse.data['error_description']);
-            }
         });
     };
 
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider.html b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider.html
index d764b90..3422752 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider.html
@@ -33,7 +33,7 @@
                     <caption class="hidden">{{:: 'table-of-identity-providers' | translate}}</caption>
                     <thead>
                     <tr>
-                        <th colspan="7" class="kc-table-actions">
+                        <th colspan="8" class="kc-table-actions">
                             <div class="dropdown pull-right" data-ng-show="access.manageIdentityProviders">
                                 <select class="form-control" ng-model="provider"
                                         ng-options="p.name group by p.groupName for p in allProviders track by p.id"
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html
index 9c79a47..aa6733a 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html
@@ -170,6 +170,20 @@
                 </div>
                 <kc-tooltip>{{:: 'want-authn-requests-signed.tooltip' | translate}}</kc-tooltip>
             </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="wantAssertionsSigned">{{:: 'want-assertions-signed' | translate}}</label>
+                <div class="col-md-6">
+                    <input ng-model="identityProvider.config.wantAssertionsSigned" id="wantAssertionsSigned" name="wantAssertionsSigned" onoffswitchvalue on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
+                </div>
+                <kc-tooltip>{{:: 'want-assertions-signed.tooltip' | translate}}</kc-tooltip>
+            </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="wantAssertionsEncrypted">{{:: 'want-assertions-encrypted' | translate}}</label>
+                <div class="col-md-6">
+                    <input ng-model="identityProvider.config.wantAssertionsEncrypted" id="wantAssertionsEncrypted" name="wantAssertionsEncrypted" onoffswitchvalue on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
+                </div>
+                <kc-tooltip>{{:: 'want-assertions-encrypted.tooltip' | translate}}</kc-tooltip>
+            </div>
             <div class="form-group" data-ng-show="identityProvider.config.wantAuthnRequestsSigned == 'true'">
                 <label class="col-md-2 control-label" for="signatureAlgorithm">{{:: 'signature-algorithm' | translate}}</label>
                 <div class="col-sm-6">
diff --git a/travis-run-tests.sh b/travis-run-tests.sh
index cf5f03b..fd5e5e3 100755
--- a/travis-run-tests.sh
+++ b/travis-run-tests.sh
@@ -1,5 +1,7 @@
 #!/bin/bash -e
 
+mvn install --no-snapshot-updates -DskipTests=true -f testsuite
+
 if [ $1 == "old" ]; then
     mvn test -B --no-snapshot-updates -f testsuite/integration
     mvn test -B --no-snapshot-updates -f testsuite/jetty