keycloak-aplcache

KEYCLOAK-6335

1/23/2018 3:09:49 PM

Changes

Details

diff --git a/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java
index a7ca83e..77c2c45 100755
--- a/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java
@@ -56,6 +56,7 @@ public class ClientRepresentation {
     protected Boolean frontchannelLogout;
     protected String protocol;
     protected Map<String, String> attributes;
+    protected Map<String, String> authenticationFlowBindingOverrides;
     protected Boolean fullScopeAllowed;
     protected Integer nodeReRegistrationTimeout;
     protected Map<String, Integer> registeredNodes;
@@ -296,6 +297,14 @@ public class ClientRepresentation {
         this.attributes = attributes;
     }
 
+    public Map<String, String> getAuthenticationFlowBindingOverrides() {
+        return authenticationFlowBindingOverrides;
+    }
+
+    public void setAuthenticationFlowBindingOverrides(Map<String, String> authenticationFlowBindingOverrides) {
+        this.authenticationFlowBindingOverrides = authenticationFlowBindingOverrides;
+    }
+
     public Integer getNodeReRegistrationTimeout() {
         return nodeReRegistrationTimeout;
     }
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java
index 14f4c0f..a5823ce 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java
@@ -346,6 +346,34 @@ public class ClientAdapter implements ClientModel {
     }
 
     @Override
+    public void setAuthenticationFlowBindingOverride(String name, String value) {
+        getDelegateForUpdate();
+        updated.setAuthenticationFlowBindingOverride(name, value);
+
+    }
+
+    @Override
+    public void removeAuthenticationFlowBindingOverride(String name) {
+        getDelegateForUpdate();
+        updated.removeAuthenticationFlowBindingOverride(name);
+
+    }
+
+    @Override
+    public String getAuthenticationFlowBindingOverride(String name) {
+        if (isUpdated()) return updated.getAuthenticationFlowBindingOverride(name);
+        return cached.getAuthFlowBindings().get(name);
+    }
+
+    @Override
+    public Map<String, String> getAuthenticationFlowBindingOverrides() {
+        if (isUpdated()) return updated.getAuthenticationFlowBindingOverrides();
+        Map<String, String> copy = new HashMap<String, String>();
+        copy.putAll(cached.getAuthFlowBindings());
+        return copy;
+    }
+
+    @Override
     public Set<ProtocolMapperModel> getProtocolMappers() {
         if (isUpdated()) return updated.getProtocolMappers();
         return cached.getProtocolMappers();
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedClient.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedClient.java
index b4b6729..787dc45 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedClient.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedClient.java
@@ -46,6 +46,7 @@ public class CachedClient extends AbstractRevisioned implements InRealm {
     protected String registrationToken;
     protected String protocol;
     protected Map<String, String> attributes = new HashMap<String, String>();
+    protected Map<String, String> authFlowBindings = new HashMap<String, String>();
     protected boolean publicClient;
     protected boolean fullScopeAllowed;
     protected boolean frontchannelLogout;
@@ -83,6 +84,7 @@ public class CachedClient extends AbstractRevisioned implements InRealm {
         enabled = model.isEnabled();
         protocol = model.getProtocol();
         attributes.putAll(model.getAttributes());
+        authFlowBindings.putAll(model.getAuthenticationFlowBindingOverrides());
         notBefore = model.getNotBefore();
         frontchannelLogout = model.isFrontchannelLogout();
         publicClient = model.isPublicClient();
@@ -256,4 +258,8 @@ public class CachedClient extends AbstractRevisioned implements InRealm {
     public boolean isUseTemplateMappers() {
         return useTemplateMappers;
     }
+
+    public Map<String, String> getAuthFlowBindings() {
+        return authFlowBindings;
+    }
 }
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 ac1a738..3a7eabb 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
@@ -271,6 +271,29 @@ public class ClientAdapter implements ClientModel, JpaModel<ClientEntity> {
     }
 
     @Override
+    public void setAuthenticationFlowBindingOverride(String name, String value) {
+        entity.getAuthFlowBindings().put(name, value);
+
+    }
+
+    @Override
+    public void removeAuthenticationFlowBindingOverride(String name) {
+        entity.getAuthFlowBindings().remove(name);
+    }
+
+    @Override
+    public String getAuthenticationFlowBindingOverride(String name) {
+        return entity.getAuthFlowBindings().get(name);
+    }
+
+    @Override
+    public Map<String, String> getAuthenticationFlowBindingOverrides() {
+        Map<String, String> copy = new HashMap<>();
+        copy.putAll(entity.getAuthFlowBindings());
+        return copy;
+    }
+
+    @Override
     public void setAttribute(String name, String value) {
         entity.getAttributes().put(name, value);
 
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 dab3fe4..7f88977 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
@@ -119,6 +119,12 @@ public class ClientEntity {
     @CollectionTable(name="CLIENT_ATTRIBUTES", joinColumns={ @JoinColumn(name="CLIENT_ID") })
     protected Map<String, String> attributes = new HashMap<String, String>();
 
+    @ElementCollection
+    @MapKeyColumn(name="BINDING_NAME")
+    @Column(name="FLOW_ID", length = 4000)
+    @CollectionTable(name="CLIENT_AUTH_FLOW_BINDINGS", joinColumns={ @JoinColumn(name="CLIENT_ID") })
+    protected Map<String, String> authFlowBindings = new HashMap<String, String>();
+
     @OneToMany(fetch = FetchType.LAZY, mappedBy = "client", cascade = CascadeType.REMOVE)
     Collection<ClientIdentityProviderMappingEntity> identityProviders = new ArrayList<ClientIdentityProviderMappingEntity>();
 
@@ -292,6 +298,14 @@ public class ClientEntity {
         this.attributes = attributes;
     }
 
+    public Map<String, String> getAuthFlowBindings() {
+        return authFlowBindings;
+    }
+
+    public void setAuthFlowBindings(Map<String, String> authFlowBindings) {
+        this.authFlowBindings = authFlowBindings;
+    }
+
     public String getProtocol() {
         return protocol;
     }
diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-4.0.0.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-4.0.0.xml
new file mode 100644
index 0000000..78c606a
--- /dev/null
+++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-4.0.0.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+  ~ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+  ~ * and other contributors as indicated by the @author tags.
+  ~ *
+  ~ * Licensed under the Apache License, Version 2.0 (the "License");
+  ~ * you may not use this file except in compliance with the License.
+  ~ * You may obtain a copy of the License at
+  ~ *
+  ~ * http://www.apache.org/licenses/LICENSE-2.0
+  ~ *
+  ~ * Unless required by applicable law or agreed to in writing, software
+  ~ * distributed under the License is distributed on an "AS IS" BASIS,
+  ~ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ * See the License for the specific language governing permissions and
+  ~ * limitations under the License.
+  -->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
+
+    <changeSet author="bburke@redhat.com" id="4.0.0-KEYCLOAK-6335">
+        <createTable tableName="CLIENT_AUTH_FLOW_BINDINGS">
+            <column name="CLIENT_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="FLOW_ID" type="VARCHAR(36)"/>
+            <column name="BINDING_NAME" type="VARCHAR(255)">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+        <addPrimaryKey columnNames="CLIENT_ID, BINDING_NAME" constraintName="CONSTRAINT_CLIENT_AUTH_FLOW_BINDINGS" tableName="CLIENT_AUTH_FLOW_BINDINGS"/>
+    </changeSet>
+</databaseChangeLog>
diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml
index d89aa96..fa824e2 100755
--- a/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml
+++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml
@@ -53,4 +53,5 @@
     <include file="META-INF/jpa-changelog-3.4.0.xml"/>
     <include file="META-INF/jpa-changelog-3.4.1.xml"/>
     <include file="META-INF/jpa-changelog-3.4.2.xml"/>
+    <include file="META-INF/jpa-changelog-4.0.0.xml"/>
 </databaseChangeLog>
diff --git a/server-spi/src/main/java/org/keycloak/models/AuthenticationFlowBindings.java b/server-spi/src/main/java/org/keycloak/models/AuthenticationFlowBindings.java
new file mode 100644
index 0000000..2515472
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/models/AuthenticationFlowBindings.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.models;
+
+/**
+ * Defines constants for authentication flow bindings.  Strings used for lookup
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface AuthenticationFlowBindings {
+    String BROWSER_BINDING = "browser";
+    String DIRECT_GRANT_BINDING = "direct_grant";
+}
diff --git a/server-spi/src/main/java/org/keycloak/models/ClientModel.java b/server-spi/src/main/java/org/keycloak/models/ClientModel.java
index 5f4403f..aa406cf 100755
--- a/server-spi/src/main/java/org/keycloak/models/ClientModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/ClientModel.java
@@ -118,6 +118,18 @@ public interface ClientModel extends RoleContainerModel,  ProtocolMapperContaine
     String getAttribute(String name);
     Map<String, String> getAttributes();
 
+    /**
+     * Get authentication flow binding override for this client.  Allows client to override an authentication flow binding.
+     *
+     * @param binding examples are "browser", "direct_grant"
+     *
+     * @return
+     */
+    public String getAuthenticationFlowBindingOverride(String binding);
+    public Map<String, String> getAuthenticationFlowBindingOverrides();
+    public void removeAuthenticationFlowBindingOverride(String binding);
+    public void setAuthenticationFlowBindingOverride(String binding, String flowId);
+
     boolean isFrontchannelLogout();
     void setFrontchannelLogout(boolean flag);
 
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/AuthenticationFlowResolver.java b/server-spi-private/src/main/java/org/keycloak/models/utils/AuthenticationFlowResolver.java
new file mode 100644
index 0000000..1a27dd8
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/AuthenticationFlowResolver.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.models.utils;
+
+import org.keycloak.models.AuthenticationFlowBindings;
+import org.keycloak.models.AuthenticationFlowModel;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ModelException;
+import org.keycloak.sessions.AuthenticationSessionModel;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class AuthenticationFlowResolver {
+
+    public static AuthenticationFlowModel resolveBrowserFlow(AuthenticationSessionModel authSession) {
+        AuthenticationFlowModel flow = null;
+        ClientModel client = authSession.getClient();
+        String clientFlow = client.getAuthenticationFlowBindingOverride(AuthenticationFlowBindings.BROWSER_BINDING);
+        if (clientFlow != null) {
+            flow = authSession.getRealm().getAuthenticationFlowById(clientFlow);
+            if (flow == null) {
+                throw new ModelException("Client " + client.getClientId() + " has browser flow override, but this flow does not exist");
+            }
+            return flow;
+        }
+        return authSession.getRealm().getBrowserFlow();
+    }
+    public static AuthenticationFlowModel resolveDirectGrantFlow(AuthenticationSessionModel authSession) {
+        AuthenticationFlowModel flow = null;
+        ClientModel client = authSession.getClient();
+        String clientFlow = client.getAuthenticationFlowBindingOverride(AuthenticationFlowBindings.DIRECT_GRANT_BINDING);
+        if (clientFlow != null) {
+            flow = authSession.getRealm().getAuthenticationFlowById(clientFlow);
+            if (flow == null) {
+                throw new ModelException("Client " + client.getClientId() + " has direct grant flow override, but this flow does not exist");
+            }
+            return flow;
+        }
+        return authSession.getRealm().getDirectGrantFlow();
+    }
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
index aa6b42c..0dad16a 100755
--- a/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
@@ -494,6 +494,7 @@ public class ModelToRepresentation {
         rep.setFrontchannelLogout(clientModel.isFrontchannelLogout());
         rep.setProtocol(clientModel.getProtocol());
         rep.setAttributes(clientModel.getAttributes());
+        rep.setAuthenticationFlowBindingOverrides(clientModel.getAuthenticationFlowBindingOverrides());
         rep.setFullScopeAllowed(clientModel.isFullScopeAllowed());
         rep.setBearerOnly(clientModel.isBearerOnly());
         rep.setConsentRequired(clientModel.isConsentRequired());
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index 34ee766..5fb5003 100755
--- a/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -1084,6 +1084,17 @@ public class RepresentationToModel {
         }
 
 
+        if (resourceRep.getAuthenticationFlowBindingOverrides() != null) {
+            for (Map.Entry<String, String> entry : resourceRep.getAuthenticationFlowBindingOverrides().entrySet()) {
+                if (entry.getValue() == null || entry.getValue().trim().equals("")) {
+                    continue;
+                } else {
+                    client.setAuthenticationFlowBindingOverride(entry.getKey(), entry.getValue());
+                }
+            }
+        }
+
+
         if (resourceRep.getRedirectUris() != null) {
             for (String redirectUri : resourceRep.getRedirectUris()) {
                 client.addRedirectUri(redirectUri);
@@ -1201,6 +1212,22 @@ public class RepresentationToModel {
                 resource.setAttribute(entry.getKey(), entry.getValue());
             }
         }
+        if (rep.getAttributes() != null) {
+            for (Map.Entry<String, String> entry : rep.getAttributes().entrySet()) {
+                resource.setAttribute(entry.getKey(), entry.getValue());
+            }
+        }
+
+        if (rep.getAuthenticationFlowBindingOverrides() != null) {
+            for (Map.Entry<String, String> entry : rep.getAuthenticationFlowBindingOverrides().entrySet()) {
+                if (entry.getValue() == null || entry.getValue().trim().equals("")) {
+                    resource.removeAuthenticationFlowBindingOverride(entry.getKey());
+                } else {
+                    resource.setAuthenticationFlowBindingOverride(entry.getKey(), entry.getValue());
+
+                }
+            }
+        }
 
 
         if (rep.getNotBefore() != null) {
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
index 7a3c3af..4464ff7 100755
--- a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
@@ -38,6 +38,7 @@ import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.utils.AuthenticationFlowResolver;
 import org.keycloak.models.utils.FormMessage;
 import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.protocol.LoginProtocol;
@@ -646,7 +647,7 @@ public class AuthenticationProcessor {
                 AuthenticationProcessor processor = new AuthenticationProcessor();
                 processor.setAuthenticationSession(clone)
                         .setFlowPath(LoginActionsService.AUTHENTICATE_PATH)
-                        .setFlowId(realm.getBrowserFlow().getId())
+                        .setFlowId(AuthenticationFlowResolver.resolveBrowserFlow(clone).getId())
                         .setForwardedErrorMessage(reset.getErrorMessage())
                         .setForwardedSuccessMessage(reset.getSuccessMessage())
                         .setConnection(connection)
diff --git a/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java b/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java
index dbed381..619a862 100755
--- a/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java
+++ b/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java
@@ -30,6 +30,7 @@ import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.utils.AuthenticationFlowResolver;
 import org.keycloak.protocol.LoginProtocol.Error;
 import org.keycloak.services.ErrorPageException;
 import org.keycloak.services.managers.AuthenticationManager;
@@ -107,7 +108,7 @@ public abstract class AuthorizationEndpointBase {
      * @return response to be returned to the browser
      */
     protected Response handleBrowserAuthenticationRequest(AuthenticationSessionModel authSession, LoginProtocol protocol, boolean isPassive, boolean redirectToAuthentication) {
-        AuthenticationFlowModel flow = getAuthenticationFlow();
+        AuthenticationFlowModel flow = getAuthenticationFlow(authSession);
         String flowId = flow.getId();
         AuthenticationProcessor processor = createProcessor(authSession, flowId, LoginActionsService.AUTHENTICATE_PATH);
         event.detail(Details.CODE_ID, authSession.getParentSession().getId());
@@ -149,8 +150,8 @@ public abstract class AuthorizationEndpointBase {
         }
     }
 
-    protected AuthenticationFlowModel getAuthenticationFlow() {
-        return realm.getBrowserFlow();
+    protected AuthenticationFlowModel getAuthenticationFlow(AuthenticationSessionModel authSession) {
+        return AuthenticationFlowResolver.resolveBrowserFlow(authSession);
     }
 
     protected void checkSsl() {
diff --git a/services/src/main/java/org/keycloak/protocol/docker/DockerEndpoint.java b/services/src/main/java/org/keycloak/protocol/docker/DockerEndpoint.java
index 0c9cb79..6ed777d 100644
--- a/services/src/main/java/org/keycloak/protocol/docker/DockerEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/docker/DockerEndpoint.java
@@ -88,7 +88,7 @@ public class DockerEndpoint extends AuthorizationEndpointBase {
     }
 
     @Override
-    protected AuthenticationFlowModel getAuthenticationFlow() {
+    protected AuthenticationFlowModel getAuthenticationFlow(AuthenticationSessionModel authSession) {
         return realm.getDockerAuthenticationFlow();
     }
 
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 ee0ff85..3be9686 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
@@ -56,6 +56,7 @@ import org.keycloak.models.KeycloakSessionFactory;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.utils.AuthenticationFlowResolver;
 import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
 import org.keycloak.protocol.oidc.TokenManager;
@@ -491,7 +492,7 @@ public class TokenEndpoint {
         authSession.setClientNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()));
         authSession.setClientNote(OIDCLoginProtocol.SCOPE_PARAM, scope);
 
-        AuthenticationFlowModel flow = realm.getDirectGrantFlow();
+        AuthenticationFlowModel flow = AuthenticationFlowResolver.resolveDirectGrantFlow(authSession);
         String flowId = flow.getId();
         AuthenticationProcessor processor = new AuthenticationProcessor();
         processor.setAuthenticationSession(authSession)
diff --git a/services/src/main/java/org/keycloak/protocol/saml/profile/ecp/SamlEcpProfileService.java b/services/src/main/java/org/keycloak/protocol/saml/profile/ecp/SamlEcpProfileService.java
index c0be2ba..cb0c367 100755
--- a/services/src/main/java/org/keycloak/protocol/saml/profile/ecp/SamlEcpProfileService.java
+++ b/services/src/main/java/org/keycloak/protocol/saml/profile/ecp/SamlEcpProfileService.java
@@ -147,7 +147,7 @@ public class SamlEcpProfileService extends SamlService {
     }
 
     @Override
-    protected AuthenticationFlowModel getAuthenticationFlow() {
+    protected AuthenticationFlowModel getAuthenticationFlow(AuthenticationSessionModel authSession) {
         for (AuthenticationFlowModel flowModel : realm.getAuthenticationFlows()) {
             if (flowModel.getAlias().equals(DefaultAuthenticationFlows.SAML_ECP_FLOW)) {
                 return flowModel;
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 33a982f..c8e3fda 100755
--- a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
+++ b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
@@ -55,6 +55,7 @@ import org.keycloak.models.RealmModel;
 import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.utils.AuthenticationFlowResolver;
 import org.keycloak.models.utils.FormMessage;
 import org.keycloak.protocol.LoginProtocol;
 import org.keycloak.protocol.LoginProtocolFactory;
@@ -1121,7 +1122,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
 
     protected Response browserAuthentication(AuthenticationSessionModel authSession, String errorMessage) {
         this.event.event(EventType.LOGIN);
-        AuthenticationFlowModel flow = realmModel.getBrowserFlow();
+        AuthenticationFlowModel flow = AuthenticationFlowResolver.resolveBrowserFlow(authSession);
         String flowId = flow.getId();
         AuthenticationProcessor processor = new AuthenticationProcessor();
         processor.setAuthenticationSession(authSession)
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 f09cfbb..fd171ee 100755
--- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
@@ -53,6 +53,7 @@ import org.keycloak.models.RealmModel;
 import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserConsentModel;
 import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.AuthenticationFlowResolver;
 import org.keycloak.models.utils.FormMessage;
 import org.keycloak.models.utils.SystemClientUtil;
 import org.keycloak.protocol.AuthorizationEndpointBase;
@@ -252,7 +253,7 @@ public class LoginActionsService {
     }
 
     protected Response processAuthentication(boolean action, String execution, AuthenticationSessionModel authSession, String errorMessage) {
-        return processFlow(action, execution, authSession, AUTHENTICATE_PATH, realm.getBrowserFlow(), errorMessage, new AuthenticationProcessor());
+        return processFlow(action, execution, authSession, AUTHENTICATE_PATH, AuthenticationFlowResolver.resolveBrowserFlow(authSession), errorMessage, new AuthenticationProcessor());
     }
 
     protected Response processFlow(boolean action, String execution, AuthenticationSessionModel authSession, String flowPath, AuthenticationFlowModel flow, String errorMessage, AuthenticationProcessor processor) {
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/forms/UsernameOnlyAuthenticator.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/forms/UsernameOnlyAuthenticator.java
new file mode 100644
index 0000000..1fd72f4
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/forms/UsernameOnlyAuthenticator.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.forms;
+
+import org.keycloak.Config;
+import org.keycloak.authentication.AuthenticationFlowContext;
+import org.keycloak.authentication.AuthenticationFlowError;
+import org.keycloak.authentication.Authenticator;
+import org.keycloak.authentication.AuthenticatorFactory;
+import org.keycloak.models.AuthenticationExecutionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.provider.ProviderConfigProperty;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class UsernameOnlyAuthenticator implements Authenticator, AuthenticatorFactory {
+    public static final String PROVIDER_ID = "testsuite-username";
+
+    @Override
+    public void authenticate(AuthenticationFlowContext context) {
+        String username = context.getHttpRequest().getDecodedFormParameters().getFirst("username");
+        UserModel user = KeycloakModelUtils.findUserByNameOrEmail(context.getSession(), context.getRealm(), username);
+        if (user == null) {
+            context.failure(AuthenticationFlowError.UNKNOWN_USER);
+            return;
+        }
+        context.setUser(user);
+        context.success();
+    }
+
+    @Override
+    public boolean requiresUser() {
+        return false;
+    }
+
+    @Override
+    public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
+        return true;
+    }
+
+    @Override
+    public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
+
+    }
+
+    @Override
+    public void action(AuthenticationFlowContext context) {
+
+    }
+
+   @Override
+    public String getDisplayType() {
+        return "Testsuite Username Only";
+    }
+
+    @Override
+    public String getReferenceCategory() {
+        return null;
+    }
+
+    @Override
+    public boolean isConfigurable() {
+        return false;
+    }
+
+    public static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
+            AuthenticationExecutionModel.Requirement.REQUIRED
+    };
+
+    @Override
+    public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
+        return REQUIREMENT_CHOICES;
+    }
+
+    @Override
+    public boolean isUserSetupAllowed() {
+        return false;
+    }
+
+    @Override
+    public String getHelpText() {
+        return "Testsuite Username authenticator.  Username parameter sets username";
+    }
+
+    @Override
+    public List<ProviderConfigProperty> getConfigProperties() {
+        return null;
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public Authenticator create(KeycloakSession session) {
+        return this;
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+
+    }
+
+    @Override
+    public String getId() {
+        return PROVIDER_ID;
+    }
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory
index 18317d0..3b28d99 100755
--- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory
@@ -19,4 +19,5 @@ org.keycloak.testsuite.forms.PassThroughAuthenticator
 org.keycloak.testsuite.forms.PassThroughRegistration
 org.keycloak.testsuite.forms.ClickThroughAuthenticator
 org.keycloak.testsuite.authentication.ExpectedParamAuthenticatorFactory
-org.keycloak.testsuite.authentication.PushButtonAuthenticatorFactory
\ No newline at end of file
+org.keycloak.testsuite.authentication.PushButtonAuthenticatorFactory
+org.keycloak.testsuite.forms.UsernameOnlyAuthenticator
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java
index 732ba0f..ca0d877 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java
@@ -197,6 +197,10 @@ public class OAuthClient {
         origin = null;
     }
 
+    public void setClientId(String clientId) {
+        this.clientId = clientId;
+    }
+
     public AuthorizationEndpointResponse doLogin(String username, String password) {
         openLoginForm();
         fillLoginForm(username, password);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/FlowOverrideTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/FlowOverrideTest.java
new file mode 100644
index 0000000..2ccaff8
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/FlowOverrideTest.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.forms;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.graphene.page.Page;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.admin.client.resource.ClientsResource;
+import org.keycloak.admin.client.resource.UserResource;
+import org.keycloak.authentication.authenticators.browser.UsernamePasswordFormFactory;
+import org.keycloak.events.Details;
+import org.keycloak.models.AuthenticationExecutionModel;
+import org.keycloak.models.AuthenticationFlowBindings;
+import org.keycloak.models.AuthenticationFlowModel;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
+import org.keycloak.testsuite.AssertEvents;
+import org.keycloak.testsuite.authentication.PushButtonAuthenticatorFactory;
+import org.keycloak.testsuite.pages.AppPage;
+import org.keycloak.testsuite.pages.ErrorPage;
+import org.keycloak.testsuite.pages.LoginPage;
+import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
+import org.keycloak.util.BasicAuthHelper;
+import org.openqa.selenium.By;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test that clients can override auth flows
+ *
+ * @author <a href="mailto:bburke@redhat.com">Bill Burke</a>
+ */
+public class FlowOverrideTest extends AbstractTestRealmKeycloakTest {
+
+    public static final String TEST_APP_DIRECT_OVERRIDE = "test-app-direct-override";
+    public static final String TEST_APP_FLOW = "test-app-flow";
+    @Rule
+    public AssertEvents events = new AssertEvents(this);
+
+    @Page
+    protected AppPage appPage;
+
+    @Page
+    protected LoginPage loginPage;
+
+    @Page
+    protected ErrorPage errorPage;
+
+    @Override
+    public void configureTestRealm(RealmRepresentation testRealm) {
+    }
+
+    @Deployment
+    public static WebArchive deploy() {
+        return RunOnServerDeployment.create(UserResource.class)
+                .addPackages(true, "org.keycloak.testsuite");
+    }
+
+
+    @Before
+    public void setupFlows() {
+        testingClient.server().run(session -> {
+            RealmModel realm = session.realms().getRealmByName("test");
+
+            ClientModel client = session.realms().getClientByClientId("test-app-flow", realm);
+            if (client != null) {
+                return;
+            }
+
+            client = session.realms().getClientByClientId("test-app", realm);
+            client.setDirectAccessGrantsEnabled(true);
+
+
+
+            // Parent flow
+            AuthenticationFlowModel browser = new AuthenticationFlowModel();
+            browser.setAlias("parent-flow");
+            browser.setDescription("browser based authentication");
+            browser.setProviderId("basic-flow");
+            browser.setTopLevel(true);
+            browser.setBuiltIn(true);
+            browser = realm.addAuthenticationFlow(browser);
+
+            // Subflow2
+            AuthenticationFlowModel subflow2 = new AuthenticationFlowModel();
+            subflow2.setTopLevel(false);
+            subflow2.setBuiltIn(true);
+            subflow2.setAlias("subflow-2");
+            subflow2.setDescription("username+password AND pushButton");
+            subflow2.setProviderId("basic-flow");
+            subflow2 = realm.addAuthenticationFlow(subflow2);
+
+            AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
+            execution.setParentFlow(browser.getId());
+            execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE);
+            execution.setFlowId(subflow2.getId());
+            execution.setPriority(20);
+            execution.setAuthenticatorFlow(true);
+            realm.addAuthenticatorExecution(execution);
+
+            // Subflow2 - push the button
+            execution = new AuthenticationExecutionModel();
+            execution.setParentFlow(subflow2.getId());
+            execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
+            execution.setAuthenticator(PushButtonAuthenticatorFactory.PROVIDER_ID);
+            execution.setPriority(10);
+            execution.setAuthenticatorFlow(false);
+
+            realm.addAuthenticatorExecution(execution);
+
+            // Subflow2 - username-password
+            execution = new AuthenticationExecutionModel();
+            execution.setParentFlow(subflow2.getId());
+            execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
+            execution.setAuthenticator(UsernamePasswordFormFactory.PROVIDER_ID);
+            execution.setPriority(20);
+            execution.setAuthenticatorFlow(false);
+
+            realm.addAuthenticatorExecution(execution);
+
+            client = realm.addClient(TEST_APP_FLOW);
+            client.setSecret("password");
+            client.setBaseUrl("http://localhost:8180/auth/realms/master/app/auth");
+            client.setManagementUrl("http://localhost:8180/auth/realms/master/app/admin");
+            client.setEnabled(true);
+            client.addRedirectUri("http://localhost:8180/auth/realms/master/app/auth/*");
+            client.setAuthenticationFlowBindingOverride(AuthenticationFlowBindings.BROWSER_BINDING, browser.getId());
+            client.setPublicClient(false);
+
+            // Parent flow
+            AuthenticationFlowModel directGrant = new AuthenticationFlowModel();
+            directGrant.setAlias("direct-override-flow");
+            directGrant.setDescription("direct grant based authentication");
+            directGrant.setProviderId("basic-flow");
+            directGrant.setTopLevel(true);
+            directGrant.setBuiltIn(true);
+            directGrant = realm.addAuthenticationFlow(directGrant);
+
+            execution = new AuthenticationExecutionModel();
+            execution.setParentFlow(directGrant.getId());
+            execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
+            execution.setAuthenticator(UsernameOnlyAuthenticator.PROVIDER_ID);
+            execution.setPriority(10);
+            execution.setAuthenticatorFlow(false);
+
+            realm.addAuthenticatorExecution(execution);
+
+            client = realm.addClient(TEST_APP_DIRECT_OVERRIDE);
+            client.setSecret("password");
+            client.setBaseUrl("http://localhost:8180/auth/realms/master/app/auth");
+            client.setManagementUrl("http://localhost:8180/auth/realms/master/app/admin");
+            client.setEnabled(true);
+            client.addRedirectUri("http://localhost:8180/auth/realms/master/app/auth/*");
+            client.setPublicClient(false);
+            client.setDirectAccessGrantsEnabled(true);
+            client.setAuthenticationFlowBindingOverride(AuthenticationFlowBindings.BROWSER_BINDING, browser.getId());
+            client.setAuthenticationFlowBindingOverride(AuthenticationFlowBindings.DIRECT_GRANT_BINDING, directGrant.getId());
+
+
+
+        });
+    }
+
+    //@Test
+    public void testRunConsole() throws Exception {
+        Thread.sleep(10000000);
+    }
+
+
+    @Test
+    public void testWithClientBrowserOverride() throws Exception {
+        oauth.setClientId(TEST_APP_FLOW);
+        String loginFormUrl = oauth.getLoginFormUrl();
+        log.info("loginFormUrl: " + loginFormUrl);
+
+        //Thread.sleep(10000000);
+
+        driver.navigate().to(loginFormUrl);
+
+        Assert.assertEquals("PushTheButton", driver.getTitle());
+
+        // Push the button. I am redirected to username+password form
+        driver.findElement(By.name("submit1")).click();
+
+
+        loginPage.assertCurrent();
+
+        // Fill username+password. I am successfully authenticated
+        oauth.fillLoginForm("test-user@localhost", "password");
+        appPage.assertCurrent();
+
+        events.expectLogin().client("test-app-flow").detail(Details.USERNAME, "test-user@localhost").assertEvent();
+    }
+
+    @Test
+    public void testNoOverrideBrowser() throws Exception {
+        String clientId = "test-app";
+        testNoOverrideBrowser(clientId);
+    }
+
+    private void testNoOverrideBrowser(String clientId) {
+        oauth.setClientId(clientId);
+        String loginFormUrl = oauth.getLoginFormUrl();
+        log.info("loginFormUrl: " + loginFormUrl);
+
+        //Thread.sleep(10000000);
+
+        driver.navigate().to(loginFormUrl);
+
+        loginPage.assertCurrent();
+
+        // Fill username+password. I am successfully authenticated
+        oauth.fillLoginForm("test-user@localhost", "password");
+        appPage.assertCurrent();
+
+        events.expectLogin().client(clientId).detail(Details.USERNAME, "test-user@localhost").assertEvent();
+    }
+
+    @Test
+    public void testGrantAccessTokenNoOverride() throws Exception {
+        testDirectGrantNoOverride("test-app");
+    }
+
+    private void testDirectGrantNoOverride(String clientId) {
+        Client httpClient = javax.ws.rs.client.ClientBuilder.newClient();
+        String grantUri = oauth.getResourceOwnerPasswordCredentialGrantUrl();
+        WebTarget grantTarget = httpClient.target(grantUri);
+
+        {   // test no password
+            String header = BasicAuthHelper.createHeader(clientId, "password");
+            Form form = new Form();
+            form.param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.PASSWORD);
+            form.param("username", "test-user@localhost");
+            Response response = grantTarget.request()
+                    .header(HttpHeaders.AUTHORIZATION, header)
+                    .post(Entity.form(form));
+            assertEquals(401, response.getStatus());
+            response.close();
+        }
+
+        {   // test invalid password
+            String header = BasicAuthHelper.createHeader(clientId, "password");
+            Form form = new Form();
+            form.param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.PASSWORD);
+            form.param("username", "test-user@localhost");
+            form.param("password", "invalid");
+            Response response = grantTarget.request()
+                    .header(HttpHeaders.AUTHORIZATION, header)
+                    .post(Entity.form(form));
+            assertEquals(401, response.getStatus());
+            response.close();
+        }
+
+        {   // test valid password
+            String header = BasicAuthHelper.createHeader(clientId, "password");
+            Form form = new Form();
+            form.param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.PASSWORD);
+            form.param("username", "test-user@localhost");
+            form.param("password", "password");
+            Response response = grantTarget.request()
+                    .header(HttpHeaders.AUTHORIZATION, header)
+                    .post(Entity.form(form));
+            assertEquals(200, response.getStatus());
+            response.close();
+        }
+
+        httpClient.close();
+        events.clear();
+    }
+
+    @Test
+    public void testGrantAccessTokenWithClientOverride() throws Exception {
+        String clientId = TEST_APP_DIRECT_OVERRIDE;
+        Client httpClient = javax.ws.rs.client.ClientBuilder.newClient();
+        String grantUri = oauth.getResourceOwnerPasswordCredentialGrantUrl();
+        WebTarget grantTarget = httpClient.target(grantUri);
+
+        {   // test no password
+            String header = BasicAuthHelper.createHeader(clientId, "password");
+            Form form = new Form();
+            form.param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.PASSWORD);
+            form.param("username", "test-user@localhost");
+            Response response = grantTarget.request()
+                    .header(HttpHeaders.AUTHORIZATION, header)
+                    .post(Entity.form(form));
+            assertEquals(200, response.getStatus());
+            response.close();
+        }
+
+        httpClient.close();
+        events.clear();
+    }
+
+    @Test
+    public void testRestInterface() throws Exception {
+        ClientsResource clients = adminClient.realm("test").clients();
+        List<ClientRepresentation> query = clients.findByClientId(TEST_APP_DIRECT_OVERRIDE);
+        ClientRepresentation clientRep = query.get(0);
+        String directGrantFlowId = clientRep.getAuthenticationFlowBindingOverrides().get(AuthenticationFlowBindings.DIRECT_GRANT_BINDING);
+        Assert.assertNotNull(directGrantFlowId);
+        clientRep.getAuthenticationFlowBindingOverrides().put(AuthenticationFlowBindings.DIRECT_GRANT_BINDING, "");
+        clients.get(clientRep.getId()).update(clientRep);
+        testDirectGrantNoOverride(TEST_APP_DIRECT_OVERRIDE);
+        clientRep.getAuthenticationFlowBindingOverrides().put(AuthenticationFlowBindings.DIRECT_GRANT_BINDING, directGrantFlowId);
+        clients.get(clientRep.getId()).update(clientRep);
+        testGrantAccessTokenWithClientOverride();
+
+        query = clients.findByClientId(TEST_APP_FLOW);
+        clientRep = query.get(0);
+        String browserFlowId = clientRep.getAuthenticationFlowBindingOverrides().get(AuthenticationFlowBindings.BROWSER_BINDING);
+        Assert.assertNotNull(browserFlowId);
+        clientRep.getAuthenticationFlowBindingOverrides().put(AuthenticationFlowBindings.BROWSER_BINDING, "");
+        clients.get(clientRep.getId()).update(clientRep);
+        testNoOverrideBrowser(TEST_APP_FLOW);
+        clientRep.getAuthenticationFlowBindingOverrides().put(AuthenticationFlowBindings.BROWSER_BINDING, browserFlowId);
+        clients.get(clientRep.getId()).update(clientRep);
+        testWithClientBrowserOverride();
+
+    }
+
+}