keycloak-aplcache

Kerberos refactored to be federation provider. Support for

2/16/2015 2:27:49 PM

Changes

broker/kerberos/src/main/java/org/keycloak/broker/kerberos/KerberosIdentityProvider.java 147(+0 -147)

broker/kerberos/src/main/java/org/keycloak/broker/kerberos/KerberosIdentityProviderFactory.java 27(+0 -27)

broker/kerberos/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderFactory 1(+0 -1)

broker/pom.xml 1(+0 -1)

forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-kerberos.html 78(+0 -78)

Details

broker/pom.xml 1(+0 -1)

diff --git a/broker/pom.xml b/broker/pom.xml
index e2ae6ff..7121b2b 100755
--- a/broker/pom.xml
+++ b/broker/pom.xml
@@ -18,7 +18,6 @@
         <module>core</module>
         <module>oidc</module>
         <module>saml</module>
-        <module>kerberos</module>
     </modules>
 
 </project>
diff --git a/core/src/main/java/org/keycloak/representations/idm/CredentialRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/CredentialRepresentation.java
index f1ea715..b7dad57 100755
--- a/core/src/main/java/org/keycloak/representations/idm/CredentialRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/CredentialRepresentation.java
@@ -10,6 +10,7 @@ public class CredentialRepresentation {
     public static final String PASSWORD_TOKEN = "password-token";
     public static final String TOTP = "totp";
     public static final String CLIENT_CERT = "cert";
+    public static final String KERBEROS = "kerberos";
 
     protected String type;
     protected String device;
diff --git a/dependencies/server-all/pom.xml b/dependencies/server-all/pom.xml
index c2546e8..f697926 100755
--- a/dependencies/server-all/pom.xml
+++ b/dependencies/server-all/pom.xml
@@ -95,7 +95,7 @@
         </dependency>
         <dependency>
             <groupId>org.keycloak</groupId>
-            <artifactId>keycloak-broker-kerberos</artifactId>
+            <artifactId>keycloak-kerberos-federation</artifactId>
             <version>${project.version}</version>
         </dependency>
         <dependency>
diff --git a/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/BasePropertiesFederationProvider.java b/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/BasePropertiesFederationProvider.java
index 0d63474..964560c 100755
--- a/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/BasePropertiesFederationProvider.java
+++ b/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/BasePropertiesFederationProvider.java
@@ -1,5 +1,6 @@
 package org.keycloak.examples.federation.properties;
 
+import org.keycloak.models.CredentialValidationOutput;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RoleModel;
@@ -128,6 +129,11 @@ public abstract class BasePropertiesFederationProvider implements UserFederation
     }
 
     @Override
