keycloak-uncached

Changes

adapters/oidc/cli-sso/login.sh 10(+0 -10)

adapters/oidc/cli-sso/logout.sh 9(+0 -9)

adapters/oidc/installed/src/main/java/org/keycloak/adapters/installed/KeycloakCliSso.java 281(+0 -281)

authz/client/src/main/java/org/keycloak/authorization/client/representation/ResourceRepresentation.java 218(+0 -218)

authz/client/src/main/java/org/keycloak/authorization/client/representation/ScopeRepresentation.java 98(+0 -98)

pom.xml 11(+11 -0)

services/src/main/java/org/keycloak/authorization/protection/resource/RegistrationResponse.java 50(+0 -50)

services/src/main/java/org/keycloak/authorization/protection/resource/representation/UmaResourceRepresentation.java 176(+0 -176)

services/src/main/java/org/keycloak/authorization/protection/resource/representation/UmaScopeRepresentation.java 98(+0 -98)

Details

diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java
index 96fbe5d..452583b 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java
@@ -19,7 +19,6 @@ package org.keycloak.adapters.authorization;
 
 import java.util.Collections;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 
 import org.jboss.logging.Logger;
@@ -30,10 +29,12 @@ import org.keycloak.adapters.spi.HttpFacade.Request;
 import org.keycloak.authorization.client.AuthzClient;
 import org.keycloak.authorization.client.ClientAuthorizationContext;
 import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.AccessToken.Authorization;
 import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
 import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.EnforcementMode;
 import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.MethodConfig;
 import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
+import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.ScopeEnforcementMode;
 import org.keycloak.representations.idm.authorization.Permission;
 
 /**
@@ -42,31 +43,23 @@ import org.keycloak.representations.idm.authorization.Permission;
 public abstract class AbstractPolicyEnforcer {
 
     private static Logger LOGGER = Logger.getLogger(AbstractPolicyEnforcer.class);
-    private final PolicyEnforcerConfig enforcerConfig;
-    private final PolicyEnforcer policyEnforcer;
+    private static final String HTTP_METHOD_DELETE = "DELETE";
 
-    private Map<String, PathConfig> paths;
-    private AuthzClient authzClient;
-    private PathMatcher pathMatcher;
+    private final PolicyEnforcer policyEnforcer;
 
-    public AbstractPolicyEnforcer(PolicyEnforcer policyEnforcer) {
+    protected AbstractPolicyEnforcer(PolicyEnforcer policyEnforcer) {
         this.policyEnforcer = policyEnforcer;
-        this.enforcerConfig = policyEnforcer.getEnforcerConfig();
-        this.authzClient = policyEnforcer.getClient();
-        this.pathMatcher = policyEnforcer.getPathMatcher();
-        this.paths = policyEnforcer.getPaths();
     }
 
     public AuthorizationContext authorize(OIDCHttpFacade httpFacade) {
-        EnforcementMode enforcementMode = this.enforcerConfig.getEnforcementMode();
+        EnforcementMode enforcementMode = getEnforcerConfig().getEnforcementMode();
 
         if (EnforcementMode.DISABLED.equals(enforcementMode)) {
             return createEmptyAuthorizationContext(true);
         }
 
         Request request = httpFacade.getRequest();
-        String path = getPath(request);
-        PathConfig pathConfig = this.pathMatcher.matches(path, this.paths);
+        PathConfig pathConfig = getPathConfig(request);
         KeycloakSecurityContext securityContext = httpFacade.getSecurityContext();
 
         if (securityContext == null) {
@@ -79,16 +72,20 @@ public abstract class AbstractPolicyEnforcer {
         AccessToken accessToken = securityContext.getToken();
 
         if (accessToken != null) {
-            LOGGER.debugf("Checking permissions for path [%s] with config [%s].", request.getURI(), pathConfig);
+            if (LOGGER.isDebugEnabled()) {
+                LOGGER.debugf("Checking permissions for path [%s] with config [%s].", request.getURI(), pathConfig);
+            }
 
             if (pathConfig == null) {
                 if (EnforcementMode.PERMISSIVE.equals(enforcementMode)) {
                     return createAuthorizationContext(accessToken, null);
                 }
 
-                LOGGER.debugf("Could not find a configuration for path [%s]", path);
+                if (LOGGER.isDebugEnabled()) {
+                    LOGGER.debugf("Could not find a configuration for path [%s]", getPath(request));
+                }
 
-                if (isDefaultAccessDeniedUri(request, enforcerConfig)) {
+                if (isDefaultAccessDeniedUri(request)) {
                     return createAuthorizationContext(accessToken, null);
                 }
 
@@ -111,10 +108,18 @@ public abstract class AbstractPolicyEnforcer {
                 }
             }
 
-            LOGGER.debugf("Sending challenge to the client. Path [%s]", pathConfig);
+            if (methodConfig != null && ScopeEnforcementMode.DISABLED.equals(methodConfig.getScopesEnforcementMode())) {
+                return createEmptyAuthorizationContext(true);
+            }
+
+            if (LOGGER.isDebugEnabled()) {
+                LOGGER.debugf("Sending challenge to the client. Path [%s]", pathConfig);
+            }
 
             if (!challenge(pathConfig, methodConfig, httpFacade)) {
-                LOGGER.debugf("Challenge not sent, sending default forbidden response. Path [%s]", pathConfig);
+                if (LOGGER.isDebugEnabled()) {
+                    LOGGER.debugf("Challenge not sent, sending default forbidden response. Path [%s]", pathConfig);
+                }
                 handleAccessDenied(httpFacade);
             }
         }
@@ -126,22 +131,21 @@ public abstract class AbstractPolicyEnforcer {
 
     protected boolean isAuthorized(PathConfig actualPathConfig, MethodConfig methodConfig, AccessToken accessToken, OIDCHttpFacade httpFacade) {
         Request request = httpFacade.getRequest();
-        PolicyEnforcerConfig enforcerConfig = getEnforcerConfig();
 
-        if (isDefaultAccessDeniedUri(request, enforcerConfig)) {
+        if (isDefaultAccessDeniedUri(request)) {
             return true;
         }
 
-        AccessToken.Authorization authorization = accessToken.getAuthorization();
+        Authorization authorization = accessToken.getAuthorization();
 
         if (authorization == null) {
             return false;
         }
 
-        List<Permission> permissions = authorization.getPermissions();
         boolean hasPermission = false;
+        List<Permission> grantedPermissions = authorization.getPermissions();
 
-        for (Permission permission : permissions) {
+        for (Permission permission : grantedPermissions) {
             if (permission.getResourceId() != null) {
                 if (isResourcePermission(actualPathConfig, permission)) {
                     hasPermission = true;
@@ -151,9 +155,11 @@ public abstract class AbstractPolicyEnforcer {
                     }
 
                     if (hasResourceScopePermission(methodConfig, permission)) {
-                        LOGGER.debugf("Authorization GRANTED for path [%s]. Permissions [%s].", actualPathConfig, permissions);
-                        if (request.getMethod().equalsIgnoreCase("DELETE") && actualPathConfig.isInstance()) {
-                            this.paths.remove(actualPathConfig);
+                        if (LOGGER.isDebugEnabled()) {
+                            LOGGER.debugf("Authorization GRANTED for path [%s]. Permissions [%s].", actualPathConfig, grantedPermissions);
+                        }
+                        if (HTTP_METHOD_DELETE.equalsIgnoreCase(request.getMethod()) && actualPathConfig.isInstance()) {
+                            policyEnforcer.getPaths().remove(actualPathConfig);
                         }
                         return true;
                     }
@@ -170,7 +176,9 @@ public abstract class AbstractPolicyEnforcer {
             return true;
         }
 
-        LOGGER.debugf("Authorization FAILED for path [%s]. Not enough permissions [%s].", actualPathConfig, permissions);
+        if (LOGGER.isDebugEnabled()) {
+            LOGGER.debugf("Authorization FAILED for path [%s]. Not enough permissions [%s].", actualPathConfig, grantedPermissions);
+        }
 
         return false;
     }
@@ -179,15 +187,21 @@ public abstract class AbstractPolicyEnforcer {
         httpFacade.getResponse().sendError(403);
     }
 
-    private boolean isDefaultAccessDeniedUri(Request request, PolicyEnforcerConfig enforcerConfig) {
-        String accessDeniedPath = enforcerConfig.getOnDenyRedirectTo();
+    protected AuthzClient getAuthzClient() {
+        return policyEnforcer.getClient();
+    }
 
-        if (accessDeniedPath != null) {
-            if (request.getURI().contains(accessDeniedPath)) {
-                return true;
-            }
-        }
-        return false;
+    protected PolicyEnforcerConfig getEnforcerConfig() {
+        return policyEnforcer.getEnforcerConfig();
+    }
+
+    protected PolicyEnforcer getPolicyEnforcer() {
+        return policyEnforcer;
+    }
+
+    private boolean isDefaultAccessDeniedUri(Request request) {
+        String accessDeniedPath = getEnforcerConfig().getOnDenyRedirectTo();
+        return accessDeniedPath != null && request.getURI().contains(accessDeniedPath);
     }
 
     private boolean hasResourceScopePermission(MethodConfig methodConfig, Permission permission) {
@@ -215,20 +229,8 @@ public abstract class AbstractPolicyEnforcer {
         return requiredScopes.isEmpty();
     }
 
-    protected AuthzClient getAuthzClient() {
-        return this.authzClient;
-    }
-
-    protected PolicyEnforcerConfig getEnforcerConfig() {
-        return enforcerConfig;
-    }
-
-    protected PolicyEnforcer getPolicyEnforcer() {
-        return policyEnforcer;
-    }
-
     private AuthorizationContext createEmptyAuthorizationContext(final boolean granted) {
-        return new ClientAuthorizationContext(authzClient) {
+        return new ClientAuthorizationContext(getAuthzClient()) {
             @Override
             public boolean hasPermission(String resourceName, String scopeName) {
                 return granted;
@@ -279,7 +281,7 @@ public abstract class AbstractPolicyEnforcer {
     }
 
     private AuthorizationContext createAuthorizationContext(AccessToken accessToken, PathConfig pathConfig) {
-        return new ClientAuthorizationContext(accessToken, pathConfig, this.paths, authzClient);
+        return new ClientAuthorizationContext(accessToken, pathConfig, policyEnforcer.getPaths(), getAuthzClient());
     }
 
     private boolean isResourcePermission(PathConfig actualPathConfig, Permission permission) {
@@ -297,4 +299,8 @@ public abstract class AbstractPolicyEnforcer {
     private boolean matchResourcePermission(PathConfig actualPathConfig, Permission permission) {
         return permission.getResourceId().equals(actualPathConfig.getId());
     }
+
+    private PathConfig getPathConfig(Request request) {
+        return isDefaultAccessDeniedUri(request) ? null : policyEnforcer.getPathMatcher().matches(getPath(request));
+    }
 }
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PathCache.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PathCache.java
index e699203..cf8815c 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PathCache.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PathCache.java
@@ -24,6 +24,8 @@ import java.util.concurrent.locks.LockSupport;
 import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
 
 /**
+ * A simple LRU cache implementation supporting expiration and maximum number of entries.
+ *
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
 public class PathCache {
@@ -43,15 +45,6 @@ public class PathCache {
      * Creates a new instance.
      *
      * @param maxEntries the maximum number of entries to keep in the cache
-     */
-    public PathCache(int maxEntries) {
-        this(maxEntries, -1);
-    }
-
-    /**
-     * Creates a new instance.
-     *
-     * @param maxEntries the maximum number of entries to keep in the cache
      * @param maxAge the time in milliseconds that an entry can stay in the cache. If {@code -1}, entries never expire
      */
     public PathCache(final int maxEntries, long maxAge) {
@@ -80,6 +73,10 @@ public class PathCache {
         }
     }
 
+    public boolean containsKey(String uri) {
+        return cache.containsKey(uri);
+    }
+
     public PathConfig get(String uri) {
         if (parkForReadAndCheckInterrupt()) {
             return null;
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java
index fe8aa1a..2d5f0cc 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java
@@ -17,11 +17,11 @@
  */
 package org.keycloak.adapters.authorization;
 
-import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -34,13 +34,13 @@ import org.keycloak.adapters.authentication.ClientCredentialsProviderUtils;
 import org.keycloak.authorization.client.AuthzClient;
 import org.keycloak.authorization.client.ClientAuthenticator;
 import org.keycloak.authorization.client.Configuration;
-import org.keycloak.authorization.client.representation.ResourceRepresentation;
-import org.keycloak.authorization.client.representation.ScopeRepresentation;
 import org.keycloak.authorization.client.resource.ProtectedResource;
+import org.keycloak.common.util.PathMatcher;
 import org.keycloak.representations.adapters.config.AdapterConfig;
 import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
 import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
 import org.keycloak.representations.idm.authorization.Permission;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -69,8 +69,9 @@ public class PolicyEnforcer {
                 }
             }
         });
-        this.pathMatcher = new PathMatcher(this.authzClient);
+
         this.paths = configurePaths(this.authzClient.protection().resource(), this.enforcerConfig);
+        this.pathMatcher = createPathMatcher(authzClient);
 
         if (LOGGER.isDebugEnabled()) {
             LOGGER.debug("Initialization complete. Path configurations:");
@@ -104,11 +105,11 @@ public class PolicyEnforcer {
         return context;
     }
 
-    PolicyEnforcerConfig getEnforcerConfig() {
+    public PolicyEnforcerConfig getEnforcerConfig() {
         return enforcerConfig;
     }
 
-    AuthzClient getClient() {
+    public AuthzClient getClient() {
         return authzClient;
     }
 
@@ -116,11 +117,11 @@ public class PolicyEnforcer {
         return paths;
     }
 
-    void addPath(PathConfig pathConfig) {
-        paths.put(pathConfig.getPath(), pathConfig);
+    public PathMatcher<PathConfig> getPathMatcher() {
+        return pathMatcher;
     }
 
-    KeycloakDeployment getDeployment() {
+    public KeycloakDeployment getDeployment() {
         return deployment;
     }
 
@@ -144,7 +145,7 @@ public class PolicyEnforcer {
     }
 
     private Map<String, PathConfig> configureDefinedPaths(ProtectedResource protectedResource, PolicyEnforcerConfig enforcerConfig) {
-        Map<String, PathConfig> paths = Collections.synchronizedMap(new HashMap<String, PathConfig>());
+        Map<String, PathConfig> paths = Collections.synchronizedMap(new LinkedHashMap<String, PathConfig>());
 
         for (PathConfig pathConfig : enforcerConfig.getPaths()) {
             ResourceRepresentation resource;
@@ -168,36 +169,11 @@ public class PolicyEnforcer {
             }
 
             if (resource == null) {
-                if (enforcerConfig.isCreateResources()) {
-                    LOGGER.debugf("Creating resource on server for path [%s].", pathConfig);
-                    ResourceRepresentation representation = new ResourceRepresentation();
-
-                    representation.setName(resourceName);
-                    representation.setType(pathConfig.getType());
-                    representation.setUri(path);
-
-                    HashSet<ScopeRepresentation> scopes = new HashSet<>();
-
-                    for (String scopeName : pathConfig.getScopes()) {
-                        ScopeRepresentation scope = new ScopeRepresentation();
-
-                        scope.setName(scopeName);
-
-                        scopes.add(scope);
-                    }
-
-                    representation.setScopes(scopes);
-
-                    ResourceRepresentation registrationResponse = protectedResource.create(representation);
-
-                    pathConfig.setId(registrationResponse.getId());
-                } else {
-                    throw new RuntimeException("Could not find matching resource on server with uri [" + path + "] or name [" + resourceName + "]. Make sure you have created a resource on the server that matches with the path configuration.");
-                }
-            } else {
-                pathConfig.setId(resource.getId());
+                throw new RuntimeException("Could not find matching resource on server with uri [" + path + "] or name [" + resourceName + "]. Make sure you have created a resource on the server that matches with the path configuration.");
             }
 
+            pathConfig.setId(resource.getId());
+
             PathConfig existingPath = null;
 
             for (PathConfig current : paths.values()) {
@@ -222,45 +198,85 @@ public class PolicyEnforcer {
         LOGGER.info("Querying the server for all resources associated with this application.");
         Map<String, PathConfig> paths = Collections.synchronizedMap(new HashMap<String, PathConfig>());
 
-        for (String id : protectedResource.findAll()) {
-            ResourceRepresentation resourceDescription = protectedResource.findById(id);
+        if (!enforcerConfig.getLazyLoadPaths()) {
+            for (String id : protectedResource.findAll()) {
+                ResourceRepresentation resourceDescription = protectedResource.findById(id);
 
-            if (resourceDescription.getUri() != null) {
-                PathConfig pathConfig = createPathConfig(resourceDescription);
-                paths.put(pathConfig.getPath(), pathConfig);
+                if (resourceDescription.getUri() != null) {
+                    PathConfig pathConfig = PathConfig.createPathConfig(resourceDescription);
+                    paths.put(pathConfig.getPath(), pathConfig);
+                }
             }
         }
 
         return paths;
     }
 
-    static PathConfig createPathConfig(ResourceRepresentation resourceDescription) {
-        PathConfig pathConfig = new PathConfig();
+    private PathMatcher<PathConfig> createPathMatcher(final AuthzClient authzClient) {
+        final PathCache pathCache = new PathCache(100, 30000);
 
-        pathConfig.setId(resourceDescription.getId());
-        pathConfig.setName(resourceDescription.getName());
+        return new PathMatcher<PathConfig>() {
+            @Override
+            public PathConfig matches(String targetUri) {
+                PathConfig pathConfig = pathCache.get(targetUri);
 
-        String uri = resourceDescription.getUri();
+                if (pathCache.containsKey(targetUri) || pathConfig != null) {
+                    return pathConfig;
+                }
 
-        if (uri == null || "".equals(uri.trim())) {
-            throw new RuntimeException("Failed to configure paths. Resource [" + resourceDescription.getName() + "] has an invalid or empty URI [" + uri + "].");
-        }
+                pathConfig = super.matches(targetUri);
 
-        pathConfig.setPath(uri);
+                if (enforcerConfig.getLazyLoadPaths() && (pathConfig == null || pathConfig.getPath().contains("*"))) {
+                    try {
+                        List<ResourceRepresentation> matchingResources = authzClient.protection().resource().findByMatchingUri(targetUri);
 
-        List<String> scopeNames = new ArrayList<>();
+                        if (!matchingResources.isEmpty()) {
+                            pathConfig = PathConfig.createPathConfig(matchingResources.get(0));
+                            paths.put(pathConfig.getPath(), pathConfig);
+                        }
+                    } catch (Exception cause) {
+                        LOGGER.errorf(cause, "Could not lazy load paths from server");
+                        return null;
+                    }
+                }
 
-        for (ScopeRepresentation scope : resourceDescription.getScopes()) {
-            scopeNames.add(scope.getName());
-        }
+                pathCache.put(targetUri, pathConfig);
 
-        pathConfig.setScopes(scopeNames);
-        pathConfig.setType(resourceDescription.getType());
+                return pathConfig;
+            }
 
-        return pathConfig;
-    }
+            @Override
+            protected String getPath(PathConfig entry) {
+                return entry.getPath();
+            }
 
-    public PathMatcher getPathMatcher() {
-        return pathMatcher;
+            @Override
+            protected Collection<PathConfig> getPaths() {
+                return paths.values();
+            }
+
+            @Override
+            protected PathConfig resolvePathConfig(PathConfig originalConfig, String path) {
+                if (originalConfig.hasPattern()) {
+                    ProtectedResource resource = authzClient.protection().resource();
+                    List<ResourceRepresentation> search = resource.findByUri(path);
+
+                    if (!search.isEmpty()) {
+                        // resource does exist on the server, cache it
+                        ResourceRepresentation targetResource = search.get(0);
+                        PathConfig config = PathConfig.createPathConfig(targetResource);
+
+                        config.setScopes(originalConfig.getScopes());
+                        config.setMethods(originalConfig.getMethods());
+                        config.setParentConfig(originalConfig);
+                        config.setEnforcementMode(originalConfig.getEnforcementMode());
+
+                        return config;
+                    }
+                }
+
+                return null;
+            }
+        };
     }
 }
diff --git a/adapters/oidc/installed/src/main/java/org/keycloak/adapters/installed/KcinitDriver.java b/adapters/oidc/installed/src/main/java/org/keycloak/adapters/installed/KcinitDriver.java
new file mode 100644
index 0000000..4693f62
--- /dev/null
+++ b/adapters/oidc/installed/src/main/java/org/keycloak/adapters/installed/KcinitDriver.java
@@ -0,0 +1,702 @@
+/*
+ * 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.adapters.installed;
+
+import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.adapters.KeycloakDeploymentBuilder;
+import org.keycloak.adapters.ServerRequest;
+import org.keycloak.common.util.Base64;
+import org.keycloak.common.util.Time;
+import org.keycloak.jose.jwe.*;
+import org.keycloak.representations.AccessTokenResponse;
+import org.keycloak.representations.adapters.config.AdapterConfig;
+import org.keycloak.representations.idm.OAuth2ErrorRepresentation;
+import org.keycloak.util.JsonSerialization;
+
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.SecretKeySpec;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.*;
+import java.nio.file.Paths;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.util.*;
+
+/**
+ * All kcinit commands that take input ask for
+ * <p>
+ * 1. . kcinit
+ * - setup and export KC_SESSION_KEY env var if not set.
+ * - checks to see if master token valid, refresh is possible, exit if token valid
+ * - performs command line login
+ * - stores master token for master client
+ * 2. app.sh is a wrapper for app cli.
+ * - token=`kcinit token app`
+ * - checks to see if token for app client has been fetched, refresh if valid, output token to sys.out if exists
+ * - if no token, login.  Prompts go to stderr.
+ * - pass token as cmd line param to app or as environment variable.
+ * <p>
+ * 3. kcinit password {password}
+ * - outputs password key that is used for encryption.
+ * - can be used in .bashrc as export KC_SESSSION_KEY=`kcinit password {password}` or just set it in .bat file
+ * <p>
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class KcinitDriver {
+
+    public static final String KC_SESSION_KEY = "KC_SESSION_KEY";
+    public static final String KC_LOGIN_CONFIG_PATH = "KC_LOGIN_CONFIG_PATH";
+    protected Map<String, String> config;
+    protected boolean debug = true;
+
+    protected static byte[] salt = new byte[]{-4, 88, 66, -101, 78, -94, 21, 105};
+
+    String[] args = null;
+
+    protected boolean forceLogin;
+    protected boolean browserLogin;
+
+    public void mainCmd(String[] args) throws Exception {
+
+        this.args = args;
+
+
+        if (args.length == 0) {
+            printHelp();
+            return;
+        }
+
+        if (args[0].equalsIgnoreCase("token")) {
+            //System.err.println("executing token");
+            token();
+        } else if (args[0].equalsIgnoreCase("login")) {
+            login();
+        } else if (args[0].equalsIgnoreCase("logout")) {
+            logout();
+        } else if (args[0].equalsIgnoreCase("env")) {
+            System.out.println(System.getenv().toString());
+        } else if (args[0].equalsIgnoreCase("install")) {
+            install();
+        } else if (args[0].equalsIgnoreCase("uninstall")) {
+            uninstall();
+        } else if (args[0].equalsIgnoreCase("password")) {
+            passwordKey();
+        } else {
+            KeycloakInstalled.console().writer().println("Unknown command: " + args[0]);
+            KeycloakInstalled.console().writer().println();
+            printHelp();
+        }
+    }
+
+    public String getHome() {
+        String home = System.getenv("HOME");
+        if (home == null) {
+            home = System.getProperty("HOME");
+            if (home == null) {
+                home = Paths.get("").toAbsolutePath().normalize().toString();
+            }
+        }
+        return home;
+    }
+
+    public void passwordKey() {
+        if (args.length < 2) {
+            printHelp();
+            System.exit(1);
+        }
+        String password = args[1];
+        try {
+            String encodedKey = generateEncryptionKey(password);
+            System.out.printf(encodedKey);
+        } catch (Exception e) {
+            e.printStackTrace();
+            System.exit(1);
+        }
+    }
+
+    protected String generateEncryptionKey(String password) throws NoSuchAlgorithmException, InvalidKeySpecException {
+        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
+        KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 100, 128);
+        SecretKey tmp = factory.generateSecret(spec);
+        byte[] aeskey = tmp.getEncoded();
+        return Base64.encodeBytes(aeskey);
+    }
+
+    public JWE createJWE() {
+        String key = getEncryptionKey();
+        if (key == null) {
+            throw new RuntimeException(KC_SESSION_KEY + " env var not set");
+        }
+        byte[] aesKey = null;
+        try {
+            aesKey = Base64.decode(key.getBytes("UTF-8"));
+        } catch (IOException e) {
+            throw new RuntimeException("invalid " + KC_SESSION_KEY + "env var");
+        }
+
+        JWE jwe = new JWE();
+        final SecretKey aesSecret = new SecretKeySpec(aesKey, "AES");
+        jwe.getKeyStorage()
+                .setEncryptionKey(aesSecret);
+        return jwe;
+    }
+
+    protected String encryptionKey;
+
+    protected String getEncryptionKey() {
+        if (encryptionKey != null) return encryptionKey;
+        return System.getenv(KC_SESSION_KEY);
+    }
+
+    public String encrypt(String payload) {
+        JWE jwe = createJWE();
+        JWEHeader jweHeader = new JWEHeader(JWEConstants.A128KW, JWEConstants.A128CBC_HS256, null);
+        try {
+            jwe.header(jweHeader).content(payload.getBytes("UTF-8"));
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException("cannot encode payload as UTF-8");
+        }
+        try {
+            return jwe.encodeJwe();
+        } catch (JWEException e) {
+            throw new RuntimeException("cannot encrypt payload", e);
+        }
+    }
+
+    public String decrypt(String encoded) {
+        JWE jwe = createJWE();
+        try {
+            jwe.verifyAndDecodeJwe(encoded);
+            byte[] content = jwe.getContent();
+            if (content == null) return null;
+            return new String(content, "UTF-8");
+        } catch (Exception ex) {
+            throw new RuntimeException("cannot decrypt payload", ex);
+
+        }
+
+    }
+
+    public static String getenv(String name, String defaultValue) {
+        String val = System.getenv(name);
+        return val == null ? defaultValue : val;
+    }
+
+    public File getConfigDirectory() {
+        return Paths.get(getHome(), getenv(KC_LOGIN_CONFIG_PATH, ".keycloak"), "kcinit").toFile();
+    }
+
+
+    public File getConfigFile() {
+        return Paths.get(getHome(), getenv(KC_LOGIN_CONFIG_PATH, ".keycloak"), "kcinit", "config.json").toFile();
+    }
+
+    public File getTokenFilePath(String client) {
+        return Paths.get(getHome(), getenv(KC_LOGIN_CONFIG_PATH, ".keycloak"), "kcinit", "tokens", client).toFile();
+    }
+
+    public File getTokenDirectory() {
+        return Paths.get(getHome(), getenv(KC_LOGIN_CONFIG_PATH, ".keycloak"), "kcinit", "tokens").toFile();
+    }
+
+    protected boolean encrypted = false;
+
+    protected void checkEnv() {
+        File configFile = getConfigFile();
+        if (!configFile.exists()) {
+            KeycloakInstalled.console().writer().println("You have not configured kcinit.  Please run 'kcinit install' to configure.");
+            System.exit(1);
+        }
+        byte[] data = new byte[0];
+        try {
+            data = readFileRaw(configFile);
+        } catch (IOException e) {
+
+        }
+        if (data == null) {
+            KeycloakInstalled.console().writer().println("Config file unreadable.  Please run 'kcinit install' to configure.");
+            System.exit(1);
+
+        }
+        String encodedJwe = null;
+        try {
+            encodedJwe = new String(data, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+
+        if (encodedJwe.contains("realm")) {
+            encrypted = false;
+            return;
+        } else {
+            encrypted = true;
+        }
+
+        if (System.getenv(KC_SESSION_KEY) == null) {
+            promptLocalPassword();
+        }
+    }
+
+    protected void promptLocalPassword() {
+        String password = KeycloakInstalled.console().passwordPrompt("Enter password to unlock kcinit config files: ");
+        try {
+            encryptionKey = generateEncryptionKey(password);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+
+    protected String readFile(File fp) {
+        try {
+            byte[] data = readFileRaw(fp);
+            if (data == null) return null;
+            String file = new String(data, "UTF-8");
+            if (!encrypted) {
+                return file;
+            }
+            String decrypted = decrypt(file);
+            if (decrypted == null)
+                throw new RuntimeException("Unable to decrypt file.  Did you set your local password correctly?");
+            return decrypted;
+        } catch (IOException e) {
+            throw new RuntimeException("failed to decrypt file: " + fp.getAbsolutePath() + " Did you set your local password correctly?", e);
+        }
+
+
+    }
+
+    protected byte[] readFileRaw(File fp) throws IOException {
+        if (!fp.exists()) return null;
+        FileInputStream fis = new FileInputStream(fp);
+        byte[] data = new byte[(int) fp.length()];
+        fis.read(data);
+        fis.close();
+        return data;
+    }
+
+    protected void writeFile(File fp, String payload) {
+        try {
+            String data = payload;
+            if (encrypted) data = encrypt(payload);
+            FileOutputStream fos = new FileOutputStream(fp);
+            fos.write(data.getBytes("UTF-8"));
+            fos.flush();
+            fos.close();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+
+    public void install() {
+        if (getEncryptionKey() == null) {
+            if (KeycloakInstalled.console().confirm("Do you want to protect tokens stored locally with a password? (y/n): ")) {
+                String password = "p";
+                String confirm = "c";
+                do {
+                    password = KeycloakInstalled.console().passwordPrompt("Enter local password: ");
+                    confirm = KeycloakInstalled.console().passwordPrompt("Confirm local password: ");
+                    if (!password.equals(confirm)) {
+                        KeycloakInstalled.console().writer().println();
+                        KeycloakInstalled.console().writer().println("Confirmation does not match.  Try again.");
+                        KeycloakInstalled.console().writer().println();
+                    }
+                } while (!password.equals(confirm));
+                try {
+                    this.encrypted = true;
+                    this.encryptionKey = generateEncryptionKey(password);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                    System.exit(1);
+                }
+            }
+        } else {
+            if (!KeycloakInstalled.console().confirm("KC_SESSION_KEY env var already set.  Do you want to use this as your local encryption key? (y/n): ")) {
+                KeycloakInstalled.console().writer().println("Unset KC_SESSION_KEY env var and run again");
+                System.exit(1);
+            }
+            this.encrypted = true;
+            this.encryptionKey = getEncryptionKey();
+        }
+        String server = KeycloakInstalled.console().readLine("Authentication server URL [http://localhost:8080/auth]: ").trim();
+        String realm = KeycloakInstalled.console().readLine("Name of realm [master]: ").trim();
+        String client = KeycloakInstalled.console().readLine("CLI client id [kcinit]: ").trim();
+        String secret = KeycloakInstalled.console().readLine("CLI client secret [none]: ").trim();
+        if (server.equals("")) {
+            server = "http://localhost:8080/auth";
+        }
+        if (realm.equals("")) {
+            realm = "master";
+        }
+        if (client.equals("")) {
+            client = "kcinit";
+        }
+        File configDir = getTokenDirectory();
+        configDir.mkdirs();
+
+        File configFile = getConfigFile();
+        Map<String, String> props = new HashMap<>();
+        props.put("server", server);
+        props.put("realm", realm);
+        props.put("client", client);
+        props.put("secret", secret);
+
+        try {
+            String json = JsonSerialization.writeValueAsString(props);
+            writeFile(configFile, json);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        KeycloakInstalled.console().writer().println();
+        KeycloakInstalled.console().writer().println("Installation complete!");
+        KeycloakInstalled.console().writer().println();
+    }
+
+
+    public void printHelp() {
+        KeycloakInstalled.console().writer().println("Commands:");
+        KeycloakInstalled.console().writer().println("  login [-f] -f forces login");
+        KeycloakInstalled.console().writer().println("  logout");
+        KeycloakInstalled.console().writer().println("  token [client] - print access token of desired client.  Defaults to default master client.  Will print either 'error', 'not-allowed',  or 'login-required' on error.");
+        KeycloakInstalled.console().writer().println("  install - Install this utility.  Will store in $HOME/.keycloak/kcinit unless " + KC_LOGIN_CONFIG_PATH + " env var is set");
+        System.exit(1);
+    }
+
+
+    public AdapterConfig getConfig() {
+        File configFile = getConfigFile();
+        if (!configFile.exists()) {
+            KeycloakInstalled.console().writer().println("You have not configured kcinit.  Please run 'kcinit install' to configure.");
+            System.exit(1);
+            return null;
+        }
+
+        AdapterConfig config = new AdapterConfig();
+        config.setAuthServerUrl((String) getConfigProperties().get("server"));
+        config.setRealm((String) getConfigProperties().get("realm"));
+        config.setResource((String) getConfigProperties().get("client"));
+        config.setSslRequired("external");
+        String secret = (String) getConfigProperties().get("secret");
+        if (secret != null && !secret.trim().equals("")) {
+            Map<String, Object> creds = new HashMap<>();
+            creds.put("secret", secret);
+            config.setCredentials(creds);
+        } else {
+            config.setPublicClient(true);
+        }
+        return config;
+    }
+
+    private Map<String, String> getConfigProperties() {
+        if (this.config != null) return this.config;
+        if (!getConfigFile().exists()) {
+            KeycloakInstalled.console().writer().println();
+            KeycloakInstalled.console().writer().println(("Config file does not exist.  Run kcinit install to set it up."));
+            System.exit(1);
+        }
+        String json = readFile(getConfigFile());
+        try {
+            Map map = JsonSerialization.readValue(json, Map.class);
+            config = (Map<String, String>) map;
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+        return this.config;
+    }
+
+    public String readToken(String client) throws Exception {
+        String json = getTokenResponse(client);
+        if (json == null) return null;
+
+
+        if (json != null) {
+            try {
+                AccessTokenResponse tokenResponse = JsonSerialization.readValue(json, AccessTokenResponse.class);
+                if (Time.currentTime() < tokenResponse.getExpiresIn()) {
+                    return tokenResponse.getToken();
+                }
+                AdapterConfig config = getConfig();
+                KeycloakInstalled installed = new KeycloakInstalled(KeycloakDeploymentBuilder.build(config));
+                installed.refreshToken(tokenResponse.getRefreshToken());
+                processResponse(installed, client);
+                return tokenResponse.getToken();
+            } catch (Exception e) {
+                File tokenFile = getTokenFilePath(client);
+                if (tokenFile.exists()) {
+                    tokenFile.delete();
+                }
+
+                return null;
+            }
+        }
+        return null;
+
+    }
+
+    public String readRefreshToken(String client) throws Exception {
+        String json = getTokenResponse(client);
+        if (json == null) return null;
+
+
+        if (json != null) {
+            try {
+                AccessTokenResponse tokenResponse = JsonSerialization.readValue(json, AccessTokenResponse.class);
+                return tokenResponse.getRefreshToken();
+            } catch (Exception e) {
+                if (debug) {
+                    e.printStackTrace();
+                }
+                File tokenFile = getTokenFilePath(client);
+                if (tokenFile.exists()) {
+                    tokenFile.delete();
+                }
+
+                return null;
+            }
+        }
+        return null;
+
+    }
+
+
+    private String getTokenResponse(String client) throws IOException {
+        File tokenFile = getTokenFilePath(client);
+        try {
+            return readFile(tokenFile);
+        } catch (Exception e) {
+            if (debug) {
+                System.err.println("Failed to read encrypted file");
+                e.printStackTrace();
+            }
+            if (tokenFile.exists()) tokenFile.delete();
+            return null;
+        }
+    }
+
+
+    public void token() throws Exception {
+        KeycloakInstalled.console().stderrOutput();
+
+        checkEnv();
+        String masterClient = getMasterClient();
+        String client = masterClient;
+        if (args.length > 1) {
+            client = args[1];
+        }
+        //System.err.println("readToken: " + client);
+        String token = readToken(client);
+        if (token != null) {
+            System.out.print(token);
+            return;
+        }
+        if (token == null && client.equals(masterClient)) {
+            //System.err.println("not logged in, logging in.");
+            doConsoleLogin();
+            token = readToken(client);
+            if (token != null) {
+                System.out.print(token);
+                return;
+            }
+
+        }
+        String masterToken = readToken(masterClient);
+        if (masterToken == null) {
+            //System.err.println("not logged in, logging in.");
+            doConsoleLogin();
+            masterToken = readToken(masterClient);
+            if (masterToken == null) {
+                System.err.println("Login failed.  Cannot retrieve token");
+                System.exit(1);
+            }
+        }
+
+        //System.err.println("exchange: " + client);
+        Client httpClient = getHttpClient();
+
+        WebTarget exchangeUrl = httpClient.target(getServer())
+                .path("/realms")
+                .path(getRealm())
+                .path("protocol/openid-connect/token");
+
+        Form form = new Form()
+                .param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.TOKEN_EXCHANGE_GRANT_TYPE)
+                .param(OAuth2Constants.CLIENT_ID, masterClient)
+                .param(OAuth2Constants.SUBJECT_TOKEN, masterToken)
+                .param(OAuth2Constants.SUBJECT_TOKEN_TYPE, OAuth2Constants.ACCESS_TOKEN_TYPE)
+                .param(OAuth2Constants.REQUESTED_TOKEN_TYPE, OAuth2Constants.REFRESH_TOKEN_TYPE)
+                .param(OAuth2Constants.AUDIENCE, client);
+        if (getMasterClientSecret() != null) {
+            form.param(OAuth2Constants.CLIENT_SECRET, getMasterClientSecret());
+        }
+        Response response = exchangeUrl.request().post(Entity.form(
+                form
+        ));
+
+        if (response.getStatus() == 401 || response.getStatus() == 403) {
+            response.close();
+            System.err.println("Not allowed to exchange for client token");
+            System.exit(1);
+        }
+
+        if (response.getStatus() != 200) {
+            if (response.getMediaType() != null && response.getMediaType().equals(MediaType.APPLICATION_JSON_TYPE)) {
+                try {
+                    String json = response.readEntity(String.class);
+                    OAuth2ErrorRepresentation error = JsonSerialization.readValue(json, OAuth2ErrorRepresentation.class);
+                    System.err.println("Failed to exchange token: " + error.getError() + ". " + error.getErrorDescription());
+                    System.exit(1);
+                } catch (Exception ignore) {
+                    ignore.printStackTrace();
+
+                }
+            }
+
+            response.close();
+            System.err.println("Unknown error exchanging for client token: " + response.getStatus());
+            System.exit(1);
+        }
+
+        String json = response.readEntity(String.class);
+        response.close();
+        AccessTokenResponse tokenResponse = JsonSerialization.readValue(json, AccessTokenResponse.class);
+        if (tokenResponse.getToken() != null) {
+            getTokenDirectory().mkdirs();
+            tokenResponse.setExpiresIn(Time.currentTime() + tokenResponse.getExpiresIn());
+            tokenResponse.setIdToken(null);
+            json = JsonSerialization.writeValueAsString(tokenResponse);
+            writeFile(getTokenFilePath(client), json);
+            System.out.printf(tokenResponse.getToken());
+        } else {
+            System.err.println("Error processing token");
+            System.exit(1);
+        }
+    }
+
+    protected String getMasterClientSecret() {
+        return getProperty("secret");
+    }
+
+    protected String getServer() {
+        return getProperty("server");
+    }
+
+    protected String getRealm() {
+        return getProperty("realm");
+    }
+
+    public String getProperty(String name) {
+        return (String) getConfigProperties().get(name);
+    }
+
+    protected boolean forceLogin() {
+        return args.length > 0 && args[0].equals("-f");
+
+    }
+
+    public Client getHttpClient() {
+        return new ResteasyClientBuilder().disableTrustManager().build();
+    }
+
+    public void login() throws Exception {
+        checkEnv();
+        this.args = Arrays.copyOfRange(this.args, 1, this.args.length);
+        for (String arg : args) {
+            if (arg.equals("-f") || arg.equals("-force")) {
+                forceLogin = true;
+                this.args = Arrays.copyOfRange(this.args, 1, this.args.length);
+            } else if (arg.equals("-browser") || arg.equals("-b")) {
+                browserLogin = true;
+                this.args = Arrays.copyOfRange(this.args, 1, this.args.length);
+            } else {
+                System.err.println("Illegal argument: " + arg);
+                printHelp();
+                System.exit(1);
+            }
+        }
+
+        String masterClient = getMasterClient();
+        if (!forceLogin && readToken(masterClient) != null) {
+            KeycloakInstalled.console().writer().println("Already logged in.  `kcinit -f` to force relogin");
+            return;
+        }
+        doConsoleLogin();
+        KeycloakInstalled.console().writer().println("Login successful!");
+    }
+
+    public void doConsoleLogin() throws Exception {
+        String masterClient = getMasterClient();
+        AdapterConfig config = getConfig();
+        KeycloakInstalled installed = new KeycloakInstalled(KeycloakDeploymentBuilder.build(config));
+        //System.err.println("calling loginCommandLine");
+        if (!installed.loginCommandLine()) {
+            System.exit(1);
+        }
+        processResponse(installed, masterClient);
+    }
+
+    private String getMasterClient() {
+        return getProperty("client");
+    }
+
+    private void processResponse(KeycloakInstalled installed, String client) throws IOException {
+        AccessTokenResponse tokenResponse = installed.getTokenResponse();
+        tokenResponse.setExpiresIn(Time.currentTime() + tokenResponse.getExpiresIn());
+        tokenResponse.setIdToken(null);
+        String json = JsonSerialization.writeValueAsString(tokenResponse);
+        getTokenDirectory().mkdirs();
+        writeFile(getTokenFilePath(client), json);
+    }
+
+    public void logout() throws Exception {
+        String token = readRefreshToken(getMasterClient());
+        if (token != null) {
+            try {
+                KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(getConfig());
+                ServerRequest.invokeLogout(deployment, token);
+            } catch (Exception e) {
+                if (debug) {
+                    e.printStackTrace();
+                }
+            }
+
+        }
+        if (getTokenDirectory().exists()) {
+            for (File fp : getTokenDirectory().listFiles()) fp.delete();
+        }
+    }
+    public void uninstall() throws Exception {
+        File configFile = getConfigFile();
+        if (configFile.exists()) configFile.delete();
+        if (getTokenDirectory().exists()) {
+            for (File fp : getTokenDirectory().listFiles()) fp.delete();
+        }
+    }
+}
diff --git a/adapters/oidc/installed/src/main/java/org/keycloak/adapters/installed/KeycloakInstalled.java b/adapters/oidc/installed/src/main/java/org/keycloak/adapters/installed/KeycloakInstalled.java
index f1fee42..4f311c2 100644
--- a/adapters/oidc/installed/src/main/java/org/keycloak/adapters/installed/KeycloakInstalled.java
+++ b/adapters/oidc/installed/src/main/java/org/keycloak/adapters/installed/KeycloakInstalled.java
@@ -39,15 +39,7 @@ import javax.ws.rs.core.Form;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.Response;
 import java.awt.*;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.io.PrintStream;
-import java.io.PrintWriter;
-import java.io.PushbackInputStream;
-import java.io.Reader;
+import java.io.*;
 import java.net.ServerSocket;
 import java.net.Socket;
 import java.net.URI;
@@ -65,6 +57,7 @@ public class KeycloakInstalled {
 
     public interface HttpResponseWriter {
         void success(PrintWriter pw, KeycloakInstalled ki);
+
         void failure(PrintWriter pw, KeycloakInstalled ki);
     }
 
@@ -86,12 +79,12 @@ public class KeycloakInstalled {
     private Locale locale;
     private HttpResponseWriter loginResponseWriter;
     private HttpResponseWriter logoutResponseWriter;
+    private ResteasyClient resteasyClient;
     Pattern callbackPattern = Pattern.compile("callback\\s*=\\s*\"([^\"]+)\"");
     Pattern paramPattern = Pattern.compile("param=\"([^\"]+)\"\\s+label=\"([^\"]+)\"\\s+mask=(\\S+)");
     Pattern codePattern = Pattern.compile("code=([^&]+)");
 
 
-
     public KeycloakInstalled() {
         InputStream config = Thread.currentThread().getContextClassLoader().getResourceAsStream(KEYCLOAK_JSON);
         deployment = KeycloakDeploymentBuilder.build(config);
@@ -179,6 +172,10 @@ public class KeycloakInstalled {
         this.logoutResponseWriter = logoutResponseWriter;
     }
 
+    public void setResteasyClient(ResteasyClient resteasyClient) {
+        this.resteasyClient = resteasyClient;
+    }
+
     public Locale getLocale() {
         return locale;
     }
@@ -302,6 +299,139 @@ public class KeycloakInstalled {
         status = Status.LOGGED_MANUAL;
     }
 
+    public static class Console {
+        protected java.io.Console console = System.console();
+        protected PrintWriter writer;
+        protected BufferedReader reader;
+
+        static Console SINGLETON = new Console();
+
+        private Console() {
+        }
+
+
+        public PrintWriter writer() {
+            if (console == null) {
+                if (writer == null) {
+                    writer = new PrintWriter(System.err, true);
+                }
+                return writer;
+            }
+            return console.writer();
+        }
+
+        public Reader reader() {
+            if (console == null) {
+                return getReader();
+            }
+            return console.reader();
+        }
+
+        protected BufferedReader getReader() {
+            if (reader != null) return reader;
+            reader = new BufferedReader(new BufferedReader(new InputStreamReader(System.in)));
+            return reader;
+        }
+
+        public Console format(String fmt, Object... args) {
+            if (console == null) {
+                writer().format(fmt, args);
+                return this;
+            }
+            console.format(fmt, args);
+            return this;
+        }
+
+        public Console printf(String format, Object... args) {
+            if (console == null) {
+                writer().printf(format, args);
+                return this;
+            }
+            console.printf(format, args);
+            return this;
+        }
+
+        public String readLine(String fmt, Object... args) {
+            if (console == null) {
+                format(fmt, args);
+                return readLine();
+            }
+            return console.readLine(fmt, args);
+        }
+
+        public boolean confirm(String fmt, Object... args) {
+            String prompt = "";
+            while (!"y".equals(prompt) && !"n".equals(prompt)) {
+                prompt = readLine(fmt, args);
+            }
+            return "y".equals(prompt);
+
+        }
+
+        public String prompt(String fmt, Object... args) {
+            String prompt = "";
+            while (prompt.equals("")) {
+                prompt = readLine(fmt, args).trim();
+            }
+            return prompt;
+
+        }
+
+        public String passwordPrompt(String fmt, Object... args) {
+            String prompt = "";
+            while (prompt.equals("")) {
+                char[] val = readPassword(fmt, args);
+                prompt = new String(val);
+                prompt = prompt.trim();
+            }
+            return prompt;
+
+        }
+
+        public String readLine() {
+            if (console == null) {
+                try {
+                    return getReader().readLine();
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+            return console.readLine();
+        }
+
+        public char[] readPassword(String fmt, Object... args) {
+            if (console == null) {
+                return readLine(fmt, args).toCharArray();
+
+            }
+            return console.readPassword(fmt, args);
+        }
+
+        public char[] readPassword() {
+            if (console == null) {
+                return readLine().toCharArray();
+            }
+            return console.readPassword();
+        }
+
+        public void flush() {
+            if (console == null) {
+                System.err.flush();
+                return;
+            }
+            console.flush();
+        }
+
+        public void stderrOutput() {
+            //System.err.println("not using System.console()");
+            console = null;
+        }
+    }
+
+    public static Console console() {
+        return Console.SINGLETON;
+    }
+
     public boolean loginCommandLine() throws IOException, ServerRequest.HttpFailure, VerificationException {
         String redirectUri = "urn:ietf:wg:oauth:2.0:oob";
 
@@ -309,7 +439,6 @@ public class KeycloakInstalled {
     }
 
 
-
     /**
      * Experimental proprietary WWW-Authentication challenge protocol.
      * WWW-Authentication: X-Text-Form-Challenge callback="{url}" param="{param-name}" label="{param-display-label}"
@@ -325,62 +454,116 @@ public class KeycloakInstalled {
                 .queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE)
                 .queryParam(OAuth2Constants.CLIENT_ID, deployment.getResourceName())
                 .queryParam(OAuth2Constants.REDIRECT_URI, redirectUri)
+                .queryParam("display", "console")
                 .queryParam(OAuth2Constants.SCOPE, OAuth2Constants.SCOPE_OPENID)
                 .build().toString();
-        ResteasyClient client = new ResteasyClientBuilder().disableTrustManager().build();
+        ResteasyClient client = createResteasyClient();
         try {
+            //System.err.println("initial request");
             Response response = client.target(authUrl).request().get();
-            if (response.getStatus() != 401) {
-                return false;
-            }
             while (true) {
-                String authenticationHeader = response.getHeaderString(HttpHeaders.WWW_AUTHENTICATE);
-                if (authenticationHeader == null) {
-                    return false;
-                }
-                if (!authenticationHeader.contains("X-Text-Form-Challenge")) {
+                if (response.getStatus() == 403) {
+                    if (response.getMediaType() != null) {
+                        String splash = response.readEntity(String.class);
+                        console().writer().println(splash);
+                    } else {
+                        System.err.println("Forbidden to login");
+                    }
                     return false;
-                }
-                if (response.getMediaType() != null) {
-                    String splash = response.readEntity(String.class);
-                    System.console().writer().println(splash);
-                }
-                Matcher m = callbackPattern.matcher(authenticationHeader);
-                if (!m.find()) return false;
-                String callback = m.group(1);
-                //System.err.println("callback: " + callback);
-                m = paramPattern.matcher(authenticationHeader);
-                Form form = new Form();
-                while (m.find()) {
-                    String param = m.group(1);
-                    String label = m.group(2);
-                    String mask = m.group(3).trim();
-                    boolean maskInput = mask.equals("true");
-                    String value = null;
-                    if (maskInput) {
-                        char[] txt = System.console().readPassword(label);
-                        value = new String(txt);
+                } else if (response.getStatus() == 401) {
+                    String authenticationHeader = response.getHeaderString(HttpHeaders.WWW_AUTHENTICATE);
+                    if (authenticationHeader == null) {
+                        System.err.println("Failure:  Invalid protocol.  No WWW-Authenticate header");
+                        return false;
+                    }
+                    //System.err.println("got header: " + authenticationHeader);
+                    if (!authenticationHeader.contains("X-Text-Form-Challenge")) {
+                        System.err.println("Failure:  Invalid WWW-Authenticate header.");
+                        return false;
+                    }
+                    if (response.getMediaType() != null) {
+                        String splash = response.readEntity(String.class);
+                        console().writer().println(splash);
                     } else {
-                        value = System.console().readLine(label);
+                        response.close();
+                    }
+                    Matcher m = callbackPattern.matcher(authenticationHeader);
+                    if (!m.find()) {
+                        System.err.println("Failure: Invalid WWW-Authenticate header.");
+                        return false;
+                    }
+                    String callback = m.group(1);
+                    //System.err.println("callback: " + callback);
+                    m = paramPattern.matcher(authenticationHeader);
+                    Form form = new Form();
+                    while (m.find()) {
+                        String param = m.group(1);
+                        String label = m.group(2);
+                        String mask = m.group(3).trim();
+                        boolean maskInput = mask.equals("true");
+                        String value = null;
+                        if (maskInput) {
+                            char[] txt = console().readPassword(label);
+                            value = new String(txt);
+                        } else {
+                            value = console().readLine(label);
+                        }
+                        form.param(param, value);
                     }
-                    form.param(param, value);
+                    response.close();
+                    client.close();
+                    client = createResteasyClient();
+                    response = client.target(callback).request().post(Entity.form(form));
+                } else if (response.getStatus() == 302) {
+                    int redirectCount = 0;
+                    do {
+                        String location = response.getLocation().toString();
+                        Matcher m = codePattern.matcher(location);
+                        if (!m.find()) {
+                            response.close();
+                            client.close();
+                            client = createResteasyClient();
+                            response = client.target(location).request().get();
+                        } else {
+                            response.close();
+                            client.close();
+                            String code = m.group(1);
+                            processCode(code, redirectUri);
+                            return true;
+                        }
+                        if (response.getStatus() == 302 && redirectCount++ > 4) {
+                            System.err.println("Too many redirects.  Aborting");
+                            return false;
+                        }
+                    } while (response.getStatus() == 302);
+                } else {
+                    System.err.println("Unknown response from server: " + response.getStatus());
+                    return false;
                 }
-                response = client.target(callback).request().post(Entity.form(form));
-                if (response.getStatus() == 401) continue;
-                if (response.getStatus() != 302) return false;
-                String location = response.getLocation().toString();
-                m = codePattern.matcher(location);
-                if (!m.find()) return false;
-                String code = m.group(1);
-                processCode(code, redirectUri);
-                return true;
             }
+        } catch (Exception ex) {
+            throw ex;
         } finally {
             client.close();
 
         }
     }
 
+    protected ResteasyClient getResteasyClient() {
+        if (this.resteasyClient == null) {
+            this.resteasyClient = createResteasyClient();
+        }
+        return this.resteasyClient;
+    }
+
+    protected ResteasyClient createResteasyClient() {
+        return new ResteasyClientBuilder()
+                .connectionCheckoutTimeout(1, TimeUnit.HOURS)
+                .connectionTTL(1, TimeUnit.HOURS)
+                .socketTimeout(1, TimeUnit.HOURS)
+                .disableTrustManager().build();
+    }
+
 
     public String getTokenString() throws VerificationException, IOException, ServerRequest.HttpFailure {
         return tokenString;
@@ -400,7 +583,7 @@ public class KeycloakInstalled {
         parseAccessToken(tokenResponse);
     }
 
-    public void refreshToken(String refreshToken)  throws IOException, ServerRequest.HttpFailure, VerificationException {
+    public void refreshToken(String refreshToken) throws IOException, ServerRequest.HttpFailure, VerificationException {
         AccessTokenResponse tokenResponse = ServerRequest.invokeRefresh(deployment, refreshToken);
         parseAccessToken(tokenResponse);
 
@@ -452,7 +635,6 @@ public class KeycloakInstalled {
     }
 
 
-
     private void processCode(String code, String redirectUri) throws IOException, ServerRequest.HttpFailure, VerificationException {
         AccessTokenResponse tokenResponse = ServerRequest.invokeAccessCodeToToken(deployment, code, redirectUri, null);
         parseAccessToken(tokenResponse);
@@ -474,86 +656,6 @@ public class KeycloakInstalled {
         return sb.toString();
     }
 
-    public static class MaskingThread extends Thread {
-        private volatile boolean stop;
-        private char echochar = '*';
-
-        public MaskingThread() {
-        }
-
-        /**
-         * Begin masking until asked to stop.
-         */
-        public void run() {
-
-            int priority = Thread.currentThread().getPriority();
-            Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
-
-            try {
-                stop = true;
-                while(stop) {
-                    System.out.print("\010" + echochar);
-                    try {
-                        // attempt masking at this rate
-                        Thread.currentThread().sleep(1);
-                    }catch (InterruptedException iex) {
-                        Thread.currentThread().interrupt();
-                        return;
-                    }
-                }
-            } finally { // restore the original priority
-                Thread.currentThread().setPriority(priority);
-            }
-        }
-
-        /**
-         * Instruct the thread to stop masking.
-         */
-        public void stopMasking() {
-            this.stop = false;
-        }
-    }
-
-    public static String readMasked(Reader reader) {
-        MaskingThread et = new MaskingThread();
-        Thread mask = new Thread(et);
-        mask.start();
-
-        BufferedReader in = new BufferedReader(reader);
-        String password = "";
-
-        try {
-            password = in.readLine();
-        } catch (IOException ioe) {
-            ioe.printStackTrace();
-        }
-        // stop masking
-        et.stopMasking();
-        // return the password entered by the user
-        return password;
-    }
-
-    private String readLine(Reader reader, boolean mask) throws IOException {
-        if (mask) {
-            System.out.print(" ");
-            return readMasked(reader);
-        }
-
-        StringBuilder sb = new StringBuilder();
-
-        char cb[] = new char[1];
-        while (reader.read(cb) != -1) {
-            char c = cb[0];
-            if ((c == '\n') || (c == '\r')) {
-                break;
-            } else {
-                sb.append(c);
-            }
-        }
-
-        return sb.toString();
-    }
-
 
     public class CallbackListener extends Thread {
 
diff --git a/adapters/oidc/kcinit/src/main/bin/kcinit b/adapters/oidc/kcinit/src/main/bin/kcinit
new file mode 100755
index 0000000..4f5c2c6
--- /dev/null
+++ b/adapters/oidc/kcinit/src/main/bin/kcinit
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+case "`uname`" in
+    CYGWIN*)
+        CFILE = `cygpath "$0"`
+        RESOLVED_NAME=`readlink -f "$CFILE"`
+        ;;
+    Darwin*)
+        RESOLVED_NAME=`readlink "$0"`
+        ;;
+    FreeBSD)
+        RESOLVED_NAME=`readlink -f "$0"`
+        ;;
+    Linux)
+        RESOLVED_NAME=`readlink -f "$0"`
+        ;;
+esac
+
+if [ "x$RESOLVED_NAME" = "x" ]; then
+    RESOLVED_NAME="$0"
+fi
+
+SCRIPTPATH=`dirname "$RESOLVED_NAME"`
+JAR=$SCRIPTPATH/kcinit-${project.version}.jar
+
+java -jar $JAR $@
diff --git a/adapters/oidc/kcinit/src/main/bin/kcinit.bat b/adapters/oidc/kcinit/src/main/bin/kcinit.bat
new file mode 100755
index 0000000..9055309
--- /dev/null
+++ b/adapters/oidc/kcinit/src/main/bin/kcinit.bat
@@ -0,0 +1,8 @@
+@echo off
+
+if "%OS%" == "Windows_NT" (
+  set "DIRNAME=%~dp0%"
+) else (
+  set DIRNAME=.\
+)
+java -jar %DIRNAME%\kcinit-${project.version}.jar %*
diff --git a/adapters/oidc/pom.xml b/adapters/oidc/pom.xml
index c380735..6bc8473 100755
--- a/adapters/oidc/pom.xml
+++ b/adapters/oidc/pom.xml
@@ -34,7 +34,7 @@
         <module>adapter-core</module>
         <module>as7-eap6</module>
         <module>installed</module>
-        <module>cli-sso</module>
+        <module>kcinit</module>
         <module>jaxrs-oauth-client</module>
         <module>jetty</module>
         <module>js</module>
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectedResource.java b/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectedResource.java
index cf2e91a..80c9e42 100644
--- a/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectedResource.java
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectedResource.java
@@ -23,11 +23,11 @@ import java.util.List;
 import java.util.concurrent.Callable;
 
 import org.keycloak.authorization.client.Configuration;
-import org.keycloak.authorization.client.representation.ResourceRepresentation;
 import org.keycloak.authorization.client.representation.ServerConfiguration;
 import org.keycloak.authorization.client.util.Http;
 import org.keycloak.authorization.client.util.Throwables;
 import org.keycloak.authorization.client.util.TokenCallable;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
 import org.keycloak.util.JsonSerialization;
 
 /**
@@ -124,11 +124,11 @@ public class ProtectedResource {
     /**
      * Query the server for a resource given its <code>name</code> where the owner is the resource server itself.
      *
-     * @param id the resource name
+     * @param name the resource name
      * @return a {@link ResourceRepresentation}
      */
     public ResourceRepresentation findByName(String name) {
-        String[] representations = find(null, name, null, configuration.getResource(), null, null, null, null);
+        String[] representations = find(null, name, null, configuration.getResource(), null, null, false, null, null);
 
         if (representations.length == 0) {
             return null;
@@ -145,7 +145,7 @@ public class ProtectedResource {
      * @return a {@link ResourceRepresentation}
      */
     public ResourceRepresentation findByName(String name, String ownerId) {
-        String[] representations = find(null, name, null, ownerId, null, null, null, null);
+        String[] representations = find(null, name, null, ownerId, null, null, false, null, null);
 
         if (representations.length == 0) {
             return null;
@@ -163,11 +163,12 @@ public class ProtectedResource {
      * @param owner the resource owner
      * @param type the resource type
      * @param scope the resource scope
+     * @param matchingUri the resource uri. Use this parameter to lookup a resource that best match the given uri
      * @param firstResult the position of the first resource to retrieve
      * @param maxResult the maximum number of resources to retrieve
      * @return an array of strings with the resource ids
      */
-    public String[] find(final String id, final String name, final String uri, final String owner, final String type, final String scope, final Integer firstResult, final Integer maxResult) {
+    public String[] find(final String id, final String name, final String uri, final String owner, final String type, final String scope, final boolean matchingUri, final Integer firstResult, final Integer maxResult) {
         Callable<String[]> callable = new Callable<String[]>() {
             @Override
             public String[] call() throws Exception {
@@ -179,6 +180,7 @@ public class ProtectedResource {
                         .param("owner", owner)
                         .param("type", type)
                         .param("scope", scope)
+                        .param("matchingUri", Boolean.valueOf(matchingUri).toString())
                         .param("deep", Boolean.FALSE.toString())
                         .param("first", firstResult != null ? firstResult.toString() : null)
                         .param("max", maxResult != null ? maxResult.toString() : null)
@@ -199,7 +201,7 @@ public class ProtectedResource {
      */
     public String[] findAll() {
         try {
-            return find(null,null , null, null, null, null, null, null);
+            return find(null,null , null, null, null, null, false, null, null);
         } catch (Exception cause) {
             throw Throwables.handleWrapException("Could not find resource", cause);
         }
@@ -233,7 +235,30 @@ public class ProtectedResource {
      * @param uri the resource uri
      */
     public List<ResourceRepresentation> findByUri(String uri) {
-        String[] ids = find(null, null, uri, null, null, null, null, null);
+        String[] ids = find(null, null, uri, null, null, null, false, null, null);
+
+        if (ids.length == 0) {
+            return Collections.emptyList();
+        }
+
+        List<ResourceRepresentation> representations = new ArrayList<>();
+
+        for (String id : ids) {
+            representations.add(findById(id));
+        }
+
+        return representations;
+    }
+
+    /**
+     * Returns a list of resources that best matches the given {@code uri}. This method queries the server for resources whose
+     * {@link ResourceRepresentation#uri} best matches the given {@code uri}.
+     *
+     * @param uri the resource uri to match
+     * @return a list of resources
+     */
+    public List<ResourceRepresentation> findByMatchingUri(String uri) {
+        String[] ids = find(null, null, uri, null, null, null, true, null, null);
 
         if (ids.length == 0) {
             return Collections.emptyList();
diff --git a/common/src/main/java/org/keycloak/common/util/RandomString.java b/common/src/main/java/org/keycloak/common/util/RandomString.java
new file mode 100644
index 0000000..70ce02d
--- /dev/null
+++ b/common/src/main/java/org/keycloak/common/util/RandomString.java
@@ -0,0 +1,66 @@
+package org.keycloak.common.util;
+
+import java.security.SecureRandom;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.Random;
+
+public class RandomString {
+
+    /**
+     * Generate a random string.
+     */
+    public String nextString() {
+        for (int idx = 0; idx < buf.length; ++idx)
+            buf[idx] = symbols[random.nextInt(symbols.length)];
+        return new String(buf);
+    }
+
+    public static final String upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+    public static final String lower = upper.toLowerCase(Locale.ROOT);
+
+    public static final String digits = "0123456789";
+
+    public static final String alphanum = upper + lower + digits;
+
+    private final Random random;
+
+    private final char[] symbols;
+
+    private final char[] buf;
+
+    public RandomString(int length, Random random, String symbols) {
+        if (length < 1) throw new IllegalArgumentException();
+        if (symbols.length() < 2) throw new IllegalArgumentException();
+        this.random = Objects.requireNonNull(random);
+        this.symbols = symbols.toCharArray();
+        this.buf = new char[length];
+    }
+
+    /**
+     * Create an alphanumeric string generator.
+     */
+    public RandomString(int length, Random random) {
+        this(length, random, alphanum);
+    }
+
+    /**
+     * Create an alphanumeric strings from a secure generator.
+     */
+    public RandomString(int length) {
+        this(length, new SecureRandom());
+    }
+
+    /**
+     * Create session identifiers.
+     */
+    public RandomString() {
+        this(21);
+    }
+
+    public static String randomCode(int length) {
+        return new RandomString(length).nextString();
+    }
+
+}
\ No newline at end of file
diff --git a/core/src/main/java/org/keycloak/AuthorizationContext.java b/core/src/main/java/org/keycloak/AuthorizationContext.java
index 0a9b332..538a70f 100644
--- a/core/src/main/java/org/keycloak/AuthorizationContext.java
+++ b/core/src/main/java/org/keycloak/AuthorizationContext.java
@@ -59,53 +59,31 @@ public class AuthorizationContext {
             return false;
         }
 
-        if (current != null) {
-            if (current.getName().equals(resourceName)) {
-                return true;
-            }
-        }
+        for (Permission permission : authorization.getPermissions()) {
+            if (resourceName.equalsIgnoreCase(permission.getResourceName()) || resourceName.equalsIgnoreCase(permission.getResourceId())) {
+                if (scopeName == null) {
+                    return true;
+                }
 
-        if (hasResourcePermission(resourceName)) {
-            for (Permission permission : authorization.getPermissions()) {
-                for (PathConfig pathHolder : paths.values()) {
-                    if (pathHolder.getId().equals(permission.getResourceId())) {
-                        if (permission.getScopes().contains(scopeName)) {
-                            return true;
-                        }
-                    }
+                if (permission.getScopes().contains(scopeName)) {
+                    return true;
                 }
             }
         }
 
-        return false;
-    }
-
-    public boolean hasResourcePermission(String resourceName) {
-        if (this.authzToken == null) {
-            return false;
-        }
-
-        Authorization authorization = this.authzToken.getAuthorization();
-
-        if (authorization == null) {
-            return false;
-        }
-
         if (current != null) {
             if (current.getName().equals(resourceName)) {
                 return true;
             }
         }
 
-        for (Permission permission : authorization.getPermissions()) {
-            if (permission.getResourceName().equals(resourceName) || permission.getResourceId().equals(resourceName)) {
-                return true;
-            }
-        }
-
         return false;
     }
 
+    public boolean hasResourcePermission(String resourceName) {
+        return hasPermission(resourceName, null);
+    }
+
     public boolean hasScopePermission(String scopeName) {
         if (this.authzToken == null) {
             return false;
diff --git a/core/src/main/java/org/keycloak/jose/jwe/JWE.java b/core/src/main/java/org/keycloak/jose/jwe/JWE.java
index 75759dd..8d954ea 100644
--- a/core/src/main/java/org/keycloak/jose/jwe/JWE.java
+++ b/core/src/main/java/org/keycloak/jose/jwe/JWE.java
@@ -18,13 +18,23 @@
 package org.keycloak.jose.jwe;
 
 import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
 
+import org.keycloak.common.util.Base64;
 import org.keycloak.common.util.Base64Url;
 import org.keycloak.common.util.BouncyIntegration;
 import org.keycloak.jose.jwe.alg.JWEAlgorithmProvider;
 import org.keycloak.jose.jwe.enc.JWEEncryptionProvider;
 import org.keycloak.util.JsonSerialization;
 
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.SecretKeySpec;
+
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
@@ -193,4 +203,66 @@ public class JWE {
         }
     }
 
+    public static String encryptUTF8(String password, String saltString, String payload) {
+        byte[] bytes = null;
+        try {
+            bytes = payload.getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException(e);
+        }
+        return encrypt(password, saltString, bytes);
+
+    }
+
+
+    public static String encrypt(String password, String saltString, byte[] payload) {
+        try {
+            byte[] salt = Base64.decode(saltString);
+            SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
+            KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 100, 128);
+            SecretKey tmp = factory.generateSecret(spec);
+            SecretKey aesKey = new SecretKeySpec(tmp.getEncoded(), "AES");
+
+            JWEHeader jweHeader = new JWEHeader(JWEConstants.A128KW, JWEConstants.A128CBC_HS256, null);
+            JWE jwe = new JWE()
+                    .header(jweHeader)
+                    .content(payload);
+
+            jwe.getKeyStorage()
+                    .setEncryptionKey(aesKey);
+
+            return jwe.encodeJwe();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static byte[] decrypt(String password, String saltString, String encodedJwe) {
+        try {
+            byte[] salt = Base64.decode(saltString);
+            SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
+            KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 100, 128);
+            SecretKey tmp = factory.generateSecret(spec);
+            SecretKey aesKey = new SecretKeySpec(tmp.getEncoded(), "AES");
+
+            JWE jwe = new JWE();
+            jwe.getKeyStorage()
+                    .setEncryptionKey(aesKey);
+
+            jwe.verifyAndDecodeJwe(encodedJwe);
+            return jwe.getContent();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static String decryptUTF8(String password, String saltString, String encodedJwe) {
+        byte[] payload = decrypt(password, saltString, encodedJwe);
+        try {
+            return new String(payload, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
 }
diff --git a/core/src/main/java/org/keycloak/OAuth2Constants.java b/core/src/main/java/org/keycloak/OAuth2Constants.java
index df54112..2ef01cb 100644
--- a/core/src/main/java/org/keycloak/OAuth2Constants.java
+++ b/core/src/main/java/org/keycloak/OAuth2Constants.java
@@ -34,6 +34,8 @@ public interface OAuth2Constants {
 
     String REDIRECT_URI = "redirect_uri";
 
+    String DISPLAY = "display";
+
     String SCOPE = "scope";
 
     String STATE = "state";
@@ -114,6 +116,7 @@ public interface OAuth2Constants {
     String UMA_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:uma-ticket";
 
 
+    String DISPLAY_CONSOLE = "console";
 }
 
 
diff --git a/core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java b/core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java
index 89dadbf..71b44a4 100644
--- a/core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java
+++ b/core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java
@@ -23,16 +23,14 @@ import java.util.List;
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonProperty;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
 public class PolicyEnforcerConfig {
 
-    @JsonProperty("create-resources")
-    @JsonInclude(JsonInclude.Include.NON_NULL)
-    private Boolean createResources = Boolean.FALSE;
-
     @JsonProperty("enforcement-mode")
     private EnforcementMode enforcementMode = EnforcementMode.ENFORCING;
 
@@ -40,6 +38,9 @@ public class PolicyEnforcerConfig {
     @JsonInclude(JsonInclude.Include.NON_EMPTY)
     private List<PathConfig> paths = new ArrayList<>();
 
+    @JsonProperty("lazy-load-paths")
+    private Boolean lazyLoadPaths = Boolean.FALSE;
+
     @JsonProperty("on-deny-redirect-to")
     @JsonInclude(JsonInclude.Include.NON_NULL)
     private String onDenyRedirectTo;
@@ -48,14 +49,18 @@ public class PolicyEnforcerConfig {
     @JsonInclude(JsonInclude.Include.NON_NULL)
     private UserManagedAccessConfig userManagedAccess;
 
-    public Boolean isCreateResources() {
-        return this.createResources;
-    }
-
     public List<PathConfig> getPaths() {
         return this.paths;
     }
 
+    public Boolean getLazyLoadPaths() {
+        return lazyLoadPaths;
+    }
+
+    public void setLazyLoadPaths(Boolean lazyLoadPaths) {
+        this.lazyLoadPaths = lazyLoadPaths;
+    }
+
     public EnforcementMode getEnforcementMode() {
         return this.enforcementMode;
     }
@@ -68,10 +73,6 @@ public class PolicyEnforcerConfig {
         return this.userManagedAccess;
     }
 
-    public void setCreateResources(Boolean createResources) {
-        this.createResources = createResources;
-    }
-
     public void setPaths(List<PathConfig> paths) {
         this.paths = paths;
     }
@@ -90,6 +91,32 @@ public class PolicyEnforcerConfig {
 
     public static class PathConfig {
 
+        public static PathConfig createPathConfig(ResourceRepresentation resourceDescription) {
+            PathConfig pathConfig = new PathConfig();
+
+            pathConfig.setId(resourceDescription.getId());
+            pathConfig.setName(resourceDescription.getName());
+
+            String uri = resourceDescription.getUri();
+
+            if (uri == null || "".equals(uri.trim())) {
+                throw new RuntimeException("Failed to configure paths. Resource [" + resourceDescription.getName() + "] has an invalid or empty URI [" + uri + "].");
+            }
+
+            pathConfig.setPath(uri);
+
+            List<String> scopeNames = new ArrayList<>();
+
+            for (ScopeRepresentation scope : resourceDescription.getScopes()) {
+                scopeNames.add(scope.getName());
+            }
+
+            pathConfig.setScopes(scopeNames);
+            pathConfig.setType(resourceDescription.getType());
+
+            return pathConfig;
+        }
+
         private String name;
         private String type;
         private String path;
@@ -231,7 +258,8 @@ public class PolicyEnforcerConfig {
 
     public enum ScopeEnforcementMode {
         ALL,
-        ANY
+        ANY,
+        DISABLED
     }
 
     public static class UserManagedAccessConfig {
diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/ResourceRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/authorization/ResourceRepresentation.java
index 071bc32..92f4717 100644
--- a/core/src/main/java/org/keycloak/representations/idm/authorization/ResourceRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/ResourceRepresentation.java
@@ -24,8 +24,10 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonSetter;
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 import org.keycloak.json.StringListMapDeserializer;
 
@@ -45,6 +47,7 @@ public class ResourceRepresentation {
     private String uri;
     private String type;
     @JsonInclude(JsonInclude.Include.NON_EMPTY)
+    @JsonProperty("scopes")
     private Set<ScopeRepresentation> scopes;
 
     @JsonProperty("icon_uri")
@@ -52,9 +55,6 @@ public class ResourceRepresentation {
     private ResourceOwnerRepresentation owner;
     private Boolean ownerManagedAccess;
 
-    @JsonInclude(JsonInclude.Include.NON_EMPTY)
-    private List<PolicyRepresentation> policies;
-
     private String displayName;
 
     @JsonDeserialize(using = StringListMapDeserializer.class)
@@ -162,17 +162,31 @@ public class ResourceRepresentation {
     }
 
     public void setUri(String uri) {
-        this.uri = uri;
+        if (uri != null && !"".equalsIgnoreCase(uri.trim())) {
+            this.uri = uri;
+        }
     }
 
     public void setType(String type) {
-        this.type = type;
+        if (type != null && !"".equalsIgnoreCase(type.trim())) {
+            this.type = type;
+        }
     }
 
     public void setScopes(Set<ScopeRepresentation> scopes) {
         this.scopes = scopes;
     }
 
+    /**
+     * TODO: This is a workaround to allow deserialization of UMA resource representation. Jackson 2.19+ support aliases, once we upgrade, change this.
+     *
+     * @param scopes
+     */
+    @JsonSetter("resource_scopes")
+    private void setScopesUma(Set<ScopeRepresentation> scopes) {
+        this.scopes = scopes;
+    }
+
     public void setIconUri(String iconUri) {
         this.iconUri = iconUri;
     }
@@ -181,10 +195,25 @@ public class ResourceRepresentation {
         return this.owner;
     }
 
+    @JsonProperty
     public void setOwner(ResourceOwnerRepresentation owner) {
         this.owner = owner;
     }
 
+    @JsonIgnore
+    public void setOwner(String ownerId) {
+        if (ownerId == null) {
+            owner = null;
+            return;
+        }
+
+        if (owner == null) {
+            owner = new ResourceOwnerRepresentation();
+        }
+
+        owner.setId(ownerId);
+    }
+
     public Boolean getOwnerManagedAccess() {
         return ownerManagedAccess;
     }
diff --git a/core/src/test/java/org/keycloak/jose/JWETest.java b/core/src/test/java/org/keycloak/jose/JWETest.java
index 31d8a8a..cc179bf 100644
--- a/core/src/test/java/org/keycloak/jose/JWETest.java
+++ b/core/src/test/java/org/keycloak/jose/JWETest.java
@@ -19,18 +19,18 @@ package org.keycloak.jose;
 
 import java.io.UnsupportedEncodingException;
 import java.security.Key;
+import java.security.spec.KeySpec;
 
 import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
 import javax.crypto.spec.SecretKeySpec;
 
 import org.junit.Assert;
 import org.junit.Test;
+import org.keycloak.common.util.Base64;
 import org.keycloak.common.util.Base64Url;
-import org.keycloak.jose.jwe.JWE;
-import org.keycloak.jose.jwe.JWEConstants;
-import org.keycloak.jose.jwe.JWEException;
-import org.keycloak.jose.jwe.JWEHeader;
-import org.keycloak.jose.jwe.JWEKeyStorage;
+import org.keycloak.jose.jwe.*;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -53,7 +53,6 @@ public class JWETest {
         testDirectEncryptAndDecrypt(aesKey, hmacKey, JWEConstants.A128CBC_HS256, PAYLOAD, true);
     }
 
-
     // Works just on OpenJDK 8. Other JDKs (IBM, Oracle) have restrictions on maximum key size of AES to be 128
     // @Test
     public void testDirect_Aes256CbcHmacSha512() throws Exception {
@@ -119,9 +118,24 @@ public class JWETest {
     }
 
     @Test
+    public void testPassword() throws Exception {
+        byte[] salt = JWEUtils.generateSecret(8);
+        String encodedSalt = Base64.encodeBytes(salt);
+        String jwe = JWE.encryptUTF8("geheim", encodedSalt, PAYLOAD);
+        String decodedContent = JWE.decryptUTF8("geheim", encodedSalt, jwe);
+        Assert.assertEquals(PAYLOAD, decodedContent);
+    }
+
+
+
+    @Test
     public void testAesKW_Aes128CbcHmacSha256() throws Exception {
         SecretKey aesKey = new SecretKeySpec(AES_128_KEY, "AES");
 
+        testAesKW_Aes128CbcHmacSha256(aesKey);
+    }
+
+    private void testAesKW_Aes128CbcHmacSha256(SecretKey aesKey) throws UnsupportedEncodingException, JWEException {
         JWEHeader jweHeader = new JWEHeader(JWEConstants.A128KW, JWEConstants.A128CBC_HS256, null);
         JWE jwe = new JWE()
                 .header(jweHeader)
@@ -146,6 +160,15 @@ public class JWETest {
         Assert.assertEquals(PAYLOAD, decodedContent);
     }
 
+    @Test
+    public void testSalt() {
+        byte[] random = JWEUtils.generateSecret(8);
+        System.out.print("new byte[] = {");
+        for (byte b : random) {
+            System.out.print(""+Byte.toString(b)+",");
+        }
+    }
+
 
     @Test
     public void externalJweAes128CbcHmacSha256Test() throws UnsupportedEncodingException, JWEException {
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/PolicyEntity.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/PolicyEntity.java
index 9b7e3b1..984c6ba 100644
--- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/PolicyEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/PolicyEntity.java
@@ -41,6 +41,7 @@ import javax.persistence.OneToMany;
 import javax.persistence.Table;
 import javax.persistence.UniqueConstraint;
 
+import org.hibernate.annotations.Nationalized;
 import org.keycloak.representations.idm.authorization.DecisionStrategy;
 import org.keycloak.representations.idm.authorization.Logic;
 
@@ -77,6 +78,7 @@ public class PolicyEntity {
     @Column(name = "NAME")
     private String name;
 
+    @Nationalized
     @Column(name = "DESCRIPTION")
     private String description;
 
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceStore.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceStore.java
index 0c82dc0..b9cecf7 100644
--- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceStore.java
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceStore.java
@@ -181,6 +181,10 @@ public class JPAResourceStore implements ResourceStore {
                 predicates.add(root.join("scopes").get("id").in(value));
             } else if ("ownerManagedAccess".equals(name)) {
                 predicates.add(builder.equal(root.get(name), Boolean.valueOf(value[0])));
+            } else if ("uri".equals(name)) {
+                predicates.add(builder.equal(builder.lower(root.get(name)), value[0].toLowerCase()));
+            } else if ("uri_not_null".equals(name)) {
+                predicates.add(builder.isNotNull(root.get("uri")));
             } else {
                 predicates.add(builder.like(builder.lower(root.get(name)), "%" + value[0].toLowerCase() + "%"));
             }
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationFlowEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationFlowEntity.java
index 2413a46..a56c2ee 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationFlowEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationFlowEntity.java
@@ -17,6 +17,8 @@
 
 package org.keycloak.models.jpa.entities;
 
+import org.hibernate.annotations.Nationalized;
+
 import javax.persistence.Access;
 import javax.persistence.AccessType;
 import javax.persistence.CascadeType;
@@ -53,6 +55,7 @@ public class AuthenticationFlowEntity {
     @Column(name="PROVIDER_ID")
     protected String providerId;
 
+    @Nationalized
     @Column(name="DESCRIPTION")
     protected String description;
 
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java
index 7ede55c..ea8a580 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java
@@ -17,6 +17,8 @@
 
 package org.keycloak.models.jpa.entities;
 
+import org.hibernate.annotations.Nationalized;
+
 import javax.persistence.Access;
 import javax.persistence.AccessType;
 import javax.persistence.CascadeType;
@@ -61,8 +63,10 @@ public class ClientEntity {
     @Column(name="ID", length = 36)
     @Access(AccessType.PROPERTY) // we do this because relationships often fetch id, but not entity.  This avoids an extra SQL
     private String id;
+    @Nationalized
     @Column(name = "NAME")
     private String name;
+    @Nationalized
     @Column(name = "DESCRIPTION")
     private String description;
     @Column(name = "CLIENT_ID")
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientTemplateEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientTemplateEntity.java
index ef57b11..4aa1c6c 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientTemplateEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientTemplateEntity.java
@@ -17,6 +17,8 @@
 
 package org.keycloak.models.jpa.entities;
 
+import org.hibernate.annotations.Nationalized;
+
 import javax.persistence.Access;
 import javax.persistence.AccessType;
 import javax.persistence.CascadeType;
@@ -51,6 +53,7 @@ public class ClientTemplateEntity {
     private String id;
     @Column(name = "NAME")
     private String name;
+    @Nationalized
     @Column(name = "DESCRIPTION")
     private String description;
     @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "clientTemplate")
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ComponentConfigEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ComponentConfigEntity.java
index e2e9304..c5a22fd 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ComponentConfigEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ComponentConfigEntity.java
@@ -17,6 +17,8 @@
 
 package org.keycloak.models.jpa.entities;
 
+import org.hibernate.annotations.Nationalized;
+
 import javax.persistence.Access;
 import javax.persistence.AccessType;
 import javax.persistence.Column;
@@ -46,6 +48,7 @@ public class ComponentConfigEntity {
 
     @Column(name = "NAME")
     protected String name;
+    @Nationalized
     @Column(name = "VALUE")
     protected String value;
 
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/GroupAttributeEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/GroupAttributeEntity.java
index b65febd..d762661 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/GroupAttributeEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/GroupAttributeEntity.java
@@ -17,6 +17,8 @@
 
 package org.keycloak.models.jpa.entities;
 
+import org.hibernate.annotations.Nationalized;
+
 import javax.persistence.Access;
 import javax.persistence.AccessType;
 import javax.persistence.Column;
@@ -51,6 +53,7 @@ public class GroupAttributeEntity {
 
     @Column(name = "NAME")
     protected String name;
+    @Nationalized
     @Column(name = "VALUE")
     protected String value;
 
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/GroupEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/GroupEntity.java
index e01ad0c..997dda7 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/GroupEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/GroupEntity.java
@@ -17,6 +17,8 @@
 
 package org.keycloak.models.jpa.entities;
 
+import org.hibernate.annotations.Nationalized;
+
 import javax.persistence.*;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -40,6 +42,7 @@ public class GroupEntity {
     @Access(AccessType.PROPERTY) // we do this because relationships often fetch id, but not entity.  This avoids an extra SQL
     protected String id;
 
+    @Nationalized
     @Column(name = "NAME")
     protected String name;
 
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmAttributeEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmAttributeEntity.java
index 0e02f23..6474bec 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmAttributeEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmAttributeEntity.java
@@ -17,6 +17,8 @@
 
 package org.keycloak.models.jpa.entities;
 
+import org.hibernate.annotations.Nationalized;
+
 import javax.persistence.Column;
 import javax.persistence.Entity;
 import javax.persistence.FetchType;
@@ -49,6 +51,7 @@ public class RealmAttributeEntity {
     @Id
     @Column(name = "NAME")
     protected String name;
+    @Nationalized
     @Column(name = "VALUE")
     protected String value;
 
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java
index 49264a5..a8d30aa 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java
@@ -17,6 +17,8 @@
 
 package org.keycloak.models.jpa.entities;
 
+import org.hibernate.annotations.Nationalized;
+
 import javax.persistence.Access;
 import javax.persistence.AccessType;
 import javax.persistence.Column;
@@ -61,8 +63,10 @@ public class RoleEntity {
     @Access(AccessType.PROPERTY) // we do this because relationships often fetch id, but not entity.  This avoids an extra SQL
     private String id;
 
+    @Nationalized
     @Column(name = "NAME")
     private String name;
+    @Nationalized
     @Column(name = "DESCRIPTION")
     private String description;
     @Column(name = "SCOPE_PARAM_REQUIRED")
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserAttributeEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserAttributeEntity.java
index bbb0f0f..fd211e4 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserAttributeEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserAttributeEntity.java
@@ -17,6 +17,8 @@
 
 package org.keycloak.models.jpa.entities;
 
+import org.hibernate.annotations.Nationalized;
+
 import javax.persistence.Access;
 import javax.persistence.AccessType;
 import javax.persistence.Column;
@@ -54,6 +56,7 @@ public class UserAttributeEntity {
 
     @Column(name = "NAME")
     protected String name;
+    @Nationalized
     @Column(name = "VALUE")
     protected String value;
 
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java
index 82062cc..538d17a 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java
@@ -19,6 +19,7 @@ package org.keycloak.models.jpa.entities;
 
 import org.hibernate.annotations.Fetch;
 import org.hibernate.annotations.FetchMode;
+import org.hibernate.annotations.Nationalized;
 import org.keycloak.models.utils.KeycloakModelUtils;
 
 import javax.persistence.Access;
@@ -69,12 +70,15 @@ public class UserEntity {
     @Access(AccessType.PROPERTY) // we do this because relationships often fetch id, but not entity.  This avoids an extra SQL
     protected String id;
 
+    @Nationalized
     @Column(name = "USERNAME")
     protected String username;
+    @Nationalized
     @Column(name = "FIRST_NAME")
     protected String firstName;
     @Column(name = "CREATED_TIMESTAMP")
     protected Long createdTimestamp;
+    @Nationalized
     @Column(name = "LAST_NAME")
     protected String lastName;
     @Column(name = "EMAIL")

pom.xml 11(+11 -0)

diff --git a/pom.xml b/pom.xml
index eab1938..ddc8d25 100755
--- a/pom.xml
+++ b/pom.xml
@@ -1414,6 +1414,17 @@
                 <version>${project.version}</version>
                 <type>zip</type>
             </dependency>
+            <dependency>
+                <groupId>org.keycloak</groupId>
+                <artifactId>kcinit</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.keycloak</groupId>
+                <artifactId>kcinit-dist</artifactId>
+                <version>${project.version}</version>
+                <type>zip</type>
+            </dependency>
         </dependencies>
     </dependencyManagement>
 
diff --git a/server-spi-private/src/main/java/org/keycloak/authentication/AuthenticationFlowError.java b/server-spi-private/src/main/java/org/keycloak/authentication/AuthenticationFlowError.java
index 2f17f77..1d60052 100755
--- a/server-spi-private/src/main/java/org/keycloak/authentication/AuthenticationFlowError.java
+++ b/server-spi-private/src/main/java/org/keycloak/authentication/AuthenticationFlowError.java
@@ -43,5 +43,6 @@ public enum AuthenticationFlowError {
 
     IDENTITY_PROVIDER_NOT_FOUND,
     IDENTITY_PROVIDER_DISABLED,
-    IDENTITY_PROVIDER_ERROR
+    IDENTITY_PROVIDER_ERROR,
+    DISPLAY_NOT_SUPPORTED
 }
diff --git a/server-spi-private/src/main/java/org/keycloak/authentication/AuthenticationFlowException.java b/server-spi-private/src/main/java/org/keycloak/authentication/AuthenticationFlowException.java
index bf8fbcf..e15386a 100755
--- a/server-spi-private/src/main/java/org/keycloak/authentication/AuthenticationFlowException.java
+++ b/server-spi-private/src/main/java/org/keycloak/authentication/AuthenticationFlowException.java
@@ -17,6 +17,8 @@
 
 package org.keycloak.authentication;
 
+import javax.ws.rs.core.Response;
+
 /**
  * Throw this exception from an Authenticator, FormAuthenticator, or FormAction if you want to completely abort the flow.
  *
@@ -25,11 +27,17 @@ package org.keycloak.authentication;
  */
 public class AuthenticationFlowException extends RuntimeException {
     private AuthenticationFlowError error;
+    private Response response;
 
     public AuthenticationFlowException(AuthenticationFlowError error) {
         this.error = error;
     }
 
+    public AuthenticationFlowException(AuthenticationFlowError error, Response response) {
+        this.error = error;
+        this.response = response;
+    }
+
     public AuthenticationFlowException(String message, AuthenticationFlowError error) {
         super(message);
         this.error = error;
@@ -53,4 +61,8 @@ public class AuthenticationFlowException extends RuntimeException {
     public AuthenticationFlowError getError() {
         return error;
     }
+
+    public Response getResponse() {
+        return response;
+    }
 }
diff --git a/server-spi-private/src/main/java/org/keycloak/authentication/DisplayTypeAuthenticatorFactory.java b/server-spi-private/src/main/java/org/keycloak/authentication/DisplayTypeAuthenticatorFactory.java
new file mode 100644
index 0000000..0754383
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/authentication/DisplayTypeAuthenticatorFactory.java
@@ -0,0 +1,21 @@
+package org.keycloak.authentication;
+
+import org.keycloak.models.KeycloakSession;
+
+/**
+ * Implement this interface when declaring your authenticator factory
+ * if your provider has support for multiple oidc display query parameter parameter types
+ * if the display query parameter is set and your factory implements this interface, this method
+ * will be called.
+ *
+ */
+public interface DisplayTypeAuthenticatorFactory {
+    /**
+     *
+     *
+     * @param session
+     * @param displayType i.e. "console", "wap", "popup" are examples
+     * @return null if display type isn't support.
+     */
+    Authenticator createDisplay(KeycloakSession session, String displayType);
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/authentication/DisplayTypeRequiredActionFactory.java b/server-spi-private/src/main/java/org/keycloak/authentication/DisplayTypeRequiredActionFactory.java
new file mode 100644
index 0000000..e22e9ff
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/authentication/DisplayTypeRequiredActionFactory.java
@@ -0,0 +1,13 @@
+package org.keycloak.authentication;
+
+import org.keycloak.models.KeycloakSession;
+
+/**
+ * Implement this interface when declaring your required action factory
+ * has support for multiple oidc display query parameter parameter types
+ * if the display query parameter is set and your factory implements this interface, this method
+ * will be called.
+ */
+public interface DisplayTypeRequiredActionFactory {
+    RequiredActionProvider createDisplay(KeycloakSession session, String displayType);
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/authentication/RequiredActionContext.java b/server-spi-private/src/main/java/org/keycloak/authentication/RequiredActionContext.java
index caaa14e..1289842 100755
--- a/server-spi-private/src/main/java/org/keycloak/authentication/RequiredActionContext.java
+++ b/server-spi-private/src/main/java/org/keycloak/authentication/RequiredActionContext.java
@@ -60,6 +60,15 @@ public interface RequiredActionContext {
     URI getActionUrl();
 
     /**
+     * Get the action URL for the required action.  This auto-generates the access code.
+     *
+     * @param authSessionIdParam if true, will embed session id as query param.  Useful for clients that don't support cookies (i.e. console)
+     *
+     * @return
+     */
+    URI getActionUrl(boolean authSessionIdParam);
+
+    /**
      * Create a Freemarker form builder that presets the user, action URI, and a generated access code
      *
      * @return
diff --git a/server-spi-private/src/main/java/org/keycloak/authentication/TextChallenge.java b/server-spi-private/src/main/java/org/keycloak/authentication/TextChallenge.java
new file mode 100644
index 0000000..b1dc9a2
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/authentication/TextChallenge.java
@@ -0,0 +1,303 @@
+package org.keycloak.authentication;
+
+import org.keycloak.forms.login.LoginFormsProvider;
+import org.keycloak.models.KeycloakSession;
+
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+/**
+ * This class encapsulates a proprietary HTTP challenge protocol designed by keycloak team which is used by text-based console
+ * clients to dynamically render and prompt for information in a textual manner.  The class is a builder which can
+ * build the challenge response (the header and response body).
+ *
+ * When doing code to token flow in OAuth, server could respond with
+ *
+ * 401
+ * WWW-Authenticate: X-Text-Form-Challenge callback="http://localhost/..."
+ *                                         param="username" label="Username: " mask=false
+ *                                         param="password" label="Password: " mask=true
+ * Content-Type: text/plain
+ *
+ * Please login with your username and password
+ *
+ *
+ * The client receives this challenge.  It first outputs whatever the text body of the message contains.  It will
+ * then prompt for username and password using the label values as prompt messages for each parameter.
+ *
+ * After the input has been entered by the user, the client does a form POST to the callback url with the values of the
+ * input parameters entered.
+ *
+ * The server can challenge with 401 as many times as it wants.  The client will look for 302 responses.  It will will
+ * follow all redirects unless the Location url has an OAuth "code" parameter.  If there is a code parameter, then the
+ * client will stop and finish the OAuth flow to obtain a token.  Any other response code other than 401 or 302 the client
+ * should abort with an error message.
+ *
+ */
+public class TextChallenge {
+
+    /**
+     * Browser is required to login.  This will abort client from doing a console login.
+     *
+     * @param session
+     * @return
+     */
+    public static Response browserRequired(KeycloakSession session) {
+        return Response.status(Response.Status.UNAUTHORIZED)
+                .header("WWW-Authenticate", "X-Text-Form-Challenge browserRequired")
+                .type(MediaType.TEXT_PLAIN)
+                .entity("\n" + session.getProvider(LoginFormsProvider.class).getMessage("browserRequired") + "\n").build();
+    }
+
+
+    /**
+     * Build challenge response for required actions
+     *
+     * @param context
+     * @return
+     */
+    public static TextChallenge challenge(RequiredActionContext context) {
+        return new TextChallenge(context);
+
+    }
+
+    /**
+     * Build challenge response for authentication flows
+     *
+     * @param context
+     * @return
+     */
+    public static TextChallenge challenge(AuthenticationFlowContext context) {
+        return new TextChallenge(context);
+
+    }
+    /**
+     * Build challenge response header only for required actions
+     *
+     * @param context
+     * @return
+     */
+    public static HeaderBuilder header(RequiredActionContext context) {
+        return new TextChallenge(context).header();
+
+    }
+
+    /**
+     * Build challenge response header only for authentication flows
+     *
+     * @param context
+     * @return
+     */
+    public static HeaderBuilder header(AuthenticationFlowContext context) {
+        return new TextChallenge(context).header();
+
+    }
+    TextChallenge(RequiredActionContext requiredActionContext) {
+        this.requiredActionContext = requiredActionContext;
+    }
+
+    TextChallenge(AuthenticationFlowContext flowContext) {
+        this.flowContext = flowContext;
+    }
+
+
+    protected RequiredActionContext requiredActionContext;
+    protected AuthenticationFlowContext flowContext;
+    protected HeaderBuilder header;
+
+    /**
+     * Create a theme form pre-populated with challenge
+     *
+     * @return
+     */
+    public LoginFormsProvider form() {
+        if (header == null) throw new RuntimeException("Header Not Set");
+        return formInternal()
+                .setStatus(Response.Status.UNAUTHORIZED)
+                .setMediaType(MediaType.TEXT_PLAIN_TYPE)
+                .setResponseHeader(HttpHeaders.WWW_AUTHENTICATE, header.build());
+    }
+
+    /**
+     * Create challenge response with a  body generated from localized
+     * message.properties of your theme
+     *
+     * @param msg message id
+     * @param params parameters to use to format the message
+     *
+     * @return
+     */
+    public Response message(String msg, String... params) {
+        if (header == null) throw new RuntimeException("Header Not Set");
+        Response response = Response.status(401)
+                .header(HttpHeaders.WWW_AUTHENTICATE, header.build())
+                .type(MediaType.TEXT_PLAIN)
+                .entity("\n" + formInternal().getMessage(msg, params) + "\n").build();
+        return response;
+    }
+
+    /**
+     * Create challenge response with a text message body
+     *
+     * @param text plain text of http response body
+     *
+     * @return
+     */
+    public Response text(String text) {
+        if (header == null) throw new RuntimeException("Header Not Set");
+        Response response = Response.status(401)
+                .header(HttpHeaders.WWW_AUTHENTICATE, header.build())
+                .type(MediaType.TEXT_PLAIN)
+                .entity("\n" + text + "\n").build();
+        return response;
+
+    }
+
+
+    /**
+     * Generate response with empty http response body
+     *
+     * @return
+     */
+    public Response response() {
+        if (header == null) throw new RuntimeException("Header Not Set");
+        Response response = Response.status(401)
+                .header(HttpHeaders.WWW_AUTHENTICATE, header.build()).build();
+        return response;
+
+    }
+
+
+
+    protected LoginFormsProvider formInternal() {
+        if (requiredActionContext != null) {
+            return requiredActionContext.form();
+        } else {
+            return flowContext.form();
+
+        }
+    }
+
+    /**
+     * Start building the header
+     *
+     * @return
+     */
+    public HeaderBuilder header() {
+        String callback;
+        if (requiredActionContext != null) {
+            callback = requiredActionContext.getActionUrl(true).toString();
+        } else {
+            callback = flowContext.getActionUrl(flowContext.generateAccessCode(), true).toString();
+
+        }
+        header = new HeaderBuilder(callback);
+        return header;
+    }
+
+    public class HeaderBuilder {
+        protected StringBuilder builder = new StringBuilder();
+
+        protected HeaderBuilder(String callback) {
+            builder.append("X-Text-Form-Challenge callback=\"").append(callback).append("\" ");
+        }
+
+        protected ParamBuilder param;
+
+        protected void checkParam() {
+            if (param != null) {
+                param.buildInternal();
+                param = null;
+            }
+        }
+
+        /**
+         * Build header string
+         *
+         * @return
+         */
+        public String build() {
+            checkParam();
+            return builder.toString();
+        }
+
+        /**
+         * Define a param
+         *
+         * @param name
+         * @return
+         */
+        public ParamBuilder param(String name) {
+            checkParam();
+            builder.append("param=\"").append(name).append("\" ");
+            param = new ParamBuilder(name);
+            return param;
+        }
+
+        public class ParamBuilder {
+            protected boolean mask;
+            protected String label;
+
+            protected ParamBuilder(String name) {
+                this.label = name;
+            }
+
+            public ParamBuilder label(String msg) {
+                this.label = formInternal().getMessage(msg);
+                return this;
+            }
+
+            public ParamBuilder labelText(String txt) {
+                this.label = txt;
+                return this;
+            }
+
+            /**
+             * Should input be masked by the client.  For example, when entering password, you don't want to show password on console.
+             *
+             * @param mask
+             * @return
+             */
+            public ParamBuilder mask(boolean mask) {
+                this.mask = mask;
+                return this;
+            }
+
+            public void buildInternal() {
+                builder.append("label=\"").append(label).append(" \" ");
+                builder.append("mask=").append(mask).append(" ");
+            }
+
+            /**
+             * Build header string
+             *
+             * @return
+             */
+            public String build() {
+                return HeaderBuilder.this.build();
+            }
+
+            public TextChallenge challenge() {
+                return TextChallenge.this;
+            }
+
+            public LoginFormsProvider form() {
+                return TextChallenge.this.form();
+            }
+
+            public Response message(String msg, String... params) {
+                return TextChallenge.this.message(msg, params);
+            }
+
+            public Response text(String text) {
+                return TextChallenge.this.text(text);
+
+            }
+
+            public ParamBuilder param(String name) {
+                return HeaderBuilder.this.param(name);
+            }
+        }
+    }
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/email/EmailTemplateProvider.java b/server-spi-private/src/main/java/org/keycloak/email/EmailTemplateProvider.java
index a60ebc0..425ec52 100755
--- a/server-spi-private/src/main/java/org/keycloak/email/EmailTemplateProvider.java
+++ b/server-spi-private/src/main/java/org/keycloak/email/EmailTemplateProvider.java
@@ -23,6 +23,7 @@ import org.keycloak.models.UserModel;
 import org.keycloak.provider.Provider;
 import org.keycloak.sessions.AuthenticationSessionModel;
 
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -76,4 +77,24 @@ public interface EmailTemplateProvider extends Provider {
 
     public void sendVerifyEmail(String link, long expirationInMinutes) throws EmailException;
 
+    /**
+     * Send formatted email
+     *
+     * @param subjectFormatKey message property that will be used to format email subject
+     * @param bodyTemplate freemarker template file
+     * @param bodyAttributes attributes used to fill template
+     * @throws EmailException
+     */
+    void send(String subjectFormatKey, String bodyTemplate, Map<String, Object> bodyAttributes) throws EmailException;
+
+    /**
+     * Send formatted email
+     *
+     * @param subjectFormatKey message property that will be used to format email subject
+     * @param subjectAttributes attributes used to fill subject format message
+     * @param bodyTemplate freemarker template file
+     * @param bodyAttributes attributes used to fill template
+     * @throws EmailException
+     */
+    void send(String subjectFormatKey, List<Object> subjectAttributes, String bodyTemplate, Map<String, Object> bodyAttributes) throws EmailException;
 }
diff --git a/server-spi-private/src/main/java/org/keycloak/events/Errors.java b/server-spi-private/src/main/java/org/keycloak/events/Errors.java
index 632c21c..95bcd81 100755
--- a/server-spi-private/src/main/java/org/keycloak/events/Errors.java
+++ b/server-spi-private/src/main/java/org/keycloak/events/Errors.java
@@ -90,5 +90,6 @@ public interface Errors {
     String NOT_LOGGED_IN = "not_logged_in";
     String UNKNOWN_IDENTITY_PROVIDER = "unknown_identity_provider";
     String ILLEGAL_ORIGIN = "illegal_origin";
+    String DISPLAY_UNSUPPORTED = "display_unsupported";
 
 }
diff --git a/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsProvider.java b/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsProvider.java
index 256b87f..31f430d 100755
--- a/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsProvider.java
+++ b/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsProvider.java
@@ -54,6 +54,8 @@ public interface LoginFormsProvider extends Provider {
 
     String getMessage(String message);
 
+    String getMessage(String message, String... parameters);
+
     Response createLogin();
 
     Response createPasswordReset();
diff --git a/server-spi-private/src/main/java/org/keycloak/models/AdminRoles.java b/server-spi-private/src/main/java/org/keycloak/models/AdminRoles.java
index c2304e8..6178dc8 100755
--- a/server-spi-private/src/main/java/org/keycloak/models/AdminRoles.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/AdminRoles.java
@@ -65,5 +65,6 @@ public class AdminRoles {
         ALL_ROLES.add(ADMIN);
         ALL_ROLES.add(CREATE_REALM);
         ALL_ROLES.add(CREATE_CLIENT);
+        ALL_ROLES.add(REALM_ADMIN);
     }
 }
diff --git a/server-spi-private/src/main/java/org/keycloak/models/Constants.java b/server-spi-private/src/main/java/org/keycloak/models/Constants.java
index 5cb1ec5..ae2f4c7 100755
--- a/server-spi-private/src/main/java/org/keycloak/models/Constants.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/Constants.java
@@ -52,6 +52,7 @@ public interface Constants {
     int DEFAULT_OFFLINE_SESSION_IDLE_TIMEOUT = 2592000;
 
     String VERIFY_EMAIL_KEY = "VERIFY_EMAIL_KEY";
+    String VERIFY_EMAIL_CODE = "VERIFY_EMAIL_CODE";
     String EXECUTION = "execution";
     String CLIENT_ID = "client_id";
     String TAB_ID = "tab_id";
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index 538dcef..06545a2 100755
--- a/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -2314,7 +2314,7 @@ public class RepresentationToModel {
         String ownerId = owner.getId();
 
         if (ownerId == null) {
-            throw new RuntimeException("No owner specified for resource [" + resource.getName() + "].");
+            ownerId = resourceServer.getId();
         }
 
         if (!resourceServer.getId().equals(ownerId)) {
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
index 537581a..db96f11 100755
--- a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
@@ -653,27 +653,33 @@ public class AuthenticationProcessor {
     public Response handleBrowserException(Exception failure) {
         if (failure instanceof AuthenticationFlowException) {
             AuthenticationFlowException e = (AuthenticationFlowException) failure;
+
             if (e.getError() == AuthenticationFlowError.INVALID_USER) {
                 ServicesLogger.LOGGER.failedAuthentication(e);
                 event.error(Errors.USER_NOT_FOUND);
+                if (e.getResponse() != null) return e.getResponse();
                 return ErrorPage.error(session, authenticationSession, Response.Status.BAD_REQUEST, Messages.INVALID_USER);
             } else if (e.getError() == AuthenticationFlowError.USER_DISABLED) {
                 ServicesLogger.LOGGER.failedAuthentication(e);
                 event.error(Errors.USER_DISABLED);
+                if (e.getResponse() != null) return e.getResponse();
                 return ErrorPage.error(session,authenticationSession, Response.Status.BAD_REQUEST, Messages.ACCOUNT_DISABLED);
             } else if (e.getError() == AuthenticationFlowError.USER_TEMPORARILY_DISABLED) {
                 ServicesLogger.LOGGER.failedAuthentication(e);
                 event.error(Errors.USER_TEMPORARILY_DISABLED);
+                if (e.getResponse() != null) return e.getResponse();
                 return ErrorPage.error(session,authenticationSession, Response.Status.BAD_REQUEST, Messages.INVALID_USER);
 
             } else if (e.getError() == AuthenticationFlowError.INVALID_CLIENT_SESSION) {
                 ServicesLogger.LOGGER.failedAuthentication(e);
                 event.error(Errors.INVALID_CODE);
+                if (e.getResponse() != null) return e.getResponse();
                 return ErrorPage.error(session, authenticationSession, Response.Status.BAD_REQUEST, Messages.INVALID_CODE);
 
             } else if (e.getError() == AuthenticationFlowError.EXPIRED_CODE) {
                 ServicesLogger.LOGGER.failedAuthentication(e);
                 event.error(Errors.EXPIRED_CODE);
+                if (e.getResponse() != null) return e.getResponse();
                 return ErrorPage.error(session, authenticationSession, Response.Status.BAD_REQUEST, Messages.EXPIRED_CODE);
 
             } else if (e.getError() == AuthenticationFlowError.FORK_FLOW) {
@@ -701,9 +707,15 @@ public class AuthenticationProcessor {
                 CacheControlUtil.noBackButtonCacheControlHeader();
                 return processor.authenticate();
 
+            } else if (e.getError() == AuthenticationFlowError.DISPLAY_NOT_SUPPORTED) {
+                ServicesLogger.LOGGER.failedAuthentication(e);
+                event.error(Errors.DISPLAY_UNSUPPORTED);
+                if (e.getResponse() != null) return e.getResponse();
+                return ErrorPage.error(session, authenticationSession, Response.Status.BAD_REQUEST, Messages.DISPLAY_UNSUPPORTED);
             } else {
                 ServicesLogger.LOGGER.failedAuthentication(e);
                 event.error(Errors.INVALID_USER_CREDENTIALS);
+                if (e.getResponse() != null) return e.getResponse();
                 return ErrorPage.error(session, authenticationSession, Response.Status.BAD_REQUEST, Messages.INVALID_USER);
             }
 
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/AttemptedAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/AttemptedAuthenticator.java
new file mode 100644
index 0000000..fc866fd
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/AttemptedAuthenticator.java
@@ -0,0 +1,46 @@
+package org.keycloak.authentication.authenticators;
+
+import org.keycloak.authentication.AuthenticationFlowContext;
+import org.keycloak.authentication.Authenticator;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+
+/**
+ * Pass-thru atheneticator that just sets the context to attempted.
+ */
+public class AttemptedAuthenticator implements Authenticator {
+
+    public static final AttemptedAuthenticator SINGLETON = new AttemptedAuthenticator();
+    @Override
+    public void authenticate(AuthenticationFlowContext context) {
+        context.attempted();
+
+    }
+
+    @Override
+    public void action(AuthenticationFlowContext context) {
+        throw new RuntimeException("Unreachable!");
+
+    }
+
+    @Override
+    public boolean requiresUser() {
+        return false;
+    }
+
+    @Override
+    public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
+        return true;
+    }
+
+    @Override
+    public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
+
+    }
+
+    @Override
+    public void close() {
+
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/CookieAuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/CookieAuthenticatorFactory.java
index 7e40298..b87dbe9 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/CookieAuthenticatorFactory.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/CookieAuthenticatorFactory.java
@@ -18,8 +18,11 @@
 package org.keycloak.authentication.authenticators.browser;
 
 import org.keycloak.Config;
+import org.keycloak.OAuth2Constants;
 import org.keycloak.authentication.Authenticator;
 import org.keycloak.authentication.AuthenticatorFactory;
+import org.keycloak.authentication.DisplayTypeAuthenticatorFactory;
+import org.keycloak.authentication.authenticators.AttemptedAuthenticator;
 import org.keycloak.models.AuthenticationExecutionModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
@@ -31,7 +34,7 @@ import java.util.List;
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
-public class CookieAuthenticatorFactory implements AuthenticatorFactory {
+public class CookieAuthenticatorFactory implements AuthenticatorFactory, DisplayTypeAuthenticatorFactory {
     public static final String PROVIDER_ID = "auth-cookie";
     static CookieAuthenticator SINGLETON = new CookieAuthenticator();
 
@@ -41,6 +44,13 @@ public class CookieAuthenticatorFactory implements AuthenticatorFactory {
     }
 
     @Override
+    public Authenticator createDisplay(KeycloakSession session, String displayType) {
+        if (displayType == null) return SINGLETON;
+        if (!OAuth2Constants.DISPLAY_CONSOLE.equalsIgnoreCase(displayType)) return null;
+        return AttemptedAuthenticator.SINGLETON;  // ignore this authenticator
+    }
+
+    @Override
     public void init(Config.Scope config) {
 
     }
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/IdentityProviderAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/IdentityProviderAuthenticator.java
index 5218347..170f9d7 100644
--- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/IdentityProviderAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/IdentityProviderAuthenticator.java
@@ -18,6 +18,7 @@
 package org.keycloak.authentication.authenticators.browser;
 
 import org.jboss.logging.Logger;
+import org.keycloak.OAuth2Constants;
 import org.keycloak.authentication.AuthenticationFlowContext;
 import org.keycloak.authentication.Authenticator;
 import org.keycloak.constants.AdapterConstants;
@@ -25,10 +26,13 @@ import org.keycloak.models.IdentityProviderModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
 import org.keycloak.services.Urls;
 import org.keycloak.services.managers.ClientSessionCode;
 
 import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import java.net.URI;
 import java.util.List;
 
 /**
@@ -66,8 +70,11 @@ public class IdentityProviderAuthenticator implements Authenticator {
                 String accessCode = new ClientSessionCode<>(context.getSession(), context.getRealm(), context.getAuthenticationSession()).getOrGenerateCode();
                 String clientId = context.getAuthenticationSession().getClient().getClientId();
                 String tabId = context.getAuthenticationSession().getTabId();
-                Response response = Response.seeOther(
-                        Urls.identityProviderAuthnRequest(context.getUriInfo().getBaseUri(), providerId, context.getRealm().getName(), accessCode, clientId, tabId))
+                URI location = Urls.identityProviderAuthnRequest(context.getUriInfo().getBaseUri(), providerId, context.getRealm().getName(), accessCode, clientId, tabId);
+                if (context.getAuthenticationSession().getClientNote(OAuth2Constants.DISPLAY) != null) {
+                    location = UriBuilder.fromUri(location).queryParam(OAuth2Constants.DISPLAY, context.getAuthenticationSession().getClientNote(OAuth2Constants.DISPLAY)).build();
+                }
+                Response response = Response.seeOther(location)
                         .build();
 
                 LOG.debugf("Redirecting to %s", providerId);
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/IdentityProviderAuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/IdentityProviderAuthenticatorFactory.java
index 635c95e..b136d33 100644
--- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/IdentityProviderAuthenticatorFactory.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/IdentityProviderAuthenticatorFactory.java
@@ -18,8 +18,11 @@
 package org.keycloak.authentication.authenticators.browser;
 
 import org.keycloak.Config;
+import org.keycloak.OAuth2Constants;
 import org.keycloak.authentication.Authenticator;
 import org.keycloak.authentication.AuthenticatorFactory;
+import org.keycloak.authentication.DisplayTypeAuthenticatorFactory;
+import org.keycloak.authentication.authenticators.AttemptedAuthenticator;
 import org.keycloak.models.AuthenticationExecutionModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
@@ -33,7 +36,7 @@ import static org.keycloak.provider.ProviderConfigProperty.STRING_TYPE;
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
-public class IdentityProviderAuthenticatorFactory implements AuthenticatorFactory {
+public class IdentityProviderAuthenticatorFactory implements AuthenticatorFactory, DisplayTypeAuthenticatorFactory {
 
     protected static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
             AuthenticationExecutionModel.Requirement.ALTERNATIVE, AuthenticationExecutionModel.Requirement.DISABLED
@@ -83,6 +86,13 @@ public class IdentityProviderAuthenticatorFactory implements AuthenticatorFactor
     }
 
     @Override
+    public Authenticator createDisplay(KeycloakSession session, String displayType) {
+        if (displayType == null) return new IdentityProviderAuthenticator();
+        if (!OAuth2Constants.DISPLAY_CONSOLE.equalsIgnoreCase(displayType)) return null;
+        return AttemptedAuthenticator.SINGLETON;  // ignore this authenticator
+    }
+
+    @Override
     public void init(Config.Scope config) {
     }
 
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/OTPFormAuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/OTPFormAuthenticatorFactory.java
index f443d28..d71659c 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/OTPFormAuthenticatorFactory.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/OTPFormAuthenticatorFactory.java
@@ -18,8 +18,11 @@
 package org.keycloak.authentication.authenticators.browser;
 
 import org.keycloak.Config;
+import org.keycloak.OAuth2Constants;
 import org.keycloak.authentication.Authenticator;
 import org.keycloak.authentication.AuthenticatorFactory;
+import org.keycloak.authentication.DisplayTypeAuthenticatorFactory;
+import org.keycloak.authentication.authenticators.console.ConsoleOTPFormAuthenticator;
 import org.keycloak.models.AuthenticationExecutionModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
@@ -32,7 +35,7 @@ import java.util.List;
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
-public class OTPFormAuthenticatorFactory implements AuthenticatorFactory {
+public class OTPFormAuthenticatorFactory implements AuthenticatorFactory, DisplayTypeAuthenticatorFactory {
 
     public static final String PROVIDER_ID = "auth-otp-form";
     public static final OTPFormAuthenticator SINGLETON = new OTPFormAuthenticator();
@@ -43,6 +46,13 @@ public class OTPFormAuthenticatorFactory implements AuthenticatorFactory {
     }
 
     @Override
+    public Authenticator createDisplay(KeycloakSession session, String displayType) {
+        if (displayType == null) return SINGLETON;
+        if (!OAuth2Constants.DISPLAY_CONSOLE.equalsIgnoreCase(displayType)) return null;
+        return ConsoleOTPFormAuthenticator.SINGLETON;
+    }
+
+    @Override
     public void init(Config.Scope config) {
 
     }
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/SpnegoAuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/SpnegoAuthenticatorFactory.java
index 9d837c6..ae5dd0c 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/SpnegoAuthenticatorFactory.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/SpnegoAuthenticatorFactory.java
@@ -18,8 +18,10 @@
 package org.keycloak.authentication.authenticators.browser;
 
 import org.keycloak.Config;
+import org.keycloak.OAuth2Constants;
 import org.keycloak.authentication.Authenticator;
 import org.keycloak.authentication.AuthenticatorFactory;
+import org.keycloak.authentication.DisplayTypeAuthenticatorFactory;
 import org.keycloak.models.AuthenticationExecutionModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
@@ -32,7 +34,7 @@ import java.util.List;
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
-public class SpnegoAuthenticatorFactory implements AuthenticatorFactory {
+public class SpnegoAuthenticatorFactory implements AuthenticatorFactory, DisplayTypeAuthenticatorFactory {
 
     public static final String PROVIDER_ID = "auth-spnego";
     public static final SpnegoAuthenticator SINGLETON = new SpnegoAuthenticator();
@@ -43,6 +45,13 @@ public class SpnegoAuthenticatorFactory implements AuthenticatorFactory {
     }
 
     @Override
+    public Authenticator createDisplay(KeycloakSession session, String displayType) {
+        if (displayType == null) return SINGLETON;
+        if (!OAuth2Constants.DISPLAY_CONSOLE.equalsIgnoreCase(displayType)) return null;
+        return SINGLETON;
+    }
+
+    @Override
     public void init(Config.Scope config) {
 
     }
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordForm.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordForm.java
index bd81263..43383a0 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordForm.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordForm.java
@@ -19,7 +19,6 @@ package org.keycloak.authentication.authenticators.browser;
 
 import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
 import org.keycloak.authentication.AuthenticationFlowContext;
-import org.keycloak.authentication.AuthenticationProcessor;
 import org.keycloak.authentication.Authenticator;
 import org.keycloak.forms.login.LoginFormsProvider;
 import org.keycloak.models.KeycloakSession;
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordFormFactory.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordFormFactory.java
index ef0c9b1..fe42f48 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordFormFactory.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordFormFactory.java
@@ -18,8 +18,11 @@
 package org.keycloak.authentication.authenticators.browser;
 
 import org.keycloak.Config;
+import org.keycloak.OAuth2Constants;
 import org.keycloak.authentication.Authenticator;
 import org.keycloak.authentication.AuthenticatorFactory;
+import org.keycloak.authentication.DisplayTypeAuthenticatorFactory;
+import org.keycloak.authentication.authenticators.console.ConsoleUsernamePasswordAuthenticator;
 import org.keycloak.models.AuthenticationExecutionModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
@@ -32,7 +35,7 @@ import java.util.List;
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
-public class UsernamePasswordFormFactory implements AuthenticatorFactory {
+public class UsernamePasswordFormFactory implements AuthenticatorFactory, DisplayTypeAuthenticatorFactory {
 
     public static final String PROVIDER_ID = "auth-username-password-form";
     public static final UsernamePasswordForm SINGLETON = new UsernamePasswordForm();
@@ -43,6 +46,13 @@ public class UsernamePasswordFormFactory implements AuthenticatorFactory {
     }
 
     @Override
+    public Authenticator createDisplay(KeycloakSession session, String displayType) {
+        if (displayType == null) return SINGLETON;
+        if (!OAuth2Constants.DISPLAY_CONSOLE.equalsIgnoreCase(displayType)) return null;
+        return ConsoleUsernamePasswordAuthenticator.SINGLETON;
+    }
+
+    @Override
     public void init(Config.Scope config) {
 
     }
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/console/ConsoleOTPFormAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/console/ConsoleOTPFormAuthenticator.java
new file mode 100755
index 0000000..fff2c80
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/console/ConsoleOTPFormAuthenticator.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.authentication.authenticators.console;
+
+import org.keycloak.authentication.AuthenticationFlowContext;
+import org.keycloak.authentication.Authenticator;
+import org.keycloak.authentication.TextChallenge;
+import org.keycloak.authentication.authenticators.browser.OTPFormAuthenticator;
+import org.keycloak.representations.idm.CredentialRepresentation;
+
+import javax.ws.rs.core.Response;
+import java.net.URI;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ConsoleOTPFormAuthenticator extends OTPFormAuthenticator implements Authenticator {
+    public static final ConsoleOTPFormAuthenticator SINGLETON = new ConsoleOTPFormAuthenticator();
+
+    public static URI getCallbackUrl(AuthenticationFlowContext context) {
+        return context.getActionUrl(context.generateAccessCode(), true);
+    }
+
+    protected TextChallenge challenge(AuthenticationFlowContext context) {
+        return TextChallenge.challenge(context)
+                .header()
+                .param(CredentialRepresentation.TOTP)
+                .label("console-otp")
+                 .challenge();
+    }
+
+    @Override
+    public void action(AuthenticationFlowContext context) {
+        validateOTP(context);
+    }
+
+
+
+    @Override
+    public void authenticate(AuthenticationFlowContext context) {
+        Response challengeResponse = challenge(context, null);
+        context.challenge(challengeResponse);
+    }
+
+    @Override
+    protected Response challenge(AuthenticationFlowContext context, String msg) {
+        if (msg == null) {
+            return challenge(context).response();
+        }
+        return challenge(context).message(msg);
+    }
+
+    @Override
+    public void close() {
+
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/console/ConsoleUsernamePasswordAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/console/ConsoleUsernamePasswordAuthenticator.java
new file mode 100755
index 0000000..4595df5
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/console/ConsoleUsernamePasswordAuthenticator.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.authentication.authenticators.console;
+
+import org.keycloak.authentication.*;
+import org.keycloak.authentication.authenticators.browser.AbstractUsernameFormAuthenticator;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.services.messages.Messages;
+
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import java.net.URI;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ConsoleUsernamePasswordAuthenticator extends AbstractUsernameFormAuthenticator implements Authenticator {
+
+    public static final ConsoleUsernamePasswordAuthenticator SINGLETON = new ConsoleUsernamePasswordAuthenticator();
+
+    @Override
+    public boolean requiresUser() {
+        return false;
+    }
+
+    protected TextChallenge challenge(AuthenticationFlowContext context) {
+        return TextChallenge.challenge(context)
+                .header()
+                .param("username")
+                .label("console-username")
+                .param("password")
+                .label("console-password")
+                .mask(true)
+                .challenge();
+    }
+
+
+    @Override
+    public void authenticate(AuthenticationFlowContext context) {
+        Response response = challenge(context).form().createForm("cli_splash.ftl");
+        context.challenge(response);
+
+
+    }
+
+    @Override
+    protected Response invalidUser(AuthenticationFlowContext context) {
+        Response response = challenge(context).message(Messages.INVALID_USER);
+        return response;
+    }
+
+    @Override
+    protected Response disabledUser(AuthenticationFlowContext context) {
+        Response response = challenge(context).message(Messages.ACCOUNT_DISABLED);
+        return response;
+    }
+
+    @Override
+    protected Response temporarilyDisabledUser(AuthenticationFlowContext context) {
+        Response response = challenge(context).message(Messages.INVALID_USER);
+        return response;
+    }
+
+    @Override
+    protected Response invalidCredentials(AuthenticationFlowContext context) {
+        Response response = challenge(context).message(Messages.INVALID_USER);
+        return response;
+    }
+
+    @Override
+    protected Response setDuplicateUserChallenge(AuthenticationFlowContext context, String eventError, String loginFormError, AuthenticationFlowError authenticatorError) {
+        context.getEvent().error(eventError);
+        Response response = challenge(context).message(loginFormError);
+
+        context.failureChallenge(authenticatorError, response);
+        return response;
+    }
+
+    @Override
+    public void action(AuthenticationFlowContext context) {
+        MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
+        if (!validateUserAndPassword(context, formData)) {
+            return;
+        }
+
+        context.success();
+    }
+
+    @Override
+    public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
+        return true;
+    }
+
+    @Override
+    public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
+    }
+
+    @Override
+    public void close() {
+
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/console/ConsoleUsernamePasswordAuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/authenticators/console/ConsoleUsernamePasswordAuthenticatorFactory.java
new file mode 100755
index 0000000..05aa235
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/console/ConsoleUsernamePasswordAuthenticatorFactory.java
@@ -0,0 +1,102 @@
+/*
+ * 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.authentication.authenticators.console;
+
+import org.keycloak.Config;
+import org.keycloak.authentication.Authenticator;
+import org.keycloak.authentication.AuthenticatorFactory;
+import org.keycloak.models.AuthenticationExecutionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.provider.ProviderConfigProperty;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ConsoleUsernamePasswordAuthenticatorFactory implements AuthenticatorFactory {
+
+    public static final String PROVIDER_ID = "console-username-password";
+
+    @Override
+    public Authenticator create(KeycloakSession session) {
+        return ConsoleUsernamePasswordAuthenticator.SINGLETON;
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public String getId() {
+        return PROVIDER_ID;
+    }
+
+    @Override
+    public String getReferenceCategory() {
+        return UserCredentialModel.PASSWORD;
+    }
+
+    @Override
+    public boolean isConfigurable() {
+        return false;
+    }
+    public static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
+            AuthenticationExecutionModel.Requirement.REQUIRED
+    };
+
+    @Override
+    public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
+        return REQUIREMENT_CHOICES;
+    }
+
+    @Override
+    public String getDisplayType() {
+        return "Username Password Challenge";
+    }
+
+    @Override
+    public String getHelpText() {
+        return "Proprietary challenge protocol for CLI clients that queries for username password";
+    }
+
+    @Override
+    public List<ProviderConfigProperty> getConfigProperties() {
+        return null;
+    }
+
+    @Override
+    public boolean isUserSetupAllowed() {
+        return false;
+    }
+
+}
diff --git a/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java b/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java
index 89471e7..ac0c5e1 100755
--- a/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java
+++ b/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java
@@ -18,6 +18,7 @@
 package org.keycloak.authentication;
 
 import org.jboss.logging.Logger;
+import org.keycloak.OAuth2Constants;
 import org.keycloak.models.AuthenticationExecutionModel;
 import org.keycloak.models.AuthenticationFlowModel;
 import org.keycloak.models.UserModel;
@@ -58,6 +59,24 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
                 || status == AuthenticationSessionModel.ExecutionStatus.SETUP_REQUIRED;
     }
 
+    protected Authenticator createAuthenticator(AuthenticatorFactory factory) {
+        String display = processor.getAuthenticationSession().getClientNote(OAuth2Constants.DISPLAY);
+        if (display == null) return factory.create(processor.getSession());
+
+
+        if (factory instanceof DisplayTypeAuthenticatorFactory) {
+            Authenticator authenticator = ((DisplayTypeAuthenticatorFactory)factory).createDisplay(processor.getSession(), display);
+            if (authenticator != null) return authenticator;
+        }
+        // todo create a provider for handling lack of display support
+        if (OAuth2Constants.DISPLAY_CONSOLE.equalsIgnoreCase(display)) {
+            throw new AuthenticationFlowException(AuthenticationFlowError.DISPLAY_NOT_SUPPORTED, TextChallenge.browserRequired(processor.getSession()));
+
+        } else {
+            return factory.create(processor.getSession());
+        }
+    }
+
 
     @Override
     public Response processAction(String actionExecution) {
@@ -86,7 +105,7 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
                 if (factory == null) {
                     throw new RuntimeException("Unable to find factory for AuthenticatorFactory: " + model.getAuthenticator() + " did you forget to declare it in a META-INF/services file?");
                 }
-                Authenticator authenticator = factory.create(processor.getSession());
+                Authenticator authenticator = createAuthenticator(factory);
                 AuthenticationProcessor.Result result = processor.createAuthenticatorContext(model, authenticator, executions);
                 logger.debugv("action: {0}", model.getAuthenticator());
                 authenticator.action(result);
@@ -161,7 +180,7 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
             if (factory == null) {
                 throw new RuntimeException("Unable to find factory for AuthenticatorFactory: " + model.getAuthenticator() + " did you forget to declare it in a META-INF/services file?");
             }
-            Authenticator authenticator = factory.create(processor.getSession());
+            Authenticator authenticator = createAuthenticator(factory);
             logger.debugv("authenticator: {0}", factory.getId());
             UserModel authUser = processor.getAuthenticationSession().getAuthenticatedUser();
 
diff --git a/services/src/main/java/org/keycloak/authentication/RequiredActionContextResult.java b/services/src/main/java/org/keycloak/authentication/RequiredActionContextResult.java
index 5e9a546..38b9c2f 100755
--- a/services/src/main/java/org/keycloak/authentication/RequiredActionContextResult.java
+++ b/services/src/main/java/org/keycloak/authentication/RequiredActionContextResult.java
@@ -33,6 +33,7 @@ import org.keycloak.services.resources.LoginActionsService;
 import org.keycloak.sessions.AuthenticationSessionModel;
 
 import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
 import java.net.URI;
 
@@ -163,6 +164,16 @@ public class RequiredActionContextResult implements RequiredActionContext {
     }
 
     @Override
+    public URI getActionUrl(boolean authSessionIdParam) {
+        URI uri = getActionUrl();
+        if (authSessionIdParam) {
+            uri = UriBuilder.fromUri(uri).queryParam(LoginActionsService.AUTH_SESSION_ID, getAuthenticationSession().getParentSession().getId()).build();
+        }
+        return uri;
+
+    }
+
+    @Override
     public LoginFormsProvider form() {
         String accessCode = generateCode();
         URI action = getActionUrl(accessCode);
diff --git a/services/src/main/java/org/keycloak/authentication/requiredactions/ConsoleTermsAndConditions.java b/services/src/main/java/org/keycloak/authentication/requiredactions/ConsoleTermsAndConditions.java
new file mode 100755
index 0000000..24c6938
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/requiredactions/ConsoleTermsAndConditions.java
@@ -0,0 +1,77 @@
+/*
+ * 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.authentication.requiredactions;
+
+import org.keycloak.Config;
+import org.keycloak.authentication.RequiredActionContext;
+import org.keycloak.authentication.RequiredActionFactory;
+import org.keycloak.authentication.RequiredActionProvider;
+import org.keycloak.authentication.TextChallenge;
+import org.keycloak.common.util.Time;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+
+import javax.ws.rs.core.Response;
+import java.util.Arrays;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ConsoleTermsAndConditions implements RequiredActionProvider {
+    public static final ConsoleTermsAndConditions SINGLETON = new ConsoleTermsAndConditions();
+    public static final String USER_ATTRIBUTE = TermsAndConditions.PROVIDER_ID;
+
+    @Override
+    public void evaluateTriggers(RequiredActionContext context) {
+
+    }
+
+
+    @Override
+    public void requiredActionChallenge(RequiredActionContext context) {
+        Response challenge = TextChallenge.challenge(context)
+                .header()
+                .param("accept")
+                .label("console-accept-terms")
+                .message("termsPlainText");
+        context.challenge(challenge);
+    }
+
+    @Override
+    public void processAction(RequiredActionContext context) {
+        String accept = context.getHttpRequest().getDecodedFormParameters().getFirst("accept");
+
+        String yes = context.form().getMessage("console-accept");
+
+        if (!accept.equals(yes)) {
+            context.getUser().removeAttribute(USER_ATTRIBUTE);
+            requiredActionChallenge(context);
+            return;
+        }
+
+        context.getUser().setAttribute(USER_ATTRIBUTE, Arrays.asList(Integer.toString(Time.currentTime())));
+
+        context.success();
+    }
+
+    @Override
+    public void close() {
+
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authentication/requiredactions/ConsoleUpdatePassword.java b/services/src/main/java/org/keycloak/authentication/requiredactions/ConsoleUpdatePassword.java
new file mode 100755
index 0000000..d499ead
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/requiredactions/ConsoleUpdatePassword.java
@@ -0,0 +1,103 @@
+/*
+ * 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.authentication.requiredactions;
+
+import org.jboss.logging.Logger;
+import org.keycloak.Config;
+import org.keycloak.authentication.*;
+import org.keycloak.events.Details;
+import org.keycloak.events.Errors;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.events.EventType;
+import org.keycloak.models.*;
+import org.keycloak.services.messages.Messages;
+import org.keycloak.services.validation.Validation;
+
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import java.net.URI;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ConsoleUpdatePassword extends UpdatePassword implements RequiredActionProvider {
+    public static final ConsoleUpdatePassword SINGLETON = new ConsoleUpdatePassword();
+
+    private static final Logger logger = Logger.getLogger(ConsoleUpdatePassword.class);
+    public static final String PASSWORD_NEW = "password-new";
+    public static final String PASSWORD_CONFIRM = "password-confirm";
+
+     protected TextChallenge challenge(RequiredActionContext context) {
+        return TextChallenge.challenge(context)
+                .header()
+                .param(PASSWORD_NEW)
+                .label("console-new-password")
+                .mask(true)
+                .param(PASSWORD_CONFIRM)
+                .label("console-confirm-password")
+                .mask(true)
+                .challenge();
+    }
+
+
+
+    @Override
+    public void requiredActionChallenge(RequiredActionContext context) {
+        context.challenge(
+                challenge(context).message("console-update-password"));
+    }
+
+    @Override
+    public void processAction(RequiredActionContext context) {
+        EventBuilder event = context.getEvent();
+        MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
+        event.event(EventType.UPDATE_PASSWORD);
+        String passwordNew = formData.getFirst(PASSWORD_NEW);
+        String passwordConfirm = formData.getFirst(PASSWORD_CONFIRM);
+
+        EventBuilder errorEvent = event.clone().event(EventType.UPDATE_PASSWORD_ERROR)
+                .client(context.getAuthenticationSession().getClient())
+                .user(context.getAuthenticationSession().getAuthenticatedUser());
+
+        if (Validation.isBlank(passwordNew)) {
+            context.challenge(challenge(context).message(Messages.MISSING_PASSWORD));
+            errorEvent.error(Errors.PASSWORD_MISSING);
+            return;
+        } else if (!passwordNew.equals(passwordConfirm)) {
+            context.challenge(challenge(context).message(Messages.NOTMATCH_PASSWORD));
+            errorEvent.error(Errors.PASSWORD_CONFIRM_ERROR);
+            return;
+        }
+
+        try {
+            context.getSession().userCredentialManager().updateCredential(context.getRealm(), context.getUser(), UserCredentialModel.password(passwordNew, false));
+            context.success();
+        } catch (ModelException me) {
+            errorEvent.detail(Details.REASON, me.getMessage()).error(Errors.PASSWORD_REJECTED);
+            context.challenge(challenge(context).text(me.getMessage()));
+            return;
+        } catch (Exception ape) {
+            errorEvent.detail(Details.REASON, ape.getMessage()).error(Errors.PASSWORD_REJECTED);
+            context.challenge(challenge(context).text(ape.getMessage()));
+            return;
+        }
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authentication/requiredactions/ConsoleUpdateProfile.java b/services/src/main/java/org/keycloak/authentication/requiredactions/ConsoleUpdateProfile.java
new file mode 100644
index 0000000..0b66bef
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/requiredactions/ConsoleUpdateProfile.java
@@ -0,0 +1,68 @@
+/*
+ * 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.authentication.requiredactions;
+
+import org.keycloak.Config;
+import org.keycloak.authentication.RequiredActionContext;
+import org.keycloak.authentication.RequiredActionFactory;
+import org.keycloak.authentication.RequiredActionProvider;
+import org.keycloak.events.Details;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.events.EventType;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.FormMessage;
+import org.keycloak.services.messages.Messages;
+import org.keycloak.services.resources.AttributeFormDataProcessor;
+import org.keycloak.services.validation.Validation;
+
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ConsoleUpdateProfile implements RequiredActionProvider {
+    public static final ConsoleUpdateProfile SINGLETON = new ConsoleUpdateProfile();
+
+    @Override
+    public void evaluateTriggers(RequiredActionContext context) {
+    }
+
+    @Override
+    public void requiredActionChallenge(RequiredActionContext context) {
+        // do nothing right now.  I think this behavior is ok.  We just defer this action until a browser login happens.
+        context.ignore();
+    }
+
+    @Override
+    public void processAction(RequiredActionContext context) {
+        throw new RuntimeException("Should be unreachable");
+
+    }
+
+
+    @Override
+    public void close() {
+
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authentication/requiredactions/ConsoleUpdateTotp.java b/services/src/main/java/org/keycloak/authentication/requiredactions/ConsoleUpdateTotp.java
new file mode 100644
index 0000000..89ef89b
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/requiredactions/ConsoleUpdateTotp.java
@@ -0,0 +1,113 @@
+/*
+ * 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.authentication.requiredactions;
+
+import org.keycloak.Config;
+import org.keycloak.authentication.RequiredActionContext;
+import org.keycloak.authentication.RequiredActionFactory;
+import org.keycloak.authentication.RequiredActionProvider;
+import org.keycloak.authentication.TextChallenge;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.events.EventType;
+import org.keycloak.forms.login.LoginFormsProvider;
+import org.keycloak.forms.login.freemarker.model.TotpBean;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.CredentialValidation;
+import org.keycloak.services.messages.Messages;
+import org.keycloak.services.validation.Validation;
+
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import java.net.URI;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ConsoleUpdateTotp implements RequiredActionProvider {
+    public static final ConsoleUpdateTotp SINGLETON = new ConsoleUpdateTotp();
+
+    @Override
+    public void evaluateTriggers(RequiredActionContext context) {
+    }
+    @Override
+    public void requiredActionChallenge(RequiredActionContext context) {
+        TotpBean totpBean = new TotpBean(context.getSession(), context.getRealm(), context.getUser(), context.getUriInfo().getRequestUriBuilder());
+        String totpSecret = totpBean.getTotpSecret();
+        context.getAuthenticationSession().setAuthNote("totpSecret", totpSecret);
+        Response challenge = challenge(context).form()
+                .setAttribute("totp", totpBean)
+                .createForm("login-config-totp-text.ftl");
+        context.challenge(challenge);
+    }
+
+    protected TextChallenge challenge(RequiredActionContext context) {
+        return TextChallenge.challenge(context)
+                .header()
+                .param("totp")
+                .label("console-otp")
+                .challenge();
+    }
+
+    @Override
+    public void processAction(RequiredActionContext context) {
+        EventBuilder event = context.getEvent();
+        event.event(EventType.UPDATE_TOTP);
+        MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
+        String totp = formData.getFirst("totp");
+        String totpSecret = context.getAuthenticationSession().getAuthNote("totpSecret");
+
+        if (Validation.isBlank(totp)) {
+            context.challenge(
+                    challenge(context).message(Messages.MISSING_TOTP)
+            );
+            return;
+        } else if (!CredentialValidation.validOTP(context.getRealm(), totp, totpSecret)) {
+            context.challenge(
+                    challenge(context).message(Messages.INVALID_TOTP)
+            );
+            return;
+        }
+
+        UserCredentialModel credentials = new UserCredentialModel();
+        credentials.setType(context.getRealm().getOTPPolicy().getType());
+        credentials.setValue(totpSecret);
+        context.getSession().userCredentialManager().updateCredential(context.getRealm(), context.getUser(), credentials);
+
+
+        // if type is HOTP, to update counter we execute validation based on supplied token
+        UserCredentialModel cred = new UserCredentialModel();
+        cred.setType(context.getRealm().getOTPPolicy().getType());
+        cred.setValue(totp);
+        context.getSession().userCredentialManager().isValid(context.getRealm(), context.getUser(), cred);
+
+        context.getAuthenticationSession().removeAuthNote("totpSecret");
+        context.success();
+    }
+
+
+    @Override
+    public void close() {
+
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authentication/requiredactions/ConsoleVerifyEmail.java b/services/src/main/java/org/keycloak/authentication/requiredactions/ConsoleVerifyEmail.java
new file mode 100755
index 0000000..e136ceb
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/requiredactions/ConsoleVerifyEmail.java
@@ -0,0 +1,152 @@
+/*
+ * 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.authentication.requiredactions;
+
+import org.jboss.logging.Logger;
+import org.keycloak.Config;
+import org.keycloak.authentication.RequiredActionContext;
+import org.keycloak.authentication.RequiredActionFactory;
+import org.keycloak.authentication.RequiredActionProvider;
+import org.keycloak.authentication.TextChallenge;
+import org.keycloak.authentication.actiontoken.verifyemail.VerifyEmailActionToken;
+import org.keycloak.common.util.RandomString;
+import org.keycloak.common.util.Time;
+import org.keycloak.email.EmailException;
+import org.keycloak.email.EmailTemplateProvider;
+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;
+import org.keycloak.models.*;
+import org.keycloak.services.Urls;
+import org.keycloak.services.messages.Messages;
+import org.keycloak.services.validation.Validation;
+import org.keycloak.sessions.AuthenticationSessionCompoundId;
+import org.keycloak.sessions.AuthenticationSessionModel;
+
+import javax.ws.rs.core.*;
+import java.net.URI;
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ConsoleVerifyEmail implements RequiredActionProvider {
+    public static final ConsoleVerifyEmail SINGLETON = new ConsoleVerifyEmail();
+    private static final Logger logger = Logger.getLogger(ConsoleVerifyEmail.class);
+    @Override
+    public void evaluateTriggers(RequiredActionContext context) {
+        if (context.getRealm().isVerifyEmail() && !context.getUser().isEmailVerified()) {
+            context.getUser().addRequiredAction(UserModel.RequiredAction.VERIFY_EMAIL);
+            logger.debug("User is required to verify email");
+        }
+    }
+
+    @Override
+    public void requiredActionChallenge(RequiredActionContext context) {
+        AuthenticationSessionModel authSession = context.getAuthenticationSession();
+
+        if (context.getUser().isEmailVerified()) {
+            context.success();
+            authSession.removeAuthNote(Constants.VERIFY_EMAIL_KEY);
+            return;
+        }
+
+        String email = context.getUser().getEmail();
+        if (Validation.isBlank(email)) {
+            context.ignore();
+            return;
+        }
+
+        Response challenge = sendVerifyEmail(context);
+        context.challenge(challenge);
+    }
+
+
+    @Override
+    public void processAction(RequiredActionContext context) {
+        EventBuilder event = context.getEvent().clone().event(EventType.VERIFY_EMAIL).detail(Details.EMAIL, context.getUser().getEmail());
+        String code = context.getAuthenticationSession().getAuthNote(Constants.VERIFY_EMAIL_CODE);
+        if (code == null) {
+            requiredActionChallenge(context);
+            return;
+        }
+
+        MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
+        String emailCode = formData.getFirst(EMAIL_CODE);
+
+        if (!code.equals(emailCode)) {
+            context.challenge(
+                    challenge(context).message(Messages.INVALID_CODE)
+            );
+            event.error(Errors.INVALID_CODE);
+            return;
+        }
+        event.success();
+        context.success();
+    }
+
+
+    @Override
+    public void close() {
+
+    }
+
+    public static String EMAIL_CODE="email_code";
+    protected TextChallenge challenge(RequiredActionContext context) {
+        return TextChallenge.challenge(context)
+                .header()
+                .param(EMAIL_CODE)
+                .label("console-email-code")
+                .challenge();
+    }
+
+     private Response sendVerifyEmail(RequiredActionContext context) throws UriBuilderException, IllegalArgumentException {
+        KeycloakSession session = context.getSession();
+        UserModel user = context.getUser();
+        AuthenticationSessionModel authSession = context.getAuthenticationSession();
+        EventBuilder event = context.getEvent().clone().event(EventType.SEND_VERIFY_EMAIL).detail(Details.EMAIL, user.getEmail());
+        String code = RandomString.randomCode(8);
+        authSession.setAuthNote(Constants.VERIFY_EMAIL_CODE, code);
+        RealmModel realm = session.getContext().getRealm();
+
+        Map<String, Object> attributes = new HashMap<>();
+        attributes.put("code", code);
+
+        try {
+            session
+              .getProvider(EmailTemplateProvider.class)
+              .setAuthenticationSession(authSession)
+              .setRealm(realm)
+              .setUser(user)
+              .send("emailVerificationSubject", "email-verification-with-code.ftl", attributes);
+            event.success();
+        } catch (EmailException e) {
+            logger.error("Failed to send verification email", e);
+            event.error(Errors.EMAIL_SEND_FAILED);
+        }
+
+        return challenge(context).text(context.form().getMessage("console-verify-email", user.getEmail()));
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authentication/requiredactions/TermsAndConditions.java b/services/src/main/java/org/keycloak/authentication/requiredactions/TermsAndConditions.java
index f4a1566..2f68782 100755
--- a/services/src/main/java/org/keycloak/authentication/requiredactions/TermsAndConditions.java
+++ b/services/src/main/java/org/keycloak/authentication/requiredactions/TermsAndConditions.java
@@ -18,9 +18,8 @@
 package org.keycloak.authentication.requiredactions;
 
 import org.keycloak.Config;
-import org.keycloak.authentication.RequiredActionContext;
-import org.keycloak.authentication.RequiredActionFactory;
-import org.keycloak.authentication.RequiredActionProvider;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.authentication.*;
 import org.keycloak.common.util.Time;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
@@ -32,7 +31,7 @@ import java.util.Arrays;
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
-public class TermsAndConditions implements RequiredActionProvider, RequiredActionFactory {
+public class TermsAndConditions implements RequiredActionProvider, RequiredActionFactory, DisplayTypeRequiredActionFactory {
     public static final String PROVIDER_ID = "terms_and_conditions";
     public static final String USER_ATTRIBUTE = PROVIDER_ID;
 
@@ -42,6 +41,15 @@ public class TermsAndConditions implements RequiredActionProvider, RequiredActio
     }
 
     @Override
+    public RequiredActionProvider createDisplay(KeycloakSession session, String displayType) {
+        if (displayType == null) return this;
+        if (!OAuth2Constants.DISPLAY_CONSOLE.equalsIgnoreCase(displayType)) return null;
+        return ConsoleTermsAndConditions.SINGLETON;
+    }
+
+
+
+    @Override
     public void init(Config.Scope config) {
 
     }
diff --git a/services/src/main/java/org/keycloak/authentication/requiredactions/UpdatePassword.java b/services/src/main/java/org/keycloak/authentication/requiredactions/UpdatePassword.java
index 1e9f37a..45fb05f 100755
--- a/services/src/main/java/org/keycloak/authentication/requiredactions/UpdatePassword.java
+++ b/services/src/main/java/org/keycloak/authentication/requiredactions/UpdatePassword.java
@@ -19,9 +19,8 @@ package org.keycloak.authentication.requiredactions;
 
 import org.jboss.logging.Logger;
 import org.keycloak.Config;
-import org.keycloak.authentication.RequiredActionContext;
-import org.keycloak.authentication.RequiredActionFactory;
-import org.keycloak.authentication.RequiredActionProvider;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.authentication.*;
 import org.keycloak.common.util.Time;
 import org.keycloak.credential.CredentialModel;
 import org.keycloak.credential.CredentialProvider;
@@ -47,7 +46,7 @@ import java.util.concurrent.TimeUnit;
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
-public class UpdatePassword implements RequiredActionProvider, RequiredActionFactory {
+public class UpdatePassword implements RequiredActionProvider, RequiredActionFactory, DisplayTypeRequiredActionFactory {
     private static final Logger logger = Logger.getLogger(UpdatePassword.class);
     @Override
     public void evaluateTriggers(RequiredActionContext context) {
@@ -142,6 +141,15 @@ public class UpdatePassword implements RequiredActionProvider, RequiredActionFac
         return this;
     }
 
+
+    @Override
+    public RequiredActionProvider createDisplay(KeycloakSession session, String displayType) {
+        if (displayType == null) return this;
+        if (!OAuth2Constants.DISPLAY_CONSOLE.equalsIgnoreCase(displayType)) return null;
+        return ConsoleUpdatePassword.SINGLETON;
+    }
+
+
     @Override
     public void init(Config.Scope config) {
 
diff --git a/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateProfile.java b/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateProfile.java
index 3ed1f12..ccaf729 100644
--- a/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateProfile.java
+++ b/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateProfile.java
@@ -18,9 +18,8 @@
 package org.keycloak.authentication.requiredactions;
 
 import org.keycloak.Config;
-import org.keycloak.authentication.RequiredActionContext;
-import org.keycloak.authentication.RequiredActionFactory;
-import org.keycloak.authentication.RequiredActionProvider;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.authentication.*;
 import org.keycloak.events.Details;
 import org.keycloak.events.EventBuilder;
 import org.keycloak.events.EventType;
@@ -41,7 +40,7 @@ import java.util.List;
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
-public class UpdateProfile implements RequiredActionProvider, RequiredActionFactory {
+public class UpdateProfile implements RequiredActionProvider, RequiredActionFactory, DisplayTypeRequiredActionFactory {
     @Override
     public void evaluateTriggers(RequiredActionContext context) {
     }
@@ -142,6 +141,16 @@ public class UpdateProfile implements RequiredActionProvider, RequiredActionFact
         return this;
     }
 
+
+    @Override
+    public RequiredActionProvider createDisplay(KeycloakSession session, String displayType) {
+        if (displayType == null) return this;
+        if (!OAuth2Constants.DISPLAY_CONSOLE.equalsIgnoreCase(displayType)) return null;
+        return ConsoleUpdateProfile.SINGLETON;
+    }
+
+
+
     @Override
     public void init(Config.Scope config) {
 
diff --git a/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateTotp.java b/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateTotp.java
index e85ec7e..188ba2a 100644
--- a/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateTotp.java
+++ b/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateTotp.java
@@ -18,9 +18,8 @@
 package org.keycloak.authentication.requiredactions;
 
 import org.keycloak.Config;
-import org.keycloak.authentication.RequiredActionContext;
-import org.keycloak.authentication.RequiredActionFactory;
-import org.keycloak.authentication.RequiredActionProvider;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.authentication.*;
 import org.keycloak.events.EventBuilder;
 import org.keycloak.events.EventType;
 import org.keycloak.models.KeycloakSession;
@@ -38,7 +37,7 @@ import javax.ws.rs.core.Response;
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
-public class UpdateTotp implements RequiredActionProvider, RequiredActionFactory {
+public class UpdateTotp implements RequiredActionProvider, RequiredActionFactory, DisplayTypeRequiredActionFactory {
     @Override
     public void evaluateTriggers(RequiredActionContext context) {
     }
@@ -105,6 +104,15 @@ public class UpdateTotp implements RequiredActionProvider, RequiredActionFactory
         return this;
     }
 
+
+    @Override
+    public RequiredActionProvider createDisplay(KeycloakSession session, String displayType) {
+        if (displayType == null) return this;
+        if (!OAuth2Constants.DISPLAY_CONSOLE.equalsIgnoreCase(displayType)) return null;
+        return ConsoleUpdateTotp.SINGLETON;
+    }
+
+
     @Override
     public void init(Config.Scope config) {
 
diff --git a/services/src/main/java/org/keycloak/authentication/requiredactions/VerifyEmail.java b/services/src/main/java/org/keycloak/authentication/requiredactions/VerifyEmail.java
index 969f350..c29c616 100755
--- a/services/src/main/java/org/keycloak/authentication/requiredactions/VerifyEmail.java
+++ b/services/src/main/java/org/keycloak/authentication/requiredactions/VerifyEmail.java
@@ -19,9 +19,8 @@ package org.keycloak.authentication.requiredactions;
 
 import org.jboss.logging.Logger;
 import org.keycloak.Config;
-import org.keycloak.authentication.RequiredActionContext;
-import org.keycloak.authentication.RequiredActionFactory;
-import org.keycloak.authentication.RequiredActionProvider;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.authentication.*;
 import org.keycloak.authentication.actiontoken.verifyemail.VerifyEmailActionToken;
 import org.keycloak.common.util.Time;
 import org.keycloak.email.EmailException;
@@ -45,7 +44,7 @@ import javax.ws.rs.core.*;
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
-public class VerifyEmail implements RequiredActionProvider, RequiredActionFactory {
+public class VerifyEmail implements RequiredActionProvider, RequiredActionFactory, DisplayTypeRequiredActionFactory {
     private static final Logger logger = Logger.getLogger(VerifyEmail.class);
     @Override
     public void evaluateTriggers(RequiredActionContext context) {
@@ -107,6 +106,14 @@ public class VerifyEmail implements RequiredActionProvider, RequiredActionFactor
         return this;
     }
 
+
+    @Override
+    public RequiredActionProvider createDisplay(KeycloakSession session, String displayType) {
+        if (displayType == null) return this;
+        if (!OAuth2Constants.DISPLAY_CONSOLE.equalsIgnoreCase(displayType)) return null;
+        return ConsoleVerifyEmail.SINGLETON;
+    }
+
     @Override
     public void init(Config.Scope config) {
 
diff --git a/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java b/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java
index b69d991..edde9cc 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java
@@ -21,6 +21,8 @@ import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation;
 import static org.keycloak.models.utils.RepresentationToModel.toModel;
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -46,6 +48,7 @@ import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriInfo;
 
 import org.jboss.resteasy.annotations.cache.NoCache;
+import org.keycloak.OAuthErrorException;
 import org.keycloak.authorization.AuthorizationProvider;
 import org.keycloak.authorization.model.Policy;
 import org.keycloak.authorization.model.Resource;
@@ -54,6 +57,7 @@ import org.keycloak.authorization.model.Scope;
 import org.keycloak.authorization.store.PolicyStore;
 import org.keycloak.authorization.store.ResourceStore;
 import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.common.util.PathMatcher;
 import org.keycloak.events.admin.OperationType;
 import org.keycloak.events.admin.ResourceType;
 import org.keycloak.models.ClientModel;
@@ -64,7 +68,7 @@ import org.keycloak.representations.idm.authorization.PolicyRepresentation;
 import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation;
 import org.keycloak.representations.idm.authorization.ResourceRepresentation;
 import org.keycloak.representations.idm.authorization.ScopeRepresentation;
-import org.keycloak.services.ErrorResponse;
+import org.keycloak.services.ErrorResponseException;
 import org.keycloak.services.resources.admin.AdminEventBuilder;
 import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
 
@@ -90,32 +94,18 @@ public class ResourceSetService {
     @Consumes("application/json")
     @Produces("application/json")
     public Response create(@Context UriInfo uriInfo, ResourceRepresentation resource) {
-        return create(uriInfo, resource, (Function<Resource, ResourceRepresentation>) resource1 -> {
-            ResourceRepresentation representation = new ResourceRepresentation();
-
-            representation.setId(resource1.getId());
+        if (resource == null) {
+            return Response.status(Status.BAD_REQUEST).build();
+        }
 
-            return representation;
-        });
-    }
+        ResourceRepresentation newResource = create(resource);
 
-    public Response create(@Context UriInfo uriInfo, ResourceRepresentation resource, Function<Resource, ?> toRepresentation) {
-        Response response = create(resource, toRepresentation);
         audit(uriInfo, resource, resource.getId(), OperationType.CREATE);
-        return response;
-    }
-
-    public Response create(ResourceRepresentation resource) {
-        return create(resource, (Function<Resource, ResourceRepresentation>) resource1 -> {
-            ResourceRepresentation representation = new ResourceRepresentation();
-
-            representation.setId(resource1.getId());
 
-            return representation;
-        });
+        return Response.status(Status.CREATED).entity(newResource).build();
     }
 
-    public Response create(ResourceRepresentation resource, Function<Resource, ?> toRepresentation) {
+    public ResourceRepresentation create(ResourceRepresentation resource) {
         requireManage();
         StoreFactory storeFactory = this.authorization.getStoreFactory();
         ResourceOwnerRepresentation owner = resource.getOwner();
@@ -123,21 +113,22 @@ public class ResourceSetService {
         if (owner == null) {
             owner = new ResourceOwnerRepresentation();
             owner.setId(resourceServer.getId());
+            resource.setOwner(owner);
         }
 
         String ownerId = owner.getId();
 
         if (ownerId == null) {
-            return ErrorResponse.error("You must specify the resource owner.", Status.BAD_REQUEST);
+            throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "You must specify the resource owner.", Status.BAD_REQUEST);
         }
 
         Resource existingResource = storeFactory.getResourceStore().findByName(resource.getName(), ownerId, this.resourceServer.getId());
 
         if (existingResource != null) {
-            return ErrorResponse.exists("Resource with name [" + resource.getName() + "] already exists.");
+            throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Resource with name [" + resource.getName() + "] already exists.", Status.CONFLICT);
         }
 
-        return Response.status(Status.CREATED).entity(toRepresentation.apply(toModel(resource, this.resourceServer, authorization))).build();
+        return toRepresentation(toModel(resource, this.resourceServer, authorization), resourceServer, authorization);
     }
 
     @Path("{id}")
@@ -198,10 +189,10 @@ public class ResourceSetService {
     @NoCache
     @Produces("application/json")
     public Response findById(@PathParam("id") String id) {
-        return findById(id, (Function<Resource, ResourceRepresentation>) resource -> toRepresentation(resource, resourceServer, authorization, true));
+        return findById(id, resource -> toRepresentation(resource, resourceServer, authorization, true));
     }
 
-    public Response findById(@PathParam("id") String id, Function<Resource, ?> toRepresentation) {
+    public Response findById(String id, Function<Resource, ? extends ResourceRepresentation> toRepresentation) {
         requireView();
         StoreFactory storeFactory = authorization.getStoreFactory();
         Resource model = storeFactory.getResourceStore().findById(id, resourceServer.getId());
@@ -340,10 +331,11 @@ public class ResourceSetService {
                          @QueryParam("owner") String owner,
                          @QueryParam("type") String type,
                          @QueryParam("scope") String scope,
+                         @QueryParam("matchingUri") Boolean matchingUri,
                          @QueryParam("deep") Boolean deep,
                          @QueryParam("first") Integer firstResult,
                          @QueryParam("max") Integer maxResult) {
-        return find(id, name, uri, owner, type, scope, deep, firstResult, maxResult, (BiFunction<Resource, Boolean, ResourceRepresentation>) (resource, deep1) -> toRepresentation(resource, resourceServer, authorization, deep1));
+        return find(id, name, uri, owner, type, scope, matchingUri, deep, firstResult, maxResult, (BiFunction<Resource, Boolean, ResourceRepresentation>) (resource, deep1) -> toRepresentation(resource, resourceServer, authorization, deep1));
     }
 
     public Response find(@QueryParam("_id") String id,
@@ -352,6 +344,7 @@ public class ResourceSetService {
                          @QueryParam("owner") String owner,
                          @QueryParam("type") String type,
                          @QueryParam("scope") String scope,
+                         @QueryParam("matchingUri") Boolean matchingUri,
                          @QueryParam("deep") Boolean deep,
                          @QueryParam("first") Integer firstResult,
                          @QueryParam("max") Integer maxResult,
@@ -413,9 +406,38 @@ public class ResourceSetService {
             search.put("scope", scopes.stream().map(Scope::getId).toArray(String[]::new));
         }
 
+        List<Resource> resources = storeFactory.getResourceStore().findByResourceServer(search, this.resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : Constants.DEFAULT_MAX_RESULTS);
+
+        if (matchingUri != null && matchingUri && resources.isEmpty()) {
+            HashMap<String, String[]> attributes = new HashMap<>();
+
+            attributes.put("uri_not_null", new String[] {"true"});
+            attributes.put("owner", new String[] {resourceServer.getId()});
+
+            List<Resource> serverResources = storeFactory.getResourceStore().findByResourceServer(attributes, this.resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : Constants.DEFAULT_MAX_RESULTS);
+            PathMatcher<Resource> pathMatcher = new PathMatcher<Resource>() {
+                @Override
+                protected String getPath(Resource entry) {
+                    return entry.getUri();
+                }
+
+                @Override
+                protected Collection<Resource> getPaths() {
+                    return serverResources;
+                }
+            };
+
+            Resource matches = pathMatcher.matches(uri);
+
+            if (matches != null) {
+                resources = Arrays.asList(matches);
+            }
+        }
+
         Boolean finalDeep = deep;
+
         return Response.ok(
-                storeFactory.getResourceStore().findByResourceServer(search, this.resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : Constants.DEFAULT_MAX_RESULTS).stream()
+                resources.stream()
                         .map(resource -> toRepresentation.apply(resource, finalDeep))
                         .collect(Collectors.toList()))
                 .build();
@@ -437,7 +459,7 @@ public class ResourceSetService {
         audit(uriInfo, resource, null, operation);
     }
 
-    private void audit(@Context UriInfo uriInfo, ResourceRepresentation resource, String id, OperationType operation) {
+    public void audit(@Context UriInfo uriInfo, ResourceRepresentation resource, String id, OperationType operation) {
         if (authorization.getRealm().isAdminEventsEnabled()) {
             if (id != null) {
                 adminEvent.operation(operation).resourcePath(uriInfo, id).representation(resource).success();
diff --git a/services/src/main/java/org/keycloak/authorization/protection/ProtectionService.java b/services/src/main/java/org/keycloak/authorization/protection/ProtectionService.java
index 8a811f1..80226cf 100644
--- a/services/src/main/java/org/keycloak/authorization/protection/ProtectionService.java
+++ b/services/src/main/java/org/keycloak/authorization/protection/ProtectionService.java
@@ -65,7 +65,7 @@ public class ProtectionService {
 
         ResteasyProviderFactory.getInstance().injectProperties(resourceManager);
 
-        ResourceService resource = new ResourceService(resourceServer, identity, resourceManager, this.authorization);
+        ResourceService resource = new ResourceService(resourceServer, identity, resourceManager);
 
         ResteasyProviderFactory.getInstance().injectProperties(resource);
 
diff --git a/services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java b/services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java
index 21fd27c..1fbe5f9 100644
--- a/services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java
+++ b/services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java
@@ -18,8 +18,6 @@
 package org.keycloak.authorization.protection.resource;
 
 import java.util.function.BiFunction;
-import java.util.function.Function;
-import java.util.stream.Collectors;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
@@ -36,16 +34,13 @@ import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriInfo;
 
 import org.jboss.resteasy.annotations.cache.NoCache;
-import org.keycloak.authorization.AuthorizationProvider;
 import org.keycloak.authorization.admin.ResourceSetService;
 import org.keycloak.authorization.identity.Identity;
 import org.keycloak.authorization.model.Resource;
 import org.keycloak.authorization.model.ResourceServer;
-import org.keycloak.authorization.protection.resource.representation.UmaResourceRepresentation;
-import org.keycloak.authorization.protection.resource.representation.UmaScopeRepresentation;
+import org.keycloak.events.admin.OperationType;
 import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation;
 import org.keycloak.representations.idm.authorization.ResourceRepresentation;
-import org.keycloak.representations.idm.authorization.ScopeRepresentation;
 import org.keycloak.services.ErrorResponseException;
 
 /**
@@ -56,39 +51,51 @@ public class ResourceService {
     private final ResourceServer resourceServer;
     private final ResourceSetService resourceManager;
     private final Identity identity;
-    private final AuthorizationProvider authorization;
 
-    public ResourceService(ResourceServer resourceServer, Identity identity, ResourceSetService resourceManager, AuthorizationProvider authorization) {
+    public ResourceService(ResourceServer resourceServer, Identity identity, ResourceSetService resourceManager) {
         this.identity = identity;
         this.resourceServer = resourceServer;
         this.resourceManager = resourceManager;
-        this.authorization = authorization;
     }
 
     @POST
     @Consumes("application/json")
     @Produces("application/json")
-    public Response create(@Context  UriInfo uriInfo, UmaResourceRepresentation umaResource) {
+    public Response create(@Context  UriInfo uriInfo, UmaResourceRepresentation resource) {
         checkResourceServerSettings();
-        if (umaResource == null) {
+
+        if (resource == null) {
             return Response.status(Status.BAD_REQUEST).build();
         }
-        return this.resourceManager.create(uriInfo, toResourceRepresentation(umaResource), (Function<Resource, UmaResourceRepresentation>) this::toUmaRepresentation);
+
+        ResourceOwnerRepresentation owner = resource.getOwner();
+
+        if (owner == null) {
+            owner = new ResourceOwnerRepresentation();
+            resource.setOwner(owner);
+        }
+
+        String ownerId = owner.getId();
+
+        if (ownerId == null) {
+            ownerId = this.identity.getId();
+        }
+
+        owner.setId(ownerId);
+
+        ResourceRepresentation newResource = resourceManager.create(resource);
+
+        resourceManager.audit(uriInfo, resource, resource.getId(), OperationType.CREATE);
+
+        return Response.status(Status.CREATED).entity(new UmaResourceRepresentation(newResource)).build();
     }
 
     @Path("{id}")
     @PUT
     @Consumes("application/json")
     @Produces("application/json")
-    public Response update(@Context UriInfo uriInfo, @PathParam("id") String id, UmaResourceRepresentation representation) {
-        ResourceRepresentation resource = toResourceRepresentation(representation);
-        Response response = this.resourceManager.update(uriInfo, id, resource);
-
-        if (response.getEntity() instanceof ResourceRepresentation) {
-            return Response.noContent().build();
-        }
-
-        return response;
+    public Response update(@Context UriInfo uriInfo, @PathParam("id") String id, ResourceRepresentation resource) {
+        return this.resourceManager.update(uriInfo, id, resource);
     }
 
     @Path("/{id}")
@@ -102,7 +109,7 @@ public class ResourceService {
     @GET
     @Produces("application/json")
     public Response findById(@PathParam("id") String id) {
-        return this.resourceManager.findById(id, (Function<Resource, UmaResourceRepresentation>) resource -> toUmaRepresentation(resource));
+        return this.resourceManager.findById(id, UmaResourceRepresentation::new);
     }
 
     @GET
@@ -114,75 +121,11 @@ public class ResourceService {
                          @QueryParam("owner") String owner,
                          @QueryParam("type") String type,
                          @QueryParam("scope") String scope,
+                         @QueryParam("matchingUri") Boolean matchingUri,
                          @QueryParam("deep") Boolean deep,
                          @QueryParam("first") Integer firstResult,
                          @QueryParam("max") Integer maxResult) {
-        return resourceManager.find(id, name, uri, owner, type, scope, deep, firstResult, maxResult, (BiFunction<Resource, Boolean, String>) (resource, deep1) -> resource.getId());
-    }
-
-    private ResourceRepresentation toResourceRepresentation(UmaResourceRepresentation umaResource) {
-        ResourceRepresentation resource = new ResourceRepresentation();
-
-        resource.setId(umaResource.getId());
-        resource.setIconUri(umaResource.getIconUri());
-        resource.setName(umaResource.getName());
-        resource.setUri(umaResource.getUri());
-        resource.setType(umaResource.getType());
-        resource.setOwnerManagedAccess(umaResource.getOwnerManagedAccess());
-
-        ResourceOwnerRepresentation owner = new ResourceOwnerRepresentation();
-        String ownerId = umaResource.getOwner();
-
-        if (ownerId == null) {
-            ownerId = this.identity.getId();
-        }
-
-        owner.setId(ownerId);
-        resource.setOwner(owner);
-
-        resource.setScopes(umaResource.getScopes().stream().map(representation -> {
-            ScopeRepresentation scopeRepresentation = new ScopeRepresentation();
-
-            scopeRepresentation.setId(representation.getId());
-            scopeRepresentation.setName(representation.getName());
-            scopeRepresentation.setIconUri(representation.getIconUri());
-
-            return scopeRepresentation;
-        }).collect(Collectors.toSet()));
-
-        resource.setAttributes(umaResource.getAttributes());
-
-        return resource;
-    }
-
-    private UmaResourceRepresentation toUmaRepresentation(Resource model) {
-        if (model == null) {
-            return null;
-        }
-
-        UmaResourceRepresentation resource = new UmaResourceRepresentation();
-
-        resource.setId(model.getId());
-        resource.setIconUri(model.getIconUri());
-        resource.setName(model.getName());
-        resource.setUri(model.getUri());
-        resource.setType(model.getType());
-
-        if (model.getOwner() != null) {
-            resource.setOwner(model.getOwner());
-        }
-
-        resource.setScopes(model.getScopes().stream().map(scopeRepresentation -> {
-            UmaScopeRepresentation umaScopeRep = new UmaScopeRepresentation();
-            umaScopeRep.setId(scopeRepresentation.getId());
-            umaScopeRep.setName(scopeRepresentation.getName());
-            umaScopeRep.setIconUri(scopeRepresentation.getIconUri());
-            return umaScopeRep;
-        }).collect(Collectors.toSet()));
-
-        resource.setAttributes(model.getAttributes());
-
-        return resource;
+        return resourceManager.find(id, name, uri, owner, type, scope, matchingUri, deep, firstResult, maxResult, (BiFunction<Resource, Boolean, String>) (resource, deep1) -> resource.getId());
     }
 
     private void checkResourceServerSettings() {
diff --git a/services/src/main/java/org/keycloak/authorization/protection/resource/UmaResourceRepresentation.java b/services/src/main/java/org/keycloak/authorization/protection/resource/UmaResourceRepresentation.java
new file mode 100644
index 0000000..302dd96
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/protection/resource/UmaResourceRepresentation.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2018 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.authorization.protection.resource;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class UmaResourceRepresentation extends ResourceRepresentation {
+
+    public UmaResourceRepresentation() {
+
+    }
+
+    public UmaResourceRepresentation(ResourceRepresentation resource) {
+        setId(resource.getId());
+        setName(resource.getName());
+        setType(resource.getType());
+        setUri(resource.getUri());
+        setIconUri(resource.getIconUri());
+        setOwner(resource.getOwner());
+        setScopes(resource.getScopes());
+        setDisplayName(resource.getDisplayName());
+        setOwnerManagedAccess(resource.getOwnerManagedAccess());
+    }
+
+    public UmaResourceRepresentation(Resource resource) {
+        setId(resource.getId());
+        setName(resource.getName());
+        setType(resource.getType());
+        setUri(resource.getUri());
+        setIconUri(resource.getIconUri());
+        setOwner(resource.getOwner());
+        setScopes(resource.getScopes().stream().map(scope -> new ScopeRepresentation(scope.getName())).collect(Collectors.toSet()));
+        setDisplayName(resource.getDisplayName());
+        setOwnerManagedAccess(resource.isOwnerManagedAccess());
+        setAttributes(resource.getAttributes());
+    }
+
+    @JsonProperty("resource_scopes")
+    @Override
+    public Set<ScopeRepresentation> getScopes() {
+        return super.getScopes();
+    }
+
+    @JsonProperty("resource_scopes")
+    @Override
+    public void setScopes(Set<ScopeRepresentation> scopes) {
+        super.setScopes(scopes);
+    }
+}
diff --git a/services/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailTemplateProvider.java b/services/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailTemplateProvider.java
index c55a585..e3e447f 100755
--- a/services/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailTemplateProvider.java
+++ b/services/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailTemplateProvider.java
@@ -192,8 +192,9 @@ public class FreeMarkerEmailTemplateProvider implements EmailTemplateProvider {
         }
     }
 
-    protected void send(String subjectKey, String template, Map<String, Object> attributes) throws EmailException {
-        send(subjectKey, Collections.emptyList(), template, attributes);
+    @Override
+    public void send(String subjectFormatKey, String bodyTemplate, Map<String, Object> bodyAttributes) throws EmailException {
+        send(subjectFormatKey, Collections.emptyList(), bodyTemplate, bodyAttributes);
     }
 
     protected EmailTemplate processTemplate(String subjectKey, List<Object> subjectAttributes, String template, Map<String, Object> attributes) throws EmailException {
@@ -229,9 +230,10 @@ public class FreeMarkerEmailTemplateProvider implements EmailTemplateProvider {
         return session.theme().getTheme(Theme.Type.EMAIL);
     }
 
-    protected void send(String subjectKey, List<Object> subjectAttributes, String template, Map<String, Object> attributes) throws EmailException {
+    @Override
+    public void send(String subjectFormatKey, List<Object> subjectAttributes, String bodyTemplate, Map<String, Object> bodyAttributes) throws EmailException {
         try {
-            EmailTemplate email = processTemplate(subjectKey, subjectAttributes, template, attributes);
+            EmailTemplate email = processTemplate(subjectFormatKey, subjectAttributes, bodyTemplate, bodyAttributes);
             send(email.getSubject(), email.getTextBody(), email.getHtmlBody());
         } catch (EmailException e) {
             throw e;
diff --git a/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java b/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
index f6beac5..a84765b 100755
--- a/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
+++ b/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
@@ -68,6 +68,7 @@ import org.keycloak.representations.idm.ScopeMappingRepresentation;
 import org.keycloak.representations.idm.UserConsentRepresentation;
 import org.keycloak.representations.idm.UserRepresentation;
 import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation;
 import org.keycloak.representations.idm.authorization.ResourceRepresentation;
 import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
 import org.keycloak.representations.idm.authorization.ScopeRepresentation;
@@ -315,7 +316,7 @@ public class ExportUtils {
                     ResourceRepresentation rep = toRepresentation(resource, settingsModel, authorization);
 
                     if (rep.getOwner().getId().equals(settingsModel.getId())) {
-                        rep.setOwner(null);
+                        rep.setOwner((ResourceOwnerRepresentation) null);
                     } else {
                         rep.getOwner().setId(null);
                     }
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 cf480fc..3d0f52d 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
@@ -332,9 +332,25 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
         Properties messagesBundle = handleThemeResources(theme, locale);
         FormMessage msg = new FormMessage(null, message);
         return formatMessage(msg, messagesBundle, locale);
+    }
 
+    @Override
+    public String getMessage(String message, String... parameters) {
+        Theme theme;
+        try {
+            theme = getTheme();
+        } catch (IOException e) {
+            logger.error("Failed to create theme", e);
+            throw new RuntimeException("Failed to create theme");
+        }
+
+        Locale locale = session.getContext().resolveLocale(user);
+        Properties messagesBundle = handleThemeResources(theme, locale);
+        FormMessage msg = new FormMessage(message, parameters);
+        return formatMessage(msg, messagesBundle, locale);
     }
-    
+
+
     /**
      * Create common attributes used in all templates.
      * 
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java
index cf0bb4a..65c66e2 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java
@@ -371,6 +371,7 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
         if (request.getResponseMode() != null) authenticationSession.setClientNote(OIDCLoginProtocol.RESPONSE_MODE_PARAM, request.getResponseMode());
         if (request.getClaims()!= null) authenticationSession.setClientNote(OIDCLoginProtocol.CLAIMS_PARAM, request.getClaims());
         if (request.getAcr() != null) authenticationSession.setClientNote(OIDCLoginProtocol.ACR_PARAM, request.getAcr());
+        if (request.getDisplay() != null) authenticationSession.setClientNote(OAuth2Constants.DISPLAY, request.getDisplay());
 
         // https://tools.ietf.org/html/rfc7636#section-4
         if (request.getCodeChallenge() != null) authenticationSession.setClientNote(OIDCLoginProtocol.CODE_CHALLENGE_PARAM, request.getCodeChallenge());
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java
index 7f5048a..4062021 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java
@@ -164,12 +164,11 @@ public class LogoutEndpoint {
      *
      * returns 204 if successful, 400 if not with a json error response.
      *
-     * @param authorizationHeader
      * @return
      */
     @POST
     @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
-    public Response logoutToken(final @HeaderParam(HttpHeaders.AUTHORIZATION) String authorizationHeader) {
+    public Response logoutToken() {
         MultivaluedMap<String, String> form = request.getDecodedFormParameters();
         checkSsl();
 
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthorizationEndpointRequest.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthorizationEndpointRequest.java
index 29edb03..083c2c3 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthorizationEndpointRequest.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthorizationEndpointRequest.java
@@ -32,6 +32,7 @@ public class AuthorizationEndpointRequest {
     String state;
     String scope;
     String loginHint;
+    String display;
     String prompt;
     String nonce;
     Integer maxAge;
@@ -111,4 +112,7 @@ public class AuthorizationEndpointRequest {
         return codeChallengeMethod;
     }
 
+    public String getDisplay() {
+        return display;
+    }
 }
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java
index 90160ee..d6cb1b7 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java
@@ -18,6 +18,7 @@
 package org.keycloak.protocol.oidc.endpoints.request;
 
 import org.jboss.logging.Logger;
+import org.keycloak.OAuth2Constants;
 import org.keycloak.constants.AdapterConstants;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
 
@@ -91,6 +92,7 @@ abstract class AuthzEndpointRequestParser {
         request.maxAge = replaceIfNotNull(request.maxAge, getIntParameter(OIDCLoginProtocol.MAX_AGE_PARAM));
         request.claims = replaceIfNotNull(request.claims, getParameter(OIDCLoginProtocol.CLAIMS_PARAM));
         request.acr = replaceIfNotNull(request.acr, getParameter(OIDCLoginProtocol.ACR_PARAM));
+        request.display = replaceIfNotNull(request.display, getParameter(OAuth2Constants.DISPLAY));
 
         // https://tools.ietf.org/html/rfc7636#section-6.1
         request.codeChallenge = replaceIfNotNull(request.codeChallenge, getParameter(OIDCLoginProtocol.CODE_CHALLENGE_PARAM));
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
index 05f9f4c..38d2489 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
@@ -776,8 +776,15 @@ public class TokenEndpoint {
         String audience = formParams.getFirst(OAuth2Constants.AUDIENCE);
         if (audience != null) {
             targetClient = realm.getClientByClientId(audience);
+            if (targetClient == null) {
+                event.detail(Details.REASON, "audience not found");
+                event.error(Errors.CLIENT_NOT_FOUND);
+                throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_CLIENT, "Audience not found", Response.Status.BAD_REQUEST);
+
+            }
         }
 
+
         if (targetClient.isConsentRequired()) {
             event.detail(Details.REASON, "audience requires consent");
             event.error(Errors.CONSENT_DENIED);
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCClientInstallation.java b/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCClientInstallation.java
index 60970d8..8dadaaf 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCClientInstallation.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCClientInstallation.java
@@ -155,7 +155,7 @@ public class KeycloakOIDCClientInstallation implements ClientInstallationProvide
             PolicyEnforcerConfig enforcerConfig = new PolicyEnforcerConfig();
 
             enforcerConfig.setEnforcementMode(null);
-            enforcerConfig.setCreateResources(null);
+            enforcerConfig.setLazyLoadPaths(null);
 
             rep.setEnforcerConfig(enforcerConfig);
 
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocol.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocol.java
index 148d840..56c0022 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocol.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocol.java
@@ -71,6 +71,7 @@ public class OIDCLoginProtocol implements LoginProtocol {
     public static final String MAX_AGE_PARAM = OAuth2Constants.MAX_AGE;
     public static final String PROMPT_PARAM = OAuth2Constants.PROMPT;
     public static final String LOGIN_HINT_PARAM = "login_hint";
+    public static final String DISPLAY_PARAM = "display";
     public static final String REQUEST_PARAM = "request";
     public static final String REQUEST_URI_PARAM = "request_uri";
     public static final String UI_LOCALES_PARAM = OAuth2Constants.UI_LOCALES_PARAM;
diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
index 803e778..edb9b51 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -21,11 +21,7 @@ import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
 import org.jboss.resteasy.spi.HttpRequest;
 import org.keycloak.OAuth2Constants;
 import org.keycloak.TokenVerifier;
-import org.keycloak.authentication.AuthenticationProcessor;
-import org.keycloak.authentication.RequiredActionContext;
-import org.keycloak.authentication.RequiredActionContextResult;
-import org.keycloak.authentication.RequiredActionFactory;
-import org.keycloak.authentication.RequiredActionProvider;
+import org.keycloak.authentication.*;
 import org.keycloak.authentication.actiontoken.DefaultActionTokenKey;
 import org.keycloak.broker.provider.IdentityProvider;
 import org.keycloak.common.ClientConnection;
@@ -761,6 +757,11 @@ public class AuthenticationManager {
         uriBuilder.queryParam(Constants.CLIENT_ID, authSession.getClient().getClientId());
         uriBuilder.queryParam(Constants.TAB_ID, authSession.getTabId());
 
+        if (uriInfo.getQueryParameters().containsKey(LoginActionsService.AUTH_SESSION_ID)) {
+            uriBuilder.queryParam(LoginActionsService.AUTH_SESSION_ID, authSession.getParentSession().getId());
+
+        }
+
         URI redirect = uriBuilder.build(realm.getName());
         return Response.status(302).location(redirect).build();
 
@@ -965,6 +966,25 @@ public class AuthenticationManager {
         authSession.setProtocolMappers(requestedProtocolMappers);
     }
 
+    public static RequiredActionProvider createRequiredAction(KeycloakSession session, RequiredActionFactory factory, AuthenticationSessionModel authSession) {
+        String display = authSession.getClientNote(OAuth2Constants.DISPLAY);
+        if (display == null) return factory.create(session);
+
+
+        if (factory instanceof DisplayTypeRequiredActionFactory) {
+            RequiredActionProvider provider = ((DisplayTypeRequiredActionFactory)factory).createDisplay(session, display);
+            if (provider != null) return provider;
+        }
+        // todo create a provider for handling lack of display support
+        if (OAuth2Constants.DISPLAY_CONSOLE.equalsIgnoreCase(display)) {
+            throw new AuthenticationFlowException(AuthenticationFlowError.DISPLAY_NOT_SUPPORTED, TextChallenge.browserRequired(session));
+
+        } else {
+            return factory.create(session);
+        }
+    }
+
+
     protected static Response executionActions(KeycloakSession session, AuthenticationSessionModel authSession,
                                                HttpRequest request, EventBuilder event, RealmModel realm, UserModel user,
                                                Set<String> requiredActions) {
@@ -982,7 +1002,15 @@ public class AuthenticationManager {
             if (factory == null) {
                 throw new RuntimeException("Unable to find factory for Required Action: " + model.getProviderId() + " did you forget to declare it in a META-INF/services file?");
             }
-            RequiredActionProvider actionProvider = factory.create(session);
+            RequiredActionProvider actionProvider = null;
+            try {
+                actionProvider = createRequiredAction(session, factory, authSession);
+            } catch (AuthenticationFlowException e) {
+                if (e.getResponse() != null) {
+                    return e.getResponse();
+                }
+                throw e;
+            }
             RequiredActionContextResult context = new RequiredActionContextResult(authSession, realm, event, session, request, user, factory);
             actionProvider.requiredActionChallenge(context);
 
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 c6fb6c8..425a889 100755
--- a/services/src/main/java/org/keycloak/services/messages/Messages.java
+++ b/services/src/main/java/org/keycloak/services/messages/Messages.java
@@ -21,6 +21,7 @@ package org.keycloak.services.messages;
  */
 public class Messages {
 
+    public static final String DISPLAY_UNSUPPORTED = "displayUnsupported";
     public static final String LOGIN_TIMEOUT = "loginTimeout";
 
     public static final String INVALID_USER = "invalidUserMessage";
diff --git a/services/src/main/java/org/keycloak/services/resources/account/AccountConsole.java b/services/src/main/java/org/keycloak/services/resources/account/AccountConsole.java
index de29fd7..85cf2a7 100644
--- a/services/src/main/java/org/keycloak/services/resources/account/AccountConsole.java
+++ b/services/src/main/java/org/keycloak/services/resources/account/AccountConsole.java
@@ -40,6 +40,7 @@ import org.keycloak.services.managers.Auth;
 import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.util.ResolveRelative;
 import org.keycloak.services.validation.Validation;
+import org.keycloak.theme.beans.MessageFormatterMethod;
 
 /**
  * Created by st on 29/03/17.
@@ -101,7 +102,8 @@ public class AccountConsole {
                 if (auth != null) {
                     Locale locale = session.getContext().resolveLocale(auth.getUser());
                     map.put("locale", locale.toLanguageTag());
-                    map.put("msg", messagesToJsonString(theme.getMessages(locale)));
+                    Properties messages = theme.getMessages(locale);
+                    map.put("msg", messagesToJsonString(messages));
                 }
             } catch (Exception e) {
                 logger.warn("Failed to load messages", e);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/MgmtPermissions.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/MgmtPermissions.java
index 6fa044f..8994718 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/permissions/MgmtPermissions.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/MgmtPermissions.java
@@ -196,6 +196,10 @@ class MgmtPermissions implements AdminPermissionEvaluator, AdminPermissionManage
         return admin;
     }
 
+    public RealmModel adminsRealm() {
+        return adminsRealm;
+    }
+
 
     @Override
     public RolePermissions roles() {
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissions.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissions.java
index 361cb0c..464f512 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissions.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissions.java
@@ -230,7 +230,20 @@ class RolePermissions implements RolePermissionEvaluator, RolePermissionManageme
                     } else {
                         return true;
                     }
-                } else {
+                } else if (role.getName().equals(AdminRoles.REALM_ADMIN)) {
+                    // check to see if we have masterRealm.admin role.  Otherwise abort
+                    if (root.adminsRealm() == null || !root.adminsRealm().getName().equals(Config.getAdminRealm())) {
+                        return adminConflictMessage(role);
+                    }
+
+                    RealmModel masterRealm = root.adminsRealm();
+                    RoleModel adminRole = masterRealm.getRole(AdminRoles.ADMIN);
+                    if (root.admin().hasRole(adminRole)) {
+                        return true;
+                    } else {
+                        return adminConflictMessage(role);
+                    }
+                 } else {
                     return adminConflictMessage(role);
                 }
 
@@ -239,6 +252,7 @@ class RolePermissions implements RolePermissionEvaluator, RolePermissionManageme
                 if (role.getContainer() instanceof RealmModel) {
                     RealmModel realm = (RealmModel)role.getContainer();
                     // If realm role is master admin role then abort
+                    // if realm name is master realm, than we know this is a admin role in master realm.
                     if (realm.getName().equals(Config.getAdminRealm())) {
                         return adminConflictMessage(role);
                     }
diff --git a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
index 7c38ba8..ef9525a 100755
--- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
@@ -16,17 +16,12 @@
  */
 package org.keycloak.services.resources;
 
+import org.keycloak.authentication.*;
 import org.keycloak.authentication.actiontoken.DefaultActionTokenKey;
 import org.jboss.logging.Logger;
 import org.jboss.resteasy.spi.HttpRequest;
 import org.keycloak.OAuth2Constants;
-import org.keycloak.authentication.AuthenticationProcessor;
-import org.keycloak.authentication.RequiredActionContext;
-import org.keycloak.authentication.RequiredActionContextResult;
-import org.keycloak.authentication.RequiredActionFactory;
-import org.keycloak.authentication.RequiredActionProvider;
 import org.keycloak.TokenVerifier;
-import org.keycloak.authentication.ExplainedVerificationException;
 import org.keycloak.authentication.actiontoken.*;
 import org.keycloak.authentication.actiontoken.resetcred.ResetCredentialsActionTokenHandler;
 import org.keycloak.authentication.authenticators.broker.AbstractIdpAuthenticator;
@@ -934,7 +929,15 @@ public class LoginActionsService {
             event.error(Errors.INVALID_CODE);
             throw new WebApplicationException(ErrorPage.error(session, authSession, Response.Status.BAD_REQUEST, Messages.INVALID_CODE));
         }
-        RequiredActionProvider provider = factory.create(session);
+        RequiredActionProvider provider = null;
+        try {
+            provider = AuthenticationManager.createRequiredAction(session, factory, authSession);
+        }  catch (AuthenticationFlowException e) {
+            if (e.getResponse() != null) {
+                return e.getResponse();
+            }
+            throw new WebApplicationException(ErrorPage.error(session, authSession, Response.Status.BAD_REQUEST, Messages.DISPLAY_UNSUPPORTED));
+        }
 
         RequiredActionContextResult context = new RequiredActionContextResult(authSession, realm, event, session, request, authSession.getAuthenticatedUser(), factory) {
             @Override
diff --git a/services/src/main/java/org/keycloak/utils/TotpUtils.java b/services/src/main/java/org/keycloak/utils/TotpUtils.java
index 67ff697..d076735 100644
--- a/services/src/main/java/org/keycloak/utils/TotpUtils.java
+++ b/services/src/main/java/org/keycloak/utils/TotpUtils.java
@@ -45,6 +45,12 @@ public class TotpUtils {
         return sb.toString();
     }
 
+    public static String decode(String totpSecretEncoded) {
+        String encoded = totpSecretEncoded.replace(" ", "");
+        byte[] bytes = Base32.decode(encoded);
+        return new String(bytes);
+    }
+
     public static String qrCode(String totpSecret, RealmModel realm, UserModel user) {
         try {
             String keyUri = realm.getOTPPolicy().getKeyURI(realm, user, totpSecret);
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory b/services/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory
index 76b7507..ee29448 100755
--- a/services/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory
+++ b/services/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory
@@ -38,4 +38,4 @@ org.keycloak.protocol.saml.profile.ecp.authenticator.HttpBasicAuthenticatorFacto
 org.keycloak.authentication.authenticators.x509.X509ClientCertificateAuthenticatorFactory
 org.keycloak.authentication.authenticators.x509.ValidateX509CertificateUsernameFactory
 org.keycloak.protocol.docker.DockerAuthenticatorFactory
-org.keycloak.authentication.authenticators.cli.CliUsernamePasswordAuthenticatorFactory
+org.keycloak.authentication.authenticators.console.ConsoleUsernamePasswordAuthenticatorFactory
diff --git a/testsuite/integration-arquillian/test-apps/photoz/keycloak-lazy-load-path-authz-service.json b/testsuite/integration-arquillian/test-apps/photoz/keycloak-lazy-load-path-authz-service.json
new file mode 100644
index 0000000..47437dc
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/photoz/keycloak-lazy-load-path-authz-service.json
@@ -0,0 +1,78 @@
+{
+  "realm": "photoz",
+  "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+  "auth-server-url": "http://localhost:8180/auth",
+  "ssl-required": "external",
+  "resource": "photoz-restful-api",
+  "bearer-only" : true,
+  "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"
+    }
+  },
+  "policy-enforcer": {
+    "enforcement-mode": "PERMISSIVE",
+    "user-managed-access": {},
+    "lazy-load-paths": true,
+    "paths": [
+      {
+        "name" : "Album Resource",
+        "path" : "/album",
+        "methods" : [
+          {
+            "method": "GET",
+            "scopes-enforcement-mode" : "DISABLED"
+          }
+        ]
+      },
+      {
+        "name" : "Album Resource",
+        "path" : "/album/{id}",
+        "methods" : [
+          {
+            "method": "DELETE",
+            "scopes" : ["album:delete"]
+          },
+          {
+            "method": "GET",
+            "scopes" : ["album:view"]
+          }
+        ]
+      },
+      {
+        "path" : "/profile"
+      },
+      {
+        "name" : "Admin Resources",
+        "path" : "/admin/*"
+      },
+      {
+        "name" : "Scope Protected Resource",
+        "path" : "/scope-any",
+        "methods": [
+          {
+            "method": "GET",
+            "scopes": ["scope-a", "scope-b"],
+            "scopes-enforcement-mode": "ANY"
+          }
+        ]
+      },
+      {
+        "name" : "Scope Protected Resource",
+        "path" : "/scope-all",
+        "methods": [
+          {
+            "method": "GET",
+            "scopes": ["scope-a", "scope-b"],
+            "scopes-enforcement-mode": "ALL"
+          }
+        ]
+      }
+    ]
+  }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java
index 94feb72..40b1242 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java
@@ -3,8 +3,8 @@ package org.keycloak.example.photoz.album;
 import org.keycloak.KeycloakSecurityContext;
 import org.keycloak.authorization.client.AuthzClient;
 import org.keycloak.authorization.client.ClientAuthorizationContext;
-import org.keycloak.authorization.client.representation.ResourceRepresentation;
-import org.keycloak.authorization.client.representation.ScopeRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
 import org.keycloak.authorization.client.resource.ProtectionResource;
 import org.keycloak.example.photoz.entity.Album;
 import org.keycloak.example.photoz.util.Transaction;
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json
index a0f8711..dd0cab1 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json
@@ -21,6 +21,16 @@
     "paths": [
       {
         "name" : "Album Resource",
+        "path" : "/album",
+        "methods" : [
+          {
+            "method": "GET",
+            "scopes-enforcement-mode" : "DISABLED"
+          }
+        ]
+      },
+      {
+        "name" : "Album Resource",
         "path" : "/album/{id}",
         "methods" : [
           {
diff --git a/testsuite/integration-arquillian/test-apps/servlet-authz/keycloak-lazy-load-authz-service.json b/testsuite/integration-arquillian/test-apps/servlet-authz/keycloak-lazy-load-authz-service.json
new file mode 100644
index 0000000..35f76d2
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/servlet-authz/keycloak-lazy-load-authz-service.json
@@ -0,0 +1,15 @@
+{
+  "realm": "servlet-authz",
+  "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+  "auth-server-url" : "http://localhost:8180/auth",
+  "ssl-required" : "external",
+  "resource" : "servlet-authz-app",
+  "public-client" : false,
+  "credentials": {
+    "secret": "secret"
+  },
+  "policy-enforcer": {
+    "on-deny-redirect-to" : "/servlet-authz-app/accessDenied.jsp",
+    "lazy-load-paths": true
+  }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/WEB-INF/keycloak.json b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/WEB-INF/keycloak.json
index 0dd6a14..d6e7f00 100644
--- a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/WEB-INF/keycloak.json
+++ b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/WEB-INF/keycloak.json
@@ -8,6 +8,7 @@
     },
     "policy-enforcer": {
         "on-deny-redirect-to": "/servlet-policy-enforcer/denied.jsp",
+        "lazy-load-paths": false,
         "paths": [
             {
                 "name": "Welcome Resource",
@@ -31,7 +32,7 @@
             },
             {
                 "name": "Pattern 5",
-                "path": "/resource/{pattern}/resource-d"
+                "path": "/a/{pattern}/resource-d"
             },
             {
                 "name": "Pattern 6",
diff --git a/testsuite/integration-arquillian/tests/base/pom.xml b/testsuite/integration-arquillian/tests/base/pom.xml
index d379474..1e5d755 100644
--- a/testsuite/integration-arquillian/tests/base/pom.xml
+++ b/testsuite/integration-arquillian/tests/base/pom.xml
@@ -228,11 +228,47 @@
                                     <type>zip</type>
                                     <outputDirectory>${containers.home}</outputDirectory>
                                 </artifactItem>
-                            </artifactItems>
+                             </artifactItems>
                         </configuration>
                     </execution>
                 </executions>
             </plugin>
+            <plugin>
+                <groupId>com.igormaznitsa</groupId>
+                <artifactId>mvn-golang-wrapper</artifactId>
+                <version>2.1.6</version>
+                <extensions>true</extensions>
+                <configuration>
+                    <goVersion>1.9.2</goVersion>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>get-mousetrap</id>
+                        <goals>
+                            <goal>get</goal>
+                        </goals>
+                        <configuration>
+                            <packages>
+                                <package>github.com/inconshreveable/mousetrap</package>
+                            </packages>
+                            <goPath>${project.build.directory}/gopath</goPath>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>get-kcinit</id>
+                        <goals>
+                            <goal>get</goal>
+                        </goals>
+                        <configuration>
+                            <packages>
+                                <package>github.com/keycloak/kcinit</package>
+                            </packages>
+                            <goPath>${project.build.directory}/gopath</goPath>
+                            <tag>0.3</tag>
+                        </configuration>
+                    </execution>
+                  </executions>
+            </plugin>
 
         </plugins>
 
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/PhotozClientAuthzTestApp.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/PhotozClientAuthzTestApp.java
index f87d481..90a6692 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/PhotozClientAuthzTestApp.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/PhotozClientAuthzTestApp.java
@@ -203,10 +203,16 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
 
         this.loginPage.form().login(username, password);
         waitForPageToLoad();//guess
-        
-        // simple check if we are at the consent page, if so just click 'Yes'
-        if (this.consentPage.isCurrent()) {
-            consentPage.confirm();
+
+        try {
+            if (!isCurrent()) {
+                // simple check if we are at the consent page, if so just click 'Yes'
+                if (this.consentPage.isCurrent()) {
+                    consentPage.confirm();
+                }
+            }
+        } catch (Exception ignore) {
+            // ignore errors when checking consent page, if an error tests will also fail
         }
 
         pause(WAIT_AFTER_OPERATION);
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/cli/exec/AbstractExec.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/cli/exec/AbstractExec.java
index b5476b5..ddfe91d 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/cli/exec/AbstractExec.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/cli/exec/AbstractExec.java
@@ -1,5 +1,6 @@
 package org.keycloak.testsuite.cli.exec;
 
+import org.keycloak.client.admin.cli.util.OsUtil;
 import org.keycloak.testsuite.cli.OsArch;
 import org.keycloak.testsuite.cli.OsUtils;
 
@@ -33,7 +34,7 @@ public abstract class AbstractExec {
 
     private boolean logStreams = Boolean.valueOf(System.getProperty("cli.log.output", "true"));
 
-    protected boolean dumpStreams;
+    protected boolean dumpStreams = true;
 
     protected String workDir = WORK_DIR;
 
@@ -177,6 +178,7 @@ public abstract class AbstractExec {
         return new String(stdout.toByteArray());
     }
 
+
     public InputStream stderr() {
         return new ByteArrayInputStream(stderr.toByteArray());
     }
@@ -224,6 +226,22 @@ public abstract class AbstractExec {
         throw new RuntimeException("Timed while waiting for content to appear in stdout");
     }
 
+    public void waitForStderr(String content) {
+        long start = System.currentTimeMillis();
+        while (System.currentTimeMillis() - start < waitTimeout) {
+            if (stderrString().indexOf(content) != -1) {
+                return;
+            }
+            try {
+                Thread.sleep(10);
+            } catch (InterruptedException e) {
+                throw new RuntimeException("Interrupted ...", e);
+            }
+        }
+
+        throw new RuntimeException("Timed while waiting for content to appear in stdout");
+    }
+
     public void sendToStdin(String s) {
         if (stdin instanceof InteractiveInputStream) {
             ((InteractiveInputStream) stdin).pushBytes(s.getBytes());
@@ -232,6 +250,10 @@ public abstract class AbstractExec {
         }
     }
 
+    public void sendLine(String s) {
+        sendToStdin(s + OsUtil.EOL);
+    }
+
 
 
     static void copyStream(InputStream is, OutputStream os) throws IOException {
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/cli/KcinitExec.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/cli/KcinitExec.java
new file mode 100644
index 0000000..513eb54
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/cli/KcinitExec.java
@@ -0,0 +1,58 @@
+package org.keycloak.testsuite.cli;
+
+import org.keycloak.testsuite.cli.exec.AbstractExec;
+import org.keycloak.testsuite.cli.exec.AbstractExecBuilder;
+
+import java.io.InputStream;
+
+/**
+ * @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
+ */
+public class KcinitExec extends AbstractExec {
+
+    public static final String WORK_DIR = System.getProperty("user.dir") + "/target";
+
+    public static final String CMD = OS_ARCH.isWindows() ? "kcinit" : "kcinit";
+
+    private KcinitExec(String workDir, String argsLine, InputStream stdin) {
+        this(workDir, argsLine, null, stdin);
+    }
+
+    private KcinitExec(String workDir, String argsLine, String env, InputStream stdin) {
+        super(workDir, argsLine, env, stdin);
+    }
+
+    @Override
+    public String getCmd() {
+        return "./" + CMD;
+    }
+
+    public static KcinitExec.Builder newBuilder() {
+        return (KcinitExec.Builder) new KcinitExec.Builder().workDir(WORK_DIR);
+    }
+
+    public static KcinitExec execute(String args) {
+        return newBuilder()
+                .argsLine(args)
+                .execute();
+    }
+
+    public static class Builder extends AbstractExecBuilder<KcinitExec> {
+
+        @Override
+        public KcinitExec execute() {
+            KcinitExec exe = new KcinitExec(workDir, argsLine, env, stdin);
+            exe.dumpStreams = dumpStreams;
+            exe.execute();
+            return exe;
+        }
+
+        @Override
+        public KcinitExec executeAsync() {
+            KcinitExec exe = new KcinitExec(workDir, argsLine, env, stdin);
+            exe.dumpStreams = dumpStreams;
+            exe.executeAsync();
+            return exe;
+        }
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractBaseServletAuthzAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractBaseServletAuthzAdapterTest.java
new file mode 100644
index 0000000..7f47bb9
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractBaseServletAuthzAdapterTest.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.adapter.example.authorization;
+
+import static org.junit.Assert.assertFalse;
+import static org.keycloak.testsuite.util.IOUtil.loadJson;
+import static org.keycloak.testsuite.util.IOUtil.loadRealm;
+import static org.keycloak.testsuite.util.WaitUtils.pause;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.List;
+
+import javax.ws.rs.core.Response;
+
+import org.jboss.arquillian.container.test.api.Deployer;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.junit.BeforeClass;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.admin.client.resource.ClientsResource;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
+import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
+import org.keycloak.testsuite.ProfileAssume;
+import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
+import org.keycloak.testsuite.util.WaitUtils;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public abstract class AbstractBaseServletAuthzAdapterTest extends AbstractExampleAdapterTest {
+
+    protected static final String REALM_NAME = "servlet-authz";
+    protected static final String RESOURCE_SERVER_ID = "servlet-authz-app";
+
+    @BeforeClass
+    public static void enabled() { ProfileAssume.assumePreview(); }
+
+    @ArquillianResource
+    private Deployer deployer;
+
+    @Override
+    public void addAdapterTestRealms(List<RealmRepresentation> testRealms) {
+        testRealms.add(
+                loadRealm(new File(TEST_APPS_HOME_DIR + "/servlet-authz-app/servlet-authz-realm.json")));
+    }
+
+    protected void performTests(ExceptionRunnable assertion) {
+        performTests(() -> importResourceServerSettings(), assertion);
+    }
+
+    protected void performTests(ExceptionRunnable beforeDeploy, ExceptionRunnable assertion) {
+        try {
+            beforeDeploy.run();
+            deployer.deploy(RESOURCE_SERVER_ID);
+            assertion.run();
+        } catch (FileNotFoundException cause) {
+            throw new RuntimeException("Failed to import authorization settings", cause);
+        } catch (Exception cause) {
+            throw new RuntimeException("Error while executing tests", cause);
+        } finally {
+            deployer.undeploy(RESOURCE_SERVER_ID);
+        }
+    }
+
+    protected boolean hasLink(String text) {
+        return getLink(text) != null;
+    }
+
+    protected boolean hasText(String text) {
+        return this.driver.getPageSource().contains(text);
+    }
+
+    private WebElement getLink(String text) {
+        return this.driver.findElement(By.xpath("//a[text() = '" + text + "']"));
+    }
+
+    protected void importResourceServerSettings() throws FileNotFoundException {
+        getAuthorizationResource().importSettings(loadJson(new FileInputStream(new File(TEST_APPS_HOME_DIR + "/servlet-authz-app/servlet-authz-app-authz-service.json")), ResourceServerRepresentation.class));
+    }
+
+    protected AuthorizationResource getAuthorizationResource() {
+        return getClientResource(RESOURCE_SERVER_ID).authorization();
+    }
+
+    protected ClientResource getClientResource(String clientId) {
+        ClientsResource clients = this.realmsResouce().realm(REALM_NAME).clients();
+        ClientRepresentation resourceServer = clients.findByClientId(clientId).get(0);
+        return clients.get(resourceServer.getId());
+    }
+
+    private void logOut() {
+        navigateTo();
+        By by = By.xpath("//a[text() = 'Sign Out']");
+        WaitUtils.waitUntilElement(by);
+        this.driver.findElement(by).click();
+        pause(500);
+    }
+
+    protected void login(String username, String password) {
+        try {
+            navigateTo();
+            Thread.sleep(2000);
+            if (this.driver.getCurrentUrl().startsWith(getResourceServerUrl().toString())) {
+                Thread.sleep(2000);
+                logOut();
+                navigateTo();
+            }
+
+            Thread.sleep(2000);
+
+            this.loginPage.form().login(username, password);
+        } catch (Exception cause) {
+            throw new RuntimeException("Login failed", cause);
+        }
+    }
+
+    private void navigateTo() {
+        this.driver.navigate().to(getResourceServerUrl());
+        WaitUtils.waitUntilElement(By.xpath("//a[text() = 'Dynamic Menu']"));
+    }
+
+    protected boolean wasDenied() {
+        return this.driver.getPageSource().contains("You can not access this resource.");
+    }
+
+    protected URL getResourceServerUrl() {
+        try {
+            return new URL(this.appServerContextRootPage + "/" + RESOURCE_SERVER_ID);
+        } catch (MalformedURLException e) {
+            throw new RuntimeException("Could not obtain resource server url.", e);
+        }
+    }
+
+    protected void navigateToDynamicMenuPage() {
+        navigateTo();
+        getLink("Dynamic Menu").click();
+    }
+
+    protected void navigateToUserPremiumPage() {
+        navigateTo();
+        getLink("User Premium").click();
+    }
+
+    protected void navigateToAdminPage() {
+        navigateTo();
+        getLink("Administration").click();
+    }
+
+    protected void updatePermissionPolicies(String permissionName, String... policyNames) {
+        for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) {
+            if (permissionName.equalsIgnoreCase(policy.getName())) {
+                StringBuilder policies = new StringBuilder("[");
+
+                for (String policyName : policyNames) {
+                    if (policies.length() > 1) {
+                        policies.append(",");
+                    }
+                    policies.append("\"").append(policyName).append("\"");
+
+                }
+
+                policies.append("]");
+
+                policy.getConfig().put("applyPolicies", policies.toString());
+                getAuthorizationResource().policies().policy(policy.getId()).update(policy);
+            }
+        }
+    }
+
+    protected void createUserPolicy(String name, String... userNames) {
+        UserPolicyRepresentation policy = new UserPolicyRepresentation();
+
+        policy.setName(name);
+
+        for (String userName : userNames) {
+            policy.addUser(userName);
+        }
+
+        assertFalse(policy.getUsers().isEmpty());
+
+        Response response = getAuthorizationResource().policies().user().create(policy);
+        response.close();
+    }
+
+    protected interface ExceptionRunnable {
+        void run() throws Exception;
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPermissiveModeAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPermissiveModeAdapterTest.java
index e1ad409..052150c 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPermissiveModeAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPermissiveModeAdapterTest.java
@@ -28,7 +28,7 @@ import org.junit.Test;
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
-public abstract class AbstractPermissiveModeAdapterTest extends AbstractServletAuthzAdapterTest {
+public abstract class AbstractPermissiveModeAdapterTest extends AbstractBaseServletAuthzAdapterTest {
 
     @Deployment(name = RESOURCE_SERVER_ID, managed = false)
     public static WebArchive deployment() throws IOException {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java
index 4cc9d4a..80ddd05 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java
@@ -75,7 +75,7 @@ import org.keycloak.util.JsonSerialization;
 public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAdapterTest {
 
     private static final String REALM_NAME = "photoz";
-    private static final String RESOURCE_SERVER_ID = "photoz-restful-api";
+    protected static final String RESOURCE_SERVER_ID = "photoz-restful-api";
     private static final int TOKEN_LIFESPAN_LEEWAY = 3; // seconds
 
     @ArquillianResource
@@ -118,16 +118,6 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
         testRealms.add(realm);
     }
 
-    @Deployment(name = PhotozClientAuthzTestApp.DEPLOYMENT_NAME)
-    public static WebArchive deploymentClient() throws IOException {
-        return exampleDeployment(PhotozClientAuthzTestApp.DEPLOYMENT_NAME);
-    }
-
-    @Deployment(name = RESOURCE_SERVER_ID, managed = false, testable = false)
-    public static WebArchive deploymentResourceServer() throws IOException {
-        return exampleDeployment(RESOURCE_SERVER_ID);
-    }
-
     @Override
     public void beforeAbstractKeycloakTest() throws Exception {
         super.beforeAbstractKeycloakTest();
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleLazyLoadPathsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleLazyLoadPathsAdapterTest.java
new file mode 100644
index 0000000..3e35a33
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleLazyLoadPathsAdapterTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.adapter.example.authorization;
+
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.keycloak.testsuite.util.IOUtil.loadJson;
+import static org.keycloak.testsuite.util.IOUtil.loadRealm;
+import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.client.LaxRedirectStrategy;
+import org.jboss.arquillian.container.test.api.Deployer;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.graphene.page.Page;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.admin.client.resource.ClientsResource;
+import org.keycloak.admin.client.resource.ResourcesResource;
+import org.keycloak.admin.client.resource.RoleResource;
+import org.keycloak.admin.client.resource.UserResource;
+import org.keycloak.admin.client.resource.UsersResource;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
+import org.keycloak.testsuite.ProfileAssume;
+import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
+import org.keycloak.testsuite.adapter.page.PhotozClientAuthzTestApp;
+import org.keycloak.util.JsonSerialization;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public abstract class AbstractPhotozExampleLazyLoadPathsAdapterTest extends AbstractPhotozExampleAdapterTest {
+
+    @Deployment(name = PhotozClientAuthzTestApp.DEPLOYMENT_NAME)
+    public static WebArchive deploymentClient() throws IOException {
+        return exampleDeployment(PhotozClientAuthzTestApp.DEPLOYMENT_NAME);
+    }
+
+    @Deployment(name = RESOURCE_SERVER_ID, managed = false, testable = false)
+    public static WebArchive deploymentResourceServer() throws IOException {
+        return exampleDeployment(RESOURCE_SERVER_ID)
+                .addAsWebInfResource(new File(TEST_APPS_HOME_DIR + "/photoz/keycloak-lazy-load-path-authz-service.json"), "keycloak.json");
+    }
+
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzAdapterTest.java
index f6a8bb2..71297a9 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzAdapterTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * Copyright 2018 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");
@@ -16,196 +16,19 @@
  */
 package org.keycloak.testsuite.adapter.example.authorization;
 
-import static org.junit.Assert.assertFalse;
-import static org.keycloak.testsuite.util.IOUtil.loadJson;
-import static org.keycloak.testsuite.util.IOUtil.loadRealm;
-import static org.keycloak.testsuite.util.WaitUtils.pause;
+import java.io.IOException;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.List;
-
-import javax.ws.rs.core.Response;
-
-import org.jboss.arquillian.container.test.api.Deployer;
-import org.jboss.arquillian.test.api.ArquillianResource;
-import org.junit.BeforeClass;
-import org.keycloak.admin.client.resource.AuthorizationResource;
-import org.keycloak.admin.client.resource.ClientResource;
-import org.keycloak.admin.client.resource.ClientsResource;
-import org.keycloak.representations.idm.ClientRepresentation;
-import org.keycloak.representations.idm.RealmRepresentation;
-import org.keycloak.representations.idm.authorization.PolicyRepresentation;
-import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
-import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
-import org.keycloak.testsuite.ProfileAssume;
-import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
-import org.keycloak.testsuite.util.WaitUtils;
-import org.openqa.selenium.By;
-import org.openqa.selenium.WebElement;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
-public abstract class AbstractServletAuthzAdapterTest extends AbstractExampleAdapterTest {
-
-    protected static final String REALM_NAME = "servlet-authz";
-    protected static final String RESOURCE_SERVER_ID = "servlet-authz-app";
-
-    @BeforeClass
-    public static void enabled() { ProfileAssume.assumePreview(); }
-
-    @ArquillianResource
-    private Deployer deployer;
-
-    @Override
-    public void addAdapterTestRealms(List<RealmRepresentation> testRealms) {
-        testRealms.add(
-                loadRealm(new File(TEST_APPS_HOME_DIR + "/servlet-authz-app/servlet-authz-realm.json")));
-    }
-
-    protected void performTests(ExceptionRunnable assertion) {
-        performTests(() -> importResourceServerSettings(), assertion);
-    }
-
-    protected void performTests(ExceptionRunnable beforeDeploy, ExceptionRunnable assertion) {
-        try {
-            beforeDeploy.run();
-            deployer.deploy(RESOURCE_SERVER_ID);
-            assertion.run();
-        } catch (FileNotFoundException cause) {
-            throw new RuntimeException("Failed to import authorization settings", cause);
-        } catch (Exception cause) {
-            throw new RuntimeException("Error while executing tests", cause);
-        } finally {
-            deployer.undeploy(RESOURCE_SERVER_ID);
-        }
-    }
-
-    protected boolean hasLink(String text) {
-        return getLink(text) != null;
-    }
-
-    protected boolean hasText(String text) {
-        return this.driver.getPageSource().contains(text);
-    }
-
-    private WebElement getLink(String text) {
-        return this.driver.findElement(By.xpath("//a[text() = '" + text + "']"));
-    }
-
-    protected void importResourceServerSettings() throws FileNotFoundException {
-        getAuthorizationResource().importSettings(loadJson(new FileInputStream(new File(TEST_APPS_HOME_DIR + "/servlet-authz-app/servlet-authz-app-authz-service.json")), ResourceServerRepresentation.class));
-    }
-
-    protected AuthorizationResource getAuthorizationResource() {
-        return getClientResource(RESOURCE_SERVER_ID).authorization();
-    }
-
-    protected ClientResource getClientResource(String clientId) {
-        ClientsResource clients = this.realmsResouce().realm(REALM_NAME).clients();
-        ClientRepresentation resourceServer = clients.findByClientId(clientId).get(0);
-        return clients.get(resourceServer.getId());
-    }
-
-    private void logOut() {
-        navigateTo();
-        By by = By.xpath("//a[text() = 'Sign Out']");
-        WaitUtils.waitUntilElement(by);
-        this.driver.findElement(by).click();
-        pause(500);
-    }
-
-    protected void login(String username, String password) {
-        try {
-            navigateTo();
-            Thread.sleep(2000);
-            if (this.driver.getCurrentUrl().startsWith(getResourceServerUrl().toString())) {
-                Thread.sleep(2000);
-                logOut();
-                navigateTo();
-            }
+public abstract class AbstractServletAuthzAdapterTest extends AbstractServletAuthzFunctionalAdapterTest {
 
-            Thread.sleep(2000);
-
-            this.loginPage.form().login(username, password);
-        } catch (Exception cause) {
-            throw new RuntimeException("Login failed", cause);
-        }
-    }
-
-    private void navigateTo() {
-        this.driver.navigate().to(getResourceServerUrl());
-        WaitUtils.waitUntilElement(By.xpath("//a[text() = 'Dynamic Menu']"));
-    }
-
-    protected boolean wasDenied() {
-        return this.driver.getPageSource().contains("You can not access this resource.");
-    }
-
-    protected URL getResourceServerUrl() {
-        try {
-            return new URL(this.appServerContextRootPage + "/" + RESOURCE_SERVER_ID);
-        } catch (MalformedURLException e) {
-            throw new RuntimeException("Could not obtain resource server url.", e);
-        }
-    }
-
-    protected void navigateToDynamicMenuPage() {
-        navigateTo();
-        getLink("Dynamic Menu").click();
-    }
-
-    protected void navigateToUserPremiumPage() {
-        navigateTo();
-        getLink("User Premium").click();
-    }
-
-    protected void navigateToAdminPage() {
-        navigateTo();
-        getLink("Administration").click();
+    @Deployment(name = RESOURCE_SERVER_ID, managed = false)
+    public static WebArchive deployment() throws IOException {
+        return exampleDeployment(RESOURCE_SERVER_ID);
     }
 
-    protected void updatePermissionPolicies(String permissionName, String... policyNames) {
-        for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) {
-            if (permissionName.equalsIgnoreCase(policy.getName())) {
-                StringBuilder policies = new StringBuilder("[");
-
-                for (String policyName : policyNames) {
-                    if (policies.length() > 1) {
-                        policies.append(",");
-                    }
-                    policies.append("\"").append(policyName).append("\"");
-
-                }
-
-                policies.append("]");
-
-                policy.getConfig().put("applyPolicies", policies.toString());
-                getAuthorizationResource().policies().policy(policy.getId()).update(policy);
-            }
-        }
-    }
-
-    protected void createUserPolicy(String name, String... userNames) {
-        UserPolicyRepresentation policy = new UserPolicyRepresentation();
-
-        policy.setName(name);
-
-        for (String userName : userNames) {
-            policy.addUser(userName);
-        }
-
-        assertFalse(policy.getUsers().isEmpty());
-
-        Response response = getAuthorizationResource().policies().user().create(policy);
-        response.close();
-    }
-
-    protected interface ExceptionRunnable {
-        void run() throws Exception;
-    }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzFunctionalAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzFunctionalAdapterTest.java
index 63852d0..35c936b 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzFunctionalAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzFunctionalAdapterTest.java
@@ -19,14 +19,11 @@ package org.keycloak.testsuite.adapter.example.authorization;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import java.io.IOException;
 import java.util.Arrays;
 import java.util.List;
 
 import javax.ws.rs.core.Response;
 
-import org.jboss.arquillian.container.test.api.Deployment;
-import org.jboss.shrinkwrap.api.spec.WebArchive;
 import org.junit.Test;
 import org.keycloak.admin.client.resource.ClientPoliciesResource;
 import org.keycloak.admin.client.resource.RealmResource;
@@ -46,12 +43,7 @@ import org.keycloak.testsuite.util.WaitUtils;
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
-public abstract class AbstractServletAuthzFunctionalAdapterTest extends AbstractServletAuthzAdapterTest {
-
-    @Deployment(name = RESOURCE_SERVER_ID, managed = false)
-    public static WebArchive deployment() throws IOException {
-        return exampleDeployment(RESOURCE_SERVER_ID);
-    }
+public abstract class AbstractServletAuthzFunctionalAdapterTest extends AbstractBaseServletAuthzAdapterTest {
 
     @Test
     public void testCanNotAccessWhenEnforcing() throws Exception {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzLazyLoadPathsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzLazyLoadPathsAdapterTest.java
new file mode 100644
index 0000000..0989aa5
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzLazyLoadPathsAdapterTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.adapter.example.authorization;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.core.Response;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.ClientPoliciesResource;
+import org.keycloak.admin.client.resource.RealmResource;
+import org.keycloak.admin.client.resource.ResourcesResource;
+import org.keycloak.admin.client.resource.RolePoliciesResource;
+import org.keycloak.admin.client.resource.RoleScopeResource;
+import org.keycloak.admin.client.resource.RolesResource;
+import org.keycloak.admin.client.resource.UserResource;
+import org.keycloak.admin.client.resource.UsersResource;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
+import org.keycloak.testsuite.util.WaitUtils;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public abstract class AbstractServletAuthzLazyLoadPathsAdapterTest extends AbstractServletAuthzFunctionalAdapterTest {
+
+    @Deployment(name = RESOURCE_SERVER_ID, managed = false)
+    public static WebArchive deployment() throws IOException {
+        return exampleDeployment(RESOURCE_SERVER_ID)
+                .addAsWebInfResource(new File(TEST_APPS_HOME_DIR + "/servlet-authz-app/keycloak-lazy-load-authz-service.json"), "keycloak.json");
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletPolicyEnforcerTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletPolicyEnforcerTest.java
index 5c6b0eb..9afed01 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletPolicyEnforcerTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletPolicyEnforcerTest.java
@@ -177,21 +177,21 @@ public abstract class AbstractServletPolicyEnforcerTest extends AbstractExampleA
         performTests(() -> {
             login("alice", "alice");
 
-            navigateTo("/resource/a/resource-d");
+            navigateTo("/a/a/resource-d");
             assertFalse(wasDenied());
             navigateTo("/resource/b/resource-d");
             assertFalse(wasDenied());
 
             updatePermissionPolicies("Pattern 5 Permission", "Deny Policy");
             login("alice", "alice");
-            navigateTo("/resource/a/resource-d");
+            navigateTo("/a/a/resource-d");
             assertTrue(wasDenied());
-            navigateTo("/resource/b/resource-d");
+            navigateTo("/a/b/resource-d");
             assertTrue(wasDenied());
 
             updatePermissionPolicies("Pattern 5 Permission", "Default Policy");
             login("alice", "alice");
-            navigateTo("/resource/b/resource-d");
+            navigateTo("/a/b/resource-d");
             assertFalse(wasDenied());
         });
     }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ProvidersTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ProvidersTest.java
index 2642a11..e5d9471 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ProvidersTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ProvidersTest.java
@@ -146,7 +146,7 @@ public class ProvidersTest extends AbstractAuthenticationTest {
                 "Validates a username and password from login form.");
         addProviderInfo(result, "auth-x509-client-username-form", "X509/Validate Username Form",
                 "Validates username and password from X509 client certificate received as a part of mutual SSL handshake.");
-        addProviderInfo(result, "cli-username-password", "Username Password Challenge",
+        addProviderInfo(result, "console-username-password", "Username Password Challenge",
                 "Proprietary challenge protocol for CLI clients that queries for username password");
         addProviderInfo(result, "direct-grant-auth-x509-username", "X509/Validate Username",
                 "Validates username and password from X509 client certificate received as a part of mutual SSL handshake.");
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourceManagementWithAuthzClientTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourceManagementWithAuthzClientTest.java
index 32865ec..bbff687 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourceManagementWithAuthzClientTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourceManagementWithAuthzClientTest.java
@@ -17,12 +17,17 @@
 
 package org.keycloak.testsuite.admin.client.authorization;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
 import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
 import java.util.stream.Collectors;
 
+import org.junit.Test;
 import org.keycloak.authorization.client.AuthzClient;
 import org.keycloak.authorization.client.Configuration;
-import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation;
 import org.keycloak.representations.idm.authorization.ResourceRepresentation;
 import org.keycloak.representations.idm.authorization.ScopeRepresentation;
 import org.keycloak.util.JsonSerialization;
@@ -35,12 +40,60 @@ public class ResourceManagementWithAuthzClientTest extends ResourceManagementTes
 
     private AuthzClient authzClient;
 
+    @Test
+    public void testFindMatchingUri() {
+        doCreateResource(new ResourceRepresentation("/*", Collections.emptySet(), "/*", null));
+        doCreateResource(new ResourceRepresentation("/resources/*", Collections.emptySet(), "/resources/*", null));
+        doCreateResource(new ResourceRepresentation("/resources/{pattern}/*", Collections.emptySet(), "/resources/{pattern}/*", null));
+        doCreateResource(new ResourceRepresentation("/resources/{pattern}/{pattern}/*", Collections.emptySet(), "/resources/{pattern}/{pattern}/*", null));
+        doCreateResource(new ResourceRepresentation("/resources/{pattern}/sub-resources/{pattern}/*", Collections.emptySet(), "/resources/{pattern}/sub-resources/{pattern}/*", null));
+        doCreateResource(new ResourceRepresentation("/resources/{pattern}/sub-resource", Collections.emptySet(), "/resources/{pattern}/sub-resources/{pattern}/*", null));
+
+        AuthzClient authzClient = getAuthzClient();
+
+        List<ResourceRepresentation> resources = authzClient.protection().resource().findByMatchingUri("/test");
+
+        assertNotNull(resources);
+        assertEquals(1, resources.size());
+        assertEquals("/*", resources.get(0).getUri());
+
+        resources = authzClient.protection().resource().findByMatchingUri("/resources/test");
+
+        assertNotNull(resources);
+        assertEquals(1, resources.size());
+        assertEquals("/resources/*", resources.get(0).getUri());
+
+        resources = authzClient.protection().resource().findByMatchingUri("/resources");
+
+        assertNotNull(resources);
+        assertEquals(1, resources.size());
+        assertEquals("/resources/*", resources.get(0).getUri());
+
+        resources = authzClient.protection().resource().findByMatchingUri("/resources/a/b");
+
+        assertNotNull(resources);
+        assertEquals(1, resources.size());
+        assertEquals("/resources/{pattern}/*", resources.get(0).getUri());
+
+        resources = authzClient.protection().resource().findByMatchingUri("/resources/a/b/c");
+
+        assertNotNull(resources);
+        assertEquals(1, resources.size());
+        assertEquals("/resources/{pattern}/{pattern}/*", resources.get(0).getUri());
+
+        resources = authzClient.protection().resource().findByMatchingUri("/resources/a/sub-resources/c/d");
+
+        assertNotNull(resources);
+        assertEquals(1, resources.size());
+        assertEquals("/resources/{pattern}/sub-resources/{pattern}/*", resources.get(0).getUri());
+    }
+
     @Override
     protected ResourceRepresentation doCreateResource(ResourceRepresentation newResource) {
-        org.keycloak.authorization.client.representation.ResourceRepresentation resource = toResourceRepresentation(newResource);
+        ResourceRepresentation resource = toResourceRepresentation(newResource);
 
         AuthzClient authzClient = getAuthzClient();
-        org.keycloak.authorization.client.representation.ResourceRepresentation response = authzClient.protection().resource().create(resource);
+        ResourceRepresentation response = authzClient.protection().resource().create(resource);
 
         return toResourceRepresentation(authzClient, response.getId());
     }
@@ -60,7 +113,7 @@ public class ResourceManagementWithAuthzClientTest extends ResourceManagementTes
     }
 
     private ResourceRepresentation toResourceRepresentation(AuthzClient authzClient, String id) {
-        org.keycloak.authorization.client.representation.ResourceRepresentation created = authzClient.protection().resource().findById(id);
+        ResourceRepresentation created = authzClient.protection().resource().findById(id);
         ResourceRepresentation resourceRepresentation = new ResourceRepresentation();
 
         resourceRepresentation.setId(created.getId());
@@ -68,11 +121,7 @@ public class ResourceManagementWithAuthzClientTest extends ResourceManagementTes
         resourceRepresentation.setIconUri(created.getIconUri());
         resourceRepresentation.setUri(created.getUri());
         resourceRepresentation.setType(created.getType());
-        ResourceOwnerRepresentation owner = new ResourceOwnerRepresentation();
-
-        owner.setId(created.getOwner());
-
-        resourceRepresentation.setOwner(owner);
+        resourceRepresentation.setOwner(created.getOwner());
         resourceRepresentation.setScopes(created.getScopes().stream().map(scopeRepresentation -> {
             ScopeRepresentation scope = new ScopeRepresentation();
 
@@ -88,8 +137,8 @@ public class ResourceManagementWithAuthzClientTest extends ResourceManagementTes
         return resourceRepresentation;
     }
 
-    private org.keycloak.authorization.client.representation.ResourceRepresentation toResourceRepresentation(ResourceRepresentation newResource) {
-        org.keycloak.authorization.client.representation.ResourceRepresentation resource = new org.keycloak.authorization.client.representation.ResourceRepresentation();
+    private ResourceRepresentation toResourceRepresentation(ResourceRepresentation newResource) {
+        ResourceRepresentation resource = new ResourceRepresentation();
 
         resource.setId(newResource.getId());
         resource.setName(newResource.getName());
@@ -102,7 +151,7 @@ public class ResourceManagementWithAuthzClientTest extends ResourceManagementTes
         }
 
         resource.setScopes(newResource.getScopes().stream().map(scopeRepresentation -> {
-            org.keycloak.authorization.client.representation.ScopeRepresentation scope = new org.keycloak.authorization.client.representation.ScopeRepresentation();
+            ScopeRepresentation scope = new ScopeRepresentation();
 
             scope.setName(scopeRepresentation.getName());
             scope.setIconUri(scopeRepresentation.getIconUri());
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/IllegalAdminUpgradeTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/IllegalAdminUpgradeTest.java
index 3318a6d..e155cb5 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/IllegalAdminUpgradeTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/IllegalAdminUpgradeTest.java
@@ -21,6 +21,7 @@ import org.jboss.shrinkwrap.api.spec.WebArchive;
 import org.junit.Assert;
 import org.junit.Test;
 import org.keycloak.admin.client.Keycloak;
+import org.keycloak.admin.client.resource.ClientResource;
 import org.keycloak.authorization.model.Policy;
 import org.keycloak.authorization.model.ResourceServer;
 import org.keycloak.models.AdminRoles;
@@ -115,6 +116,12 @@ public class IllegalAdminUpgradeTest extends AbstractKeycloakTest {
         session.userCredentialManager().updateCredential(realm, user, UserCredentialModel.password("password"));
     }
 
+    //@Test
+    public void testConsole() throws Exception {
+        testingClient.server().run(IllegalAdminUpgradeTest::setupUsers);
+        Thread.sleep(10000000);
+    }
+
     @Test
     public void testRestEvaluation() throws Exception {
         testingClient.server().run(IllegalAdminUpgradeTest::setupUsers);
@@ -141,6 +148,7 @@ public class IllegalAdminUpgradeTest extends AbstractKeycloakTest {
         RoleRepresentation realmQueryUsers = adminClient.realm(TEST).clients().get(realmAdminClient.getId()).roles().get(AdminRoles.QUERY_USERS).toRepresentation();
         RoleRepresentation realmQueryClients = adminClient.realm(TEST).clients().get(realmAdminClient.getId()).roles().get(AdminRoles.QUERY_CLIENTS).toRepresentation();
         RoleRepresentation realmQueryGroups = adminClient.realm(TEST).clients().get(realmAdminClient.getId()).roles().get(AdminRoles.QUERY_GROUPS).toRepresentation();
+        RoleRepresentation realmAdmin = adminClient.realm(TEST).clients().get(realmAdminClient.getId()).roles().get(AdminRoles.REALM_ADMIN).toRepresentation();
 
         ClientRepresentation masterClient = adminClient.realm("master").clients().findByClientId(TEST + "-realm").get(0);
         RoleRepresentation masterManageAuthorization = adminClient.realm("master").clients().get(masterClient.getId()).roles().get(AdminRoles.MANAGE_AUTHORIZATION).toRepresentation();
@@ -187,6 +195,168 @@ public class IllegalAdminUpgradeTest extends AbstractKeycloakTest {
             }
 
             roles.clear();
+            roles.add(realmAdmin);
+            try {
+                realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+                Assert.fail("should fail with forbidden exception");
+            } catch (ClientErrorException e) {
+                Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+            }
+
+            roles.clear();
+            roles.add(realmManageClients);
+            try {
+                realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+                Assert.fail("should fail with forbidden exception");
+            } catch (ClientErrorException e) {
+                Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+            }
+
+            roles.clear();
+            roles.add(realmViewClients);
+            try {
+                realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+                Assert.fail("should fail with forbidden exception");
+            } catch (ClientErrorException e) {
+                Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+            }
+
+            roles.clear();
+            roles.add(realmManageEvents);
+            try {
+                realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+                Assert.fail("should fail with forbidden exception");
+            } catch (ClientErrorException e) {
+                Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+            }
+
+            roles.clear();
+            roles.add(realmViewEvents);
+            try {
+                realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+                Assert.fail("should fail with forbidden exception");
+            } catch (ClientErrorException e) {
+                Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+            }
+
+            roles.clear();
+            roles.add(realmManageIdentityProviders);
+            try {
+                realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+                Assert.fail("should fail with forbidden exception");
+            } catch (ClientErrorException e) {
+                Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+            }
+
+            roles.clear();
+            roles.add(realmViewIdentityProviders);
+            try {
+                realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+                Assert.fail("should fail with forbidden exception");
+            } catch (ClientErrorException e) {
+                Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+            }
+
+            roles.clear();
+            roles.add(realmManageRealm);
+            try {
+                realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+                Assert.fail("should fail with forbidden exception");
+            } catch (ClientErrorException e) {
+                Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+            }
+
+            roles.clear();
+            roles.add(realmViewRealm);
+            try {
+                realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+                Assert.fail("should fail with forbidden exception");
+            } catch (ClientErrorException e) {
+                Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+            }
+
+            roles.clear();
+            roles.add(realmImpersonate);
+            try {
+                realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+                Assert.fail("should fail with forbidden exception");
+            } catch (ClientErrorException e) {
+                Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+            }
+
+            roles.clear();
+            roles.add(realmManageUsers);
+            realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+            realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+            roles.clear();
+            roles.add(realmViewUsers);
+            realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+            realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+            roles.clear();
+            roles.add(realmQueryUsers);
+            realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+            realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+            roles.clear();
+            roles.add(realmQueryGroups);
+            realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+            realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+            roles.clear();
+            roles.add(realmQueryClients);
+            realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+            realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+            realmClient.close();
+        }
+        // test master user with manage_users can't assign realm's admin roles
+        {
+            ClientRepresentation client = realmAdminClient;
+            Keycloak realmClient = AdminClientUtil.createAdminClient(suiteContext.isAdapterCompatTesting(),
+                    "master", "userAdmin", "password", Constants.ADMIN_CLI_CLIENT_ID, null);
+            roles.clear();
+            roles.add(realmManageAuthorization);
+            try {
+                realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+                Assert.fail("should fail with forbidden exception");
+            } catch (ClientErrorException e) {
+                Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+            }
+
+            roles.clear();
+            roles.add(realmViewAuthorization);
+            try {
+                realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+                Assert.fail("should fail with forbidden exception");
+            } catch (ClientErrorException e) {
+                Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+            }
+
+            roles.clear();
+            roles.add(realmAdmin);
+            try {
+                realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+                Assert.fail("should fail with forbidden exception");
+            } catch (ClientErrorException e) {
+                Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+            }
+
+            roles.clear();
             roles.add(realmManageClients);
             try {
                 realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthzClientCredentialsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthzClientCredentialsTest.java
index 7fece47..63726b5 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthzClientCredentialsTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthzClientCredentialsTest.java
@@ -42,7 +42,6 @@ import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator;
 import org.keycloak.authorization.client.AuthzClient;
 import org.keycloak.authorization.client.ClientAuthenticator;
 import org.keycloak.authorization.client.Configuration;
-import org.keycloak.authorization.client.representation.ResourceRepresentation;
 import org.keycloak.authorization.client.resource.ProtectionResource;
 import org.keycloak.authorization.client.util.HttpResponseException;
 import org.keycloak.jose.jws.JWSInput;
@@ -56,6 +55,7 @@ import org.keycloak.representations.idm.authorization.AuthorizationResponse;
 import org.keycloak.representations.idm.authorization.Permission;
 import org.keycloak.representations.idm.authorization.PermissionRequest;
 import org.keycloak.representations.idm.authorization.PermissionResponse;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
 import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
 import org.keycloak.testsuite.util.ClientBuilder;
 import org.keycloak.testsuite.util.RealmBuilder;
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/ConflictingScopePermissionTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/ConflictingScopePermissionTest.java
index b55f348..7393a94 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/ConflictingScopePermissionTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/ConflictingScopePermissionTest.java
@@ -38,8 +38,6 @@ import org.keycloak.admin.client.resource.ClientsResource;
 import org.keycloak.admin.client.resource.RealmResource;
 import org.keycloak.authorization.client.AuthzClient;
 import org.keycloak.authorization.client.Configuration;
-import org.keycloak.authorization.client.representation.ResourceRepresentation;
-import org.keycloak.authorization.client.representation.ScopeRepresentation;
 import org.keycloak.jose.jws.JWSInput;
 import org.keycloak.jose.jws.JWSInputException;
 import org.keycloak.representations.AccessToken;
@@ -48,7 +46,9 @@ import org.keycloak.representations.idm.authorization.AuthorizationResponse;
 import org.keycloak.representations.idm.authorization.Permission;
 import org.keycloak.representations.idm.authorization.PolicyRepresentation;
 import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
 import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
 import org.keycloak.testsuite.util.ClientBuilder;
 import org.keycloak.testsuite.util.RealmBuilder;
 import org.keycloak.testsuite.util.UserBuilder;
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/KcinitTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/KcinitTest.java
new file mode 100644
index 0000000..f23a63c
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/KcinitTest.java
@@ -0,0 +1,564 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.cli;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.graphene.page.Page;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.UserResource;
+import org.keycloak.authentication.authenticators.console.ConsoleUsernamePasswordAuthenticatorFactory;
+import org.keycloak.authentication.requiredactions.TermsAndConditions;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.credential.CredentialModel;
+import org.keycloak.models.*;
+import org.keycloak.models.utils.TimeBasedOTP;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
+import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionManagement;
+import org.keycloak.services.resources.admin.permissions.AdminPermissions;
+import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
+import org.keycloak.testsuite.AssertEvents;
+import org.keycloak.testsuite.forms.PassThroughAuthenticator;
+import org.keycloak.testsuite.pages.AppPage;
+import org.keycloak.testsuite.pages.ErrorPage;
+import org.keycloak.testsuite.pages.LoginPage;
+import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
+import org.keycloak.testsuite.util.GreenMailRule;
+import org.keycloak.testsuite.util.MailUtils;
+import org.keycloak.testsuite.util.OAuthClient;
+import org.keycloak.util.JsonSerialization;
+import org.keycloak.utils.TotpUtils;
+
+import javax.mail.internet.MimeMessage;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Test that clients can override auth flows
+ *
+ * @author <a href="mailto:bburke@redhat.com">Bill Burke</a>
+ */
+public class KcinitTest extends AbstractTestRealmKeycloakTest {
+
+    public static final String KCINIT_CLIENT = "kcinit";
+    public static final String APP = "app";
+    public static final String UNAUTHORIZED_APP = "unauthorized_app";
+    @Rule
+    public AssertEvents events = new AssertEvents(this);
+
+    @Override
+    public void configureTestRealm(RealmRepresentation testRealm) {
+    }
+
+    @Deployment
+    public static WebArchive deploy() {
+        return RunOnServerDeployment.create(UserResource.class)
+                .addPackages(true, "org.keycloak.testsuite");
+    }
+
+
+    @Before
+    public void setupFlows() {
+        RequiredActionProviderRepresentation rep = adminClient.realm("test").flows().getRequiredAction("terms_and_conditions");
+        rep.setEnabled(true);
+        adminClient.realm("test").flows().updateRequiredAction("terms_and_conditions", rep);
+
+        testingClient.server().run(session -> {
+            RealmModel realm = session.realms().getRealmByName("test");
+
+            ClientModel client = session.realms().getClientByClientId("kcinit", realm);
+            if (client != null) {
+                return;
+            }
+
+            ClientModel kcinit = realm.addClient(KCINIT_CLIENT);
+            kcinit.setSecret("password");
+            kcinit.setEnabled(true);
+            kcinit.addRedirectUri("urn:ietf:wg:oauth:2.0:oob");
+            kcinit.setPublicClient(false);
+
+            ClientModel app = realm.addClient(APP);
+            app.setSecret("password");
+            app.setEnabled(true);
+            app.setPublicClient(false);
+
+            ClientModel unauthorizedApp = realm.addClient(UNAUTHORIZED_APP);
+            unauthorizedApp.setSecret("password");
+            unauthorizedApp.setEnabled(true);
+            unauthorizedApp.setPublicClient(false);
+
+            // permission for client to client exchange to "target" client
+            AdminPermissionManagement management = AdminPermissions.management(session, realm);
+            management.clients().setPermissionsEnabled(app, true);
+            ClientPolicyRepresentation clientRep = new ClientPolicyRepresentation();
+            clientRep.setName("to");
+            clientRep.addClient(kcinit.getId());
+            ResourceServer server = management.realmResourceServer();
+            Policy clientPolicy = management.authz().getStoreFactory().getPolicyStore().create(clientRep, server);
+            management.clients().exchangeToPermission(app).addAssociatedPolicy(clientPolicy);
+            PasswordPolicy policy = realm.getPasswordPolicy();
+            policy = PasswordPolicy.parse(session, "hashIterations(1)");
+            realm.setPasswordPolicy(policy);
+
+            UserModel user = session.users().addUser(realm, "bburke");
+            session.userCredentialManager().updateCredential(realm, user, UserCredentialModel.password("password"));
+            user.setEnabled(true);
+            user.setEmail("p@p.com");
+            user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
+            user.addRequiredAction(UserModel.RequiredAction.CONFIGURE_TOTP);
+            user.addRequiredAction(UserModel.RequiredAction.VERIFY_EMAIL);
+            user.addRequiredAction(TermsAndConditions.PROVIDER_ID);
+            user.addRequiredAction(UserModel.RequiredAction.UPDATE_PROFILE);
+
+            user = session.users().addUser(realm, "wburke");
+            session.userCredentialManager().updateCredential(realm, user, UserCredentialModel.password("password"));
+            user.setEnabled(true);
+            user = session.users().addUser(realm, "tbrady");
+            session.userCredentialManager().updateCredential(realm, user, UserCredentialModel.password("password"));
+            user.setEnabled(true);
+            user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
+
+            // Parent flow
+            AuthenticationFlowModel browser = new AuthenticationFlowModel();
+            browser.setAlias("no-console-flow");
+            browser.setDescription("browser based authentication");
+            browser.setProviderId("basic-flow");
+            browser.setTopLevel(true);
+            browser.setBuiltIn(true);
+            browser = realm.addAuthenticationFlow(browser);
+
+            AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
+            execution.setParentFlow(browser.getId());
+            execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
+            execution.setPriority(20);
+            execution.setAuthenticator(PassThroughAuthenticator.PROVIDER_ID);
+            realm.addAuthenticatorExecution(execution);
+
+        });
+    }
+
+    //@Test
+    public void testDemo() throws Exception {
+        testingClient.server().run(session -> {
+            RealmModel realm = session.realms().getRealmByName("test");
+            Map<String, String> smtp = new HashMap<>();
+            smtp.put("host", "smtp.gmail.com");
+            smtp.put("port", "465");
+            smtp.put("fromDisplayName", "Keycloak SSO");
+            smtp.put("from", "****");
+            smtp.put("replyToDisplayName", "Keycloak no-reply");
+            smtp.put("replyTo", "reply-to@keycloak.org");
+            smtp.put("ssl", "true");
+            smtp.put("auth", "true");
+            smtp.put("user", "*****");
+            smtp.put("password", "****");
+            realm.setSmtpConfig(smtp);
+
+        });
+
+        Thread.sleep(100000000);
+    }
+
+    @Test
+    public void testBrowserRequired() throws Exception {
+        // that that a browser require challenge is sent back if authentication flow doesn't support console display mode
+        testingClient.server().run(session -> {
+            RealmModel realm = session.realms().getRealmByName("test");
+            ClientModel kcinit = realm.getClientByClientId(KCINIT_CLIENT);
+            AuthenticationFlowModel flow = realm.getFlowByAlias("no-console-flow");
+            kcinit.setAuthenticationFlowBindingOverride(AuthenticationFlowBindings.BROWSER_BINDING, flow.getId());
+
+
+        });
+
+        testInstall();
+        // login
+        //System.out.println("login....");
+        KcinitExec exe = KcinitExec.newBuilder()
+                .argsLine("login")
+                .executeAsync();
+        exe.waitCompletion();
+        Assert.assertEquals(1, exe.exitCode());
+        Assert.assertTrue(exe.stderrString().contains("Browser required to login"));
+        //Assert.assertEquals("stderr first line", "Browser required to login", exe.stderrLines().get(1));
+
+
+        testingClient.server().run(session -> {
+            RealmModel realm = session.realms().getRealmByName("test");
+            ClientModel kcinit = realm.getClientByClientId(KCINIT_CLIENT);
+            kcinit.removeAuthenticationFlowBindingOverride(AuthenticationFlowBindings.BROWSER_BINDING);
+
+
+        });
+    }
+
+
+    @Test
+    public void testBadCommand() throws Exception {
+        KcinitExec exe = KcinitExec.execute("covfefe");
+        Assert.assertEquals(1, exe.exitCode());
+        Assert.assertEquals("stderr first line", "Error: unknown command \"covfefe\" for \"kcinit\"", exe.stderrLines().get(0));
+    }
+
+    //@Test
+    public void testInstall() throws Exception {
+        KcinitExec exe = KcinitExec.execute("uninstall");
+        Assert.assertEquals(0, exe.exitCode());
+
+        exe = KcinitExec.newBuilder()
+                .argsLine("install")
+                .executeAsync();
+        //System.out.println(exe.stderrString());
+        //exe.waitForStderr("(y/n):");
+        //exe.sendLine("n");
+        exe.waitForStderr("Authentication server URL [http://localhost:8080/auth]:");
+        exe.sendLine(OAuthClient.AUTH_SERVER_ROOT);
+        //System.out.println(exe.stderrString());
+        exe.waitForStderr("Name of realm [master]:");
+        exe.sendLine("test");
+        //System.out.println(exe.stderrString());
+        exe.waitForStderr("client id [kcinit]:");
+        exe.sendLine("");
+        //System.out.println(exe.stderrString());
+        exe.waitForStderr("Client secret [none]:");
+        exe.sendLine("password");
+        //System.out.println(exe.stderrString());
+        exe.waitCompletion();
+        Assert.assertEquals(0, exe.exitCode());
+    }
+
+    @Test
+    public void testBasic() throws Exception {
+        testInstall();
+        // login
+        //System.out.println("login....");
+        KcinitExec exe = KcinitExec.newBuilder()
+                .argsLine("login")
+                .executeAsync();
+        //System.out.println(exe.stderrString());
+        exe.waitForStderr("Username:");
+        exe.sendLine("wburke");
+        //System.out.println(exe.stderrString());
+        exe.waitForStderr("Password:");
+        exe.sendLine("password");
+        //System.out.println(exe.stderrString());
+        exe.waitForStderr("Login successful");
+        exe.waitCompletion();
+        Assert.assertEquals(0, exe.exitCode());
+        Assert.assertEquals(0, exe.stdoutLines().size());
+
+        exe = KcinitExec.execute("token");
+        Assert.assertEquals(0, exe.exitCode());
+        Assert.assertEquals(1, exe.stdoutLines().size());
+        String token = exe.stdoutLines().get(0).trim();
+        //System.out.println("token: " + token);
+        String introspect = oauth.introspectAccessTokenWithClientCredential("kcinit", "password", token);
+        Map json = JsonSerialization.readValue(introspect, Map.class);
+        Assert.assertTrue(json.containsKey("active"));
+        Assert.assertTrue((Boolean)json.get("active"));
+        //System.out.println("introspect");
+        //System.out.println(introspect);
+
+        exe = KcinitExec.execute("token app");
+        Assert.assertEquals(0, exe.exitCode());
+        Assert.assertEquals(1, exe.stdoutLines().size());
+        String appToken = exe.stdoutLines().get(0).trim();
+        Assert.assertFalse(appToken.equals(token));
+        //System.out.println("token: " + token);
+        introspect = oauth.introspectAccessTokenWithClientCredential("kcinit", "password", appToken);
+        json = JsonSerialization.readValue(introspect, Map.class);
+        Assert.assertTrue(json.containsKey("active"));
+        Assert.assertTrue((Boolean)json.get("active"));
+
+
+        exe = KcinitExec.execute("token badapp");
+        Assert.assertEquals(1, exe.exitCode());
+        Assert.assertEquals(0, exe.stdoutLines().size());
+        Assert.assertEquals(1, exe.stderrLines().size());
+        Assert.assertTrue(exe.stderrLines().get(0), exe.stderrLines().get(0).contains("failed to exchange token: invalid_client Audience not found"));
+
+        exe = KcinitExec.execute("logout");
+        Assert.assertEquals(0, exe.exitCode());
+
+        introspect = oauth.introspectAccessTokenWithClientCredential("kcinit", "password", token);
+        json = JsonSerialization.readValue(introspect, Map.class);
+        Assert.assertTrue(json.containsKey("active"));
+        Assert.assertFalse((Boolean)json.get("active"));
+
+
+
+    }
+
+    @Test
+    public void testTerms() throws Exception {
+        testingClient.server().run(session -> {
+            RealmModel realm = session.realms().getRealmByName("test");
+            UserModel user = session.users().getUserByUsername("wburke", realm);
+            user.addRequiredAction(TermsAndConditions.PROVIDER_ID);
+        });
+
+        testInstall();
+
+        KcinitExec exe = KcinitExec.newBuilder()
+                .argsLine("login")
+                .executeAsync();
+        exe.waitForStderr("Username:");
+        exe.sendLine("wburke");
+        exe.waitForStderr("Password:");
+        exe.sendLine("password");
+        exe.waitForStderr("Accept Terms? [y/n]:");
+        exe.sendLine("y");
+        exe.waitForStderr("Login successful");
+        exe.waitCompletion();
+        Assert.assertEquals(0, exe.exitCode());
+        Assert.assertEquals(0, exe.stdoutLines().size());
+    }
+
+
+    @Test
+    public void testUpdateProfile() throws Exception {
+        // expects that updateProfile is a passthrough
+        testingClient.server().run(session -> {
+            RealmModel realm = session.realms().getRealmByName("test");
+            UserModel user = session.users().getUserByUsername("wburke", realm);
+            user.addRequiredAction(UserModel.RequiredAction.UPDATE_PROFILE);
+        });
+
+        try {
+            testInstall();
+
+            //Thread.sleep(100000000);
+
+            KcinitExec exe = KcinitExec.newBuilder()
+                    .argsLine("login")
+                    .executeAsync();
+            try {
+                exe.waitForStderr("Username:");
+                exe.sendLine("wburke");
+                exe.waitForStderr("Password:");
+                exe.sendLine("password");
+
+                exe.waitForStderr("Login successful");
+                exe.waitCompletion();
+                Assert.assertEquals(0, exe.exitCode());
+                Assert.assertEquals(0, exe.stdoutLines().size());
+            } catch (Exception ex) {
+                System.out.println(exe.stderrString());
+                throw ex;
+            }
+        } finally {
+
+            testingClient.server().run(session -> {
+                RealmModel realm = session.realms().getRealmByName("test");
+                UserModel user = session.users().getUserByUsername("wburke", realm);
+                user.removeRequiredAction(UserModel.RequiredAction.UPDATE_PROFILE);
+            });
+        }
+    }
+
+
+    @Test
+    public void testUpdatePassword() throws Exception {
+        testingClient.server().run(session -> {
+            RealmModel realm = session.realms().getRealmByName("test");
+            UserModel user = session.users().getUserByUsername("wburke", realm);
+            user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
+        });
+
+        try {
+            testInstall();
+
+            KcinitExec exe = KcinitExec.newBuilder()
+                    .argsLine("login")
+                    .executeAsync();
+            exe.waitForStderr("Username:");
+            exe.sendLine("wburke");
+            exe.waitForStderr("Password:");
+            exe.sendLine("password");
+            exe.waitForStderr("New Password:");
+            exe.sendLine("pw");
+            exe.waitForStderr("Confirm Password:");
+            exe.sendLine("pw");
+            exe.waitForStderr("Login successful");
+            exe.waitCompletion();
+            Assert.assertEquals(0, exe.exitCode());
+            Assert.assertEquals(0, exe.stdoutLines().size());
+
+            exe = KcinitExec.newBuilder()
+                    .argsLine("login -f")
+                    .executeAsync();
+            exe.waitForStderr("Username:");
+            exe.sendLine("wburke");
+            exe.waitForStderr("Password:");
+            exe.sendLine("pw");
+            exe.waitForStderr("Login successful");
+            exe.waitCompletion();
+            Assert.assertEquals(0, exe.exitCode());
+            Assert.assertEquals(0, exe.stdoutLines().size());
+
+            exe = KcinitExec.execute("logout");
+            Assert.assertEquals(0, exe.exitCode());
+        } finally {
+
+            testingClient.server().run(session -> {
+                RealmModel realm = session.realms().getRealmByName("test");
+                UserModel user = session.users().getUserByUsername("wburke", realm);
+                session.userCredentialManager().updateCredential(realm, user, UserCredentialModel.password("password"));
+            });
+        }
+
+    }
+
+    protected TimeBasedOTP totp = new TimeBasedOTP();
+
+
+    @Test
+    public void testConfigureTOTP() throws Exception {
+        testingClient.server().run(session -> {
+            RealmModel realm = session.realms().getRealmByName("test");
+            UserModel user = session.users().getUserByUsername("wburke", realm);
+            user.addRequiredAction(UserModel.RequiredAction.CONFIGURE_TOTP);
+        });
+
+        try {
+
+            testInstall();
+
+            KcinitExec exe = KcinitExec.newBuilder()
+                    .argsLine("login")
+                    .executeAsync();
+            exe.waitForStderr("Username:");
+            exe.sendLine("wburke");
+            exe.waitForStderr("Password:");
+            exe.sendLine("password");
+            exe.waitForStderr("One Time Password:");
+
+            Pattern p = Pattern.compile("Open the application and enter the key\\s+(.+)\\s+Use the following configuration values");
+            //Pattern p = Pattern.compile("Open the application and enter the key");
+
+            String stderr = exe.stderrString();
+            //System.out.println("***************");
+            //System.out.println(stderr);
+            //System.out.println("***************");
+            Matcher m = p.matcher(stderr);
+            Assert.assertTrue(m.find());
+            String otpSecret = m.group(1).trim();
+
+            //System.out.println("***************");
+            //System.out.println(otpSecret);
+            //System.out.println("***************");
+
+            otpSecret = TotpUtils.decode(otpSecret);
+            String code = totp.generateTOTP(otpSecret);
+            //System.out.println("***************");
+            //System.out.println("code: " + code);
+            //System.out.println("***************");
+            exe.sendLine(code);
+            Thread.sleep(100);
+            //stderr = exe.stderrString();
+            //System.out.println("***************");
+            //System.out.println(stderr);
+            //System.out.println("***************");
+            exe.waitForStderr("Login successful");
+            exe.waitCompletion();
+            Assert.assertEquals(0, exe.exitCode());
+            Assert.assertEquals(0, exe.stdoutLines().size());
+
+
+            exe = KcinitExec.execute("logout");
+            Assert.assertEquals(0, exe.exitCode());
+
+            exe = KcinitExec.newBuilder()
+                    .argsLine("login")
+                    .executeAsync();
+            exe.waitForStderr("Username:");
+            exe.sendLine("wburke");
+            exe.waitForStderr("Password:");
+            exe.sendLine("password");
+            exe.waitForStderr("One Time Password:");
+            exe.sendLine(totp.generateTOTP(otpSecret));
+            exe.waitForStderr("Login successful");
+            exe.waitCompletion();
+
+            exe = KcinitExec.execute("logout");
+            Assert.assertEquals(0, exe.exitCode());
+        } finally {
+            testingClient.server().run(session -> {
+                RealmModel realm = session.realms().getRealmByName("test");
+                UserModel user = session.users().getUserByUsername("wburke", realm);
+                session.userCredentialManager().disableCredentialType(realm, user, CredentialModel.OTP);
+            });
+        }
+
+
+    }
+
+    @Rule
+    public GreenMailRule greenMail = new GreenMailRule();
+
+    @Test
+    public void testVerifyEmail() throws Exception {
+        testingClient.server().run(session -> {
+            RealmModel realm = session.realms().getRealmByName("test");
+            UserModel user = session.users().getUserByUsername("test-user@localhost", realm);
+            user.addRequiredAction(UserModel.RequiredAction.VERIFY_EMAIL);
+        });
+
+        testInstall();
+
+        KcinitExec exe = KcinitExec.newBuilder()
+                .argsLine("login")
+                .executeAsync();
+        exe.waitForStderr("Username:");
+        exe.sendLine("test-user@localhost");
+        exe.waitForStderr("Password:");
+        exe.sendLine("password");
+        exe.waitForStderr("Email Code:");
+
+        Assert.assertEquals(1, greenMail.getReceivedMessages().length);
+
+        MimeMessage message = greenMail.getReceivedMessages()[0];
+
+        String text = MailUtils.getBody(message).getText();
+        Assert.assertTrue(text.contains("Please verify your email address by entering in the following code."));
+        String code = text.substring("Please verify your email address by entering in the following code.".length()).trim();
+
+        exe.sendLine(code);
+
+        exe.waitForStderr("Login successful");
+        exe.waitCompletion();
+        Assert.assertEquals(0, exe.exitCode());
+        Assert.assertEquals(0, exe.stdoutLines().size());
+
+
+        exe = KcinitExec.execute("logout");
+        Assert.assertEquals(0, exe.exitCode());
+    }
+
+
+
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/ChallengeFlowTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/ChallengeFlowTest.java
index 72024f6..5855979 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/ChallengeFlowTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/ChallengeFlowTest.java
@@ -24,29 +24,21 @@ import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
-import org.keycloak.OAuth2Constants;
-import org.keycloak.admin.client.resource.ClientsResource;
 import org.keycloak.admin.client.resource.UserResource;
-import org.keycloak.authentication.authenticators.browser.UsernamePasswordFormFactory;
-import org.keycloak.authentication.authenticators.cli.CliUsernamePasswordAuthenticatorFactory;
-import org.keycloak.events.Details;
+import org.keycloak.authentication.authenticators.console.ConsoleUsernamePasswordAuthenticatorFactory;
 import org.keycloak.models.AuthenticationExecutionModel;
 import org.keycloak.models.AuthenticationFlowBindings;
 import org.keycloak.models.AuthenticationFlowModel;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.RealmModel;
-import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
 import org.keycloak.testsuite.AssertEvents;
-import org.keycloak.testsuite.authentication.PushButtonAuthenticatorFactory;
 import org.keycloak.testsuite.pages.AppPage;
 import org.keycloak.testsuite.pages.ErrorPage;
 import org.keycloak.testsuite.pages.LoginPage;
 import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
 import org.keycloak.testsuite.util.OAuthClient;
-import org.keycloak.util.BasicAuthHelper;
-import org.openqa.selenium.By;
 
 import javax.ws.rs.client.Client;
 import javax.ws.rs.client.ClientBuilder;
@@ -55,9 +47,6 @@ import javax.ws.rs.client.WebTarget;
 import javax.ws.rs.core.Form;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.Response;
-import javax.ws.rs.core.UriBuilder;
-import java.net.URI;
-import java.net.URL;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.regex.Matcher;
@@ -120,7 +109,7 @@ public class ChallengeFlowTest extends AbstractTestRealmKeycloakTest {
             AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
             execution.setParentFlow(browser.getId());
             execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
-            execution.setAuthenticator(CliUsernamePasswordAuthenticatorFactory.PROVIDER_ID);
+            execution.setAuthenticator(ConsoleUsernamePasswordAuthenticatorFactory.PROVIDER_ID);
             execution.setPriority(10);
             execution.setAuthenticatorFlow(false);
             realm.addAuthenticatorExecution(execution);
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyPhotozExampleAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyPhotozExampleAdapterTest.java
index 42cde44..6139c22 100644
--- a/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyPhotozExampleAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyPhotozExampleAdapterTest.java
@@ -24,6 +24,6 @@ import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
  */
 @AppServerContainer("app-server-wildfly")
 //@AdapterLibsLocationProperty("adapter.libs.wildfly")
-public class WildflyPhotozExampleAdapterTest extends AbstractPhotozExampleAdapterTest {
+public class WildflyPhotozExampleAdapterTest extends AbstractPhotozExampleNoLazyLoadPathsAdapterTest {
 
 }
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyPhotozExampleLazyLoadPathsAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyPhotozExampleLazyLoadPathsAdapterTest.java
new file mode 100644
index 0000000..a23ba15
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyPhotozExampleLazyLoadPathsAdapterTest.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.adapter.example.authorization;
+
+import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
+
+/**
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+@AppServerContainer("app-server-wildfly")
+//@AdapterLibsLocationProperty("adapter.libs.wildfly")
+public class WildflyPhotozExampleLazyLoadPathsAdapterTest extends AbstractPhotozExampleLazyLoadPathsAdapterTest {
+
+}
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyServletAuthzAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyServletAuthzAdapterTest.java
index 13a444f..7a7223d 100644
--- a/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyServletAuthzAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyServletAuthzAdapterTest.java
@@ -26,6 +26,6 @@ import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
 @RunAsClient
 @AppServerContainer("app-server-wildfly")
 //@AdapterLibsLocationProperty("adapter.libs.wildfly")
-public class WildflyServletAuthzAdapterTest extends AbstractServletAuthzFunctionalAdapterTest {
+public class WildflyServletAuthzAdapterTest extends AbstractServletAuthzAdapterTest {
 
 }
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyServletAuthzLazyLoadPathsAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyServletAuthzLazyLoadPathsAdapterTest.java
new file mode 100644
index 0000000..7964736
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyServletAuthzLazyLoadPathsAdapterTest.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.adapter.example.authorization;
+
+import org.jboss.arquillian.container.test.api.RunAsClient;
+import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@RunAsClient
+@AppServerContainer("app-server-wildfly")
+//@AdapterLibsLocationProperty("adapter.libs.wildfly")
+public class WildflyServletAuthzLazyLoadPathsAdapterTest extends AbstractServletAuthzLazyLoadPathsAdapterTest {
+
+}
diff --git a/themes/src/main/resources/theme/base/email/html/email-verification-with-code.ftl b/themes/src/main/resources/theme/base/email/html/email-verification-with-code.ftl
new file mode 100644
index 0000000..b4a01c9
--- /dev/null
+++ b/themes/src/main/resources/theme/base/email/html/email-verification-with-code.ftl
@@ -0,0 +1,5 @@
+<html>
+<body>
+${msg("emailVerificationBodyCodeHtml",code)?no_esc}
+</body>
+</html>
diff --git a/themes/src/main/resources/theme/base/email/messages/messages_en.properties b/themes/src/main/resources/theme/base/email/messages/messages_en.properties
index e04e947..b2fd0c0 100755
--- a/themes/src/main/resources/theme/base/email/messages/messages_en.properties
+++ b/themes/src/main/resources/theme/base/email/messages/messages_en.properties
@@ -45,3 +45,7 @@ linkExpirationFormatter.timePeriodUnit.hours=hours
 linkExpirationFormatter.timePeriodUnit.hours.1=hour
 linkExpirationFormatter.timePeriodUnit.days=days
 linkExpirationFormatter.timePeriodUnit.days.1=day
+
+emailVerificationBodyCode=Please verify your email address by entering in the following code.\n\n{0}\n\n.
+emailVerificationBodyCodeHtml=<p>Please verify your email address by entering in the following code.</p><p><b>{0}</b></p>
+
diff --git a/themes/src/main/resources/theme/base/email/text/email-verification-with-code.ftl b/themes/src/main/resources/theme/base/email/text/email-verification-with-code.ftl
new file mode 100644
index 0000000..4ffb7d8
--- /dev/null
+++ b/themes/src/main/resources/theme/base/email/text/email-verification-with-code.ftl
@@ -0,0 +1,2 @@
+<#ftl output_format="plainText">
+${msg("emailVerificationBodyCode",code)}
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/login/login-config-totp-text.ftl b/themes/src/main/resources/theme/base/login/login-config-totp-text.ftl
new file mode 100755
index 0000000..d609182
--- /dev/null
+++ b/themes/src/main/resources/theme/base/login/login-config-totp-text.ftl
@@ -0,0 +1,31 @@
+<#ftl output_format="plainText">
+${msg("loginTotpIntro")}
+
+${msg("loginTotpStep1")}
+
+<#list totp.policy.supportedApplications as app>
+* ${app}
+</#list>
+
+${msg("loginTotpManualStep2")}
+
+    ${totp.totpSecretEncoded}
+
+
+${msg("loginTotpManualStep3")}
+
+- ${msg("loginTotpType")}: ${msg("loginTotp." + totp.policy.type)}
+- ${msg("loginTotpAlgorithm")}: ${totp.policy.getAlgorithmKey()}
+- ${msg("loginTotpDigits")}: ${totp.policy.digits}
+<#if totp.policy.type = "totp">
+- ${msg("loginTotpInterval")}: ${totp.policy.period}
+
+<#elseif totp.policy.type = "hotp">
+- ${msg("loginTotpCounter")}: ${totp.policy.initialCounter}
+
+</#if>
+
+Enter in your one time password so we can verify you have installed it correctly.
+
+
+
diff --git a/themes/src/main/resources/theme/base/login/login-verify-email-code-text.ftl b/themes/src/main/resources/theme/base/login/login-verify-email-code-text.ftl
new file mode 100644
index 0000000..87abcd7
--- /dev/null
+++ b/themes/src/main/resources/theme/base/login/login-verify-email-code-text.ftl
@@ -0,0 +1,2 @@
+<#ftl output_format="plainText">
+${msg("console-verify-email",email, code)}
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/login/messages/messages_en.properties b/themes/src/main/resources/theme/base/login/messages/messages_en.properties
index d4c12e4..5f4c6a2 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
@@ -34,9 +34,12 @@ emailForgotTitle=Forgot Your Password?
 updatePasswordTitle=Update password
 codeSuccessTitle=Success code
 codeErrorTitle=Error code\: {0}
+displayUnsupported=Requested display type unsupported
+browserRequired=Browser required to login
 
 termsTitle=Terms and Conditions
 termsText=<p>Terms and conditions to be defined</p>
+termsPlainText=Terms and conditions to be defined.
 
 recaptchaFailed=Invalid Recaptcha
 recaptchaNotConfigured=Recaptcha is required, but not configured
@@ -66,6 +69,7 @@ country=Country
 emailVerified=Email verified
 gssDelegationCredential=GSS Delegation Credential
 
+loginTotpIntro=You are required to set up a One Time Password generator to access this account
 loginTotpStep1=Install one of the following applications on your mobile
 loginTotpStep2=Open the application and scan the barcode
 loginTotpStep3=Enter the one-time code provided by the application and click Submit to finish the setup
@@ -278,3 +282,14 @@ noCertificate=[No Certificate]
 
 pageNotFound=Page not found
 internalServerError=An internal server error has occurred
+
+console-username=Username:
+console-password=Password:
+console-otp=One Time Password:
+console-new-password=New Password:
+console-confirm-password=Confirm Password:
+console-update-password=Update of your password is required.
+console-verify-email=You are required to verify your email address.  An email has been sent to {0} that contains a verification code.  Please enter this code into the input below.
+console-email-code=Email Code:
+console-accept-terms=Accept Terms? [y/n]:
+console-accept=y
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/index.ftl b/themes/src/main/resources/theme/keycloak-preview/account/index.ftl
index 872e42e..3cc9398 100644
--- a/themes/src/main/resources/theme/keycloak-preview/account/index.ftl
+++ b/themes/src/main/resources/theme/keycloak-preview/account/index.ftl
@@ -8,7 +8,7 @@
             var baseUrl = '${baseUrl}';
             var realm = '${realm}';
             var resourceUrl = '${resourceUrl}';
-            
+                
             <#if referrer??>
                 var referrer = '${referrer}';
                 var referrer_uri = '${referrer_uri}';
@@ -17,6 +17,9 @@
             <#if msg??>
                 var locale = '${locale}';
                 var l18n_msg = JSON.parse('${msg?no_esc}');
+            <#else>
+                var locale = 'en';
+                var l18n_msg = {};
             </#if>
         </script>
 
@@ -29,16 +32,6 @@
 
         <link rel="icon" href="${resourceUrl}/app/assets/img/favicon.ico" type="image/x-icon"/>
 
-        <#if properties.styles?has_content>
-            <#list properties.styles?split(' ') as style>
-            <link href="${resourceUrl}/${style}" rel="stylesheet"/>
-            </#list>
-        </#if>
-
-        <link rel="stylesheet" href="${resourceUrl}/styles.css">
-
-        <!--<script src="${authUrl}/js/${resourceVersion}/keycloak.js" type="text/javascript"></script>-->
-
         <!-- PatternFly -->
         <!-- iPad retina icon -->
         <link rel="apple-touch-icon-precomposed" sizes="152x152"
@@ -65,56 +58,150 @@
               media="screen, print">
         <link href="${resourceUrl}/node_modules/patternfly/dist/css/patternfly-additions.min.css" rel="stylesheet"
               media="screen, print">
-        <script src="${resourceUrl}/node_modules/jquery/dist/jquery.min.js"></script>
+
         <script src="${resourceUrl}/node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
-        <script src="${resourceUrl}/node_modules/jquery-match-height/dist/jquery.matchHeight-min.js"></script>
         <script src="${resourceUrl}/node_modules/patternfly/dist/js/patternfly.min.js"></script>
+        <script src="${authUrl}/js/keycloak.js"></script>
 
-        <!-- Polyfill(s) for older browsers -->
-        <script src="${resourceUrl}/node_modules/core-js/client/shim.min.js"></script>
-
-        <#if properties.scripts?has_content>
-            <#list properties.scripts?split(' ') as script>
-            <script type="text/javascript" src="${resourceUrl}/${script}"></script>
-            </#list>
-        </#if>
-
-        <script src="${resourceUrl}/node_modules/zone.js/dist/zone.js"></script>
-        <script src="${resourceUrl}/node_modules/systemjs/dist/system.src.js"></script>
-
-        <script src="${resourceUrl}/systemjs.config.js"></script>
         <script>
-            System.import('${resourceUrl}/main.js').catch(function (err) {
-                console.error(err);
+            var keycloak = Keycloak('${authUrl}/realms/${realm}/account/keycloak.json');
+            keycloak.init({onLoad: 'check-sso'}).success(function(authenticated) {
+                var loadjs = function (url,loadListener) {
+                    const script = document.createElement("script");
+                    script.src = resourceUrl + url;
+                    if (loadListener) script.addEventListener("load", loadListener);
+                    document.head.appendChild(script); 
+                };
+                loadjs("/node_modules/core-js/client/shim.min.js", function(){
+                    loadjs("/node_modules/zone.js/dist/zone.min.js");
+                    loadjs("/node_modules/systemjs/dist/system.src.js", function() {
+                        loadjs("/systemjs.config.js");
+                        System.import('${resourceUrl}/main.js').catch(function (err) {
+                            console.error(err);
+                        });
+                        if (!keycloak.authenticated) document.getElementById("signInButton").style.visibility='visible';
+                    });
+                });
+            }).error(function() {
+                alert('failed to initialize keycloak');
             });
         </script>
-    </head>
-
-    <app-root>
-        <style>
-            .kc-background {
-                background: url('${resourceUrl}/img/keycloak-bg.png') top left no-repeat;
-                background-size: cover;
-            }
 
-            .logo-centered {
-                position: fixed;
-                top: 50%;
-                left: 50%;
-                transform: translate(-50%, -50%);
-            }
 
-            .kc-logo-text {
-                background-image: url("${resourceUrl}/img/keycloak-logo-text.png");
-                background-repeat: no-repeat;
-                width: 250px;
-                height: 38px;
-            }
-        </style>
+   <!-- TODO: We should save these css and js into variables and then load in
+        main.ts for better performance.  These might be loaded twice.
+        -->
+        <#if properties.styles?has_content>
+            <#list properties.styles?split(' ') as style>
+        <link href="${resourceUrl}/${style}" rel="stylesheet"/>
+            </#list>
+    <a href="../../../../../../../../keycloak-quickstarts/app-profile-jee-html5/src/main/webapp/index.html"></a>
+        </#if>
 
-        <body class="cards-pf kc-background">
-            <div class='logo-centered kc-logo-text'/>
-        </body>
-    </app-root>
+        <#if properties.scripts?has_content>
+            <#list properties.scripts?split(' ') as script>
+        <script type="text/javascript" src="${resourceUrl}/${script}"></script>
+            </#list>
+        </#if>
+        </head>
+    <body>
 
+        
+        
+<!-- Top Navigation -->
+        <nav class="navbar navbar-pf-alt">
+
+            <div class="navbar-header">
+                <a href="http://www.keycloak.org" class="navbar-brand">
+                    <img class="navbar-brand-icon" type="image/svg+xml" src="${resourceUrl}/app/assets/img/keycloak-logo-min.png" alt="" width="auto" height="30px"/>
+                </a>
+            </div>
+            <nav class="collapse navbar-collapse">
+                <ul class="nav navbar-nav">
+                </ul>
+                
+                <!-- This sign in button is only displayed in the rare case where we go directly to this page and we aren't logged in.
+                     Note javascript code above that changes its visibility for that case.  Also, because we are not logged in
+                     we are unable to localize the button's message.  Not sure what to do about that yet.
+                -->
+                <ul class="nav navbar-nav navbar-right navbar-iconic">
+                    <li><button id="signInButton" style="visibility:hidden" onclick="keycloak.login();" class="btn btn-primary btn-lg" type="button">Sign In</button></li>
+                </ul>
+            </nav>
+        </nav>
+        
+<!--Top Nav -->
+        
+<!-- Home Page --->
+    <div class="cards-pf" id="welcomeScreen">
+        <div><h1 class="text-center">Welcome to Keycloak Account Management</h1></div>
+        <div class="container-fluid container-cards-pf">
+            <div class="row row-cards-pf">
+                <div class="col-xs-12 col-sm-6 col-md-4 col-lg-3">
+                    <div class="card-pf card-pf-view card-pf-view-select card-pf-view-single-select">
+                        <div class="card-pf-body">
+                            <div class="card-pf-top-element">
+                                <span class="fa pficon-user card-pf-icon-circle"></span>
+                            </div>
+                            <h2 class="card-pf-title text-center">
+                                Personal Info
+                            </h2>
+                            <h3 class="card-pf-info text-center">
+                                <a href="${baseUrl}/#/account">Account</a>
+                            </h3>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-xs-12 col-sm-6 col-md-4 col-lg-3">
+                    <div class="card-pf card-pf-view card-pf-view-select card-pf-view-single-select">
+                        <div class="card-pf-body">
+                            <div class="card-pf-top-element">
+                                <span class="fa fa-shield card-pf-icon-circle"></span>
+                            </div>
+                            <h2 class="card-pf-title text-center">
+                                Account Security
+                            </h2>
+                            <h3 class="card-pf-info text-center">
+                                More stuff goes here
+                            </h3>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-xs-12 col-sm-6 col-md-4 col-lg-3">
+                    <div class="card-pf card-pf-view card-pf-view-select card-pf-view-single-select">
+                        <div class="card-pf-body">
+                            <div class="card-pf-top-element">
+                                <span class="fa fa-th card-pf-icon-circle"></span>
+                            </div>
+                            <h2 class="card-pf-title text-center">
+                                Applications
+                            </h2>
+                            <h3 class="card-pf-info text-center">
+                                More stuff goes here
+                            </h3>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-xs-12 col-sm-6 col-md-4 col-lg-3">
+                    <div class="card-pf card-pf-view card-pf-view-select card-pf-view-single-select">
+                        <div class="card-pf-body">
+                            <div class="card-pf-top-element">
+                                <span class="fa pficon-repository card-pf-icon-circle"></span>
+                            </div>
+                            <h2 class="card-pf-title text-center">
+                                My Resources
+                            </h2>
+                            <h3 class="card-pf-info text-center">
+                                More stuff goes here
+                            </h3>
+                        </div>
+                    </div>
+                </div>
+
+            </div>
+        </div>
+    </div>
+
+        <app-root></app-root>
+    </body>
 </html>
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app.component.html b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app.component.html
index 2a65d8b..cd1e829 100644
--- a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app.component.html
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app.component.html
@@ -1,9 +1,11 @@
-<app-top-nav></app-top-nav>
-<app-side-nav></app-side-nav>
+<div *ngIf="kcService.authenticated()">
+    <app-top-nav [showSideNav]="showSideNav"></app-top-nav>
+</div>
+
+<app-side-nav *ngIf="showSideNav"></app-side-nav>
 
 
 <div class="container-fluid container-pf-alt-nav-pf-vertical-alt {{this.contentWidthClass}}"> <!-- collapsed-nav hidden-nav -->
 
     <router-outlet></router-outlet>
-    
 </div>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app.component.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app.component.ts
index 04b2c2e..72a19af 100644
--- a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app.component.ts
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app.component.ts
@@ -15,10 +15,12 @@
  * limitations under the License.
  */
 import {Component, HostListener} from '@angular/core';
-
+import {Router, NavigationEnd} from '@angular/router';
+import 'rxjs/add/observable/of'; // needed for ngx-translate
 import {TranslateService} from '@ngx-translate/core';
 
 import {ResponsivenessService, ContentWidthClass, MenuClickListener} from "./responsiveness-service/responsiveness.service";
+import {KeycloakService} from "./keycloak-service/keycloak.service";
 
 declare const locale: string;
 
@@ -30,8 +32,12 @@ declare const locale: string;
 export class AppComponent implements MenuClickListener {
     
     private contentWidthClass: ContentWidthClass = this.respSvc.calcSideContentWidthClass();
+    private showSideNav: boolean = false;
     
-    constructor(translate: TranslateService, private respSvc: ResponsivenessService) {
+    constructor(translate: TranslateService, 
+                private respSvc: ResponsivenessService,
+                private router: Router,
+                private kcService: KeycloakService) {
         // this language will be used as a fallback when a translation isn't found in the current language
         translate.setDefaultLang('en');
 
@@ -39,6 +45,19 @@ export class AppComponent implements MenuClickListener {
         translate.use(locale);
         
         this.respSvc.addMenuClickListener(this);
+        
+        // show side nav if we are past the welcome screen
+        this.router.events.subscribe(value => {
+            if (value instanceof NavigationEnd) {
+                const navEnd = value as NavigationEnd;
+                console.log(navEnd.url);
+                if (navEnd.url !== '/') {
+                    this.showSideNav = true;
+                    var welcomeScreen = document.getElementById('welcomeScreen')
+                    if (welcomeScreen) welcomeScreen.remove();
+                }
+            }
+        });
     }
     
     public menuClicked() : void {
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app.module.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app.module.ts
index 6fdd6db..3aec819 100644
--- a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app.module.ts
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app.module.ts
@@ -26,6 +26,7 @@ import { TranslateModule } from '@ngx-translate/core';
 
 import { KeycloakService } from './keycloak-service/keycloak.service';
 import { KEYCLOAK_HTTP_PROVIDER } from './keycloak-service/keycloak.http';
+import {KeycloakGuard} from './keycloak-service/keycloak.guard';
 
 import {ResponsivenessService} from './responsiveness-service/responsiveness.service'
 
@@ -38,55 +39,15 @@ import { TopNavComponent } from './top-nav/top-nav.component';
 import { NotificationComponent } from './top-nav/notification.component';
 import { ToastNotifier } from './top-nav/toast.notifier';
 import { SideNavComponent } from './side-nav/side-nav.component';
-import { AccountPageComponent } from './content/account-page/account-page.component';
-import { PasswordPageComponent } from './content/password-page/password-page.component';
-import { PageNotFoundComponent } from './content/page-not-found/page-not-found.component';
 
-import { AuthenticatorPageComponent } from './content/authenticator-page/authenticator-page.component';
-
-import { SessionsPageComponent } from './content/sessions-page/sessions-page.component';
-import { LargeSessionCardComponent } from './content/sessions-page/large-session-card.component';
-import { SmallSessionCardComponent } from './content/sessions-page/small-session-card.component';
-
-import { ApplicationsPageComponent } from './content/applications-page/applications-page.component';
-import { LargeAppCardComponent } from './content/applications-page/large-app-card.component';
-import { SmallAppCardComponent } from './content/applications-page/small-app-card.component';
-import { RowAppCardComponent } from './content/applications-page/row-app-card.component';
-
-import { ToolbarComponent } from './content/widgets/toolbar.component';
-
-import {OrderbyPipe} from './pipes/orderby.pipe';
-import {FilterbyPipe} from './pipes/filterby.pipe';
-
-const routes: Routes = [
-    { path: 'account', component: AccountPageComponent },
-    { path: 'password', component: PasswordPageComponent },
-    { path: 'authenticator', component: AuthenticatorPageComponent },
-    { path: 'sessions', component: SessionsPageComponent },
-    { path: 'applications', component: ApplicationsPageComponent },
-    { path: '', redirectTo: '/account', pathMatch: 'full' },
-    { path: '**', component: PageNotFoundComponent}
-];
+/* Routing Module */
+import { AppRoutingModule } from './app-routing.module';
 
 const decs = [
     AppComponent,
     TopNavComponent,
     NotificationComponent,
     SideNavComponent,
-    AccountPageComponent,
-    PasswordPageComponent,
-    PageNotFoundComponent,
-    AuthenticatorPageComponent,
-    SessionsPageComponent,
-    LargeSessionCardComponent,
-    SmallSessionCardComponent,
-    ApplicationsPageComponent,
-    LargeAppCardComponent,
-    SmallAppCardComponent,
-    RowAppCardComponent,
-    ToolbarComponent,
-    OrderbyPipe,
-    FilterbyPipe
 ];
 
 export const ORIGINAL_INCOMING_URL: Location = window.location;
@@ -100,10 +61,11 @@ export const ORIGINAL_INCOMING_URL: Location = window.location;
     TranslateModule.forRoot({
         loader: {provide: TranslateLoader, useClass: DeclaredVarTranslateLoader}
     }),
-    RouterModule.forRoot(routes)
+    AppRoutingModule,
   ],
   providers: [
     KeycloakService,
+    KeycloakGuard,
     KEYCLOAK_HTTP_PROVIDER,
     ResponsivenessService,
     AccountServiceClient,
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app-routing.module.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app-routing.module.ts
new file mode 100644
index 0000000..f11cdaf
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app-routing.module.ts
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2018 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.
+ */
+import { NgModule }             from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+import {KeycloakGuard} from './keycloak-service/keycloak.guard';
+
+import { HomePageComponent } from './content/home-page/home-page.component';
+
+declare const resourceUrl: string;
+
+export const routes: Routes = [
+        {path: '', canActivateChild:[KeycloakGuard], children: [
+            { path: 'account', loadChildren: resourceUrl + '/app/content/account-page/account.module.js#AccountModule' },
+            { path: 'password', loadChildren: resourceUrl + '/app/content/password-page/password.module.js#PasswordModule' },
+            { path: 'authenticator', loadChildren: resourceUrl + '/app/content/authenticator-page/authenticator.module.js#AuthenticatorModule' },
+            { path: 'sessions', loadChildren: resourceUrl + '/app/content/sessions-page/sessions.module.js#SessionsModule' },
+            { path: 'applications', loadChildren: resourceUrl + '/app/content/applications-page/applications.module.js#ApplicationsModule' },
+            { path: ':**', loadChildren: resourceUrl + '/app/content/page-not-found/page-not-found.module.js#PageNotFoundModule' },
+            ]
+        }
+    ];
+
+@NgModule({
+  imports: [RouterModule.forRoot(routes)],
+  exports: [RouterModule],
+  declarations: [HomePageComponent]
+})
+export class AppRoutingModule {}
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/assets/img/keycloak-logo-min.png b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/assets/img/keycloak-logo-min.png
new file mode 100644
index 0000000..0bbf1d5
Binary files /dev/null and b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/assets/img/keycloak-logo-min.png differ
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/account-page/account.module.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/account-page/account.module.ts
new file mode 100644
index 0000000..1e65b14
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/account-page/account.module.ts
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2018 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.
+ */
+import { NgModule }      from '@angular/core';
+import { CommonModule }  from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { TranslateModule } from '@ngx-translate/core';
+
+import { AccountPageComponent } from './account-page.component';
+import { AccountRoutingModule } from './account-routing.module';
+
+@NgModule({
+  imports:      [ CommonModule, FormsModule, TranslateModule, AccountRoutingModule ],
+  declarations: [ AccountPageComponent ],
+  providers:    [ ]
+})
+export class AccountModule {}
+
+
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/account-page/account-routing.module.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/account-page/account-routing.module.ts
new file mode 100644
index 0000000..635f4b8
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/account-page/account-routing.module.ts
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2018 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.
+ */
+import { NgModule }             from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+import { AccountPageComponent } from './account-page.component';
+
+const routes: Routes = [
+    { path: '**', component: AccountPageComponent },
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule]
+})
+export class AccountRoutingModule {}
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/applications.module.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/applications.module.ts
new file mode 100644
index 0000000..5cc64fc
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/applications.module.ts
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2018 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.
+ */
+import { NgModule }      from '@angular/core';
+import { CommonModule }  from '@angular/common';
+
+import { TranslateModule } from '@ngx-translate/core';
+
+import {WidgetsModule} from '../widgets/widgets.module';
+
+import { ApplicationsPageComponent } from './applications-page.component';
+import { ApplicationsRoutingModule } from './applications-routing.module';
+import { LargeAppCardComponent } from './large-app-card.component';
+import { SmallAppCardComponent } from './small-app-card.component';
+import { RowAppCardComponent } from './row-app-card.component';
+
+@NgModule({
+  imports:      [ CommonModule, 
+                  TranslateModule, 
+                  ApplicationsRoutingModule,
+                  WidgetsModule ],
+  declarations: [ ApplicationsPageComponent,
+                  LargeAppCardComponent,
+                  SmallAppCardComponent,
+                  RowAppCardComponent ],
+  providers:    [ ]
+})
+export class ApplicationsModule {}
+
+
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/applications-routing.module.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/applications-routing.module.ts
new file mode 100644
index 0000000..f35db61
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/applications-routing.module.ts
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2018 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.
+ */
+import { NgModule }             from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+import { ApplicationsPageComponent } from './applications-page.component';
+
+const routes: Routes = [
+    { path: '**', component: ApplicationsPageComponent },
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule]
+})
+export class ApplicationsRoutingModule {}
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/authenticator-page/authenticator.module.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/authenticator-page/authenticator.module.ts
new file mode 100644
index 0000000..780bd7c
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/authenticator-page/authenticator.module.ts
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2018 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.
+ */
+import { NgModule }      from '@angular/core';
+import { CommonModule }  from '@angular/common';
+
+import { TranslateModule } from '@ngx-translate/core';
+
+import { AuthenticatorPageComponent } from './authenticator-page.component';
+import { AuthenticatorRoutingModule } from './authenticator-routing.module';
+
+@NgModule({
+  imports:      [ CommonModule, TranslateModule, AuthenticatorRoutingModule ],
+  declarations: [ AuthenticatorPageComponent ],
+  providers:    [ ]
+})
+export class AuthenticatorModule {}
+
+
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/authenticator-page/authenticator-routing.module.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/authenticator-page/authenticator-routing.module.ts
new file mode 100644
index 0000000..47c4d73
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/authenticator-page/authenticator-routing.module.ts
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2018 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.
+ */
+import { NgModule }             from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+import { AuthenticatorPageComponent } from './authenticator-page.component';
+
+const routes: Routes = [
+    { path: '**', component: AuthenticatorPageComponent },
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule]
+})
+export class AuthenticatorRoutingModule {}
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/home-page/home-page.component.css b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/home-page/home-page.component.css
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/home-page/home-page.component.css
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/home-page/home-page.component.html b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/home-page/home-page.component.html
new file mode 100644
index 0000000..27a4940
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/home-page/home-page.component.html
@@ -0,0 +1,68 @@
+<div class="cards-pf">
+    <div><h1 class="text-center">Welcome to Keycloak Account Management</h1></div>
+    <div class="container-fluid container-cards-pf">
+        <div class="row row-cards-pf">
+            <div class="col-xs-12 col-sm-6 col-md-4 col-lg-3">
+                <div class="card-pf card-pf-view card-pf-view-select card-pf-view-single-select">
+                    <div class="card-pf-body">
+                        <div class="card-pf-top-element">
+                            <span class="fa pficon-user card-pf-icon-circle"></span>
+                        </div>
+                        <h2 class="card-pf-title text-center">
+                            Personal Info
+                        </h2>
+                        <h3 class="card-pf-info text-center">
+                            More stuff goes here
+                        </h3>
+                    </div>
+                </div>
+            </div>
+            <div class="col-xs-12 col-sm-6 col-md-4 col-lg-3">
+                <div class="card-pf card-pf-view card-pf-view-select card-pf-view-single-select">
+                    <div class="card-pf-body">
+                        <div class="card-pf-top-element">
+                            <span class="fa fa-shield card-pf-icon-circle"></span>
+                        </div>
+                        <h2 class="card-pf-title text-center">
+                            Account Security
+                        </h2>
+                        <h3 class="card-pf-info text-center">
+                            More stuff goes here
+                        </h3>
+                    </div>
+                </div>
+            </div>
+            <div class="col-xs-12 col-sm-6 col-md-4 col-lg-3">
+                <div class="card-pf card-pf-view card-pf-view-select card-pf-view-single-select">
+                    <div class="card-pf-body">
+                        <div class="card-pf-top-element">
+                            <span class="fa fa-th card-pf-icon-circle"></span>
+                        </div>
+                        <h2 class="card-pf-title text-center">
+                            Applications
+                        </h2>
+                        <h3 class="card-pf-info text-center">
+                            More stuff goes here
+                        </h3>
+                    </div>
+                </div>
+            </div>
+            <div class="col-xs-12 col-sm-6 col-md-4 col-lg-3">
+                <div class="card-pf card-pf-view card-pf-view-select card-pf-view-single-select">
+                    <div class="card-pf-body">
+                        <div class="card-pf-top-element">
+                            <span class="fa pficon-repository card-pf-icon-circle"></span>
+                        </div>
+                        <h2 class="card-pf-title text-center">
+                            My Resources
+                        </h2>
+                        <h3 class="card-pf-info text-center">
+                            More stuff goes here
+                        </h3>
+                    </div>
+                </div>
+            </div>
+            
+        </div>
+    </div>
+</div>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/home-page/home-page.component.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/home-page/home-page.component.ts
new file mode 100644
index 0000000..1a0c781
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/home-page/home-page.component.ts
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {Component} from '@angular/core';
+
+@Component({
+    selector: 'home-page',
+    templateUrl: './home-page.component.html',
+    styleUrls: ['./home-page.component.css']
+})
+export class HomePageComponent {
+    
+    constructor() {}
+    
+}
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/page-not-found/page-not-found.module.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/page-not-found/page-not-found.module.ts
new file mode 100644
index 0000000..9274bb1
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/page-not-found/page-not-found.module.ts
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2018 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.
+ */
+import { NgModule }      from '@angular/core';
+import { CommonModule }  from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { TranslateModule } from '@ngx-translate/core';
+
+import { PageNotFoundComponent } from './page-not-found.component';
+import { PageNotFoundRoutingModule } from './page-not-found-routing.module';
+
+@NgModule({
+  imports:      [ CommonModule, FormsModule, TranslateModule, PageNotFoundRoutingModule ],
+  declarations: [ PageNotFoundComponent ],
+  providers:    [ ]
+})
+export class PageNotFoundModule {}
+
+
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/page-not-found/page-not-found-routing.module.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/page-not-found/page-not-found-routing.module.ts
new file mode 100644
index 0000000..a23ca00
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/page-not-found/page-not-found-routing.module.ts
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2018 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.
+ */
+import { NgModule }             from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+import { PageNotFoundComponent } from './page-not-found.component';
+
+const routes: Routes = [
+    { path: '**', component: PageNotFoundComponent}
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule]
+})
+export class PageNotFoundRoutingModule {}
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/password-page/password.module.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/password-page/password.module.ts
new file mode 100644
index 0000000..9bd5d9d
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/password-page/password.module.ts
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2018 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.
+ */
+import { NgModule }      from '@angular/core';
+import { CommonModule }  from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { TranslateModule } from '@ngx-translate/core';
+
+import { PasswordPageComponent } from './password-page.component';
+import { PasswordRoutingModule } from './password-routing.module';
+
+@NgModule({
+  imports:      [ CommonModule, FormsModule, TranslateModule, PasswordRoutingModule ],
+  declarations: [ PasswordPageComponent ],
+  providers:    [ ]
+})
+export class PasswordModule {}
+
+
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/password-page/password-routing.module.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/password-page/password-routing.module.ts
new file mode 100644
index 0000000..3208b40
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/password-page/password-routing.module.ts
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2018 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.
+ */
+import { NgModule }             from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+import { PasswordPageComponent } from './password-page.component';
+
+const routes: Routes = [
+    { path: '**', component: PasswordPageComponent },
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule]
+})
+export class PasswordRoutingModule {}
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/sessions.module.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/sessions.module.ts
new file mode 100644
index 0000000..cec77c8
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/sessions.module.ts
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2018 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.
+ */
+import { NgModule }      from '@angular/core';
+import { CommonModule }  from '@angular/common';
+
+import { TranslateModule } from '@ngx-translate/core';
+
+import {WidgetsModule} from '../widgets/widgets.module';
+
+import { SessionsRoutingModule } from './sessions-routing.module';
+
+import { SessionsPageComponent } from './sessions-page.component';
+import { LargeSessionCardComponent } from './large-session-card.component';
+import { SmallSessionCardComponent } from './small-session-card.component';
+
+@NgModule({
+  imports:      [ CommonModule, 
+                  TranslateModule, 
+                  SessionsRoutingModule,
+                  WidgetsModule ],
+  declarations: [ SessionsPageComponent,
+                  LargeSessionCardComponent,
+                  SmallSessionCardComponent ],
+  providers:    [ ]
+})
+export class SessionsModule {}
+
+
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/sessions-page.component.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/sessions-page.component.ts
index 95a519a..2ffb989 100644
--- a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/sessions-page.component.ts
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/sessions-page.component.ts
@@ -18,7 +18,7 @@ import {Component, OnInit} from '@angular/core';
 import {Response} from '@angular/http';
 
 import {AccountServiceClient} from '../../account-service/account.service';
- import {TranslateUtil} from '../../ngx-translate/translate.util';
+import {TranslateUtil} from '../../ngx-translate/translate.util';
 
 import {View} from '../widgets/toolbar.component';
 import {PropertyLabel} from '../widgets/property.label';
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/sessions-routing.module.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/sessions-routing.module.ts
new file mode 100644
index 0000000..da6e3ab
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/sessions-routing.module.ts
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2018 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.
+ */
+import { NgModule }             from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+import { SessionsPageComponent } from './sessions-page.component';
+
+const routes: Routes = [
+    { path: '**', component: SessionsPageComponent },
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule]
+})
+export class SessionsRoutingModule {}
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/widgets/widgets.module.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/widgets/widgets.module.ts
new file mode 100644
index 0000000..422fd30
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/widgets/widgets.module.ts
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2018 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.
+ */
+import { NgModule }      from '@angular/core';
+import { CommonModule }  from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { ToolbarComponent } from './toolbar.component';
+import {OrderbyPipe} from './orderby.pipe';
+import {FilterbyPipe} from './filterby.pipe';
+
+@NgModule({
+  imports:      [ CommonModule, FormsModule ],
+  declarations: [ ToolbarComponent, OrderbyPipe, FilterbyPipe ],
+  exports: [ ToolbarComponent,
+             OrderbyPipe,
+             FilterbyPipe ],
+  providers:    [ ]
+})
+export class WidgetsModule {}
+
+
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/keycloak.guard.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/keycloak.guard.ts
new file mode 100644
index 0000000..39239a1
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/keycloak.guard.ts
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2018 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.
+ */
+ 
+import {Injectable} from '@angular/core';
+import {CanActivateChild, ActivatedRouteSnapshot, RouterStateSnapshot} from '@angular/router';
+import {KeycloakService} from './keycloak.service';
+
+@Injectable()
+export class KeycloakGuard implements CanActivateChild {
+    constructor(private keycloakService: KeycloakService) {}
+    
+    canActivateChild(route: ActivatedRouteSnapshot,
+                state: RouterStateSnapshot): boolean {
+
+        if (this.keycloakService.authenticated()) {
+            return true;
+        }
+
+        this.keycloakService.login();
+        return false;
+    }
+}
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/keycloak.js b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/keycloak.js
index a784936..80b4477 100644
--- a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/keycloak.js
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/keycloak.js
@@ -40,6 +40,8 @@
             }
         }
 
+        var useNonce = true;
+        
         kc.init = function (initOptions) {
             kc.authenticated = false;
 
@@ -50,7 +52,7 @@
             } else if (initOptions && initOptions.adapter === 'default') {
                 adapter = loadAdapter();
             } else {
-                if (window.Cordova) {
+                if (window.Cordova || window.cordova) {
                     adapter = loadAdapter('cordova');
                 } else {
                     adapter = loadAdapter();
@@ -58,6 +60,10 @@
             }
 
             if (initOptions) {
+                if (typeof initOptions.useNonce !== 'undefined') {
+                    useNonce = initOptions.useNonce;
+                }
+
                 if (typeof initOptions.checkLoginIframe !== 'undefined') {
                     loginIframe.enable = initOptions.checkLoginIframe;
                 }
@@ -159,10 +165,15 @@
                 var callback = parseCallback(window.location.href);
 
                 if (callback) {
-                    setupCheckLoginIframe();
                     window.history.replaceState({}, null, callback.newUrl);
-                    processCallback(callback, initPromise);
-                    return;
+                }
+
+                if (callback && callback.valid) {
+                    return setupCheckLoginIframe().success(function() {
+                        processCallback(callback, initPromise);
+                    }).error(function (e) {
+                        initPromise.setError();
+                    });
                 } else if (initOptions) {
                     if (initOptions.token && initOptions.refreshToken) {
                         setToken(initOptions.token, initOptions.refreshToken, initOptions.idToken);
@@ -221,7 +232,7 @@
             var callbackState = {
                 state: state,
                 nonce: nonce,
-                redirectUri: encodeURIComponent(redirectUri),
+                redirectUri: encodeURIComponent(redirectUri)
             }
 
             if (options && options.prompt) {
@@ -230,22 +241,25 @@
 
             callbackStorage.add(callbackState);
 
-            var action = 'auth';
+            var baseUrl;
             if (options && options.action == 'register') {
-                action = 'registrations';
+                baseUrl = kc.endpoints.register();
+            } else {
+                baseUrl = kc.endpoints.authorize();
             }
 
             var scope = (options && options.scope) ? "openid " + options.scope : "openid";
 
-            var url = getRealmUrl()
-                + '/protocol/openid-connect/' + action
+            var url = baseUrl
                 + '?client_id=' + encodeURIComponent(kc.clientId)
                 + '&redirect_uri=' + encodeURIComponent(redirectUri)
                 + '&state=' + encodeURIComponent(state)
-                + '&nonce=' + encodeURIComponent(nonce)
                 + '&response_mode=' + encodeURIComponent(kc.responseMode)
                 + '&response_type=' + encodeURIComponent(kc.responseType)
                 + '&scope=' + encodeURIComponent(scope);
+                if (useNonce) {
+                    url = url + '&nonce=' + encodeURIComponent(nonce);
+                }
 
             if (options && options.prompt) {
                 url += '&prompt=' + encodeURIComponent(options.prompt);
@@ -275,8 +289,7 @@
         }
 
         kc.createLogoutUrl = function(options) {
-            var url = getRealmUrl()
-                + '/protocol/openid-connect/logout'
+            var url = kc.endpoints.logout()
                 + '?redirect_uri=' + encodeURIComponent(adapter.redirectUri(options, false));
 
             return url;
@@ -295,11 +308,14 @@
         }
 
         kc.createAccountUrl = function(options) {
-            var url = getRealmUrl()
+            var realm = getRealmUrl();
+            var url = undefined;
+            if (typeof realm !== 'undefined') {
+                url = realm
                 + '/account'
                 + '?referrer=' + encodeURIComponent(kc.clientId)
                 + '&referrer_uri=' + encodeURIComponent(adapter.redirectUri(options));
-
+            }
             return url;
         }
 
@@ -347,7 +363,7 @@
         }
 
         kc.loadUserInfo = function() {
-            var url = getRealmUrl() + '/protocol/openid-connect/userinfo';
+            var url = kc.endpoints.userinfo();
             var req = new XMLHttpRequest();
             req.open('GET', url, true);
             req.setRequestHeader('Accept', 'application/json');
@@ -412,7 +428,7 @@
                     promise.setSuccess(false);
                 } else {
                     var params = 'grant_type=refresh_token&' + 'refresh_token=' + kc.refreshToken;
-                    var url = getRealmUrl() + '/protocol/openid-connect/token';
+                    var url = kc.endpoints.token();
 
                     refreshQueue.push(promise);
 
@@ -486,10 +502,14 @@
         }
 
         function getRealmUrl() {
-            if (kc.authServerUrl.charAt(kc.authServerUrl.length - 1) == '/') {
-                return kc.authServerUrl + 'realms/' + encodeURIComponent(kc.realm);
+            if (typeof kc.authServerUrl !== 'undefined') {
+                if (kc.authServerUrl.charAt(kc.authServerUrl.length - 1) == '/') {
+                    return kc.authServerUrl + 'realms/' + encodeURIComponent(kc.realm);
+                } else {
+                    return kc.authServerUrl + '/realms/' + encodeURIComponent(kc.realm);
+                }
             } else {
-                return kc.authServerUrl + '/realms/' + encodeURIComponent(kc.realm);
+            	return undefined;
             }
         }
 
@@ -523,7 +543,7 @@
 
             if ((kc.flow != 'implicit') && code) {
                 var params = 'code=' + code + '&grant_type=authorization_code';
-                var url = getRealmUrl() + '/protocol/openid-connect/token';
+                var url = kc.endpoints.token();
 
                 var req = new XMLHttpRequest();
                 req.open('POST', url, true);
@@ -560,9 +580,9 @@
 
                 setToken(accessToken, refreshToken, idToken, timeLocal);
 
-                if ((kc.tokenParsed && kc.tokenParsed.nonce != oauth.storedNonce) ||
+                if (useNonce && ((kc.tokenParsed && kc.tokenParsed.nonce != oauth.storedNonce) ||
                     (kc.refreshTokenParsed && kc.refreshTokenParsed.nonce != oauth.storedNonce) ||
-                    (kc.idTokenParsed && kc.idTokenParsed.nonce != oauth.storedNonce)) {
+                    (kc.idTokenParsed && kc.idTokenParsed.nonce != oauth.storedNonce))) {
 
                     console.info('[KEYCLOAK] Invalid nonce, clearing token');
                     kc.clearToken();
@@ -587,6 +607,65 @@
                 configUrl = config;
             }
 
+            function setupOidcEndoints(oidcConfiguration) {
+                if (! oidcConfiguration) {
+                    kc.endpoints = {
+                        authorize: function() {
+                            return getRealmUrl() + '/protocol/openid-connect/auth';
+                        },
+                        token: function() {
+                            return getRealmUrl() + '/protocol/openid-connect/token';
+                        },
+                        logout: function() {
+                            return getRealmUrl() + '/protocol/openid-connect/logout';
+                        },
+                        checkSessionIframe: function() {
+                            var src = getRealmUrl() + '/protocol/openid-connect/login-status-iframe.html';
+                            if (kc.iframeVersion) {
+                              src = src + '?version=' + kc.iframeVersion;
+                            }
+                            return src;
+                        },
+                        register: function() {
+                            return getRealmUrl() + '/protocol/openid-connect/registrations';
+                        },
+                        userinfo: function() {
+                            return getRealmUrl() + '/protocol/openid-connect/userinfo';
+                        }
+                    };
+                } else {
+                    kc.endpoints = {
+                        authorize: function() {
+                            return oidcConfiguration.authorization_endpoint;
+                        },
+                        token: function() {
+                            return oidcConfiguration.token_endpoint;
+                        },
+                        logout: function() {
+                            if (!oidcConfiguration.end_session_endpoint) {
+                                throw "Not supported by the OIDC server";
+                            }
+                            return oidcConfiguration.end_session_endpoint;
+                        },
+                        checkSessionIframe: function() {
+                            if (!oidcConfiguration.check_session_iframe) {
+                                throw "Not supported by the OIDC server";
+                            }
+                            return oidcConfiguration.check_session_iframe;
+                        },
+                        register: function() {
+                            throw 'Redirection to "Register user" page not supported in standard OIDC mode';
+                        },
+                        userinfo: function() {
+                            if (!oidcConfiguration.userinfo_endpoint) {
+                                throw "Not supported by the OIDC server";
+                            }
+                            return oidcConfiguration.userinfo_endpoint;
+                        }
+                    }
+                }
+            }
+
             if (configUrl) {
                 var req = new XMLHttpRequest();
                 req.open('GET', configUrl, true);
@@ -601,7 +680,7 @@
                             kc.realm = config['realm'];
                             kc.clientId = config['resource'];
                             kc.clientSecret = (config['credentials'] || {})['secret'];
-
+                            setupOidcEndoints(null);
                             promise.setSuccess();
                         } else {
                             promise.setError();
@@ -611,30 +690,62 @@
 
                 req.send();
             } else {
-                if (!config['url']) {
-                    var scripts = document.getElementsByTagName('script');
-                    for (var i = 0; i < scripts.length; i++) {
-                        if (scripts[i].src.match(/.*keycloak\.js/)) {
-                            config.url = scripts[i].src.substr(0, scripts[i].src.indexOf('/js/keycloak.js'));
-                            break;
-                        }
-                    }
-                }
-
-                if (!config.realm) {
-                    throw 'realm missing';
-                }
-
                 if (!config.clientId) {
                     throw 'clientId missing';
                 }
 
-                kc.authServerUrl = config.url;
-                kc.realm = config.realm;
                 kc.clientId = config.clientId;
                 kc.clientSecret = (config.credentials || {}).secret;
 
-                promise.setSuccess();
+                var oidcProvider = config['oidcProvider'];
+                if (!oidcProvider) {
+                    if (!config['url']) {
+                        var scripts = document.getElementsByTagName('script');
+                        for (var i = 0; i < scripts.length; i++) {
+                            if (scripts[i].src.match(/.*keycloak\.js/)) {
+                                config.url = scripts[i].src.substr(0, scripts[i].src.indexOf('/js/keycloak.js'));
+                                break;
+                            }
+                        }
+                    }
+                    if (!config.realm) {
+                        throw 'realm missing';
+                    }
+
+                    kc.authServerUrl = config.url;
+                    kc.realm = config.realm;
+                    setupOidcEndoints(null);
+                    promise.setSuccess();
+                } else {
+                    if (typeof oidcProvider === 'string') {
+                        var oidcProviderConfigUrl;
+                        if (oidcProvider.charAt(oidcProvider.length - 1) == '/') {
+                            oidcProviderConfigUrl = oidcProvider + '.well-known/openid-configuration';
+                        } else {
+                            oidcProviderConfigUrl = oidcProvider + '/.well-known/openid-configuration';
+                        }
+                        var req = new XMLHttpRequest();
+                        req.open('GET', oidcProviderConfigUrl, true);
+                        req.setRequestHeader('Accept', 'application/json');
+
+                        req.onreadystatechange = function () {
+                            if (req.readyState == 4) {
+                                if (req.status == 200 || fileLoaded(req)) {
+                                    var oidcProviderConfig = JSON.parse(req.responseText);
+                                    setupOidcEndoints(oidcProviderConfig);
+                                    promise.setSuccess();
+                                } else {
+                                    promise.setError();
+                                }
+                            }
+                        };
+
+                        req.send();
+                    } else {
+                        setupOidcEndoints(oidcProvider);
+                        promise.setSuccess();
+                    }
+                }
             }
 
             return promise.promise;
@@ -753,23 +864,137 @@
         }
 
         function parseCallback(url) {
-            var oauth = new CallbackParser(url, kc.responseMode).parseUri();
+            var oauth = parseCallbackUrl(url);
+            if (!oauth) {
+                return;
+            }
+
             var oauthState = callbackStorage.get(oauth.state);
 
-            if (oauthState && (oauth.code || oauth.error || oauth.access_token || oauth.id_token)) {
+            if (oauthState) {
+                oauth.valid = true;
                 oauth.redirectUri = oauthState.redirectUri;
                 oauth.storedNonce = oauthState.nonce;
                 oauth.prompt = oauthState.prompt;
+            }
 
-                if (oauth.fragment) {
-                    oauth.newUrl += '#' + oauth.fragment;
+            return oauth;
+        }
+
+        function parseCallbackUrl(url) {
+            var supportedParams;
+            switch (kc.flow) {
+                case 'standard':
+                    supportedParams = ['code', 'state', 'session_state'];
+                    break;
+                case 'implicit':
+                    supportedParams = ['access_token', 'id_token', 'state', 'session_state'];
+                    break;
+                case 'hybrid':
+                    supportedParams = ['access_token', 'id_token', 'code', 'state', 'session_state'];
+                    break;
+            }
+
+            supportedParams.push('error');
+            supportedParams.push('error_description');
+            supportedParams.push('error_uri');
+
+            var queryIndex = url.indexOf('?');
+            var fragmentIndex = url.indexOf('#');
+
+            var newUrl;
+            var parsed;
+
+            if (kc.responseMode === 'query' && queryIndex !== -1) {
+                newUrl = url.substring(0, queryIndex);
+                parsed = parseCallbackParams(url.substring(queryIndex + 1, fragmentIndex !== -1 ? fragmentIndex : url.length), supportedParams);
+                if (parsed.paramsString !== '') {
+                    newUrl += '?' + parsed.paramsString;
+                }
+                if (fragmentIndex !== -1) {
+                    newUrl += url.substring(fragmentIndex);
                 }
+            } else if (kc.responseMode === 'fragment' && fragmentIndex !== -1) {
+                newUrl = url.substring(0, fragmentIndex);
+                parsed = parseCallbackParams(url.substring(fragmentIndex + 1), supportedParams);
+                if (parsed.paramsString !== '') {
+                    newUrl += '#' + parsed.paramsString;
+                }
+            }
 
-                return oauth;
+            if (parsed && parsed.oauthParams) {
+                if (kc.flow === 'standard' || kc.flow === 'hybrid') {
+                    if ((parsed.oauthParams.code || parsed.oauthParams.error) && parsed.oauthParams.state) {
+                        parsed.oauthParams.newUrl = newUrl;
+                        return parsed.oauthParams;
+                    }
+                } else if (kc.flow === 'implicit') {
+                    if ((parsed.oauthParams.access_token || parsed.oauthParams.error) && parsed.oauthParams.state) {
+                        parsed.oauthParams.newUrl = newUrl;
+                        return parsed.oauthParams;
+                    }
+                }
             }
         }
 
+        function parseCallbackParams(paramsString, supportedParams) {
+            var p = paramsString.split('&');
+            var result = {
+                paramsString: '',
+                oauthParams: {}
+            }
+            for (var i = 0; i < p.length; i++) {
+                var t = p[i].split('=');
+                if (supportedParams.indexOf(t[0]) !== -1) {
+                    result.oauthParams[t[0]] = t[1];
+                } else {
+                    if (result.paramsString !== '') {
+                        result.paramsString += '&';
+                    }
+                    result.paramsString += p[i];
+                }
+            }
+            return result;
+        }
+
         function createPromise() {
+            if (typeof Promise === "function") {
+                return createNativePromise();
+            } else {
+                return createLegacyPromise();
+            }
+        }
+
+        function createNativePromise() {
+            // Need to create a native Promise which also preserves the
+            // interface of the custom promise type previously used by the API
+            var p = {
+                setSuccess: function(result) {
+                    p.success = true;
+                    p.resolve(result);
+                },
+
+                setError: function(result) {
+                    p.success = false;
+                    p.reject(result);
+                }
+            };
+            p.promise = new Promise(function(resolve, reject) {
+                p.resolve = resolve;
+                p.reject = reject;
+            });
+            p.promise.success = function(callback) {
+                p.promise.then(callback);
+                return p.promise;
+            }
+            p.promise.error = function(callback) {
+                p.promise.catch(callback);
+                return p.promise;
+            }
+            return p;
+        }
+
+        function createLegacyPromise() {
             var p = {
                 setSuccess: function(result) {
                     p.success = true;
@@ -826,23 +1051,20 @@
             loginIframe.iframe = iframe;
 
             iframe.onload = function() {
-                var realmUrl = getRealmUrl();
-                if (realmUrl.charAt(0) === '/') {
+                var authUrl = kc.endpoints.authorize();
+                if (authUrl.charAt(0) === '/') {
                     loginIframe.iframeOrigin = getOrigin();
                 } else {
-                    loginIframe.iframeOrigin = realmUrl.substring(0, realmUrl.indexOf('/', 8));
+                    loginIframe.iframeOrigin = authUrl.substring(0, authUrl.indexOf('/', 8));
                 }
                 promise.setSuccess();
 
                 setTimeout(check, loginIframe.interval * 1000);
             }
 
-            var src = getRealmUrl() + '/protocol/openid-connect/login-status-iframe.html';
-            if (kc.iframeVersion) {
-                src = src + '?version=' + kc.iframeVersion;
-            }
-
+            var src = kc.endpoints.checkSessionIframe();
             iframe.setAttribute('src', src );
+            iframe.setAttribute('title', 'keycloak-session-iframe' );
             iframe.style.display = 'none';
             document.body.appendChild(iframe);
 
@@ -920,7 +1142,12 @@
                     },
 
                     accountManagement : function() {
-                        window.location.href = kc.createAccountUrl();
+                        var accountUrl = kc.createAccountUrl();
+                        if (typeof accountUrl !== 'undefined') {
+                            window.location.href = accountUrl;
+                        } else {
+                            throw "Not supported by the OIDC server";
+                        }
                         return createPromise().promise;
                     },
 
@@ -934,12 +1161,7 @@
                         } else if (kc.redirectUri) {
                             return kc.redirectUri;
                         } else {
-                            var redirectUri = location.href;
-                            if (location.hash && encodeHash) {
-                                redirectUri = redirectUri.substring(0, location.href.indexOf('#'));
-                                redirectUri += (redirectUri.indexOf('?') == -1 ? '?' : '&') + 'redirect_fragment=' + encodeURIComponent(location.hash.substring(1));
-                            }
-                            return redirectUri;
+                            return location.href;
                         }
                     }
                 };
@@ -947,7 +1169,14 @@
 
             if (type == 'cordova') {
                 loginIframe.enable = false;
-
+                var cordovaOpenWindowWrapper = function(loginUrl, target, options) {
+                    if (window.cordova && window.cordova.InAppBrowser) {
+                        // Use inappbrowser for IOS and Android if available
+                        return window.cordova.InAppBrowser.open(loginUrl, target, options);
+                    } else {
+                        return window.open(loginUrl, target, options);
+                    }
+                };
                 return {
                     login: function(options) {
                         var promise = createPromise();
@@ -958,8 +1187,7 @@
                         }
 
                         var loginUrl = kc.createLoginUrl(options);
-                        var ref = window.open(loginUrl, '_blank', o);
-
+                        var ref = cordovaOpenWindowWrapper(loginUrl, '_blank', o);
                         var completed = false;
 
                         ref.addEventListener('loadstart', function(event) {
@@ -992,7 +1220,7 @@
                         var promise = createPromise();
 
                         var logoutUrl = kc.createLogoutUrl(options);
-                        var ref = window.open(logoutUrl, '_blank', 'location=no,hidden=yes');
+                        var ref = cordovaOpenWindowWrapper(logoutUrl, '_blank', 'location=no,hidden=yes');
 
                         var error;
 
@@ -1025,7 +1253,7 @@
 
                     register : function() {
                         var registerUrl = kc.createRegisterUrl();
-                        var ref = window.open(registerUrl, '_blank', 'location=no');
+                        var ref = cordovaOpenWindowWrapper(registerUrl, '_blank', 'location=no');
                         ref.addEventListener('loadstart', function(event) {
                             if (event.url.indexOf('http://localhost') == 0) {
                                 ref.close();
@@ -1035,12 +1263,16 @@
 
                     accountManagement : function() {
                         var accountUrl = kc.createAccountUrl();
-                        var ref = window.open(accountUrl, '_blank', 'location=no');
-                        ref.addEventListener('loadstart', function(event) {
-                            if (event.url.indexOf('http://localhost') == 0) {
-                                ref.close();
-                            }
-                        });
+                        if (typeof accountUrl !== 'undefined') {
+                            var ref = cordovaOpenWindowWrapper(accountUrl, '_blank', 'location=no');
+                            ref.addEventListener('loadstart', function(event) {
+                                if (event.url.indexOf('http://localhost') == 0) {
+                                    ref.close();
+                                }
+                            });
+                        } else {
+                            throw "Not supported by the OIDC server";
+                        }
                     },
 
                     redirectUri: function(options) {
@@ -1170,99 +1402,6 @@
 
             return new CookieStorage();
         }
-
-        var CallbackParser = function(uriToParse, responseMode) {
-            if (!(this instanceof CallbackParser)) {
-                return new CallbackParser(uriToParse, responseMode);
-            }
-            var parser = this;
-
-            var initialParse = function() {
-                var baseUri = null;
-                var queryString = null;
-                var fragmentString = null;
-
-                var questionMarkIndex = uriToParse.indexOf("?");
-                var fragmentIndex = uriToParse.indexOf("#", questionMarkIndex + 1);
-                if (questionMarkIndex == -1 && fragmentIndex == -1) {
-                    baseUri = uriToParse;
-                } else if (questionMarkIndex != -1) {
-                    baseUri = uriToParse.substring(0, questionMarkIndex);
-                    queryString = uriToParse.substring(questionMarkIndex + 1);
-                    if (fragmentIndex != -1) {
-                        fragmentIndex = queryString.indexOf("#");
-                        fragmentString = queryString.substring(fragmentIndex + 1);
-                        queryString = queryString.substring(0, fragmentIndex);
-                    }
-                } else {
-                    baseUri = uriToParse.substring(0, fragmentIndex);
-                    fragmentString = uriToParse.substring(fragmentIndex + 1);
-                }
-
-                return { baseUri: baseUri, queryString: queryString, fragmentString: fragmentString };
-            }
-
-            var parseParams = function(paramString) {
-                var result = {};
-                var params = paramString.split('&');
-                for (var i = 0; i < params.length; i++) {
-                    var p = params[i].split('=');
-                    var paramName = decodeURIComponent(p[0]);
-                    var paramValue = decodeURIComponent(p[1]);
-                    result[paramName] = paramValue;
-                }
-                return result;
-            }
-
-            var handleQueryParam = function(paramName, paramValue, oauth) {
-                var supportedOAuthParams = [ 'code', 'state', 'error', 'error_description' ];
-
-                for (var i = 0 ; i< supportedOAuthParams.length ; i++) {
-                    if (paramName === supportedOAuthParams[i]) {
-                        oauth[paramName] = paramValue;
-                        return true;
-                    }
-                }
-                return false;
-            }
-
-
-            parser.parseUri = function() {
-                var parsedUri = initialParse();
-
-                var queryParams = {};
-                if (parsedUri.queryString) {
-                    queryParams = parseParams(parsedUri.queryString);
-                }
-
-                var oauth = { newUrl: parsedUri.baseUri };
-                for (var param in queryParams) {
-                    switch (param) {
-                        case 'redirect_fragment':
-                            oauth.fragment = queryParams[param];
-                            break;
-                        default:
-                            if (responseMode != 'query' || !handleQueryParam(param, queryParams[param], oauth)) {
-                                oauth.newUrl += (oauth.newUrl.indexOf('?') == -1 ? '?' : '&') + param + '=' + encodeURIComponent(queryParams[param]);
-                            }
-                            break;
-                    }
-                }
-
-                if (responseMode === 'fragment') {
-                    var fragmentParams = {};
-                    if (parsedUri.fragmentString) {
-                        fragmentParams = parseParams(parsedUri.fragmentString);
-                    }
-                    for (var param in fragmentParams) {
-                        oauth[param] = fragmentParams[param];
-                    }
-                }
-
-                return oauth;
-            }
-        }
-
     }
 
     if ( typeof module === "object" && module && typeof module.exports === "object" ) {
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/keycloak.service.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/keycloak.service.ts
index 54f2eaa..97a7407 100644
--- a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/keycloak.service.ts
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/keycloak.service.ts
@@ -15,6 +15,7 @@
  * limitations under the License.
  */
 import {Injectable} from '@angular/core';
+import {KeycloakLoginOptions} from './keycloak.d';
 
 // If using a local keycloak.js, uncomment this import.  With keycloak.js fetched
 // from the server, you get a compile-time warning on use of the Keycloak()
@@ -23,7 +24,7 @@ import {Injectable} from '@angular/core';
 // 
 import * as Keycloak from './keycloak';
 
-type KeycloakClient = Keycloak.KeycloakInstance;
+export type KeycloakClient = Keycloak.KeycloakInstance;
 type InitOptions = Keycloak.KeycloakInitOptions;
 
 @Injectable()
@@ -52,17 +53,21 @@ export class KeycloakService {
                 });
         });
     }
+    
+    static setKeycloakAuth(kc:KeycloakClient) {
+        this.keycloakAuth = kc;
+    }
 
     authenticated(): boolean {
         return KeycloakService.keycloakAuth.authenticated;
     }
 
-    login() {
-        KeycloakService.keycloakAuth.login();
+    login(options?: KeycloakLoginOptions) {
+        KeycloakService.keycloakAuth.login(options);
     }
 
-    logout() {
-        KeycloakService.keycloakAuth.logout();
+    logout(redirectUri?: string) {
+        KeycloakService.keycloakAuth.logout({redirectUri: redirectUri});
     }
 
     account() {
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/top-nav.component.html b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/top-nav.component.html
index 4b30962..0669b26 100644
--- a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/top-nav.component.html
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/top-nav.component.html
@@ -1,17 +1,17 @@
-
+<!-- Top Nav -->
 
 <nav class="navbar navbar-pf-alt">
     <notification></notification>
 
     <div class="navbar-header">
-        <button (click)="menuClicked()" type="button" class="navbar-toggle">
+        <button *ngIf="keycloakService.authenticated() && showSideNav" (click)="menuClicked()" type="button" class="navbar-toggle">
             <span class="sr-only">Toggle navigation</span>
             <span class="icon-bar"></span>
             <span class="icon-bar"></span>
             <span class="icon-bar"></span>
         </button>
         <a href="http://www.keycloak.org" class="navbar-brand">
-            <img class="navbar-brand-icon" type="image/svg+xml" src="{{resourceUrl}}/app/assets/img/keycloak-logo.png" alt="" width="auto" height="30px"/>
+            <img class="navbar-brand-icon" type="image/svg+xml" src="{{resourceUrl}}/app/assets/img/keycloak-logo-min.png" alt="" width="auto" height="30px"/>
         </a>
     </div>
     <nav class="collapse navbar-collapse">
@@ -21,17 +21,7 @@
             <li *ngIf="referrer.exists()">
                 <a class="nav-item-iconic" href="{{referrer.getUri()}}"><span class="pficon-arrow"></span> {{'backTo' | translate:referrer.getName()}}</a>
             </li>
-            <li class="dropdown">
-                <a class="dropdown-toggle nav-item-iconic" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
-                    <span title="Help" class="fa pficon-help"></span>
-                    <span class="caret"></span>
-                </a>
-                <ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
-                    <li><a href="#">Help</a></li>
-                    <li><a href="#">About</a></li>
-                </ul>
-            </li>
-            <li class="dropdown">
+            <li class="dropdown" (click)="logout()">
                 <a class="dropdown-toggle nav-item-iconic" id="dropdownMenu2" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
                     <span title="Username" class="fa pficon-user"></span>
                     <span class="caret"></span>
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/top-nav.component.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/top-nav.component.ts
index 88a0c14..65f3ea0 100644
--- a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/top-nav.component.ts
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/top-nav.component.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {Component, OnInit} from '@angular/core';
+import {Component, OnInit, Input} from '@angular/core';
 import {TranslateUtil} from '../ngx-translate/translate.util';
 import {KeycloakService} from '../keycloak-service/keycloak.service';
 import {ResponsivenessService} from "../responsiveness-service/responsiveness.service";
@@ -30,6 +30,7 @@ declare const referrer_uri: string;
     styleUrls: ['./top-nav.component.css']
 })
 export class TopNavComponent implements OnInit {
+    @Input() showSideNav: String;
 
     public resourceUrl: string = resourceUrl;
     
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/main.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/main.ts
index b5db7b0..ded194f 100644
--- a/themes/src/main/resources/theme/keycloak-preview/account/resources/main.ts
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/main.ts
@@ -21,7 +21,8 @@ import { platformBrowser } from '@angular/platform-browser';
 import { AppModule } from './app/app.module';
 //import { environment } from './environments/environment';
 
-import { KeycloakService } from './app/keycloak-service/keycloak.service';
+import { KeycloakService, KeycloakClient } from './app/keycloak-service/keycloak.service';
+
 
 //if (environment.production) {
 //  enableProdMode();
@@ -30,17 +31,17 @@ import { KeycloakService } from './app/keycloak-service/keycloak.service';
 declare const authUrl: string;
 declare const resourceUrl: string;
 declare const realm: string;
+declare const keycloak: KeycloakClient;
+
+KeycloakService.setKeycloakAuth(keycloak);
+
+loadCss('/styles.css');
+platformBrowserDynamic().bootstrapModule(AppModule);
 
-const noLogin: boolean = false; // convenient for development
-if (noLogin) {
-    platformBrowserDynamic().bootstrapModule(AppModule);
-} else {
-    KeycloakService.init(authUrl + '/realms/' + realm + '/account/keycloak.json',
-                         {onLoad: 'login-required'})
-        .then(() => {
-            platformBrowserDynamic().bootstrapModule(AppModule);
-        })
-        .catch((e: any) => {
-            console.log('Error in bootstrap: ' + JSON.stringify(e));
-        });
+function loadCss(url:string) {
+    const link = document.createElement("link");
+    link.href = resourceUrl + url;
+    link.rel = "stylesheet";
+    link.media = "screen, print";
+    document.head.appendChild(link);
 }
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/package.json b/themes/src/main/resources/theme/keycloak-preview/account/resources/package.json
index 1ea8c3c..d2d733a 100644
--- a/themes/src/main/resources/theme/keycloak-preview/account/resources/package.json
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/package.json
@@ -24,18 +24,22 @@
   "author": "Stan Silvert",
   "license": "Apache 2.0",
   "dependencies": {
-    "@angular/common": "~4.0.0",
-    "@angular/compiler": "~4.0.0",
-    "@angular/core": "~4.0.0",
-    "@angular/forms": "~4.0.0",
-    "@angular/http": "~4.0.0",
-    "@angular/platform-browser": "~4.0.0",
-    "@angular/platform-browser-dynamic": "~4.0.0",
-    "@angular/router": "~4.0.0",
-    "@ngx-translate/core": "^7.1.0",
+    "@angular/animations": "'5.0.0'",
+    "@angular/common": "'5.0.0'",
+    "@angular/compiler": "'5.0.0'",
+    "@angular/compiler-cli": "'5.0.0'",
+    "@angular/core": "'5.0.0'",
+    "@angular/forms": "'5.0.0'",
+    "@angular/http": "'5.0.0'",
+    "@angular/platform-browser": "'5.0.0'",
+    "@angular/platform-browser-dynamic": "'5.0.0'",
+    "@angular/platform-server": "'5.0.0'",
+    "@angular/router": "'5.0.0'",
+    "@ngx-translate/core": "^9.1.1",
     "core-js": "^2.4.1",
     "patternfly": "^3.23.2",
-    "rxjs": "^5.4.2",
+    "rxjs": "5.5.2",
+    "rxjs-system-bundle": "^5.5.6",
     "systemjs": "^0.20.17",
     "zone.js": "^0.8.4"
   },
@@ -55,7 +59,7 @@
     "protractor": "~4.0.14",
     "rimraf": "^2.5.4",
     "tslint": "^3.15.1",
-    "typescript": "^2.4.2"
+    "typescript": "2.4.2"
   },
   "repository": {}
 }
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/styles.css b/themes/src/main/resources/theme/keycloak-preview/account/resources/styles.css
index 58e1a7d..e69de29 100644
--- a/themes/src/main/resources/theme/keycloak-preview/account/resources/styles.css
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/styles.css
@@ -1,5 +0,0 @@
-h1 {
-  color: #369;
-  font-family: Arial, Helvetica, sans-serif;
-  font-size: 250%;
-}
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/systemjs.config.js b/themes/src/main/resources/theme/keycloak-preview/account/resources/systemjs.config.js
index 7814857..6a100cd 100644
--- a/themes/src/main/resources/theme/keycloak-preview/account/resources/systemjs.config.js
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/systemjs.config.js
@@ -14,20 +14,32 @@
       'app': resourceUrl + '/app',
 
       // angular bundles
-      '@angular/core': 'npm:@angular/core/bundles/core.umd.js',
-      '@angular/common': 'npm:@angular/common/bundles/common.umd.js',
-      '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
-      '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
-      '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
-      '@angular/http': 'npm:@angular/http/bundles/http.umd.js',
-      '@angular/router': 'npm:@angular/router/bundles/router.umd.js',
-      '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
+      '@angular/core': 'npm:@angular/core/bundles/core.umd.min.js',
+      '@angular/common': 'npm:@angular/common/bundles/common.umd.min.js',
+      '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.min.js',
+      '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.min.js',
+      '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.min.js',
+      '@angular/http': 'npm:@angular/http/bundles/http.umd.min.js',
+      '@angular/router': 'npm:@angular/router/bundles/router.umd.min.js',
+      '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.min.js',
       
       // other libraries
-      '@ngx-translate/core':       'npm:@ngx-translate/core/bundles/core.umd.js',
-      'rxjs':                      'npm:rxjs',
+      '@ngx-translate/core':       'npm:@ngx-translate/core/bundles/core.umd.min.js',
       'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js'
     },
+    bundles: {
+        "npm:rxjs-system-bundle/Rx.system.min.js": [
+          "rxjs",
+          "rxjs/*",
+          "rxjs/operator/*",
+          "rxjs/observable/*",
+          "rxjs/scheduler/*",
+          "rxjs/symbol/*",
+          "rxjs/add/operator/*",
+          "rxjs/add/observable/*",
+          "rxjs/util/*"
+        ]
+      },
     // packages tells the System loader how to load when no filename and/or no extension
     packages: {
       app: {
@@ -42,7 +54,7 @@
           defaultExtension: 'js'
       },
       rxjs: {
-        defaultExtension: 'js'
+        defaultExtension: false
       }
     }
   });
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/yarn.lock b/themes/src/main/resources/theme/keycloak-preview/account/resources/yarn.lock
index c335da6..4bdfbbf 100644
--- a/themes/src/main/resources/theme/keycloak-preview/account/resources/yarn.lock
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/yarn.lock
@@ -2,41 +2,80 @@
 # yarn lockfile v1
 
 
-"@angular/common@~4.0.0":
-  version "4.0.3"
-  resolved "https://registry.yarnpkg.com/@angular/common/-/common-4.0.3.tgz#17472895eb425f2812b3a79162b5b494d2506a5b"
+"@angular/animations@'5.0.0'":
+  version "5.2.7"
+  resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-5.2.7.tgz#a99fe128f3809e3f8082441a6e676992b9352db7"
+  dependencies:
+    tslib "^1.7.1"
+
+"@angular/common@'5.0.0'":
+  version "5.2.7"
+  resolved "https://registry.yarnpkg.com/@angular/common/-/common-5.2.7.tgz#11bb9f00afe91af8d772ecdff83c2e179f9a67a0"
+  dependencies:
+    tslib "^1.7.1"
+
+"@angular/compiler-cli@'5.0.0'":
+  version "5.2.7"
+  resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-5.2.7.tgz#3bad9d5e4c25ebc51e1bb67b71b26a97d6d3bbc5"
+  dependencies:
+    chokidar "^1.4.2"
+    minimist "^1.2.0"
+    reflect-metadata "^0.1.2"
+    tsickle "^0.27.2"
+
+"@angular/compiler@'5.0.0'":
+  version "5.2.7"
+  resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-5.2.7.tgz#31dcc7ac18cc23cb115e76c541b982737ff93ec9"
+  dependencies:
+    tslib "^1.7.1"
 
-"@angular/compiler@~4.0.0":
-  version "4.0.3"
-  resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-4.0.3.tgz#8b3cad338ac539328e10a6a4bfaa057094f7bc89"
+"@angular/core@'5.0.0'":
+  version "5.2.7"
+  resolved "https://registry.yarnpkg.com/@angular/core/-/core-5.2.7.tgz#e5607fc39d90f9fe4fbaaeeeb6cdb9371966bfe3"
+  dependencies:
+    tslib "^1.7.1"
 
-"@angular/core@~4.0.0":
-  version "4.0.3"
-  resolved "https://registry.yarnpkg.com/@angular/core/-/core-4.0.3.tgz#61be21db6aa5778e33159ffd38cbbebaf16120d9"
+"@angular/forms@'5.0.0'":
+  version "5.2.7"
+  resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-5.2.7.tgz#a0c41fdb6b8ba2c32653cc3058d5fa47db91fbf8"
+  dependencies:
+    tslib "^1.7.1"
 
-"@angular/forms@~4.0.0":
-  version "4.0.3"
-  resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-4.0.3.tgz#fb8e6e0aede782bf58730a31d1179b323271816e"
+"@angular/http@'5.0.0'":
+  version "5.2.7"
+  resolved "https://registry.yarnpkg.com/@angular/http/-/http-5.2.7.tgz#a163f6958f12d2665419123861b0d613c1c82afb"
+  dependencies:
+    tslib "^1.7.1"
 
-"@angular/http@~4.0.0":
-  version "4.0.3"
-  resolved "https://registry.yarnpkg.com/@angular/http/-/http-4.0.3.tgz#efbb701a215ec7704c021676484b85ed47392f4b"
+"@angular/platform-browser-dynamic@'5.0.0'":
+  version "5.2.7"
+  resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-5.2.7.tgz#91f903c1f73de2fba1004bc31ed1c3117c7f0406"
+  dependencies:
+    tslib "^1.7.1"
 
-"@angular/platform-browser-dynamic@~4.0.0":
-  version "4.0.3"
-  resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-4.0.3.tgz#5fa3b98f725999b631d7d7174e6e43dcbf6aa9ac"
+"@angular/platform-browser@'5.0.0'":
+  version "5.2.7"
+  resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-5.2.7.tgz#cfd86040c25380aabf0322ef47aecc61f23cc532"
+  dependencies:
+    tslib "^1.7.1"
 
-"@angular/platform-browser@~4.0.0":
-  version "4.0.3"
-  resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-4.0.3.tgz#170b18d5af4ee02b248aa6a1f1e0584ac841681e"
+"@angular/platform-server@'5.0.0'":
+  version "5.2.7"
+  resolved "https://registry.yarnpkg.com/@angular/platform-server/-/platform-server-5.2.7.tgz#e2eb290952dbe6ab04b4edae115a0d02b8b2dc4c"
+  dependencies:
+    domino "^1.0.29"
+    tslib "^1.7.1"
+    xhr2 "^0.1.4"
 
-"@angular/router@~4.0.0":
-  version "4.0.3"
-  resolved "https://registry.yarnpkg.com/@angular/router/-/router-4.0.3.tgz#24184e9b1266c4ad017b2be81573464b1e4c5dfa"
+"@angular/router@'5.0.0'":
+  version "5.2.7"
+  resolved "https://registry.yarnpkg.com/@angular/router/-/router-5.2.7.tgz#691c375fe32f01bea56d169469ad8f254b136af9"
+  dependencies:
+    tslib "^1.7.1"
 
-"@ngx-translate/core@^7.1.0":
-  version "7.1.0"
-  resolved "https://registry.yarnpkg.com/@ngx-translate/core/-/core-7.1.0.tgz#5087a65c8ff312e4244ca0646ed45cde83b170cd"
+"@ngx-translate/core@^9.1.1":
+  version "9.1.1"
+  resolved "https://registry.yarnpkg.com/@ngx-translate/core/-/core-9.1.1.tgz#ae103928836b8a9e069fd2e2e76fa2198cc7e628"
 
 "@types/jasmine@2.5.36", "@types/jasmine@^2.5.36":
   version "2.5.36"
@@ -471,7 +510,7 @@ chalk@^1.1.1, chalk@^1.1.3:
     strip-ansi "^3.0.0"
     supports-color "^2.0.0"
 
-chokidar@1.7.0, chokidar@^1.4.1:
+chokidar@1.7.0, chokidar@^1.4.1, chokidar@^1.4.2:
   version "1.7.0"
   resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468"
   dependencies:
@@ -769,6 +808,10 @@ dom-serialize@^2.2.0:
     extend "^3.0.0"
     void-elements "^2.0.0"
 
+domino@^1.0.29:
+  version "1.0.30"
+  resolved "https://registry.yarnpkg.com/domino/-/domino-1.0.30.tgz#54a4154ecae968616680f8feba3cedff355c71f4"
+
 drmonty-datatables-colvis@~1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/drmonty-datatables-colvis/-/drmonty-datatables-colvis-1.1.2.tgz#96ab9edfb48643cc2edda3f87b88933cdee8127c"
@@ -2196,6 +2239,10 @@ readdirp@^2.0.0:
     readable-stream "^2.0.2"
     set-immediate-shim "^1.0.1"
 
+reflect-metadata@^0.1.2:
+  version "0.1.12"
+  resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.12.tgz#311bf0c6b63cd782f228a81abe146a2bfa9c56f2"
+
 regex-cache@^0.4.2:
   version "0.4.3"
   resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d7114145"
@@ -2310,9 +2357,13 @@ rx@4.1.0:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782"
 
-rxjs@^5.4.2:
-  version "5.4.2"
-  resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.4.2.tgz#2a3236fcbf03df57bae06fd6972fd99e5c08fcf7"
+rxjs-system-bundle@^5.5.6:
+  version "5.5.6"
+  resolved "https://registry.yarnpkg.com/rxjs-system-bundle/-/rxjs-system-bundle-5.5.6.tgz#3af9c02a36938f750fe33751cca2397fcf3d04b8"
+
+rxjs@5.5.2:
+  version "5.5.2"
+  resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.2.tgz#28d403f0071121967f18ad665563255d54236ac3"
   dependencies:
     symbol-observable "^1.0.1"
 
@@ -2497,6 +2548,12 @@ socket.io@1.7.3:
     socket.io-client "1.7.3"
     socket.io-parser "2.3.1"
 
+source-map-support@^0.5.0:
+  version "0.5.3"
+  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.3.tgz#2b3d5fff298cfa4d1afd7d4352d569e9a0158e76"
+  dependencies:
+    source-map "^0.6.0"
+
 source-map-support@~0.4.0:
   version "0.4.15"
   resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.15.tgz#03202df65c06d2bd8c7ec2362a193056fef8d3b1"
@@ -2507,6 +2564,10 @@ source-map@^0.5.3, source-map@^0.5.6:
   version "0.5.6"
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
 
+source-map@^0.6.0:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+
 spawn-command@^0.0.2-1:
   version "0.0.2"
   resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2.tgz#9544e1a43ca045f8531aac1a48cb29bdae62338e"
@@ -2614,8 +2675,8 @@ supports-color@^3.2.3:
     has-flag "^1.0.0"
 
 symbol-observable@^1.0.1:
-  version "1.0.4"
-  resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d"
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
 
 systemjs@^0.20.17:
   version "0.20.17"
@@ -2673,6 +2734,19 @@ tree-kill@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.1.0.tgz#c963dcf03722892ec59cba569e940b71954d1729"
 
+tsickle@^0.27.2:
+  version "0.27.2"
+  resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.27.2.tgz#f33d46d046f73dd5c155a37922e422816e878736"
+  dependencies:
+    minimist "^1.2.0"
+    mkdirp "^0.5.1"
+    source-map "^0.6.0"
+    source-map-support "^0.5.0"
+
+tslib@^1.7.1:
+  version "1.9.0"
+  resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.0.tgz#e37a86fda8cbbaf23a057f473c9f4dc64e5fc2e8"
+
 tslint@^3.15.1:
   version "3.15.1"
   resolved "https://registry.yarnpkg.com/tslint/-/tslint-3.15.1.tgz#da165ca93d8fdc2c086b51165ee1bacb48c98ea5"
@@ -2706,7 +2780,7 @@ type-is@~1.6.14:
     media-typer "0.3.0"
     mime-types "~2.1.15"
 
-typescript@^2.4.2:
+typescript@2.4.2:
   version "2.4.2"
   resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.4.2.tgz#f8395f85d459276067c988aa41837a8f82870844"
 
@@ -2868,6 +2942,10 @@ wtf-8@1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/wtf-8/-/wtf-8-1.0.0.tgz#392d8ba2d0f1c34d1ee2d630f15d0efb68e1048a"
 
+xhr2@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/xhr2/-/xhr2-0.1.4.tgz#7f87658847716db5026323812f818cadab387a5f"
+
 xml2js@0.4.4:
   version "0.4.4"
   resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.4.tgz#3111010003008ae19240eba17497b57c729c555d"
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/theme.properties b/themes/src/main/resources/theme/keycloak-preview/account/theme.properties
index 4cd0e08..fb2e245 100644
--- a/themes/src/main/resources/theme/keycloak-preview/account/theme.properties
+++ b/themes/src/main/resources/theme/keycloak-preview/account/theme.properties
@@ -1,3 +1,2 @@
 parent=base
-import=login/keycloak
 deprecatedMode=false
\ No newline at end of file