keycloak-uncached

Changes

pom.xml 53(+38 -15)

services/src/main/java/org/keycloak/services/resources/QRCodeResource.java 106(+0 -106)

Details

diff --git a/adapters/oidc/js/src/main/resources/keycloak.js b/adapters/oidc/js/src/main/resources/keycloak.js
index 6aa116a..fa35049 100755
--- a/adapters/oidc/js/src/main/resources/keycloak.js
+++ b/adapters/oidc/js/src/main/resources/keycloak.js
@@ -192,7 +192,7 @@
                 redirectUri += (redirectUri.indexOf('?') == -1 ? '?' : '&') + 'prompt=' + options.prompt;
             }
 
-            sessionStorage.oauthState = JSON.stringify({ state: state, nonce: nonce, redirectUri: encodeURIComponent(redirectUri) });
+            localStorage.oauthState = JSON.stringify({ state: state, nonce: nonce, redirectUri: encodeURIComponent(redirectUri) });
 
             var action = 'auth';
             if (options && options.action == 'register') {
@@ -689,10 +689,10 @@
         function parseCallback(url) {
             var oauth = new CallbackParser(url, kc.responseMode).parseUri();
 
-            var sessionState = sessionStorage.oauthState && JSON.parse(sessionStorage.oauthState);
+            var sessionState = localStorage.oauthState && JSON.parse(localStorage.oauthState);
 
             if (sessionState && (oauth.code || oauth.error || oauth.access_token || oauth.id_token) && oauth.state && oauth.state == sessionState.state) {
-                delete sessionStorage.oauthState;
+                delete localStorage.oauthState;
 
                 oauth.redirectUri = sessionState.redirectUri;
                 oauth.storedNonce = sessionState.nonce;
diff --git a/core/src/main/java/org/keycloak/representations/idm/AuthenticatorConfigRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/AuthenticatorConfigRepresentation.java
index dda7418..258f925 100755
--- a/core/src/main/java/org/keycloak/representations/idm/AuthenticatorConfigRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/AuthenticatorConfigRepresentation.java
@@ -27,9 +27,17 @@ import java.util.Map;
 */
 public class AuthenticatorConfigRepresentation implements Serializable {
 
+    private String id;
     private String alias;
     private Map<String, String> config = new HashMap<String, String>();
 
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
 
     public String getAlias() {
         return alias;
@@ -39,8 +47,6 @@ public class AuthenticatorConfigRepresentation implements Serializable {
         this.alias = alias;
     }
 
-
-
     public Map<String, String> getConfig() {
         return config;
     }
diff --git a/docbook/auth-server-docs/reference/en/en-US/modules/logout.xml b/docbook/auth-server-docs/reference/en/en-US/modules/logout.xml
index 9f5bfa3..ea6eb4e 100755
--- a/docbook/auth-server-docs/reference/en/en-US/modules/logout.xml
+++ b/docbook/auth-server-docs/reference/en/en-US/modules/logout.xml
@@ -20,7 +20,7 @@
     <para>
         There are multiple ways you can logout from a web application.  For Java EE servlet containers, you can call
         HttpServletRequest.logout().
-        For any other browser application, you can point the browser at the url <literal>http://auth-server/auth/realms/{realm-name}/tokens/logout?redirect_uri=encodedRedirectUri</literal>.
+        For any other browser application, you can point the browser at the url <literal>http://auth-server/auth/realms/{realm-name}/protocol/openid-connect/logout?redirect_uri=encodedRedirectUri</literal>.
         This will log you out if you have an SSO session with your browser.
     </para>
 </section>
\ No newline at end of file
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/Config.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/Config.java
index ae3edaa..4e628e5 100644
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/Config.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/Config.java
@@ -17,6 +17,9 @@
 
 package org.keycloak.admin.client;
 
+import static org.keycloak.OAuth2Constants.CLIENT_CREDENTIALS;
+import static org.keycloak.OAuth2Constants.PASSWORD;
+
 /**
  * @author rodrigo.sasaki@icarros.com.br
  */
@@ -28,14 +31,21 @@ public class Config {
     private String password;
     private String clientId;
     private String clientSecret;
+    private String grantType;
 
     public Config(String serverUrl, String realm, String username, String password, String clientId, String clientSecret) {
+        this(serverUrl, realm, username, password, clientId, clientSecret, PASSWORD);
+    }
+
+    public Config(String serverUrl, String realm, String username, String password, String clientId, String clientSecret, String grantType) {
         this.serverUrl = serverUrl;
         this.realm = realm;
         this.username = username;
         this.password = password;
         this.clientId = clientId;
         this.clientSecret = clientSecret;
+        this.grantType = grantType;
+        checkGrantType(grantType);
     }
 
     public String getServerUrl() {
@@ -86,8 +96,23 @@ public class Config {
         this.clientSecret = clientSecret;
     }
 
-    public boolean isPublicClient(){
+    public boolean isPublicClient() {
         return clientSecret == null;
     }
 
+    public String getGrantType() {
+        return grantType;
+    }
+
+    public void setGrantType(String grantType) {
+        this.grantType = grantType;
+        checkGrantType(grantType);
+    }
+
+    public static void checkGrantType(String grantType) {
+        if (!PASSWORD.equals(grantType) && !CLIENT_CREDENTIALS.equals(grantType)) {
+            throw new IllegalArgumentException("Unsupported grantType: " + grantType +
+                    " (only " + PASSWORD + " and " + CLIENT_CREDENTIALS + " are supported)");
+        }
+    }
 }
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/Keycloak.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/Keycloak.java
index 52c5561..f86170e 100755
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/Keycloak.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/Keycloak.java
@@ -28,24 +28,24 @@ import org.keycloak.admin.client.token.TokenManager;
 
 import java.net.URI;
 
+import static org.keycloak.OAuth2Constants.PASSWORD;
+
 /**
  * Provides a Keycloak client. By default, this implementation uses a {@link ResteasyClient RESTEasy client} with the
  * default {@link ResteasyClientBuilder} settings. To customize the underling client, use a {@link KeycloakBuilder} to
  * create a Keycloak client.
  *
- * @see KeycloakBuilder
- *
  * @author rodrigo.sasaki@icarros.com.br
+ * @see KeycloakBuilder
  */
 public class Keycloak {
-
     private final Config config;
     private final TokenManager tokenManager;
     private final ResteasyWebTarget target;
     private final ResteasyClient client;
 
-    Keycloak(String serverUrl, String realm, String username, String password, String clientId, String clientSecret, ResteasyClient resteasyClient){
-        config = new Config(serverUrl, realm, username, password, clientId, clientSecret);
+    Keycloak(String serverUrl, String realm, String username, String password, String clientId, String clientSecret, String grantType, ResteasyClient resteasyClient) {
+        config = new Config(serverUrl, realm, username, password, clientId, clientSecret, grantType);
         client = resteasyClient != null ? resteasyClient : new ResteasyClientBuilder().connectionPoolSize(10).build();
 
         tokenManager = new TokenManager(config, client);
@@ -55,27 +55,27 @@ public class Keycloak {
         target.register(new BearerAuthFilter(tokenManager));
     }
 
-    public static Keycloak getInstance(String serverUrl, String realm, String username, String password, String clientId, String clientSecret){
-        return new Keycloak(serverUrl, realm, username, password, clientId, clientSecret, null);
+    public static Keycloak getInstance(String serverUrl, String realm, String username, String password, String clientId, String clientSecret) {
+        return new Keycloak(serverUrl, realm, username, password, clientId, clientSecret, PASSWORD, null);
     }
 
-    public static Keycloak getInstance(String serverUrl, String realm, String username, String password, String clientId){
-        return new Keycloak(serverUrl, realm, username, password, clientId, null, null);
+    public static Keycloak getInstance(String serverUrl, String realm, String username, String password, String clientId) {
+        return new Keycloak(serverUrl, realm, username, password, clientId, null, PASSWORD, null);
     }
 
-    public RealmsResource realms(){
+    public RealmsResource realms() {
         return target.proxy(RealmsResource.class);
     }
 
-    public RealmResource realm(String realmName){
+    public RealmResource realm(String realmName) {
         return realms().realm(realmName);
     }
 
-    public ServerInfoResource serverInfo(){
+    public ServerInfoResource serverInfo() {
         return target.proxy(ServerInfoResource.class);
     }
 
-    public TokenManager tokenManager(){
+    public TokenManager tokenManager() {
         return tokenManager;
     }
 
@@ -98,5 +98,4 @@ public class Keycloak {
     public void close() {
         client.close();
     }
-
 }
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/KeycloakBuilder.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/KeycloakBuilder.java
index 5a61ffb..e192d9a 100644
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/KeycloakBuilder.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/KeycloakBuilder.java
@@ -20,15 +20,17 @@ package org.keycloak.admin.client;
 import org.jboss.resteasy.client.jaxrs.ResteasyClient;
 import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
 
+import static org.keycloak.OAuth2Constants.CLIENT_CREDENTIALS;
+import static org.keycloak.OAuth2Constants.PASSWORD;
+
 /**
  * Provides a {@link Keycloak} client builder with the ability to customize the underlying
  * {@link ResteasyClient RESTEasy client} used to communicate with the Keycloak server.
- *
+ * <p>
  * <p>Example usage with a connection pool size of 20:</p>
- *
  * <pre>
  *   Keycloak keycloak = KeycloakBuilder.builder()
- *     .serverUrl("https:/sso.example.com/auth")
+ *     .serverUrl("https://sso.example.com/auth")
  *     .realm("realm")
  *     .username("user")
  *     .password("pass")
@@ -37,6 +39,16 @@ import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
  *     .resteasyClient(new ResteasyClientBuilder().connectionPoolSize(20).build())
  *     .build();
  * </pre>
+ * <p>Example usage with grant_type=client_credentials</p>
+ * <pre>
+ *   Keycloak keycloak = KeycloakBuilder.builder()
+ *     .serverUrl("https://sso.example.com/auth")
+ *     .realm("example")
+ *     .grantType(OAuth2Constants.CLIENT_CREDENTIALS)
+ *     .clientId("client")
+ *     .clientSecret("secret")
+ *     .build();
+ * </pre>
  *
  * @author Scott Rossillo
  * @see ResteasyClientBuilder
@@ -48,6 +60,7 @@ public class KeycloakBuilder {
     private String password;
     private String clientId;
     private String clientSecret;
+    private String grantType = PASSWORD;
     private ResteasyClient resteasyClient;
 
     public KeycloakBuilder serverUrl(String serverUrl) {
@@ -60,6 +73,12 @@ public class KeycloakBuilder {
         return this;
     }
 
+    public KeycloakBuilder grantType(String grantType) {
+        Config.checkGrantType(grantType);
+        this.grantType = grantType;
+        return this;
+    }
+
     public KeycloakBuilder username(String username) {
         this.username = username;
         return this;
@@ -97,19 +116,25 @@ public class KeycloakBuilder {
             throw new IllegalStateException("realm required");
         }
 
-        if (username == null) {
-            throw new IllegalStateException("username required");
-        }
-
-        if (password == null) {
-            throw new IllegalStateException("password required");
+        if (PASSWORD.equals(grantType)) {
+            if (username == null) {
+                throw new IllegalStateException("username required");
+            }
+
+            if (password == null) {
+                throw new IllegalStateException("password required");
+            }
+        } else if (CLIENT_CREDENTIALS.equals(grantType)) {
+            if (clientSecret == null) {
+                throw new IllegalStateException("clientSecret required with grant_type=client_credentials");
+            }
         }
 
         if (clientId == null) {
             throw new IllegalStateException("clientId required");
         }
 
-        return new Keycloak(serverUrl, realm, username, password, clientId, clientSecret, resteasyClient);
+        return new Keycloak(serverUrl, realm, username, password, clientId, clientSecret, grantType, resteasyClient);
     }
 
     private KeycloakBuilder() {
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java
index 51e9a45..df13c0b 100755
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java
@@ -35,6 +35,7 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import java.util.List;
+import java.util.Map;
 
 /**
  * @author rodrigo.sasaki@icarros.com.br
@@ -132,4 +133,13 @@ public interface UserResource {
     @Path("role-mappings")
     public RoleMappingResource roles();
 
+
+    @GET
+    @Path("consents")
+    public List<Map<String, Object>> getConsents();
+
+    @DELETE
+    @Path("consents/{client}")
+    public void revokeConsent(@PathParam("client") String clientId);
+
 }
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UsersResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UsersResource.java
index 9497f3a..93a0638 100755
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UsersResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UsersResource.java
@@ -19,13 +19,7 @@ package org.keycloak.admin.client.resource;
 
 import org.keycloak.representations.idm.UserRepresentation;
 
-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.MediaType;
 import javax.ws.rs.core.Response;
 import java.util.List;
@@ -59,4 +53,7 @@ public interface UsersResource {
     @Path("{id}")
     UserResource get(@PathParam("id") String id);
 
+    @Path("{id}")
+    @DELETE
+    Response delete(@PathParam("id") String id);
 }
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/token/TokenManager.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/token/TokenManager.java
index e325681..bb32dae 100644
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/token/TokenManager.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/token/TokenManager.java
@@ -27,11 +27,12 @@ import org.keycloak.representations.AccessTokenResponse;
 import javax.ws.rs.BadRequestException;
 import javax.ws.rs.core.Form;
 
+import static org.keycloak.OAuth2Constants.*;
+
 /**
  * @author rodrigo.sasaki@icarros.com.br
  */
 public class TokenManager {
-
     private static final long DEFAULT_MIN_VALIDITY = 30;
 
     private AccessTokenResponse currentToken;
@@ -39,61 +40,67 @@ public class TokenManager {
     private long minTokenValidity = DEFAULT_MIN_VALIDITY;
     private final Config config;
     private final TokenService tokenService;
+    private final String accessTokenGrantType;
 
-    public TokenManager(Config config, ResteasyClient client){
+    public TokenManager(Config config, ResteasyClient client) {
         this.config = config;
         ResteasyWebTarget target = client.target(config.getServerUrl());
-        if(!config.isPublicClient()){
+        if (!config.isPublicClient()) {
             target.register(new BasicAuthFilter(config.getClientId(), config.getClientSecret()));
         }
-        tokenService = target.proxy(TokenService.class);
+        this.tokenService = target.proxy(TokenService.class);
+        this.accessTokenGrantType = config.getGrantType();
+
+        if (CLIENT_CREDENTIALS.equals(accessTokenGrantType) && config.isPublicClient()) {
+            throw new IllegalArgumentException("Can't use " + GRANT_TYPE + "=" + CLIENT_CREDENTIALS + " with public client");
+        }
     }
 
-    public String getAccessTokenString(){
+    public String getAccessTokenString() {
         return getAccessToken().getToken();
     }
 
-    public synchronized AccessTokenResponse getAccessToken(){
-        if(currentToken == null){
+    public synchronized AccessTokenResponse getAccessToken() {
+        if (currentToken == null) {
             grantToken();
-        }else if(tokenExpired()){
+        } else if (tokenExpired()) {
             refreshToken();
         }
         return currentToken;
     }
 
-    public AccessTokenResponse grantToken(){
-        Form form = new Form()
-                .param("grant_type", "password")
-                .param("username", config.getUsername())
+    public AccessTokenResponse grantToken() {
+        Form form = new Form().param(GRANT_TYPE, accessTokenGrantType);
+        if (PASSWORD.equals(accessTokenGrantType)) {
+            form.param("username", config.getUsername())
                 .param("password", config.getPassword());
+        }
 
-        if(config.isPublicClient()){
-            form.param("client_id", config.getClientId());
+        if (config.isPublicClient()) {
+            form.param(CLIENT_ID, config.getClientId());
         }
 
         int requestTime = Time.currentTime();
         synchronized (this) {
-            currentToken = tokenService.grantToken( config.getRealm(), form.asMap() );
+            currentToken = tokenService.grantToken(config.getRealm(), form.asMap());
             expirationTime = requestTime + currentToken.getExpiresIn();
         }
         return currentToken;
     }
 
-    public AccessTokenResponse refreshToken(){
-        Form form = new Form()
-                .param("grant_type", "refresh_token")
-                .param("refresh_token", currentToken.getRefreshToken());
+    public AccessTokenResponse refreshToken() {
+        Form form = new Form().param(GRANT_TYPE, REFRESH_TOKEN)
+                              .param(REFRESH_TOKEN, currentToken.getRefreshToken());
 
-        if(config.isPublicClient()){
-            form.param("client_id", config.getClientId());
+        if (config.isPublicClient()) {
+            form.param(CLIENT_ID, config.getClientId());
         }
 
         try {
             int requestTime = Time.currentTime();
 
             synchronized (this) {
-                currentToken = tokenService.refreshToken( config.getRealm(), form.asMap() );
+                currentToken = tokenService.refreshToken(config.getRealm(), form.asMap());
                 expirationTime = requestTime + currentToken.getExpiresIn();
             }
             return currentToken;
diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/conn/DefaultLiquibaseConnectionProvider.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/conn/DefaultLiquibaseConnectionProvider.java
index a011b41..b9018f8 100644
--- a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/conn/DefaultLiquibaseConnectionProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/conn/DefaultLiquibaseConnectionProvider.java
@@ -39,6 +39,7 @@ import org.keycloak.Config;
 import org.keycloak.connections.jpa.updater.liquibase.LiquibaseJpaUpdaterProvider;
 import org.keycloak.connections.jpa.updater.liquibase.PostgresPlusDatabase;
 import org.keycloak.connections.jpa.updater.liquibase.lock.CustomInsertLockRecordGenerator;
+import org.keycloak.connections.jpa.updater.liquibase.lock.CustomLockDatabaseChangeLogGenerator;
 import org.keycloak.connections.jpa.updater.liquibase.lock.CustomLockService;
 import org.keycloak.connections.jpa.updater.liquibase.lock.DummyLockService;
 import org.keycloak.models.KeycloakSession;
@@ -68,6 +69,7 @@ public class DefaultLiquibaseConnectionProvider implements LiquibaseConnectionPr
 
     protected void baseLiquibaseInitialization() {
         ServiceLocator sl = ServiceLocator.getInstance();
+        sl.setResourceAccessor(new ClassLoaderResourceAccessor(getClass().getClassLoader()));
 
         if (!System.getProperties().containsKey("liquibase.scan.packages")) {
             if (sl.getPackages().remove("liquibase.core")) {
@@ -84,6 +86,10 @@ public class DefaultLiquibaseConnectionProvider implements LiquibaseConnectionPr
 
             sl.getPackages().remove("liquibase.ext");
             sl.getPackages().remove("liquibase.sdk");
+
+            String lockPackageName = DummyLockService.class.getPackage().getName();
+            logger.debugf("Added package %s to liquibase", lockPackageName);
+            sl.addPackageToScan(lockPackageName);
         }
 
         LogFactory.setInstance(new LogWrapper());
@@ -93,6 +99,9 @@ public class DefaultLiquibaseConnectionProvider implements LiquibaseConnectionPr
 
         // Change command for creating lock and drop DELETE lock record from it
         SqlGeneratorFactory.getInstance().register(new CustomInsertLockRecordGenerator());
+
+        // Use "SELECT FOR UPDATE" for locking database
+        SqlGeneratorFactory.getInstance().register(new CustomLockDatabaseChangeLogGenerator());
     }
 
 
@@ -125,10 +134,6 @@ public class DefaultLiquibaseConnectionProvider implements LiquibaseConnectionPr
         String changelog = (database instanceof DB2Database) ? LiquibaseJpaUpdaterProvider.DB2_CHANGELOG :  LiquibaseJpaUpdaterProvider.CHANGELOG;
         logger.debugf("Using changelog file: %s", changelog);
 
-        // We wrap liquibase update in CustomLockService provided by DBLockProvider. No need to lock inside liquibase itself.
-        // NOTE: This can't be done in baseLiquibaseInitialization() as liquibase always restarts lock service
-        LockServiceFactory.getInstance().register(new DummyLockService());
-
         return new Liquibase(changelog, new ClassLoaderResourceAccessor(getClass().getClassLoader()), database);
     }
 
diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/lock/CustomLockDatabaseChangeLogGenerator.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/lock/CustomLockDatabaseChangeLogGenerator.java
new file mode 100644
index 0000000..09d5495
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/lock/CustomLockDatabaseChangeLogGenerator.java
@@ -0,0 +1,86 @@
+/*
+ * 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.connections.jpa.updater.liquibase.lock;
+
+import liquibase.database.Database;
+import liquibase.database.core.DB2Database;
+import liquibase.database.core.H2Database;
+import liquibase.database.core.MSSQLDatabase;
+import liquibase.database.core.MySQLDatabase;
+import liquibase.database.core.OracleDatabase;
+import liquibase.database.core.PostgresDatabase;
+import liquibase.sql.Sql;
+import liquibase.sql.UnparsedSql;
+import liquibase.sqlgenerator.SqlGeneratorChain;
+import liquibase.sqlgenerator.core.LockDatabaseChangeLogGenerator;
+import liquibase.statement.core.LockDatabaseChangeLogStatement;
+import org.jboss.logging.Logger;
+
+/**
+ * We use "SELECT FOR UPDATE" pessimistic locking (Same algorithm like Hibernate LockMode.PESSIMISTIC_WRITE )
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class CustomLockDatabaseChangeLogGenerator extends LockDatabaseChangeLogGenerator {
+
+    private static final Logger logger = Logger.getLogger(CustomLockDatabaseChangeLogGenerator.class);
+
+    @Override
+    public int getPriority() {
+        return super.getPriority() + 1; // Ensure bigger priority than LockDatabaseChangeLogGenerator
+    }
+
+    @Override
+    public Sql[] generateSql(LockDatabaseChangeLogStatement statement, Database database, SqlGeneratorChain sqlGeneratorChain) {
+
+        Sql selectForUpdateSql = generateSelectForUpdate(database);
+
+        return new Sql[] { selectForUpdateSql };
+    }
+
+
+    private Sql generateSelectForUpdate(Database database) {
+        String catalog = database.getLiquibaseCatalogName();
+        String schema = database.getLiquibaseSchemaName();
+        String rawLockTableName = database.getDatabaseChangeLogLockTableName();
+
+        String lockTableName = database.escapeTableName(catalog, schema, rawLockTableName);
+        String idColumnName  = database.escapeColumnName(catalog, schema, rawLockTableName, "ID");
+
+        String sqlBase = "SELECT " + idColumnName + " FROM " + lockTableName;
+        String sqlWhere = " WHERE " + idColumnName + "=1";
+
+        String sql;
+        if (database instanceof MySQLDatabase || database instanceof PostgresDatabase || database instanceof H2Database ||
+                database instanceof OracleDatabase) {
+            sql = sqlBase + sqlWhere + " FOR UPDATE";
+        } else if (database instanceof MSSQLDatabase) {
+            sql = sqlBase + " WITH (UPDLOCK, ROWLOCK)" + sqlWhere;
+        } else if (database instanceof DB2Database) {
+            sql = sqlBase + sqlWhere +  " FOR READ ONLY WITH RS USE AND KEEP UPDATE LOCKS";
+        } else {
+            sql = sqlBase + sqlWhere;
+            logger.warnf("No direct support for database %s . Database lock may not work correctly", database.getClass().getName());
+        }
+
+        logger.debugf("SQL command for pessimistic lock: %s", sql);
+        
+        return new UnparsedSql(sql);
+    }
+
+}
diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/lock/CustomLockService.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/lock/CustomLockService.java
index 0c246ca..3efbb23 100644
--- a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/lock/CustomLockService.java
+++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/lock/CustomLockService.java
@@ -18,25 +18,16 @@
 package org.keycloak.connections.jpa.updater.liquibase.lock;
 
 import java.lang.reflect.Field;
-import java.text.DateFormat;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Date;
 
-import liquibase.database.Database;
 import liquibase.database.core.DerbyDatabase;
 import liquibase.exception.DatabaseException;
-import liquibase.exception.LockException;
 import liquibase.executor.Executor;
 import liquibase.executor.ExecutorService;
-import liquibase.lockservice.DatabaseChangeLogLock;
 import liquibase.lockservice.StandardLockService;
-import liquibase.logging.LogFactory;
-import liquibase.sql.visitor.AbstractSqlVisitor;
-import liquibase.sql.visitor.SqlVisitor;
 import liquibase.statement.core.CreateDatabaseChangeLogLockTableStatement;
 import liquibase.statement.core.DropTableStatement;
 import liquibase.statement.core.InitializeDatabaseChangeLogLockTableStatement;
+import liquibase.statement.core.LockDatabaseChangeLogStatement;
 import liquibase.statement.core.RawSqlStatement;
 import org.jboss.logging.Logger;
 import org.keycloak.common.util.Time;
@@ -51,24 +42,6 @@ public class CustomLockService extends StandardLockService {
 
     private static final Logger log = Logger.getLogger(CustomLockService.class);
 
-    private long changeLogLocRecheckTimeMillis = -1;
-
-    @Override
-    public void setChangeLogLockRecheckTime(long changeLogLocRecheckTime) {
-        super.setChangeLogLockRecheckTime(changeLogLocRecheckTime);
-        this.changeLogLocRecheckTimeMillis = changeLogLocRecheckTime;
-    }
-
-    // Bug in StandardLockService.getChangeLogLockRecheckTime()
-    @Override
-    public Long getChangeLogLockRecheckTime() {
-        if (changeLogLocRecheckTimeMillis == -1) {
-            return super.getChangeLogLockRecheckTime();
-        } else {
-            return changeLogLocRecheckTimeMillis;
-        }
-    }
-
     @Override
     public void init() throws DatabaseException {
         boolean createdTable = false;
@@ -84,8 +57,8 @@ public class CustomLockService extends StandardLockService {
                 database.commit();
             } catch (DatabaseException de) {
                 log.warn("Failed to create lock table. Maybe other transaction created in the meantime. Retrying...");
-                if (log.isDebugEnabled()) {
-                    log.debug(de.getMessage(), de); //Log details at debug level
+                if (log.isTraceEnabled()) {
+                    log.trace(de.getMessage(), de); //Log details at trace level
                 }
                 database.rollback();
                 throw new LockRetryException(de);
@@ -115,8 +88,8 @@ public class CustomLockService extends StandardLockService {
 
             } catch (DatabaseException de) {
                 log.warn("Failed to insert first record to the lock table. Maybe other transaction inserted in the meantime. Retrying...");
-                if (log.isDebugEnabled()) {
-                    log.debug(de.getMessage(), de); // Log details at debug level
+                if (log.isTraceEnabled()) {
+                    log.trace(de.getMessage(), de); // Log details at trace level
                 }
                 database.rollback();
                 throw new LockRetryException(de);
@@ -140,34 +113,88 @@ public class CustomLockService extends StandardLockService {
     }
 
     @Override
-    public void waitForLock() throws LockException {
+    public void waitForLock() {
         boolean locked = false;
         long startTime = Time.toMillis(Time.currentTime());
         long timeToGiveUp = startTime + (getChangeLogLockWaitTime());
+        boolean nextAttempt = true;
 
-        while (!locked && Time.toMillis(Time.currentTime()) < timeToGiveUp) {
+        while (nextAttempt) {
             locked = acquireLock();
             if (!locked) {
                 int remainingTime = ((int)(timeToGiveUp / 1000)) - Time.currentTime();
-                log.debugf("Waiting for changelog lock... Remaining time: %d seconds", remainingTime);
-                try {
-                    Thread.sleep(getChangeLogLockRecheckTime());
-                } catch (InterruptedException e) {
-                    e.printStackTrace();
+                if (remainingTime > 0) {
+                    log.debugf("Will try to acquire log another time. Remaining time: %d seconds", remainingTime);
+                } else {
+                    nextAttempt = false;
                 }
+            } else {
+                nextAttempt = false;
             }
         }
 
         if (!locked) {
-            DatabaseChangeLogLock[] locks = listLocks();
-            String lockedBy;
-            if (locks.length > 0) {
-                DatabaseChangeLogLock lock = locks[0];
-                lockedBy = lock.getLockedBy() + " since " + DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).format(lock.getLockGranted());
+            int timeout = ((int)(getChangeLogLockWaitTime() / 1000));
+            throw new IllegalStateException("Could not acquire change log lock within specified timeout " + timeout + " seconds.  Currently locked by other transaction");
+        }
+    }
+
+    @Override
+    public boolean acquireLock() {
+        if (hasChangeLogLock) {
+            // We already have a lock
+            return true;
+        }
+
+        Executor executor = ExecutorService.getInstance().getExecutor(database);
+
+        try {
+            database.rollback();
+
+            // Ensure table created and lock record inserted
+            this.init();
+        } catch (DatabaseException de) {
+            throw new IllegalStateException("Failed to retrieve lock", de);
+        }
+
+        try {
+            log.debug("Trying to lock database");
+            executor.execute(new LockDatabaseChangeLogStatement());
+            log.debug("Successfully acquired database lock");
+
+            hasChangeLogLock = true;
+            database.setCanCacheLiquibaseTableInfo(true);
+            return true;
+
+        } catch (DatabaseException de) {
+            log.warn("Lock didn't yet acquired. Will possibly retry to acquire lock. Details: " + de.getMessage());
+            if (log.isTraceEnabled()) {
+                log.debug(de.getMessage(), de);
+            }
+            return false;
+        }
+    }
+
+
+    @Override
+    public void releaseLock() {
+        try {
+            if (hasChangeLogLock) {
+                log.debug("Going to release database lock");
+                database.commit();
             } else {
-                lockedBy = "UNKNOWN";
+                log.warn("Attempt to release lock, which is not owned by current transaction");
+            }
+        } catch (Exception e) {
+            log.error("Database error during release lock", e);
+        } finally {
+            try {
+                hasChangeLogLock = false;
+                database.setCanCacheLiquibaseTableInfo(false);
+                database.rollback();
+            } catch (DatabaseException e) {
+                ;
             }
-            throw new LockException("Could not acquire change log lock.  Currently locked by " + lockedBy);
         }
     }
 
diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/lock/DummyLockService.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/lock/DummyLockService.java
index 61ef19f..954822a 100644
--- a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/lock/DummyLockService.java
+++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/lock/DummyLockService.java
@@ -17,6 +17,7 @@
 
 package org.keycloak.connections.jpa.updater.liquibase.lock;
 
+import liquibase.exception.DatabaseException;
 import liquibase.exception.LockException;
 import liquibase.lockservice.StandardLockService;
 
@@ -28,6 +29,15 @@ import liquibase.lockservice.StandardLockService;
 public class DummyLockService extends StandardLockService {
 
     @Override
+    public int getPriority() {
+        return Integer.MAX_VALUE;
+    }
+
+    @Override
+    public void init() throws DatabaseException {
+    }
+
+    @Override
     public void waitForLock() throws LockException {
     }
 
diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/lock/LiquibaseDBLockProvider.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/lock/LiquibaseDBLockProvider.java
index 8769053..f44641e 100644
--- a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/lock/LiquibaseDBLockProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/lock/LiquibaseDBLockProvider.java
@@ -46,7 +46,7 @@ public class LiquibaseDBLockProvider implements DBLockProvider {
     private final LiquibaseDBLockProviderFactory factory;
     private final KeycloakSession session;
 
-    private LockService lockService;
+    private CustomLockService lockService;
     private Connection dbConnection;
 
     private int maxAttempts = DEFAULT_MAX_ATTEMPTS;
@@ -69,7 +69,6 @@ public class LiquibaseDBLockProvider implements DBLockProvider {
 
             this.lockService = new CustomLockService();
             lockService.setChangeLogLockWaitTime(factory.getLockWaitTimeoutMillis());
-            lockService.setChangeLogLockRecheckTime(factory.getLockRecheckTimeMillis());
             lockService.setDatabase(liquibase.getDatabase());
         } catch (LiquibaseException exception) {
             safeRollbackConnection();
@@ -94,16 +93,15 @@ public class LiquibaseDBLockProvider implements DBLockProvider {
                 lockService.waitForLock();
                 this.maxAttempts = DEFAULT_MAX_ATTEMPTS;
                 return;
-            } catch (LockException le) {
-                if (le.getCause() != null && le.getCause() instanceof LockRetryException) {
-                    // Indicates we should try to acquire lock again in different transaction
-                    restart();
-                    maxAttempts--;
-                } else {
-                    throw new IllegalStateException("Failed to retrieve lock", le);
-
-                    // TODO: Possibility to forcefully retrieve lock after timeout instead of just give-up?
-                }
+            } catch (LockRetryException le) {
+                // Indicates we should try to acquire lock again in different transaction
+                safeRollbackConnection();
+                restart();
+                maxAttempts--;
+            } catch (RuntimeException re) {
+                safeRollbackConnection();
+                safeCloseConnection();
+                throw re;
             }
         }
     }
@@ -111,15 +109,17 @@ public class LiquibaseDBLockProvider implements DBLockProvider {
 
     @Override
     public void releaseLock() {
-        try {
-            lockService.releaseLock();
-        } catch (LockException e) {
-            logger.error("Could not release lock", e);
-        }
+        lockService.releaseLock();
         lockService.reset();
     }
 
     @Override
+    public boolean supportsForcedUnlock() {
+        // Implementation based on "SELECT FOR UPDATE" can't force unlock as it's locked by other transaction
+        return false;
+    }
+
+    @Override
     public void destroyLockInfo() {
         try {
             this.lockService.destroy();
diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/lock/LiquibaseDBLockProviderFactory.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/lock/LiquibaseDBLockProviderFactory.java
index 5ce8be3..3026f7d 100644
--- a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/lock/LiquibaseDBLockProviderFactory.java
+++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/lock/LiquibaseDBLockProviderFactory.java
@@ -31,24 +31,17 @@ public class LiquibaseDBLockProviderFactory implements DBLockProviderFactory {
 
     private static final Logger logger = Logger.getLogger(LiquibaseDBLockProviderFactory.class);
 
-    private long lockRecheckTimeMillis;
     private long lockWaitTimeoutMillis;
 
-    protected long getLockRecheckTimeMillis() {
-        return lockRecheckTimeMillis;
-    }
-
     protected long getLockWaitTimeoutMillis() {
         return lockWaitTimeoutMillis;
     }
 
     @Override
     public void init(Config.Scope config) {
-        int lockRecheckTime = config.getInt("lockRecheckTime", 2);
         int lockWaitTimeout = config.getInt("lockWaitTimeout", 900);
-        this.lockRecheckTimeMillis = Time.toMillis(lockRecheckTime);
         this.lockWaitTimeoutMillis = Time.toMillis(lockWaitTimeout);
-        logger.debugf("Liquibase lock provider configured with lockWaitTime: %d seconds, lockRecheckTime: %d seconds", lockWaitTimeout, lockRecheckTime);
+        logger.debugf("Liquibase lock provider configured with lockWaitTime: %d seconds", lockWaitTimeout);
     }
 
     @Override
@@ -63,7 +56,6 @@ public class LiquibaseDBLockProviderFactory implements DBLockProviderFactory {
 
     @Override
     public void setTimeouts(long lockRecheckTimeMillis, long lockWaitTimeoutMillis) {
-        this.lockRecheckTimeMillis = lockRecheckTimeMillis;
         this.lockWaitTimeoutMillis = lockWaitTimeoutMillis;
     }
 
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
index 5da9b4e..3172665 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
@@ -127,6 +127,10 @@ public class JpaRealmProvider implements RealmProvider {
         em.refresh(realm);
         RealmAdapter adapter = new RealmAdapter(session, em, realm);
         session.users().preRemove(adapter);
+
+        realm.getDefaultGroups().clear();
+        em.flush();
+
         int num = em.createNamedQuery("deleteGroupRoleMappingsByRealm")
                 .setParameter("realm", realm).executeUpdate();
         num = em.createNamedQuery("deleteGroupAttributesByRealm")
diff --git a/model/mongo/src/main/java/org/keycloak/connections/mongo/lock/MongoDBLockProvider.java b/model/mongo/src/main/java/org/keycloak/connections/mongo/lock/MongoDBLockProvider.java
index e3383fe..1b36889 100644
--- a/model/mongo/src/main/java/org/keycloak/connections/mongo/lock/MongoDBLockProvider.java
+++ b/model/mongo/src/main/java/org/keycloak/connections/mongo/lock/MongoDBLockProvider.java
@@ -125,6 +125,11 @@ public class MongoDBLockProvider implements DBLockProvider {
     }
 
     @Override
+    public boolean supportsForcedUnlock() {
+        return true;
+    }
+
+    @Override
     public void destroyLockInfo() {
         db.getCollection(DB_LOCK_COLLECTION).remove(new BasicDBObject());
         logger.debugf("Destroyed lock collection");

pom.xml 53(+38 -15)

diff --git a/pom.xml b/pom.xml
index bc90c8a..074a35e 100755
--- a/pom.xml
+++ b/pom.xml
@@ -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.
+-->
 
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
@@ -107,6 +107,7 @@
         <minify.plugin.version>1.7.2</minify.plugin.version>
         <osgi.bundle.plugin.version>2.3.7</osgi.bundle.plugin.version>
         <wildfly.plugin.version>1.0.1.Final</wildfly.plugin.version>
+        <nexus.staging.plugin.version>1.6.5</nexus.staging.plugin.version>
         
         <!-- Surefire Settings -->
         <surefire.memory.settings>-Xms512m -Xmx2048m -XX:MetaspaceSize=96m -XX:MaxMetaspaceSize=256m</surefire.memory.settings>
@@ -1230,6 +1231,16 @@
         <pluginManagement>
             <plugins>
                 <plugin>
+                    <groupId>org.sonatype.plugins</groupId>
+                    <artifactId>nexus-staging-maven-plugin</artifactId>
+                    <version>${nexus.staging.plugin.version}</version>
+                    <extensions>true</extensions>
+                    <configuration>
+                        <nexusUrl>https://repository.jboss.org/nexus</nexusUrl>
+                        <serverId>jboss-releases-repository</serverId>
+                    </configuration>
+                </plugin>
+                <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-release-plugin</artifactId>
                     <version>2.5.2</version>
@@ -1367,5 +1378,17 @@
                 </plugins>
             </build>
         </profile>
+        
+        <profile>
+            <id>nexus-staging</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.sonatype.plugins</groupId>
+                        <artifactId>nexus-staging-maven-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
     </profiles>
 </project>
diff --git a/server-spi/src/main/java/org/keycloak/models/dblock/DBLockProvider.java b/server-spi/src/main/java/org/keycloak/models/dblock/DBLockProvider.java
index b5fb417..d9dc131 100644
--- a/server-spi/src/main/java/org/keycloak/models/dblock/DBLockProvider.java
+++ b/server-spi/src/main/java/org/keycloak/models/dblock/DBLockProvider.java
@@ -34,10 +34,19 @@ public interface DBLockProvider extends Provider {
     void waitForLock();
 
 
+    /**
+     * Release previously acquired lock
+     */
     void releaseLock();
 
 
     /**
+     * @return true if provider supports forced unlock at startup
+     */
+    boolean supportsForcedUnlock();
+
+
+    /**
      * Will destroy whole state of DB lock (drop table/collection to track locking).
      * */
     void destroyLockInfo();
diff --git a/server-spi/src/main/java/org/keycloak/models/KeycloakSessionFactory.java b/server-spi/src/main/java/org/keycloak/models/KeycloakSessionFactory.java
index 3aa15d8..0028ae7 100755
--- a/server-spi/src/main/java/org/keycloak/models/KeycloakSessionFactory.java
+++ b/server-spi/src/main/java/org/keycloak/models/KeycloakSessionFactory.java
@@ -20,8 +20,10 @@ package org.keycloak.models;
 import org.keycloak.provider.Provider;
 import org.keycloak.provider.ProviderEventManager;
 import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
 
 import java.util.List;
+import java.util.Set;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -30,6 +32,8 @@ import java.util.List;
 public interface KeycloakSessionFactory extends ProviderEventManager {
     KeycloakSession create();
 
+    Set<Spi> getSpis();
+
     <T extends Provider> ProviderFactory<T> getProviderFactory(Class<T> clazz);
 
     <T extends Provider> ProviderFactory<T> getProviderFactory(Class<T> clazz, String id);
diff --git a/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
index 64079b0..7f7f7e0 100755
--- a/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
+++ b/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
@@ -717,6 +717,7 @@ public class ModelToRepresentation {
 
     public static AuthenticatorConfigRepresentation toRepresentation(AuthenticatorConfigModel model) {
         AuthenticatorConfigRepresentation rep = new AuthenticatorConfigRepresentation();
+        rep.setId(model.getId());
         rep.setAlias(model.getAlias());
         rep.setConfig(model.getConfig());
         return rep;
diff --git a/services/src/main/java/org/keycloak/email/DefaultEmailSenderProvider.java b/services/src/main/java/org/keycloak/email/DefaultEmailSenderProvider.java
index dc35f39..a2469ec 100644
--- a/services/src/main/java/org/keycloak/email/DefaultEmailSenderProvider.java
+++ b/services/src/main/java/org/keycloak/email/DefaultEmailSenderProvider.java
@@ -25,6 +25,7 @@ import org.keycloak.models.UserModel;
 import org.keycloak.services.ServicesLogger;
 
 import javax.mail.Message;
+import javax.mail.MessagingException;
 import javax.mail.Multipart;
 import javax.mail.Session;
 import javax.mail.Transport;
@@ -54,6 +55,7 @@ public class DefaultEmailSenderProvider implements EmailSenderProvider {
 
     @Override
     public void send(RealmModel realm, UserModel user, String subject, String textBody, String htmlBody) throws EmailException {
+        Transport transport = null;
         try {
             String address = user.getEmail();
             Map<String, String> config = realm.getSmtpConfig();
@@ -114,7 +116,7 @@ public class DefaultEmailSenderProvider implements EmailSenderProvider {
             msg.saveChanges();
             msg.setSentDate(new Date());
 
-            Transport transport = session.getTransport("smtp");
+            transport = session.getTransport("smtp");
             if (auth) {
                 transport.connect(config.get("user"), config.get("password"));
             } else {
@@ -124,6 +126,14 @@ public class DefaultEmailSenderProvider implements EmailSenderProvider {
         } catch (Exception e) {
             logger.failedToSendEmail(e);
             throw new EmailException(e);
+        } finally {
+            if (transport != null) {
+                try {
+                    transport.close();
+                } catch (MessagingException e) {
+                    logger.warn("Failed to close transport", e);
+                }
+            }
         }
     }
 
diff --git a/services/src/main/java/org/keycloak/forms/account/freemarker/FreeMarkerAccountProvider.java b/services/src/main/java/org/keycloak/forms/account/freemarker/FreeMarkerAccountProvider.java
index 893e816..58c39ec 100755
--- a/services/src/main/java/org/keycloak/forms/account/freemarker/FreeMarkerAccountProvider.java
+++ b/services/src/main/java/org/keycloak/forms/account/freemarker/FreeMarkerAccountProvider.java
@@ -188,7 +188,7 @@ public class FreeMarkerAccountProvider implements AccountProvider {
 
         switch (page) {
             case TOTP:
-                attributes.put("totp", new TotpBean(session, realm, user, baseUri));
+                attributes.put("totp", new TotpBean(session, realm, user));
                 break;
             case FEDERATED_IDENTITY:
                 attributes.put("federatedIdentity", new AccountFederatedIdentityBean(session, realm, user, uriInfo.getBaseUri(), stateChecker));
diff --git a/services/src/main/java/org/keycloak/forms/account/freemarker/model/TotpBean.java b/services/src/main/java/org/keycloak/forms/account/freemarker/model/TotpBean.java
old mode 100755
new mode 100644
index 1312e5d..197f985
--- a/services/src/main/java/org/keycloak/forms/account/freemarker/model/TotpBean.java
+++ b/services/src/main/java/org/keycloak/forms/account/freemarker/model/TotpBean.java
@@ -14,12 +14,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.keycloak.forms.account.freemarker.model;
 
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.utils.Base32;
+import org.keycloak.models.utils.HmacOTP;
+import org.keycloak.utils.TotpUtils;
 
 import java.io.UnsupportedEncodingException;
 import java.net.URI;
@@ -34,35 +37,15 @@ public class TotpBean {
 
     private final String totpSecret;
     private final String totpSecretEncoded;
+    private final String totpSecretQrCode;
     private final boolean enabled;
-    private final String contextUrl;
-    private final String keyUri;
 
-    public TotpBean(KeycloakSession session, RealmModel realm, UserModel user, URI baseUri) {
+    public TotpBean(KeycloakSession session, RealmModel realm, UserModel user) {
         this.enabled = session.users().configuredForCredentialType(realm.getOTPPolicy().getType(), realm, user);
-        this.contextUrl = baseUri.getPath();
-
-        this.totpSecret = randomString(20);
-        this.totpSecretEncoded = Base32.encode(totpSecret.getBytes());
-        this.keyUri = realm.getOTPPolicy().getKeyURI(realm, user, this.totpSecret);
-    }
-
-    private static String randomString(int length) {
-        String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVW1234567890";
-        StringBuilder sb = new StringBuilder();
-        for (int i = 0; i < length; i++) {
-            char c = chars.charAt(random.nextInt(chars.length()));
-            sb.append(c);
-        }
-        return sb.toString();
-    }
-
-    private static final SecureRandom random;
 
-    static
-    {
-        random = new SecureRandom();
-        random.nextInt();
+        this.totpSecret = HmacOTP.generateSecret(20);
+        this.totpSecretEncoded = TotpUtils.encode(totpSecret);
+        this.totpSecretQrCode = TotpUtils.qrCode(totpSecret, realm, user);
     }
 
     public boolean isEnabled() {
@@ -74,19 +57,11 @@ public class TotpBean {
     }
 
     public String getTotpSecretEncoded() {
-        StringBuilder sb = new StringBuilder();
-        for (int i = 0; i < totpSecretEncoded.length(); i += 4) {
-            sb.append(totpSecretEncoded.substring(i, i + 4 < totpSecretEncoded.length() ? i + 4 : totpSecretEncoded.length()));
-            if (i + 4 < totpSecretEncoded.length()) {
-                sb.append(" ");
-            }
-        }
-        return sb.toString();
+        return totpSecretEncoded;
     }
 
-    public String getTotpSecretQrCodeUrl() throws UnsupportedEncodingException {
-        String contents = URLEncoder.encode(keyUri, "utf-8");
-        return contextUrl + "qrcode" + "?size=246x246&contents=" + contents;
+    public String getTotpSecretQrCode() {
+        return totpSecretQrCode;
     }
 
 }
diff --git a/services/src/main/java/org/keycloak/forms/login/freemarker/FreeMarkerLoginFormsProvider.java b/services/src/main/java/org/keycloak/forms/login/freemarker/FreeMarkerLoginFormsProvider.java
index 1a81870..44ee44d 100755
--- a/services/src/main/java/org/keycloak/forms/login/freemarker/FreeMarkerLoginFormsProvider.java
+++ b/services/src/main/java/org/keycloak/forms/login/freemarker/FreeMarkerLoginFormsProvider.java
@@ -279,7 +279,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
 
         switch (page) {
             case LOGIN_CONFIG_TOTP:
-                attributes.put("totp", new TotpBean(realm, user, baseUri));
+                attributes.put("totp", new TotpBean(realm, user));
                 break;
             case LOGIN_UPDATE_PROFILE:
                 UpdateProfileContext userCtx = (UpdateProfileContext) attributes.get(LoginFormsProvider.UPDATE_PROFILE_CONTEXT_ATTR);
diff --git a/services/src/main/java/org/keycloak/forms/login/freemarker/model/TotpBean.java b/services/src/main/java/org/keycloak/forms/login/freemarker/model/TotpBean.java
index a403176..d1e1d9c 100755
--- a/services/src/main/java/org/keycloak/forms/login/freemarker/model/TotpBean.java
+++ b/services/src/main/java/org/keycloak/forms/login/freemarker/model/TotpBean.java
@@ -20,6 +20,7 @@ import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.utils.Base32;
 import org.keycloak.models.utils.HmacOTP;
+import org.keycloak.utils.TotpUtils;
 
 import java.io.UnsupportedEncodingException;
 import java.net.URI;
@@ -32,17 +33,15 @@ public class TotpBean {
 
     private final String totpSecret;
     private final String totpSecretEncoded;
+    private final String totpSecretQrCode;
     private final boolean enabled;
-    private final String contextUrl;
-    private final String keyUri;
 
-    public TotpBean(RealmModel realm, UserModel user, URI baseUri) {
+    public TotpBean(RealmModel realm, UserModel user) {
         this.enabled = user.isOtpEnabled();
-        this.contextUrl = baseUri.getPath();
-        
+
         this.totpSecret = HmacOTP.generateSecret(20);
-        this.totpSecretEncoded = Base32.encode(totpSecret.getBytes());
-        this.keyUri = realm.getOTPPolicy().getKeyURI(realm, user, this.totpSecret);
+        this.totpSecretEncoded = TotpUtils.encode(totpSecret);
+        this.totpSecretQrCode = TotpUtils.qrCode(totpSecret, realm, user);
     }
 
     public boolean isEnabled() {
@@ -54,19 +53,11 @@ public class TotpBean {
     }
 
     public String getTotpSecretEncoded() {
-        StringBuilder sb = new StringBuilder();
-        for (int i = 0; i < totpSecretEncoded.length(); i += 4) {
-            sb.append(totpSecretEncoded.substring(i, i + 4 < totpSecretEncoded.length() ? i + 4 : totpSecretEncoded.length()));
-            if (i + 4 < totpSecretEncoded.length()) {
-                sb.append(" ");
-            }
-        }
-        return sb.toString();
+        return totpSecretEncoded;
     }
 
-    public String getTotpSecretQrCodeUrl() throws UnsupportedEncodingException {
-        String contents = URLEncoder.encode(keyUri, "utf-8");
-        return contextUrl + "qrcode" + "?size=246x246&contents=" + contents;
+    public String getTotpSecretQrCode() {
+        return totpSecretQrCode;
     }
 
 }
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/AdapterInstallationClientRegistrationProvider.java b/services/src/main/java/org/keycloak/services/clientregistration/AdapterInstallationClientRegistrationProvider.java
index 65a4339..86a71e3 100644
--- a/services/src/main/java/org/keycloak/services/clientregistration/AdapterInstallationClientRegistrationProvider.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/AdapterInstallationClientRegistrationProvider.java
@@ -17,18 +17,17 @@
 
 package org.keycloak.services.clientregistration;
 
-import org.keycloak.authentication.AuthenticationProcessor;
-import org.keycloak.events.Errors;
 import org.keycloak.events.EventBuilder;
 import org.keycloak.events.EventType;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
-import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil;
-import org.keycloak.services.ForbiddenException;
 import org.keycloak.services.managers.ClientManager;
 import org.keycloak.services.managers.RealmManager;
 
-import javax.ws.rs.*;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 
@@ -52,12 +51,7 @@ public class AdapterInstallationClientRegistrationProvider implements ClientRegi
         event.event(EventType.CLIENT_INFO);
 
         ClientModel client = session.getContext().getRealm().getClientByClientId(clientId);
-
-        if (auth.isAuthenticated()) {
-            auth.requireView(client);
-        } else {
-            authenticateClient(client);
-        }
+        auth.requireView(client);
 
         ClientManager clientManager = new ClientManager(new RealmManager(session));
         Object rep = clientManager.toInstallationRepresentation(session.getContext().getRealm(), client, session.getContext().getAuthServerUrl());
@@ -80,29 +74,4 @@ public class AdapterInstallationClientRegistrationProvider implements ClientRegi
     public void close() {
     }
 
-    private void authenticateClient(ClientModel client) {
-        if (client.isPublicClient()) {
-            return;
-        }
-
-        AuthenticationProcessor processor = AuthorizeClientUtil.getAuthenticationProcessor(session, event);
-
-        Response response = processor.authenticateClient();
-        if (response != null) {
-            event.client(client.getClientId()).error(Errors.NOT_ALLOWED);
-            throw new ForbiddenException();
-        }
-
-        ClientModel authClient = processor.getClient();
-        if (client == null) {
-            event.client(client.getClientId()).error(Errors.NOT_ALLOWED);
-            throw new ForbiddenException();
-        }
-
-        if (!authClient.getClientId().equals(client.getClientId())) {
-            event.client(client.getClientId()).error(Errors.NOT_ALLOWED);
-            throw new ForbiddenException();
-        }
-    }
-
 }
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationAuth.java b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationAuth.java
index 5dc7285..5564ef2 100644
--- a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationAuth.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationAuth.java
@@ -17,20 +17,28 @@
 
 package org.keycloak.services.clientregistration;
 
-import com.sun.xml.bind.v2.runtime.reflect.opt.Const;
+import org.jboss.resteasy.spi.Failure;
+import org.jboss.resteasy.spi.NotFoundException;
 import org.jboss.resteasy.spi.UnauthorizedException;
 import org.keycloak.Config;
+import org.keycloak.authentication.AuthenticationProcessor;
 import org.keycloak.common.util.Time;
 import org.keycloak.events.Errors;
 import org.keycloak.events.EventBuilder;
-import org.keycloak.models.*;
+import org.keycloak.models.AdminRoles;
+import org.keycloak.models.ClientInitialAccessModel;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.Constants;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil;
 import org.keycloak.representations.JsonWebToken;
 import org.keycloak.services.ForbiddenException;
 import org.keycloak.util.TokenUtil;
 
 import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
@@ -49,8 +57,6 @@ public class ClientRegistrationAuth {
     public ClientRegistrationAuth(KeycloakSession session, EventBuilder event) {
         this.session = session;
         this.event = event;
-
-        init();
     }
 
     private void init() {
@@ -67,41 +73,39 @@ public class ClientRegistrationAuth {
             return;
         }
 
-        jwt = ClientRegistrationTokenUtils.parseToken(realm, uri, split[1]);
+        jwt = ClientRegistrationTokenUtils.verifyToken(realm, uri, split[1]);
+        if (jwt == null) {
+            throw unauthorized();
+        }
 
         if (isInitialAccessToken()) {
             initialAccessModel = session.sessions().getClientInitialAccessModel(session.getContext().getRealm(), jwt.getId());
             if (initialAccessModel == null) {
-                throw new ForbiddenException();
+                throw unauthorized();
             }
         }
     }
 
-    public boolean isAuthenticated() {
-        return jwt != null;
-    }
-
-    public boolean isBearerToken() {
-        return TokenUtil.TOKEN_TYPE_BEARER.equals(jwt.getType());
+    private boolean isBearerToken() {
+        return jwt != null && TokenUtil.TOKEN_TYPE_BEARER.equals(jwt.getType());
     }
 
     public boolean isInitialAccessToken() {
-        return ClientRegistrationTokenUtils.TYPE_INITIAL_ACCESS_TOKEN.equals(jwt.getType());
+        return jwt != null && ClientRegistrationTokenUtils.TYPE_INITIAL_ACCESS_TOKEN.equals(jwt.getType());
     }
 
     public boolean isRegistrationAccessToken() {
-        return ClientRegistrationTokenUtils.TYPE_REGISTRATION_ACCESS_TOKEN.equals(jwt.getType());
+        return jwt != null && ClientRegistrationTokenUtils.TYPE_REGISTRATION_ACCESS_TOKEN.equals(jwt.getType());
     }
 
     public void requireCreate() {
-        if (!isAuthenticated()) {
-            event.error(Errors.NOT_ALLOWED);
-            throw new UnauthorizedException();
-        }
+        init();
 
         if (isBearerToken()) {
             if (hasRole(AdminRoles.MANAGE_CLIENTS, AdminRoles.CREATE_CLIENT)) {
                 return;
+            } else {
+                throw forbidden();
             }
         } else if (isInitialAccessToken()) {
             if (initialAccessModel.getRemainingCount() > 0) {
@@ -111,58 +115,55 @@ public class ClientRegistrationAuth {
             }
         }
 
-        event.error(Errors.NOT_ALLOWED);
-        throw new ForbiddenException();
+        throw unauthorized();
     }
 
     public void requireView(ClientModel client) {
-        if (!isAuthenticated()) {
-            event.error(Errors.NOT_ALLOWED);
-            throw new UnauthorizedException();
-        }
-
-        if (client == null) {
-            event.error(Errors.NOT_ALLOWED);
-            throw new ForbiddenException();
-        }
+        init();
 
         if (isBearerToken()) {
             if (hasRole(AdminRoles.MANAGE_CLIENTS, AdminRoles.VIEW_CLIENTS)) {
+                if (client == null) {
+                    throw notFound();
+                }
                 return;
+            } else {
+                throw forbidden();
             }
         } else if (isRegistrationAccessToken()) {
-            if (client.getRegistrationToken() != null && client.getRegistrationToken().equals(jwt.getId())) {
+            if (client.getRegistrationToken() != null && client != null && client.getRegistrationToken().equals(jwt.getId())) {
+                return;
+            }
+        } else if (isInitialAccessToken()) {
+            throw unauthorized();
+        } else {
+            if (authenticateClient(client)) {
                 return;
             }
         }
 
-        event.error(Errors.NOT_ALLOWED);
-        throw new ForbiddenException();
+        throw unauthorized();
     }
 
     public void requireUpdate(ClientModel client) {
-        if (!isAuthenticated()) {
-            event.error(Errors.NOT_ALLOWED);
-            throw new UnauthorizedException();
-        }
-
-        if (client == null) {
-            event.error(Errors.NOT_ALLOWED);
-            throw new ForbiddenException();
-        }
+        init();
 
         if (isBearerToken()) {
             if (hasRole(AdminRoles.MANAGE_CLIENTS)) {
+                if (client == null) {
+                    throw notFound();
+                }
                 return;
+            } else {
+                throw forbidden();
             }
         } else if (isRegistrationAccessToken()) {
-            if (client.getRegistrationToken() != null && client.getRegistrationToken().equals(jwt.getId())) {
+            if (client.getRegistrationToken() != null && client != null && client.getRegistrationToken().equals(jwt.getId())) {
                 return;
             }
         }
 
-        event.error(Errors.NOT_ALLOWED);
-        throw new ForbiddenException();
+        throw unauthorized();
     }
 
     public ClientInitialAccessModel getInitialAccessModel() {
@@ -207,4 +208,46 @@ public class ClientRegistrationAuth {
         }
     }
 
+    private boolean authenticateClient(ClientModel client) {
+        if (client.isPublicClient()) {
+            return true;
+        }
+
+        AuthenticationProcessor processor = AuthorizeClientUtil.getAuthenticationProcessor(session, event);
+
+        Response response = processor.authenticateClient();
+        if (response != null) {
+            event.client(client.getClientId()).error(Errors.NOT_ALLOWED);
+            throw unauthorized();
+        }
+
+        ClientModel authClient = processor.getClient();
+        if (client == null) {
+            event.client(client.getClientId()).error(Errors.NOT_ALLOWED);
+            throw unauthorized();
+        }
+
+        if (!authClient.getClientId().equals(client.getClientId())) {
+            event.client(client.getClientId()).error(Errors.NOT_ALLOWED);
+            throw unauthorized();
+        }
+
+        return true;
+    }
+
+    private Failure unauthorized() {
+        event.error(Errors.NOT_ALLOWED);
+        return new UnauthorizedException();
+    }
+
+    private Failure forbidden() {
+        event.error(Errors.NOT_ALLOWED);
+        return new ForbiddenException();
+    }
+
+    private Failure notFound() {
+        event.error(Errors.NOT_ALLOWED);
+        return new NotFoundException("Client not found");
+    }
+
 }
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationTokenUtils.java b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationTokenUtils.java
index 2cd74ba..2fe65cc 100755
--- a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationTokenUtils.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationTokenUtils.java
@@ -28,7 +28,6 @@ import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.representations.JsonWebToken;
-import org.keycloak.services.ForbiddenException;
 import org.keycloak.services.Urls;
 import org.keycloak.util.TokenUtil;
 
@@ -57,37 +56,37 @@ public class ClientRegistrationTokenUtils {
         return createToken(realm, uri, model.getId(), TYPE_INITIAL_ACCESS_TOKEN, model.getExpiration() > 0 ? model.getTimestamp() + model.getExpiration() : 0);
     }
 
-    public static JsonWebToken parseToken(RealmModel realm, UriInfo uri, String token) {
+    public static JsonWebToken verifyToken(RealmModel realm, UriInfo uri, String token) {
         JWSInput input;
         try {
             input = new JWSInput(token);
         } catch (JWSInputException e) {
-            throw new ForbiddenException(e);
+            return null;
         }
 
         if (!RSAProvider.verify(input, realm.getPublicKey())) {
-            throw new ForbiddenException("Invalid signature");
+            return null;
         }
 
         JsonWebToken jwt;
         try {
             jwt = input.readJsonContent(JsonWebToken.class);
         } catch (JWSInputException e) {
-            throw new ForbiddenException(e);
+            return null;
         }
 
         if (!getIssuer(realm, uri).equals(jwt.getIssuer())) {
-            throw new ForbiddenException("Issuer doesn't match");
+            return null;
         }
 
         if (!jwt.isActive()) {
-            throw new ForbiddenException("Expired token");
+            return null;
         }
 
         if (!(TokenUtil.TOKEN_TYPE_BEARER.equals(jwt.getType()) ||
                 TYPE_INITIAL_ACCESS_TOKEN.equals(jwt.getType()) ||
                 TYPE_REGISTRATION_ACCESS_TOKEN.equals(jwt.getType()))) {
-            throw new ForbiddenException("Invalid token type");
+            return null;
         }
 
         return jwt;
diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java
index 3ca473b..90b495c 100755
--- a/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java
+++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java
@@ -40,6 +40,7 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory {
 
     private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER;
 
+    private Set<Spi> spis = new HashSet<>();
     private Map<Class<? extends Provider>, String> provider = new HashMap<Class<? extends Provider>, String>();
     private Map<Class<? extends Provider>, Map<String, ProviderFactory>> factoriesMap = new HashMap<Class<? extends Provider>, Map<String, ProviderFactory>>();
     protected CopyOnWriteArrayList<ProviderEventListener> listeners = new CopyOnWriteArrayList<ProviderEventListener>();
@@ -80,6 +81,8 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory {
 
     protected void loadSPIs(ProviderManager pm, ServiceLoader<Spi> load) {
         for (Spi spi : load) {
+            spis.add(spi);
+
             Map<String, ProviderFactory> factories = new HashMap<String, ProviderFactory>();
             factoriesMap.put(spi.getProviderClass(), factories);
 
@@ -139,6 +142,11 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory {
     }
 
     @Override
+    public Set<Spi> getSpis() {
+        return spis;
+    }
+
+    @Override
     public <T extends Provider> ProviderFactory<T> getProviderFactory(Class<T> clazz) {
          return getProviderFactory(clazz, provider.get(clazz));
     }
diff --git a/services/src/main/java/org/keycloak/services/managers/DBLockManager.java b/services/src/main/java/org/keycloak/services/managers/DBLockManager.java
index 1909c36..8aace4b 100644
--- a/services/src/main/java/org/keycloak/services/managers/DBLockManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/DBLockManager.java
@@ -34,52 +34,38 @@ public class DBLockManager {
 
     protected static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER;
 
-    public void waitForLock(KeycloakSessionFactory sessionFactory) {
-        KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
+    private final KeycloakSession session;
 
-            @Override
-            public void run(KeycloakSession session) {
-                DBLockProvider lock = getDBLock(session);
-                lock.waitForLock();
-            }
-
-        });
+    public DBLockManager(KeycloakSession session) {
+        this.session = session;
     }
 
 
-    public void releaseLock(KeycloakSessionFactory sessionFactory) {
-        KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
-
-            @Override
-            public void run(KeycloakSession session) {
-                DBLockProvider lock = getDBLock(session);
+    public void checkForcedUnlock() {
+        if (Boolean.getBoolean("keycloak.dblock.forceUnlock")) {
+            DBLockProvider lock = getDBLock();
+            if (lock.supportsForcedUnlock()) {
+                logger.forcedReleaseDBLock();
                 lock.releaseLock();
+            } else {
+                throw new IllegalStateException("Forced unlock requested, but provider " + lock + " doesn't support it");
             }
-
-        });
-    }
-
-
-    public void checkForcedUnlock(KeycloakSessionFactory sessionFactory) {
-        if (Boolean.getBoolean("keycloak.dblock.forceUnlock")) {
-            logger.forcedReleaseDBLock();
-            releaseLock(sessionFactory);
         }
     }
 
 
     // Try to detect ID from realmProvider
-    public DBLockProvider getDBLock(KeycloakSession session) {
-        String realmProviderId = getRealmProviderId(session);
+    public DBLockProvider getDBLock() {
+        String realmProviderId = getRealmProviderId();
         return session.getProvider(DBLockProvider.class, realmProviderId);
     }
 
-    public DBLockProviderFactory getDBLockFactory(KeycloakSession session) {
-        String realmProviderId = getRealmProviderId(session);
+    public DBLockProviderFactory getDBLockFactory() {
+        String realmProviderId = getRealmProviderId();
         return (DBLockProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(DBLockProvider.class, realmProviderId);
     }
 
-    private String getRealmProviderId(KeycloakSession session) {
+    private String getRealmProviderId() {
         RealmProviderFactory realmProviderFactory = (RealmProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(RealmProvider.class);
         return realmProviderFactory.getId();
     }
diff --git a/services/src/main/java/org/keycloak/services/messages/Messages.java b/services/src/main/java/org/keycloak/services/messages/Messages.java
index d9a2fe3..7fae66d 100755
--- a/services/src/main/java/org/keycloak/services/messages/Messages.java
+++ b/services/src/main/java/org/keycloak/services/messages/Messages.java
@@ -154,6 +154,10 @@ public class Messages {
 
     public static final String IDENTITY_PROVIDER_LINK_SUCCESS = "identityProviderLinkSuccess";
 
+    public static final String STALE_CODE = "staleCodeMessage";
+
+    public static final String STALE_CODE_ACCOUNT = "staleCodeAccountMessage";
+
     public static final String IDENTITY_PROVIDER_NOT_UNIQUE = "identityProviderNotUniqueMessage";
 
     public static final String REALM_SUPPORTS_NO_CREDENTIALS = "realmSupportsNoCredentialsMessage";
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java b/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java
index 54378bf..1e42aad 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java
@@ -389,10 +389,16 @@ public class AuthenticationManagementResource {
         String provider = data.get("provider");
 
         // make sure provider is one of the registered providers
-        ProviderFactory f = session.getKeycloakSessionFactory().getProviderFactory(Authenticator.class, provider);
+        ProviderFactory f;
+        if (parentFlow.getProviderId().equals(AuthenticationFlow.CLIENT_FLOW)) {
+            f = session.getKeycloakSessionFactory().getProviderFactory(ClientAuthenticator.class, provider);
+        } else {
+            f = session.getKeycloakSessionFactory().getProviderFactory(Authenticator.class, provider);
+        }
         if (f == null) {
             throw new BadRequestException("No authentication provider found for id: " + provider);
         }
+
         AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
         execution.setParentFlow(parentFlow.getId());
         execution.setRequirement(AuthenticationExecutionModel.Requirement.DISABLED);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/info/ServerInfoAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/info/ServerInfoAdminResource.java
index 4454ccb..3ea8fd1 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/info/ServerInfoAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/info/ServerInfoAdminResource.java
@@ -95,10 +95,7 @@ public class ServerInfoAdminResource {
     private void setProviders(ServerInfoRepresentation info) {
         LinkedHashMap<String, SpiInfoRepresentation> spiReps = new LinkedHashMap<>();
 
-        List<Spi> spis = new LinkedList<>();
-        for (Spi spi : ServiceLoader.load(Spi.class)) {
-            spis.add(spi);
-        }
+        List<Spi> spis = new LinkedList<>(session.getKeycloakSessionFactory().getSpis());
         Collections.sort(spis, new Comparator<Spi>() {
             @Override
             public int compare(Spi s1, Spi s2) {
diff --git a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
index 27d47b5..a9fb79f 100755
--- a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
+++ b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
@@ -34,6 +34,7 @@ import org.keycloak.common.ClientConnection;
 import org.keycloak.common.util.ObjectUtil;
 import org.keycloak.common.util.Time;
 import org.keycloak.events.Details;
+import org.keycloak.events.Errors;
 import org.keycloak.events.EventBuilder;
 import org.keycloak.events.EventType;
 import org.keycloak.forms.login.LoginFormsProvider;
@@ -146,7 +147,12 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
         }
 
         try {
-            ClientSessionCode clientSessionCode = parseClientSessionCode(code);
+            ParsedCodeContext parsedCode = parseClientSessionCode(code);
+            if (parsedCode.response != null) {
+                return parsedCode.response;
+            }
+
+            ClientSessionCode clientSessionCode = parsedCode.clientSessionCode;
             IdentityProvider identityProvider = getIdentityProvider(session, realmModel, providerId);
             Response response = identityProvider.performLogin(createAuthenticationRequest(providerId, clientSessionCode));
 
@@ -245,14 +251,14 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
     }
 
     public Response authenticated(BrokeredIdentityContext context) {
-        ClientSessionCode clientCode = null;
         IdentityProviderModel identityProviderConfig = context.getIdpConfig();
-        try {
-            clientCode = parseClientSessionCode(context.getCode());
-        } catch (Exception e) {
-            return redirectToErrorPage(Messages.IDENTITY_PROVIDER_AUTHENTICATION_FAILED, e, identityProviderConfig.getProviderId());
 
+        ParsedCodeContext parsedCode = parseClientSessionCode(context.getCode());
+        if (parsedCode.response != null) {
+            return parsedCode.response;
         }
+        ClientSessionCode clientCode = parsedCode.clientSessionCode;
+
         String providerId = identityProviderConfig.getAlias();
         if (!identityProviderConfig.isStoreToken()) {
             if (isDebugEnabled()) {
@@ -318,6 +324,11 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
             return Response.status(302).location(redirect).build();
 
         } else {
+            Response response = validateUser(federatedUser, realmModel);
+            if (response != null) {
+                return response;
+            }
+
             updateFederatedIdentity(context, federatedUser);
             clientSession.setAuthenticatedUser(federatedUser);
 
@@ -325,12 +336,27 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
         }
     }
 
+    public Response validateUser(UserModel user, RealmModel realm) {
+        if (!user.isEnabled()) {
+            event.error(Errors.USER_DISABLED);
+            return ErrorPage.error(session, Messages.ACCOUNT_DISABLED);
+        }
+        if (realm.isBruteForceProtected()) {
+            event.error(Errors.USER_TEMPORARILY_DISABLED);
+            return ErrorPage.error(session, Messages.ACCOUNT_DISABLED);
+        }
+        return null;
+    }
+
     // Callback from LoginActionsService after first login with broker was done and Keycloak account is successfully linked/created
     @GET
     @Path("/after-first-broker-login")
     public Response afterFirstBrokerLogin(@QueryParam("code") String code) {
-        ClientSessionCode clientCode = parseClientSessionCode(code);
-        ClientSessionModel clientSession = clientCode.getClientSession();
+        ParsedCodeContext parsedCode = parseClientSessionCode(code);
+        if (parsedCode.response != null) {
+            return parsedCode.response;
+        }
+        ClientSessionModel clientSession = parsedCode.clientSessionCode.getClientSession();
 
         try {
             this.event.detail(Details.CODE_ID, clientSession.getId())
@@ -443,8 +469,11 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
     @GET
     @Path("/after-post-broker-login")
     public Response afterPostBrokerLoginFlow(@QueryParam("code") String code) {
-        ClientSessionCode clientCode = parseClientSessionCode(code);
-        ClientSessionModel clientSession = clientCode.getClientSession();
+        ParsedCodeContext parsedCode = parseClientSessionCode(code);
+        if (parsedCode.response != null) {
+            return parsedCode.response;
+        }
+        ClientSessionModel clientSession = parsedCode.clientSessionCode.getClientSession();
 
         try {
             SerializedBrokeredIdentityContext serializedCtx = SerializedBrokeredIdentityContext.readFromClientSession(clientSession, PostBrokerLoginConstants.PBL_BROKERED_IDENTITY_CONTEXT);
@@ -530,9 +559,15 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
 
     @Override
     public Response cancelled(String code) {
-        ClientSessionCode clientCode = ClientSessionCode.parse(code, this.session, this.realmModel);
-        if (clientCode.getClientSession() == null || !clientCode.isValid(AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) {
-            return redirectToErrorPage(Messages.INVALID_CODE);
+        ParsedCodeContext parsedCode = parseClientSessionCode(code);
+        if (parsedCode.response != null) {
+            return parsedCode.response;
+        }
+        ClientSessionCode clientCode = parsedCode.clientSessionCode;
+
+        Response accountManagementFailedLinking = checkAccountManagementFailedLinking(clientCode.getClientSession(), Messages.CONSENT_DENIED);
+        if (accountManagementFailedLinking != null) {
+            return accountManagementFailedLinking;
         }
 
         return browserAuthentication(clientCode.getClientSession(), null);
@@ -540,10 +575,17 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
 
     @Override
     public Response error(String code, String message) {
-        ClientSessionCode clientCode = ClientSessionCode.parse(code, this.session, this.realmModel);
-        if (clientCode.getClientSession() == null || !clientCode.isValid(AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) {
-            return redirectToErrorPage(Messages.INVALID_CODE);
+        ParsedCodeContext parsedCode = parseClientSessionCode(code);
+        if (parsedCode.response != null) {
+            return parsedCode.response;
+        }
+        ClientSessionCode clientCode = parsedCode.clientSessionCode;
+
+        Response accountManagementFailedLinking = checkAccountManagementFailedLinking(clientCode.getClientSession(), message);
+        if (accountManagementFailedLinking != null) {
+            return accountManagementFailedLinking;
         }
+
         return browserAuthentication(clientCode.getClientSession(), message);
     }
 
@@ -604,36 +646,60 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
 
     }
 
-    private ClientSessionCode parseClientSessionCode(String code) {
+    private ParsedCodeContext parseClientSessionCode(String code) {
         ClientSessionCode clientCode = ClientSessionCode.parse(code, this.session, this.realmModel);
 
-        if (clientCode != null && clientCode.isValid(AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) {
+        if (clientCode != null) {
             ClientSessionModel clientSession = clientCode.getClientSession();
 
-            if (clientSession != null) {
-                ClientModel client = clientSession.getClient();
+            if (clientSession.getUserSession() != null) {
+                this.event.session(clientSession.getUserSession());
+            }
 
-                if (client == null) {
-                    throw new IdentityBrokerException("Invalid client");
-                }
+            ClientModel client = clientSession.getClient();
+
+            if (client != null) {
 
                 logger.debugf("Got authorization code from client [%s].", client.getClientId());
                 this.event.client(client);
                 this.session.getContext().setClient(client);
 
-                if (clientSession.getUserSession() != null) {
-                    this.event.session(clientSession.getUserSession());
+                if (!clientCode.isValid(AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) {
+                    logger.debugf("Authorization code is not valid. Client session ID: %s, Client session's action: %s", clientSession.getId(), clientSession.getAction());
+
+                    // Check if error happened during login or during linking from account management
+                    Response accountManagementFailedLinking = checkAccountManagementFailedLinking(clientCode.getClientSession(), Messages.STALE_CODE_ACCOUNT);
+                    Response staleCodeError = (accountManagementFailedLinking != null) ? accountManagementFailedLinking : redirectToErrorPage(Messages.STALE_CODE);
+
+
+                    return ParsedCodeContext.response(staleCodeError);
                 }
-            }
 
-            if (isDebugEnabled()) {
-                logger.debugf("Authorization code is valid.");
-            }
+                if (isDebugEnabled()) {
+                    logger.debugf("Authorization code is valid.");
+                }
 
-            return clientCode;
+                return ParsedCodeContext.clientSessionCode(clientCode);
+            }
         }
 
-        throw new IdentityBrokerException("Invalid code, please login again through your client.");
+        logger.debugf("Authorization code is not valid. Code: %s", code);
+        Response staleCodeError = redirectToErrorPage(Messages.STALE_CODE);
+        return ParsedCodeContext.response(staleCodeError);
+    }
+
+    private Response checkAccountManagementFailedLinking(ClientSessionModel clientSession, String error, Object... parameters) {
+        if (clientSession.getUserSession() != null && clientSession.getClient() != null && clientSession.getClient().getClientId().equals(ACCOUNT_MANAGEMENT_CLIENT_ID)) {
+
+            this.event.event(EventType.FEDERATED_IDENTITY_LINK);
+            UserModel user = clientSession.getUserSession().getUser();
+            this.event.user(user);
+            this.event.detail(Details.USERNAME, user.getUsername());
+
+            return redirectToAccountErrorPage(clientSession, error, parameters);
+        } else {
+            return null;
+        }
     }
 
     private AuthenticationRequest createAuthenticationRequest(String providerId, ClientSessionCode clientSessionCode) {
@@ -808,4 +874,22 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
             this.session.getTransaction().rollback();
         }
     }
+
+
+    private static class ParsedCodeContext {
+        private ClientSessionCode clientSessionCode;
+        private Response response;
+
+        public static ParsedCodeContext clientSessionCode(ClientSessionCode clientSessionCode) {
+            ParsedCodeContext ctx = new ParsedCodeContext();
+            ctx.clientSessionCode = clientSessionCode;
+            return ctx;
+        }
+
+        public static ParsedCodeContext response(Response response) {
+            ParsedCodeContext ctx = new ParsedCodeContext();
+            ctx.response = response;
+            return ctx;
+        }
+    }
 }
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 4de67ed..0294e9c 100644
--- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
+++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
@@ -25,6 +25,7 @@ import org.keycloak.Config;
 import org.keycloak.exportimport.ExportImportManager;
 import org.keycloak.migration.MigrationModelManager;
 import org.keycloak.models.*;
+import org.keycloak.models.dblock.DBLockProvider;
 import org.keycloak.services.managers.DBLockManager;
 import org.keycloak.models.utils.PostMigrationEvent;
 import org.keycloak.models.utils.RepresentationToModel;
@@ -82,7 +83,6 @@ public class KeycloakApplication extends Application {
         singletons.add(new ServerVersionResource());
         singletons.add(new RealmsResource());
         singletons.add(new AdminRoot());
-        classes.add(QRCodeResource.class);
         classes.add(ThemeResource.class);
         classes.add(JsResource.class);
 
@@ -92,9 +92,10 @@ public class KeycloakApplication extends Application {
 
         ExportImportManager exportImportManager;
 
-        DBLockManager dbLockManager = new DBLockManager();
-        dbLockManager.checkForcedUnlock(sessionFactory);
-        dbLockManager.waitForLock(sessionFactory);
+        DBLockManager dbLockManager = new DBLockManager(sessionFactory.create());
+        dbLockManager.checkForcedUnlock();
+        DBLockProvider dbLock = dbLockManager.getDBLock();
+        dbLock.waitForLock();
         try {
             migrateModel();
 
@@ -131,7 +132,7 @@ public class KeycloakApplication extends Application {
 
             importAddUser();
         } finally {
-            dbLockManager.releaseLock(sessionFactory);
+            dbLock.releaseLock();
         }
 
         if (exportImportManager.isRunExport()) {
diff --git a/services/src/main/java/org/keycloak/utils/TotpUtils.java b/services/src/main/java/org/keycloak/utils/TotpUtils.java
new file mode 100644
index 0000000..f9f3b60
--- /dev/null
+++ b/services/src/main/java/org/keycloak/utils/TotpUtils.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.utils;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.client.j2se.MatrixToImageWriter;
+import com.google.zxing.common.BitMatrix;
+import com.google.zxing.qrcode.QRCodeWriter;
+import org.keycloak.common.util.Base64;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.Base32;
+
+import java.io.ByteArrayOutputStream;
+import java.net.URLEncoder;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class TotpUtils {
+
+    public static String encode(String totpSecret) {
+        String encoded = Base32.encode(totpSecret.getBytes());
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < encoded.length(); i += 4) {
+            sb.append(encoded.substring(i, i + 4 < encoded.length() ? i + 4 : encoded.length()));
+            if (i + 4 < encoded.length()) {
+                sb.append(" ");
+            }
+        }
+        return sb.toString();
+    }
+
+    public static String qrCode(String totpSecret, RealmModel realm, UserModel user) {
+        try {
+            String keyUri = realm.getOTPPolicy().getKeyURI(realm, user, totpSecret);
+
+            int width = 246;
+            int height = 246;
+
+            QRCodeWriter writer = new QRCodeWriter();
+            final BitMatrix bitMatrix = writer.encode(keyUri, BarcodeFormat.QR_CODE, width, height);
+
+            ByteArrayOutputStream bos = new ByteArrayOutputStream();
+            MatrixToImageWriter.writeToStream(bitMatrix, "png", bos);
+            bos.close();
+
+            return Base64.encodeBytes(bos.toByteArray());
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/RealmTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/RealmTest.java
index 6b842b0..40ed6ee 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/RealmTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/RealmTest.java
@@ -27,6 +27,7 @@ import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.GroupRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.representations.idm.RoleRepresentation;
 import org.keycloak.services.managers.RealmManager;
@@ -35,6 +36,7 @@ import org.keycloak.util.JsonSerialization;
 
 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.LinkedList;
@@ -441,4 +443,22 @@ public class RealmTest extends AbstractClientTest {
         assertEquals(certificate, rep.getCertificate());
     }
 
+    @Test
+    // KEYCLOAK-2700
+    public void deleteRealmWithDefaultGroups() throws IOException {
+        RealmRepresentation rep = new RealmRepresentation();
+        rep.setRealm("foo");
+
+        GroupRepresentation group = new GroupRepresentation();
+        group.setName("default1");
+        group.setPath("/default1");
+
+        rep.setGroups(Collections.singletonList(group));
+        rep.setDefaultGroups(Collections.singletonList("/default1"));
+
+        keycloak.realms().create(rep);
+
+        keycloak.realm(rep.getRealm()).remove();
+    }
+
 }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/UserTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/UserTest.java
index eafe169..c2f4b7c 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/UserTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/UserTest.java
@@ -240,6 +240,20 @@ public class UserTest extends AbstractClientTest {
     }
 
     @Test
+    public void delete() {
+        Response response = realm.users().delete( createUser() );
+        assertEquals(204, response.getStatus());
+        response.close();
+    }
+
+    @Test
+    public void deleteNonExistent() {
+        Response response = realm.users().delete( "does-not-exist" );
+        assertEquals(404, response.getStatus());
+        response.close();
+    }
+
+    @Test
     public void searchPaginated() {
         createUsers();
 
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
index b45213a..4ccf3cb 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
@@ -40,6 +40,7 @@ import org.keycloak.testsuite.broker.util.UserSessionStatusServlet.UserSessionSt
 import org.keycloak.testsuite.pages.AccountFederatedIdentityPage;
 import org.keycloak.testsuite.pages.AccountPasswordPage;
 import org.keycloak.testsuite.pages.AccountUpdateProfilePage;
+import org.keycloak.testsuite.pages.ErrorPage;
 import org.keycloak.testsuite.pages.LoginPage;
 import org.keycloak.testsuite.pages.LoginUpdateProfilePage;
 import org.keycloak.testsuite.pages.OAuthGrantPage;
@@ -109,6 +110,9 @@ public abstract class AbstractIdentityProviderTest {
     @WebResource
     protected AccountFederatedIdentityPage accountFederatedIdentityPage;
 
+    @WebResource
+    protected ErrorPage errorPage;
+
     protected KeycloakSession session;
 
     protected int logoutTimeOffset = 0;
@@ -168,10 +172,6 @@ public abstract class AbstractIdentityProviderTest {
             Time.setOffset(0);
         }
 
-        String afterLogoutUrl = driver.getCurrentUrl();
-        String afterLogoutPageSource = driver.getPageSource();
-        System.out.println("afterLogoutUrl: " + afterLogoutUrl);
-        //System.out.println("after logout page source: " + afterLogoutPageSource);
 
         driver.navigate().to("http://localhost:8081/test-app");
 
@@ -215,7 +215,6 @@ public abstract class AbstractIdentityProviderTest {
 
         String currentUrl = this.driver.getCurrentUrl();
         assertTrue(currentUrl.startsWith("http://localhost:8082/auth/"));
-        System.out.println(this.driver.getCurrentUrl());
         // log in to identity provider
         this.loginPage.login(username, "password");
         doAfterProviderAuthentication();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java
index 76911d2..1af21f5 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java
@@ -38,6 +38,7 @@ import org.keycloak.models.ClientModel;
 import org.keycloak.models.Constants;
 import org.keycloak.models.FederatedIdentityModel;
 import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserFederationProviderModel;
@@ -70,6 +71,73 @@ public abstract class AbstractKeycloakIdentityProviderTest extends AbstractIdent
     }
 
     @Test
+    public void testDisabledUser() {
+        setUpdateProfileFirstLogin(session.realms().getRealmByName("realm-with-broker"), IdentityProviderRepresentation.UPFLM_OFF);
+
+        driver.navigate().to("http://localhost:8081/test-app");
+        loginPage.clickSocial(getProviderId());
+        loginPage.login("test-user", "password");
+        System.out.println(driver.getPageSource());
+        driver.navigate().to("http://localhost:8081/test-app/logout");
+
+        try {
+            KeycloakSession session = brokerServerRule.startSession();
+            session.users().getUserByUsername("test-user", session.realms().getRealmByName("realm-with-broker")).setEnabled(false);
+            brokerServerRule.stopSession(session, true);
+
+            driver.navigate().to("http://localhost:8081/test-app");
+
+            loginPage.clickSocial(getProviderId());
+            loginPage.login("test-user", "password");
+
+            assertTrue(errorPage.isCurrent());
+            assertEquals("Account is disabled, contact admin.", errorPage.getError());
+        } finally {
+            KeycloakSession session = brokerServerRule.startSession();
+            session.users().getUserByUsername("test-user", session.realms().getRealmByName("realm-with-broker")).setEnabled(true);
+            brokerServerRule.stopSession(session, true);
+        }
+    }
+
+    @Test
+    public void testTemporarilyDisabledUser() {
+        setUpdateProfileFirstLogin(session.realms().getRealmByName("realm-with-broker"), IdentityProviderRepresentation.UPFLM_OFF);
+
+        driver.navigate().to("http://localhost:8081/test-app");
+        loginPage.clickSocial(getProviderId());
+        loginPage.login("test-user", "password");
+        driver.navigate().to("http://localhost:8081/test-app/logout");
+
+        try {
+            KeycloakSession session = brokerServerRule.startSession();
+            RealmModel brokerRealm = session.realms().getRealmByName("realm-with-broker");
+            brokerRealm.setBruteForceProtected(true);
+            brokerRealm.setFailureFactor(2);
+            brokerServerRule.stopSession(session, true);
+
+            driver.navigate().to("http://localhost:8081/test-app");
+            loginPage.login("test-user", "fail");
+            loginPage.login("test-user", "fail");
+
+            driver.navigate().to("http://localhost:8081/test-app");
+
+            assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
+
+            loginPage.clickSocial(getProviderId());
+            loginPage.login("test-user", "password");
+
+            assertTrue(errorPage.isCurrent());
+            assertEquals("Account is disabled, contact admin.", errorPage.getError());
+        } finally {
+            KeycloakSession session = brokerServerRule.startSession();
+            RealmModel brokerRealm = session.realms().getRealmByName("realm-with-broker");
+            brokerRealm.setBruteForceProtected(false);
+            brokerRealm.setFailureFactor(0);
+            brokerServerRule.stopSession(session, true);
+        }
+    }
+
+    @Test
     public void testSuccessfulAuthenticationUpdateProfileOnMissing_nothingMissing() {
         IdentityProviderModel identityProviderModel = getIdentityProviderModel();
         setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_MISSING);
@@ -362,7 +430,6 @@ public abstract class AbstractKeycloakIdentityProviderTest extends AbstractIdent
         revokeGrant();
 
         // Logout from account management
-        System.out.println("*** logout from account management");
         accountFederatedIdentityPage.logout();
         assertTrue(driver.getTitle().equals("Log in to realm-with-broker"));
         assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
@@ -502,7 +569,7 @@ public abstract class AbstractKeycloakIdentityProviderTest extends AbstractIdent
 
             driver.navigate().to("http://localhost:8081/test-app/logout");
             String currentUrl = this.driver.getCurrentUrl();
-            System.out.println("after logout currentUrl: " + currentUrl);
+//            System.out.println("after logout currentUrl: " + currentUrl);
             assertTrue(currentUrl.startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
 
             unconfigureUserRetrieveToken("test-user");
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeyCloakServerBrokerBasicTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeyCloakServerBrokerBasicTest.java
index ad3e9ff..5626751 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeyCloakServerBrokerBasicTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeyCloakServerBrokerBasicTest.java
@@ -30,7 +30,6 @@ import org.keycloak.services.managers.RealmManager;
 import org.keycloak.testsuite.Constants;
 import org.keycloak.testsuite.KeycloakServer;
 import org.keycloak.testsuite.pages.AccountApplicationsPage;
-import org.keycloak.testsuite.pages.OAuthGrantPage;
 import org.keycloak.testsuite.rule.AbstractKeycloakRule;
 import org.keycloak.testsuite.rule.WebResource;
 import org.keycloak.util.JsonSerialization;
@@ -69,9 +68,6 @@ public class OIDCKeyCloakServerBrokerBasicTest extends AbstractKeycloakIdentityP
     };
 
     @WebResource
-    private OAuthGrantPage grantPage;
-
-    @WebResource
     protected AccountApplicationsPage accountApplicationsPage;
 
     @Override
@@ -120,6 +116,16 @@ public class OIDCKeyCloakServerBrokerBasicTest extends AbstractKeycloakIdentityP
     }
 
     @Test
+    public void testDisabledUser() {
+        super.testDisabledUser();
+    }
+
+    @Test
+    public void testTemporarilyDisabledUser() {
+        super.testTemporarilyDisabledUser();
+    }
+
+    @Test
     public void testLogoutWorksWithTokenTimeout() {
         Keycloak keycloak = Keycloak.getInstance("http://localhost:8081/auth", "master", "admin", "admin", org.keycloak.models.Constants.ADMIN_CLI_CLIENT_ID);
         RealmRepresentation realm = keycloak.realm("realm-with-oidc-identity-provider").toRepresentation();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeycloakServerBrokerWithConsentTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeycloakServerBrokerWithConsentTest.java
new file mode 100644
index 0000000..d648cdd
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeycloakServerBrokerWithConsentTest.java
@@ -0,0 +1,264 @@
+/*
+ * 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.broker;
+
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.keycloak.admin.client.Keycloak;
+import org.keycloak.admin.client.resource.RealmResource;
+import org.keycloak.common.util.Time;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.IdentityProviderRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.testsuite.KeycloakServer;
+import org.keycloak.testsuite.rule.AbstractKeycloakRule;
+import org.openqa.selenium.NoSuchElementException;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class OIDCKeycloakServerBrokerWithConsentTest extends AbstractIdentityProviderTest {
+
+    private static final int PORT = 8082;
+
+    private static Keycloak keycloak1;
+    private static Keycloak keycloak2;
+
+    @ClassRule
+    public static AbstractKeycloakRule oidcServerRule = new AbstractKeycloakRule() {
+
+        @Override
+        protected void configureServer(KeycloakServer server) {
+            server.getConfig().setPort(PORT);
+        }
+
+        @Override
+        protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) {
+            server.importRealm(getClass().getResourceAsStream("/broker-test/test-broker-realm-with-kc-oidc.json"));
+
+            // Disable update profile
+            RealmModel realm = getRealm(session);
+            setUpdateProfileFirstLogin(realm, IdentityProviderRepresentation.UPFLM_OFF);
+        }
+
+        @Override
+        protected String[] getTestRealms() {
+            return new String[] { "realm-with-oidc-identity-provider" };
+        }
+    };
+
+
+    @BeforeClass
+    public static void before() {
+        keycloak1 = Keycloak.getInstance("http://localhost:8081/auth", "master", "admin", "admin", org.keycloak.models.Constants.ADMIN_CLI_CLIENT_ID);
+        keycloak2 = Keycloak.getInstance("http://localhost:8082/auth", "master", "admin", "admin", org.keycloak.models.Constants.ADMIN_CLI_CLIENT_ID);
+
+        // Require broker to show consent screen
+        RealmResource brokeredRealm = keycloak2.realm("realm-with-oidc-identity-provider");
+        List<ClientRepresentation> clients = brokeredRealm.clients().findByClientId("broker-app");
+        Assert.assertEquals(1, clients.size());
+        ClientRepresentation brokerApp = clients.get(0);
+        brokerApp.setConsentRequired(true);
+        brokeredRealm.clients().get(brokerApp.getId()).update(brokerApp);
+
+
+        // Change timeouts on realm-with-broker to lower values
+        RealmResource realmWithBroker = keycloak1.realm("realm-with-broker");
+        RealmRepresentation realmRep = realmWithBroker.toRepresentation();
+        realmRep.setAccessCodeLifespanLogin(30);;
+        realmRep.setAccessCodeLifespan(30);
+        realmRep.setAccessCodeLifespanUserAction(30);
+        realmWithBroker.update(realmRep);
+    }
+
+
+    @Override
+    protected String getProviderId() {
+        return "kc-oidc-idp";
+    }
+
+
+    // KEYCLOAK-2769
+    @Test
+    public void testConsentDeniedWithExpiredClientSession() throws Exception {
+        // Login to broker
+        loginIDP("test-user");
+
+        // Set time offset
+        Time.setOffset(60);
+        try {
+            // User rejected consent
+            grantPage.assertCurrent();
+            grantPage.cancel();
+
+            // Assert error page with backToApplication link displayed
+            errorPage.assertCurrent();
+            errorPage.clickBackToApplication();
+
+            assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
+
+        } finally {
+            Time.setOffset(0);
+        }
+    }
+
+
+    // KEYCLOAK-2769
+    @Test
+    public void testConsentDeniedWithExpiredAndClearedClientSession() throws Exception {
+        // Login to broker again
+        loginIDP("test-user");
+
+        // Set time offset
+        Time.setOffset(60);
+        try {
+            // Manually remove expiredSessions TODO: Will require custom endpoint when migrate to integration-arquillian
+            brokerServerRule.stopSession(this.session, true);
+            this.session = brokerServerRule.startSession();
+
+            session.sessions().removeExpired(getRealm());
+
+            brokerServerRule.stopSession(this.session, true);
+            this.session = brokerServerRule.startSession();
+
+            // User rejected consent
+            grantPage.assertCurrent();
+            grantPage.cancel();
+
+            // Assert error page without backToApplication link (clientSession expired and was removed on the server)
+            errorPage.assertCurrent();
+            try {
+                errorPage.clickBackToApplication();
+                fail("Not expected to have link backToApplication available");
+            } catch (NoSuchElementException nsee) {
+                // Expected;
+            }
+
+        } finally {
+            Time.setOffset(0);
+        }
+    }
+
+
+    // KEYCLOAK-2801
+    @Test
+    public void testAccountManagementLinkingAndExpiredClientSession() throws Exception {
+        // Login as pedroigor to account management
+        loginToAccountManagement("pedroigor");
+
+        // Link my "pedroigor" identity with "test-user" from brokered Keycloak
+        accountFederatedIdentityPage.clickAddProvider(getProviderId());
+
+        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8082/auth/"));
+        this.loginPage.login("test-user", "password");
+
+        // Set time offset
+        Time.setOffset(60);
+        try {
+            // User rejected consent
+            grantPage.assertCurrent();
+            grantPage.cancel();
+
+            // Assert account error page with "staleCodeAccount" error displayed
+            accountFederatedIdentityPage.assertCurrent();
+            Assert.assertEquals("The page expired. Please try one more time.", accountFederatedIdentityPage.getError());
+
+
+            // Try to link one more time
+            accountFederatedIdentityPage.clickAddProvider(getProviderId());
+
+            assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8082/auth/"));
+            this.loginPage.login("test-user", "password");
+
+            Time.setOffset(120);
+
+            // User granted consent
+            grantPage.assertCurrent();
+            grantPage.accept();
+
+            // Assert account error page with "staleCodeAccount" error displayed
+            accountFederatedIdentityPage.assertCurrent();
+            Assert.assertEquals("The page expired. Please try one more time.", accountFederatedIdentityPage.getError());
+
+        } finally {
+            Time.setOffset(0);
+        }
+
+        // Revoke consent
+        RealmResource brokeredRealm = keycloak2.realm("realm-with-oidc-identity-provider");
+        List<UserRepresentation> users = brokeredRealm.users().search("test-user", 0, 1);
+        brokeredRealm.users().get(users.get(0).getId()).revokeConsent("broker-app");
+    }
+
+
+    @Test
+    public void testLoginCancelConsent() throws Exception {
+        // Try to login
+        loginIDP("test-user");
+
+        // User rejected consent
+        grantPage.assertCurrent();
+        grantPage.cancel();
+
+        // Assert back on login page
+        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/"));
+        assertTrue(driver.getTitle().equals("Log in to realm-with-broker"));
+    }
+
+
+    // KEYCLOAK-2802
+    @Test
+    public void testAccountManagementLinkingCancelConsent() throws Exception {
+        // Login as pedroigor to account management
+        loginToAccountManagement("pedroigor");
+
+        // Link my "pedroigor" identity with "test-user" from brokered Keycloak
+        accountFederatedIdentityPage.clickAddProvider(getProviderId());
+
+        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8082/auth/"));
+        this.loginPage.login("test-user", "password");
+
+        // User rejected consent
+        grantPage.assertCurrent();
+        grantPage.cancel();
+
+        // Assert account error page with "consentDenied" error displayed
+        accountFederatedIdentityPage.assertCurrent();
+        Assert.assertEquals("Consent denied.", accountFederatedIdentityPage.getError());
+    }
+
+
+    private void loginToAccountManagement(String username) {
+        accountFederatedIdentityPage.realm("realm-with-broker");
+        accountFederatedIdentityPage.open();
+        assertTrue(driver.getTitle().equals("Log in to realm-with-broker"));
+        loginPage.login(username, "password");
+        assertTrue(accountFederatedIdentityPage.isCurrent());
+    }
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/DBLockTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/DBLockTest.java
index bc0cb99..14fde53 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/DBLockTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/DBLockTest.java
@@ -54,17 +54,17 @@ public class DBLockTest extends AbstractModelTest {
         super.before();
 
         // Set timeouts for testing
-        DBLockManager lockManager = new DBLockManager();
-        DBLockProviderFactory lockFactory = lockManager.getDBLockFactory(session);
+        DBLockManager lockManager = new DBLockManager(session);
+        DBLockProviderFactory lockFactory = lockManager.getDBLockFactory();
         lockFactory.setTimeouts(LOCK_RECHECK_MILLIS, LOCK_TIMEOUT_MILLIS);
 
         // Drop lock table, just to simulate racing threads for create lock table and insert lock record into it.
-        lockManager.getDBLock(session).destroyLockInfo();
+        lockManager.getDBLock().destroyLockInfo();
 
         commit();
     }
 
-    // @Test // TODO: Running -Dtest=DBLockTest,UserModelTest might cause issues sometimes. Reenable this once DB lock is refactored.
+    @Test
     public void testLockConcurrently() throws Exception {
         long startupTime = System.currentTimeMillis();
 
@@ -112,7 +112,7 @@ public class DBLockTest extends AbstractModelTest {
     }
 
     private void lock(KeycloakSession session, Semaphore semaphore) {
-        DBLockProvider dbLock = new DBLockManager().getDBLock(session);
+        DBLockProvider dbLock = new DBLockManager(session).getDBLock();
         dbLock.waitForLock();
         try {
             semaphore.increase();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/ErrorPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/ErrorPage.java
index b821331..3077db4 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/ErrorPage.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/ErrorPage.java
@@ -16,8 +16,10 @@
  */
 package org.keycloak.testsuite.pages;
 
+import org.junit.Assert;
 import org.keycloak.testsuite.OAuthClient;
 import org.keycloak.testsuite.rule.WebResource;
+import org.openqa.selenium.By;
 import org.openqa.selenium.WebElement;
 import org.openqa.selenium.support.FindBy;
 
@@ -32,12 +34,19 @@ public class ErrorPage extends AbstractPage {
     @FindBy(className = "instruction")
     private WebElement errorMessage;
 
+    @FindBy(id = "backToApplication")
+    private WebElement backToApplicationLink;
+
     public String getError() {
         return errorMessage.getText();
     }
 
+    public void clickBackToApplication() {
+        backToApplicationLink.click();
+    }
+
     public boolean isCurrent() {
-        return driver.getTitle().equals("We're sorry...");
+        return driver.getTitle() != null && driver.getTitle().equals("We're sorry...");
     }
 
     @Override
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/provider/URLProvider.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/provider/URLProvider.java
index 301fc86..424705c 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/provider/URLProvider.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/provider/URLProvider.java
@@ -74,7 +74,7 @@ public class URLProvider extends URLResourceProvider {
         }
 
         try {
-            if ("true".equals(System.getProperty("app.server.eap6"))) {
+            if ("eap6".equals(System.getProperty("app.server"))) {
                 if (url == null) {
                     url = new URL("http://localhost:8080/");
                 }
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/AbstractPageWithInjectedUrl.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/AbstractPageWithInjectedUrl.java
index fcbdc97..e678db4 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/AbstractPageWithInjectedUrl.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/AbstractPageWithInjectedUrl.java
@@ -32,7 +32,7 @@ public abstract class AbstractPageWithInjectedUrl extends AbstractPage {
 
     //EAP6 URL fix
     protected URL createInjectedURL(String url) {
-        if (System.getProperty("app.server.eap6","false").equals("false")) {
+        if (!System.getProperty("app.server").equals("eap6")) {
             return null;
         }
         try {
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 1e708b6..a3badef 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
@@ -48,6 +48,11 @@ public abstract class AbstractServletsAdapterTest extends AbstractAdapterTest {
                 .addAsWebInfResource(keycloakJSON, "keycloak.json")
                 .addAsWebInfResource(jbossDeploymentStructure, JBOSS_DEPLOYMENT_STRUCTURE_XML);
 
+        URL keystore = AbstractServletsAdapterTest.class.getResource(webInfPath + "keystore.jks");
+        if (keystore != null) {
+            deployment.addAsWebInfResource(keystore, "classes/keystore.jks");
+        }
+
         addContextXml(deployment, name);
 
         return deployment;
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractDemoExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractDemoExampleAdapterTest.java
index fafbf29..b1df6e0 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractDemoExampleAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractDemoExampleAdapterTest.java
@@ -344,16 +344,8 @@ public abstract class AbstractDemoExampleAdapterTest extends AbstractExampleAdap
 
         String serverLogPath = null;
 
-        if (System.getProperty("app.server.wildfly", "false").equals("true")) {
-            serverLogPath = System.getProperty("app.server.wildfly.home") + "/standalone/log/server.log";
-        }
-
-        if (System.getProperty("app.server.eap6", "false").equals("true")) {
-            serverLogPath = System.getProperty("app.server.eap6.home") + "/standalone/log/server.log";
-        }
-
-        if (System.getProperty("app.server.eap7", "false").equals("true")) {
-            serverLogPath = System.getProperty("app.server.eap7.home") + "/standalone/log/server.log";
+        if (System.getProperty("app.server").equals("wildfly") || System.getProperty("app.server").equals("eap6") || System.getProperty("app.server").equals("eap")) {
+            serverLogPath = System.getProperty("app.server.home") + "/standalone/log/server.log";
         }
 
         String appServerUrl;
@@ -364,6 +356,7 @@ public abstract class AbstractDemoExampleAdapterTest extends AbstractExampleAdap
         }
 
         if (serverLogPath != null) {
+            log.info("Checking app server log at: " + serverLogPath);
             File serverLog = new File(serverLogPath);
             String serverLogContent = FileUtils.readFileToString(serverLog);
             UserRepresentation bburke = ApiUtil.findUserByUsername(testRealmResource(), "bburke@redhat.com");
@@ -373,6 +366,8 @@ public abstract class AbstractDemoExampleAdapterTest extends AbstractExampleAdap
 
             assertTrue(matcher.find());
             assertTrue(serverLogContent.contains("User '" + bburke.getId() + "' invoking '" + appServerUrl + "database/customers' on client 'database-service'"));
+        } else {
+            log.info("Checking app server log on app-server: \"" + System.getProperty("app.server") + "\" is not supported.");
         }
     }
 }
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 e4c9226..e0d87f7 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
@@ -168,6 +168,7 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
 
         jsConsoleExamplePage.init();
         jsConsoleExamplePage.getProfile();
+        pause(500);
         assertTrue(jsConsoleExamplePage.getOutputText().contains("Failed to load profile"));
 
         jsConsoleExamplePage.logIn();
@@ -306,7 +307,7 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
 
         logInAndInit("implicit");
 
-        pause(5000);
+        pause(6000);
 
         assertTrue(jsConsoleExamplePage.getEventsText().contains("Access token expired"));
     }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java
index 181d502..ab7b1f5 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java
@@ -25,7 +25,6 @@ import org.junit.Ignore;
 import org.junit.Test;
 import org.keycloak.OAuth2Constants;
 import org.keycloak.common.Version;
-import org.keycloak.common.util.Time;
 import org.keycloak.constants.AdapterConstants;
 import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
 import org.keycloak.representations.VersionRepresentation;
@@ -49,6 +48,7 @@ import java.util.concurrent.TimeUnit;
 import static org.junit.Assert.*;
 import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlEquals;
 import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWithLoginUrlOf;
+import static org.keycloak.testsuite.util.WaitUtils.pause;
 
 /**
  *
@@ -226,7 +226,8 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd
         demoRealmRep.setSsoSessionIdleTimeout(1);
         testRealmResource().update(demoRealmRep);
 
-//		Thread.sleep(2000);
+		pause(2000);
+
         productPortal.navigateTo();
         assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
 
@@ -253,16 +254,16 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd
         demoRealmRep.setSsoSessionIdleTimeout(1);
         testRealmResource().update(demoRealmRep);
 
-        Time.setOffset(2);
+        pause(2000);
 
         productPortal.navigateTo();
         assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
 
         // need to cleanup so other tests don't fail, so invalidate http sessions on remote clients.
         demoRealmRep.setSsoSessionIdleTimeout(originalIdle);
+        testRealmResource().update(demoRealmRep);
         // note: sessions invalidated after each test, see: AbstractKeycloakTest.afterAbstractKeycloakTest()
 
-        Time.setOffset(0);
     }
 
     @Test
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupTest.java
index ad3793e..1e2abdb 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupTest.java
@@ -37,6 +37,7 @@ import java.io.IOException;
 import java.net.URI;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Set;
 
 
 /**
@@ -188,19 +189,6 @@ public class GroupTest extends AbstractGroupTest {
         Assert.assertEquals(1, level3Group.getRealmRoles().size());
         Assert.assertTrue(level3Group.getRealmRoles().contains("level3Role"));
 
-        try {
-            GroupRepresentation notFound = realm.getGroupByPath("/notFound");
-            Assert.fail();
-        } catch (NotFoundException e) {
-
-        }
-        try {
-            GroupRepresentation notFound = realm.getGroupByPath("/top/notFound");
-            Assert.fail();
-        } catch (NotFoundException e) {
-
-        }
-
         UserRepresentation user = realm.users().search("direct-login", -1, -1).get(0);
         realm.users().get(user.getId()).joinGroup(level3Group.getId());
         List<GroupRepresentation> membership = realm.users().get(user.getId()).groups();
@@ -231,5 +219,27 @@ public class GroupTest extends AbstractGroupTest {
         realm.removeDefaultGroup(level3Group.getId());
         defaultGroups = realm.getDefaultGroups();
         Assert.assertEquals(0, defaultGroups.size());
+
+        realm.groups().group(topGroup.getId()).remove();
+
+        try {
+            realm.getGroupByPath("/top/level2/level3");
+            Assert.fail("Group should not have been found");
+        }
+        catch (NotFoundException e) {}
+
+        try {
+            realm.getGroupByPath("/top/level2");
+            Assert.fail("Group should not have been found");
+        }
+        catch (NotFoundException e) {}
+
+        try {
+            realm.getGroupByPath("/top");
+            Assert.fail("Group should not have been found");
+        }
+        catch (NotFoundException e) {}
+
+        Assert.assertNull(login("direct-login", "resource-owner", "secret", user.getId()).getRealmAccess());
     }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AdapterInstallationConfigTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AdapterInstallationConfigTest.java
index 4272025..0a4fe4b 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AdapterInstallationConfigTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AdapterInstallationConfigTest.java
@@ -103,9 +103,9 @@ public class AdapterInstallationConfigTest extends AbstractClientRegistrationTes
 
         try {
             reg.getAdapterConfig(client.getClientId());
-            fail("Expected 403");
+            fail("Expected 401");
         } catch (ClientRegistrationException e) {
-            assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
+            assertEquals(401, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
         }
     }
 
@@ -115,9 +115,9 @@ public class AdapterInstallationConfigTest extends AbstractClientRegistrationTes
 
         try {
             reg.getAdapterConfig(client2.getClientId());
-            fail("Expected 403");
+            fail("Expected 401");
         } catch (ClientRegistrationException e) {
-            assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
+            assertEquals(401, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
         }
     }
 
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationTest.java
index 70bfed0..0ef5e26 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationTest.java
@@ -126,8 +126,14 @@ public class ClientRegistrationTest extends AbstractClientRegistrationTest {
     @Test
     public void getClientNotFound() throws ClientRegistrationException {
         authManageClients();
+        assertNull(reg.get("invalid"));
+    }
+
+    @Test
+    public void getClientNotFoundNoAccess() throws ClientRegistrationException {
+        authNoAccess();
         try {
-            reg.get(CLIENT_ID);
+            reg.get("invalid");
             fail("Expected 403");
         } catch (ClientRegistrationException e) {
             assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
@@ -181,10 +187,14 @@ public class ClientRegistrationTest extends AbstractClientRegistrationTest {
     public void updateClientNotFound() throws ClientRegistrationException {
         authManageClients();
         try {
-            updateClient();
-            fail("Expected 403");
+            ClientRepresentation client = new ClientRepresentation();
+            client.setClientId("invalid");
+
+            reg.update(client);
+
+            fail("Expected 404");
         } catch (ClientRegistrationException e) {
-            assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
+            assertEquals(404, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
         }
     }
 
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/InitialAccessTokenTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/InitialAccessTokenTest.java
index 4f4d204..ad33f52 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/InitialAccessTokenTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/InitialAccessTokenTest.java
@@ -58,7 +58,7 @@ public class InitialAccessTokenTest extends AbstractClientRegistrationTest {
         try {
             reg.create(rep);
         } catch (ClientRegistrationException e) {
-            Assert.assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
+            Assert.assertEquals(401, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
         }
     }
 
@@ -79,7 +79,7 @@ public class InitialAccessTokenTest extends AbstractClientRegistrationTest {
         try {
             reg.create(rep);
         } catch (ClientRegistrationException e) {
-            Assert.assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
+            Assert.assertEquals(401, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
         }
     }
 
@@ -113,7 +113,7 @@ public class InitialAccessTokenTest extends AbstractClientRegistrationTest {
         try {
             reg.create(rep);
         } catch (ClientRegistrationException e) {
-            Assert.assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
+            Assert.assertEquals(401, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
         }
     }
 
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/RegistrationAccessTokenTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/RegistrationAccessTokenTest.java
index c6fb960..96a0010 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/RegistrationAccessTokenTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/RegistrationAccessTokenTest.java
@@ -59,8 +59,8 @@ public class RegistrationAccessTokenTest extends AbstractClientRegistrationTest 
             try {
                 reg.get(client.getClientId());
                 fail("Expected 403");
-            } catch (ClientRegistrationException e) {
-                assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
+            } catch (Exception e) {
+                assertEquals(401, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
             }
         }
         return null;
@@ -82,9 +82,9 @@ public class RegistrationAccessTokenTest extends AbstractClientRegistrationTest 
         reg.auth(Auth.token("invalid"));
         try {
             reg.get(client.getClientId());
-            fail("Expected 403");
+            fail("Expected 401");
         } catch (ClientRegistrationException e) {
-            assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
+            assertEquals(401, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
         }
     }
 
@@ -109,9 +109,9 @@ public class RegistrationAccessTokenTest extends AbstractClientRegistrationTest 
         reg.auth(Auth.token("invalid"));
         try {
             reg.update(client);
-            fail("Expected 403");
+            fail("Expected 401");
         } catch (ClientRegistrationException e) {
-            assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
+            assertEquals(401, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
         }
 
         assertEquals("http://root", getClient(client.getId()).getRootUrl());
@@ -128,9 +128,9 @@ public class RegistrationAccessTokenTest extends AbstractClientRegistrationTest 
         reg.auth(Auth.token("invalid"));
         try {
             reg.delete(client);
-            fail("Expected 403");
+            fail("Expected 401");
         } catch (ClientRegistrationException e) {
-            assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
+            assertEquals(401, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
         }
         assertNotNull(getClient(client.getId()));
     }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/demorealm.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/demorealm.json
index bb6ce6e..fbe54ce 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/demorealm.json
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/demorealm.json
@@ -174,8 +174,8 @@
             "redirectUris": [
                 "/secure-portal/*"
             ],
-            "attributes": {
-                "jwt.credential.certificate": "MIICnTCCAYUCBgFPPLDaTzANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdjbGllbnQxMB4XDTE1MDgxNzE3MjI0N1oXDTI1MDgxNzE3MjQyN1owEjEQMA4GA1UEAwwHY2xpZW50MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAIUjjgv+V3s96O+Za9002Lp/trtGuHBeaeVL9dFKMKzO2MPqdRmHB4PqNlDdd28Rwf5Xn6iWdFpyUKOnI/yXDLhdcuFpR0sMNK/C9Lt+hSpPFLuzDqgtPgDotlMxiHIWDOZ7g9/gPYNXbNvjv8nSiyqoguoCQiiafW90bPHsiVLdP7ZIUwCcfi1qQm7FhxRJ1NiW5dvUkuCnnWEf0XR+Wzc5eC9EgB0taLFiPsSEIlWMm5xlahYyXkPdNOqZjiRnrTWm5Y4uk8ZcsD/KbPTf/7t7cQXipVaswgjdYi1kK2/zRwOhg1QwWFX/qmvdd+fLxV0R6VqRDhn7Qep2cxwMxLsCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAKE6OA46sf20bz8LZPoiNsqRwBUDkaMGXfnob7s/hJZIIwDEx0IAQ3uKsG7q9wb+aA6s+v7S340zb2k3IxuhFaHaZpAd4CyR5cn1FHylbzoZ7rI/3ASqHDqpljdJaFqPH+m7nZWtyDvtZf+gkZ8OjsndwsSBK1d/jMZPp29qYbl1+XfO7RCp/jDqro/R3saYFaIFiEZPeKn1hUJn6BO48vxH1xspSu9FmlvDOEAOz4AuM58z4zRMP49GcFdCWr1wkonJUHaSptJaQwmBwLFUkCbE5I1ixGMb7mjEud6Y5jhfzJiZMo2U8RfcjNbrN0diZl3jB6LQIwESnhYSghaTjNQ=="
+            "attributes" : {
+                "jwt.credential.certificate" : "MIICqTCCAZECBgFT0Ngs/DANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1zZWN1cmUtcG9ydGFsMB4XDTE2MDQwMTA4MDA0MVoXDTI2MDQwMTA4MDIyMVowGDEWMBQGA1UEAwwNc2VjdXJlLXBvcnRhbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJa4GixpmzP511AmI0eLPLORyJwXS8908MUvdG3hmh8jMOIhe28XjIFeZSY09vFxh22F2SUMjxU/B2Hw4PDJUkebuNR7rXhOIYCJAo6eEZzjSBY/wngFtfm74zJ/eLCobBtDvIld7jobdHTfE1Oz9+GzvtG0k7cm7ubrLT0J4I1UsFZj3b//3wa+O0vNaTwHC1Jz/m59VbtXqyO4xEzIdl416cnGCmEmk5qd5h1de2UoLi/CTad8HftIJhzN1qhlySzW/9Ha70aYlDH2hiibDsXDTrNaMdaaLik7I8Rv/nIbggysG863PKZo8wknDe62QctH5VYSSktiy4gjSJkGh7ECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAZnnx+AHQ8txugGcFK8gWjildDgk+v31fBHBDvmLQaSzsUaIOJaK4wnlwUI+VfR46HmBXhjlDCobFLUptd+kz0G7xapcIn3b5jLrySUUD7L+LAp1vNOQU4mKhTGS3IEvNB73D3GH9rQ+M3KEcoN3f99fNKqKsUdxbmZqGf4VOQ57PUfLBw4PJJGlROPosBc7ivPRyeYnKekhoCTynq30BAD1FA1BA8ppcY4ZVGADPTAgMJxpglpFY9LiqCwdLAGW1ttnsyIJ7DpT+kybhhk7c+MU7gyQdv8xPnMR0bSCB9hndowgBn5oZ393aMscwMNCzwJ0aWBs1sUyn3X0RIsu9Jg=="
             }
         },
         {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal/WEB-INF/keycloak.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal/WEB-INF/keycloak.json
index 6b8d13f..de290de 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal/WEB-INF/keycloak.json
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal/WEB-INF/keycloak.json
@@ -1,10 +1,17 @@
 {
-  "realm" : "demo",
-  "resource" : "secure-portal",
-  "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
-  "auth-server-url" : "http://localhost:8180/auth",
-  "ssl-required" : "external",
-  "credentials" : {
-      "secret": "password"
-   }
+  "realm": "demo",
+  "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+  "auth-server-url": "http://localhost:8180/auth",
+  "ssl-required": "external",
+  "resource": "secure-portal",
+  "credentials": {
+    "jwt": {
+      "client-key-password": "password",
+      "client-keystore-file": "classpath:keystore.jks",
+      "client-keystore-password": "password",
+      "client-key-alias": "secure-portal",
+      "token-timeout": 10,
+      "client-keystore-type": "jks"
+    }
+  }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal/WEB-INF/keystore.jks b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal/WEB-INF/keystore.jks
new file mode 100644
index 0000000..399be7a
Binary files /dev/null and b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal/WEB-INF/keystore.jks differ
diff --git a/testsuite/integration-arquillian/tests/pom.xml b/testsuite/integration-arquillian/tests/pom.xml
index ae0fa92..913d3fa 100755
--- a/testsuite/integration-arquillian/tests/pom.xml
+++ b/testsuite/integration-arquillian/tests/pom.xml
@@ -753,7 +753,7 @@
                 <dependency>
                     <groupId>org.jboss.arquillian.container</groupId>
                     <artifactId>undertow-embedded</artifactId>
-                    <version>1.0.0.Alpha1-SNAPSHOT</version>
+                    <version>1.0.0.Alpha2</version>
                 </dependency>
 
                 <dependency>
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 5803146..8c0727f 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
@@ -135,6 +135,8 @@ federatedIdentityRemovingLastProviderMessage=You can''t remove last federated id
 identityProviderRedirectErrorMessage=Failed to redirect to identity provider.
 identityProviderRemovedMessage=Identity provider removed successfully.
 identityProviderAlreadyLinkedMessage=Federated identity returned by {0} is already linked to another user.
+staleCodeAccountMessage=The page expired. Please try one more time.
+consentDenied=Consent denied.
 
 accountDisabledMessage=Account is disabled, contact admin.
 
diff --git a/themes/src/main/resources/theme/base/account/totp.ftl b/themes/src/main/resources/theme/base/account/totp.ftl
index 1cfefba..8ab36fa 100755
--- a/themes/src/main/resources/theme/base/account/totp.ftl
+++ b/themes/src/main/resources/theme/base/account/totp.ftl
@@ -30,7 +30,7 @@
             </li>
             <li>
                 <p>${msg("totpStep2")}</p>
-                <img src="${totp.totpSecretQrCodeUrl}" alt="Figure: Barcode"><br/>
+                <img src="data:image/png;base64, ${totp.totpSecretQrCode}" alt="Figure: Barcode"><br/>
                 <span class="code">${totp.totpSecretEncoded}</span>
             </li>
             <li>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/create-execution.html b/themes/src/main/resources/theme/base/admin/resources/partials/create-execution.html
index 6956088..dd333a9 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/create-execution.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/create-execution.html
@@ -1,6 +1,8 @@
 <div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+
     <div>
         <h1 data-ng-show="parentFlow.providerId == 'basic-flow'">{{:: 'create-authenticator-execution' | translate}}</h1>
+        <h1 data-ng-show="parentFlow.providerId == 'client-flow'">{{:: 'create-authenticator-execution' | translate}}</h1>
         <h1 data-ng-show="parentFlow.providerId == 'for-flow'">{{:: 'create-form-action-execution' | translate}}</h1>
     </div>
     <kc-tabs-authentication></kc-tabs-authentication>
diff --git a/themes/src/main/resources/theme/base/login/error.ftl b/themes/src/main/resources/theme/base/login/error.ftl
index 95de521..c069e26 100755
--- a/themes/src/main/resources/theme/base/login/error.ftl
+++ b/themes/src/main/resources/theme/base/login/error.ftl
@@ -8,7 +8,7 @@
         <div id="kc-error-message">
             <p class="instruction">${message.summary}</p>
             <#if client?? && client.baseUrl?has_content>
-                <p><a href="${client.baseUrl}">${msg("backToApplication")}</a></p>
+                <p><a id="backToApplication" href="${client.baseUrl}">${msg("backToApplication")}</a></p>
             </#if>
         </div>
     </#if>
diff --git a/themes/src/main/resources/theme/base/login/login-config-totp.ftl b/themes/src/main/resources/theme/base/login/login-config-totp.ftl
index 649a833..07a8801 100755
--- a/themes/src/main/resources/theme/base/login/login-config-totp.ftl
+++ b/themes/src/main/resources/theme/base/login/login-config-totp.ftl
@@ -34,7 +34,7 @@
             </li>
             <li>
                 <p>${msg("loginTotpStep2")}</p>
-                <img src="${totp.totpSecretQrCodeUrl}" alt="Figure: Barcode"><br/>
+                <img src="data:image/png;base64, ${totp.totpSecretQrCode}" alt="Figure: Barcode"><br/>
                 <span class="code">${totp.totpSecretEncoded}</span>
             </li>
             <li>
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 ee8a767..ec02b1c 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
@@ -202,6 +202,7 @@ invalidCodeMessage=An error occurred, please login again through your applicatio
 identityProviderUnexpectedErrorMessage=Unexpected error when authenticating with identity provider
 identityProviderNotFoundMessage=Could not find an identity provider with the identifier.
 identityProviderLinkSuccess=Your account was successfully linked with {0} account {1} .
+staleCodeMessage=This page is no longer valid, please go back to your application and login again
 realmSupportsNoCredentialsMessage=Realm does not support any credential type.
 identityProviderNotUniqueMessage=Realm supports multiple identity providers. Could not determine which identity provider should be used to authenticate with.
 emailVerifiedMessage=Your email address has been verified.
diff --git a/themes/src/main/resources/theme/keycloak/account/resources/css/account.css b/themes/src/main/resources/theme/keycloak/account/resources/css/account.css
index c1353d7..be0c31e 100644
--- a/themes/src/main/resources/theme/keycloak/account/resources/css/account.css
+++ b/themes/src/main/resources/theme/keycloak/account/resources/css/account.css
@@ -204,17 +204,20 @@ ol li {
 
 ol li img {
     margin-top: 15px;
-    width: 180px;
     margin-bottom: 5px;
     border: 1px solid #eee;
 }
 
 ol li span {
-    bottom: 80px;
-    left: 200px;
+    padding: 15px;
+    background-color: #f5f5f5;
+    border: 1px solid #eee;
+    top: 46px;
+    left: 270px;
+    right: 50px;
     position: absolute;
     font-family: courier, ​monospace;
-    font-size: 13px;
+    font-size: 25px;
 }
 
 hr + .form-horizontal {