+    public Set<String> getSupportedCredentialTypes() {
+        return supportedCredentialTypes;
+    }
+
+    @Override
     public boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input) {
         for (UserCredentialModel cred : input) {
             if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
@@ -156,6 +162,11 @@ public abstract class BasePropertiesFederationProvider implements UserFederation
     }
 
     @Override
+    public CredentialValidationOutput validCredentials(RealmModel realm, UserCredentialModel credential) {
+        return CredentialValidationOutput.failed();
+    }
+
+    @Override
     public void close() {
 
     }
diff --git a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/impl/KerberosUsernamePasswordAuthenticator.java b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/impl/KerberosUsernamePasswordAuthenticator.java
new file mode 100644
index 0000000..6515950
--- /dev/null
+++ b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/impl/KerberosUsernamePasswordAuthenticator.java
@@ -0,0 +1,126 @@
+package org.keycloak.federation.kerberos.impl;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.Configuration;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
+import org.jboss.logging.Logger;
+import org.keycloak.federation.kerberos.CommonKerberosConfig;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class KerberosUsernamePasswordAuthenticator {
+
+    private static final Logger logger = Logger.getLogger(KerberosUsernamePasswordAuthenticator.class);
+
+    private final CommonKerberosConfig config;
+
+    public KerberosUsernamePasswordAuthenticator(CommonKerberosConfig config) {
+        this.config = config;
+    }
+
+    /**
+     * Returns true if user with given username exists in kerberos database
+     *
+     * @param username username without Kerberos realm attached
+     * @return true if user available
+     */
+    public boolean isUserAvailable(String username) {
+        String principal = getKerberosPrincipal(username);
+
+        logger.debug("Checking existence of principal: " + principal);
+        try {
+            LoginContext loginContext = new LoginContext("does-not-matter", null,
+                    createJaasCallbackHandler(principal, "fake-password-which-nobody-has"),
+                    createJaasConfiguration());
+
+            loginContext.login();
+
+            throw new IllegalStateException("Didn't expect to end here");
+        } catch (LoginException le) {
+            String message = le.getMessage();
+            logger.debug("Message from kerberos: " + message);
+
+            // Bit cumbersome, but seems to work with tested kerberos servers
+            boolean exists = (!message.contains("Client not found"));
+            return exists;
+        }
+    }
+
+    /**
+     * Returns true if user was successfully authenticated against Kerberos
+     *
+     * @param username username without Kerberos realm attached
+     * @param password kerberos password
+     * @return  true if user was successfully authenticated
+     */
+    public boolean validUser(String username, String password) {
+        String principal = getKerberosPrincipal(username);
+
+        logger.debug("Validating password of principal: " + principal);
+        try {
+            LoginContext loginContext = new LoginContext("does-not-matter", null,
+                    createJaasCallbackHandler(principal, password),
+                    createJaasConfiguration());
+
+            loginContext.login();
+            logger.debug("Principal " + principal + " authenticated succesfully");
+
+            loginContext.logout();
+            return true;
+        } catch (LoginException le) {
+            logger.debug("Failed to authenticate user " + username, le);
+            return false;
+        }
+    }
+
+
+    protected String getKerberosPrincipal(String username) {
+        return username + "@" + config.getKerberosRealm();
+    }
+
+    protected CallbackHandler createJaasCallbackHandler(final String principal, final String password) {
+        return new CallbackHandler() {
+
+            @Override
+            public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+                for (Callback callback : callbacks) {
+                    if (callback instanceof NameCallback) {
+                        NameCallback nameCallback = (NameCallback) callback;
+                        nameCallback.setName(principal);
+                    } else if (callback instanceof PasswordCallback) {
+                        PasswordCallback passwordCallback = (PasswordCallback) callback;
+                        passwordCallback.setPassword(password.toCharArray());
+                    } else {
+                        throw new UnsupportedCallbackException(callback, "Unsupported callback: " + callback.getClass().getCanonicalName());
+                    }
+                }
+            }
+        };
+    }
+
+    protected Configuration createJaasConfiguration() {
+        return new Configuration() {
+
+            @Override
+            public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
+                Map<String, Object> options = new HashMap<String, Object>();
+                options.put("storeKey", "true");
+                options.put("debug", String.valueOf(config.getDebug()));
+                AppConfigurationEntry kerberosLMConfiguration = new AppConfigurationEntry("com.sun.security.auth.module.Krb5LoginModule", AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options);
+                return new AppConfigurationEntry[] { kerberosLMConfiguration };
+            }
+        };
+    }
+}
diff --git a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosConfig.java b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosConfig.java
new file mode 100644
index 0000000..950f9cd
--- /dev/null
+++ b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosConfig.java
@@ -0,0 +1,34 @@
+package org.keycloak.federation.kerberos;
+
+import org.keycloak.models.UserFederationProvider;
+import org.keycloak.models.UserFederationProviderModel;
+
+/**
+ * Configuration specific to {@link KerberosFederationProvider}
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class KerberosConfig extends CommonKerberosConfig {
+
+    public KerberosConfig(UserFederationProviderModel userFederationProvider) {
+        super(userFederationProvider);
+    }
+
+    public UserFederationProvider.EditMode getEditMode() {
+        String editModeString = getConfig().get("editMode");
+        if (editModeString == null) {
+            return UserFederationProvider.EditMode.UNSYNCED;
+        } else {
+            return UserFederationProvider.EditMode.valueOf(editModeString);
+        }
+    }
+
+    public boolean isAllowPasswordAuthentication() {
+        return Boolean.valueOf(getConfig().get("allowPasswordAuthentication"));
+    }
+
+    public boolean isUpdateProfileFirstLogin() {
+        return Boolean.valueOf(getConfig().get("updateProfileFirstLogin"));
+    }
+
+}
diff --git a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java
new file mode 100644
index 0000000..35f5885
--- /dev/null
+++ b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java
@@ -0,0 +1,241 @@
+package org.keycloak.federation.kerberos;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.jboss.logging.Logger;
+import org.keycloak.federation.kerberos.impl.KerberosUsernamePasswordAuthenticator;
+import org.keycloak.federation.kerberos.impl.SPNEGOAuthenticator;
+import org.keycloak.models.CredentialValidationOutput;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserCredentialValueModel;
+import org.keycloak.models.UserFederationProvider;
+import org.keycloak.models.UserFederationProviderModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.KerberosConstants;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class KerberosFederationProvider implements UserFederationProvider {
+
+    private static final Logger logger = Logger.getLogger(KerberosFederationProvider.class);
+    public static final String KERBEROS_PRINCIPAL = "KERBEROS_PRINCIPAL";
+
+    protected KeycloakSession session;
+    protected UserFederationProviderModel model;
+    protected KerberosConfig kerberosConfig;
+    protected KerberosFederationProviderFactory factory;
+
+    public KerberosFederationProvider(KeycloakSession session,UserFederationProviderModel model, KerberosFederationProviderFactory factory) {
+        this.session = session;
+        this.model = model;
+        this.kerberosConfig = new KerberosConfig(model);
+        this.factory = factory;
+    }
+
+    @Override
+    public UserModel proxy(UserModel local) {
+        if (kerberosConfig.getEditMode() == EditMode.READ_ONLY) {
+            return new ReadOnlyKerberosUserModelDelegate(local, this);
+        } else {
+            return local;
+        }
+    }
+
+    @Override
+    public boolean synchronizeRegistrations() {
+        return false;
+    }
+
+    @Override
+    public UserModel register(RealmModel realm, UserModel user) {
+        return null;
+    }
+
+    @Override
+    public boolean removeUser(RealmModel realm, UserModel user) {
+        // TODO: Not sure if federation provider is expected to delete user in localStorage. Looks rather like a bug in UserFederationManager.removeUser .
+        return session.userStorage().removeUser(realm, user);
+    }
+
+    @Override
+    public UserModel getUserByUsername(RealmModel realm, String username) {
+        if (username.contains("@")) {
+            username = username.split("@")[0];
+        }
+
+        KerberosUsernamePasswordAuthenticator authenticator = factory.createKerberosUsernamePasswordAuthenticator(kerberosConfig);
+        if (authenticator.isUserAvailable(username)) {
+            return findOrCreateAuthenticatedUser(realm, username);
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public UserModel getUserByEmail(RealmModel realm, String email) {
+        return null;
+    }
+
+    @Override
+    public List<UserModel> searchByAttributes(Map<String, String> attributes, RealmModel realm, int maxResults) {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public void preRemove(RealmModel realm) {
+
+    }
+
+    @Override
+    public void preRemove(RealmModel realm, RoleModel role) {
+
+    }
+
+    @Override
+    public boolean isValid(UserModel local) {
+        // KerberosUsernamePasswordAuthenticator.isUserAvailable is an overhead, so avoid it for now
+
+        String kerberosPrincipal = local.getUsername() + "@" + kerberosConfig.getKerberosRealm();
+        return model.getId().equals(local.getFederationLink()) && kerberosPrincipal.equals(local.getAttribute(KERBEROS_PRINCIPAL));
+    }
+
+    @Override
+    public Set<String> getSupportedCredentialTypes(UserModel local) {
+        Set<String> supportedCredTypes = new HashSet<String>();
+        supportedCredTypes.add(UserCredentialModel.KERBEROS);
+
+        if (kerberosConfig.isAllowPasswordAuthentication()) {
+            boolean passwordSupported = true;
+            if (kerberosConfig.getEditMode() == EditMode.UNSYNCED ) {
+
+                // Password from KC database has preference over kerberos password
+                for (UserCredentialValueModel cred : local.getCredentialsDirectly()) {
+                    if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
+                        passwordSupported = false;
+                    }
+                }
+            }
+
+            if (passwordSupported) {
+                supportedCredTypes.add(UserCredentialModel.PASSWORD);
+            }
+        }
+
+        return supportedCredTypes;
+    }
+
+    @Override
+    public Set<String> getSupportedCredentialTypes() {
+        Set<String> supportedCredTypes = new HashSet<String>();
+        supportedCredTypes.add(UserCredentialModel.KERBEROS);
+        return supportedCredTypes;
+    }
+
+    @Override
+    public boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input) {
+        for (UserCredentialModel cred : input) {
+            if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
+                return validPassword(user.getUsername(), cred.getValue());
+            } else {
+                return false; // invalid cred type
+            }
+        }
+        return true;
+    }
+
+    protected boolean validPassword(String username, String password) {
+        if (kerberosConfig.isAllowPasswordAuthentication()) {
+            KerberosUsernamePasswordAuthenticator authenticator = factory.createKerberosUsernamePasswordAuthenticator(kerberosConfig);
+            return authenticator.validUser(username, password);
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input) {
+        return validCredentials(realm, user, Arrays.asList(input));
+    }
+
+    @Override
+    public CredentialValidationOutput validCredentials(RealmModel realm, UserCredentialModel credential) {
+        if (credential.getType().equals(UserCredentialModel.KERBEROS)) {
+            String spnegoToken = credential.getValue();
+            SPNEGOAuthenticator spnegoAuthenticator = factory.createSPNEGOAuthenticator(spnegoToken, kerberosConfig);
+
+            spnegoAuthenticator.authenticate();
+
+            if (spnegoAuthenticator.isAuthenticated()) {
+                Map<String, Object> state = new HashMap<String, Object>();
+                state.put(KerberosConstants.GSS_DELEGATION_CREDENTIAL, spnegoAuthenticator.getDelegationCredential());
+
+                String username = spnegoAuthenticator.getAuthenticatedUsername();
+                UserModel user = findOrCreateAuthenticatedUser(realm, username);
+
+                return new CredentialValidationOutput(user, CredentialValidationOutput.Status.AUTHENTICATED, state);
+            }  else {
+                Map<String, Object> state = new HashMap<String, Object>();
+                state.put(KerberosConstants.RESPONSE_TOKEN, spnegoAuthenticator.getResponseToken());
+                return new CredentialValidationOutput(null, CredentialValidationOutput.Status.CONTINUE, state);
+            }
+
+        } else {
+            return CredentialValidationOutput.failed();
+        }
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    /**
+     * Called after successful authentication
+     *
+     * @param realm
+     * @param username username without realm prefix
+     * @return
+     */
+    protected UserModel findOrCreateAuthenticatedUser(RealmModel realm, String username) {
+        UserModel user = session.userStorage().getUserByUsername(username, realm);
+        if (user != null) {
+            logger.debug("Kerberos authenticated user " + username + " found in Keycloak storage");
+            if (!isValid(user)) {
+                throw new IllegalStateException("User with username " + username + " already exists, but is not linked to provider [" + model.getDisplayName() +
+                        "] or kerberos principal is not correct. Kerberos principal on user is: " + user.getAttribute(KERBEROS_PRINCIPAL));
+            }
+
+            return proxy(user);
+        } else {
+            return importUserToKeycloak(realm, username);
+        }
+    }
+
+    protected UserModel importUserToKeycloak(RealmModel realm, String username) {
+        // Just guessing email from kerberos realm
+        String email = username + "@" + kerberosConfig.getKerberosRealm().toLowerCase();
+
+        logger.info("Creating kerberos user: " + username + ", email: " + email + " to local Keycloak storage");
+        UserModel user = session.userStorage().addUser(realm, username);
+        user.setEnabled(true);
+        user.setEmail(email);
+        user.setFederationLink(model.getId());
+        user.setAttribute(KERBEROS_PRINCIPAL, username + "@" + kerberosConfig.getKerberosRealm());
+
+        if (kerberosConfig.isUpdateProfileFirstLogin()) {
+            user.addRequiredAction(UserModel.RequiredAction.UPDATE_PROFILE);
+        }
+
+        return proxy(user);
+    }
+}
diff --git a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProviderFactory.java b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProviderFactory.java
new file mode 100644
index 0000000..871587c
--- /dev/null
+++ b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProviderFactory.java
@@ -0,0 +1,80 @@
+package org.keycloak.federation.kerberos;
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.Set;
+
+import org.jboss.logging.Logger;
+import org.keycloak.Config;
+import org.keycloak.federation.kerberos.impl.KerberosServerSubjectAuthenticator;
+import org.keycloak.federation.kerberos.impl.KerberosUsernamePasswordAuthenticator;
+import org.keycloak.federation.kerberos.impl.SPNEGOAuthenticator;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.UserFederationProvider;
+import org.keycloak.models.UserFederationProviderFactory;
+import org.keycloak.models.UserFederationProviderModel;
+
+/**
+ * Factory for standalone Kerberos federation provider. Standalone means that it's not backed by LDAP. For Kerberos backed by LDAP (like MS AD or ApacheDS environment)
+ * you should rather use LDAP Federation Provider.
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class KerberosFederationProviderFactory implements UserFederationProviderFactory {
+
+    private static final Logger logger = Logger.getLogger(KerberosFederationProviderFactory.class);
+    public static final String PROVIDER_NAME = "kerberos";
+    @Override
+    public UserFederationProvider getInstance(KeycloakSession session, UserFederationProviderModel model) {
+        return new KerberosFederationProvider(session, model, this);
+    }
+
+    @Override
+    public Set<String> getConfigurationOptions() {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public String getId() {
+        return PROVIDER_NAME;
+    }
+
+    @Override
+    public void syncAllUsers(KeycloakSessionFactory sessionFactory, String realmId, UserFederationProviderModel model) {
+        logger.warn("Sync users not supported for this provider");
+    }
+
+    @Override
+    public void syncChangedUsers(KeycloakSessionFactory sessionFactory, String realmId, UserFederationProviderModel model, Date lastSync) {
+        logger.warn("Sync users not supported for this provider");
+    }
+
+    @Override
+    public UserFederationProvider create(KeycloakSession session) {
+        throw new IllegalAccessError("Illegal to call this method");
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    protected SPNEGOAuthenticator createSPNEGOAuthenticator(String spnegoToken, CommonKerberosConfig kerberosConfig) {
+        KerberosServerSubjectAuthenticator kerberosAuth = createKerberosSubjectAuthenticator(kerberosConfig);
+        return new SPNEGOAuthenticator(kerberosConfig, kerberosAuth, spnegoToken);
+    }
+
+    protected KerberosServerSubjectAuthenticator createKerberosSubjectAuthenticator(CommonKerberosConfig kerberosConfig) {
+        return new KerberosServerSubjectAuthenticator(kerberosConfig);
+    }
+
+    protected KerberosUsernamePasswordAuthenticator createKerberosUsernamePasswordAuthenticator(CommonKerberosConfig kerberosConfig) {
+        return new KerberosUsernamePasswordAuthenticator(kerberosConfig);
+    }
+}
diff --git a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/ReadOnlyKerberosUserModelDelegate.java b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/ReadOnlyKerberosUserModelDelegate.java
new file mode 100644
index 0000000..7ac3ca7
--- /dev/null
+++ b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/ReadOnlyKerberosUserModelDelegate.java
@@ -0,0 +1,28 @@
+package org.keycloak.federation.kerberos;
+
+import org.keycloak.models.ModelReadOnlyException;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.UserModelDelegate;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ReadOnlyKerberosUserModelDelegate extends UserModelDelegate {
+
+    protected KerberosFederationProvider provider;
+
+    public ReadOnlyKerberosUserModelDelegate(UserModel delegate, KerberosFederationProvider provider) {
+        super(delegate);
+        this.provider = provider;
+    }
+
+    @Override
+    public void updateCredential(UserCredentialModel cred) {
+        if (provider.getSupportedCredentialTypes(delegate).contains(cred.getType())) {
+            throw new ModelReadOnlyException("Can't change password in Keycloak database. Change password with your Kerberos server");
+        }
+
+        delegate.updateCredential(cred);
+    }
+}
diff --git a/federation/kerberos/src/main/resources/META-INF/services/org.keycloak.models.UserFederationProviderFactory b/federation/kerberos/src/main/resources/META-INF/services/org.keycloak.models.UserFederationProviderFactory
new file mode 100644
index 0000000..040f5c9
--- /dev/null
+++ b/federation/kerberos/src/main/resources/META-INF/services/org.keycloak.models.UserFederationProviderFactory
@@ -0,0 +1 @@
+org.keycloak.federation.kerberos.KerberosFederationProviderFactory
\ No newline at end of file
diff --git a/federation/ldap/pom.xml b/federation/ldap/pom.xml
index 7149e43..a449335 100755
--- a/federation/ldap/pom.xml
+++ b/federation/ldap/pom.xml
@@ -27,6 +27,12 @@
         </dependency>
         <dependency>
             <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-kerberos-federation</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
             <artifactId>keycloak-picketlink-api</artifactId>
             <version>${project.version}</version>
             <scope>provided</scope>
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/kerberos/LDAPProviderKerberosConfig.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/kerberos/LDAPProviderKerberosConfig.java
new file mode 100644
index 0000000..b98c5ca
--- /dev/null
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/kerberos/LDAPProviderKerberosConfig.java
@@ -0,0 +1,20 @@
+package org.keycloak.federation.ldap.kerberos;
+
+import org.keycloak.federation.kerberos.CommonKerberosConfig;
+import org.keycloak.models.UserFederationProviderModel;
+
+/**
+ * Configuration specific to {@link org.keycloak.federation.ldap.LDAPFederationProvider}
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class LDAPProviderKerberosConfig extends CommonKerberosConfig {
+
+    public LDAPProviderKerberosConfig(UserFederationProviderModel userFederationProvider) {
+        super(userFederationProvider);
+    }
+
+    public boolean isUseKerberosForPasswordAuthentication() {
+        return Boolean.valueOf(getConfig().get("useKerberosForPasswordAuthentication"));
+    }
+}
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java
index a49a989..9a92604 100755
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java
@@ -1,6 +1,10 @@
 package org.keycloak.federation.ldap;
 
 import org.jboss.logging.Logger;
+import org.keycloak.federation.kerberos.impl.KerberosUsernamePasswordAuthenticator;
+import org.keycloak.federation.kerberos.impl.SPNEGOAuthenticator;
+import org.keycloak.federation.ldap.kerberos.LDAPProviderKerberosConfig;
+import org.keycloak.models.CredentialValidationOutput;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.ModelException;
 import org.keycloak.models.RealmModel;
@@ -10,6 +14,7 @@ import org.keycloak.models.UserCredentialValueModel;
 import org.keycloak.models.UserFederationProvider;
 import org.keycloak.models.UserFederationProviderModel;
 import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.KerberosConstants;
 import org.picketlink.idm.IdentityManagementException;
 import org.picketlink.idm.IdentityManager;
 import org.picketlink.idm.PartitionManager;
@@ -17,7 +22,7 @@ import org.picketlink.idm.model.basic.BasicModel;
 import org.picketlink.idm.model.basic.User;
 import org.picketlink.idm.query.IdentityQuery;
 
-import java.util.Collections;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
@@ -36,28 +41,32 @@ public class LDAPFederationProvider implements UserFederationProvider {
     public static final String SYNC_REGISTRATIONS = "syncRegistrations";
     public static final String EDIT_MODE = "editMode";
 
+    protected LDAPFederationProviderFactory factory;
     protected KeycloakSession session;
     protected UserFederationProviderModel model;
     protected PartitionManager partitionManager;
     protected EditMode editMode;
+    protected LDAPProviderKerberosConfig kerberosConfig;
 
-    protected static final Set<String> supportedCredentialTypes = new HashSet<String>();
+    protected final Set<String> supportedCredentialTypes = new HashSet<String>();
 
-    static
-    {
-        supportedCredentialTypes.add(UserCredentialModel.PASSWORD);
-    }
-
-    public LDAPFederationProvider(KeycloakSession session, UserFederationProviderModel model, PartitionManager partitionManager) {
+    public LDAPFederationProvider(LDAPFederationProviderFactory factory, KeycloakSession session, UserFederationProviderModel model, PartitionManager partitionManager) {
+        this.factory = factory;
         this.session = session;
         this.model = model;
         this.partitionManager = partitionManager;
+        this.kerberosConfig = new LDAPProviderKerberosConfig(model);
         String editModeString = model.getConfig().get(EDIT_MODE);
         if (editModeString == null) {
             editMode = EditMode.READ_ONLY;
         } else {
             editMode = EditMode.valueOf(editModeString);
         }
+
+        supportedCredentialTypes.add(UserCredentialModel.PASSWORD);
+        if (kerberosConfig.isAllowKerberosAuthentication()) {
+            supportedCredentialTypes.add(UserCredentialModel.KERBEROS);
+        }
     }
 
     private ModelException convertIDMException(IdentityManagementException ie) {
@@ -97,10 +106,12 @@ public class LDAPFederationProvider implements UserFederationProvider {
 
     @Override
     public Set<String> getSupportedCredentialTypes(UserModel local) {
+        Set<String> supportedCredentialTypes = new HashSet<String>(this.supportedCredentialTypes);
         if (editMode == EditMode.UNSYNCED ) {
             for (UserCredentialValueModel cred : local.getCredentialsDirectly()) {
                 if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
-                    return Collections.emptySet();
+                    // User has changed password in KC local database. Use KC password instead of LDAP password
+                    supportedCredentialTypes.remove(UserCredentialModel.PASSWORD);
                 }
             }
         }
@@ -108,6 +119,11 @@ public class LDAPFederationProvider implements UserFederationProvider {
     }
 
     @Override
+    public Set<String> getSupportedCredentialTypes() {
+        return new HashSet<String>(this.supportedCredentialTypes);
+    }
+
+    @Override
     public boolean synchronizeRegistrations() {
         return "true".equalsIgnoreCase(model.getConfig().get(SYNC_REGISTRATIONS)) && editMode == EditMode.WRITABLE;
     }
@@ -244,6 +260,8 @@ public class LDAPFederationProvider implements UserFederationProvider {
         imported.setLastName(picketlinkUser.getLastName());
         imported.setFederationLink(model.getId());
         imported.setAttribute(LDAP_ID, picketlinkUser.getId());
+
+        logger.debugf("Added new user from LDAP. Username: " + imported.getUsername() + ", Email: ", imported.getEmail() + ", LDAP_ID: " + picketlinkUser.getId());
         return proxy(imported);
     }
 
@@ -285,10 +303,17 @@ public class LDAPFederationProvider implements UserFederationProvider {
     }
 
     public boolean validPassword(String username, String password) {
-        try {
-            return LDAPUtils.validatePassword(partitionManager, username, password);
-        } catch (IdentityManagementException ie) {
-            throw convertIDMException(ie);
+        if (kerberosConfig.isAllowKerberosAuthentication() && kerberosConfig.isUseKerberosForPasswordAuthentication()) {
+            // Use Kerberos JAAS (Krb5LoginModule)
+            KerberosUsernamePasswordAuthenticator authenticator = factory.createKerberosUsernamePasswordAuthenticator(kerberosConfig);
+            return authenticator.validUser(username, password);
+        } else {
+            // Use Naming LDAP API
+            try {
+                return LDAPUtils.validatePassword(partitionManager, username, password);
+            } catch (IdentityManagementException ie) {
+                throw convertIDMException(ie);
+            }
         }
     }
 
@@ -307,14 +332,37 @@ public class LDAPFederationProvider implements UserFederationProvider {
 
     @Override
     public boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input) {
-        for (UserCredentialModel cred : input) {
-            if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
-                return validPassword(user.getUsername(), cred.getValue());
-            } else {
-                return false; // invalid cred type
+        return validCredentials(realm, user, Arrays.asList(input));
+    }
+
+    @Override
+    public CredentialValidationOutput validCredentials(RealmModel realm, UserCredentialModel credential) {
+        if (credential.getType().equals(UserCredentialModel.KERBEROS)) {
+            if (kerberosConfig.isAllowKerberosAuthentication()) {
+                String spnegoToken = credential.getValue();
+                SPNEGOAuthenticator spnegoAuthenticator = factory.createSPNEGOAuthenticator(spnegoToken, kerberosConfig);
+
+                spnegoAuthenticator.authenticate();
+
+                if (spnegoAuthenticator.isAuthenticated()) {
+                    Map<String, Object> state = new HashMap<String, Object>();
+                    state.put(KerberosConstants.GSS_DELEGATION_CREDENTIAL, spnegoAuthenticator.getDelegationCredential());
+
+                    // TODO: This assumes that LDAP "uid" is equal to kerberos principal name. Like uid "hnelson" and kerberos principal "hnelson@KEYCLOAK.ORG".
+                    // Check if it's correct or if LDAP attribute for mapping kerberos principal should be available (For ApacheDS it seems to be attribute "krb5PrincipalName" but on MSAD it's likely different)
+                    String username = spnegoAuthenticator.getAuthenticatedUsername();
+                    UserModel user = findOrCreateAuthenticatedUser(realm, username);
+
+                    return new CredentialValidationOutput(user, CredentialValidationOutput.Status.AUTHENTICATED, state);
+                }  else {
+                    Map<String, Object> state = new HashMap<String, Object>();
+                    state.put(KerberosConstants.RESPONSE_TOKEN, spnegoAuthenticator.getResponseToken());
+                    return new CredentialValidationOutput(null, CredentialValidationOutput.Status.CONTINUE, state);
+                }
             }
         }
-        return true;
+
+        return CredentialValidationOutput.failed();
     }
 
     @Override
@@ -330,7 +378,6 @@ public class LDAPFederationProvider implements UserFederationProvider {
             if (currentUser == null) {
                 // Add new user to Keycloak
                 importUserFromPicketlink(realm, picketlinkUser);
-                logger.debugf("Added new user from LDAP: %s", username);
             } else {
                 if ((fedModel.getId().equals(currentUser.getFederationLink())) && (picketlinkUser.getId().equals(currentUser.getAttribute(LDAPFederationProvider.LDAP_ID)))) {
                     // Update keycloak user
@@ -345,4 +392,27 @@ public class LDAPFederationProvider implements UserFederationProvider {
             }
         }
     }
+
+    /**
+     * Called after successful kerberos authentication
+     *
+     * @param realm
+     * @param username username without realm prefix
+     * @return
+     */
+    protected UserModel findOrCreateAuthenticatedUser(RealmModel realm, String username) {
+        UserModel user = session.userStorage().getUserByUsername(username, realm);
+        if (user != null) {
+            logger.debug("Kerberos authenticated user " + username + " found in Keycloak storage");
+            if (!isValid(user)) {
+                throw new IllegalStateException("User with username " + username + " already exists, but is not linked to provider [" + model.getDisplayName() +
+                        "] or LDAP_ID is not correct. LDAP_ID on user is: " + user.getAttribute(LDAP_ID));
+            }
+
+            return proxy(user);
+        } else {
+            // Creating user to local storage
+            return getUserByUsername(realm, username);
+        }
+    }
 }
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java
index 44987e9..16a877f 100755
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java
@@ -2,6 +2,11 @@ package org.keycloak.federation.ldap;
 
 import org.jboss.logging.Logger;
 import org.keycloak.Config;
