keycloak-memoizeit

merge conflicts

4/15/2015 12:18:47 PM

Changes

misc/Testsuite.md 10(+3 -7)

README.md 107(+70 -37)

services/src/main/java/org/keycloak/services/resources/flows/OAuthRedirect.java 76(+0 -76)

Details

diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java
index 60b1f4a..95e1f6a 100755
--- a/broker/oidc/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java
@@ -33,8 +33,8 @@ import org.keycloak.events.EventType;
 import org.keycloak.models.FederatedIdentityModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
+import org.keycloak.services.ErrorPage;
 import org.keycloak.services.messages.Messages;
-import org.keycloak.services.resources.flows.Flows;
 
 import javax.ws.rs.GET;
 import javax.ws.rs.QueryParam;
@@ -215,7 +215,7 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
                 //logger.error("Failed " + getConfig().getAlias() + " broker login: " + error);
                 event.event(EventType.LOGIN);
                 event.error(error);
-                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
+                return ErrorPage.error(session, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
             }
 
             try {
@@ -240,7 +240,7 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
             }
             event.event(EventType.LOGIN);
             event.error(Errors.IDENTITY_PROVIDER_LOGIN_FAILURE);
-            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
+            return ErrorPage.error(session, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
         }
 
         public SimpleHttp generateTokenRequest(String authorizationCode) {
diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java
index eb73c0a..2d0c16b 100755
--- a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java
@@ -30,16 +30,15 @@ import org.keycloak.jose.jws.JWSInput;
 import org.keycloak.jose.jws.crypto.RSAProvider;
 import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.RealmModel;
-import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.representations.AccessTokenResponse;
 import org.keycloak.representations.IDToken;
 import org.keycloak.representations.JsonWebToken;
+import org.keycloak.services.ErrorPage;
 import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.messages.Messages;
 import org.keycloak.services.resources.IdentityBrokerService;
 import org.keycloak.services.resources.RealmsResource;
-import org.keycloak.services.resources.flows.Flows;
 import org.keycloak.util.JsonSerialization;
 import org.keycloak.util.PemUtils;
 
@@ -120,14 +119,14 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
                 EventBuilder event = new EventBuilder(realm, session, clientConnection);
                 event.event(EventType.LOGOUT);
                 event.error(Errors.USER_SESSION_NOT_FOUND);
-                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
+                return ErrorPage.error(session, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
             }
             if (userSession.getState() != UserSessionModel.State.LOGGING_OUT) {
                 logger.error("usersession in different state");
                 EventBuilder event = new EventBuilder(realm, session, clientConnection);
                 event.event(EventType.LOGOUT);
                 event.error(Errors.USER_SESSION_NOT_FOUND);
-                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.SESSION_NOT_ACTIVE);
+                return ErrorPage.error(session, Messages.SESSION_NOT_ACTIVE);
             }
             return AuthenticationManager.finishBrowserLogout(session, realm, userSession, uriInfo, clientConnection, headers);
         }
diff --git a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java
index b13ff09..e2a1902 100755
--- a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java
+++ b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java
@@ -39,12 +39,15 @@ import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
 import org.keycloak.saml.processing.core.util.JAXPValidationUtil;
 import org.keycloak.saml.processing.core.util.XMLEncryptionUtil;
 import org.keycloak.saml.processing.core.util.XMLSignatureUtil;
+import org.keycloak.services.ErrorPage;
 import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.messages.Messages;
-import org.keycloak.services.resources.flows.Flows;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
+import org.keycloak.services.ErrorPage;
+import org.keycloak.services.managers.AuthenticationManager;
+import org.keycloak.services.messages.Messages;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.FormParam;
@@ -133,18 +136,18 @@ public class SAMLEndpoint {
             if (!checkSsl()) {
                 event.event(EventType.LOGIN);
                 event.error(Errors.SSL_REQUIRED);
-                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.HTTPS_REQUIRED);
+                return ErrorPage.error(session, Messages.HTTPS_REQUIRED);
             }
             if (!realm.isEnabled()) {
                 event.event(EventType.LOGIN_ERROR);
                 event.error(Errors.REALM_DISABLED);
-                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.REALM_NOT_ENABLED);
+                return ErrorPage.error(session, Messages.REALM_NOT_ENABLED);
             }
 
             if (samlRequest == null && samlResponse == null) {
                 event.event(EventType.LOGIN);
                 event.error(Errors.INVALID_REQUEST);
-                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST );
+                return ErrorPage.error(session, Messages.INVALID_REQUEST);
 
             }
             return null;
@@ -180,7 +183,7 @@ public class SAMLEndpoint {
                 event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
                 event.error(Errors.INVALID_SAML_RESPONSE);
                 event.detail(Details.REASON, "invalid_destination");
-                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST);
+                return ErrorPage.error(session, Messages.INVALID_REQUEST);
             }
             if (config.isValidateSignature()) {
                 try {
@@ -189,7 +192,7 @@ public class SAMLEndpoint {
                     logger.error("validation failed", e);
                     event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
                     event.error(Errors.INVALID_SIGNATURE);
-                    return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUESTER);
+                    return ErrorPage.error(session, Messages.INVALID_REQUESTER);
                 }
             }
 
@@ -202,7 +205,7 @@ public class SAMLEndpoint {
             } else {
                 event.event(EventType.LOGIN);
                 event.error(Errors.INVALID_TOKEN);
-                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST);
+                return ErrorPage.error(session, Messages.INVALID_REQUEST);
             }
         }
 