+import org.keycloak.federation.kerberos.CommonKerberosConfig;
+import org.keycloak.federation.kerberos.KerberosConfig;
+import org.keycloak.federation.kerberos.impl.KerberosServerSubjectAuthenticator;
+import org.keycloak.federation.kerberos.impl.KerberosUsernamePasswordAuthenticator;
+import org.keycloak.federation.kerberos.impl.SPNEGOAuthenticator;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
 import org.keycloak.models.KeycloakSessionTask;
@@ -45,7 +50,7 @@ public class LDAPFederationProviderFactory implements UserFederationProviderFact
     public LDAPFederationProvider getInstance(KeycloakSession session, UserFederationProviderModel model) {
         PartitionManagerProvider idmProvider = session.getProvider(PartitionManagerProvider.class);
         PartitionManager partition = idmProvider.getPartitionManager(model);
-        return new LDAPFederationProvider(session, model, partition);
+        return new LDAPFederationProvider(this, session, model, partition);
     }
 
     @Override
@@ -140,4 +145,17 @@ public class LDAPFederationProviderFactory implements UserFederationProviderFact
         LDAPFederationProvider ldapFedProvider = getInstance(session, fedModel);
         ldapFedProvider.importPicketlinkUsers(realm, users, fedModel);
     }
+
+    protected SPNEGOAuthenticator createSPNEGOAuthenticator(String spnegoToken, CommonKerberosConfig kerberosConfig) {
+        KerberosServerSubjectAuthenticator kerberosAuth = createKerberosSubjectAuthenticator(kerberosConfig);
+        return new SPNEGOAuthenticator(kerberosConfig, kerberosAuth, spnegoToken);
+    }
+
+    protected KerberosServerSubjectAuthenticator createKerberosSubjectAuthenticator(CommonKerberosConfig kerberosConfig) {
+        return new KerberosServerSubjectAuthenticator(kerberosConfig);
+    }
+
+    protected KerberosUsernamePasswordAuthenticator createKerberosUsernamePasswordAuthenticator(CommonKerberosConfig kerberosConfig) {
+        return new KerberosUsernamePasswordAuthenticator(kerberosConfig);
+    }
 }