@@ -335,7 +338,7 @@ public class SAMLEndpoint {
                 event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
                 event.error(Errors.INVALID_SAML_RESPONSE);
                 event.detail(Details.REASON, "invalid_destination");
-                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_FEDERATED_IDENTITY_ACTION);
+                return ErrorPage.error(session, Messages.INVALID_FEDERATED_IDENTITY_ACTION);
             }
             if (config.isValidateSignature()) {
                 try {
@@ -344,7 +347,7 @@ public class SAMLEndpoint {
                     logger.error("validation failed", e);
                     event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
                     event.error(Errors.INVALID_SIGNATURE);
-                    return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_FEDERATED_IDENTITY_ACTION);
+                    return ErrorPage.error(session, Messages.INVALID_FEDERATED_IDENTITY_ACTION);
                 }
             }
             if (statusResponse instanceof ResponseType) {
@@ -363,20 +366,20 @@ public class SAMLEndpoint {
                 logger.error("no valid user session");
                 event.event(EventType.LOGOUT);
                 event.error(Errors.USER_SESSION_NOT_FOUND);
-                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
+                return ErrorPage.error(session, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
             }
             UserSessionModel userSession = session.sessions().getUserSession(realm, relayState);
             if (userSession == null) {
                 logger.error("no valid user session");
                 event.event(EventType.LOGOUT);
                 event.error(Errors.USER_SESSION_NOT_FOUND);
-                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
+                return ErrorPage.error(session, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
             }
             if (userSession.getState() != UserSessionModel.State.LOGGING_OUT) {
                 logger.error("usersession in different state");
                 event.event(EventType.LOGOUT);
                 event.error(Errors.USER_SESSION_NOT_FOUND);
-                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.SESSION_NOT_ACTIVE);
+                return ErrorPage.error(session, Messages.SESSION_NOT_ACTIVE);
             }
             return AuthenticationManager.finishBrowserLogout(session, realm, userSession, uriInfo, clientConnection, headers);
         }
diff --git a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
index f88c45d..98fd6ef 100755
--- a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
+++ b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
@@ -30,7 +30,6 @@ import org.keycloak.events.EventBuilder;
 import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.FederatedIdentityModel;
 import org.keycloak.models.RealmModel;
-import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.protocol.saml.SAML2AuthnRequestBuilder;
 import org.keycloak.protocol.saml.SAML2LogoutRequestBuilder;
diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.RC1.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.RC1.xml
index 6fc69db..aa78acb 100755
--- a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.RC1.xml
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.RC1.xml
@@ -32,6 +32,9 @@
                 <constraints nullable="false"/>
             </column>
         </createTable>
+        <addColumn tableName="CREDENTIAL">
+            <column name="CREATED_DATE" type="BIGINT"/>
+        </addColumn>
         <addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_IDPM" tableName="IDENTITY_PROVIDER_MAPPER"/>
         <addPrimaryKey columnNames="IDP_MAPPER_ID, NAME" constraintName="CONSTRAINT_IDPMConfig" tableName="IDP_MAPPER_CONFIG"/>
         <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="IDENTITY_PROVIDER_MAPPER" constraintName="FK_IDPM_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
@@ -48,5 +51,23 @@
         </update>
         <dropColumn tableName="CLIENT" columnName="DTYPE"/>
         <renameColumn tableName="CLIENT" newColumnName="CLIENT_ID" oldColumnName="NAME"/>
+        <renameColumn tableName="REALM" newColumnName="MASTER_ADMIN_CLIENT" oldColumnName="MASTER_ADMIN_APP"/>
+
+        <renameTable oldTableName="REALM_APPLICATION" newTableName="REALM_CLIENT"/>
+        <renameColumn tableName="REALM_CLIENT" newColumnName="CLIENT_ID" oldColumnName="APPLICATION_ID"/>
+
+        <renameTable oldTableName="APPLICATION_DEFAULT_ROLES" newTableName="CLIENT_DEFAULT_ROLES"/>
+        <renameColumn tableName="CLIENT_DEFAULT_ROLES" newColumnName="CLIENT_ID" oldColumnName="APPLICATION_ID"/>
+
+        <renameTable oldTableName="APP_NODE_REGISTRATIONS" newTableName="CLIENT_NODE_REGISTRATIONS"/>
+        <renameColumn tableName="CLIENT_NODE_REGISTRATIONS" newColumnName="CLIENT_ID" oldColumnName="APPLICATION_ID"/>
+
+        <renameColumn tableName="KEYCLOAK_ROLE" newColumnName="CLIENT" oldColumnName="APPLICATION"/>
+        <renameColumn tableName="KEYCLOAK_ROLE" newColumnName="CLIENT_ROLE" oldColumnName="APPLICATION_ROLE"/>
+        <renameColumn tableName="KEYCLOAK_ROLE" newColumnName="CLIENT_REALM_CONSTRAINT" oldColumnName="APP_REALM_CONSTRAINT"/>
+
+        <dropUniqueConstraint tableName="KEYCLOAK_ROLE" constraintName="UK_J3RWUVD56ONTGSUHOGM184WW2"/>
+        <addUniqueConstraint columnNames="NAME,CLIENT_REALM_CONSTRAINT" constraintName="UK_J3RWUVD56ONTGSUHOGM184WW2-2" tableName="KEYCLOAK_ROLE"/>
+
     </changeSet>
 </databaseChangeLog>
diff --git a/connections/mongo-update/src/main/java/org/keycloak/connections/mongo/updater/impl/updates/Update1_2_0_RC1.java b/connections/mongo-update/src/main/java/org/keycloak/connections/mongo/updater/impl/updates/Update1_2_0_RC1.java
index 5954199..c980067 100644
--- a/connections/mongo-update/src/main/java/org/keycloak/connections/mongo/updater/impl/updates/Update1_2_0_RC1.java
+++ b/connections/mongo-update/src/main/java/org/keycloak/connections/mongo/updater/impl/updates/Update1_2_0_RC1.java
@@ -19,6 +19,9 @@ public class Update1_2_0_RC1 extends Update {
     public void update(KeycloakSession session) {
         convertApplicationsToClients();
         convertOAuthClientsToClients();
+
+        db.getCollection("realms").update(new BasicDBObject(), new BasicDBObject("$rename", new BasicDBObject("adminAppId", "clientId")), false, true);
+
     }
 
     private void convertApplicationsToClients() {
@@ -31,6 +34,10 @@ public class Update1_2_0_RC1 extends Update {
         DBCollection roles = db.getCollection("roles");
         roles.update(new BasicDBObject(), new BasicDBObject("$rename", new BasicDBObject("applicationId", "clientId")), false, true);
         log.debugv("Renamed roles.applicationId to roles.clientId");
+
+        db.getCollection("clients").dropIndex("realmId_1_name_1");
+        ensureIndex("clients", new String[]{"realmId", "clientId"}, true, false);
+
     }
 
     private void convertOAuthClientsToClients() {
diff --git a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-events-jboss-logging/main/module.xml b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-events-jboss-logging/main/module.xml
index b786cfc..00943ab 100755
--- a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-events-jboss-logging/main/module.xml
+++ b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-events-jboss-logging/main/module.xml
@@ -12,6 +12,7 @@
         <module name="org.keycloak.keycloak-events-api"/>
         <module name="org.jboss.logging"/>
         <module name="javax.api"/>
+        <module name="javax.ws.rs.api"/>
     </dependencies>
 
 </module>
diff --git a/docbook/reference/en/en-US/modules/security-vulnerabilities.xml b/docbook/reference/en/en-US/modules/security-vulnerabilities.xml
index 359c55e..0756ebd 100755
--- a/docbook/reference/en/en-US/modules/security-vulnerabilities.xml
+++ b/docbook/reference/en/en-US/modules/security-vulnerabilities.xml
@@ -128,7 +128,8 @@
         <para>
             In the admin console, per realm, you can set up a password policy to enforce that users pick hard to guess passwords.
             A password has to match all policies. The password policies that can be configured are hash iterations, length, digits,
-            lowercase, uppercase, special characters, not username and regex patterns. Multiple regex patterns, separated by comma,
+            lowercase, uppercase, special characters, not username, regex patterns and expired passwords. Expired Passwords policy 
+            restricts a user from resetting his password to N old expired passwords. Multiple regex patterns, separated by comma, 
             can be specified. If there's more than one regex added, password has to match all fully.
             Increasing number of Hash Iterations (n) does not worsen anything (and certainly not the cipher) and it greatly increases the 
             resistance to dictionary attacks. However the drawback to increasing n is that it has some cost (CPU usage, energy, delay) for 
diff --git a/events/jboss-logging/pom.xml b/events/jboss-logging/pom.xml
index cf05f96..0f407d4 100755
--- a/events/jboss-logging/pom.xml
+++ b/events/jboss-logging/pom.xml
@@ -37,6 +37,11 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-jaxrs</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
             <scope>test</scope>
diff --git a/events/jboss-logging/src/main/java/org/keycloak/events/log/JBossLoggingEventListenerProvider.java b/events/jboss-logging/src/main/java/org/keycloak/events/log/JBossLoggingEventListenerProvider.java
index badc0ee..dba4304 100755
--- a/events/jboss-logging/src/main/java/org/keycloak/events/log/JBossLoggingEventListenerProvider.java
+++ b/events/jboss-logging/src/main/java/org/keycloak/events/log/JBossLoggingEventListenerProvider.java
@@ -3,23 +3,35 @@ package org.keycloak.events.log;
 import org.jboss.logging.Logger;
 import org.keycloak.events.Event;
 import org.keycloak.events.EventListenerProvider;
+import org.keycloak.models.KeycloakContext;
+import org.keycloak.models.KeycloakSession;
 
+import javax.ws.rs.core.Cookie;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.UriInfo;
 import java.util.Map;
+import java.util.logging.Level;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
 public class JBossLoggingEventListenerProvider implements EventListenerProvider {
 
+    private final KeycloakSession session;
     private final Logger logger;
+    private final Logger.Level successLevel;
+    private final Logger.Level errorLevel;
 
-    public JBossLoggingEventListenerProvider(Logger logger) {
+    public JBossLoggingEventListenerProvider(KeycloakSession session, Logger logger, Logger.Level successLevel, Logger.Level errorLevel) {
+        this.session = session;
         this.logger = logger;
+        this.successLevel = successLevel;
+        this.errorLevel = errorLevel;
     }
 
     @Override
     public void onEvent(Event event) {
-        Logger.Level level = event.getError() != null ? Logger.Level.WARN : Logger.Level.INFO;
+        Logger.Level level = event.getError() != null ? errorLevel : successLevel;
 
         if (logger.isEnabled(level)) {
             StringBuilder sb = new StringBuilder();
@@ -55,7 +67,31 @@ public class JBossLoggingEventListenerProvider implements EventListenerProvider 
                 }
             }
 
-            logger.log(level, sb.toString());
+            if (logger.isTraceEnabled()) {
+                KeycloakContext context = session.getContext();
+                UriInfo uriInfo = context.getUri();
+                HttpHeaders headers = context.getRequestHeaders();
+                if (uriInfo != null) {
+                    sb.append(", requestUri=");
+                    sb.append(uriInfo.getRequestUri().toString());
+                }
+
+                if (headers != null) {
+                    sb.append(", cookies=[");
+                    boolean f = true;
+                    for (Map.Entry<String, Cookie> e : headers.getCookies().entrySet()) {
+                        if (f) {
+                            f = false;
+                        } else {
+                            sb.append(", ");
+                        }
+                        sb.append(e.getValue().toString());
+                    }
+                    sb.append("]");
+                }
+            }
+
+            logger.log(logger.isTraceEnabled() ? Logger.Level.TRACE : level, sb.toString());
         }
     }
 
diff --git a/events/jboss-logging/src/main/java/org/keycloak/events/log/JBossLoggingEventListenerProviderFactory.java b/events/jboss-logging/src/main/java/org/keycloak/events/log/JBossLoggingEventListenerProviderFactory.java
index 1c91cea..07ae4e2 100755
--- a/events/jboss-logging/src/main/java/org/keycloak/events/log/JBossLoggingEventListenerProviderFactory.java
+++ b/events/jboss-logging/src/main/java/org/keycloak/events/log/JBossLoggingEventListenerProviderFactory.java
@@ -6,6 +6,9 @@ import org.keycloak.events.EventListenerProvider;
 import org.keycloak.events.EventListenerProviderFactory;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
+import sun.rmi.runtime.Log;
+
+import java.util.logging.Level;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@@ -16,13 +19,18 @@ public class JBossLoggingEventListenerProviderFactory implements EventListenerPr
 
     private static final Logger logger = Logger.getLogger("org.keycloak.events");
 
+    private Logger.Level successLevel;
+    private Logger.Level errorLevel;
+
     @Override
     public EventListenerProvider create(KeycloakSession session) {
-        return new JBossLoggingEventListenerProvider(logger);
+        return new JBossLoggingEventListenerProvider(session, logger, successLevel, errorLevel);
     }
 
     @Override
     public void init(Config.Scope config) {
+        successLevel = Logger.Level.valueOf(config.get("success-level", "debug").toUpperCase());
+        errorLevel = Logger.Level.valueOf(config.get("error-level", "warn").toUpperCase());
     }
 
     @Override
diff --git a/examples/admin-client/example-realm.json b/examples/admin-client/example-realm.json
index 3317f48..3ce2eaa 100755
--- a/examples/admin-client/example-realm.json
+++ b/examples/admin-client/example-realm.json
@@ -24,7 +24,7 @@
     ],
     "clients": [
         {
-            "name": "examples-admin-client",
+            "clientId": "examples-admin-client",
             "enabled": true,
             "fullScopeAllowed": true,
             "baseUrl": "/examples-admin-client",
diff --git a/examples/basic-auth/basicauthrealm.json b/examples/basic-auth/basicauthrealm.json
index 0ed1bf5..b8b2acd 100644
--- a/examples/basic-auth/basicauthrealm.json
+++ b/examples/basic-auth/basicauthrealm.json
@@ -43,7 +43,7 @@
     },
     "clients": [
         {
-            "name": "basic-auth-service",
+            "clientId": "basic-auth-service",
             "enabled": true,
             "adminUrl": "/basicauth",
             "baseUrl": "/basicauth",
diff --git a/examples/broker/facebook-authentication/facebook-identity-provider-realm.json b/examples/broker/facebook-authentication/facebook-identity-provider-realm.json
index cc8c636..580ab5a 100644
--- a/examples/broker/facebook-authentication/facebook-identity-provider-realm.json
+++ b/examples/broker/facebook-authentication/facebook-identity-provider-realm.json
@@ -32,7 +32,7 @@
     },
     "clients": [
         {
-            "name": "facebook-authentication",
+            "clientId": "facebook-authentication",
             "enabled": true,
             "publicClient" : true,
             "adminUrl": "/facebook-authentication",
diff --git a/examples/broker/google-authentication/google-identity-provider-realm.json b/examples/broker/google-authentication/google-identity-provider-realm.json
index a721e34..7db619f 100644
--- a/examples/broker/google-authentication/google-identity-provider-realm.json
+++ b/examples/broker/google-authentication/google-identity-provider-realm.json
@@ -32,7 +32,7 @@
     },
     "clients": [
         {
-            "name": "google-authentication",
+            "clientId": "google-authentication",
             "enabled": true,
             "publicClient" : true,
             "adminUrl": "/google-authentication",
diff --git a/examples/broker/saml-broker-authentication/saml-broker-authentication-realm.json b/examples/broker/saml-broker-authentication/saml-broker-authentication-realm.json
index ad757ab..dac9e28 100644
--- a/examples/broker/saml-broker-authentication/saml-broker-authentication-realm.json
+++ b/examples/broker/saml-broker-authentication/saml-broker-authentication-realm.json
@@ -32,7 +32,7 @@
     },
     "clients": [
         {
-            "name": "saml-broker-authentication",
+            "clientId": "saml-broker-authentication",
             "enabled": true,
             "publicClient" : true,
             "adminUrl": "/saml-broker-authentication",
diff --git a/examples/broker/saml-broker-authentication/saml-broker-realm.json b/examples/broker/saml-broker-authentication/saml-broker-realm.json
index 8d0bad4..7ac492b 100644
--- a/examples/broker/saml-broker-authentication/saml-broker-realm.json
+++ b/examples/broker/saml-broker-authentication/saml-broker-realm.json
@@ -28,7 +28,7 @@
     },
     "clients": [
         {
-            "name": "http://localhost:8080/auth/realms/saml-broker-authentication-realm",
+            "clientId": "http://localhost:8080/auth/realms/saml-broker-authentication-realm",
             "protocol": "saml",
             "enabled": true,
             "redirectUris": [
diff --git a/examples/broker/twitter-authentication/twitter-identity-provider-realm.json b/examples/broker/twitter-authentication/twitter-identity-provider-realm.json
index f9add02..e4c94cb 100644
--- a/examples/broker/twitter-authentication/twitter-identity-provider-realm.json
+++ b/examples/broker/twitter-authentication/twitter-identity-provider-realm.json
@@ -33,7 +33,7 @@
     },
     "clients": [
         {
-            "name": "twitter-authentication",
+            "clientId": "twitter-authentication",
             "enabled": true,
             "publicClient" : true,
             "adminUrl": "/twitter-authentication",
@@ -52,7 +52,7 @@
             ]
         },
         {
-          "name": "admin-client",
+          "clientId": "admin-client",
           "enabled": true,
           "fullScopeAllowed": true,
           "baseUrl": "/admin-client",
diff --git a/examples/cordova/example-realm.json b/examples/cordova/example-realm.json
index d07a5d3..13b2864 100755
--- a/examples/cordova/example-realm.json
+++ b/examples/cordova/example-realm.json
@@ -43,7 +43,7 @@
     ],
     "clients": [
         {
-            "name": "cordova",
+            "clientId": "cordova",
             "enabled": true,
             "publicClient": true,
             "redirectUris": ["http://localhost"],
diff --git a/examples/cors/cors-realm.json b/examples/cors/cors-realm.json
index 9e59580..05321fb 100755
--- a/examples/cors/cors-realm.json
+++ b/examples/cors/cors-realm.json
@@ -44,7 +44,7 @@
     ],
     "clients": [
         {
-            "name": "angular-cors-product",
+            "clientId": "angular-cors-product",
             "enabled": true,
             "publicClient": true,
             "baseUrl": "http://localhost:8080/angular-cors-product/index.html",
diff --git a/examples/demo-template/testrealm.json b/examples/demo-template/testrealm.json
index 4b4d669..92643aa 100755
--- a/examples/demo-template/testrealm.json
+++ b/examples/demo-template/testrealm.json
@@ -94,7 +94,7 @@
     ],
     "clients": [
         {
-            "name": "customer-portal",
+            "clientId": "customer-portal",
             "enabled": true,
             "adminUrl": "/customer-portal",
             "baseUrl": "/customer-portal",
@@ -104,7 +104,7 @@
             "secret": "password"
         },
         {
-            "name": "customer-portal-js",
+            "clientId": "customer-portal-js",
             "enabled": true,
             "publicClient": true,
             "baseUrl": "/customer-portal-js",
@@ -113,7 +113,7 @@
             ]
         },
         {
-            "name": "angular-product",
+            "clientId": "angular-product",
             "enabled": true,
             "publicClient": true,
             "baseUrl": "/angular-product/index.html",
@@ -122,7 +122,7 @@
             ]
         },
         {
-            "name": "customer-portal-cli",
+            "clientId": "customer-portal-cli",
             "enabled": true,
             "publicClient": true,
             "redirectUris": [
@@ -131,7 +131,7 @@
             ]
         },
         {
-            "name": "product-portal",
+            "clientId": "product-portal",
             "enabled": true,
             "adminUrl": "/product-portal",
             "baseUrl": "/product-portal",
@@ -141,14 +141,14 @@
             "secret": "password"
         },
         {
-            "name": "database-service",
+            "clientId": "database-service",
             "enabled": true,
             "adminUrl": "/database",
             "baseUrl": "/database",
             "bearerOnly": true
         },
         {
-            "name": "third-party",
+            "clientId": "third-party",
             "enabled": true,
             "consentRequired": true,
             "redirectUris": [
@@ -158,7 +158,7 @@
             "secret": "password"
         },
         {
-            "name": "admin-client",
+            "clientId": "admin-client",
             "enabled": true,
             "publicClient": true,
             "directGrantsOnly": true,
diff --git a/examples/fuse/testrealm.json b/examples/fuse/testrealm.json
index d474e66..f1b21ee 100644
--- a/examples/fuse/testrealm.json
+++ b/examples/fuse/testrealm.json
@@ -136,7 +136,7 @@
     },
     "clients": [
         {
-            "name": "customer-portal",
+            "clientId": "customer-portal",
             "enabled": true,
             "adminUrl": "http://localhost:8181/customer-portal",
             "baseUrl": "http://localhost:8181/customer-portal",
@@ -146,7 +146,7 @@
             "secret": "password"
         },
         {
-            "name": "product-portal",
+            "clientId": "product-portal",
             "enabled": true,
             "adminUrl": "http://localhost:8181/product-portal",
             "baseUrl": "http://localhost:8181/product-portal",
@@ -156,7 +156,7 @@
             "secret": "password"
         },
         {
-            "name": "builtin-cxf-app",
+            "clientId": "builtin-cxf-app",
             "enabled": true,
             "adminUrl": "http://localhost:8181/cxf",
             "baseUrl": "http://localhost:8181/cxf",
@@ -166,21 +166,21 @@
             "secret": "password"
         },
         {
-            "name": "custom-cxf-endpoint",
+            "clientId": "custom-cxf-endpoint",
             "enabled": true,
             "adminUrl": "http://localhost:8282/PersonServiceCF",
             "baseUrl": "http://localhost:8282/PersonServiceCF",
             "bearerOnly": true
         },
         {
-            "name": "admin-camel-endpoint",
+            "clientId": "admin-camel-endpoint",
             "enabled": true,
             "adminUrl": "http://localhost:8383/admin-camel-endpoint",
             "baseUrl": "http://localhost:8383/admin-camel-endpoint",
             "bearerOnly": true
         },
         {
-            "name": "ssh-jmx-admin-client",
+            "clientId": "ssh-jmx-admin-client",
             "enabled": true,
             "publicClient": false,
             "directGrantsOnly": true,
@@ -194,4 +194,4 @@
             "roles": [ "admin", "jmxAdmin" ]
         }
     ]
-}
\ No newline at end of file
+}
diff --git a/examples/js-console/example-realm.json b/examples/js-console/example-realm.json
index 81c92aa..260e938 100755
--- a/examples/js-console/example-realm.json
+++ b/examples/js-console/example-realm.json
@@ -43,7 +43,7 @@
     ],
     "clients": [
         {
-            "name": "js-console",
+            "clientId": "js-console",
             "enabled": true,
             "publicClient": true,
             "baseUrl": "/js-console",
diff --git a/examples/kerberos/kerberosrealm.json b/examples/kerberos/kerberosrealm.json
index 006961c..6b2b999 100644
--- a/examples/kerberos/kerberosrealm.json
+++ b/examples/kerberos/kerberosrealm.json
@@ -15,7 +15,7 @@
     ],
     "clients": [
         {
-            "name": "kerberos-app",
+            "clientId": "kerberos-app",
             "enabled": true,
             "baseUrl": "/kerberos-portal",
             "redirectUris": [
@@ -91,4 +91,4 @@
             }
         }
     ]
-}
\ No newline at end of file
+}
diff --git a/examples/multi-tenant/tenant1-realm.json b/examples/multi-tenant/tenant1-realm.json
index e759b12..8140b51 100644
--- a/examples/multi-tenant/tenant1-realm.json
+++ b/examples/multi-tenant/tenant1-realm.json
@@ -42,7 +42,7 @@
     ],
     "clients": [
         {
-            "name": "multi-tenant",
+            "clientId": "multi-tenant",
             "enabled": true,
             "adminUrl": "/multitenant/tenant1",
             "baseUrl": "/multitenant/tenant1",
diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProvider.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProvider.java
index cb3949a..fe38f2c 100755
--- a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProvider.java
+++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProvider.java
@@ -47,7 +47,7 @@ import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.models.utils.FormMessage;
-import org.keycloak.services.resources.flows.Urls;
+import org.keycloak.services.Urls;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccountFederatedIdentityBean.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccountFederatedIdentityBean.java
index 685ff45..1c779ba 100755
--- a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccountFederatedIdentityBean.java
+++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccountFederatedIdentityBean.java
@@ -6,7 +6,7 @@ import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.services.resources.AccountService;
-import org.keycloak.services.resources.flows.Urls;
+import org.keycloak.services.Urls;
 
 import javax.ws.rs.core.UriBuilder;
 import java.net.URI;
diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/UrlBean.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/UrlBean.java
index de7e432..001ffde 100755
--- a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/UrlBean.java
+++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/UrlBean.java
@@ -2,7 +2,7 @@ package org.keycloak.account.freemarker.model;
 
 import org.keycloak.freemarker.Theme;
 import org.keycloak.models.RealmModel;
-import org.keycloak.services.resources.flows.Urls;
+import org.keycloak.services.Urls;
 
 import java.net.URI;
 
diff --git a/forms/common-themes/src/main/resources/theme/base/account/messages/messages_de.properties b/forms/common-themes/src/main/resources/theme/base/account/messages/messages_de.properties
index 6145692..27902dd 100644
--- a/forms/common-themes/src/main/resources/theme/base/account/messages/messages_de.properties
+++ b/forms/common-themes/src/main/resources/theme/base/account/messages/messages_de.properties
@@ -89,12 +89,14 @@ identityProviderRemovedMessage=Identity Provider erfolgreich entfernt.
 accountDisabledMessage=Benutzerkonto ist gesperrt, bitte kontaktieren Sie den Admin.
 
 accountTemporarilyDisabledMessage=Benutzerkonto ist tempor\u00E4r gesperrt, bitte kontaktieren Sie den Admin oder versuchen Sie es sp\u00E4ter noch einmal.
-invalidPasswordMinLengthMessage=Ung\u00FCltiges Passwort: minimum l\u00E4nge {0}.
-invalidPasswordMinDigitsMessage=Ung\u00FCltiges Passwort: muss mindestens {0} Zahl(en) beinhalten.
-invalidPasswordMinLowerCaseCharsMessage=Ung\u00FCltiges Passwort: muss mindestens {0} Kleinbuchstaben beinhalten.
-invalidPasswordMinUpperCaseCharsMessage=Ung\u00FCltiges Passwort: muss mindestens {0} Grossbuchstaben beinhalten.
+invalidPasswordMinLengthMessage=Ung\u00FCltiges Passwort\: minimum l\u00E4nge {0}.
+invalidPasswordMinDigitsMessage=Ung\u00FCltiges Passwort\: muss mindestens {0} Zahl(en) beinhalten.
+invalidPasswordMinLowerCaseCharsMessage=Ung\u00FCltiges Passwort\: muss mindestens {0} Kleinbuchstaben beinhalten.
+invalidPasswordMinUpperCaseCharsMessage=Ung\u00FCltiges Passwort\: muss mindestens {0} Grossbuchstaben beinhalten.
 invalidPasswordMinSpecialCharsMessage=Ung\u00FCltiges Passwort\: muss mindestens {0} Spezialzeichen beinhalten.
 invalidPasswordNotUsernameMessage=Ung\u00FCltiges Passwort\: darf nicht gleich sein wie Benutzername.
+invalidPasswordRegexPatternMessage=Ung\u00FCltiges Passwort\: nicht Regex-Muster (n) entsprechen.
+invalidPasswordHistoryMessage=Ung\u00FCltiges Passwort: darf nicht gleich einem der letzten {0} Passwortgeschichte.
 
 locale_de=Deutsch
 locale_en=Englisch
diff --git a/forms/common-themes/src/main/resources/theme/base/account/messages/messages_en.properties b/forms/common-themes/src/main/resources/theme/base/account/messages/messages_en.properties
index 48a51aa..713904b 100755
--- a/forms/common-themes/src/main/resources/theme/base/account/messages/messages_en.properties
+++ b/forms/common-themes/src/main/resources/theme/base/account/messages/messages_en.properties
@@ -93,7 +93,9 @@ invalidPasswordMinLowerCaseCharsMessage=Invalid password: must contain at least 
 invalidPasswordMinDigitsMessage=Invalid password: must contain at least {0} numerical digits.
 invalidPasswordMinUpperCaseCharsMessage=Invalid password: must contain at least {0} upper case characters.
 invalidPasswordMinSpecialCharsMessage=Invalid password: must contain at least {0} special characters.
-invalidPasswordNotUsernameMessage=Invalid password\: must not be equal to the username.
+invalidPasswordNotUsernameMessage=Invalid password: must not be equal to the username.
+invalidPasswordRegexPatternMessage=Invalid password: fails to match regex pattern(s).
+invalidPasswordHistoryMessage=Invalid password: must not be equal to any of last {0} passwords.
 
 locale_de=German
 locale_en=English
diff --git a/forms/common-themes/src/main/resources/theme/base/account/messages/messages_pt_BR.properties b/forms/common-themes/src/main/resources/theme/base/account/messages/messages_pt_BR.properties
index bf3144e..52e0f4d 100644
--- a/forms/common-themes/src/main/resources/theme/base/account/messages/messages_pt_BR.properties
+++ b/forms/common-themes/src/main/resources/theme/base/account/messages/messages_pt_BR.properties
@@ -1,3 +1,18 @@
+doSave=Salvar
+doCancel=Cancelar
+doLogOutAllSessions=Sair de todas as sess\u00F5es
+doRemove=Remover
+doAdd=Adicionar
+doSignOut=Sair
+
+editAccountHtmlTtile=Editar Conta
+federatedIdentitiesHtmlTitle=Identidades Federadas
+accountLogHtmlTitle=Log da conta
+changePasswordHtmlTitle=Alterar senha
+sessionsHtmlTitle=Sess\u00F5es
+accountManagementTitle=Gerenciamento de Contas Keycloak
+authenticatorTitle=Autenticator
+
 authenticatorCode=C\u00F3digo autenticador
 email=Email
 firstName=Primeiro nome
@@ -12,6 +27,36 @@ region=Estado
 postal_code=CEP
 country=Pa\u00EDs
 
+requiredFields=Campos obrigat\u00F3rios
+allFieldsRequired=Todos os campos s\u00E3o obrigat\u00F3rios
+
+backToApplication=&laquo; Voltar para aplica\u00E7\u00E3o
+backTo=Voltar para {0}
+
+date=Data
+event=Evento
+ip=IP
+client=Cliente
+clients=Clientes
+details=Detalhes
+started=Iniciado
+lastAccess=\u00DAltimo acesso
+expires=Expira
+applications=Aplica\u00E7\u00F5es
+
+account=Account
+federatedIdentity=Federated Identity
+authenticator=Authenticator
+sessions=Sessions
+log=Log
+
+configureAuthenticators=Autenticadores Configurados
+mobile=Mobile
+totpStep1=Instalar <a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> ou <a href="http://code.google.com/p/google-authenticator/" target="_blank">Google Authenticator</a> em seu dispositivo mobile.
+totpStep2=Abra o aplicativo e escaneie o c\u00F3digo de barras ou digite o c\u00F3digo.
+totpStep3=Digite o c\u00F3digo fornecido pelo aplicativo e clique em Salvar para concluir a configura\u00E7\u00E3o.
+
+
 missingFirstNameMessage=Por favor, informe o primeiro nome.
 invalidEmailMessage=E-mail inv\u00E1lido.
 missingLastNameMessage=Por favor, informe o sobrenome.
@@ -41,18 +86,18 @@ federatedIdentityRemovingLastProviderMessage=Voc\u00EA n\u00E3o pode remover a \
 identityProviderRedirectErrorMessage=Falha ao redirecionar para o provedor de identidade
 identityProviderRemovedMessage=Provedor de identidade removido com sucesso
 
-accountDisabledMessage=Conta desativada, contate administrador
-
-doLogOutAllSessions=Sair de todas sess\u00F5es
+accountDisabledMessage=Conta desativada, contate o administrador
 
 accountTemporarilyDisabledMessage=A conta est\u00E1 temporariamente indispon\u00EDvel, contate administrador ou tente novamente mais tarde
-invalidPasswordMinLengthMessage=Senha inv\u00E1lida: comprimento m\u00EDnimo {0}
-invalidPasswordMinLowerCaseCharsMessage=Senha inv\u00E1lida: deve conter pelo menos {0} caracteres min\u00FAsculos
-invalidPasswordMinDigitsMessage=Senha inv\u00E1lida: deve conter pelo menos {0} d\u00EDgitos num\u00E9ricos
-invalidPasswordMinUpperCaseCharsMessage=Senha inv\u00E1lida: deve conter pelo menos {0} caracteres mai\u00FAsculos
-invalidPasswordMinSpecialCharsMessage=Senha inv\u00E1lida: deve conter pelo menos {0} caracteres especiais
+invalidPasswordMinLengthMessage=Senha inv\u00E1lida\: comprimento m\u00EDnimo {0}
+invalidPasswordMinLowerCaseCharsMessage=Senha inv\u00E1lida\: deve conter pelo menos {0} caracteres min\u00FAsculos
+invalidPasswordMinDigitsMessage=Senha inv\u00E1lida\: deve conter pelo menos {0} d\u00EDgitos num\u00E9ricos
+invalidPasswordMinUpperCaseCharsMessage=Senha inv\u00E1lida\: deve conter pelo menos {0} caracteres mai\u00FAsculos
+invalidPasswordMinSpecialCharsMessage=Senha inv\u00E1lida\: deve conter pelo menos {0} caracteres especiais
 invalidPasswordNotUsernameMessage=Senha inv\u00E1lida\: n\u00E3o deve ser igual ao nome de usu\u00E1rio
+invalidPasswordRegexPatternMessage=Senha inv\u00E1lida\: n\u00E3o correspondem ao padr\u00E3o regex(s).
+invalidPasswordHistoryMessage=Senha inv\u00E1lida\: n\u00E3o deve ser igual a qualquer uma {0} \u00FAltima hist\u00F3ria senha.
 
 locale_de=Deutsch
 locale_en=English
-locale_pt-BR=Portugu\u00EAs (BR)
+locale_pt-BR=Portugu\u00EAs (BR)
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
index da0b2eb..df05a13 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
@@ -900,14 +900,15 @@ module.factory('PasswordPolicy', function() {
     var p = {};
 
     p.policyMessages = {
-        hashIterations: "Number of hashing iterations.  Default is 1.  Recommended is 50000.",
-        length:         "Minimal password length (integer type). Default value is 8.",
-        digits:         "Minimal number (integer type) of digits in password. Default value is 1.",
-        lowerCase:      "Minimal number (integer type) of lowercase characters in password. Default value is 1.",
-        upperCase:      "Minimal number (integer type) of uppercase characters in password. Default value is 1.",
-        specialChars:   "Minimal number (integer type) of special characters in password. Default value is 1.",
-        notUsername:    "Block passwords that are equal to the username",
-        regexPatterns:  "Block passwords that do not match all of the regex patterns (string type)."
+        hashIterations: 	"Number of hashing iterations.  Default is 1.  Recommended is 50000.",
+        length:         	"Minimal password length (integer type). Default value is 8.",
+        digits:         	"Minimal number (integer type) of digits in password. Default value is 1.",
+        lowerCase:      	"Minimal number (integer type) of lowercase characters in password. Default value is 1.",
+        upperCase:      	"Minimal number (integer type) of uppercase characters in password. Default value is 1.",
+        specialChars:   	"Minimal number (integer type) of special characters in password. Default value is 1.",
+        notUsername:    	"Block passwords that are equal to the username",
+        regexPatterns:  	"Block passwords that do not match all of the regex patterns (string type).",
+        passwordHistory:  	"Block passwords that are equal to previous passwords. Default value is 3."
     }
 
     p.allPolicies = [
@@ -918,7 +919,8 @@ module.factory('PasswordPolicy', function() {
         { name: 'upperCase', value: 1 },
         { name: 'specialChars', value: 1 },
         { name: 'notUsername', value: 1 },
-        { name: 'regexPatterns', value: ''}
+        { name: 'regexPatterns', value: ''},
+        { name: 'passwordHistory', value: 3 }
     ];
 
     p.parse = function(policyString) {
diff --git a/forms/common-themes/src/main/resources/theme/base/login/messages/messages_de.properties b/forms/common-themes/src/main/resources/theme/base/login/messages/messages_de.properties
index 75ffd3e..9de1c21 100644
--- a/forms/common-themes/src/main/resources/theme/base/login/messages/messages_de.properties
+++ b/forms/common-themes/src/main/resources/theme/base/login/messages/messages_de.properties
@@ -125,12 +125,14 @@ accountPasswordUpdatedMessage=Ihr Passwort wurde aktualisiert.
 
 noAccessMessage=Kein Zugriff
 
-invalidPasswordMinLengthMessage=Ung\u00FCltiges Passwort: minimum l\u00E4nge {0}.
-invalidPasswordMinDigitsMessage=Ung\u00FCltiges Passwort: muss mindestens {0} Zahl(en) beinhalten.
-invalidPasswordMinLowerCaseCharsMessage=Ung\u00FCltiges Passwort: muss mindestens {0} Kleinbuchstaben beinhalten.
-invalidPasswordMinUpperCaseCharsMessage=Ung\u00FCltiges Passwort: muss mindestens {0} Grossbuchstaben beinhalten.
-invalidPasswordMinSpecialCharsMessage=Ung\u00FCltiges Passwort: muss mindestens {0} Spezialzeichen beinhalten.
+invalidPasswordMinLengthMessage=Ung\u00FCltiges Passwort\: minimum l\u00E4nge {0}.
+invalidPasswordMinDigitsMessage=Ung\u00FCltiges Passwort\: muss mindestens {0} Zahl(en) beinhalten.
+invalidPasswordMinLowerCaseCharsMessage=Ung\u00FCltiges Passwort\: muss mindestens {0} Kleinbuchstaben beinhalten.
+invalidPasswordMinUpperCaseCharsMessage=Ung\u00FCltiges Passwort\: muss mindestens {0} Grossbuchstaben beinhalten.
+invalidPasswordMinSpecialCharsMessage=Ung\u00FCltiges Passwort\: muss mindestens {0} Spezialzeichen beinhalten.
 invalidPasswordNotUsernameMessage=Ung\u00FCltiges Passwort\: darf nicht gleich sein wie Benutzername.
+invalidPasswordRegexPatternMessage=Ung\u00FCltiges Passwort\: nicht Regex-Muster (n) entsprechen.
+invalidPasswordHistoryMessage=Ung\u00FCltiges Passwort\: darf nicht gleich einem der letzten {0} Passwortgeschichte.
 
 failedToProcessResponseMessage=Konnte Response nicht verarbeiten.
 httpsRequiredMessage=HTTPS erforderlich.
diff --git a/forms/common-themes/src/main/resources/theme/base/login/messages/messages_en.properties b/forms/common-themes/src/main/resources/theme/base/login/messages/messages_en.properties
index 7c1347f..6d34f4e 100755
--- a/forms/common-themes/src/main/resources/theme/base/login/messages/messages_en.properties
+++ b/forms/common-themes/src/main/resources/theme/base/login/messages/messages_en.properties
@@ -69,7 +69,7 @@ personalInfo=Personal Info:
 
 role_admin=Admin
 role_realm-admin=Realm Admin
-role_create-realm=Crate realm
+role_create-realm=Create realm
 role_view-realm=View realm
 role_view-users=View users
 role_view-applications=View applications
@@ -127,7 +127,9 @@ invalidPasswordMinDigitsMessage=Invalid password: must contain at least {0} nume
 invalidPasswordMinLowerCaseCharsMessage=Invalid password: must contain at least {0} lower case characters.
 invalidPasswordMinUpperCaseCharsMessage=Invalid password: must contain at least {0} upper case characters.
 invalidPasswordMinSpecialCharsMessage=Invalid password: must contain at least {0} special characters.
-invalidPasswordNotUsernameMessage=Invalid password\: must not be equal to the username.
+invalidPasswordNotUsernameMessage=Invalid password: must not be equal to the username.
+invalidPasswordRegexPatternMessage=Invalid password: fails to match regex pattern(s).
+invalidPasswordHistoryMessage=Invalid password: must not be equal to any of last {0} passwords.
 
 failedToProcessResponseMessage=Failed to process response
 httpsRequiredMessage=HTTPS required
diff --git a/forms/common-themes/src/main/resources/theme/base/login/messages/messages_pt_BR.properties b/forms/common-themes/src/main/resources/theme/base/login/messages/messages_pt_BR.properties
index 58dba84..e33f5aa 100644
--- a/forms/common-themes/src/main/resources/theme/base/login/messages/messages_pt_BR.properties
+++ b/forms/common-themes/src/main/resources/theme/base/login/messages/messages_pt_BR.properties
@@ -11,8 +11,6 @@ registerWithTitle=Registre-se com {0}
 registerWithTitleHtml=Registre-se com <strong>{0}</strong>
 loginTitle=Entrar em {0}
 loginTitleHtml=Entrar em <strong>{0}</strong>
-loginOauthTitle=Acesso tempor\u00E1rio para {0}
-loginOauthTitleHtml=Acesso tempor\u00E1rio para <strong>{0}<strong> solicitado por <strong>{1}</strong>.
 loginTotpTitle=Configura\u00E7\u00E3o do autenticador mobile
 loginProfileTitle=Atualiza\u00E7\u00E3o de Informa\u00E7\u00F5es da Conta
 oauthGrantTitle=Concess\u00E3o OAuth
@@ -22,12 +20,14 @@ errorTitleHtml=N\u00F3s <strong>lamentamos</strong> ...
 emailVerifyTitle=Verifica\u00E7\u00E3o de e-mail
 emailForgotTitle=Esqueceu sua senha?
 updatePasswordTitle=Atualiza\u00E7\u00E3o de senha
+codeSuccessTitle=C\u00F3digo de sucesso
+codeErrorTitle=C\u00F3digo de erro\: {0}
 
 noAccount=Novo usu\u00E1rio?
 username=Nome de usu\u00E1rio
-givenName=Primeiro nome
 usernameOrEmail=Nome de usu\u00E1rio ou email
 firstName=Primeiro nome
+givenName=Primeiro nome
 fullName=Nome completo
 lastName=Sobrenome
 familyName=Sobrenome
@@ -36,7 +36,7 @@ password=Senha
 passwordConfirm=Confirme a senha
 passwordNew=Nova senha
 passwordNewConfirm=Confirma\u00E7\u00E3o da nova senha
-rememberMe=Relembre-me
+rememberMe=Mantenha-me conectado
 authenticatorCode=C\u00F3digo autenticador
 address=Endere\u00E7o
 street=Logradouro
@@ -48,11 +48,12 @@ emailVerified=Email verificado
 gssDelegationCredential=gss delega\u00E7\u00E3o credencial
 
 loginTotpStep1=Instale <a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> ou <a href="http://code.google.com/p/google-authenticator/" target="_blank">Google Authenticator</a> em seu celular
-loginTotpStep2=Abra o aplicativo e escanei o c\u00F3digo de barras ou digite o c\u00F3digo
-loginTotpStep3=Digite o c\u00F3digo autenticador fornecido pelo aplicativo e clique em Enviar para concluir a configura\u00E7\u00E3o
+loginTotpStep2=Abra o aplicativo e escaneie o c\u00F3digo de barras ou digite o c\u00F3digo
+loginTotpStep3=Digite o c\u00F3digo fornecido pelo aplicativo e clique em Enviar para concluir a configura\u00E7\u00E3o
 loginTotpOneTime=C\u00F3digo autenticador
 
 oauthGrantRequest=Voc\u00EA concede esses privil\u00E9gios de acesso?
+inResource=em <strong>{0}</strong>
 
 emailVerifyInstruction1=Um e-mail com instru\u00E7\u00F5es para verificar o seu endere\u00E7o de e-mail foi enviado para voc\u00EA.
 emailVerifyInstruction2=Voc\u00EA n\u00E3o recebeu um c\u00F3digo de verifica\u00E7\u00E3o em seu e-mail?
@@ -62,6 +63,8 @@ backToLogin=&laquo; Voltar
 
 emailInstruction=Digite seu nome de usu\u00E1rio ou endere\u00E7o de email e n\u00F3s lhe enviaremos instru\u00E7\u00F5es sobre como criar uma nova senha.
 
+copyCodeInstruction=Por favor, copie o c\u00F3digo e cole-o em sua aplica\u00E7\u00E3o:
+
 personalInfo=Informa\u00E7\u00F5es pessoais:
 
 role_admin=Admin
@@ -119,12 +122,14 @@ accountPasswordUpdatedMessage=Sua senha foi atualizada
 
 noAccessMessage=Sem acesso
 
-invalidPasswordMinLengthMessage=Senha inv\u00E1lida: comprimento m\u00EDnimo {0}
-invalidPasswordMinDigitsMessage=Senha inv\u00E1lida: deve conter pelo menos {0} d\u00EDgitos num\u00E9ricos
-invalidPasswordMinLowerCaseCharsMessage=Senha inv\u00E1lida: deve conter pelo menos {0} caracteres min\u00FAsculos
-invalidPasswordMinUpperCaseCharsMessage=Senha inv\u00E1lida: deve conter pelo menos {0} caracteres mai\u00FAsculos
-invalidPasswordMinSpecialCharsMessage=Senha inv\u00E1lida: deve conter pelo menos {0} caracteres especiais
-invalidPasswordNotUsernameMessage=Senha inv\u00E1lida: n\u00E3o deve ser igual ao nome de usu\u00E1rio
+invalidPasswordMinLengthMessage=Senha inv\u00E1lida\: comprimento m\u00EDnimo {0}
+invalidPasswordMinDigitsMessage=Senha inv\u00E1lida\: deve conter pelo menos {0} d\u00EDgitos num\u00E9ricos
+invalidPasswordMinLowerCaseCharsMessage=Senha inv\u00E1lida\: deve conter pelo menos {0} caracteres min\u00FAsculos
+invalidPasswordMinUpperCaseCharsMessage=Senha inv\u00E1lida\: deve conter pelo menos {0} caracteres mai\u00FAsculos
+invalidPasswordMinSpecialCharsMessage=Senha inv\u00E1lida\: deve conter pelo menos {0} caracteres especiais
+invalidPasswordNotUsernameMessage=Senha inv\u00E1lida\: n\u00E3o deve ser igual ao nome de usu\u00E1rio
+invalidPasswordRegexPatternMessage=Senha inv\u00E1lida\: n\u00E3o correspondem ao padr\u00E3o regex(s).
+invalidPasswordHistoryMessage=Senha inv\u00E1lida\: n\u00E3o deve ser igual a qualquer uma {0} \u00FAltima hist\u00F3ria senha.
 
 failedToProcessResponseMessage=Falha ao processar a resposta
 httpsRequiredMessage=HTTPS requerido
diff --git a/forms/login-api/src/main/java/org/keycloak/login/LoginFormsProvider.java b/forms/login-api/src/main/java/org/keycloak/login/LoginFormsProvider.java
index c3a71d9..8e5ef24 100755
--- a/forms/login-api/src/main/java/org/keycloak/login/LoginFormsProvider.java
+++ b/forms/login-api/src/main/java/org/keycloak/login/LoginFormsProvider.java
@@ -21,12 +21,6 @@ import org.keycloak.provider.Provider;
  */
 public interface LoginFormsProvider extends Provider {
 
-    public LoginFormsProvider setRealm(RealmModel realm);
-
-    public LoginFormsProvider setUriInfo(UriInfo uriInfo);
-
-    public LoginFormsProvider setHttpHeaders(HttpHeaders httpHeaders);
-
     public Response createResponse(UserModel.RequiredAction action);
 
     public Response createLogin();
@@ -67,14 +61,8 @@ public interface LoginFormsProvider extends Provider {
 
     public LoginFormsProvider setSuccess(String message, Object ... parameters);
 
-    public LoginFormsProvider setWarning(String message, Object ... parameters);
-
     public LoginFormsProvider setUser(UserModel user);
 
-    public LoginFormsProvider setClient(ClientModel client);
-
-    public LoginFormsProvider setQueryParams(MultivaluedMap<String, String> queryParams);
-
     public LoginFormsProvider setResponseHeader(String headerName, String headerValue);
 
     public LoginFormsProvider setFormData(MultivaluedMap<String, String> formData);
diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
index 228a146..7e5cb78 100755
--- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
@@ -37,9 +37,8 @@ import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.utils.FormMessage;
 import org.keycloak.services.messages.Messages;
-import org.keycloak.services.resources.flows.Urls;
+import org.keycloak.services.Urls;
 
-import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
@@ -79,39 +78,20 @@ import java.util.concurrent.TimeUnit;
 
     private KeycloakSession session;
     private FreeMarkerUtil freeMarker;
-    private RealmModel realm;
 
     private UserModel user;
 
-    private ClientModel client;
     private ClientSessionModel clientSession;
 
-    private UriInfo uriInfo;
-
-    private HttpHeaders httpHeaders;
-
     public FreeMarkerLoginFormsProvider(KeycloakSession session, FreeMarkerUtil freeMarker) {
         this.session = session;
         this.freeMarker = freeMarker;
     }
 
-    public LoginFormsProvider setRealm(RealmModel realm) {
-        this.realm = realm;
-        return this;
-    }
-
-    public LoginFormsProvider setUriInfo(UriInfo uriInfo) {
-        this.uriInfo = uriInfo;
-        return this;
-    }
-
-    @Override
-    public LoginFormsProvider setHttpHeaders(HttpHeaders httpHeaders) {
-        this.httpHeaders = httpHeaders;
-        return this;
-    }
-
     public Response createResponse(UserModel.RequiredAction action) {
+        RealmModel realm = session.getContext().getRealm();
+        UriInfo uriInfo = session.getContext().getUri();
+
         String actionMessage;
         LoginFormsPages page;
 
@@ -150,13 +130,17 @@ import java.util.concurrent.TimeUnit;
         }
 
         if (messages == null) {
-            setWarning(actionMessage);
+            setMessage(MessageType.WARNING, actionMessage);
         }
 
         return createResponse(page);
     }
 
     private Response createResponse(LoginFormsPages page) {
+        RealmModel realm = session.getContext().getRealm();
+        ClientModel client = session.getContext().getClient();
+        UriInfo uriInfo = session.getContext().getUri();
+
         MultivaluedMap<String, String> queryParameterMap = queryParams != null ? queryParams : new MultivaluedMapImpl<String, String>();
 
         String requestURI = uriInfo.getBaseUri().getPath();
@@ -191,7 +175,7 @@ import java.util.concurrent.TimeUnit;
         }
 
         Properties messagesBundle;
-        Locale locale = LocaleHelper.getLocale(realm, user, uriInfo, httpHeaders);
+        Locale locale = LocaleHelper.getLocale(realm, user, uriInfo, session.getContext().getRequestHeaders());
         try {
             messagesBundle = theme.getMessages(locale);
             attributes.put("msg", new MessageFormatterMethod(locale, messagesBundle));
@@ -222,7 +206,7 @@ import java.util.concurrent.TimeUnit;
 
         if (realm != null) {
             attributes.put("realm", new RealmBean(realm));
-            attributes.put("social", new IdentityProviderBean(realm, baseUri, this.uriInfo));
+            attributes.put("social", new IdentityProviderBean(realm, baseUri, uriInfo));
             attributes.put("url", new UrlBean(realm, theme, baseUri, this.actionUri));
 
             if (realm.isInternationalizationEnabled()) {
@@ -365,22 +349,11 @@ import java.util.concurrent.TimeUnit;
         return this;
     }
 
-    @Override
-    public FreeMarkerLoginFormsProvider setWarning(String message, Object ... parameters) {
-        setMessage(MessageType.WARNING, message, parameters);
-        return this;
-    }
-
     public FreeMarkerLoginFormsProvider setUser(UserModel user) {
         this.user = user;
         return this;
     }
 
-    public FreeMarkerLoginFormsProvider setClient(ClientModel client) {
-        this.client = client;
-        return this;
-    }
-
     public FreeMarkerLoginFormsProvider setFormData(MultivaluedMap<String, String> formData) {
         this.formData = formData;
         return this;
@@ -412,12 +385,6 @@ import java.util.concurrent.TimeUnit;
     }
 
     @Override
-    public LoginFormsProvider setQueryParams(MultivaluedMap<String, String> queryParams) {
-        this.queryParams = queryParams;
-        return this;
-    }
-
-    @Override
     public LoginFormsProvider setActionUri(URI actionUri) {
         this.actionUri = actionUri;
         return this;
diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/IdentityProviderBean.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/IdentityProviderBean.java
index 65e0272..23c6e69 100755
--- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/IdentityProviderBean.java
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/IdentityProviderBean.java
@@ -23,7 +23,7 @@ package org.keycloak.login.freemarker.model;
 
 import org.keycloak.models.IdentityProviderModel;
 import org.keycloak.models.RealmModel;
-import org.keycloak.services.resources.flows.Urls;
+import org.keycloak.services.Urls;
 
 import javax.ws.rs.core.UriInfo;
 import java.net.URI;
diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/UrlBean.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/UrlBean.java
index eb9a40b..4c25bb3 100755
--- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/UrlBean.java
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/UrlBean.java
@@ -23,7 +23,7 @@ package org.keycloak.login.freemarker.model;
 
 import org.keycloak.freemarker.Theme;
 import org.keycloak.models.RealmModel;
-import org.keycloak.services.resources.flows.Urls;
+import org.keycloak.services.Urls;
 
 import java.net.URI;
 
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/Keycloak.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/Keycloak.java
index 20914e5..6d6c7be 100755
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/Keycloak.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/Keycloak.java
@@ -26,7 +26,7 @@ public class Keycloak {
 
         target = client.target(config.getServerUrl());
 
-        target.register(new BearerAuthFilter(tokenManager.getAccessTokenString()));
+        target.register(new BearerAuthFilter(tokenManager));
     }
 
     public static Keycloak getInstance(String serverUrl, String realm, String username, String password, String clientId, String clientSecret){
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/BearerAuthFilter.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/BearerAuthFilter.java
index 1442513..a66c14c 100644
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/BearerAuthFilter.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/BearerAuthFilter.java
@@ -1,5 +1,7 @@
 package org.keycloak.admin.client.resource;
 
+import org.keycloak.admin.client.token.TokenManager;
+
 import javax.ws.rs.client.ClientRequestContext;
 import javax.ws.rs.client.ClientRequestFilter;
 import javax.ws.rs.core.HttpHeaders;
@@ -11,14 +13,23 @@ import java.io.IOException;
 public class BearerAuthFilter implements ClientRequestFilter {
 
     private final String tokenString;
+    private final TokenManager tokenManager;
 
     public BearerAuthFilter(String tokenString) {
         this.tokenString = tokenString;
+        this.tokenManager = null;
+    }
+
+    public BearerAuthFilter(TokenManager tokenManager) {
+        this.tokenManager = tokenManager;
+        this.tokenString = null;
     }
 
+
     @Override
     public void filter(ClientRequestContext requestContext) throws IOException {
-        String authHeader = "Bearer " + tokenString;
+        String authHeader = "Bearer " + (tokenManager != null ? tokenManager.getAccessTokenString() : tokenString);
+
         requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, authHeader);
     }
 
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RoleMappingResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RoleMappingResource.java
index 65ffe04..bd8a91a 100755
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RoleMappingResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RoleMappingResource.java
@@ -22,7 +22,7 @@ public interface RoleMappingResource {
     @Path("realm")
     public RoleScopeResource realmLevel();
 
-    @Path("applications/{appName}")
-    public RoleScopeResource applicationLevel(@PathParam("appName") String appName);
+    @Path("clients/{clientId}")
+    public RoleScopeResource clientLevel(@PathParam("clientId") String clientId);
 
 }
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/token/TokenManager.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/token/TokenManager.java
index e38da50..4e694eb 100644
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/token/TokenManager.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/token/TokenManager.java
@@ -66,8 +66,7 @@ public class TokenManager {
 
         Form form = new Form()
                 .param("grant_type", "refresh_token")
-                .param("username", config.getUsername())
-                .param("password", config.getPassword());
+                .param("refresh_token", currentToken.getRefreshToken());
 
         if(config.isPublicClient()){
             form.param("client_id", config.getClientId());
diff --git a/misc/DatabaseTesting.md b/misc/DatabaseTesting.md
index 0ae699d..07feccb 100644
--- a/misc/DatabaseTesting.md
+++ b/misc/DatabaseTesting.md
@@ -1,11 +1,20 @@
 Test with various databases
 ===========================
 
+MongoDB
+-------
+
+The Keycloak testsuite uses an embedded MongoDB when running tests so you don't have to have one running locally.  
+
+Run tests:
+
+    mvn install -Pmongo
+
 
 MySQL
 -----
 
-Use the official [MySQL docker image](https://registry.hub.docker.com/_/mysql/).
+The simplest way to test with MySQL is to use the official [MySQL docker image](https://registry.hub.docker.com/_/mysql/).
 
 Start MySQL:
 
@@ -13,7 +22,7 @@ Start MySQL:
    
 Run tests:
 
-    mvn clean install -Dkeycloak.connectionsJpa.url=jdbc:mysql://`docker inspect --format '{{ .NetworkSettings.IPAddress }}' mysql`/keycloak -Dkeycloak.connectionsJpa.driver=com.mysql.jdbc.Driver -Dkeycloak.connectionsJpa.user=keycloak -Dkeycloak.connectionsJpa.password=keycloak    
+    mvn install -Dkeycloak.connectionsJpa.url=jdbc:mysql://`docker inspect --format '{{ .NetworkSettings.IPAddress }}' mysql`/keycloak -Dkeycloak.connectionsJpa.driver=com.mysql.jdbc.Driver -Dkeycloak.connectionsJpa.user=keycloak -Dkeycloak.connectionsJpa.password=keycloak    
     
 Stop MySQl:
 
@@ -23,7 +32,7 @@ Stop MySQl:
 PostgreSQL
 ----------
 
-Use the official [PostgreSQL docker image](https://registry.hub.docker.com/_/postgres/).
+The simplest way to test with PostgreSQL is to use the official [PostgreSQL docker image](https://registry.hub.docker.com/_/postgres/).
 
 Start PostgreSQL:
 
@@ -31,7 +40,7 @@ Start PostgreSQL:
    
 Run tests:
 
-    mvn clean install -Dkeycloak.connectionsJpa.url=jdbc:postgresql://`docker inspect --format '{{ .NetworkSettings.IPAddress }}' postgres`:5432/keycloak -Dkeycloak.connectionsJpa.driver=org.postgresql.Driver -Dkeycloak.connectionsJpa.user=keycloak -Dkeycloak.connectionsJpa.password=keycloak    
+    mvn install -Dkeycloak.connectionsJpa.url=jdbc:postgresql://`docker inspect --format '{{ .NetworkSettings.IPAddress }}' postgres`:5432/keycloak -Dkeycloak.connectionsJpa.driver=org.postgresql.Driver -Dkeycloak.connectionsJpa.user=keycloak -Dkeycloak.connectionsJpa.password=keycloak    
     
 Stop PostgreSQL:
 
diff --git a/misc/HackingOnKeycloak.md b/misc/HackingOnKeycloak.md
new file mode 100644
index 0000000..d6bd5dc
--- /dev/null
+++ b/misc/HackingOnKeycloak.md
@@ -0,0 +1,70 @@
+Hacking on Keycloak
+===================
+
+GitHub Repository
+-----------------
+
+### Create a GitHub account if you don't already have one
+
+[Join GitHub](https://github.com/join)
+
+### Fork Keycloak repository into your account
+
+[https://github.com/keycloak/keycloak](https://github.com/keycloak/keycloak)
+
+### Clone your newly forked copy onto your local workspace
+
+    git clone https://github.com/<your username>/keycloak.git
+    cd keycloak
+    
+### Add a remote ref to upstream for pulling future updates
+    
+    git remote add upstream https://github.com/keycloak/keycloak.git
+    
+### Pull later updates from upstream
+
+    git fetch upstream
+    git rebase upstream/master
+    
+    
+Discuss changes
+---------------
+
+Before starting work on a new feature or anything besides a minor bug fix join the [Keycloak Dev mailing list](https://lists.jboss.org/mailman/listinfo/keycloak-dev) 
+and send a mail about your proposed changes. This is vital as otherwise you may waste days implementing a feature that is later rejected.
+
+Once you have received feedback from the mailing list if there's not one already create a (JIRA issue)[https://issues.jboss.org/browse/KEYCLOAK].
+ 
+
+Implement changes
+-----------------
+
+We don't currently enforce a code style in Keycloak, but a good reference is the code style used by WildFly. This can be
+retrieved from (https://github.com/wildfly/wildfly-core/tree/master/ide-configs)[https://github.com/wildfly/wildfly-core/tree/master/ide-configs].
+
+If your changes requires updates to the database read [Updating Database Schema](UpdatingDatabaseSchema.md).
+
+To try your changes out manually you can quickly start Keycloak from within your IDEA or Maven, to find out how to do this
+read [Testsuite](Testsuite.md). It's also important that you add tests to the testsuite for your changes.  
+ 
+
+Get your changes merged into upstream
+-------------------------------------
+
+Here's a quick check list for a good pull request (PR):
+
+* Discussed and agreed on Keycloak Dev mailing list
+* One commit per PR
+* One feature/change per PR
+* No changes to code not directly related to your change (e.g. no formatting changes or refactoring to existing code, if you want to refactor/improve existing code that's a separate discussion to mailing list and JIRA issue)
+* A JIRA associated with your PR (include the JIRA issue number in commit comment)
+* All tests in testsuite pass
+* Do a rebase on upstream master
+
+Once you're happy with your changes go to GitHub and create a PR.
+
+
+Release Keycloak
+----------------
+
+* [Release Process](ReleaseProcess.md)
\ No newline at end of file
diff --git a/misc/ReleaseProcess.md b/misc/ReleaseProcess.md
new file mode 100644
index 0000000..08fd1b6
--- /dev/null
+++ b/misc/ReleaseProcess.md
@@ -0,0 +1,80 @@
+## Test
+
+* Make sure tests pass on Travis
+* Make sure tests pass on Jenkins
+* Go through the (manual testing)[https://docs.google.com/spreadsheets/d/17C_WEHNE03r5DxN71OXGJaytjA6_WjZKCXRcsnmNQD4]
+
+## Create release
+
+* Get from github
+```
+$ git@github.com:keycloak/keycloak.git
+```
+
+* Build everything to make sure its kosher.
+```
+$ cd keycloak
+$ mvn install
+```
+
+* Build javadoc and jaxrs-doc
+```
+$ mvn javadoc:javadoc
+# This is for jaxrs-docs
+$ cd services
+$ mvn package
+# back to root keycloak dir
+$ cd ..
+```
+
+* Upload to Nexus (from project root)
+```
+$ mvn -Pdistribution deploy
+```
+
+* Login to Nexus and release the maven repository uploads in the staging area.
+
+* Upload src and distro zips to sf.net/projects/keycloak.  This includes appliance, war-dist, each adapter, and proxy distros.  You need to create an adapters folder on sf.net and each uploaded adapter there.
+
+* Upload documentation to docs.jboss.org
+```
+$ sftp keycloak@filemgmt.jboss.org
+> cd docs_htdocs/keycloak/docs
+> mkdir 1.0.0.Final (or whatever version)
+> quit
+
+$ unzip distribution/examples-docs-zip/target/keycloak-examples-docs-dist.zip
+$ cd docs
+$ rsync -rv --protocol=28 * keycloak@filemgmt.jboss.org:/docs_htdocs/keycloak/docs/1.0.0.Final
+```
+
+* tag release
+```
+$ git tag -a -m "1.0.0.Final" 1.0.0.Final
+$ git push --tags
+```
+
+## Update Bower
+```
+$ git clone https://github.com/keycloak/keycloak-js-bower
+$ cp <keycloak.js from dist> dist/keycloak-js-bower
+$ cp <keycloak.min.js from dist> dist/keycloak-js-bower
+```
+Edit bower.json and set version (include -beta -rc, but not -final). Create tag.
+
+## Update OpenShift Cartridge
+
+See https://github.com/keycloak/openshift-keycloak-cartridge for details
+
+## Update Docker image
+
+Instructions TBD
+
+## Maven central
+
+Releases are automatically synced to Maven central, but this can take up to one day
+
+## Announce
+
+* Update Magnolia site to link keycloak docs and announcements.
+* Write a blog and email about release including links to download, migration guide, docs, and blurb about what's new
\ No newline at end of file
diff --git a/misc/UpdatingDatabaseSchema.md b/misc/UpdatingDatabaseSchema.md
index 1dcee57..274669c 100644
--- a/misc/UpdatingDatabaseSchema.md
+++ b/misc/UpdatingDatabaseSchema.md
@@ -76,4 +76,4 @@ It should be added last to the `DefaultMongoUpdaterProvider#updates` array.
 Testing database migration
 --------------------------
 
-Get the database from an old version of Keycloak that includes the demo applications. Start the server with this and test it. 
\ No newline at end of file
+Get the database from an old version of Keycloak that includes the demo applications. Start the server with this and test it.
\ No newline at end of file
diff --git a/model/api/pom.xml b/model/api/pom.xml
index 9653491..1b97730 100755
--- a/model/api/pom.xml
+++ b/model/api/pom.xml
@@ -15,6 +15,11 @@
 
     <dependencies>
         <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-jaxrs</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <groupId>net.iharder</groupId>
             <artifactId>base64</artifactId>
             <scope>provided</scope>
diff --git a/model/api/src/main/java/org/keycloak/models/entities/CredentialEntity.java b/model/api/src/main/java/org/keycloak/models/entities/CredentialEntity.java
index 6255836..5493fe2 100755
--- a/model/api/src/main/java/org/keycloak/models/entities/CredentialEntity.java
+++ b/model/api/src/main/java/org/keycloak/models/entities/CredentialEntity.java
@@ -5,11 +5,23 @@ package org.keycloak.models.entities;
  */
 public class CredentialEntity {
 
+    private String id;
     private String type;
     private String value;
     private String device;
     private byte[] salt;
     private int hashIterations;
+    private long createdDate;
+    private UserEntity user;
+    
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
 
     public String getType() {
         return type;
@@ -50,4 +62,21 @@ public class CredentialEntity {
     public void setHashIterations(int hashIterations) {
         this.hashIterations = hashIterations;
     }
+
+    public long getCreatedDate() {
+        return createdDate;
+    }
+
+    public void setCreatedDate(long createdDate) {
+        this.createdDate = createdDate;
+    }
+    
+    public UserEntity getUser() {
+        return user;
+    }
+
+    public void setUser(UserEntity user) {
+        this.user = user;
+    }
+    
 }
diff --git a/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java b/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java
index bba27c8..3c978f9 100755
--- a/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java
+++ b/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java
@@ -64,7 +64,7 @@ public class RealmEntity extends AbstractIdentifiableEntity {
     private List<String> eventsListeners = new ArrayList<String>();
     private List<String> enabledEventTypes = new ArrayList<String>();
 
-    private String adminAppId;
+    private String masterAdminClient;
 
     private boolean internationalizationEnabled;
     private List<String> supportedLocales = new ArrayList<String>();
@@ -391,12 +391,12 @@ public class RealmEntity extends AbstractIdentifiableEntity {
         this.enabledEventTypes = enabledEventTypes;
     }
     
-    public String getAdminAppId() {
-        return adminAppId;
+    public String getMasterAdminClient() {
+        return masterAdminClient;
     }
 
-    public void setAdminAppId(String adminAppId) {
-        this.adminAppId = adminAppId;
+    public void setMasterAdminClient(String masterAdminClient) {
+        this.masterAdminClient = masterAdminClient;
     }
 
     public List<UserFederationProviderEntity> getUserFederationProviders() {
diff --git a/model/api/src/main/java/org/keycloak/models/KeycloakContext.java b/model/api/src/main/java/org/keycloak/models/KeycloakContext.java
new file mode 100644
index 0000000..ac13d3f
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/KeycloakContext.java
@@ -0,0 +1,23 @@
+package org.keycloak.models;
+
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.UriInfo;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public interface KeycloakContext {
+
+    UriInfo getUri();
+
+    HttpHeaders getRequestHeaders();
+
+    RealmModel getRealm();
+
+    void setRealm(RealmModel realm);
+
+    ClientModel getClient();
+
+    void setClient(ClientModel client);
+
+}
diff --git a/model/api/src/main/java/org/keycloak/models/KeycloakSession.java b/model/api/src/main/java/org/keycloak/models/KeycloakSession.java
index 3dc38e5..b6f35e6 100755
--- a/model/api/src/main/java/org/keycloak/models/KeycloakSession.java
+++ b/model/api/src/main/java/org/keycloak/models/KeycloakSession.java
@@ -10,6 +10,8 @@ import java.util.Set;
  */
 public interface KeycloakSession {
 
+    KeycloakContext getContext();
+
     KeycloakTransactionManager getTransaction();
 
     <T extends Provider> T getProvider(Class<T> clazz);
diff --git a/model/api/src/main/java/org/keycloak/models/PasswordPolicy.java b/model/api/src/main/java/org/keycloak/models/PasswordPolicy.java
index 703e9c6..59d2877 100755
--- a/model/api/src/main/java/org/keycloak/models/PasswordPolicy.java
+++ b/model/api/src/main/java/org/keycloak/models/PasswordPolicy.java
@@ -1,11 +1,15 @@
 package org.keycloak.models;
 
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
+
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
@@ -18,6 +22,7 @@ public class PasswordPolicy {
     public static final String INVALID_PASSWORD_MIN_SPECIAL_CHARS_MESSAGE = "invalidPasswordMinSpecialCharsMessage";
     public static final String INVALID_PASSWORD_NOT_USERNAME = "invalidPasswordNotUsernameMessage";
     public static final String INVALID_PASSWORD_REGEX_PATTERN = "invalidPasswordRegexPatternMessage";
+    public static final String INVALID_PASSWORD_HISTORY = "invalidPasswordHistoryMessage";
 
     private List<Policy> policies;
     private String policyString;
@@ -67,10 +72,12 @@ public class PasswordPolicy {
             } else if (name.equals(HashIterations.NAME)) {
                 list.add(new HashIterations(args));
             } else if (name.equals(RegexPatterns.NAME)) {
-                for(String regexPattern : args) {
+                for (String regexPattern : args) {
                     Pattern.compile(regexPattern);
                 }
                 list.add(new RegexPatterns(args));
+            } else if (name.equals(PasswordHistory.NAME)) {
+                list.add(new PasswordHistory(args));
             }
         }
         return list;
@@ -92,9 +99,35 @@ public class PasswordPolicy {
         return -1;
     }
 
-    public Error validate(String username, String password) {
+    /**
+     *
+     * @return -1 if no expired passwords setting
+     */
+    public int getExpiredPasswords() {
+        if (policies == null)
+            return -1;
+        for (Policy p : policies) {
+            if (p instanceof PasswordHistory) {
+                return ((PasswordHistory) p).passwordHistoryPolicyValue;
+            }
+
+        }
+        return -1;
+    }
+
+    public Error validate(UserModel user, String password) {
         for (Policy p : policies) {
-            Error error = p.validate(username, password);
+            Error error = p.validate(user, password);
+            if (error != null) {
+                return error;
+            }
+        }
+        return null;
+    }
+    
+    public Error validate(String user, String password) {
+        for (Policy p : policies) {
+            Error error = p.validate(user, password);
             if (error != null) {
                 return error;
             }
@@ -103,7 +136,8 @@ public class PasswordPolicy {
     }
 
     private static interface Policy {
-        public Error validate(String username, String password);
+        public Error validate(UserModel user, String password);
+        public Error validate(String user, String password);
     }
 
     public static class Error {
@@ -131,9 +165,15 @@ public class PasswordPolicy {
         public HashIterations(String[] args) {
             iterations = intArg(NAME, 1, args);
         }
+        
 
         @Override
-        public Error validate(String username, String password) {
+        public Error validate(String user, String password) {
+            return null;
+        }
+        
+        @Override
+        public Error validate(UserModel user, String password) {
             return null;
         }
     }
@@ -148,6 +188,11 @@ public class PasswordPolicy {
         public Error validate(String username, String password) {
             return username.equals(password) ? new Error(INVALID_PASSWORD_NOT_USERNAME) : null;
         }
+        
+        @Override
+        public Error validate(UserModel user, String password) {
+            return validate(user.getUsername(), password);
+        }
     }
 
     private static class Length implements Policy {
@@ -157,11 +202,17 @@ public class PasswordPolicy {
         public Length(String[] args) {
             min = intArg(NAME, 8, args);
         }
+        
 
         @Override
         public Error validate(String username, String password) {
             return password.length() < min ? new Error(INVALID_PASSWORD_MIN_LENGTH_MESSAGE, min) : null;
         }
+        
+        @Override
+        public Error validate(UserModel user, String password) {
+            return validate(user.getUsername(), password);
+        }
     }
 
     private static class Digits implements Policy {
@@ -171,6 +222,7 @@ public class PasswordPolicy {
         public Digits(String[] args) {
             min = intArg(NAME, 1, args);
         }
+        
 
         @Override
         public Error validate(String username, String password) {
@@ -182,6 +234,11 @@ public class PasswordPolicy {
             }
             return count < min ? new Error(INVALID_PASSWORD_MIN_DIGITS_MESSAGE, min) : null;
         }
+        
+        @Override
+        public Error validate(UserModel user, String password) {
+            return validate(user.getUsername(), password);
+        }
     }
 
     private static class LowerCase implements Policy {
@@ -191,7 +248,7 @@ public class PasswordPolicy {
         public LowerCase(String[] args) {
             min = intArg(NAME, 1, args);
         }
-
+        
         @Override
         public Error validate(String username, String password) {
             int count = 0;
@@ -202,6 +259,11 @@ public class PasswordPolicy {
             }
             return count < min ? new Error(INVALID_PASSWORD_MIN_LOWER_CASE_CHARS_MESSAGE, min) : null;
         }
+        
+        @Override
+        public Error validate(UserModel user, String password) {
+            return validate(user.getUsername(), password);
+        }
     }
 
     private static class UpperCase implements Policy {
@@ -222,6 +284,11 @@ public class PasswordPolicy {
             }
             return count < min ? new Error(INVALID_PASSWORD_MIN_UPPER_CASE_CHARS_MESSAGE, min) : null;
         }
+        
+        @Override
+        public Error validate(UserModel user, String password) {
+            return validate(user.getUsername(), password);
+        }
     }
 
     private static class SpecialChars implements Policy {
@@ -231,7 +298,7 @@ public class PasswordPolicy {
         public SpecialChars(String[] args) {
             min = intArg(NAME, 1, args);
         }
-
+        
         @Override
         public Error validate(String username, String password) {
             int count = 0;
@@ -242,6 +309,11 @@ public class PasswordPolicy {
             }
             return count < min ? new Error(INVALID_PASSWORD_MIN_SPECIAL_CHARS_MESSAGE, min) : null;
         }
+        
+        @Override
+        public Error validate(UserModel user, String password) {
+            return validate(user.getUsername(), password);
+        }
     }
 
     private static class RegexPatterns implements Policy {
@@ -256,17 +328,96 @@ public class PasswordPolicy {
         public Error validate(String username, String password) {
             Pattern pattern = null;
             Matcher matcher = null;
-            for(String regexPattern : regexPatterns) {
+            for (String regexPattern : regexPatterns) {
                 pattern = Pattern.compile(regexPattern);
                 matcher = pattern.matcher(password);
                 if (!matcher.matches()) {
-                    return new Error(INVALID_PASSWORD_REGEX_PATTERN, (Object)regexPatterns);
+                    return new Error(INVALID_PASSWORD_REGEX_PATTERN, (Object) regexPatterns);
                 }
             }
             return null;
         }
+
+        @Override
+        public Error validate(UserModel user, String password) {
+            return validate(user.getUsername(), password);
+        }
     }
 
+    private static class PasswordHistory implements Policy {
+        private static final String NAME = "passwordHistory";
+        private int passwordHistoryPolicyValue;
+
+        public PasswordHistory(String[] args) {
+            passwordHistoryPolicyValue = intArg(NAME, 3, args);
+        }
+        
+        @Override
+        public Error validate(String user, String password) {
+            return null;
+        }
+
+        @Override
+        public Error validate(UserModel user, String password) {
+            
+            if (passwordHistoryPolicyValue != -1) {
+            
+                UserCredentialValueModel cred = getCredentialValueModel(user, UserCredentialModel.PASSWORD);
+                if (cred != null) {
+                    if(new Pbkdf2PasswordEncoder(cred.getSalt()).verify(password, cred.getValue(), cred.getHashIterations())) {
+                        return new Error(INVALID_PASSWORD_HISTORY, passwordHistoryPolicyValue);
+                    }
+                }
+
+                List<UserCredentialValueModel> passwordExpiredCredentials = getCredentialValueModels(user, passwordHistoryPolicyValue - 1,
+                        UserCredentialModel.PASSWORD_HISTORY);
+                for (UserCredentialValueModel credential : passwordExpiredCredentials) {
+                    if (new Pbkdf2PasswordEncoder(credential.getSalt()).verify(password, credential.getValue(), credential.getHashIterations())) {
+                        return new Error(INVALID_PASSWORD_HISTORY, passwordHistoryPolicyValue);
+                    }
+                }
+            }
+            return null;
+        }
+
+        private UserCredentialValueModel getCredentialValueModel(UserModel user, String credType) {
+            for (UserCredentialValueModel model : user.getCredentialsDirectly()) {
+                if (model.getType().equals(credType)) {
+                    return model;
+                }
+            }
+
+            return null;
+        }
+
+        private List<UserCredentialValueModel> getCredentialValueModels(UserModel user, int expiredPasswordsPolicyValue,
+                String credType) {
+            List<UserCredentialValueModel> credentialModels = new ArrayList<UserCredentialValueModel>();
+            for (UserCredentialValueModel model : user.getCredentialsDirectly()) {
+                if (model.getType().equals(credType)) {
+                    credentialModels.add(model);
+                }
+            }
+
+            Collections.sort(credentialModels, new Comparator<UserCredentialValueModel>() {
+                public int compare(UserCredentialValueModel credFirst, UserCredentialValueModel credSecond) {
+                    if (credFirst.getCreatedDate() > credSecond.getCreatedDate()) {
+                        return -1;
+                    } else if (credFirst.getCreatedDate() < credSecond.getCreatedDate()) {
+                        return 1;
+                    } else {
+                        return 0;
+                    }
+                }
+            });
+
+            if (credentialModels.size() > expiredPasswordsPolicyValue) {
+                return credentialModels.subList(0, expiredPasswordsPolicyValue);
+            }
+            return credentialModels;
+        }
+    }
+    
     private static int intArg(String policy, int defaultValue, String... args) {
         if (args == null || args.length == 0) {
             return defaultValue;
diff --git a/model/api/src/main/java/org/keycloak/models/UserCredentialModel.java b/model/api/src/main/java/org/keycloak/models/UserCredentialModel.java
index 5fb6005..083eea8 100755
--- a/model/api/src/main/java/org/keycloak/models/UserCredentialModel.java
+++ b/model/api/src/main/java/org/keycloak/models/UserCredentialModel.java
@@ -8,6 +8,7 @@ import java.util.UUID;
  */
 public class UserCredentialModel {
     public static final String PASSWORD = "password";
+    public static final String PASSWORD_HISTORY = "password-history";
     public static final String PASSWORD_TOKEN = "password-token";
 
     // Secret is same as password but it is not hashed
diff --git a/model/api/src/main/java/org/keycloak/models/UserCredentialValueModel.java b/model/api/src/main/java/org/keycloak/models/UserCredentialValueModel.java
index bd1213f..663eaec 100755
--- a/model/api/src/main/java/org/keycloak/models/UserCredentialValueModel.java
+++ b/model/api/src/main/java/org/keycloak/models/UserCredentialValueModel.java
@@ -12,6 +12,7 @@ public class UserCredentialValueModel {
     private String device;
     private byte[] salt;
     private int hashIterations;
+    private long createdDate;
 
     public String getType() {
         return type;
@@ -52,4 +53,13 @@ public class UserCredentialValueModel {
     public void setHashIterations(int iterations) {
         this.hashIterations = iterations;
     }
+
+    public long getCreatedDate() {
+        return createdDate;
+    }
+
+    public void setCreatedDate(long createdDate) {
+        this.createdDate = createdDate;
+    }
+    
 }
diff --git a/model/api/src/main/java/org/keycloak/models/UserFederationManager.java b/model/api/src/main/java/org/keycloak/models/UserFederationManager.java
index 1016341..94d0226 100755
--- a/model/api/src/main/java/org/keycloak/models/UserFederationManager.java
+++ b/model/api/src/main/java/org/keycloak/models/UserFederationManager.java
@@ -323,7 +323,7 @@ public class UserFederationManager implements UserProvider {
     public void updateCredential(RealmModel realm, UserModel user, UserCredentialModel credential) {
         if (credential.getType().equals(UserCredentialModel.PASSWORD)) {
             if (realm.getPasswordPolicy() != null) {
-                PasswordPolicy.Error error = realm.getPasswordPolicy().validate(user.getUsername(), credential.getValue());
+                PasswordPolicy.Error error = realm.getPasswordPolicy().validate(user, credential.getValue());
                 if (error != null) throw new ModelException(error.getMessage(), error.getParameters());
             }
         }
diff --git a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index 6987151..00e86f0 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -66,7 +66,7 @@ public class RepresentationToModel {
         if (rep.getFailureFactor() != null) newRealm.setFailureFactor(rep.getFailureFactor());
         if (rep.isEventsEnabled() != null) newRealm.setEventsEnabled(rep.isEventsEnabled());
         if (rep.getEventsExpiration() != null) newRealm.setEventsExpiration(rep.getEventsExpiration());
-        if (rep.getEventsListeners() != null) newRealm.setEventsListeners(new HashSet<String>(rep.getEventsListeners()));
+        if (rep.getEventsListeners() != null) newRealm.setEventsListeners(new HashSet<>(rep.getEventsListeners()));
 
         if (rep.getNotBefore() != null) newRealm.setNotBefore(rep.getNotBefore());
 
@@ -411,8 +411,8 @@ public class RepresentationToModel {
         if (rep.getEmailTheme() != null) realm.setEmailTheme(rep.getEmailTheme());
         if (rep.isEventsEnabled() != null) realm.setEventsEnabled(rep.isEventsEnabled());
         if (rep.getEventsExpiration() != null) realm.setEventsExpiration(rep.getEventsExpiration());
-        if (rep.getEventsListeners() != null) realm.setEventsListeners(new HashSet<String>(rep.getEventsListeners()));
-        if (rep.getEnabledEventTypes() != null) realm.setEnabledEventTypes(new HashSet<String>(rep.getEnabledEventTypes()));
+        if (rep.getEventsListeners() != null) realm.setEventsListeners(new HashSet<>(rep.getEventsListeners()));
+        if (rep.getEnabledEventTypes() != null) realm.setEnabledEventTypes(new HashSet<>(rep.getEnabledEventTypes()));
 
         if (rep.getPasswordPolicy() != null) realm.setPasswordPolicy(new PasswordPolicy(rep.getPasswordPolicy()));
 
diff --git a/model/api/src/test/java/org/keycloak/models/PasswordPolicyTest.java b/model/api/src/test/java/org/keycloak/models/PasswordPolicyTest.java
index 3bde361..14f28fb 100644
--- a/model/api/src/test/java/org/keycloak/models/PasswordPolicyTest.java
+++ b/model/api/src/test/java/org/keycloak/models/PasswordPolicyTest.java
@@ -1,5 +1,9 @@
 package org.keycloak.models;
 
+import static org.junit.Assert.fail;
+
+import java.util.regex.PatternSyntaxException;
+
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -79,6 +83,48 @@ public class PasswordPolicyTest {
         Assert.assertEquals("invalidPasswordNotUsernameMessage", policy.validate("jdoe", "jdoe").getMessage());
         Assert.assertNull(policy.validate("jdoe", "ab&d1234"));
     }
+    
+    @Test
+    public void testRegexPatterns() {
+        PasswordPolicy policy = null;
+        try {
+            policy = new PasswordPolicy("regexPatterns");
+            fail("Expected NullPointerEXception: Regex Pattern cannot be null.");
+        } catch (NullPointerException e) {
+            // Expected NPE as regex pattern is null.
+        }
+        
+        try {
+            policy = new PasswordPolicy("regexPatterns(*)");
+            fail("Expected PatternSyntaxException: Regex Pattern cannot be null.");
+        } catch (PatternSyntaxException e) {
+            // Expected PSE as regex pattern(or any of its token) is not quantifiable.
+        }
+        
+        try {
+            policy = new PasswordPolicy("regexPatterns(*,**)");
+            fail("Expected PatternSyntaxException: Regex Pattern cannot be null.");
+        } catch (PatternSyntaxException e) {
+            // Expected PSE as regex pattern(or any of its token) is not quantifiable.
+        }
+        
+        //Fails to match one of the regex pattern
+        policy = new PasswordPolicy("regexPatterns(jdoe,j*d)");
+        Assert.assertEquals("invalidPasswordRegexPatternMessage", policy.validate("jdoe", "jdoe").getMessage());
+        
+        ////Fails to match all of the regex patterns
+        policy = new PasswordPolicy("regexPatterns(j*p,j*d,adoe)");
+        Assert.assertEquals("invalidPasswordRegexPatternMessage", policy.validate("jdoe", "jdoe").getMessage());
+        
+        policy = new PasswordPolicy("regexPatterns([a-z][a-z][a-z][a-z][0-9])");
+        Assert.assertEquals("invalidPasswordRegexPatternMessage", policy.validate("jdoe", "jdoe").getMessage());
+        
+        policy = new PasswordPolicy("regexPatterns(jdoe)");
+        Assert.assertNull(policy.validate("jdoe", "jdoe"));
+        
+        policy = new PasswordPolicy("regexPatterns([a-z][a-z][a-z][a-z][0-9])");
+        Assert.assertNull(policy.validate("jdoe", "jdoe0"));
+    }
 
     @Test
     public void testComplex() {
diff --git a/model/file/src/main/java/org/keycloak/models/file/adapter/RealmAdapter.java b/model/file/src/main/java/org/keycloak/models/file/adapter/RealmAdapter.java
index 31b4bfc..cdd658a 100755
--- a/model/file/src/main/java/org/keycloak/models/file/adapter/RealmAdapter.java
+++ b/model/file/src/main/java/org/keycloak/models/file/adapter/RealmAdapter.java
@@ -966,14 +966,14 @@ public class RealmAdapter implements RealmModel {
     @Override
     public void setMasterAdminClient(ClientModel client) {
         if (client == null) {
-            realm.setAdminAppId(null);
+            realm.setMasterAdminClient(null);
             this.masterAdminApp = null;
         } else {
             String appId = client.getId();
             if (appId == null) {
                 throw new IllegalStateException("Master Admin app not initialized.");
             }
-            realm.setAdminAppId(appId);
+            realm.setMasterAdminClient(appId);
             this.masterAdminApp = client;
         }
     }
diff --git a/model/file/src/main/java/org/keycloak/models/file/adapter/UserAdapter.java b/model/file/src/main/java/org/keycloak/models/file/adapter/UserAdapter.java
index e9ba84a..236f95c 100755
--- a/model/file/src/main/java/org/keycloak/models/file/adapter/UserAdapter.java
+++ b/model/file/src/main/java/org/keycloak/models/file/adapter/UserAdapter.java
@@ -16,7 +16,9 @@
  */
 package org.keycloak.models.file.adapter;
 
-import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientModel;
+import static org.keycloak.models.utils.Pbkdf2PasswordEncoder.getSalt;
+
 import org.keycloak.models.PasswordPolicy;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RoleModel;
@@ -28,11 +30,14 @@ import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+
 import org.keycloak.connections.file.InMemoryModel;
 import org.keycloak.models.ModelDuplicateException;
 import org.keycloak.models.entities.FederatedIdentityEntity;
@@ -209,29 +214,80 @@ public class UserAdapter implements UserModel, Comparable {
 
     @Override
     public void updateCredential(UserCredentialModel cred) {
+
+        if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
+            updatePasswordCredential(cred);
+        } else {
+            CredentialEntity credentialEntity = getCredentialEntity(user, cred.getType());
+
+            if (credentialEntity == null) {
+                credentialEntity = setCredentials(user, cred);
+                credentialEntity.setValue(cred.getValue());
+                user.getCredentials().add(credentialEntity);
+            } else {
+                credentialEntity.setValue(cred.getValue());
+            }
+        }
+    }
+
+    private void updatePasswordCredential(UserCredentialModel cred) {
         CredentialEntity credentialEntity = getCredentialEntity(user, cred.getType());
 
         if (credentialEntity == null) {
-            credentialEntity = new CredentialEntity();
-            credentialEntity.setType(cred.getType());
-            credentialEntity.setDevice(cred.getDevice());
+            credentialEntity = setCredentials(user, cred);
+            setValue(credentialEntity, cred);
             user.getCredentials().add(credentialEntity);
-        }
-        if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
-            byte[] salt = Pbkdf2PasswordEncoder.getSalt();
-            int hashIterations = 1;
+        } else {
+
+            int expiredPasswordsPolicyValue = -1;
             PasswordPolicy policy = realm.getPasswordPolicy();
-            if (policy != null) {
-                hashIterations = policy.getHashIterations();
-                if (hashIterations == -1) hashIterations = 1;
+            if(policy != null) {
+                expiredPasswordsPolicyValue = policy.getExpiredPasswords();
+            }
+            
+            if (expiredPasswordsPolicyValue != -1) {
+                user.getCredentials().remove(credentialEntity);
+                credentialEntity.setType(UserCredentialModel.PASSWORD_HISTORY);
+                user.getCredentials().add(credentialEntity);
+
+                List<CredentialEntity> credentialEntities = getCredentialEntities(user, UserCredentialModel.PASSWORD_HISTORY);
+                if (credentialEntities.size() > expiredPasswordsPolicyValue - 1) {
+                    user.getCredentials().removeAll(credentialEntities.subList(expiredPasswordsPolicyValue - 1, credentialEntities.size()));
+                }
+
+                credentialEntity = setCredentials(user, cred);
+                setValue(credentialEntity, cred);
+                user.getCredentials().add(credentialEntity);
+            } else {
+                List<CredentialEntity> credentialEntities = getCredentialEntities(user, UserCredentialModel.PASSWORD_HISTORY);
+                if (credentialEntities != null && credentialEntities.size() > 0) {
+                    user.getCredentials().removeAll(credentialEntities);
+                }
+                setValue(credentialEntity, cred);
             }
-            credentialEntity.setValue(new Pbkdf2PasswordEncoder(salt).encode(cred.getValue(), hashIterations));
-            credentialEntity.setSalt(salt);
-            credentialEntity.setHashIterations(hashIterations);
-        } else {
-            credentialEntity.setValue(cred.getValue());
         }
+    }
+    
+    private CredentialEntity setCredentials(UserEntity user, UserCredentialModel cred) {
+        CredentialEntity credentialEntity = new CredentialEntity();
+        credentialEntity.setType(cred.getType());
+        credentialEntity.setCreatedDate(new Date().getTime());
         credentialEntity.setDevice(cred.getDevice());
+        return credentialEntity;
+    }
+
+    private void setValue(CredentialEntity credentialEntity, UserCredentialModel cred) {
+        byte[] salt = getSalt();
+        int hashIterations = 1;
+        PasswordPolicy policy = realm.getPasswordPolicy();
+        if (policy != null) {
+            hashIterations = policy.getHashIterations();
+            if (hashIterations == -1)
+                hashIterations = 1;
+        }
+        credentialEntity.setValue(new Pbkdf2PasswordEncoder(salt).encode(cred.getValue(), hashIterations));
+        credentialEntity.setSalt(salt);
+        credentialEntity.setHashIterations(hashIterations);
     }
 
     private CredentialEntity getCredentialEntity(UserEntity userEntity, String credType) {
@@ -244,6 +300,30 @@ public class UserAdapter implements UserModel, Comparable {
         return null;
     }
 
+    private List<CredentialEntity> getCredentialEntities(UserEntity userEntity, String credType) {
+        List<CredentialEntity> credentialEntities = new ArrayList<CredentialEntity>();
+        for (CredentialEntity entity : userEntity.getCredentials()) {
+            if (entity.getType().equals(credType)) {
+                credentialEntities.add(entity);
+            }
+        }
+        
+        // Avoiding direct use of credSecond.getCreatedDate() - credFirst.getCreatedDate() to prevent Integer Overflow
+        // Orders from most recent to least recent
+        Collections.sort(credentialEntities, new Comparator<CredentialEntity>() {
+            public int compare(CredentialEntity credFirst, CredentialEntity credSecond) {
+                if (credFirst.getCreatedDate() > credSecond.getCreatedDate()) {
+                    return -1;
+                } else if (credFirst.getCreatedDate() < credSecond.getCreatedDate()) {
+                    return 1;
+                } else {
+                    return 0;
+                }
+            }
+        });
+        return credentialEntities;
+    }
+
     @Override
     public List<UserCredentialValueModel> getCredentialsDirectly() {
         List<CredentialEntity> credentials = new ArrayList<CredentialEntity>(user.getCredentials());
@@ -253,6 +333,7 @@ public class UserAdapter implements UserModel, Comparable {
             UserCredentialValueModel credModel = new UserCredentialValueModel();
             credModel.setType(credEntity.getType());
             credModel.setDevice(credEntity.getDevice());
+            credModel.setCreatedDate(credEntity.getCreatedDate());
             credModel.setValue(credEntity.getValue());
             credModel.setSalt(credEntity.getSalt());
             credModel.setHashIterations(credEntity.getHashIterations());
@@ -272,6 +353,7 @@ public class UserAdapter implements UserModel, Comparable {
         //    credentialEntity.setId(KeycloakModelUtils.generateId());
             credentialEntity.setType(credModel.getType());
         //    credentialEntity.setUser(user);
+            credModel.setCreatedDate(credModel.getCreatedDate());
             user.getCredentials().add(credentialEntity);
         }
 
diff --git a/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanRealmCache.java b/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanRealmCache.java
index b5204e8..ec93a7a 100755
--- a/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanRealmCache.java
+++ b/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/InfinispanRealmCache.java
@@ -3,7 +3,7 @@ package org.keycloak.models.cache.infinispan;
 import org.infinispan.Cache;
 import org.jboss.logging.Logger;
 import org.keycloak.models.cache.RealmCache;
-import org.keycloak.models.cache.entities.CachedApplication;
+import org.keycloak.models.cache.entities.CachedClient;
 import org.keycloak.models.cache.entities.CachedRealm;
 import org.keycloak.models.cache.entities.CachedRole;
 
@@ -77,19 +77,19 @@ public class InfinispanRealmCache implements RealmCache {
     }
 
     @Override
-    public CachedApplication getApplication(String id) {
+    public CachedClient getApplication(String id) {
         if (!enabled) return null;
-        return get(id, CachedApplication.class);
+        return get(id, CachedClient.class);
     }
 
     @Override
-    public void invalidateApplication(CachedApplication app) {
+    public void invalidateApplication(CachedClient app) {
         logger.tracev("Removing application {0}", app.getId());
         cache.remove(app.getId());
     }
 
     @Override
-    public void addCachedApplication(CachedApplication app) {
+    public void addCachedClient(CachedClient app) {
         if (!enabled) return;
         logger.tracev("Adding application {0}", app.getId());
         cache.put(app.getId(), app);
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/ClientAdapter.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/ClientAdapter.java
index 5330fbc..436405e 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/ClientAdapter.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/ClientAdapter.java
@@ -6,7 +6,7 @@ import org.keycloak.models.ProtocolMapperModel;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RoleContainerModel;
 import org.keycloak.models.RoleModel;
-import org.keycloak.models.cache.entities.CachedApplication;
+import org.keycloak.models.cache.entities.CachedClient;
 
 import java.util.HashMap;
 import java.util.HashSet;
@@ -24,9 +24,9 @@ public class ClientAdapter implements ClientModel {
     protected RealmCache cache;
 
     protected ClientModel updated;
-    protected CachedApplication cached;
+    protected CachedClient cached;
 
-    public ClientAdapter(RealmModel cachedRealm, CachedApplication cached, CacheRealmProvider cacheSession, RealmCache cache) {
+    public ClientAdapter(RealmModel cachedRealm, CachedClient cached, CacheRealmProvider cacheSession, RealmCache cache) {
         this.cachedRealm = cachedRealm;
         this.cache = cache;
         this.cacheSession = cacheSession;
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheRealmProvider.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheRealmProvider.java
index 933ac74..13f6924 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheRealmProvider.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheRealmProvider.java
@@ -6,8 +6,8 @@ import org.keycloak.models.KeycloakTransaction;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RealmProvider;
 import org.keycloak.models.RoleModel;
-import org.keycloak.models.cache.entities.CachedApplication;
-import org.keycloak.models.cache.entities.CachedApplicationRole;
+import org.keycloak.models.cache.entities.CachedClient;
+import org.keycloak.models.cache.entities.CachedClientRole;
 import org.keycloak.models.cache.entities.CachedRealm;
 import org.keycloak.models.cache.entities.CachedRealmRole;
 import org.keycloak.models.cache.entities.CachedRole;
@@ -235,7 +235,7 @@ public class DefaultCacheRealmProvider implements CacheRealmProvider {
             if (model == null) return null;
             if (roleInvalidations.contains(id)) return model;
             if (model.getContainer() instanceof ClientModel) {
-                cached = new CachedApplicationRole(((ClientModel) model.getContainer()).getId(), model, realm);
+                cached = new CachedClientRole(((ClientModel) model.getContainer()).getId(), model, realm);
             } else {
                 cached = new CachedRealmRole(model, realm);
             }
@@ -254,7 +254,7 @@ public class DefaultCacheRealmProvider implements CacheRealmProvider {
     @Override
     public ClientModel getClientById(String id, RealmModel realm) {
         if (!cache.isEnabled()) return getDelegate().getClientById(id, realm);
-        CachedApplication cached = cache.getApplication(id);
+        CachedClient cached = cache.getApplication(id);
         if (cached != null && !cached.getRealm().equals(realm.getId())) {
             cached = null;
         }
@@ -263,8 +263,8 @@ public class DefaultCacheRealmProvider implements CacheRealmProvider {
             ClientModel model = getDelegate().getClientById(id, realm);
             if (model == null) return null;
             if (appInvalidations.contains(id)) return model;
-            cached = new CachedApplication(cache, getDelegate(), realm, model);
-            cache.addCachedApplication(cached);
+            cached = new CachedClient(cache, getDelegate(), realm, model);
+            cache.addCachedClient(cached);
         } else if (appInvalidations.contains(id)) {
             return getDelegate().getClientById(id, realm);
         } else if (managedApplications.containsKey(id)) {
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java
index d302c48..e490814 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java
@@ -66,7 +66,7 @@ public class CachedRealm {
     private String accountTheme;
     private String adminTheme;
     private String emailTheme;
-    private String masterAdminApp;
+    private String masterAdminClient;
 
     private List<RequiredCredentialModel> requiredCredentials = new ArrayList<RequiredCredentialModel>();
     private List<UserFederationProviderModel> userFederationProviders = new ArrayList<UserFederationProviderModel>();
@@ -81,7 +81,6 @@ public class CachedRealm {
     private Set<String> enabledEventTypes = new HashSet<String>();
     private List<String> defaultRoles = new LinkedList<String>();
     private Map<String, String> realmRoles = new HashMap<String, String>();
-    private Map<String, String> applications = new HashMap<String, String>();
     private Map<String, String> clients = new HashMap<String, String>();
     private boolean internationalizationEnabled;
     private Set<String> supportedLocales = new HashSet<String>();
@@ -155,7 +154,7 @@ public class CachedRealm {
         eventsListeners.addAll(model.getEventsListeners());
         enabledEventTypes.addAll(model.getEnabledEventTypes());
         defaultRoles.addAll(model.getDefaultRoles());
-        masterAdminApp = model.getMasterAdminClient().getId();
+        masterAdminClient = model.getMasterAdminClient().getId();
 
         for (RoleModel role : model.getRoles()) {
             realmRoles.put(role.getName(), role.getId());
@@ -163,10 +162,10 @@ public class CachedRealm {
             cache.addCachedRole(cachedRole);
         }
 
-        for (ClientModel app : model.getClients()) {
-            applications.put(app.getClientId(), app.getId());
-            CachedApplication cachedApp = new CachedApplication(cache, delegate, model, app);
-            cache.addCachedApplication(cachedApp);
+        for (ClientModel client : model.getClients()) {
+            clients.put(client.getClientId(), client.getId());
+            CachedClient cachedClient = new CachedClient(cache, delegate, model, client);
+            cache.addCachedClient(cachedClient);
         }
 
         internationalizationEnabled = model.isInternationalizationEnabled();
@@ -180,8 +179,8 @@ public class CachedRealm {
         return id;
     }
 
-    public String getMasterAdminApp() {
-        return masterAdminApp;
+    public String getMasterAdminClient() {
+        return masterAdminClient;
     }
 
     public String getName() {
@@ -196,10 +195,6 @@ public class CachedRealm {
         return realmRoles;
     }
 
-    public Map<String, String> getApplications() {
-        return applications;
-    }
-
     public Map<String, String> getClients() {
         return clients;
     }
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/MemoryRealmCache.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/MemoryRealmCache.java
index 25064c6..3b04670 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/MemoryRealmCache.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/MemoryRealmCache.java
@@ -1,6 +1,6 @@
 package org.keycloak.models.cache;
 
-import org.keycloak.models.cache.entities.CachedApplication;
+import org.keycloak.models.cache.entities.CachedClient;
 import org.keycloak.models.cache.entities.CachedRealm;
 import org.keycloak.models.cache.entities.CachedRole;
 
@@ -14,7 +14,7 @@ public class MemoryRealmCache implements RealmCache {
 
     protected ConcurrentHashMap<String, CachedRealm> realmCache = new ConcurrentHashMap<String, CachedRealm>();
     protected ConcurrentHashMap<String, CachedRealm> realmCacheByName = new ConcurrentHashMap<String, CachedRealm>();
-    protected ConcurrentHashMap<String, CachedApplication> applicationCache = new ConcurrentHashMap<String, CachedApplication>();
+    protected ConcurrentHashMap<String, CachedClient> applicationCache = new ConcurrentHashMap<String, CachedClient>();
     protected ConcurrentHashMap<String, CachedRole> roleCache = new ConcurrentHashMap<String, CachedRole>();
     protected volatile boolean enabled = true;
 
@@ -72,18 +72,18 @@ public class MemoryRealmCache implements RealmCache {
     }
 
     @Override
-    public CachedApplication getApplication(String id) {
+    public CachedClient getApplication(String id) {
         if (!enabled) return null;
         return applicationCache.get(id);
     }
 
     @Override
-    public void invalidateApplication(CachedApplication app) {
+    public void invalidateApplication(CachedClient app) {
         applicationCache.remove(app.getId());
     }
 
     @Override
-    public void addCachedApplication(CachedApplication app) {
+    public void addCachedClient(CachedClient app) {
         if (!enabled) return;
         applicationCache.put(app.getId(), app);
     }
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java
index f2532af..68a65ac 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java
@@ -474,7 +474,7 @@ public class RealmAdapter implements RealmModel {
     public Map<String, ClientModel> getClientNameMap() {
         if (updated != null) return updated.getClientNameMap();
         Map<String, ClientModel> map = new HashMap<String, ClientModel>();
-        for (String id : cached.getApplications().values()) {
+        for (String id : cached.getClients().values()) {
             ClientModel model = cacheSession.getClientById(id, this);
             if (model == null) {
                 throw new IllegalStateException("Cached application not found: " + id);
@@ -488,7 +488,7 @@ public class RealmAdapter implements RealmModel {
     public List<ClientModel> getClients() {
         if (updated != null) return updated.getClients();
         List<ClientModel> apps = new LinkedList<ClientModel>();
-        for (String id : cached.getApplications().values()) {
+        for (String id : cached.getClients().values()) {
             ClientModel model = cacheSession.getClientById(id, this);
             if (model == null) {
                 throw new IllegalStateException("Cached application not found: " + id);
@@ -531,7 +531,7 @@ public class RealmAdapter implements RealmModel {
     @Override
     public ClientModel getClientByClientId(String clientId) {
         if (updated != null) return updated.getClientByClientId(clientId);
-        String id = cached.getApplications().get(clientId);
+        String id = cached.getClients().get(clientId);
         if (id == null) return null;
         return getClientById(id);
     }
@@ -752,7 +752,7 @@ public class RealmAdapter implements RealmModel {
     
     @Override
     public ClientModel getMasterAdminClient() {
-        return cacheSession.getRealm(Config.getAdminRealm()).getClientById(cached.getMasterAdminApp());
+        return cacheSession.getRealm(Config.getAdminRealm()).getClientById(cached.getMasterAdminClient());
     }
 
     @Override
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmCache.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmCache.java
index 3d66462..b683f57 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmCache.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmCache.java
@@ -1,6 +1,6 @@
 package org.keycloak.models.cache;
 
-import org.keycloak.models.cache.entities.CachedApplication;
+import org.keycloak.models.cache.entities.CachedClient;
 import org.keycloak.models.cache.entities.CachedRealm;
 import org.keycloak.models.cache.entities.CachedRole;
 
@@ -21,11 +21,11 @@ public interface RealmCache {
 
     void invalidateCachedRealmById(String id);
 
-    CachedApplication getApplication(String id);
+    CachedClient getApplication(String id);
 
-    void invalidateApplication(CachedApplication app);
+    void invalidateApplication(CachedClient app);
 
-    void addCachedApplication(CachedApplication app);
+    void addCachedClient(CachedClient app);
 
     void invalidateCachedApplicationById(String id);
 
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RoleAdapter.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RoleAdapter.java
index 0f30ecd..3323338 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RoleAdapter.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RoleAdapter.java
@@ -3,7 +3,7 @@ package org.keycloak.models.cache;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RoleContainerModel;
 import org.keycloak.models.RoleModel;
-import org.keycloak.models.cache.entities.CachedApplicationRole;
+import org.keycloak.models.cache.entities.CachedClientRole;
 import org.keycloak.models.cache.entities.CachedRealmRole;
 import org.keycloak.models.cache.entities.CachedRole;
 import org.keycloak.models.utils.KeycloakModelUtils;
@@ -106,8 +106,8 @@ public class RoleAdapter implements RoleModel {
         if (cached instanceof CachedRealmRole) {
             return realm;
         } else {
-            CachedApplicationRole appRole = (CachedApplicationRole)cached;
-            return realm.getClientById(appRole.getAppId());
+            CachedClientRole appRole = (CachedClientRole)cached;
+            return realm.getClientById(appRole.getIdClient());
         }
     }
 
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
index b04a92c..7ebb242 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
@@ -545,9 +545,9 @@ public class ClientAdapter implements ClientModel {
 
     @Override
     public RoleModel getRole(String name) {
-        TypedQuery<RoleEntity> query = em.createNamedQuery("getAppRoleByName", RoleEntity.class);
+        TypedQuery<RoleEntity> query = em.createNamedQuery("getClientRoleByName", RoleEntity.class);
         query.setParameter("name", name);
-        query.setParameter("application", entity);
+        query.setParameter("client", entity);
         List<RoleEntity> roles = query.getResultList();
         if (roles.size() == 0) return null;
         return new RoleAdapter(realm, em, roles.get(0));
@@ -563,8 +563,8 @@ public class ClientAdapter implements ClientModel {
         RoleEntity roleEntity = new RoleEntity();
         roleEntity.setId(id);
         roleEntity.setName(name);
-        roleEntity.setApplication(entity);
-        roleEntity.setApplicationRole(true);
+        roleEntity.setClient(entity);
+        roleEntity.setClientRole(true);
         roleEntity.setRealmId(realm.getId());
         em.persist(roleEntity);
         entity.getRoles().add(roleEntity);
@@ -581,13 +581,13 @@ public class ClientAdapter implements ClientModel {
 
         session.users().preRemove(getRealm(), roleModel);
         RoleEntity role = RoleAdapter.toRoleEntity(roleModel, em);
-        if (!role.isApplicationRole()) return false;
+        if (!role.isClientRole()) return false;
 
         entity.getRoles().remove(role);
         entity.getDefaultRoles().remove(role);
         em.createNativeQuery("delete from COMPOSITE_ROLE where CHILD_ROLE = :role").setParameter("role", role).executeUpdate();
         em.createNamedQuery("deleteScopeMappingByRole").setParameter("role", role).executeUpdate();
-        role.setApplication(null);
+        role.setClient(null);
         em.flush();
         em.remove(role);
         em.flush();
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 f8a25f1..26322d4 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
@@ -96,17 +96,17 @@ public class ClientEntity {
     @Column(name="NODE_REREG_TIMEOUT")
     private int nodeReRegistrationTimeout;
 
-    @OneToMany(fetch = FetchType.EAGER, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "application")
+    @OneToMany(fetch = FetchType.EAGER, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "client")
     Collection<RoleEntity> roles = new ArrayList<RoleEntity>();
 
     @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true)
-    @JoinTable(name="APPLICATION_DEFAULT_ROLES", joinColumns = { @JoinColumn(name="APPLICATION_ID")}, inverseJoinColumns = { @JoinColumn(name="ROLE_ID")})
+    @JoinTable(name="CLIENT_DEFAULT_ROLES", joinColumns = { @JoinColumn(name="CLIENT_ID")}, inverseJoinColumns = { @JoinColumn(name="ROLE_ID")})
     Collection<RoleEntity> defaultRoles = new ArrayList<RoleEntity>();
 
     @ElementCollection
     @MapKeyColumn(name="NAME")
     @Column(name="VALUE")
-    @CollectionTable(name="APP_NODE_REGISTRATIONS", joinColumns={ @JoinColumn(name="APPLICATION_ID") })
+    @CollectionTable(name="CLIENT_NODE_REGISTRATIONS", joinColumns={ @JoinColumn(name="CLIENT_ID") })
     Map<String, Integer> registeredNodes = new HashMap<String, Integer>();
 
     public RealmEntity getRealm() {
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/CredentialEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/CredentialEntity.java
index de4039c..bdbfe84 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/CredentialEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/CredentialEntity.java
@@ -37,7 +37,9 @@ public class CredentialEntity {
     protected byte[] salt;
     @Column(name="HASH_ITERATIONS")
     protected int hashIterations;
-
+    @Column(name="CREATED_DATE")
+    protected long createdDate;
+    
     @ManyToOne(fetch = FetchType.LAZY)
     @JoinColumn(name="USER_ID")
     protected UserEntity user;
@@ -97,4 +99,13 @@ public class CredentialEntity {
     public void setHashIterations(int hashIterations) {
         this.hashIterations = hashIterations;
     }
+
+    public long getCreatedDate() {
+        return createdDate;
+    }
+
+    public void setCreatedDate(long createdDate) {
+        this.createdDate = createdDate;
+    }
+    
 }
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
index d39b62e..efdb4a8 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
@@ -104,8 +104,8 @@ public class RealmEntity {
     List<UserFederationProviderEntity> userFederationProviders = new ArrayList<UserFederationProviderEntity>();
 
     @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true)
-    @JoinTable(name="REALM_APPLICATION", joinColumns={ @JoinColumn(name="REALM_ID") }, inverseJoinColumns={ @JoinColumn(name="APPLICATION_ID") })
-    Collection<ClientEntity> applications = new ArrayList<ClientEntity>();
+    @JoinTable(name="REALM_CLIENT", joinColumns={ @JoinColumn(name="REALM_ID") }, inverseJoinColumns={ @JoinColumn(name="CLIENT_ID") })
+    Collection<ClientEntity> clients = new ArrayList<>();
 
     @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
     Collection<RoleEntity> roles = new ArrayList<RoleEntity>();
@@ -136,8 +136,8 @@ public class RealmEntity {
     protected Set<String> enabledEventTypes = new HashSet<String>();
 
     @OneToOne
-    @JoinColumn(name="MASTER_ADMIN_APP")
-    protected ClientEntity masterAdminApp;
+    @JoinColumn(name="MASTER_ADMIN_CLIENT")
+    protected ClientEntity masterAdminClient;
 
     @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
     protected List<IdentityProviderEntity> identityProviders = new ArrayList<IdentityProviderEntity>();
@@ -318,12 +318,12 @@ public class RealmEntity {
         this.requiredCredentials = requiredCredentials;
     }
 
-    public Collection<ClientEntity> getApplications() {
-        return applications;
+    public Collection<ClientEntity> getClients() {
+        return clients;
     }
 
-    public void setApplications(Collection<ClientEntity> applications) {
-        this.applications = applications;
+    public void setClients(Collection<ClientEntity> clients) {
+        this.clients = clients;
     }
 
     public Collection<RoleEntity> getRoles() {
@@ -437,12 +437,12 @@ public class RealmEntity {
         this.enabledEventTypes = enabledEventTypes;
     }
     
-    public ClientEntity getMasterAdminApp() {
-        return masterAdminApp;
+    public ClientEntity getMasterAdminClient() {
+        return masterAdminClient;
     }
 
-    public void setMasterAdminApp(ClientEntity masterAdminApp) {
-        this.masterAdminApp = masterAdminApp;
+    public void setMasterAdminClient(ClientEntity masterAdminClient) {
+        this.masterAdminClient = masterAdminClient;
     }
 
     public List<UserFederationProviderEntity> getUserFederationProviders() {
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 ffbd711..262a469 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
@@ -21,11 +21,11 @@ import java.util.Collection;
  */
 @Entity
 @Table(name="KEYCLOAK_ROLE", uniqueConstraints = {
-        @UniqueConstraint(columnNames = { "NAME", "APP_REALM_CONSTRAINT" })
+        @UniqueConstraint(columnNames = { "NAME", "CLIENT_REALM_CONSTRAINT" })
 })
 @NamedQueries({
-        @NamedQuery(name="getAppRoleByName", query="select role from RoleEntity role where role.name = :name and role.application = :application"),
-        @NamedQuery(name="getRealmRoleByName", query="select role from RoleEntity role where role.applicationRole = false and role.name = :name and role.realm = :realm")
+        @NamedQuery(name="getClientRoleByName", query="select role from RoleEntity role where role.name = :name and role.client = :client"),
+        @NamedQuery(name="getRealmRoleByName", query="select role from RoleEntity role where role.clientRole = false and role.name = :name and role.realm = :realm")
 })
 
 public class RoleEntity {
@@ -46,16 +46,16 @@ public class RoleEntity {
     @JoinColumn(name = "REALM")
     private RealmEntity realm;
 
-    @Column(name="APPLICATION_ROLE")
-    private boolean applicationRole;
+    @Column(name="CLIENT_ROLE")
+    private boolean clientRole;
 
     @ManyToOne(fetch = FetchType.LAZY)
-    @JoinColumn(name = "APPLICATION")
-    private ClientEntity application;
+    @JoinColumn(name = "CLIENT")
+    private ClientEntity client;
 
-    // Hack to ensure that either name+application or name+realm are unique. Needed due to MS-SQL as it don't allow multiple NULL values in the column, which is part of constraint
-    @Column(name="APP_REALM_CONSTRAINT", length = 36)
-    private String appRealmConstraint;
+    // Hack to ensure that either name+client or name+realm are unique. Needed due to MS-SQL as it don't allow multiple NULL values in the column, which is part of constraint
+    @Column(name="CLIENT_REALM_CONSTRAINT", length = 36)
+    private String clientRealmConstraint;
 
     @ManyToMany(fetch = FetchType.LAZY, cascade = {})
     @JoinTable(name = "COMPOSITE_ROLE", joinColumns = @JoinColumn(name = "COMPOSITE"), inverseJoinColumns = @JoinColumn(name = "CHILD_ROLE"))
@@ -101,12 +101,12 @@ public class RoleEntity {
         this.compositeRoles = compositeRoles;
     }
 
-    public boolean isApplicationRole() {
-        return applicationRole;
+    public boolean isClientRole() {
+        return clientRole;
     }
 
-    public void setApplicationRole(boolean applicationRole) {
-        this.applicationRole = applicationRole;
+    public void setClientRole(boolean clientRole) {
+        this.clientRole = clientRole;
     }
 
     public RealmEntity getRealm() {
@@ -115,26 +115,26 @@ public class RoleEntity {
 
     public void setRealm(RealmEntity realm) {
         this.realm = realm;
-        this.appRealmConstraint = realm.getId();
+        this.clientRealmConstraint = realm.getId();
     }
 
-    public ClientEntity getApplication() {
-        return application;
+    public ClientEntity getClient() {
+        return client;
     }
 
-    public void setApplication(ClientEntity application) {
-        this.application = application;
-        if (application != null) {
-            this.appRealmConstraint = application.getId();
+    public void setClient(ClientEntity client) {
+        this.client = client;
+        if (client != null) {
+            this.clientRealmConstraint = client.getId();
         }
     }
 
-    public String getAppRealmConstraint() {
-        return appRealmConstraint;
+    public String getClientRealmConstraint() {
+        return clientRealmConstraint;
     }
 
-    public void setAppRealmConstraint(String appRealmConstraint) {
-        this.appRealmConstraint = appRealmConstraint;
+    public void setClientRealmConstraint(String clientRealmConstraint) {
+        this.clientRealmConstraint = clientRealmConstraint;
     }
 
     @Override
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
index f887670..1be423a 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
@@ -91,7 +91,7 @@ public class JpaRealmProvider implements RealmProvider {
 
         RealmAdapter adapter = new RealmAdapter(session, em, realm);
         session.users().preRemove(adapter);
-        for (ClientEntity a : new LinkedList<>(realm.getApplications())) {
+        for (ClientEntity a : new LinkedList<>(realm.getClients())) {
             adapter.removeClient(a.getId());
         }
 
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
index 4370165..2ab9ac7 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
@@ -619,8 +619,8 @@ public class RealmAdapter implements RealmModel {
     @Override
     public List<ClientModel> getClients() {
         List<ClientModel> list = new ArrayList<ClientModel>();
-        if (realm.getApplications() == null) return list;
-        for (ClientEntity entity : realm.getApplications()) {
+        if (realm.getClients() == null) return list;
+        for (ClientEntity entity : realm.getClients()) {
             list.add(new ClientAdapter(this, em, session, entity));
         }
         return list;
@@ -633,15 +633,15 @@ public class RealmAdapter implements RealmModel {
 
     @Override
     public ClientModel addClient(String id, String clientId) {
-        ClientEntity applicationData = new ClientEntity();
-        applicationData.setId(id);
-        applicationData.setClientId(clientId);
-        applicationData.setEnabled(true);
-        applicationData.setRealm(realm);
-        realm.getApplications().add(applicationData);
-        em.persist(applicationData);
+        ClientEntity entity = new ClientEntity();
+        entity.setId(id);
+        entity.setClientId(clientId);
+        entity.setEnabled(true);
+        entity.setRealm(realm);
+        realm.getClients().add(entity);
+        em.persist(entity);
         em.flush();
-        final ClientModel resource = new ClientAdapter(this, em, session, applicationData);
+        final ClientModel resource = new ClientAdapter(this, em, session, entity);
         em.flush();
         session.getKeycloakSessionFactory().publish(new ClientCreationEvent() {
             @Override
@@ -655,15 +655,15 @@ public class RealmAdapter implements RealmModel {
     @Override
     public boolean removeClient(String id) {
         if (id == null) return false;
-        ClientModel application = getClientById(id);
-        if (application == null) return false;
+        ClientModel client = getClientById(id);
+        if (client == null) return false;
 
-        for (RoleModel role : application.getRoles()) {
-            application.removeRole(role);
+        for (RoleModel role : client.getRoles()) {
+            client.removeRole(role);
         }
 
         ClientEntity clientEntity = null;
-        Iterator<ClientEntity> it = realm.getApplications().iterator();
+        Iterator<ClientEntity> it = realm.getClients().iterator();
         while (it.hasNext()) {
             ClientEntity ae = it.next();
             if (ae.getId().equals(id)) {
@@ -672,12 +672,12 @@ public class RealmAdapter implements RealmModel {
                 break;
             }
         }
-        for (ClientEntity a : realm.getApplications()) {
+        for (ClientEntity a : realm.getClients()) {
             if (a.getId().equals(id)) {
                 clientEntity = a;
             }
         }
-        if (application == null) {
+        if (client == null) {
             return false;
         }
         em.remove(clientEntity);
@@ -1066,13 +1066,13 @@ public class RealmAdapter implements RealmModel {
 
     @Override
     public ClientModel getMasterAdminClient() {
-        return new ClientAdapter(this, em, session, realm.getMasterAdminApp());
+        return new ClientAdapter(this, em, session, realm.getMasterAdminClient());
     }
 
     @Override
     public void setMasterAdminClient(ClientModel client) {
         ClientEntity appEntity = client !=null ? em.getReference(ClientEntity.class, client.getId()) : null;
-        realm.setMasterAdminApp(appEntity);
+        realm.setMasterAdminClient(appEntity);
         em.flush();
     }
 
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java
index 1687855..4a8e258 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java
@@ -104,8 +104,8 @@ public class RoleAdapter implements RoleModel {
 
     @Override
     public RoleContainerModel getContainer() {
-        if (role.isApplicationRole()) {
-            return realm.getClientById(role.getApplication().getId());
+        if (role.isClientRole()) {
+            return realm.getClientById(role.getClient().getId());
 
         } else {
             return realm;
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java
index a51d0de..2216d22 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java
@@ -18,7 +18,11 @@ import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
 
 import javax.persistence.EntityManager;
 import javax.persistence.TypedQuery;
+
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -160,7 +164,6 @@ public class UserAdapter implements UserModel {
         }
     }
 
-
     @Override
     public String getFirstName() {
         return user.getFirstName();
@@ -208,33 +211,86 @@ public class UserAdapter implements UserModel {
 
     @Override
     public void updateCredential(UserCredentialModel cred) {
+
+        if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
+            updatePasswordCredential(cred);
+        } else {
+            CredentialEntity credentialEntity = getCredentialEntity(user, cred.getType());
+
+            if (credentialEntity == null) {
+                credentialEntity = setCredentials(user, cred);
+                credentialEntity.setValue(cred.getValue());
+                em.persist(credentialEntity);
+                user.getCredentials().add(credentialEntity);
+            } else {
+                credentialEntity.setValue(cred.getValue());
+            }
+        }
+        em.flush();
+    }
+
+    private void updatePasswordCredential(UserCredentialModel cred) {
         CredentialEntity credentialEntity = getCredentialEntity(user, cred.getType());
 
         if (credentialEntity == null) {
-            credentialEntity = new CredentialEntity();
-            credentialEntity.setId(KeycloakModelUtils.generateId());
-            credentialEntity.setType(cred.getType());
-            credentialEntity.setDevice(cred.getDevice());
-            credentialEntity.setUser(user);
+            credentialEntity = setCredentials(user, cred);
+            setValue(credentialEntity, cred);
             em.persist(credentialEntity);
             user.getCredentials().add(credentialEntity);
-        }
-        if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
-            byte[] salt = getSalt();
-            int hashIterations = 1;
+        } else {
+            
+            int expiredPasswordsPolicyValue = -1;
             PasswordPolicy policy = realm.getPasswordPolicy();
-            if (policy != null) {
-                hashIterations = policy.getHashIterations();
-                if (hashIterations == -1) hashIterations = 1;
+            if(policy != null) {
+                expiredPasswordsPolicyValue = policy.getExpiredPasswords();
+            }
+
+            if (expiredPasswordsPolicyValue != -1) {
+                user.getCredentials().remove(credentialEntity);
+                credentialEntity.setType(UserCredentialModel.PASSWORD_HISTORY);
+                user.getCredentials().add(credentialEntity);
+
+                List<CredentialEntity> credentialEntities = getCredentialEntities(user, UserCredentialModel.PASSWORD_HISTORY);
+                if (credentialEntities.size() > expiredPasswordsPolicyValue - 1) {
+                    user.getCredentials().removeAll(credentialEntities.subList(expiredPasswordsPolicyValue - 1, credentialEntities.size()));
+                }
+
+                credentialEntity = setCredentials(user, cred);
+                setValue(credentialEntity, cred);
+                em.persist(credentialEntity);
+                user.getCredentials().add(credentialEntity);
+            } else {
+                List<CredentialEntity> credentialEntities = getCredentialEntities(user, UserCredentialModel.PASSWORD_HISTORY);
+                if (credentialEntities != null && credentialEntities.size() > 0) {
+                    user.getCredentials().removeAll(credentialEntities);
+                }
+                setValue(credentialEntity, cred);
             }
-            credentialEntity.setValue(new Pbkdf2PasswordEncoder(salt).encode(cred.getValue(), hashIterations));
-            credentialEntity.setSalt(salt);
-            credentialEntity.setHashIterations(hashIterations);
-        } else {
-            credentialEntity.setValue(cred.getValue());
         }
+    }
+    
+    private CredentialEntity setCredentials(UserEntity user, UserCredentialModel cred) {
+        CredentialEntity credentialEntity = new CredentialEntity();
+        credentialEntity.setId(KeycloakModelUtils.generateId());
+        credentialEntity.setType(cred.getType());
+        credentialEntity.setCreatedDate(new Date().getTime());
         credentialEntity.setDevice(cred.getDevice());
-        em.flush();
+        credentialEntity.setUser(user);
+        return credentialEntity;
+    }
+
+    private void setValue(CredentialEntity credentialEntity, UserCredentialModel cred) {
+        byte[] salt = getSalt();
+        int hashIterations = 1;
+        PasswordPolicy policy = realm.getPasswordPolicy();
+        if (policy != null) {
+            hashIterations = policy.getHashIterations();
+            if (hashIterations == -1)
+                hashIterations = 1;
+        }
+        credentialEntity.setValue(new Pbkdf2PasswordEncoder(salt).encode(cred.getValue(), hashIterations));
+        credentialEntity.setSalt(salt);
+        credentialEntity.setHashIterations(hashIterations);
     }
 
     private CredentialEntity getCredentialEntity(UserEntity userEntity, String credType) {
@@ -247,6 +303,30 @@ public class UserAdapter implements UserModel {
         return null;
     }
 
+    private List<CredentialEntity> getCredentialEntities(UserEntity userEntity, String credType) {
+        List<CredentialEntity> credentialEntities = new ArrayList<CredentialEntity>();
+        for (CredentialEntity entity : userEntity.getCredentials()) {
+            if (entity.getType().equals(credType)) {
+                credentialEntities.add(entity);
+            }
+        }
+
+        // Avoiding direct use of credSecond.getCreatedDate() - credFirst.getCreatedDate() to prevent Integer Overflow
+        // Orders from most recent to least recent
+        Collections.sort(credentialEntities, new Comparator<CredentialEntity>() {
+            public int compare(CredentialEntity credFirst, CredentialEntity credSecond) {
+                if (credFirst.getCreatedDate() > credSecond.getCreatedDate()) {
+                    return -1;
+                } else if (credFirst.getCreatedDate() < credSecond.getCreatedDate()) {
+                    return 1;
+                } else {
+                    return 0;
+                }
+            }
+        });
+        return credentialEntities;
+    }
+
     @Override
     public List<UserCredentialValueModel> getCredentialsDirectly() {
         List<CredentialEntity> credentials = new ArrayList<CredentialEntity>(user.getCredentials());
@@ -258,6 +338,7 @@ public class UserAdapter implements UserModel {
                 credModel.setType(credEntity.getType());
                 credModel.setDevice(credEntity.getDevice());
                 credModel.setValue(credEntity.getValue());
+                credModel.setCreatedDate(credEntity.getCreatedDate());
                 credModel.setSalt(credEntity.getSalt());
                 credModel.setHashIterations(credEntity.getHashIterations());
 
@@ -276,6 +357,7 @@ public class UserAdapter implements UserModel {
             credentialEntity = new CredentialEntity();
             credentialEntity.setId(KeycloakModelUtils.generateId());
             credentialEntity.setType(credModel.getType());
+            credentialEntity.setCreatedDate(credModel.getCreatedDate());
             credentialEntity.setUser(user);
             em.persist(credentialEntity);
             user.getCredentials().add(credentialEntity);
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
index d7a534a..e515dde 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
@@ -91,7 +91,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
 
     @Override
     public SslRequired getSslRequired() {
-        return SslRequired.valueOf(realm.getSslRequired());
+        return realm.getSslRequired() != null ? SslRequired.valueOf(realm.getSslRequired()) : null;
     }
 
     @Override
@@ -604,11 +604,11 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
         DBObject query = new QueryBuilder()
                 .and("realmId").is(getId())
                 .get();
-        List<MongoClientEntity> appDatas = getMongoStore().loadEntities(MongoClientEntity.class, query, invocationContext);
+        List<MongoClientEntity> clientEntities = getMongoStore().loadEntities(MongoClientEntity.class, query, invocationContext);
 
         List<ClientModel> result = new ArrayList<ClientModel>();
-        for (MongoClientEntity appData : appDatas) {
-            result.add(new ClientAdapter(session, this, appData, invocationContext));
+        for (MongoClientEntity clientEntity : clientEntities) {
+            result.add(new ClientAdapter(session, this, clientEntity, invocationContext));
         }
         return result;
     }
@@ -620,14 +620,14 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
 
     @Override
     public ClientModel addClient(String id, String clientId) {
-        MongoClientEntity appData = new MongoClientEntity();
-        appData.setId(id);
-        appData.setClientId(clientId);
-        appData.setRealmId(getId());
-        appData.setEnabled(true);
-        getMongoStore().insertEntity(appData, invocationContext);
-
-        final ClientModel model = new ClientAdapter(session, this, appData, invocationContext);
+        MongoClientEntity clientEntity = new MongoClientEntity();
+        clientEntity.setId(id);
+        clientEntity.setClientId(clientId);
+        clientEntity.setRealmId(getId());
+        clientEntity.setEnabled(true);
+        getMongoStore().insertEntity(clientEntity, invocationContext);
+
+        final ClientModel model = new ClientAdapter(session, this, clientEntity, invocationContext);
         session.getKeycloakSessionFactory().publish(new ClientCreationEvent() {
             @Override
             public ClientModel getCreatedClient() {
@@ -979,14 +979,14 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
 
     @Override
     public ClientModel getMasterAdminClient() {
-        MongoClientEntity appData = getMongoStore().loadEntity(MongoClientEntity.class, realm.getAdminAppId(), invocationContext);
+        MongoClientEntity appData = getMongoStore().loadEntity(MongoClientEntity.class, realm.getMasterAdminClient(), invocationContext);
         return appData != null ? new ClientAdapter(session, this, appData, invocationContext) : null;
     }
 
     @Override
     public void setMasterAdminClient(ClientModel client) {
         String adminAppId = client != null ? client.getId() : null;
-        realm.setAdminAppId(adminAppId);
+        realm.setMasterAdminClient(adminAppId);
         updateRealm();
     }
 
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java
index 874f7d8..1214323 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java
@@ -1,5 +1,7 @@
 package org.keycloak.models.mongo.keycloak.adapters;
 
+import static org.keycloak.models.utils.Pbkdf2PasswordEncoder.getSalt;
+
 import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
@@ -17,6 +19,8 @@ import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -29,7 +33,7 @@ import java.util.Set;
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
 public class UserAdapter extends AbstractMongoAdapter<MongoUserEntity> implements UserModel {
-
+    
     private final MongoUserEntity user;
     private final RealmModel realm;
     private final KeycloakSession session;
@@ -177,31 +181,81 @@ public class UserAdapter extends AbstractMongoAdapter<MongoUserEntity> implement
 
     @Override
     public void updateCredential(UserCredentialModel cred) {
+
+        if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
+            updatePasswordCredential(cred);
+        } else {
+            CredentialEntity credentialEntity = getCredentialEntity(user, cred.getType());
+
+            if (credentialEntity == null) {
+                credentialEntity = setCredentials(user, cred);
+                credentialEntity.setValue(cred.getValue());
+                user.getCredentials().add(credentialEntity);
+            } else {
+                credentialEntity.setValue(cred.getValue());
+            }
+        }
+        getMongoStore().updateEntity(user, invocationContext);
+    }
+
+    private void updatePasswordCredential(UserCredentialModel cred) {
         CredentialEntity credentialEntity = getCredentialEntity(user, cred.getType());
 
         if (credentialEntity == null) {
-            credentialEntity = new CredentialEntity();
-            credentialEntity.setType(cred.getType());
-            credentialEntity.setDevice(cred.getDevice());
+            credentialEntity = setCredentials(user, cred);
+            setValue(credentialEntity, cred);
             user.getCredentials().add(credentialEntity);
-        }
-        if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
-            byte[] salt = Pbkdf2PasswordEncoder.getSalt();
-            int hashIterations = 1;
+        } else {
+
+            int expiredPasswordsPolicyValue = -1;
             PasswordPolicy policy = realm.getPasswordPolicy();
-            if (policy != null) {
-                hashIterations = policy.getHashIterations();
-                if (hashIterations == -1) hashIterations = 1;
+            if(policy != null) {
+                expiredPasswordsPolicyValue = policy.getExpiredPasswords();
+            }
+            
+            if (expiredPasswordsPolicyValue != -1) {
+                user.getCredentials().remove(credentialEntity);
+                credentialEntity.setType(UserCredentialModel.PASSWORD_HISTORY);
+                user.getCredentials().add(credentialEntity);
+
+                List<CredentialEntity> credentialEntities = getCredentialEntities(user, UserCredentialModel.PASSWORD_HISTORY);
+                if (credentialEntities.size() > expiredPasswordsPolicyValue - 1) {
+                    user.getCredentials().removeAll(credentialEntities.subList(expiredPasswordsPolicyValue - 1, credentialEntities.size()));
+                }
+
+                credentialEntity = setCredentials(user, cred);
+                setValue(credentialEntity, cred);
+                user.getCredentials().add(credentialEntity);
+            } else {
+                List<CredentialEntity> credentialEntities = getCredentialEntities(user, UserCredentialModel.PASSWORD_HISTORY);
+                if (credentialEntities != null && credentialEntities.size() > 0) {
+                    user.getCredentials().removeAll(credentialEntities);
+                }
+                setValue(credentialEntity, cred);
             }
-            credentialEntity.setValue(new Pbkdf2PasswordEncoder(salt).encode(cred.getValue(), hashIterations));
-            credentialEntity.setSalt(salt);
-            credentialEntity.setHashIterations(hashIterations);
-        } else {
-            credentialEntity.setValue(cred.getValue());
         }
+    }
+    
+    private CredentialEntity setCredentials(MongoUserEntity user, UserCredentialModel cred) {
+        CredentialEntity credentialEntity = new CredentialEntity();
+        credentialEntity.setType(cred.getType());
+        credentialEntity.setCreatedDate(new Date().getTime());
         credentialEntity.setDevice(cred.getDevice());
+        return credentialEntity;
+    }
 
-        getMongoStore().updateEntity(user, invocationContext);
+    private void setValue(CredentialEntity credentialEntity, UserCredentialModel cred) {
+        byte[] salt = getSalt();
+        int hashIterations = 1;
+        PasswordPolicy policy = realm.getPasswordPolicy();
+        if (policy != null) {
+            hashIterations = policy.getHashIterations();
+            if (hashIterations == -1)
+                hashIterations = 1;
+        }
+        credentialEntity.setValue(new Pbkdf2PasswordEncoder(salt).encode(cred.getValue(), hashIterations));
+        credentialEntity.setSalt(salt);
+        credentialEntity.setHashIterations(hashIterations);
     }
 
     private CredentialEntity getCredentialEntity(MongoUserEntity userEntity, String credType) {
@@ -214,6 +268,30 @@ public class UserAdapter extends AbstractMongoAdapter<MongoUserEntity> implement
         return null;
     }
 
+    private List<CredentialEntity> getCredentialEntities(MongoUserEntity userEntity, String credType) {
+        List<CredentialEntity> credentialEntities = new ArrayList<CredentialEntity>();
+        for (CredentialEntity entity : userEntity.getCredentials()) {
+            if (entity.getType().equals(credType)) {
+                credentialEntities.add(entity);
+            }
+        }
+        
+        // Avoiding direct use of credSecond.getCreatedDate() - credFirst.getCreatedDate() to prevent Integer Overflow
+        // Orders from most recent to least recent
+        Collections.sort(credentialEntities, new Comparator<CredentialEntity>() {
+            public int compare(CredentialEntity credFirst, CredentialEntity credSecond) {
+                if (credFirst.getCreatedDate() > credSecond.getCreatedDate()) {
+                    return -1;
+                } else if (credFirst.getCreatedDate() < credSecond.getCreatedDate()) {
+                    return 1;
+                } else {
+                    return 0;
+                }
+            }
+        });
+        return credentialEntities;
+    }
+
     @Override
     public List<UserCredentialValueModel> getCredentialsDirectly() {
         List<CredentialEntity> credentials = user.getCredentials();
@@ -222,6 +300,7 @@ public class UserAdapter extends AbstractMongoAdapter<MongoUserEntity> implement
             UserCredentialValueModel credModel = new UserCredentialValueModel();
             credModel.setType(credEntity.getType());
             credModel.setDevice(credEntity.getDevice());
+            credModel.setCreatedDate(credEntity.getCreatedDate());
             credModel.setValue(credEntity.getValue());
             credModel.setSalt(credEntity.getSalt());
             credModel.setHashIterations(credEntity.getHashIterations());
@@ -239,6 +318,7 @@ public class UserAdapter extends AbstractMongoAdapter<MongoUserEntity> implement
         if (credentialEntity == null) {
             credentialEntity = new CredentialEntity();
             credentialEntity.setType(credModel.getType());
+            credModel.setCreatedDate(credModel.getCreatedDate());
             user.getCredentials().add(credentialEntity);
         }
 

README.md 107(+70 -37)

diff --git a/README.md b/README.md
index 1d05652..68c2ff0 100755
--- a/README.md
+++ b/README.md
@@ -1,39 +1,72 @@
-keycloak
+Keycloak
 ========
 
-Please visit [http://keycloak.org](http://keycloak.org) for more information on Keycloak including how to download, documentation,
-and video tutorials.  
-
-Keycloak is an SSO Service for web apps and REST services.
-It can be used for social applications as well as enterprise applications.  It is based on OpenID Connect with support for SAML 2.0 as well.
-Here's some of the features:
-
-* SSO and Single Log Out for browser applications
-* Social Broker.  Enable Google, Facebook, Yahoo, Twitter, GitHub, LinkedIn social login with no code required.
-* Optional LDAP/Active Directory integration
-* Optional User Registration
-* Password and TOTP support (via Google Authenticator or FreeOTP).  Client cert auth coming soon.
-* User session management from both admin and user perspective
-* Customizable themes for user facing pages: login, grant pages, account management, emails, and admin console all customizable!
-* OAuth Bearer token auth for REST Services
-* Integrated Browser App to REST Service token propagation
-* Admin REST API
-* OAuth 2.0 Grant requests
-* CORS Support
-* CORS Web Origin management and validation
-* Completely centrally managed user and role mapping metadata.  Minimal configuration at the application side
-* Admin Console for managing users, roles, role mappings, applications, user sessions, allowed CORS web origins, and OAuth clients.
-* Deployable as a WAR, appliance, or an Openshift  cloud service (SaaS).
-* Supports JBoss AS7, EAP 6.x, Wildfly, Tomcat, and Jetty applications.   Plans to support Node.js, RAILS, GRAILS, and other non-Java applications.
-* Javascript/HTML 5 adapter for pure Javascript apps
-* Session management from admin console
-* Revocation policies
-* Password policies
-* OpenID Connect Support
-* SAML Support
-* Token claim and SAML assertion mappings, role name mappings, etc.  Ability to configure exactly what information you want in your tokens and SAML documents
-* IDP brokering or chaining.  You can set up Keycloak to be a child IDP to another SAML or OIDC IDP.
-* Kerberos bridging.  Logged in Kerberos users can access Keycloak SAML or OIDC applications via our Kerberos bridge.
-
-Please visit [http://keycloak.org](http://keycloak.org) for more information on Keycloak including how to download, documentation,
-and video tutorials.
+Keycloak is an SSO Service for web apps and REST services. For more information visit [http://keycloak.org](http://keycloak.org).  
+
+
+Building
+--------
+
+Ensure you have JDK 7 (or newer), Maven 3.2.1 (or newer) and Git installed
+
+    java -version
+    mvn -version
+    git --version
+    
+First clone the Keycloak repository:
+    
+    git clone https://github.com/keycloak/keycloak.git
+    cd keycloak
+    
+To build Keycloak run:
+
+    mvn install
+    
+This will build all modules and run the testsuite. 
+
+To build the distribution run:
+
+    mvn install -Pdistribution
+    
+Once completed you will find distribution archives in `distribution`.
+
+
+Starting Keycloak
+-----------------
+
+To start Keycloak during development first build as specficied above, then run:
+
+    mvn -f testsuite/integration/pom.xml exec:java -Pkeycloak-server 
+
+
+To start Keycloak from the appliance distribution first build the distribution it as specified above, then run:
+
+    tar xfz distribution/appliance-dist/target/keycloak-appliance-dist-all-<VERSION>.tar.gz
+    cd keycloak-appliance-dist-all-<VERSION>/keycloak
+    bin/standalone.sh
+    
+To stop the server press `Ctrl + C`.
+
+
+Contributing
+------------
+
+* See [Hacking on Keycloak](misc/HackingOnKeycloak.md)
+
+
+Documentation
+-------------
+
+* [User Guide, Admin REST API and Javadocs](http://keycloak.jboss.org/docs)
+* Developer documentation
+    * [Hacking on Keycloak](misc/HackingOnKeycloak.md) - how to become a Keycloak contributor
+    * [Testsuite](misc/Testsuite.md) - details about testsuite, but also how to quickly run Keycloak during development and a few test tools (OTP generation, LDAP server, Mail server)
+    * [Database Testing](misc/DatabaseTesting.md) - how to do testing of Keycloak on different databases
+    * [Updating Database](misc/UpdatingDatabaseSchema.md) - how to change the Keycloak database
+    * [Release Process](misc/ReleaseProcess.md) - how to release Keycloak
+
+
+License
+-------
+
+* [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0)
\ No newline at end of file
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2LoginResponseBuilder.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2LoginResponseBuilder.java
index bc0bb26..0b7dfb4 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2LoginResponseBuilder.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2LoginResponseBuilder.java
@@ -17,8 +17,10 @@ import org.keycloak.dom.saml.v2.assertion.AssertionType;
 import org.keycloak.dom.saml.v2.assertion.AuthnStatementType;
 import org.keycloak.dom.saml.v2.assertion.ConditionsType;
 import org.keycloak.dom.saml.v2.assertion.SubjectConfirmationDataType;
+import org.keycloak.dom.saml.v2.assertion.AudienceRestrictionType;
 import org.keycloak.dom.saml.v2.protocol.ResponseType;
 import org.w3c.dom.Document;
+import java.net.URI;
 
 import static org.keycloak.saml.common.util.StringUtil.isNotNull;
 
@@ -156,6 +158,11 @@ public class SAML2LoginResponseBuilder {
 
         AssertionType assertion = responseType.getAssertions().get(0).getAssertion();
 
+        //Add request issuer as the audience restriction
+        AudienceRestrictionType audience = new AudienceRestrictionType();
+        audience.addAudience(URI.create(requestIssuer));
+        assertion.getConditions().addCondition(audience);
+
         //Update Conditions NotOnOrAfter
         if(assertionExpiration > 0) {
             ConditionsType conditions = assertion.getConditions();
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
index b38d5b9..747c43c 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
@@ -30,7 +30,7 @@ import org.keycloak.services.managers.ResourceAdminManager;
 import org.keycloak.services.messages.Messages;
 import org.keycloak.services.resources.RealmsResource;
 import org.keycloak.services.resources.admin.ClientAttributeCertificateResource;
-import org.keycloak.services.resources.flows.Flows;
+import org.keycloak.services.ErrorPage;
 import org.w3c.dom.Document;
 
 import javax.ws.rs.core.HttpHeaders;
@@ -152,7 +152,7 @@ public class SamlProtocol implements LoginProtocol {
               return builder.redirectBinding().response();
           }
         } catch (Exception e) {
-            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.FAILED_TO_PROCESS_RESPONSE );
+            return ErrorPage.error(session, Messages.FAILED_TO_PROCESS_RESPONSE);
         }
     }
 
@@ -309,7 +309,7 @@ public class SamlProtocol implements LoginProtocol {
             samlDocument = builder.buildDocument(samlModel);
         } catch (Exception e) {
             logger.error("failed", e);
-            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo,headers, Messages.FAILED_TO_PROCESS_RESPONSE);
+            return ErrorPage.error(session, Messages.FAILED_TO_PROCESS_RESPONSE);
         }
 
         SAML2BindingBuilder2 bindingBuilder = new SAML2BindingBuilder2();
@@ -331,7 +331,7 @@ public class SamlProtocol implements LoginProtocol {
                 publicKey = SamlProtocolUtils.getEncryptionValidationKey(client);
             } catch (Exception e) {
                 logger.error("failed", e);
-                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.FAILED_TO_PROCESS_RESPONSE);
+                return ErrorPage.error(session, Messages.FAILED_TO_PROCESS_RESPONSE);
             }
             bindingBuilder.encrypt(publicKey);
         }
@@ -343,7 +343,7 @@ public class SamlProtocol implements LoginProtocol {
             }
         } catch (Exception e) {
             logger.error("failed", e);
-            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.FAILED_TO_PROCESS_RESPONSE );
+            return ErrorPage.error(session, Messages.FAILED_TO_PROCESS_RESPONSE);
         }
     }
 
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java
index 3b1ff99..ff936e5 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java
@@ -23,7 +23,7 @@ import org.keycloak.services.managers.ClientSessionCode;
 import org.keycloak.services.managers.HttpAuthenticationManager;
 import org.keycloak.services.messages.Messages;
 import org.keycloak.services.resources.RealmsResource;
-import org.keycloak.services.resources.flows.Flows;
+import org.keycloak.services.ErrorPage;
 import org.keycloak.util.StreamUtil;
 import org.keycloak.saml.common.constants.GeneralConstants;
 import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
@@ -102,18 +102,18 @@ public class SamlService {
             if (!checkSsl()) {
                 event.event(EventType.LOGIN);
                 event.error(Errors.SSL_REQUIRED);
-                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.HTTPS_REQUIRED );
+                return ErrorPage.error(session, Messages.HTTPS_REQUIRED);
             }
             if (!realm.isEnabled()) {
                 event.event(EventType.LOGIN_ERROR);
                 event.error(Errors.REALM_DISABLED);
-                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.REALM_NOT_ENABLED);
+                return ErrorPage.error(session, Messages.REALM_NOT_ENABLED);
             }
 
             if (samlRequest == null && samlResponse == null) {
                 event.event(EventType.LOGIN);
                 event.error(Errors.INVALID_TOKEN);
-                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST );
+                return ErrorPage.error(session, Messages.INVALID_REQUEST);
 
             }
             return null;
@@ -127,7 +127,7 @@ public class SamlService {
             if (!uriInfo.getAbsolutePath().toString().equals(statusResponse.getDestination())) {
                 event.error(Errors.INVALID_SAML_LOGOUT_RESPONSE);
                 event.detail(Details.REASON, "invalid_destination");
-                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST);
+                return ErrorPage.error(session, Messages.INVALID_REQUEST);
             }
 
             AuthenticationManager.AuthResult authResult = authManager.authenticateIdentityCookie(session, realm, uriInfo, clientConnection, headers, false);
@@ -135,7 +135,7 @@ public class SamlService {
                 logger.warn("Unknown saml response.");
                 event.event(EventType.LOGOUT);
                 event.error(Errors.INVALID_TOKEN);
-                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST);
+                return ErrorPage.error(session, Messages.INVALID_REQUEST);
             }
             // assume this is a logout response
             UserSessionModel userSession = authResult.getSession();
@@ -144,7 +144,7 @@ public class SamlService {
                 logger.warn("UserSession is not tagged as logging out.");
                 event.event(EventType.LOGOUT);
                 event.error(Errors.INVALID_SAML_LOGOUT_RESPONSE);
-                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST);
+                return ErrorPage.error(session, Messages.INVALID_REQUEST);
             }
             logger.debug("logout response");
             Response response = authManager.browserLogout(session, realm, userSession, uriInfo, clientConnection, headers);
@@ -157,7 +157,7 @@ public class SamlService {
             if (documentHolder == null) {
                 event.event(EventType.LOGIN);
                 event.error(Errors.INVALID_TOKEN);
-                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST);
+                return ErrorPage.error(session, Messages.INVALID_REQUEST);
             }
 
             SAML2Object samlObject = documentHolder.getSamlObject();
@@ -169,32 +169,34 @@ public class SamlService {
             if (client == null) {
                 event.event(EventType.LOGIN);
                 event.error(Errors.CLIENT_NOT_FOUND);
-                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.UNKNOWN_LOGIN_REQUESTER);
+                return ErrorPage.error(session, Messages.UNKNOWN_LOGIN_REQUESTER);
             }
 
             if (!client.isEnabled()) {
                 event.event(EventType.LOGIN);
                 event.error(Errors.CLIENT_DISABLED);
-                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.LOGIN_REQUESTER_NOT_ENABLED);
+                return ErrorPage.error(session, Messages.LOGIN_REQUESTER_NOT_ENABLED);
             }
             if ((client instanceof ClientModel) && ((ClientModel)client).isBearerOnly()) {
                 event.event(EventType.LOGIN);
                 event.error(Errors.NOT_ALLOWED);
-                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.BEARER_ONLY);
+                return ErrorPage.error(session, Messages.BEARER_ONLY);
             }
             if (client.isDirectGrantsOnly()) {
                 event.event(EventType.LOGIN);
                 event.error(Errors.NOT_ALLOWED);
-                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.DIRECT_GRANTS_ONLY );
+                return ErrorPage.error(session, Messages.DIRECT_GRANTS_ONLY);
             }
 
+            session.getContext().setClient(client);
+
             try {
                 verifySignature(documentHolder, client);
             } catch (VerificationException e) {
                 SamlService.logger.error("request validation failed", e);
                 event.event(EventType.LOGIN);
                 event.error(Errors.INVALID_SIGNATURE);
-                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUESTER);
+                return ErrorPage.error(session, Messages.INVALID_REQUESTER);
             }
             logger.debug("verified request");
             if (samlObject instanceof AuthnRequestType) {
@@ -212,7 +214,7 @@ public class SamlService {
             } else {
                 event.event(EventType.LOGIN);
                 event.error(Errors.INVALID_TOKEN);
-                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST);
+                return ErrorPage.error(session, Messages.INVALID_REQUEST);
             }
         }
 
@@ -226,7 +228,7 @@ public class SamlService {
             if (!uriInfo.getAbsolutePath().equals(requestAbstractType.getDestination())) {
                 event.error(Errors.INVALID_SAML_AUTHN_REQUEST);
                 event.detail(Details.REASON, "invalid_destination");
-                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST);
+                return ErrorPage.error(session, Messages.INVALID_REQUEST);
             }
             String bindingType = getBindingType(requestAbstractType);
             if ("true".equals(client.getAttribute(SamlProtocol.SAML_FORCE_POST_BINDING))) bindingType = SamlProtocol.SAML_POST_BINDING;
@@ -248,7 +250,7 @@ public class SamlService {
 
             if (redirect == null) {
                 event.error(Errors.INVALID_REDIRECT_URI);
-                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REDIRECT_URI );
+                return ErrorPage.error(session, Messages.INVALID_REDIRECT_URI);
             }
 
 
@@ -271,7 +273,7 @@ public class SamlService {
                 } else {
                     event.error(Errors.INVALID_SAML_AUTHN_REQUEST);
                     event.detail(Details.REASON, "unsupported_nameid_format");
-                    return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.UNSUPPORTED_NAME_ID_FORMAT);
+                    return ErrorPage.error(session, Messages.UNSUPPORTED_NAME_ID_FORMAT);
                 }
             }
 
@@ -283,7 +285,7 @@ public class SamlService {
             HttpAuthenticationManager.HttpAuthOutput httpAuthOutput = httpAuthManager.spnegoAuthenticate(headers);
             if (httpAuthOutput.getResponse() != null) return httpAuthOutput.getResponse();
 
-            LoginFormsProvider forms = Flows.forms(session, realm, clientSession.getClient(), uriInfo, headers)
+            LoginFormsProvider forms = session.getProvider(LoginFormsProvider.class)
                     .setClientSessionCode(new ClientSessionCode(realm, clientSession).getCode());
 
             // Attach state from SPNEGO authentication
@@ -335,7 +337,7 @@ public class SamlService {
             if (!uriInfo.getAbsolutePath().equals(logoutRequest.getDestination())) {
                 event.error(Errors.INVALID_SAML_LOGOUT_REQUEST);
                 event.detail(Details.REASON, "invalid_destination");
-                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST);
+                return ErrorPage.error(session, Messages.INVALID_REQUEST);
             }
 
             // authenticate identity cookie, but ignore an access token timeout as we're logging out anyways.
@@ -374,7 +376,7 @@ public class SamlService {
             if (redirectUri != null) {
                 redirectUri = RedirectUtils.verifyRedirectUri(uriInfo, redirectUri, realm, client);
                 if (redirectUri == null) {
-                    return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REDIRECT_URI );
+                    return ErrorPage.error(session, Messages.INVALID_REDIRECT_URI);
                 }
             }
             if (redirectUri != null) {
diff --git a/server/src/main/resources/META-INF/keycloak-server.json b/server/src/main/resources/META-INF/keycloak-server.json
index 1f92932..1d7b17f 100755
--- a/server/src/main/resources/META-INF/keycloak-server.json
+++ b/server/src/main/resources/META-INF/keycloak-server.json
@@ -14,6 +14,13 @@
         }
     },
 
+    "eventsListener": {
+        "jboss-logging" : {
+            "success-level": "debug",
+            "error-level": "warn"
+        }
+    },
+
     "realm": {
         "provider": "jpa"
     },
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 e33cb67..8306b7a 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
@@ -25,8 +25,7 @@ import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.managers.ClientSessionCode;
 import org.keycloak.services.managers.HttpAuthenticationManager;
 import org.keycloak.services.messages.Messages;
-import org.keycloak.services.resources.flows.Flows;
-import org.keycloak.services.resources.flows.Urls;
+import org.keycloak.services.Urls;
 
 import javax.ws.rs.GET;
 import javax.ws.rs.core.Context;
@@ -116,7 +115,7 @@ public class AuthorizationEndpoint {
         action = Action.REGISTER;
 
         if (!realm.isRegistrationAllowed()) {
-            throw new ErrorPageException(session, realm, uriInfo, headers, Messages.REGISTRATION_NOT_ALLOWED);
+            throw new ErrorPageException(session, Messages.REGISTRATION_NOT_ALLOWED);
         }
 
         return this;
@@ -148,21 +147,21 @@ public class AuthorizationEndpoint {
     private void checkSsl() {
         if (!uriInfo.getBaseUri().getScheme().equals("https") && realm.getSslRequired().isRequired(clientConnection)) {
             event.error(Errors.SSL_REQUIRED);
-            throw new ErrorPageException(session, realm, uriInfo, headers, Messages.HTTPS_REQUIRED);
+            throw new ErrorPageException(session, Messages.HTTPS_REQUIRED);
         }
     }
 
     private void checkRealm() {
         if (!realm.isEnabled()) {
             event.error(Errors.REALM_DISABLED);
-            throw new ErrorPageException(session, realm, uriInfo, headers, Messages.REALM_NOT_ENABLED);
+            throw new ErrorPageException(session, Messages.REALM_NOT_ENABLED);
         }
     }
 
     private void checkClient() {
         if (clientId == null) {
             event.error(Errors.INVALID_REQUEST);
-            throw new ErrorPageException(session, realm, uriInfo, headers, Messages.MISSING_PARAMETER, OIDCLoginProtocol.CLIENT_ID_PARAM );
+            throw new ErrorPageException(session, Messages.MISSING_PARAMETER, OIDCLoginProtocol.CLIENT_ID_PARAM );
         }
 
         event.client(clientId);
@@ -170,18 +169,20 @@ public class AuthorizationEndpoint {
         client = realm.getClientByClientId(clientId);
         if (client == null) {
             event.error(Errors.CLIENT_NOT_FOUND);
-            throw new ErrorPageException(session, realm, uriInfo, headers, Messages.CLIENT_NOT_FOUND );
+            throw new ErrorPageException(session, Messages.CLIENT_NOT_FOUND );
         }
 
         if ((client instanceof ClientModel) && ((ClientModel) client).isBearerOnly()) {
             event.error(Errors.NOT_ALLOWED);
-            throw new ErrorPageException(session, realm, uriInfo, headers, Messages.BEARER_ONLY );
+            throw new ErrorPageException(session, Messages.BEARER_ONLY );
         }
 
         if (client.isDirectGrantsOnly()) {
             event.error(Errors.NOT_ALLOWED);
-            throw new ErrorPageException(session, realm, uriInfo, headers, Messages.DIRECT_GRANTS_ONLY);
+            throw new ErrorPageException(session, Messages.DIRECT_GRANTS_ONLY);
         }
+
+        session.getContext().setClient(client);
     }
 
     private void checkResponseType() {
@@ -190,7 +191,7 @@ public class AuthorizationEndpoint {
                 responseType = legacyResponseType;
             } else {
                 event.error(Errors.INVALID_REQUEST);
-                throw new ErrorPageException(session, realm, uriInfo, headers, Messages.MISSING_PARAMETER, OIDCLoginProtocol.RESPONSE_TYPE_PARAM );
+                throw new ErrorPageException(session, Messages.MISSING_PARAMETER, OIDCLoginProtocol.RESPONSE_TYPE_PARAM );
             }
         }
 
@@ -200,7 +201,7 @@ public class AuthorizationEndpoint {
             action = Action.CODE;
         } else {
             event.error(Errors.INVALID_REQUEST);
-            throw new ErrorPageException(session, realm, uriInfo, headers, Messages.INVALID_PARAMETER, OIDCLoginProtocol.RESPONSE_TYPE_PARAM );
+            throw new ErrorPageException(session, Messages.INVALID_PARAMETER, OIDCLoginProtocol.RESPONSE_TYPE_PARAM );
         }
     }
 
@@ -210,7 +211,7 @@ public class AuthorizationEndpoint {
         redirectUri = RedirectUtils.verifyRedirectUri(uriInfo, redirectUriParam, realm, client);
         if (redirectUri == null) {
             event.error(Errors.INVALID_REDIRECT_URI);
-            throw new ErrorPageException(session, realm, uriInfo, headers, Messages.INVALID_PARAMETER, OIDCLoginProtocol.REDIRECT_URI_PARAM);
+            throw new ErrorPageException(session, Messages.INVALID_PARAMETER, OIDCLoginProtocol.REDIRECT_URI_PARAM);
         }
     }
 
@@ -238,7 +239,7 @@ public class AuthorizationEndpoint {
             IdentityProviderModel identityProviderModel = realm.getIdentityProviderByAlias(idpHint);
 
             if (identityProviderModel == null) {
-                return Flows.forms(session, realm, null, uriInfo, headers)
+                return session.getProvider(LoginFormsProvider.class)
                         .setError(Messages.IDENTITY_PROVIDER_NOT_FOUND, idpHint)
                         .createErrorPage();
             }
@@ -272,14 +273,13 @@ public class AuthorizationEndpoint {
                     return buildRedirectToIdentityProvider(identityProviders.get(0).getAlias(), accessCode);
                 }
 
-                return Flows.forms(session, realm, null, uriInfo, headers).setError(Messages.IDENTITY_PROVIDER_NOT_UNIQUE, realm.getName()).createErrorPage();
+                return session.getProvider(LoginFormsProvider.class).setError(Messages.IDENTITY_PROVIDER_NOT_UNIQUE, realm.getName()).createErrorPage();
             }
 
-            return Flows.forms(session, realm, null, uriInfo, headers).setError(Messages.REALM_SUPPORTS_NO_CREDENTIALS, realm.getName()).createErrorPage();
+            return session.getProvider(LoginFormsProvider.class).setError(Messages.REALM_SUPPORTS_NO_CREDENTIALS, realm.getName()).createErrorPage();
         }
 
-        LoginFormsProvider forms = Flows.forms(session, realm, clientSession.getClient(), uriInfo, headers)
-                .setClientSessionCode(accessCode);
+        LoginFormsProvider forms = session.getProvider(LoginFormsProvider.class).setClientSessionCode(accessCode);
 
         // Attach state from SPNEGO authentication
         if (httpAuthOutput.getChallenge() != null) {
@@ -307,7 +307,7 @@ public class AuthorizationEndpoint {
     private Response buildRegister() {
         authManager.expireIdentityCookie(realm, uriInfo, clientConnection);
 
-        return Flows.forms(session, realm, client, uriInfo, headers)
+        return session.getProvider(LoginFormsProvider.class)
                 .setClientSessionCode(new ClientSessionCode(realm, clientSession).getCode())
                 .createRegistration();
     }
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 5bab0e0..31cdca3 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
@@ -24,7 +24,7 @@ import org.keycloak.services.ErrorResponseException;
 import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.messages.Messages;
 import org.keycloak.services.resources.Cors;
-import org.keycloak.services.resources.flows.Flows;
+import org.keycloak.services.ErrorPage;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.GET;
@@ -92,7 +92,7 @@ public class LogoutEndpoint {
                 event.event(EventType.LOGOUT);
                 event.detail(Details.REDIRECT_URI, redirect);
                 event.error(Errors.INVALID_REDIRECT_URI);
-                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REDIRECT_URI);
+                return ErrorPage.error(session, Messages.INVALID_REDIRECT_URI);
             }
             redirect = validatedUri;
         }
@@ -112,7 +112,7 @@ public class LogoutEndpoint {
             if (error) {
                 event.event(EventType.LOGOUT);
                 event.error(Errors.INVALID_TOKEN);
-                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.SESSION_NOT_ACTIVE);
+                return ErrorPage.error(session, Messages.SESSION_NOT_ACTIVE);
             }
         }
 
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 60b1b98..43538ee 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
@@ -27,7 +27,7 @@ import org.keycloak.services.ErrorResponseException;
 import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.managers.ClientSessionCode;
 import org.keycloak.services.resources.Cors;
-import org.keycloak.services.resources.flows.Urls;
+import org.keycloak.services.Urls;
 
 import javax.ws.rs.OPTIONS;
 import javax.ws.rs.POST;
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java
index ba2f99c..f337163 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java
@@ -36,7 +36,7 @@ import org.keycloak.representations.AccessToken;
 import org.keycloak.services.ErrorResponseException;
 import org.keycloak.services.managers.AppAuthManager;
 import org.keycloak.services.resources.Cors;
-import org.keycloak.services.resources.flows.Urls;
+import org.keycloak.services.Urls;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.FormParam;
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/ValidateTokenEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/ValidateTokenEndpoint.java
index 249aa30..1e074b8 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/ValidateTokenEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/ValidateTokenEndpoint.java
@@ -15,10 +15,9 @@ import org.keycloak.models.RealmModel;
 import org.keycloak.protocol.oidc.TokenManager;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.services.ErrorResponseException;
-import org.keycloak.services.resources.flows.Urls;
+import org.keycloak.services.Urls;
 
 import javax.ws.rs.GET;
-import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.*;
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java
index 6c327b1..666586f 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java
@@ -18,7 +18,6 @@ import org.keycloak.protocol.oidc.endpoints.ValidateTokenEndpoint;
 import org.keycloak.protocol.oidc.representations.JSONWebKeySet;
 import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.resources.RealmsResource;
-import org.keycloak.services.resources.flows.Flows;
 
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
@@ -204,7 +203,7 @@ public class OIDCLoginProtocolService {
     @Path("oauth/oob")
     @GET
     public Response installedAppUrnCallback(final @QueryParam("code") String code, final @QueryParam("error") String error, final @QueryParam("error_description") String errorDescription) {
-        LoginFormsProvider forms = Flows.forms(session, realm, null, uriInfo, headers);
+        LoginFormsProvider forms = session.getProvider(LoginFormsProvider.class);
         if (code != null) {
             return forms.setClientSessionCode(code).createCode();
         } else {
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java
index 6cf2d50..80aee4a 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java
@@ -1,18 +1,17 @@
 package org.keycloak.protocol.oidc;
 
 import org.keycloak.OAuth2Constants;
+import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation;
 import org.keycloak.services.resources.RealmsResource;
-import org.keycloak.services.resources.flows.Urls;
+import org.keycloak.services.Urls;
 import org.keycloak.wellknown.WellKnownProvider;
 
 import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
-import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Set;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@@ -29,8 +28,17 @@ public class OIDCWellKnownProvider implements WellKnownProvider {
 
     public static final List<String> DEFAULT_RESPONSE_MODES_SUPPORTED = list("query");
 
+    private KeycloakSession session;
+
+    public OIDCWellKnownProvider(KeycloakSession session) {
+        this.session = session;
+    }
+
     @Override
-    public Object getConfig(RealmModel realm, UriInfo uriInfo) {
+    public Object getConfig() {
+        UriInfo uriInfo = session.getContext().getUri();
+        RealmModel realm = session.getContext().getRealm();
+
         UriBuilder uriBuilder = RealmsResource.protocolUrl(uriInfo);
 
         OIDCConfigurationRepresentation config = new OIDCConfigurationRepresentation();
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProviderFactory.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProviderFactory.java
index e49a993..cf913f1 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProviderFactory.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProviderFactory.java
@@ -11,16 +11,13 @@ import org.keycloak.wellknown.WellKnownProviderFactory;
  */
 public class OIDCWellKnownProviderFactory implements WellKnownProviderFactory {
 
-    private WellKnownProvider provider;
-
     @Override
     public WellKnownProvider create(KeycloakSession session) {
-        return provider;
+        return new OIDCWellKnownProvider(session);
     }
 
     @Override
     public void init(Config.Scope config) {
-        provider = new OIDCWellKnownProvider();
     }
 
     @Override
@@ -29,7 +26,6 @@ public class OIDCWellKnownProviderFactory implements WellKnownProviderFactory {
 
     @Override
     public void close() {
-        provider = null;
     }
 
     @Override
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/utils/RedirectUtils.java b/services/src/main/java/org/keycloak/protocol/oidc/utils/RedirectUtils.java
index 68da825..df650f6 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/utils/RedirectUtils.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/utils/RedirectUtils.java
@@ -4,7 +4,7 @@ import org.jboss.logging.Logger;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.Constants;
 import org.keycloak.models.RealmModel;
-import org.keycloak.services.resources.flows.Urls;
+import org.keycloak.services.Urls;
 
 import javax.ws.rs.core.UriInfo;
 import java.net.URI;
diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java
new file mode 100644
index 0000000..1b0e567
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java
@@ -0,0 +1,50 @@
+package org.keycloak.services;
+
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakContext;
+import org.keycloak.models.RealmModel;
+
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.UriInfo;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class DefaultKeycloakContext implements KeycloakContext {
+
+    private RealmModel realm;
+
+    private ClientModel client;
+
+    @Override
+    public UriInfo getUri() {
+        return ResteasyProviderFactory.getContextData(UriInfo.class);
+    }
+
+    @Override
+    public HttpHeaders getRequestHeaders() {
+        return ResteasyProviderFactory.getContextData(HttpHeaders.class);
+    }
+
+    @Override
+    public RealmModel getRealm() {
+        return realm;
+    }
+
+    @Override
+    public void setRealm(RealmModel realm) {
+        this.realm = realm;
+    }
+
+    @Override
+    public ClientModel getClient() {
+        return client;
+    }
+
+    @Override
+    public void setClient(ClientModel client) {
+        this.client = client;
+    }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java
index e83f84d..190750f 100755
--- a/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java
+++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java
@@ -1,5 +1,8 @@
 package org.keycloak.services;
 
+import org.jboss.resteasy.spi.HttpRequest;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.keycloak.models.KeycloakContext;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
 import org.keycloak.models.KeycloakTransactionManager;
@@ -12,6 +15,7 @@ import org.keycloak.models.cache.CacheUserProvider;
 import org.keycloak.provider.Provider;
 import org.keycloak.provider.ProviderFactory;
 
+import javax.ws.rs.core.UriInfo;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
@@ -32,11 +36,18 @@ public class DefaultKeycloakSession implements KeycloakSession {
     private UserProvider userModel;
     private UserSessionProvider sessionProvider;
     private UserFederationManager federationManager;
+    private KeycloakContext context;
 
     public DefaultKeycloakSession(DefaultKeycloakSessionFactory factory) {
         this.factory = factory;
         this.transactionManager = new DefaultKeycloakTransactionManager();
         federationManager = new UserFederationManager(this);
+        context = new DefaultKeycloakContext();
+    }
+
+    @Override
+    public KeycloakContext getContext() {
+        return context;
     }
 
     private RealmProvider getRealmProvider() {
diff --git a/services/src/main/java/org/keycloak/services/ErrorPageException.java b/services/src/main/java/org/keycloak/services/ErrorPageException.java
index 634ea7a..1b9d070 100644
--- a/services/src/main/java/org/keycloak/services/ErrorPageException.java
+++ b/services/src/main/java/org/keycloak/services/ErrorPageException.java
@@ -1,13 +1,9 @@
 package org.keycloak.services;
 
 import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.RealmModel;
-import org.keycloak.services.resources.flows.Flows;
 
 import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.Response;
-import javax.ws.rs.core.UriInfo;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@@ -15,24 +11,18 @@ import javax.ws.rs.core.UriInfo;
 public class ErrorPageException extends WebApplicationException {
 
     private final KeycloakSession session;
-    private final RealmModel realm;
-    private final UriInfo uriInfo;
-    private final HttpHeaders httpHeaders;
     private final String errorMessage;
     private final Object[] parameters;
 
-    public ErrorPageException(KeycloakSession session, RealmModel realm, UriInfo uriInfo, HttpHeaders headers, String errorMessage, Object ... parameters) {
+    public ErrorPageException(KeycloakSession session, String errorMessage, Object... parameters) {
         this.session = session;
-        this.realm = realm;
-        this.uriInfo = uriInfo;
-        this.httpHeaders = headers;
         this.errorMessage = errorMessage;
         this.parameters = parameters;
     }
 
     @Override
     public Response getResponse() {
-        return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, httpHeaders, errorMessage, parameters);
+        return ErrorPage.error(session, errorMessage, parameters);
     }
 
 }
diff --git a/services/src/main/java/org/keycloak/services/ErrorResponseException.java b/services/src/main/java/org/keycloak/services/ErrorResponseException.java
index 0dd3e13..6216d58 100644
--- a/services/src/main/java/org/keycloak/services/ErrorResponseException.java
+++ b/services/src/main/java/org/keycloak/services/ErrorResponseException.java
@@ -1,14 +1,10 @@
 package org.keycloak.services;
 
 import org.keycloak.OAuth2Constants;
-import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.RealmModel;
-import org.keycloak.services.resources.flows.Flows;
 
 import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
-import javax.ws.rs.core.UriInfo;
 import java.util.HashMap;
 import java.util.Map;
 
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 8cd9483..bd512df 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -30,8 +30,7 @@ import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.services.resources.IdentityBrokerService;
 import org.keycloak.services.resources.LoginActionsService;
 import org.keycloak.services.resources.RealmsResource;
-import org.keycloak.services.resources.flows.Flows;
-import org.keycloak.services.resources.flows.Urls;
+import org.keycloak.services.Urls;
 import org.keycloak.services.util.CookieHelper;
 import org.keycloak.services.validation.Validation;
 import org.keycloak.util.Time;
@@ -401,7 +400,7 @@ public class AuthenticationManager {
             if (action != null) {
                 accessCode.setRequiredAction(action);
 
-                LoginFormsProvider loginFormsProvider = Flows.forms(session, realm, client, uriInfo, request.getHttpHeaders()).setClientSessionCode(accessCode.getCode())
+                LoginFormsProvider loginFormsProvider = session.getProvider(LoginFormsProvider.class).setClientSessionCode(accessCode.getCode())
                         .setUser(user);
                 if (action.equals(UserModel.RequiredAction.VERIFY_EMAIL)) {
                     event.clone().event(EventType.SEND_VERIFY_EMAIL).detail(Details.EMAIL, user.getEmail()).success();
@@ -425,10 +424,9 @@ public class AuthenticationManager {
                 }
             }
 
-            return Flows.forms(session, realm, client, uriInfo, request.getHttpHeaders())
+            return session.getProvider(LoginFormsProvider.class)
                     .setClientSessionCode(accessCode.getCode())
                     .setAccessRequest(realmRoles, resourceRoles)
-                    .setClient(client)
                     .createOAuthGrant(clientSession);
         }
 
diff --git a/services/src/main/java/org/keycloak/services/managers/HttpAuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/HttpAuthenticationManager.java
index 1cca55c..0deb28f 100755
--- a/services/src/main/java/org/keycloak/services/managers/HttpAuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/HttpAuthenticationManager.java
@@ -25,7 +25,7 @@ import org.keycloak.constants.KerberosConstants;
 import org.keycloak.protocol.oidc.TokenManager;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.services.messages.Messages;
-import org.keycloak.services.resources.flows.Flows;
+import org.keycloak.services.ErrorPage;
 
 /**
  * Handle HTTP authentication types requiring complex handshakes with multiple HTTP request/responses
@@ -114,7 +114,7 @@ public class HttpAuthenticationManager {
         Response response;
         if (!user.isEnabled()) {
             event.error(Errors.USER_DISABLED);
-            response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.ACCOUNT_DISABLED);
+            response = ErrorPage.error(session, Messages.ACCOUNT_DISABLED);
         } else {
             UserSessionModel userSession = session.sessions().createUserSession(realm, user, user.getUsername(), clientConnection.getRemoteAddr(), authMethod, false, null, null);
 
diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
index 53b460a..58f11e9 100755
--- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
@@ -132,6 +132,8 @@ public class RealmManager {
         realm.setMaxDeltaTimeSeconds(60 * 60 * 12); // 12 hours
         realm.setFailureFactor(30);
         realm.setSslRequired(SslRequired.EXTERNAL);
+
+        realm.setEventsListeners(Collections.singleton("jboss-logging"));
     }
 
     public boolean removeRealm(RealmModel realm) {
diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java
index 71fb570..3fb84c6 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -24,7 +24,9 @@ package org.keycloak.services.resources;
 import org.jboss.logging.Logger;
 import org.jboss.resteasy.spi.BadRequestException;
 import org.jboss.resteasy.spi.HttpRequest;
+import org.keycloak.AbstractOAuthClient;
 import org.keycloak.ClientConnection;
+import org.keycloak.OAuth2Constants;
 import org.keycloak.account.AccountPages;
 import org.keycloak.account.AccountProvider;
 import org.keycloak.events.Details;
@@ -32,6 +34,7 @@ import org.keycloak.events.Event;
 import org.keycloak.events.EventBuilder;
 import org.keycloak.events.EventStoreProvider;
 import org.keycloak.events.EventType;
+import org.keycloak.login.LoginFormsProvider;
 import org.keycloak.models.*;
 import org.keycloak.models.utils.FormMessage;
 import org.keycloak.models.utils.ModelToRepresentation;
@@ -47,9 +50,7 @@ import org.keycloak.services.managers.Auth;
 import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.managers.ClientSessionCode;
 import org.keycloak.services.messages.Messages;
-import org.keycloak.services.resources.flows.Flows;
-import org.keycloak.services.resources.flows.OAuthRedirect;
-import org.keycloak.services.resources.flows.Urls;
+import org.keycloak.services.Urls;
 import org.keycloak.services.util.CookieHelper;
 import org.keycloak.services.util.ResolveRelative;
 import org.keycloak.services.validation.Validation;
@@ -66,6 +67,7 @@ import javax.ws.rs.core.Cookie;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.NewCookie;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
@@ -230,7 +232,7 @@ public class AccountService {
             try {
                 require(AccountRoles.MANAGE_ACCOUNT);
             } catch (ForbiddenException e) {
-                return Flows.forms(session, realm, null, uriInfo, headers).setError(Messages.NO_ACCESS).createErrorPage();
+                return session.getProvider(LoginFormsProvider.class).setError(Messages.NO_ACCESS).createErrorPage();
             }
 
             setReferrerOnPage();
@@ -873,4 +875,42 @@ public class AccountService {
         }
     }
 
+    class OAuthRedirect extends AbstractOAuthClient {
+
+        /**
+         * closes client
+         */
+        public void stop() {
+        }
+
+        public Response redirect(UriInfo uriInfo, String redirectUri) {
+            String state = getStateCode();
+
+            UriBuilder uriBuilder = UriBuilder.fromUri(authUrl)
+                    .queryParam(OAuth2Constants.CLIENT_ID, clientId)
+                    .queryParam(OAuth2Constants.REDIRECT_URI, redirectUri)
+                    .queryParam(OAuth2Constants.STATE, state)
+                    .queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE);
+            if (scope != null) {
+                uriBuilder.queryParam(OAuth2Constants.SCOPE, scope);
+            }
+
+            URI url = uriBuilder.build();
+
+            // todo httpOnly!
+            NewCookie cookie = new NewCookie(getStateCookieName(), state, getStateCookiePath(uriInfo), null, null, -1, isSecure);
+            logger.debug("NewCookie: " + cookie.toString());
+            logger.debug("Oauth Redirect to: " + url);
+            return Response.status(302)
+                    .location(url)
+                    .cookie(cookie).build();
+        }
+
+        private String getStateCookiePath(UriInfo uriInfo) {
+            if (stateCookiePath != null) return stateCookiePath;
+            return uriInfo.getBaseUri().getRawPath();
+        }
+
+    }
+
 }
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java b/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java
index 24d76b2..e8c5447 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java
@@ -26,7 +26,7 @@ import org.keycloak.services.managers.ClientManager;
 import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.services.resources.KeycloakApplication;
-import org.keycloak.services.resources.flows.Urls;
+import org.keycloak.services.Urls;
 
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java
index f0decf3..98eb743 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java
@@ -23,7 +23,7 @@ import org.keycloak.services.managers.ClientManager;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.services.managers.ResourceAdminManager;
 import org.keycloak.services.resources.KeycloakApplication;
-import org.keycloak.services.resources.flows.Flows;
+import org.keycloak.services.ErrorResponse;
 import org.keycloak.util.JsonSerialization;
 import org.keycloak.util.Time;
 
@@ -100,7 +100,7 @@ public class ClientResource {
             RepresentationToModel.updateClient(rep, client);
             return Response.noContent().build();
         } catch (ModelDuplicateException e) {
-            return Flows.errors().exists("Client " + rep.getClientId() + " already exists");
+            return ErrorResponse.exists("Client " + rep.getClientId() + " already exists");
         }
     }
 
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java
index 270b7ac..b68bfc7 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java
@@ -11,7 +11,7 @@ import org.keycloak.models.RealmModel;
 import org.keycloak.models.utils.ModelToRepresentation;
 import org.keycloak.models.utils.RepresentationToModel;
 import org.keycloak.representations.idm.ClientRepresentation;
-import org.keycloak.services.resources.flows.Flows;
+import org.keycloak.services.ErrorResponse;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.GET;
@@ -91,7 +91,7 @@ public class ClientsResource {
             ClientModel clientModel = RepresentationToModel.createClient(session, realm, rep, true);
             return Response.created(uriInfo.getAbsolutePathBuilder().path(getClientPath(clientModel)).build()).build();
         } catch (ModelDuplicateException e) {
-            return Flows.errors().exists("Client " + rep.getClientId() + " already exists");
+            return ErrorResponse.exists("Client " + rep.getClientId() + " already exists");
         }
     }
 
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java
index 3f1983c..fcc1d63 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java
@@ -25,8 +25,8 @@ import org.keycloak.representations.idm.ConfigPropertyRepresentation;
 import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
 import org.keycloak.representations.idm.IdentityProviderMapperTypeRepresentation;
 import org.keycloak.representations.idm.IdentityProviderRepresentation;
+import org.keycloak.services.ErrorResponse;
 import org.keycloak.representations.idm.ProtocolMapperRepresentation;
-import org.keycloak.services.resources.flows.Flows;
 import org.keycloak.social.SocialIdentityProvider;
 
 import javax.ws.rs.Consumes;
@@ -113,7 +113,7 @@ public class IdentityProviderResource {
 
             return Response.noContent().build();
         } catch (ModelDuplicateException e) {
-            return Flows.errors().exists("Identity Provider " + providerRep.getAlias() + " already exists");
+            return ErrorResponse.exists("Identity Provider " + providerRep.getAlias() + " already exists");
         }
     }
 
@@ -187,7 +187,7 @@ public class IdentityProviderResource {
             IdentityProviderFactory factory = getIdentityProviderFactory();
             return factory.create(identityProviderModel).export(uriInfo, realm, format);
         } catch (Exception e) {
-            return Flows.errors().error("Could not export public broker configuration for identity provider [" + identityProviderModel.getProviderId() + "].", Response.Status.NOT_FOUND);
+            return ErrorResponse.error("Could not export public broker configuration for identity provider [" + identityProviderModel.getProviderId() + "].", Response.Status.NOT_FOUND);
         }
     }
 
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java
index ef0e94b..34cdc78 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java
@@ -17,7 +17,7 @@ import org.keycloak.models.utils.RepresentationToModel;
 import org.keycloak.provider.ProviderFactory;
 import org.keycloak.representations.idm.IdentityProviderRepresentation;
 import org.keycloak.services.managers.ResourceAdminManager;
-import org.keycloak.services.resources.flows.Flows;
+import org.keycloak.services.ErrorResponse;
 import org.keycloak.social.SocialIdentityProvider;
 
 import javax.ws.rs.Consumes;
@@ -132,7 +132,7 @@ public class IdentityProvidersResource {
 
             return Response.created(uriInfo.getAbsolutePathBuilder().path(representation.getProviderId()).build()).build();
         } catch (ModelDuplicateException e) {
-            return Flows.errors().exists("Identity Provider " + representation.getAlias() + " already exists");
+            return ErrorResponse.exists("Identity Provider " + representation.getAlias() + " already exists");
         }
     }
 
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
index 5fea57d..6031c2e 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
@@ -29,7 +29,7 @@ import org.keycloak.services.managers.LDAPConnectionTestManager;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.services.managers.ResourceAdminManager;
 import org.keycloak.services.managers.UsersSyncManager;
-import org.keycloak.services.resources.flows.Flows;
+import org.keycloak.services.ErrorResponse;
 import org.keycloak.timer.TimerProvider;
 
 import javax.ws.rs.Consumes;
@@ -191,11 +191,11 @@ public class RealmAdminResource {
 
             return Response.noContent().build();
         } catch (PatternSyntaxException e) {
-            return Flows.errors().exists("Specified regex pattern(s) is invalid.");
+            return ErrorResponse.exists("Specified regex pattern(s) is invalid.");
         } catch (ModelDuplicateException e) {
-            return Flows.errors().exists("Realm " + rep.getRealm() + " already exists.");
+            return ErrorResponse.exists("Realm " + rep.getRealm() + " already exists.");
         }  catch (Exception e) {
-            return Flows.errors().exists("Failed to update " + rep.getRealm() + " Realm.");
+            return ErrorResponse.exists("Failed to update " + rep.getRealm() + " Realm.");
         }
     }
 
@@ -442,7 +442,7 @@ public class RealmAdminResource {
         auth.init(RealmAuth.Resource.REALM).requireManage();
 
         boolean result = new LDAPConnectionTestManager().testLDAP(action, connectionUrl, bindDn, bindCredential);
-        return result ? Response.noContent().build() : Flows.errors().error("LDAP test error", Response.Status.BAD_REQUEST);
+        return result ? Response.noContent().build() : ErrorResponse.error("LDAP test error", Response.Status.BAD_REQUEST);
     }
 
     @Path("identity-provider")
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java
index 21fc79e..c9fea3d 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java
@@ -18,7 +18,7 @@ import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.services.ForbiddenException;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.services.resources.KeycloakApplication;
-import org.keycloak.services.resources.flows.Flows;
+import org.keycloak.services.ErrorResponse;
 import org.keycloak.util.JsonSerialization;
 
 import javax.ws.rs.Consumes;
@@ -131,7 +131,7 @@ public class RealmsAdminResource {
 
             return Response.created(location).build();
         } catch (ModelDuplicateException e) {
-            return Flows.errors().exists("Realm " + rep.getRealm() + " already exists");
+            return ErrorResponse.exists("Realm " + rep.getRealm() + " already exists");
         }
     }
 
@@ -166,7 +166,7 @@ public class RealmsAdminResource {
             try {
                 realm = realmManager.importRealm(rep);
             } catch (ModelDuplicateException e) {
-                return Flows.errors().exists("Realm " + rep.getRealm() + " already exists");
+                return ErrorResponse.exists("Realm " + rep.getRealm() + " already exists");
             }
 
             grantPermissionsToRealmCreator(realm);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java
index 66d37a9..fa0064f 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java
@@ -9,7 +9,7 @@ import org.keycloak.models.RoleContainerModel;
 import org.keycloak.models.RoleModel;
 import org.keycloak.models.utils.ModelToRepresentation;
 import org.keycloak.representations.idm.RoleRepresentation;
-import org.keycloak.services.resources.flows.Flows;
+import org.keycloak.services.ErrorResponse;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
@@ -79,7 +79,7 @@ public class RoleContainerResource extends RoleResource {
             role.setDescription(rep.getDescription());
             return Response.created(uriInfo.getAbsolutePathBuilder().path(role.getName()).build()).build();
         } catch (ModelDuplicateException e) {
-            return Flows.errors().exists("Role with name " + rep.getName() + " already exists");
+            return ErrorResponse.exists("Role with name " + rep.getName() + " already exists");
         }
     }
 
@@ -144,7 +144,7 @@ public class RoleContainerResource extends RoleResource {
             updateRole(rep, role);
             return Response.noContent().build();
         } catch (ModelDuplicateException e) {
-            return Flows.errors().exists("Role with name " + rep.getName() + " already exists");
+            return ErrorResponse.exists("Role with name " + rep.getName() + " already exists");
         }
     }
 
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
index 8bad898..a1dd22d 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
@@ -36,8 +36,8 @@ import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.managers.ClientSessionCode;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.services.managers.UserManager;
-import org.keycloak.services.resources.flows.Flows;
-import org.keycloak.services.resources.flows.Urls;
+import org.keycloak.services.ErrorResponse;
+import org.keycloak.services.Urls;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
@@ -123,9 +123,9 @@ public class UsersResource {
 
             return Response.noContent().build();
         } catch (ModelDuplicateException e) {
-            return Flows.errors().exists("User exists with same username or email");
+            return ErrorResponse.exists("User exists with same username or email");
         } catch (ModelReadOnlyException re) {
-            return Flows.errors().exists("User is read only!");
+            return ErrorResponse.exists("User is read only!");
         }
     }
 
@@ -143,10 +143,10 @@ public class UsersResource {
 
         // Double-check duplicated username and email here due to federation
         if (session.users().getUserByUsername(rep.getUsername(), realm) != null) {
-            return Flows.errors().exists("User exists with same username");
+            return ErrorResponse.exists("User exists with same username");
         }
         if (rep.getEmail() != null && session.users().getUserByEmail(rep.getEmail(), realm) != null) {
-            return Flows.errors().exists("User exists with same email");
+            return ErrorResponse.exists("User exists with same email");
         }
 
         try {
@@ -162,7 +162,7 @@ public class UsersResource {
             if (session.getTransaction().isActive()) {
                 session.getTransaction().setRollbackOnly();
             }
-            return Flows.errors().exists("User exists with same username or email");
+            return ErrorResponse.exists("User exists with same username or email");
         }
     }
 
@@ -287,7 +287,7 @@ public class UsersResource {
             throw new NotFoundException("User not found");
         }
         if (session.users().getFederatedIdentity(user, provider, realm) != null) {
-            return Flows.errors().exists("User is already linked with provider");
+            return ErrorResponse.exists("User is already linked with provider");
         }
 
         FederatedIdentityModel socialLink = new FederatedIdentityModel(provider, rep.getUserId(), rep.getUserName());
@@ -352,7 +352,7 @@ public class UsersResource {
         if (removed) {
             return Response.noContent().build();
         } else {
-            return Flows.errors().error("User couldn't be deleted", Response.Status.BAD_REQUEST);
+            return ErrorResponse.error("User couldn't be deleted", Response.Status.BAD_REQUEST);
         }
     }
 
@@ -657,6 +657,8 @@ public class UsersResource {
         UserCredentialModel cred = RepresentationToModel.convertCredential(pass);
         try {
             session.users().updateCredential(realm, user, cred);
+        } catch (IllegalStateException ise) {
+            throw new BadRequestException("Resetting to N old passwords is not allowed.");
         } catch (ModelReadOnlyException mre) {
             throw new BadRequestException("Can't reset password as account is read only");
         }
@@ -700,19 +702,19 @@ public class UsersResource {
 
         UserModel user = session.users().getUserByUsername(username, realm);
         if (user == null) {
-            return Flows.errors().error("User not found", Response.Status.NOT_FOUND);
+            return ErrorResponse.error("User not found", Response.Status.NOT_FOUND);
         }
 
         if (!user.isEnabled()) {
-            return Flows.errors().error("User is disabled", Response.Status.BAD_REQUEST);
+            return ErrorResponse.error("User is disabled", Response.Status.BAD_REQUEST);
         }
 
         if (user.getEmail() == null) {
-            return Flows.errors().error("User email missing", Response.Status.BAD_REQUEST);
+            return ErrorResponse.error("User email missing", Response.Status.BAD_REQUEST);
         }
 
         if(redirectUri != null && clientId == null){
-            return Flows.errors().error("Client id missing", Response.Status.BAD_REQUEST);
+            return ErrorResponse.error("Client id missing", Response.Status.BAD_REQUEST);
         }
 
         if(clientId == null){
@@ -721,14 +723,14 @@ public class UsersResource {
 
         ClientModel client = realm.getClientByClientId(clientId);
         if (client == null || !client.isEnabled()) {
-            return Flows.errors().error(clientId + " not enabled", Response.Status.INTERNAL_SERVER_ERROR);
+            return ErrorResponse.error(clientId + " not enabled", Response.Status.INTERNAL_SERVER_ERROR);
         }
 
         String redirect;
         if(redirectUri != null){
             redirect = RedirectUtils.verifyRedirectUri(uriInfo, redirectUri, realm, client);
             if(redirect == null){
-                return Flows.errors().error("Invalid redirect uri.", Response.Status.BAD_REQUEST);
+                return ErrorResponse.error("Invalid redirect uri.", Response.Status.BAD_REQUEST);
             }
         }else{
             redirect = Urls.accountBase(uriInfo.getBaseUri()).path("/").build(realm.getName()).toString();
@@ -758,7 +760,7 @@ public class UsersResource {
             return Response.ok().build();
         } catch (EmailException e) {
             logger.error("Failed to send password reset email", e);
-            return Flows.errors().error("Failed to send email", Response.Status.INTERNAL_SERVER_ERROR);
+            return ErrorResponse.error("Failed to send email", Response.Status.INTERNAL_SERVER_ERROR);
         }
     }
 
diff --git a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
index 7550eb8..0d4b38b 100755
--- a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
+++ b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
@@ -31,6 +31,7 @@ import org.keycloak.events.Details;
 import org.keycloak.events.Errors;
 import org.keycloak.events.EventBuilder;
 import org.keycloak.events.EventType;
+import org.keycloak.login.LoginFormsProvider;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.FederatedIdentityModel;
@@ -49,8 +50,9 @@ import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.managers.AuthenticationManager.AuthResult;
 import org.keycloak.services.managers.ClientSessionCode;
 import org.keycloak.services.messages.Messages;
-import org.keycloak.services.resources.flows.Flows;
-import org.keycloak.services.resources.flows.Urls;
+import org.keycloak.services.ErrorResponse;
+import org.keycloak.services.ErrorPage;
+import org.keycloak.services.Urls;
 import org.keycloak.services.validation.Validation;
 import org.keycloak.social.SocialIdentityProvider;
 import org.keycloak.util.ObjectUtil;
@@ -190,15 +192,16 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
                     return badRequest("Invalid client.");
                 }
 
+                session.getContext().setClient(clientModel);
+
                 if (!clientModel.isAllowedRetrieveTokenFromIdentityProvider(providerId)) {
                     return corsResponse(badRequest("Client [" + audience + "] not authorized to retrieve tokens from identity provider [" + providerId + "]."), clientModel);
                 }
 
                 if (clientModel.isConsentRequired()) {
-                    return corsResponse(Flows.forms(this.session, this.realmModel, clientModel, this.uriInfo, headers)
+                    return corsResponse(session.getProvider(LoginFormsProvider.class)
                             .setClientSessionCode(authManager.extractAuthorizationHeaderToken(this.request.getHttpHeaders()))
                             .setAccessRequest("Your information from " + providerId + " identity provider.")
-                            .setClient(clientModel)
                             .setActionUri(this.uriInfo.getRequestUri())
                             .createOAuthGrant(null), clientModel);
                 }
@@ -420,7 +423,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
         }
 
         fireErrorEvent(message, throwable);
-        return Flows.forwardToSecurityFailurePage(this.session, this.realmModel, this.uriInfo, headers, message, parameters);
+        return ErrorPage.error(this.session, message, parameters);
     }
 
     private Response redirectToLoginPage(Throwable t, ClientSessionCode clientCode) {
@@ -431,7 +434,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
         }
 
         fireErrorEvent(message);
-        return Flows.forms(this.session, this.realmModel, clientCode.getClientSession().getClient(), this.uriInfo, headers)
+        return session.getProvider(LoginFormsProvider.class)
                 .setClientSessionCode(clientCode.getCode())
                 .setError(message)
                 .createLogin();
@@ -439,7 +442,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
 
     private Response badRequest(String message) {
         fireErrorEvent(message);
-        return Flows.errors().error(message, Status.BAD_REQUEST);
+        return ErrorResponse.error(message, Status.BAD_REQUEST);
     }
 
     public static IdentityProvider getIdentityProvider(KeycloakSession session, RealmModel realm, String alias) {
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 106be1c..f3f5937 100755
--- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
@@ -53,8 +53,8 @@ import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.managers.ClientSessionCode;
 import org.keycloak.services.messages.Messages;
-import org.keycloak.services.resources.flows.Flows;
-import org.keycloak.services.resources.flows.Urls;
+import org.keycloak.services.ErrorPage;
+import org.keycloak.services.Urls;
 import org.keycloak.services.util.CookieHelper;
 import org.keycloak.services.validation.Validation;
 
@@ -162,7 +162,7 @@ public class LoginActionsService {
                 return false;
             } else if (!clientCode.isValid(requiredAction)) {
                 event.error(Errors.INVALID_CODE);
-                response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_CODE);
+                response = ErrorPage.error(session, Messages.INVALID_CODE);
                 return false;
             } else {
                 return true;
@@ -174,7 +174,7 @@ public class LoginActionsService {
                 return false;
             } else if (!(clientCode.isValid(requiredAction) || clientCode.isValid(alternativeRequiredAction))) {
                 event.error(Errors.INVALID_CODE);
-                response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo,headers, Messages.INVALID_CODE);
+                response = ErrorPage.error(session, Messages.INVALID_CODE);
                 return false;
             } else {
                 return true;
@@ -184,20 +184,21 @@ public class LoginActionsService {
         public boolean check(String code) {
             if (!checkSsl()) {
                 event.error(Errors.SSL_REQUIRED);
-                response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.HTTPS_REQUIRED);
+                response = ErrorPage.error(session, Messages.HTTPS_REQUIRED);
                 return false;
             }
             if (!realm.isEnabled()) {
                 event.error(Errors.REALM_DISABLED);
-                response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.REALM_NOT_ENABLED);
+                response = ErrorPage.error(session, Messages.REALM_NOT_ENABLED);
                 return false;
             }
             clientCode = ClientSessionCode.parse(code, session, realm);
             if (clientCode == null) {
                 event.error(Errors.INVALID_CODE);
-                response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.UNKNOWN_CODE);
+                response = ErrorPage.error(session, Messages.UNKNOWN_CODE);
                 return false;
             }
+            session.getContext().setClient(clientCode.getClientSession().getClient());
             return true;
         }
     }
@@ -226,10 +227,9 @@ public class LoginActionsService {
             clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE);
         }
 
-        LoginFormsProvider forms = Flows.forms(session, realm, clientSession.getClient(), uriInfo, headers)
-                .setClientSessionCode(clientSessionCode.getCode());
-
-        return forms.createLogin();
+        return session.getProvider(LoginFormsProvider.class)
+                .setClientSessionCode(clientSessionCode.getCode())
+                .createLogin();
     }
 
     /**
@@ -244,7 +244,7 @@ public class LoginActionsService {
         event.event(EventType.REGISTER);
         if (!realm.isRegistrationAllowed()) {
             event.error(Errors.REGISTRATION_DISABLED);
-            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.REGISTRATION_NOT_ALLOWED);
+            return ErrorPage.error(session, Messages.REGISTRATION_NOT_ALLOWED);
         }
 
         Checks checks = new Checks();
@@ -258,7 +258,7 @@ public class LoginActionsService {
 
         authManager.expireIdentityCookie(realm, uriInfo, clientConnection);
 
-        return Flows.forms(session, realm, clientSession.getClient(), uriInfo, headers)
+        return session.getProvider(LoginFormsProvider.class)
                 .setClientSessionCode(clientSessionCode.getCode())
                 .createRegistration();
     }
@@ -278,17 +278,17 @@ public class LoginActionsService {
         event.event(EventType.LOGIN);
         if (!checkSsl()) {
             event.error(Errors.SSL_REQUIRED);
-            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.HTTPS_REQUIRED);
+            return ErrorPage.error(session, Messages.HTTPS_REQUIRED);
         }
 
         if (!realm.isEnabled()) {
             event.error(Errors.REALM_DISABLED);
-            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.REALM_NOT_ENABLED);
+            return ErrorPage.error(session, Messages.REALM_NOT_ENABLED);
         }
         ClientSessionCode clientCode = ClientSessionCode.parse(code, session, realm);
         if (clientCode == null) {
             event.error(Errors.INVALID_CODE);
-            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.UNKNOWN_CODE);
+            return ErrorPage.error(session, Messages.UNKNOWN_CODE);
         }
 
         ClientSessionModel clientSession = clientCode.getClientSession();
@@ -297,7 +297,8 @@ public class LoginActionsService {
         if (!clientCode.isValid(ClientSessionModel.Action.AUTHENTICATE) || clientSession.getUserSession() != null) {
             clientCode.setAction(ClientSessionModel.Action.AUTHENTICATE);
             event.client(clientSession.getClient()).error(Errors.EXPIRED_CODE);
-            return Flows.forms(this.session, realm, clientSession.getClient(), uriInfo, headers).setError(Messages.EXPIRED_CODE)
+            return session.getProvider(LoginFormsProvider.class)
+                    .setError(Messages.EXPIRED_CODE)
                     .setClientSessionCode(clientCode.getCode())
                     .createLogin();
         }
@@ -320,13 +321,15 @@ public class LoginActionsService {
         ClientModel client = clientSession.getClient();
         if (client == null) {
             event.error(Errors.CLIENT_NOT_FOUND);
-            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.UNKNOWN_LOGIN_REQUESTER);
+            return ErrorPage.error(session, Messages.UNKNOWN_LOGIN_REQUESTER);
         }
         if (!client.isEnabled()) {
             event.error(Errors.CLIENT_NOT_FOUND);
-            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.LOGIN_REQUESTER_NOT_ENABLED);
+            return ErrorPage.error(session, Messages.LOGIN_REQUESTER_NOT_ENABLED);
         }
 
+        session.getContext().setClient(clientSession.getClient());
+
         if (formData.containsKey("cancel")) {
             event.error(Errors.REJECTED_BY_USER);
             LoginProtocol protocol = session.getProvider(LoginProtocol.class, clientSession.getAuthMethod());
@@ -358,14 +361,14 @@ public class LoginActionsService {
                 return authManager.nextActionAfterAuthentication(session, userSession, clientSession, clientConnection, request, uriInfo, event);
             case ACCOUNT_TEMPORARILY_DISABLED:
                 event.error(Errors.USER_TEMPORARILY_DISABLED);
-                return Flows.forms(this.session, realm, client, uriInfo, headers)
+                return session.getProvider(LoginFormsProvider.class)
                         .setError(Messages.ACCOUNT_TEMPORARILY_DISABLED)
                         .setFormData(formData)
                         .setClientSessionCode(clientCode.getCode())
                         .createLogin();
             case ACCOUNT_DISABLED:
                 event.error(Errors.USER_DISABLED);
-                return Flows.forms(this.session, realm, client, uriInfo, headers)
+                return session.getProvider(LoginFormsProvider.class)
                         .setError(Messages.ACCOUNT_DISABLED)
                         .setClientSessionCode(clientCode.getCode())
                         .setFormData(formData).createLogin();
@@ -375,19 +378,21 @@ public class LoginActionsService {
                 String passwordToken = new JWSBuilder().jsonContent(new PasswordToken(realm.getName(), user.getId())).rsa256(realm.getPrivateKey());
                 formData.add(CredentialRepresentation.PASSWORD_TOKEN, passwordToken);
 
-                return Flows.forms(this.session, realm, client, uriInfo, headers)
+                return session.getProvider(LoginFormsProvider.class)
                         .setFormData(formData)
                         .setClientSessionCode(clientCode.getCode())
                         .createLoginTotp();
             case INVALID_USER:
                 event.error(Errors.USER_NOT_FOUND);
-                return Flows.forms(this.session, realm, client, uriInfo, headers).setError(Messages.INVALID_USER)
+                return session.getProvider(LoginFormsProvider.class)
+                        .setError(Messages.INVALID_USER)
                         .setFormData(formData)
                         .setClientSessionCode(clientCode.getCode())
                         .createLogin();
             default:
                 event.error(Errors.INVALID_USER_CREDENTIALS);
-                return Flows.forms(this.session, realm, client, uriInfo, headers).setError(Messages.INVALID_USER)
+                return session.getProvider(LoginFormsProvider.class)
+                        .setError(Messages.INVALID_USER)
                         .setFormData(formData)
                         .setClientSessionCode(clientCode.getCode())
                         .createLogin();
@@ -409,25 +414,25 @@ public class LoginActionsService {
         event.event(EventType.REGISTER);
         if (!checkSsl()) {
             event.error(Errors.SSL_REQUIRED);
-            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.HTTPS_REQUIRED);
+            return ErrorPage.error(session, Messages.HTTPS_REQUIRED);
         }
 
         if (!realm.isEnabled()) {
             event.error(Errors.REALM_DISABLED);
-            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.REALM_NOT_ENABLED);
+            return ErrorPage.error(session, Messages.REALM_NOT_ENABLED);
         }
         if (!realm.isRegistrationAllowed()) {
             event.error(Errors.REGISTRATION_DISABLED);
-            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.REGISTRATION_NOT_ALLOWED);
+            return ErrorPage.error(session, Messages.REGISTRATION_NOT_ALLOWED);
         }
         ClientSessionCode clientCode = ClientSessionCode.parse(code, session, realm);
         if (clientCode == null) {
             event.error(Errors.INVALID_CODE);
-            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.UNKNOWN_CODE);
+            return ErrorPage.error(session, Messages.UNKNOWN_CODE);
         }
         if (!clientCode.isValid(ClientSessionModel.Action.AUTHENTICATE)) {
             event.error(Errors.INVALID_CODE);
-            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_CODE);
+            return ErrorPage.error(session, Messages.INVALID_CODE);
         }
 
         String username = formData.getFirst(Validation.FIELD_USERNAME);
@@ -446,19 +451,20 @@ public class LoginActionsService {
 
         if (!realm.isEnabled()) {
             event.error(Errors.REALM_DISABLED);
-            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.REALM_NOT_ENABLED);
+            return ErrorPage.error(session, Messages.REALM_NOT_ENABLED);
         }
         ClientModel client = clientSession.getClient();
         if (client == null) {
             event.error(Errors.CLIENT_NOT_FOUND);
-            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.UNKNOWN_LOGIN_REQUESTER);
+            return ErrorPage.error(session, Messages.UNKNOWN_LOGIN_REQUESTER);
         }
 
         if (!client.isEnabled()) {
             event.error(Errors.CLIENT_DISABLED);
-            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.LOGIN_REQUESTER_NOT_ENABLED);
+            return ErrorPage.error(session, Messages.LOGIN_REQUESTER_NOT_ENABLED);
         }
 
+        session.getContext().setClient(client);
 
         List<String> requiredCredentialTypes = new LinkedList<String>();
         for (RequiredCredentialModel m : realm.getRequiredCredentials()) {
@@ -470,7 +476,7 @@ public class LoginActionsService {
 
         if (errors != null && !errors.isEmpty()) {
             event.error(Errors.INVALID_REGISTRATION);
-            return Flows.forms(session, realm, client, uriInfo, headers)
+            return session.getProvider(LoginFormsProvider.class)
                     .setErrors(errors)
                     .setFormData(formData)
                     .setClientSessionCode(clientCode.getCode())
@@ -480,7 +486,7 @@ public class LoginActionsService {
         // Validate that user with this username doesn't exist in realm or any federation provider
         if (session.users().getUserByUsername(username, realm) != null) {
             event.error(Errors.USERNAME_IN_USE);
-            return Flows.forms(session, realm, client, uriInfo, headers)
+            return session.getProvider(LoginFormsProvider.class)
                     .setError(Messages.USERNAME_EXISTS)
                     .setFormData(formData)
                     .setClientSessionCode(clientCode.getCode())
@@ -490,7 +496,7 @@ public class LoginActionsService {
         // Validate that user with this email doesn't exist in realm or any federation provider
         if (email != null && session.users().getUserByEmail(email, realm) != null) {
             event.error(Errors.EMAIL_IN_USE);
-            return Flows.forms(session, realm, client, uriInfo, headers)
+            return session.getProvider(LoginFormsProvider.class)
                     .setError(Messages.EMAIL_EXISTS)
                     .setFormData(formData)
                     .setClientSessionCode(clientCode.getCode())
@@ -527,7 +533,7 @@ public class LoginActionsService {
             // User already registered, but force him to update password
             if (!passwordUpdateSuccessful) {
                 user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
-                return Flows.forms(session, realm, client, uriInfo, headers)
+                return session.getProvider(LoginFormsProvider.class)
                         .setError(passwordUpdateError, passwordUpdateErrorParameters)
                         .setClientSessionCode(clientCode.getCode())
                         .createResponse(UserModel.RequiredAction.UPDATE_PASSWORD);
@@ -556,7 +562,7 @@ public class LoginActionsService {
 
 
         if (!checkSsl()) {
-            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.HTTPS_REQUIRED);
+            return ErrorPage.error(session, Messages.HTTPS_REQUIRED);
         }
 
         String code = formData.getFirst("code");
@@ -564,7 +570,7 @@ public class LoginActionsService {
         ClientSessionCode accessCode = ClientSessionCode.parse(code, session, realm);
         if (accessCode == null || !accessCode.isValid(ClientSessionModel.Action.OAUTH_GRANT)) {
             event.error(Errors.INVALID_CODE);
-            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_ACCESS_CODE);
+            return ErrorPage.error(session, Messages.INVALID_ACCESS_CODE);
         }
         ClientSessionModel clientSession = accessCode.getClientSession();
         event.detail(Details.CODE_ID, clientSession.getId());
@@ -588,7 +594,7 @@ public class LoginActionsService {
         if (!AuthenticationManager.isSessionValid(realm, userSession)) {
             AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, clientConnection, headers);
             event.error(Errors.INVALID_CODE);
-            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.SESSION_NOT_ACTIVE);
+            return ErrorPage.error(session, Messages.SESSION_NOT_ACTIVE);
         }
         event.session(userSession);
 
@@ -625,8 +631,10 @@ public class LoginActionsService {
 
         List<FormMessage> errors = Validation.validateUpdateProfileForm(formData);
         if (errors != null && !errors.isEmpty()) {
-            return Flows.forms(session, realm, null, uriInfo, headers).setUser(user).setErrors(errors)
+            return session.getProvider(LoginFormsProvider.class)
                     .setClientSessionCode(accessCode.getCode())
+                    .setUser(user)
+                    .setErrors(errors)
                     .createResponse(RequiredAction.UPDATE_PROFILE);
         }
 
@@ -643,7 +651,9 @@ public class LoginActionsService {
 
             // check for duplicated email
             if (userByEmail != null && !userByEmail.getId().equals(user.getId())) {
-                return Flows.forms(session, realm, null, uriInfo, headers).setUser(user).setError(Messages.EMAIL_EXISTS)
+                return session.getProvider(LoginFormsProvider.class)
+                        .setUser(user)
+                        .setError(Messages.EMAIL_EXISTS)
                         .setClientSessionCode(accessCode.getCode())
                         .createResponse(RequiredAction.UPDATE_PROFILE);
             }
@@ -682,7 +692,7 @@ public class LoginActionsService {
         String totp = formData.getFirst("totp");
         String totpSecret = formData.getFirst("totpSecret");
 
-        LoginFormsProvider loginForms = Flows.forms(session, realm, null, uriInfo, headers).setUser(user);
+        LoginFormsProvider loginForms = session.getProvider(LoginFormsProvider.class).setUser(user);
         if (Validation.isEmpty(totp)) {
             return loginForms.setError(Messages.MISSING_TOTP)
                     .setClientSessionCode(accessCode.getCode())
@@ -727,7 +737,8 @@ public class LoginActionsService {
         String passwordNew = formData.getFirst("password-new");
         String passwordConfirm = formData.getFirst("password-confirm");
 
-        LoginFormsProvider loginForms = Flows.forms(session, realm, null, uriInfo, headers).setUser(user);
+        LoginFormsProvider loginForms = session.getProvider(LoginFormsProvider.class)
+                .setUser(user);
         if (Validation.isEmpty(passwordNew)) {
             return loginForms.setError(Messages.MISSING_PASSWORD)
                     .setClientSessionCode(accessCode.getCode())
@@ -757,7 +768,9 @@ public class LoginActionsService {
         if (clientSession.getAction().equals(ClientSessionModel.Action.RECOVER_PASSWORD)) {
             String actionCookieValue = getActionCookie();
             if (actionCookieValue == null || !actionCookieValue.equals(userSession.getId())) {
-                return Flows.forms(session, realm, clientSession.getClient(), uriInfo, headers).setSuccess(Messages.ACCOUNT_PASSWORD_UPDATED).createInfoPage();
+                return session.getProvider(LoginFormsProvider.class)
+                        .setSuccess(Messages.ACCOUNT_PASSWORD_UPDATED)
+                        .createInfoPage();
             }
         }
 
@@ -789,7 +802,9 @@ public class LoginActionsService {
 
             String actionCookieValue = getActionCookie();
             if (actionCookieValue == null || !actionCookieValue.equals(userSession.getId())) {
-                return Flows.forms(session, realm, clientSession.getClient(), uriInfo, headers).setSuccess(Messages.EMAIL_VERIFIED).createInfoPage();
+                return session.getProvider(LoginFormsProvider.class)
+                        .setSuccess(Messages.EMAIL_VERIFIED)
+                        .createInfoPage();
             }
 
             event = event.clone().removeDetail(Details.EMAIL).event(EventType.LOGIN);
@@ -807,7 +822,7 @@ public class LoginActionsService {
 
             createActionCookie(realm, uriInfo, clientConnection, userSession.getId());
 
-            return Flows.forms(session, realm, null, uriInfo, headers)
+            return session.getProvider(LoginFormsProvider.class)
                     .setClientSessionCode(accessCode.getCode())
                     .setUser(userSession.getUser())
                     .createResponse(RequiredAction.VERIFY_EMAIL);
@@ -824,11 +839,11 @@ public class LoginActionsService {
                 return checks.response;
             }
             ClientSessionCode accessCode = checks.clientCode;
-            return Flows.forms(session, realm, null, uriInfo, headers)
+            return session.getProvider(LoginFormsProvider.class)
                     .setClientSessionCode(accessCode.getCode())
                     .createResponse(RequiredAction.UPDATE_PASSWORD);
         } else {
-            return Flows.forms(session, realm, null, uriInfo, headers)
+            return session.getProvider(LoginFormsProvider.class)
                     .setClientSessionCode(code)
                     .createPasswordReset();
         }
@@ -841,16 +856,16 @@ public class LoginActionsService {
                                       final MultivaluedMap<String, String> formData) {
         event.event(EventType.SEND_RESET_PASSWORD);
         if (!checkSsl()) {
-            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.HTTPS_REQUIRED);
+            return ErrorPage.error(session, Messages.HTTPS_REQUIRED);
         }
         if (!realm.isEnabled()) {
             event.error(Errors.REALM_DISABLED);
-            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.REALM_NOT_ENABLED);
+            return ErrorPage.error(session, Messages.REALM_NOT_ENABLED);
         }
         ClientSessionCode accessCode = ClientSessionCode.parse(code, session, realm);
         if (accessCode == null) {
             event.error(Errors.INVALID_CODE);
-            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.UNKNOWN_CODE);
+            return ErrorPage.error(session, Messages.UNKNOWN_CODE);
         }
         ClientSessionModel clientSession = accessCode.getClientSession();
 
@@ -858,12 +873,14 @@ public class LoginActionsService {
 
         ClientModel client = clientSession.getClient();
         if (client == null) {
-            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.UNKNOWN_LOGIN_REQUESTER);
+            return ErrorPage.error(session, Messages.UNKNOWN_LOGIN_REQUESTER);
         }
         if (!client.isEnabled()) {
-            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.LOGIN_REQUESTER_NOT_ENABLED);
+            return ErrorPage.error(session, Messages.LOGIN_REQUESTER_NOT_ENABLED);
         }
 
+        session.getContext().setClient(client);
+
         event.client(client.getClientId())
                 .detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
                 .detail(Details.RESPONSE_TYPE, "code")
@@ -904,7 +921,8 @@ public class LoginActionsService {
             } catch (EmailException e) {
                 event.error(Errors.EMAIL_SEND_FAILED);
                 logger.error("Failed to send password reset email", e);
-                return Flows.forms(this.session, realm, client, uriInfo, headers).setError(Messages.EMAIL_SENT_ERROR)
+                return session.getProvider(LoginFormsProvider.class)
+                        .setError(Messages.EMAIL_SENT_ERROR)
                         .setClientSessionCode(accessCode.getCode())
                         .createErrorPage();
             }
@@ -912,7 +930,10 @@ public class LoginActionsService {
             createActionCookie(realm, uriInfo, clientConnection, userSession.getId());
         }
 
-        return Flows.forms(session, realm, client,  uriInfo, headers).setSuccess(Messages.EMAIL_SENT).setClientSessionCode(accessCode.getCode()).createPasswordReset();
+        return session.getProvider(LoginFormsProvider.class)
+                .setSuccess(Messages.EMAIL_SENT)
+                .setClientSessionCode(accessCode.getCode())
+                .createPasswordReset();
     }
 
     private String getActionCookie() {
diff --git a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
index 571f111..b3dd570 100755
--- a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
@@ -24,7 +24,6 @@ import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
-import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
@@ -39,17 +38,6 @@ public class RealmsResource {
     protected static Logger logger = Logger.getLogger(RealmsResource.class);
 
     @Context
-    protected UriInfo uriInfo;
-
-    @Context
-    protected HttpHeaders headers;
-
-    /*
-    @Context
-    protected ResourceContext resourceContext;
-    */
-
-    @Context
     protected KeycloakSession session;
 
     @Context
@@ -62,18 +50,10 @@ public class RealmsResource {
         return uriInfo.getBaseUriBuilder().path(RealmsResource.class).path(RealmsResource.class, "getRealmResource");
     }
 
-    public static UriBuilder realmBaseUrl(UriBuilder base) {
-        return base.path(RealmsResource.class).path(RealmsResource.class, "getRealmResource");
-    }
-
     public static UriBuilder accountUrl(UriBuilder base) {
         return base.path(RealmsResource.class).path(RealmsResource.class, "getAccountService");
     }
 
-    public static UriBuilder protocolUrl(UriBuilder base) {
-        return base.path(RealmsResource.class).path(RealmsResource.class, "getProtocol");
-    }
-
     public static UriBuilder protocolUrl(UriInfo uriInfo) {
         return uriInfo.getBaseUriBuilder().path(RealmsResource.class).path(RealmsResource.class, "getProtocol");
     }
@@ -87,9 +67,8 @@ public class RealmsResource {
     public Object getLoginStatusIframe(final @PathParam("realm") String name,
                                        @QueryParam("client_id") String client_id,
                                        @QueryParam("origin") String origin) {
-        // backward compatibility
-        RealmManager realmManager = new RealmManager(session);
-        RealmModel realm = locateRealm(name, realmManager);
+        RealmModel realm = init(name);
+
         EventBuilder event = new EventBuilder(realm, session, clientConnection);
         AuthenticationManager authManager = new AuthenticationManager(protector);
 
@@ -104,8 +83,8 @@ public class RealmsResource {
     @Path("{realm}/protocol/{protocol}")
     public Object getProtocol(final @PathParam("realm") String name,
                                             final @PathParam("protocol") String protocol) {
-        RealmManager realmManager = new RealmManager(session);
-        RealmModel realm = locateRealm(name, realmManager);
+        RealmModel realm = init(name);
+
         EventBuilder event = new EventBuilder(realm, session, clientConnection);
         AuthenticationManager authManager = new AuthenticationManager(protector);
 
@@ -125,8 +104,7 @@ public class RealmsResource {
 
     @Path("{realm}/login-actions")
     public LoginActionsService getLoginActionsService(final @PathParam("realm") String name) {
-        RealmManager realmManager = new RealmManager(session);
-        RealmModel realm = locateRealm(name, realmManager);
+        RealmModel realm = init(name);
         EventBuilder event = new EventBuilder(realm, session, clientConnection);
         AuthenticationManager authManager = new AuthenticationManager(protector);
         LoginActionsService service = new LoginActionsService(realm, authManager, event);
@@ -136,26 +114,26 @@ public class RealmsResource {
 
     @Path("{realm}/clients-managements")
     public ClientsManagementService getClientsManagementService(final @PathParam("realm") String name) {
-        RealmManager realmManager = new RealmManager(session);
-        RealmModel realm = locateRealm(name, realmManager);
+        RealmModel realm = init(name);
         EventBuilder event = new EventBuilder(realm, session, clientConnection);
         ClientsManagementService service = new ClientsManagementService(realm, event);
         ResteasyProviderFactory.getInstance().injectProperties(service);
         return service;
     }
 
-    protected RealmModel locateRealm(String name, RealmManager realmManager) {
-        RealmModel realm = realmManager.getRealmByName(name);
+    private RealmModel init(String realmName) {
+        RealmManager realmManager = new RealmManager(session);
+        RealmModel realm = realmManager.getRealmByName(realmName);
         if (realm == null) {
             throw new NotFoundException("Realm does not exist");
         }
+        session.getContext().setRealm(realm);
         return realm;
     }
 
     @Path("{realm}/account")
     public AccountService getAccountService(final @PathParam("realm") String name) {
-        RealmManager realmManager = new RealmManager(session);
-        RealmModel realm = locateRealm(name, realmManager);
+        RealmModel realm = init(name);
 
         ClientModel client = realm.getClientNameMap().get(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID);
         if (client == null || !client.isEnabled()) {
@@ -172,8 +150,7 @@ public class RealmsResource {
 
     @Path("{realm}")
     public PublicRealmResource getRealmResource(final @PathParam("realm") String name) {
-        RealmManager realmManager = new RealmManager(session);
-        RealmModel realm = locateRealm(name, realmManager);
+        RealmModel realm = init(name);
         PublicRealmResource realmResource = new PublicRealmResource(realm);
         ResteasyProviderFactory.getInstance().injectProperties(realmResource);
         return realmResource;
@@ -181,8 +158,7 @@ public class RealmsResource {
 
     @Path("{realm}/broker")
     public IdentityBrokerService getBrokerService(final @PathParam("realm") String name) {
-        RealmManager realmManager = new RealmManager(session);
-        RealmModel realm = locateRealm(name, realmManager);
+        RealmModel realm = init(name);
 
         IdentityBrokerService brokerService = new IdentityBrokerService(realm);
         ResteasyProviderFactory.getInstance().injectProperties(brokerService);
@@ -195,12 +171,12 @@ public class RealmsResource {
     @GET
     @Path("{realm}/.well-known/{provider}")
     @Produces(MediaType.APPLICATION_JSON)
-    public Response getWellKnown(final @PathParam("realm") String realmName,
+    public Response getWellKnown(final @PathParam("realm") String name,
                               final @PathParam("provider") String providerName) {
-        RealmManager realmManager = new RealmManager(session);
-        RealmModel realm = locateRealm(realmName, realmManager);
+        init(name);
+
         WellKnownProvider wellKnown = session.getProvider(WellKnownProvider.class, providerName);
-        return Response.ok(wellKnown.getConfig(realm, uriInfo)).build();
+        return Response.ok(wellKnown.getConfig()).build();
     }
 
 }
diff --git a/services/src/main/java/org/keycloak/wellknown/WellKnownProvider.java b/services/src/main/java/org/keycloak/wellknown/WellKnownProvider.java
index d4b80d7..ed9021f 100755
--- a/services/src/main/java/org/keycloak/wellknown/WellKnownProvider.java
+++ b/services/src/main/java/org/keycloak/wellknown/WellKnownProvider.java
@@ -1,16 +1,12 @@
 package org.keycloak.wellknown;
 
-import org.keycloak.models.RealmModel;
 import org.keycloak.provider.Provider;
 
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.UriInfo;
-
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
 public interface WellKnownProvider extends Provider {
 
-    Object getConfig(RealmModel realm, UriInfo uriInfo);
+    Object getConfig();
 
 }
diff --git a/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterIdentityProvider.java b/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterIdentityProvider.java
index b209fce..bda5d8e 100755
--- a/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterIdentityProvider.java
+++ b/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterIdentityProvider.java
@@ -37,7 +37,7 @@ import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.services.managers.ClientSessionCode;
 import org.keycloak.services.messages.Messages;
-import org.keycloak.services.resources.flows.Flows;
+import org.keycloak.services.ErrorPage;
 import org.keycloak.social.SocialIdentityProvider;
 import twitter4j.Twitter;
 import twitter4j.TwitterFactory;
@@ -159,7 +159,7 @@ public class TwitterIdentityProvider extends AbstractIdentityProvider<OAuth2Iden
             EventBuilder event = new EventBuilder(realm, session, clientConnection);
             event.event(EventType.LOGIN);
             event.error("twitter_login_failed");
-            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.UNEXPECTED_ERROR_HANDLING_RESPONSE);
+            return ErrorPage.error(session, Messages.UNEXPECTED_ERROR_HANDLING_RESPONSE);
         }
 
         private ClientSessionCode parseClientSessionCode(String code) {
diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml
index 2cefe1b..8aa46ca 100755
--- a/testsuite/integration/pom.xml
+++ b/testsuite/integration/pom.xml
@@ -44,6 +44,11 @@
             <version>${project.version}</version>
         </dependency>
         <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>event-listener-sysout-example</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
             <groupId>log4j</groupId>
             <artifactId>log4j</artifactId>
         </dependency>
diff --git a/testsuite/integration/src/main/resources/log4j.properties b/testsuite/integration/src/main/resources/log4j.properties
index cfd1259..d94d2f9 100755
--- a/testsuite/integration/src/main/resources/log4j.properties
+++ b/testsuite/integration/src/main/resources/log4j.properties
@@ -6,6 +6,9 @@ log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%c] %m%n
 
 log4j.logger.org.keycloak=info
 
+# Enable to view events
+# log4j.logger.org.keycloak.events=debug
+
 # Enable to view loaded SPI and Providers
 # log4j.logger.org.keycloak.services.DefaultKeycloakSessionFactory=debug
 # log4j.logger.org.keycloak.provider.ProviderManager=debug
diff --git a/testsuite/integration/src/main/resources/META-INF/keycloak-server.json b/testsuite/integration/src/main/resources/META-INF/keycloak-server.json
index 188db1e..2c56c58 100755
--- a/testsuite/integration/src/main/resources/META-INF/keycloak-server.json
+++ b/testsuite/integration/src/main/resources/META-INF/keycloak-server.json
@@ -7,6 +7,13 @@
         "provider": "${keycloak.eventStore.provider:jpa}"
     },
 
+    "eventsListener": {
+        "jboss-logging" : {
+            "success-level": "debug",
+            "error-level": "warn"
+        }
+    },
+
     "realm": {
         "provider": "${keycloak.realm.provider:jpa}"
     },
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
index 999fdb8..91ced7f 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
@@ -229,7 +229,7 @@ public class AccountTest {
     }
 
     @Test
-    public void changePasswordWithPasswordPolicy() {
+    public void changePasswordWithLengthPasswordPolicy() {
         keycloakRule.update(new KeycloakRule.KeycloakSetup() {
             @Override
             public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
@@ -262,6 +262,55 @@ public class AccountTest {
             });
         }
     }
+    
+    @Test
+    public void changePasswordWithPasswordHistoryPolicy() {
+        keycloakRule.update(new KeycloakRule.KeycloakSetup() {
+            @Override
+            public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+                appRealm.setPasswordPolicy(new PasswordPolicy("passwordHistory(2)"));
+            }
+        });
+
+        try {
+            changePasswordPage.open();
+            loginPage.login("test-user@localhost", "password");
+
+            events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT + "?path=password").assertEvent();
+
+            changePasswordPage.changePassword("password", "password", "password");
+
+            Assert.assertEquals("Invalid password: must not be equal to any of last 2 passwords.", profilePage.getError());
+
+            changePasswordPage.changePassword("password", "password1", "password1");
+
+            Assert.assertEquals("Your password has been updated.", profilePage.getSuccess());
+            
+            events.expectAccount(EventType.UPDATE_PASSWORD).assertEvent();
+            
+            changePasswordPage.changePassword("password1", "password", "password");
+
+            Assert.assertEquals("Invalid password: must not be equal to any of last 2 passwords.", profilePage.getError());
+
+            changePasswordPage.changePassword("password1", "password1", "password1");
+
+            Assert.assertEquals("Invalid password: must not be equal to any of last 2 passwords.", profilePage.getError());
+            
+            changePasswordPage.changePassword("password1", "password2", "password2");
+
+            Assert.assertEquals("Your password has been updated.", profilePage.getSuccess());
+
+            events.expectAccount(EventType.UPDATE_PASSWORD).assertEvent();
+            
+        } finally {
+            keycloakRule.update(new KeycloakRule.KeycloakSetup() {
+                @Override
+                public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+                    appRealm.setPasswordPolicy(new PasswordPolicy(null));
+                }
+            });
+        }
+    }
 
     @Test
     public void changeProfile() {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java
index ba41ceb..550de17 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java
@@ -51,6 +51,7 @@ import org.keycloak.testsuite.rule.WebResource;
 import org.keycloak.testsuite.rule.WebRule;
 import org.keycloak.testutils.KeycloakServer;
 import org.keycloak.util.BasicAuthHelper;
+import org.keycloak.util.Time;
 import org.openqa.selenium.WebDriver;
 
 import javax.ws.rs.client.Client;
@@ -303,7 +304,7 @@ public class AdapterTestStrategy extends ExternalResource {
         session.getTransaction().commit();
         session.close();
 
-        Thread.sleep(2000);
+        Time.setOffset(2);
 
 
         // test SSO
@@ -315,6 +316,8 @@ public class AdapterTestStrategy extends ExternalResource {
         realm.setSsoSessionIdleTimeout(originalIdle);
         session.getTransaction().commit();
         session.close();
+
+        Time.setOffset(0);
     }
 
     public void testLoginSSOIdleRemoveExpiredUserSessions() throws Exception {
@@ -336,7 +339,7 @@ public class AdapterTestStrategy extends ExternalResource {
         session.getTransaction().commit();
         session.close();
 
-        Thread.sleep(2000);
+        Time.setOffset(2);
 
         session = keycloakRule.startSession();
         realm = session.realms().getRealmByName("demo");
@@ -356,6 +359,8 @@ public class AdapterTestStrategy extends ExternalResource {
         realm.setSsoSessionIdleTimeout(originalIdle);
         session.getTransaction().commit();
         session.close();
+
+        Time.setOffset(0);
     }
 
     public void testLoginSSOMax() throws Exception {
@@ -377,7 +382,7 @@ public class AdapterTestStrategy extends ExternalResource {
         session.getTransaction().commit();
         session.close();
 
-        Thread.sleep(2000);
+        Time.setOffset(2);
 
 
         // test SSO
@@ -389,6 +394,8 @@ public class AdapterTestStrategy extends ExternalResource {
         realm.setSsoSessionMaxLifespan(original);
         session.getTransaction().commit();
         session.close();
+
+        Time.setOffset(0);
     }
 
     /**
@@ -541,7 +548,7 @@ public class AdapterTestStrategy extends ExternalResource {
         driver.navigate().to(logoutUri);
 
         // Wait until accessToken is expired
-        Thread.sleep(2000);
+        Time.setOffset(2);
 
         // Assert that http session was invalidated
         driver.navigate().to(APP_SERVER_BASE_URL + "/session-portal");
@@ -563,6 +570,8 @@ public class AdapterTestStrategy extends ExternalResource {
             }
 
         }, "demo");
+
+        Time.setOffset(0);
     }
 
     /**
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
index 9026ebc..ea06aac 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
@@ -38,7 +38,7 @@ import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserModel.RequiredAction;
 import org.keycloak.representations.IDToken;
-import org.keycloak.services.resources.flows.Urls;
+import org.keycloak.services.Urls;
 import org.keycloak.testsuite.OAuthClient;
 import org.keycloak.testsuite.OAuthClient.AccessTokenResponse;
 import org.keycloak.testsuite.broker.util.UserSessionStatusServlet.UserSessionStatus;
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java
index 1b53664..f4e2c93 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java
@@ -56,8 +56,7 @@ import javax.mail.internet.MimeMessage;
 import java.io.IOException;
 import java.util.Collections;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.*;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@@ -240,6 +239,74 @@ public class ResetPasswordTest {
         assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
     }
 
+    private void resetPassword(String username, String password) throws IOException, MessagingException {
+        loginPage.open();
+        loginPage.resetPassword();
+
+        resetPasswordPage.assertCurrent();
+
+        resetPasswordPage.changePassword(username);
+
+        resetPasswordPage.assertCurrent();
+
+        String sessionId = events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).user(userId)
+                .detail(Details.USERNAME, username).detail(Details.EMAIL, "login@test.com").assertEvent().getSessionId();
+
+        assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
+
+        MimeMessage message = greenMail.getReceivedMessages()[greenMail.getReceivedMessages().length - 1];
+
+        String body = (String) message.getContent();
+        String changePasswordUrl = MailUtil.getLink(body);
+
+        driver.navigate().to(changePasswordUrl.trim());
+
+        updatePasswordPage.assertCurrent();
+
+        updatePasswordPage.changePassword(password, password);
+
+        events.expectRequiredAction(EventType.UPDATE_PASSWORD).user(userId).session(sessionId)
+                .detail(Details.USERNAME, username).assertEvent();
+
+        assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
+
+        events.expectLogin().user(userId).detail(Details.USERNAME, username).session(sessionId).assertEvent();
+
+        oauth.openLogout();
+
+        events.expectLogout(sessionId).user(userId).session(sessionId).assertEvent();
+    }
+
+    private void resetPasswordInvalidPassword(String username, String password, String error) throws IOException, MessagingException {
+        loginPage.open();
+        loginPage.resetPassword();
+
+        resetPasswordPage.assertCurrent();
+
+        resetPasswordPage.changePassword(username);
+
+        resetPasswordPage.assertCurrent();
+
+        events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).user(userId)
+                .detail(Details.USERNAME, username).detail(Details.EMAIL, "login@test.com").assertEvent().getSessionId();
+
+        assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
+
+        MimeMessage message = greenMail.getReceivedMessages()[greenMail.getReceivedMessages().length - 1];
+
+        String body = (String) message.getContent();
+        String changePasswordUrl = MailUtil.getLink(body);
+
+        driver.navigate().to(changePasswordUrl.trim());
+
+        updatePasswordPage.assertCurrent();
+
+        updatePasswordPage.changePassword(password, password);
+
+        assertTrue(updatePasswordPage.isCurrent());
+        assertEquals(error, updatePasswordPage.getError());
+    }
+
     @Test
     public void resetPasswordWrongEmail() throws IOException, MessagingException, InterruptedException {
         loginPage.open();
@@ -253,8 +320,6 @@ public class ResetPasswordTest {
 
         assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
 
-        Thread.sleep(1000);
-
         assertEquals(0, greenMail.getReceivedMessages().length);
 
         events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).user((String) null).session((String) null).detail(Details.USERNAME, "invalid").removeDetail(Details.EMAIL).removeDetail(Details.CODE_ID).error("user_not_found").assertEvent();
@@ -318,8 +383,6 @@ public class ResetPasswordTest {
 
             assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
 
-            Thread.sleep(1000);
-
             assertEquals(0, greenMail.getReceivedMessages().length);
 
             events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).session((String) null).user(userId).detail(Details.USERNAME, "login-test").removeDetail(Details.CODE_ID).error("user_disabled").assertEvent();
@@ -358,8 +421,6 @@ public class ResetPasswordTest {
 
             assertEquals("You should receive an email shortly with further instructions.", resetPasswordPage.getSuccessMessage());
 
-            Thread.sleep(1000);
-
             assertEquals(0, greenMail.getReceivedMessages().length);
 
             events.expectRequiredAction(EventType.SEND_RESET_PASSWORD_ERROR).session((String) null).user(userId).detail(Details.USERNAME, "login-test").removeDetail(Details.CODE_ID).error("invalid_email").assertEvent();
@@ -396,8 +457,6 @@ public class ResetPasswordTest {
 
             assertEquals("Failed to send email, please try again later.", errorPage.getError());
 
-            Thread.sleep(1000);
-
             assertEquals(0, greenMail.getReceivedMessages().length);
 
             events.expectRequiredAction(EventType.SEND_RESET_PASSWORD_ERROR).user(userId).detail(Details.USERNAME, "login-test").removeDetail(Details.CODE_ID).error(Errors.EMAIL_SEND_FAILED).assertEvent();
@@ -412,7 +471,7 @@ public class ResetPasswordTest {
     }
 
     @Test
-    public void resetPasswordWithPasswordPolicy() throws IOException, MessagingException {
+    public void resetPasswordWithLengthPasswordPolicy() throws IOException, MessagingException {
         keycloakRule.update(new KeycloakRule.KeycloakSetup() {
             @Override
             public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
@@ -470,6 +529,31 @@ public class ResetPasswordTest {
     }
 
     @Test
+    public void resetPasswordWithPasswordHisoryPolicy() throws IOException, MessagingException {
+        keycloakRule.update(new KeycloakRule.KeycloakSetup() {
+            @Override
+            public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+                //Block passwords that are equal to previous passwords. Default value is 3.
+                appRealm.setPasswordPolicy(new PasswordPolicy("passwordHistory"));
+            }
+        });
+        
+        resetPassword("login-test", "password1");
+        resetPasswordInvalidPassword("login-test", "password1", "Invalid password: must not be equal to any of last 3 passwords.");
+
+        resetPassword("login-test", "password2");
+        resetPasswordInvalidPassword("login-test", "password1", "Invalid password: must not be equal to any of last 3 passwords.");
+        resetPasswordInvalidPassword("login-test", "password2", "Invalid password: must not be equal to any of last 3 passwords.");
+
+        resetPassword("login-test", "password3");
+        resetPasswordInvalidPassword("login-test", "password1", "Invalid password: must not be equal to any of last 3 passwords.");
+        resetPasswordInvalidPassword("login-test", "password2", "Invalid password: must not be equal to any of last 3 passwords.");
+        resetPasswordInvalidPassword("login-test", "password3", "Invalid password: must not be equal to any of last 3 passwords.");
+
+        resetPassword("login-test", "password");
+    }
+
+    @Test
     public void resetPasswordNewBrowserSession() throws IOException, MessagingException {
         String username = "login-test";
 
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AuthenticationManagerTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AuthenticationManagerTest.java
index d43e636..15aeffa 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AuthenticationManagerTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AuthenticationManagerTest.java
@@ -18,6 +18,7 @@ import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.managers.AuthenticationManager.AuthenticationStatus;
 import org.keycloak.services.managers.BruteForceProtector;
+import org.keycloak.util.Time;
 
 import javax.ws.rs.core.MultivaluedMap;
 import java.util.UUID;
@@ -238,10 +239,12 @@ public class AuthenticationManagerTest extends AbstractModelTest {
             String passwordToken = new JWSBuilder().jsonContent(new PasswordToken(realm.getName(), "invalid")).rsa256(realm.getPrivateKey());
             formData.add(CredentialRepresentation.PASSWORD_TOKEN, passwordToken);
 
-            Thread.sleep(2000);
+            Time.setOffset(2);
 
             AuthenticationStatus status = am.authenticateForm(session, dummyConnection, realm, formData);
             Assert.assertEquals(AuthenticationStatus.INVALID_CREDENTIALS, status);
+
+            Time.setOffset(0);
         } finally {
             realm.setAccessCodeLifespanUserAction(lifespan);
         }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java
index 1fd4528..0920be5 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java
@@ -55,6 +55,7 @@ import org.keycloak.testsuite.rule.KeycloakRule;
 import org.keycloak.testsuite.rule.WebResource;
 import org.keycloak.testsuite.rule.WebRule;
 import org.keycloak.util.BasicAuthHelper;
+import org.keycloak.util.Time;
 import org.openqa.selenium.WebDriver;
 
 import javax.ws.rs.client.Client;
@@ -211,10 +212,7 @@ public class AccessTokenTest {
 
         String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
 
-        try {
-            Thread.sleep(2000);
-        } catch (InterruptedException e) {
-        }
+        Time.setOffset(2);
 
         OAuthClient.AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password");
         Assert.assertEquals(400, response.getStatusCode());
@@ -231,6 +229,8 @@ public class AccessTokenTest {
                 appRealm.setAccessCodeLifespan(60);
             }
         });
+
+        Time.setOffset(0);
     }
 
     @Test
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java
index 3776c03..363a1e9 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java
@@ -143,7 +143,7 @@ public class RefreshTokenTest {
 
         Assert.assertEquals(sessionId, refreshToken.getSessionState());
 
-        Thread.sleep(2000);
+        Time.setOffset(2);
 
         AccessTokenResponse response = oauth.doRefreshTokenRequest(refreshTokenString, "password");
         AccessToken refreshedToken = oauth.verifyToken(response.getAccessToken());
@@ -157,8 +157,8 @@ public class RefreshTokenTest {
         Assert.assertThat(response.getExpiresIn(), allOf(greaterThanOrEqualTo(250), lessThanOrEqualTo(300)));
         Assert.assertThat(refreshedToken.getExpiration() - Time.currentTime(), allOf(greaterThanOrEqualTo(250), lessThanOrEqualTo(300)));
 
-        Assert.assertThat(refreshedToken.getExpiration() - token.getExpiration(), allOf(greaterThanOrEqualTo(1), lessThanOrEqualTo(5)));
-        Assert.assertThat(refreshedRefreshToken.getExpiration() - refreshToken.getExpiration(), allOf(greaterThanOrEqualTo(1), lessThanOrEqualTo(5)));
+        Assert.assertThat(refreshedToken.getExpiration() - token.getExpiration(), allOf(greaterThanOrEqualTo(1), lessThanOrEqualTo(10)));
+        Assert.assertThat(refreshedRefreshToken.getExpiration() - refreshToken.getExpiration(), allOf(greaterThanOrEqualTo(1), lessThanOrEqualTo(10)));
 
         Assert.assertNotEquals(token.getId(), refreshedToken.getId());
         Assert.assertNotEquals(refreshToken.getId(), refreshedRefreshToken.getId());
@@ -177,6 +177,8 @@ public class RefreshTokenTest {
         Event refreshEvent = events.expectRefresh(tokenEvent.getDetails().get(Details.REFRESH_TOKEN_ID), sessionId).assertEvent();
         Assert.assertNotEquals(tokenEvent.getDetails().get(Details.TOKEN_ID), refreshEvent.getDetails().get(Details.TOKEN_ID));
         Assert.assertNotEquals(tokenEvent.getDetails().get(Details.REFRESH_TOKEN_ID), refreshEvent.getDetails().get(Details.UPDATED_REFRESH_TOKEN_ID));
+
+        Time.setOffset(0);
     }
 
     PrivateKey privateKey;
@@ -277,7 +279,7 @@ public class RefreshTokenTest {
         session.getTransaction().commit();
         session.close();
 
-        Thread.sleep(2000);
+        Time.setOffset(2);
 
         tokenResponse = oauth.doRefreshTokenRequest(tokenResponse.getRefreshToken(), "password");
 
@@ -302,7 +304,7 @@ public class RefreshTokenTest {
         session.getTransaction().commit();
         session.close();
 
-        Thread.sleep(2000);
+        Time.setOffset(4);
         tokenResponse = oauth.doRefreshTokenRequest(tokenResponse.getRefreshToken(), "password");
 
         session = keycloakRule.startSession();
@@ -323,7 +325,7 @@ public class RefreshTokenTest {
         session.close();
 
         events.clear();
-        Thread.sleep(2000);
+        Time.setOffset(6);
         tokenResponse = oauth.doRefreshTokenRequest(tokenResponse.getRefreshToken(), "password");
 
         // test idle timeout
@@ -341,6 +343,8 @@ public class RefreshTokenTest {
         session.close();
 
         events.clear();
+
+        Time.setOffset(0);
     }
 
     @Test
@@ -365,7 +369,7 @@ public class RefreshTokenTest {
         session.getTransaction().commit();
         session.close();
 
-        Thread.sleep(1000);
+        Time.setOffset(1);
 
         tokenResponse = oauth.doRefreshTokenRequest(tokenResponse.getRefreshToken(), "password");
 
@@ -383,6 +387,8 @@ public class RefreshTokenTest {
         events.expectRefresh(refreshId, sessionId).error(Errors.INVALID_TOKEN);
 
         events.clear();
+
+        Time.setOffset(0);
     }
 
     @Test
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountFederatedIdentityPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountFederatedIdentityPage.java
index 2e3556e..4c02798 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountFederatedIdentityPage.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountFederatedIdentityPage.java
@@ -2,7 +2,7 @@ package org.keycloak.testsuite.pages;
 
 import javax.ws.rs.core.UriBuilder;
 
-import org.keycloak.services.resources.flows.Urls;
+import org.keycloak.services.Urls;
 import org.keycloak.testsuite.Constants;
 import org.openqa.selenium.By;
 
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountLogPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountLogPage.java
index 846b526..2a7c307 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountLogPage.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountLogPage.java
@@ -21,7 +21,7 @@
  */
 package org.keycloak.testsuite.pages;
 
-import org.keycloak.services.resources.flows.Urls;
+import org.keycloak.services.Urls;
 import org.keycloak.testsuite.Constants;
 import org.openqa.selenium.By;
 import org.openqa.selenium.WebElement;
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountSessionsPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountSessionsPage.java
index 5467a48..bfadc6d 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountSessionsPage.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountSessionsPage.java
@@ -21,7 +21,7 @@
  */
 package org.keycloak.testsuite.pages;
 
-import org.keycloak.services.resources.flows.Urls;
+import org.keycloak.services.Urls;
 import org.keycloak.testsuite.Constants;
 import org.openqa.selenium.By;
 import org.openqa.selenium.WebElement;
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java
index 1f0b7a2..2be207c 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java
@@ -103,11 +103,6 @@ public class LoginPage extends AbstractPage {
         return loginErrorMessage != null ? loginErrorMessage.getText() : null;
     }
 
-    public String getWarning() {
-        return loginWarningMessage != null ? loginWarningMessage.getText() : null;
-    }
-
-
     public boolean isCurrent() {
         return driver.getTitle().equals("Log in to test") || driver.getTitle().equals("Anmeldung bei test");
     }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPasswordUpdatePage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPasswordUpdatePage.java
index be5df8c..d8adea4 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPasswordUpdatePage.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPasswordUpdatePage.java
@@ -38,6 +38,9 @@ public class LoginPasswordUpdatePage extends AbstractPage {
     @FindBy(css = "input[type=\"submit\"]")
     private WebElement submitButton;
 
+    @FindBy(className = "feedback-error")
+    private WebElement loginErrorMessage;
+
     public void changePassword(String newPassword, String passwordConfirm) {
         newPasswordInput.sendKeys(newPassword);
         passwordConfirmInput.sendKeys(passwordConfirm);
@@ -53,4 +56,8 @@ public class LoginPasswordUpdatePage extends AbstractPage {
         throw new UnsupportedOperationException();
     }
 
+    public String getError() {
+        return loginErrorMessage != null ? loginErrorMessage.getText() : null;
+    }
+
 }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlBindingTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlBindingTest.java
index e91d58c..5470041 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlBindingTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlBindingTest.java
@@ -126,11 +126,6 @@ public class SamlBindingTest {
     @WebResource
     protected LoginPage loginPage;
 
-    //@Test
-    public void runit() throws Exception {
-        Thread.sleep(10000000);
-    }
-
     protected void checkLoggedOut(String mainUrl) {
         String pageSource = driver.getPageSource();
         System.out.println("*** logout pagesouce ***");