diff --git a/federation/pom.xml b/federation/pom.xml
index b990d51..7c1487a 100755
--- a/federation/pom.xml
+++ b/federation/pom.xml
@@ -17,6 +17,7 @@
 
     <modules>
         <module>ldap</module>
+        <module>kerberos</module>
     </modules>
 
 </project>
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js
index ff854af..054c218 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js
@@ -875,6 +875,36 @@ module.config([ '$routeProvider', function($routeProvider) {
             },
             controller : 'LDAPCtrl'
         })
+        .when('/realms/:realm/user-federation/providers/kerberos/:instance', {
+            templateUrl : 'partials/federated-kerberos.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                instance : function(UserFederationInstanceLoader) {
+                    return UserFederationInstanceLoader();
+                },
+                providerFactory : function() {
+                    return { id: "kerberos" };
+                }
+            },
+            controller : 'GenericUserFederationCtrl'
+        })
+        .when('/create/user-federation/:realm/providers/kerberos', {
+            templateUrl : 'partials/federated-kerberos.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                instance : function() {
+                    return {};
+                },
+                providerFactory : function() {
+                    return { id: "kerberos" };
+                }
+            },
+            controller : 'GenericUserFederationCtrl'
+        })
         .when('/create/user-federation/:realm/providers/:provider', {
             templateUrl : 'partials/federated-generic.html',
             resolve : {
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js
index 5c25e1b..52b313a 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js
@@ -435,7 +435,7 @@ module.controller('RealmRequiredCredentialsCtrl', function($scope, Realm, realm,
     $scope.userCredentialOptions = {
         'multiple' : true,
         'simple_tags' : true,
-        'tags' : ['password', 'totp', 'cert']
+        'tags' : ['password', 'totp', 'cert', 'kerberos']
     };
 
     $scope.changed = false;
@@ -653,8 +653,7 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
         $scope.identityProvider.name = providerFactory.name;
         $scope.identityProvider.enabled = true;
         $scope.identityProvider.updateProfileFirstLogin = true;
-        // Kerberos is suggested as default provider, others not
-        $scope.identityProvider.authenticateByDefault = (providerFactory.id === "kerberos");
+        $scope.identityProvider.authenticateByDefault = false;
         $scope.newIdentityProvider = true;
     }
 
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js
index 1f36f90..fb0ce1b 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js
@@ -378,9 +378,23 @@ module.controller('GenericUserFederationCtrl', function($scope, $location, Notif
             instance.priority = 0;
             $scope.fullSyncEnabled = false;
             $scope.changedSyncEnabled = false;
+
+            if (providerFactory.id === 'kerberos') {
+                instance.config.debug = false;
+                instance.config.allowPasswordAuthentication = true;
+                instance.config.editMode = 'UNSYNCED';
+                instance.config.updateProfileFirstLogin = true;
+                instance.config.allowKerberosAuthentication = true;
+            }
         } else {
             $scope.fullSyncEnabled = (instance.fullSyncPeriod && instance.fullSyncPeriod > 0);
             $scope.changedSyncEnabled = (instance.changedSyncPeriod && instance.changedSyncPeriod > 0);
+
+            if (providerFactory.id === 'kerberos') {
+                instance.config.debug = (instance.config.debug === 'true' || instance.config.debug === true);
+                instance.config.allowPasswordAuthentication = (instance.config.allowPasswordAuthentication === 'true' || instance.config.allowPasswordAuthentication === true);
+                instance.config.updateProfileFirstLogin = (instance.config.updateProfileFirstLogin === 'true' || instance.config.updateProfileFirstLogin === true);
+            }
         }
 
         $scope.changed = false;
@@ -488,25 +502,30 @@ module.controller('LDAPCtrl', function($scope, $location, Notifications, Dialog,
             instance.providerName = "ldap";
             instance.config = {};
             instance.priority = 0;
-            $scope.syncRegistrations = false;
 
-            $scope.userAccountControlsAfterPasswordUpdate = true;
-            instance.config.userAccountControlsAfterPasswordUpdate = "true";
+            instance.config.syncRegistrations = false;
+            instance.config.userAccountControlsAfterPasswordUpdate = true;
+            instance.config.connectionPooling = true;
+            instance.config.pagination = true;
 
-            $scope.connectionPooling = true;
-            instance.config.connectionPooling = "true";
+            instance.config.allowKerberosAuthentication = false;
+            instance.config.debug = false;
+            instance.config.useKerberosForPasswordAuthentication = false;
 
-            $scope.pagination = true;
-            instance.config.pagination = "true";
             instance.config.batchSizeForSync = DEFAULT_BATCH_SIZE;
 
             $scope.fullSyncEnabled = false;
             $scope.changedSyncEnabled = false;
         } else {
-            $scope.syncRegistrations = instance.config.syncRegistrations && instance.config.syncRegistrations == "true";
-            $scope.userAccountControlsAfterPasswordUpdate = instance.config.userAccountControlsAfterPasswordUpdate && instance.config.userAccountControlsAfterPasswordUpdate == "true";
-            $scope.connectionPooling = instance.config.connectionPooling && instance.config.connectionPooling == "true";
-            $scope.pagination = instance.config.pagination && instance.config.pagination == "true";
+            instance.config.syncRegistrations = (instance.config.syncRegistrations === 'true' || instance.config.syncRegistrations === true);
+            instance.config.userAccountControlsAfterPasswordUpdate = (instance.config.userAccountControlsAfterPasswordUpdate === 'true' || instance.config.userAccountControlsAfterPasswordUpdate === true);
+            instance.config.connectionPooling = (instance.config.connectionPooling === 'true' || instance.config.connectionPooling === true);
+            instance.config.pagination = (instance.config.pagination === 'true' || instance.config.pagination === true);
+
+            instance.config.allowKerberosAuthentication = (instance.config.allowKerberosAuthentication === 'true' || instance.config.allowKerberosAuthentication === true);
+            instance.config.debug = (instance.config.debug === 'true' || instance.config.debug === true);
+            instance.config.useKerberosForPasswordAuthentication = (instance.config.useKerberosForPasswordAuthentication === 'true' || instance.config.useKerberosForPasswordAuthentication === true);
+
             if (!instance.config.batchSizeForSync) {
                 instance.config.batchSizeForSync = DEFAULT_BATCH_SIZE;
             }
@@ -534,21 +553,6 @@ module.controller('LDAPCtrl', function($scope, $location, Notifications, Dialog,
 
     $scope.realm = realm;
 
-    function watchBooleanProperty(propertyName) {
-        $scope.$watch(propertyName, function() {
-            if ($scope[propertyName]) {
-                $scope.instance.config[propertyName] = "true";
-            } else {
-                $scope.instance.config[propertyName] = "false";
-            }
-        })
-    }
-
-    watchBooleanProperty('syncRegistrations');
-    watchBooleanProperty('userAccountControlsAfterPasswordUpdate');
-    watchBooleanProperty('connectionPooling');
-    watchBooleanProperty('pagination');
-
     $scope.$watch('fullSyncEnabled', function(newVal, oldVal) {
         if (oldVal == newVal) {
             return;
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-kerberos.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-kerberos.html
new file mode 100644
index 0000000..1b30221
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-kerberos.html
@@ -0,0 +1,118 @@
+<div class="bs-sidebar col-sm-3 " data-ng-include data-src="'partials/realm-menu.html'"></div>
+<div id="content-area" class="col-sm-9" role="main">
+    <ul class="nav nav-tabs nav-tabs-pf">
+        <li><a href="#/realms/{{realm.realm}}/users">User List</a></li>
+        <li><a href="#/realms/{{realm.realm}}/user-federation">Federation</a></li>
+    </ul>
+    <div id="content">
+        <ol class="breadcrumb" data-ng-hide="create">
+            <li><a href="#/realms/{{realm.realm}}/user-federation">Federation Providers</a></li>
+            <li><a href="#/realms/{{realm.realm}}/user-federation/providers/{{instance.providerName}}/{{instance.id}}">{{instance.displayName}}</a></li>
+            <li class="active">Kerberos Settings</li>
+        </ol>
+        <ol class="breadcrumb" data-ng-show="create">
+            <li><a href="#/realms/{{realm.realm}}/user-federation">Federation Providers</a></li>
+            <li class="active">Add Kerberos Provider</li>
+        </ol>
+        <h2 class="pull-left" data-ng-hide="create">Kerberos Provider Settings <span tooltip-placement="right" tooltip="Use Kerberos federation provider just if you don't need Kerberos integrated with LDAP! For Kerberos+LDAP use LDAP Federation provider instead"
+                                                                                     class="fa fa-info-circle"></span></h2>
+        <h2 class="pull-left" data-ng-show="create">Add Standalone Kerberos Provider <span tooltip-placement="right" tooltip="Use Kerberos federation provider just if you don't need Kerberos integrated with LDAP! For Kerberos+LDAP use LDAP Federation provider instead"
+                                                                                           class="fa fa-info-circle"></span></h2>
+        <p class="subtitle"><span class="required">*</span> Required fields</p>
+        <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
+
+            <fieldset>
+                <legend><span class="text">Required Settings</span></legend>
+                <div class="form-group clearfix" data-ng-show="!create">
+                    <label class="col-sm-2 control-label" for="providerId">Provider ID </label>
+                    <div class="col-sm-4">
+                        <input class="form-control" id="providerId" type="text" ng-model="instance.id" readonly>
+                    </div>
+                </div>
+                <div class="form-group clearfix">
+                    <label class="col-sm-2 control-label" for="consoleDisplayName">Console display name </label>
+                    <div class="col-sm-4">
+                        <input class="form-control" id="consoleDisplayName" type="text" ng-model="instance.displayName" placeholder="defaults to id">
+                    </div>
+                    <span tooltip-placement="right" tooltip="Display name of provider when linked in admin console." class="fa fa-info-circle"></span>
+                </div>
+                <div class="form-group clearfix">
+                    <label class="col-sm-2 control-label" for="priority">Priority </label>
+                    <div class="col-sm-4">
+                        <input class="form-control" id="priority" type="text" ng-model="instance.priority">
+                    </div>
+                    <span tooltip-placement="right" tooltip="Priority of provider when doing a user lookup.  Lowest first." class="fa fa-info-circle"></span>
+                </div>
+
+                <div class="form-group clearfix">
+                    <label class="col-sm-2 control-label" for="kerberosRealm">Kerberos Realm <span class="required">*</span></label>
+                    <div class="col-sm-4">
+                        <input class="form-control" id="kerberosRealm" type="text" ng-model="instance.config.kerberosRealm" required>
+                    </div>
+                    <span tooltip-placement="right" tooltip="Name of kerberos realm. For example FOO.ORG" class="fa fa-info-circle"></span>
+                </div>
+                <div class="form-group clearfix">
+                    <label class="col-sm-2 control-label" for="serverPrincipal">Server principal <span class="required">*</span></label>
+                    <div class="col-sm-4">
+                        <input class="form-control" id="serverPrincipal" type="text" ng-model="instance.config.serverPrincipal" required>
+                    </div>
+                    <span tooltip-placement="right" tooltip="Full name of server principal for HTTP service including server and domain name. For example HTTP/host.foo.org@FOO.ORG" class="fa fa-info-circle"></span>
+                </div>
+                <div class="form-group clearfix">
+                    <label class="col-sm-2 control-label" for="keyTab">KeyTab <span class="required">*</span></label>
+                    <div class="col-sm-4">
+                        <input class="form-control" id="keyTab" type="text" ng-model="instance.config.keyTab" required>
+                    </div>
+                    <span tooltip-placement="right" tooltip="Location of Kerberos KeyTab file containing the credentials of server principal. For example /etc/krb5.keytab" class="fa fa-info-circle"></span>
+                </div>
+                <div class="form-group">
+                    <label class="col-sm-2 control-label" for="debug">Debug </label>
+                    <div class="col-sm-4">
+                        <input ng-model="instance.config.debug" id="debug" onoffswitch />
+                    </div>
+                    <span tooltip-placement="right" tooltip="Enable/disable debug logging to standard output for Krb5LoginModule." class="fa fa-info-circle"></span>
+                </div>
+
+                <div class="form-group">
+                    <label class="col-sm-2 control-label" for="allowPasswordAuthentication">Allow Password Authentication </label>
+                    <div class="col-sm-4">
+                        <input ng-model="instance.config.allowPasswordAuthentication" id="allowPasswordAuthentication" onoffswitch />
+                    </div>
+                    <span tooltip-placement="right" tooltip="Enable/disable possibility of username/password authentication against Kerberos database" class="fa fa-info-circle"></span>
+                </div>
+                <div class="form-group" data-ng-show="instance.config.allowPasswordAuthentication">
+                    <label class="col-sm-2 control-label" for="editMode">Edit mode</label>
+                    <div class="col-sm-4">
+                        <div class="select-kc">
+                            <select id="editMode"
+                                    ng-model="instance.config.editMode">
+                                <option>READ_ONLY</option>
+                                <option>UNSYNCED</option>
+                            </select>
+                        </div>
+                    </div>
+                    <span tooltip-placement="right" tooltip="READ_ONLY means that password updates are not allowed and user always authenticates with Kerberos password. UNSYNCED means user can change his password in Keycloak database and this one will be used instead of Kerberos password then" class="fa fa-info-circle"></span>
+                </div>
+                <div class="form-group">
+                    <label class="col-sm-2 control-label" for="updateProfileFirstLogin">Update Profile First Login </label>
+                    <div class="col-sm-4">
+                        <input ng-model="instance.config.updateProfileFirstLogin" id="updateProfileFirstLogin" onoffswitch />
+                    </div>
+                    <span tooltip-placement="right" tooltip="Update profile on first login" class="fa fa-info-circle"></span>
+                </div>
+
+            </fieldset>
+
+            <div class="pull-right form-actions" data-ng-show="create && access.manageUsers">
+                <button kc-cancel data-ng-click="cancel()">Cancel</button>
+                <button kc-save data-ng-show="changed">Save</button>
+            </div>
+
+            <div class="pull-right form-actions" data-ng-show="!create && access.manageUsers">
+                <button kc-reset data-ng-show="changed">Clear changes</button>
+                <button kc-save  data-ng-show="changed">Save</button>
+                <button kc-delete data-ng-click="remove()" data-ng-hide="changed">Delete</button>
+            </div>
+        </form>
+    </div>
+</div>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-ldap.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-ldap.html
index 69c53e2..66ff5c2 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-ldap.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-ldap.html
@@ -58,7 +58,7 @@
                 <div class="form-group clearfix block">
                     <label class="col-sm-2 control-label" for="syncRegistrations">Sync Registrations</label>
                     <div class="col-sm-4">
-                        <input ng-model="syncRegistrations" name="syncRegistrations" id="syncRegistrations" onoffswitch />
+                        <input ng-model="instance.config.syncRegistrations" name="syncRegistrations" id="syncRegistrations" onoffswitch />
                     </div>
                     <span tooltip-placement="right" tooltip="Should newly created users be created within LDAP store?  Priority effects which provider is chose to sync the new user." class="fa fa-info-circle"></span>
                 </div>
@@ -139,21 +139,21 @@
                 <div class="form-group clearfix">
                     <label class="col-sm-2 control-label" for="connectionPooling">Connection pooling</label>
                     <div class="col-sm-4">
-                        <input ng-model="connectionPooling" name="connectionPooling" id="connectionPooling" onoffswitch />
+                        <input ng-model="instance.config.connectionPooling" name="connectionPooling" id="connectionPooling" onoffswitch />
                     </div>
                     <span tooltip-placement="right" tooltip="Does Keycloak should use connection pooling for accessing LDAP server" class="fa fa-info-circle"></span>
                 </div>
                 <div class="form-group clearfix">
                     <label class="col-sm-2 control-label" for="pagination">Pagination</label>
                     <div class="col-sm-4">
-                        <input ng-model="pagination" name="pagination" id="pagination" onoffswitch />
+                        <input ng-model="instance.config.pagination" name="pagination" id="pagination" onoffswitch />
                     </div>
                     <span tooltip-placement="right" tooltip="Does the LDAP server support pagination." class="fa fa-info-circle"></span>
                 </div>
                 <div class="form-group clearfix" data-ng-show="instance.config.vendor === 'ad' ">
                     <label class="col-sm-2 control-label" for="userAccountControlsAfterPasswordUpdate">Enable Account After Password Update</label>
                     <div class="col-sm-4">
-                        <input ng-model="userAccountControlsAfterPasswordUpdate" name="userAccountControlsAfterPasswordUpdate" id="userAccountControlsAfterPasswordUpdate" onoffswitch />
+                        <input ng-model="instance.config.userAccountControlsAfterPasswordUpdate" name="userAccountControlsAfterPasswordUpdate" id="userAccountControlsAfterPasswordUpdate" onoffswitch />
                     </div>
                     <span tooltip-placement="right" tooltip="Useful just for Active Directory. If enabled, then Keycloak will always set
                     Active Directory userAccountControl attribute to 512 after password update. This would mean that particular user will be enabled in Active Directory" class="fa fa-info-circle"></span>
@@ -161,6 +161,52 @@
             </fieldset>
 
             <fieldset>
+                <legend><span class="text">Kerberos integration</span></legend>
+                <div class="form-group">
+                    <label class="col-sm-2 control-label" for="allowKerberosAuthentication">Allow Kerberos authentication </label>
+                    <div class="col-sm-4">
+                        <input ng-model="instance.config.allowKerberosAuthentication" id="allowKerberosAuthentication" onoffswitch />
+                    </div>
+                    <span tooltip-placement="right" tooltip="Enable/disable HTTP authentication of users with SPNEGO/Kerberos tokens. The data about authenticated users will be provisioned from this LDAP server" class="fa fa-info-circle"></span>
+                </div>
+                <div class="form-group clearfix" data-ng-show="instance.config.allowKerberosAuthentication">
+                    <label class="col-sm-2 control-label" for="kerberosRealm">Kerberos Realm <span class="required">*</span></label>
+                    <div class="col-sm-4">
+                        <input class="form-control" id="kerberosRealm" type="text" ng-model="instance.config.kerberosRealm" ng-required="instance.config.allowKerberosAuthentication">
+                    </div>
+                    <span tooltip-placement="right" tooltip="Name of kerberos realm. For example FOO.ORG" class="fa fa-info-circle"></span>
+                </div>
+                <div class="form-group clearfix" data-ng-show="instance.config.allowKerberosAuthentication">
+                    <label class="col-sm-2 control-label" for="serverPrincipal">Server principal <span class="required">*</span></label>
+                    <div class="col-sm-4">
+                        <input class="form-control" id="serverPrincipal" type="text" ng-model="instance.config.serverPrincipal" ng-required="instance.config.allowKerberosAuthentication">
+                    </div>
+                    <span tooltip-placement="right" tooltip="Full name of server principal for HTTP service including server and domain name. For example HTTP/host.foo.org@FOO.ORG" class="fa fa-info-circle"></span>
+                </div>
+                <div class="form-group clearfix" data-ng-show="instance.config.allowKerberosAuthentication">
+                    <label class="col-sm-2 control-label" for="keyTab">KeyTab <span class="required">*</span></label>
+                    <div class="col-sm-4">
+                        <input class="form-control" id="keyTab" type="text" ng-model="instance.config.keyTab" ng-required="instance.config.allowKerberosAuthentication">
+                    </div>
+                    <span tooltip-placement="right" tooltip="Location of Kerberos KeyTab file containing the credentials of server principal. For example /etc/krb5.keytab" class="fa fa-info-circle"></span>
+                </div>
+                <div class="form-group" data-ng-show="instance.config.allowKerberosAuthentication">
+                    <label class="col-sm-2 control-label" for="debug">Debug </label>
+                    <div class="col-sm-4">
+                        <input ng-model="instance.config.debug" id="debug" onoffswitch />
+                    </div>
+                    <span tooltip-placement="right" tooltip="Enable/disable debug logging to standard output for Krb5LoginModule." class="fa fa-info-circle"></span>
+                </div>
+                <div class="form-group" data-ng-show="instance.config.allowKerberosAuthentication">
+                    <label class="col-sm-2 control-label" for="debug">Use Kerberos For Password Authentication </label>
+                    <div class="col-sm-4">
+                        <input ng-model="instance.config.useKerberosForPasswordAuthentication" id="useKerberosForPasswordAuthentication" onoffswitch />
+                    </div>
+                    <span tooltip-placement="right" tooltip="Use Kerberos login module for authenticate username/password against Kerberos server instead of authenticating against LDAP server with Directory Service API" class="fa fa-info-circle"></span>
+                </div>
+            </fieldset>
+
+            <fieldset>
                 <legend><span class="text">Sync settings</span></legend>
                 <div class="form-group clearfix">
                     <label class="col-sm-2 control-label" for="batchSizeForSync">Batch size</label>
diff --git a/forms/common-themes/src/main/resources/theme/login/base/messages/messages.properties b/forms/common-themes/src/main/resources/theme/login/base/messages/messages.properties
index c7e1fff..5c5d1f7 100755
--- a/forms/common-themes/src/main/resources/theme/login/base/messages/messages.properties
+++ b/forms/common-themes/src/main/resources/theme/login/base/messages/messages.properties
@@ -98,8 +98,7 @@ actionPasswordWarning=You need to change your password to activate your account.
 actionEmailWarning=You need to verify your email address to activate your account.
 actionFollow=Please fill in the fields below.
 
-errorKerberosLogin=Kerberos ticket not available. Use different login mechanism
-errorKerberosLinkAccount=Kerberos ticket not available.
+errorKerberosLogin=Kerberos ticket not available. Authenticate with password.
 
 successHeader=Success!
 errorHeader=Error!
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 0d1f931..45f3adb 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
@@ -54,6 +54,8 @@ public interface LoginFormsProvider extends Provider {
 
     public LoginFormsProvider setQueryParams(MultivaluedMap<String, String> queryParams);
 
+    public LoginFormsProvider setResponseHeader(String headerName, String headerValue);
+
     public LoginFormsProvider setFormData(MultivaluedMap<String, String> formData);
 
     public LoginFormsProvider setStatus(Response.Status status);
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 1360800..44a7585 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
@@ -57,6 +57,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
     private List<RoleModel> realmRolesRequested;
     private MultivaluedMap<String, RoleModel> resourceRolesRequested;
     private MultivaluedMap<String, String> queryParams;
+    private Map<String, String> httpResponseHeaders = new HashMap<String, String>();
     private String accessRequestMessage;
     private URI actionUri;
 
@@ -226,6 +227,9 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
             String result = freeMarker.processTemplate(attributes, Templates.getTemplate(page), theme);
             Response.ResponseBuilder builder = Response.status(status).type(MediaType.TEXT_HTML).entity(result);
             BrowserSecurityHeaderSetup.headers(builder, realm);
+            for (Map.Entry<String, String> entry : httpResponseHeaders.entrySet()) {
+                builder.header(entry.getKey(), entry.getValue());
+            }
             return builder.build();
         } catch (FreeMarkerException e) {
             logger.error("Failed to process template", e);
@@ -336,6 +340,12 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
     }
 
     @Override
+    public LoginFormsProvider setResponseHeader(String headerName, String headerValue) {
+        this.httpResponseHeaders.put(headerName, headerValue);
+        return this;
+    }
+
+    @Override
     public void close() {
     }
 
diff --git a/model/api/src/main/java/org/keycloak/models/CredentialValidationOutput.java b/model/api/src/main/java/org/keycloak/models/CredentialValidationOutput.java
new file mode 100644
index 0000000..70e4093
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/CredentialValidationOutput.java
@@ -0,0 +1,46 @@
+package org.keycloak.models;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Output of credential validation
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class CredentialValidationOutput {
+
+    private final UserModel authenticatedUser; // authenticated user.
+    private final Status authStatus;           // status whether user is authenticated or more steps needed
+    private final Map<String, Object> state;   // Additional state related to authentication. It can contain data to be sent back to client or data about used credentials.
+
+    public CredentialValidationOutput(UserModel authenticatedUser, Status authStatus, Map<String, Object> state) {
+        this.authenticatedUser = authenticatedUser;
+        this.authStatus = authStatus;
+        this.state = state;
+    }
+
+    public static CredentialValidationOutput failed() {
+        return new CredentialValidationOutput(null, CredentialValidationOutput.Status.FAILED, new HashMap<String, Object>());
+    }
+
+    public UserModel getAuthenticatedUser() {
+        return authenticatedUser;
+    }
+
+    public Status getAuthStatus() {
+        return authStatus;
+    }
+
+    public Map<String, Object> getState() {
+        return state;
+    }
+
+    public CredentialValidationOutput merge(CredentialValidationOutput that) {
+        throw new IllegalStateException("Not supported yet");
+    }
+
+    public static enum Status {
+        AUTHENTICATED, FAILED, CONTINUE
+    }
+}
diff --git a/model/api/src/main/java/org/keycloak/models/RequiredCredentialModel.java b/model/api/src/main/java/org/keycloak/models/RequiredCredentialModel.java
index 9d77a7a..0840b04 100755
--- a/model/api/src/main/java/org/keycloak/models/RequiredCredentialModel.java
+++ b/model/api/src/main/java/org/keycloak/models/RequiredCredentialModel.java
@@ -54,6 +54,7 @@ public class RequiredCredentialModel {
     public static final RequiredCredentialModel TOTP;
     public static final RequiredCredentialModel CLIENT_CERT;
     public static final RequiredCredentialModel SECRET;
+    public static final RequiredCredentialModel KERBEROS;
 
     static {
         Map<String, RequiredCredentialModel> map = new HashMap<String, RequiredCredentialModel>();
@@ -81,6 +82,12 @@ public class RequiredCredentialModel {
         CLIENT_CERT.setSecret(false);
         CLIENT_CERT.setFormLabel("clientCertificate");
         map.put(CLIENT_CERT.getType(), CLIENT_CERT);
+        KERBEROS = new RequiredCredentialModel();
+        KERBEROS.setType(UserCredentialModel.KERBEROS);
+        KERBEROS.setInput(false);
+        KERBEROS.setSecret(false);
+        KERBEROS.setFormLabel("kerberos");
+        map.put(KERBEROS.getType(), KERBEROS);
         BUILT_IN = Collections.unmodifiableMap(map);
     }
 }
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 2f1fbf7..5fb6005 100755
--- a/model/api/src/main/java/org/keycloak/models/UserCredentialModel.java
+++ b/model/api/src/main/java/org/keycloak/models/UserCredentialModel.java
@@ -14,6 +14,7 @@ public class UserCredentialModel {
     public static final String SECRET = "secret";
     public static final String TOTP = "totp";
     public static final String CLIENT_CERT = "cert";
+    public static final String KERBEROS = "kerberos";
 
     protected String type;
     protected String value;
@@ -49,6 +50,13 @@ public class UserCredentialModel {
         return model;
     }
 
+    public static UserCredentialModel kerberos(String token) {
+        UserCredentialModel model = new UserCredentialModel();
+        model.setType(KERBEROS);
+        model.setValue(token);
+        return model;
+    }
+
     public static UserCredentialModel generateSecret() {
         UserCredentialModel model = new UserCredentialModel();
         model.setType(SECRET);
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 1b3bdd1..7d3ecf4 100755
--- a/model/api/src/main/java/org/keycloak/models/UserFederationManager.java
+++ b/model/api/src/main/java/org/keycloak/models/UserFederationManager.java
@@ -3,6 +3,7 @@ package org.keycloak.models;
 import org.jboss.logging.Logger;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
@@ -355,27 +356,40 @@ public class UserFederationManager implements UserProvider {
 
     @Override
     public boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input) {
-        UserFederationProvider link = getFederationLink(realm, user);
-        if (link != null) {
-            validateUser(realm, user);
-            Set<String> supportedCredentialTypes = link.getSupportedCredentialTypes(user);
-            if (supportedCredentialTypes.size() > 0) {
-                List<UserCredentialModel> fedCreds = new ArrayList<UserCredentialModel>();
-                List<UserCredentialModel> localCreds = new ArrayList<UserCredentialModel>();
-                for (UserCredentialModel cred : input) {
-                    if (supportedCredentialTypes.contains(cred.getType())) {
-                        fedCreds.add(cred);
-                    } else {
-                        localCreds.add(cred);
-                    }
-                }
-                if (!link.validCredentials(realm, user, fedCreds)) {
-                    return false;
+        return validCredentials(realm, user, Arrays.asList(input));
+    }
+
+    @Override
+    public CredentialValidationOutput validCredentials(RealmModel realm, UserCredentialModel... input) {
+        List<UserFederationProviderModel> fedProviderModels = realm.getUserFederationProviders();
+        List<UserFederationProvider> fedProviders = new ArrayList<UserFederationProvider>();
+        for (UserFederationProviderModel fedProviderModel : fedProviderModels) {
+            fedProviders.add(getFederationProvider(fedProviderModel));
+        }
+
+        CredentialValidationOutput result = null;
+        for (UserCredentialModel cred : input) {
+            UserFederationProvider providerSupportingCreds = null;
+
+            // Find provider, which supports required credential type
+            for (UserFederationProvider fedProvider : fedProviders) {
+                if (fedProvider.getSupportedCredentialTypes().contains(cred.getType())) {
+                    providerSupportingCreds = fedProvider;
+                    break;
                 }
-                return session.userStorage().validCredentials(realm, user, localCreds);
             }
+
+            if (providerSupportingCreds == null) {
+                logger.warn("Don't have provider supporting credentials of type " + cred.getType());
+                return CredentialValidationOutput.failed();
+            }
+
+            CredentialValidationOutput currentResult = providerSupportingCreds.validCredentials(realm, cred);
+            result = (result == null) ? currentResult : result.merge(currentResult);
         }
-        return session.userStorage().validCredentials(realm, user, input);
+
+        // For now, validCredentials(realm, input) is not supported for local userProviders
+        return (result != null) ? result : CredentialValidationOutput.failed();
     }
 
     @Override
diff --git a/model/api/src/main/java/org/keycloak/models/UserFederationProvider.java b/model/api/src/main/java/org/keycloak/models/UserFederationProvider.java
index 16e81b5..948e8c9 100755
--- a/model/api/src/main/java/org/keycloak/models/UserFederationProvider.java
+++ b/model/api/src/main/java/org/keycloak/models/UserFederationProvider.java
@@ -131,6 +131,14 @@ public interface UserFederationProvider extends Provider {
     Set<String> getSupportedCredentialTypes(UserModel user);
 
     /**
+     * What UserCredentialModel types should be handled by this provider? This is called in scenarios when we don't know user,
+     * who is going to authenticate (For example Kerberos authentication).
+     *
+     * @return
+     */
+    Set<String> getSupportedCredentialTypes();
+
+    /**
      * Validate credentials for this user.  This method will only be called with credential parameters supported
      * by this provider
      *
@@ -141,6 +149,15 @@ public interface UserFederationProvider extends Provider {
      */
     boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input);
     boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input);
+
+    /**
+     * Validate credentials of unknown user. The authenticated user is recognized based on provided credentials and returned back in CredentialValidationOutput
+     * @param realm
+     * @param input
+     * @return
+     */
+    CredentialValidationOutput validCredentials(RealmModel realm, UserCredentialModel credential);
+
     void close();
 
 }
diff --git a/model/api/src/main/java/org/keycloak/models/UserProvider.java b/model/api/src/main/java/org/keycloak/models/UserProvider.java
index 0d0a25b..b508638 100755
--- a/model/api/src/main/java/org/keycloak/models/UserProvider.java
+++ b/model/api/src/main/java/org/keycloak/models/UserProvider.java
@@ -43,5 +43,7 @@ public interface UserProvider extends Provider {
 
     boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input);
     boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input);
+    CredentialValidationOutput validCredentials(RealmModel realm, UserCredentialModel... input);
+
     void close();
 }
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheUserProvider.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheUserProvider.java
index 017a26e..8dbb044 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheUserProvider.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheUserProvider.java
@@ -1,5 +1,6 @@
 package org.keycloak.models.cache;
 
+import org.keycloak.models.CredentialValidationOutput;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakTransaction;
 import org.keycloak.models.RealmModel;
@@ -283,6 +284,11 @@ public class DefaultCacheUserProvider implements CacheUserProvider {
     }
 
     @Override
+    public CredentialValidationOutput validCredentials(RealmModel realm, UserCredentialModel... input) {
+        return getDelegate().validCredentials(realm, input);
+    }
+
+    @Override
     public void preRemove(RealmModel realm) {
         realmInvalidations.add(realm.getId());
         getDelegate().preRemove(realm);
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheUserProvider.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheUserProvider.java
index e468b16..857b3e8 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheUserProvider.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheUserProvider.java
@@ -1,5 +1,6 @@
 package org.keycloak.models.cache;
 
+import org.keycloak.models.CredentialValidationOutput;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RoleModel;
@@ -156,6 +157,11 @@ public class NoCacheUserProvider implements CacheUserProvider {
     }
 
     @Override
+    public CredentialValidationOutput validCredentials(RealmModel realm, UserCredentialModel... input) {
+        return getDelegate().validCredentials(realm, input);
+    }
+
+    @Override
     public void preRemove(RealmModel realm) {
         getDelegate().preRemove(realm);
     }
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
index 438c700..c94c068 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
@@ -1,6 +1,7 @@
 package org.keycloak.models.jpa;
 
 import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.CredentialValidationOutput;
 import org.keycloak.models.FederatedIdentityModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
@@ -375,4 +376,10 @@ public class JpaUserProvider implements UserProvider {
     public boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input) {
         return CredentialValidation.validCredentials(realm, user, input);
     }
+
+    @Override
+    public CredentialValidationOutput validCredentials(RealmModel realm, UserCredentialModel... input) {
+        // Not supported yet
+        return null;
+    }
 }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
index 9422711..5babac0 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
@@ -6,6 +6,7 @@ import com.mongodb.QueryBuilder;
 import org.keycloak.connections.mongo.api.MongoStore;
 import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
 import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.CredentialValidationOutput;
 import org.keycloak.models.FederatedIdentityModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
@@ -378,4 +379,10 @@ public class MongoUserProvider implements UserProvider {
     public boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input) {
         return CredentialValidation.validCredentials(realm, user, input);
     }
+
+    @Override
+    public CredentialValidationOutput validCredentials(RealmModel realm, UserCredentialModel... input) {
+        // Not supported yet
+        return null;
+    }
 }
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectService.java b/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectService.java
index fd20b16..acf39cf 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectService.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectService.java
@@ -41,6 +41,7 @@ import org.keycloak.services.ForbiddenException;
 import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.managers.AuthenticationManager.AuthenticationStatus;
 import org.keycloak.services.managers.ClientSessionCode;
+import org.keycloak.services.managers.HttpAuthenticationManager;
 import org.keycloak.services.resources.Cors;
 import org.keycloak.services.resources.RealmsResource;
 import org.keycloak.services.resources.flows.Flows;
@@ -883,6 +884,11 @@ public class OpenIDConnectService {
         response = authManager.checkNonFormAuthentication(session, clientSession, realm, uriInfo, request, clientConnection, headers, event);
         if (response != null) return response;
 
+        // SPNEGO/Kerberos authentication TODO: This should be somehow pluggable instead of hardcoded this way (Authentication interceptors?)
+        HttpAuthenticationManager httpAuthManager = new HttpAuthenticationManager(session, clientSession, realm, uriInfo, request, clientConnection, event);
+        HttpAuthenticationManager.HttpAuthOutput httpAuthOutput = httpAuthManager.spnegoAuthenticate();
+        if (httpAuthOutput.getResponse() != null) return httpAuthOutput.getResponse();
+
         if (prompt != null && prompt.equals("none")) {
             OpenIDConnect oauth = new OpenIDConnect(session, realm, uriInfo);
             return oauth.cancelLogin(clientSession);
@@ -911,6 +917,11 @@ public class OpenIDConnectService {
         LoginFormsProvider forms = Flows.forms(session, realm, clientSession.getClient(), uriInfo)
                 .setClientSessionCode(accessCode);
 
+        // Attach state from SPNEGO authentication
+        if (httpAuthOutput.getChallenge() != null) {
+            httpAuthOutput.getChallenge().sendChallenge(forms);
+        }
+
         String rememberMeUsername = AuthenticationManager.getRememberMeUsername(realm, headers);
 
         if (loginHint != null || rememberMeUsername != null) {
diff --git a/services/src/main/java/org/keycloak/services/managers/HttpAuthenticationChallenge.java b/services/src/main/java/org/keycloak/services/managers/HttpAuthenticationChallenge.java
new file mode 100644
index 0000000..0be8bcc
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/managers/HttpAuthenticationChallenge.java
@@ -0,0 +1,11 @@
+package org.keycloak.services.managers;
+
+import org.keycloak.login.LoginFormsProvider;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public interface HttpAuthenticationChallenge {
+
+    void addChallenge(LoginFormsProvider loginFormsProvider);
+}
diff --git a/services/src/main/java/org/keycloak/services/managers/HttpAuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/HttpAuthenticationManager.java
new file mode 100644
index 0000000..ca0488e
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/managers/HttpAuthenticationManager.java
@@ -0,0 +1,167 @@
+package org.keycloak.services.managers;
+
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import org.jboss.logging.Logger;
+import org.jboss.resteasy.spi.HttpRequest;
+import org.keycloak.ClientConnection;
+import org.keycloak.events.Errors;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.login.LoginFormsProvider;
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.CredentialValidationOutput;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RequiredCredentialModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.utils.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;
+
+/**
+ * Handle HTTP authentication types requiring complex handshakes with multiple HTTP request/responses
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class HttpAuthenticationManager {
+
+    private static final Logger logger = Logger.getLogger(HttpAuthenticationManager.class);
+
+    private KeycloakSession session;
+    private RealmModel realm;
+    private UriInfo uriInfo;
+    private HttpRequest request;
+    private EventBuilder event;
+    private ClientConnection clientConnection;
+    private ClientSessionModel clientSession;
+
+    public HttpAuthenticationManager(KeycloakSession session, ClientSessionModel clientSession, RealmModel realm, UriInfo uriInfo,
+                                     HttpRequest request,
+                                     ClientConnection clientConnection,
+                                     EventBuilder event) {
+        this.session = session;
+        this.realm = realm;
+        this.uriInfo = uriInfo;
+        this.request = request;
+        this.event = event;
+        this.clientConnection = clientConnection;
+        this.clientSession = clientSession;
+    }
+
+
+    public HttpAuthOutput spnegoAuthenticate() {
+        boolean kerberosSupported = false;
+        for (RequiredCredentialModel c : realm.getRequiredCredentials()) {
+            if (c.getType().equals(CredentialRepresentation.KERBEROS)) {
+                logger.debug("Kerberos authentication is supported");
+                kerberosSupported = true;
+            }
+        }
+
+        if (!kerberosSupported) {
+            return new HttpAuthOutput(null, null);
+        }
+
+        String authHeader = request.getHttpHeaders().getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION);
+
+        // Case when we don't yet have any Negotiate header
+        if (authHeader == null) {
+            return challengeNegotiation(null);
+        }
+
+        String[] tokens = authHeader.split(" ");
+        if (tokens.length != 2) {
+            logger.warn("Invalid length of tokens: " + tokens.length);
+            return challengeNegotiation(null);
+        } else if (!KerberosConstants.NEGOTIATE.equalsIgnoreCase(tokens[0])) {
+            logger.warn("Unknown scheme " + tokens[0]);
+            return challengeNegotiation(null);
+        } else {
+            String spnegoToken = tokens[1];
+            UserCredentialModel spnegoCredential = UserCredentialModel.kerberos(spnegoToken);
+
+            CredentialValidationOutput output = session.users().validCredentials(realm, spnegoCredential);
+
+            if (output.getAuthStatus() == CredentialValidationOutput.Status.AUTHENTICATED) {
+                return sendResponse(output.getAuthenticatedUser(), "spnego");
+            }  else {
+                String spnegoResponseToken = (String) output.getState().get(KerberosConstants.RESPONSE_TOKEN);
+                return challengeNegotiation(spnegoResponseToken);
+            }
+        }
+    }
+
+
+    // Send response after successful authentication
+    private HttpAuthOutput sendResponse(UserModel user, String authMethod) {
+        Response response;
+        if (!user.isEnabled()) {
+            event.error(Errors.USER_DISABLED);
+            response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, Messages.ACCOUNT_DISABLED);
+        } else {
+            UserSessionModel userSession = session.sessions().createUserSession(realm, user, user.getUsername(), clientConnection.getRemoteAddr(), authMethod, false);
+            TokenManager.attachClientSession(userSession, clientSession);
+            event.session(userSession);
+            response = AuthenticationManager.nextActionAfterAuthentication(session, userSession, clientSession, clientConnection, request, uriInfo, event);
+        }
+
+        return new HttpAuthOutput(response, null);
+    }
+
+
+    private HttpAuthOutput challengeNegotiation(final String negotiateToken) {
+        return new HttpAuthOutput(null, new HttpAuthChallenge() {
+
+            @Override
+            public void sendChallenge(LoginFormsProvider loginFormsProvider) {
+                String negotiateHeader = negotiateToken == null ? KerberosConstants.NEGOTIATE : KerberosConstants.NEGOTIATE + " " + negotiateToken;
+
+                if (logger.isTraceEnabled()) {
+                    logger.trace("Sending back " + HttpHeaders.WWW_AUTHENTICATE + ": " + negotiateHeader);
+                }
+
+                loginFormsProvider.setStatus(Response.Status.UNAUTHORIZED);
+                loginFormsProvider.setResponseHeader(HttpHeaders.WWW_AUTHENTICATE, negotiateHeader);
+                loginFormsProvider.setWarning("errorKerberosLogin");
+            }
+
+        });
+    }
+
+
+    public class HttpAuthOutput {
+
+        // It's non-null if we want to immediately send response to user
+        private final Response response;
+
+        // It's non-null if challenge should be attached to rendered login form
+        private final HttpAuthChallenge challenge;
+
+        public HttpAuthOutput(Response response, HttpAuthChallenge challenge) {
+            this.response = response;
+            this.challenge = challenge;
+        }
+
+        public Response getResponse() {
+            return response;
+        }
+
+        public HttpAuthChallenge getChallenge() {
+            return challenge;
+        }
+    }
+
+
+    public interface HttpAuthChallenge {
+
+        void sendChallenge(LoginFormsProvider loginFormsProvider);
+
+    }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UserFederationResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UserFederationResource.java
index 751f092..730252c 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UserFederationResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UserFederationResource.java
@@ -5,9 +5,12 @@ import org.jboss.resteasy.annotations.cache.NoCache;
 import org.jboss.resteasy.spi.NotFoundException;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
+import org.keycloak.models.RequiredCredentialModel;
+import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserFederationProvider;
 import org.keycloak.models.UserFederationProviderFactory;
 import org.keycloak.models.UserFederationProviderModel;
+import org.keycloak.models.utils.KerberosConstants;
 import org.keycloak.models.utils.ModelToRepresentation;
 import org.keycloak.provider.ProviderFactory;
 import org.keycloak.representations.idm.UserFederationProviderFactoryRepresentation;
@@ -118,6 +121,7 @@ public class UserFederationResource {
         UserFederationProviderModel model = realm.addUserFederationProvider(rep.getProviderName(), rep.getConfig(), rep.getPriority(), displayName,
                 rep.getFullSyncPeriod(), rep.getChangedSyncPeriod(), rep.getLastSync());
         new UsersSyncManager().refreshPeriodicSyncForProvider(session.getKeycloakSessionFactory(), session.getProvider(TimerProvider.class), model, realm.getId());
+        checkKerberosCredential(model);
 
         return Response.created(uriInfo.getAbsolutePathBuilder().path(model.getId()).build()).build();
     }
@@ -141,6 +145,7 @@ public class UserFederationResource {
                 rep.getFullSyncPeriod(), rep.getChangedSyncPeriod(), rep.getLastSync());
         realm.updateUserFederationProvider(model);
         new UsersSyncManager().refreshPeriodicSyncForProvider(session.getKeycloakSessionFactory(), session.getProvider(TimerProvider.class), model, realm.getId());
+        checkKerberosCredential(model);
     }
 
     /**
@@ -223,5 +228,23 @@ public class UserFederationResource {
         throw new NotFoundException("could not find provider");
     }
 
+    // Automatically add "kerberos" to required realm credentials if it's supported by saved provider
+    private void checkKerberosCredential(UserFederationProviderModel model) {
+        String allowKerberosCfg = model.getConfig().get(KerberosConstants.ALLOW_KERBEROS_AUTHENTICATION);
+        if (Boolean.valueOf(allowKerberosCfg)) {
+            boolean found = false;
+            List<RequiredCredentialModel> currentCreds = realm.getRequiredCredentials();
+            for (RequiredCredentialModel cred : currentCreds) {
+                if (cred.getType().equals(UserCredentialModel.KERBEROS)) {
+                    found = true;
+                }
+            }
+
+            if (!found) {
+                realm.addRequiredCredential(UserCredentialModel.KERBEROS);
+                logger.info("Added 'kerberos' to required realm credentials");
+            }
+        }
+    }
 
 }
diff --git a/testsuite/integration/src/main/java/org/keycloak/testutils/DummyUserFederationProvider.java b/testsuite/integration/src/main/java/org/keycloak/testutils/DummyUserFederationProvider.java
index 753409a..9964421 100755
--- a/testsuite/integration/src/main/java/org/keycloak/testutils/DummyUserFederationProvider.java
+++ b/testsuite/integration/src/main/java/org/keycloak/testutils/DummyUserFederationProvider.java
@@ -1,5 +1,6 @@
 package org.keycloak.testutils;
 
+import org.keycloak.models.CredentialValidationOutput;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserCredentialModel;
@@ -77,6 +78,11 @@ public class DummyUserFederationProvider implements UserFederationProvider {
     }
 
     @Override
+    public Set<String> getSupportedCredentialTypes() {
+        return Collections.emptySet();
+    }
+
+    @Override
     public boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input) {
         return false;
     }
@@ -87,6 +93,11 @@ public class DummyUserFederationProvider implements UserFederationProvider {
     }
 
     @Override
+    public CredentialValidationOutput validCredentials(RealmModel realm, UserCredentialModel credential) {
+        return CredentialValidationOutput.failed();
+    }
+
+    @Override
     public void close() {
 
     }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderModelTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderModelTest.java
index 45f638c..2dd3164 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderModelTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderModelTest.java
@@ -18,7 +18,6 @@
 package org.keycloak.testsuite.broker;
 
 import org.junit.Before;
-import org.keycloak.broker.kerberos.KerberosIdentityProviderFactory;
 import org.keycloak.broker.oidc.OIDCIdentityProviderFactory;
 import org.keycloak.broker.saml.SAMLIdentityProviderFactory;
 import org.keycloak.social.facebook.FacebookIdentityProviderFactory;
@@ -48,7 +47,6 @@ public abstract class AbstractIdentityProviderModelTest extends AbstractModelTes
         this.expectedProviders.add(FacebookIdentityProviderFactory.PROVIDER_ID);
         this.expectedProviders.add(GitHubIdentityProviderFactory.PROVIDER_ID);
         this.expectedProviders.add(TwitterIdentityProviderFactory.PROVIDER_ID);
-        this.expectedProviders.add(KerberosIdentityProviderFactory.PROVIDER_ID);
 
         this.expectedProviders = Collections.unmodifiableSet(this.expectedProviders);
     }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/ImportIdentityProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/ImportIdentityProviderTest.java
index 9b9d723..36d2bda 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/ImportIdentityProviderTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/ImportIdentityProviderTest.java
@@ -18,9 +18,6 @@
 package org.keycloak.testsuite.broker;
 
 import org.junit.Test;
-import org.keycloak.broker.kerberos.KerberosIdentityProvider;
-import org.keycloak.broker.kerberos.KerberosIdentityProviderConfig;
-import org.keycloak.broker.kerberos.KerberosIdentityProviderFactory;
 import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
 import org.keycloak.broker.oidc.OIDCIdentityProvider;
 import org.keycloak.broker.oidc.OIDCIdentityProviderConfig;
@@ -157,8 +154,6 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
                     assertGitHubIdentityProviderConfig(identityProvider);
                 } else if (TwitterIdentityProviderFactory.PROVIDER_ID.equals(providerId)) {
                     assertTwitterIdentityProviderConfig(identityProvider);
-                } else if (KerberosIdentityProviderFactory.PROVIDER_ID.equals(providerId)) {
-                    assertKerberosIdentityProviderConfig(identityProvider);
                 } else {
                     continue;
                 }
@@ -276,21 +271,6 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
         assertEquals("clientSecret", config.getClientSecret());
     }
 
-    private void assertKerberosIdentityProviderConfig(IdentityProviderModel identityProvider) {
-        KerberosIdentityProvider kerberosIdentityProvider = new KerberosIdentityProviderFactory().create(identityProvider);
-        KerberosIdentityProviderConfig config = kerberosIdentityProvider.getConfig();
-
-        assertEquals("model-kerberos", config.getId());
-        assertEquals(KerberosIdentityProviderFactory.PROVIDER_ID, config.getProviderId());
-        assertEquals("Kerberos", config.getName());
-        assertEquals(true, config.isEnabled());
-        assertEquals(true, config.isUpdateProfileFirstLogin());
-        assertEquals(false, config.isAuthenticateByDefault());
-        assertEquals("HTTP/server.domain.org@DOMAIN.ORG", config.getServerPrincipal());
-        assertEquals("/etc/http.keytab", config.getKeyTab());
-        assertTrue(config.getDebug());
-    }
-
     private RealmModel installTestRealm() throws IOException {
         RealmRepresentation realmRepresentation = loadJson("broker-test/test-realm-with-broker.json");