keycloak-aplcache

Changes

core/src/main/java/org/keycloak/representations/idm/AuthenticationMappingRepresentation.java 39(+0 -39)

spi/authentication-spi/src/main/java/org/keycloak/spi/authentication/AuthResult.java 44(+0 -44)

Details

diff --git a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
index fc72ef0..a4278d2 100755
--- a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
@@ -39,7 +39,6 @@ public class RealmRepresentation {
     protected Map<String, List<UserRoleMappingRepresentation>> applicationRoleMappings;
     protected Map<String, List<ScopeMappingRepresentation>> applicationScopeMappings;
     protected List<SocialMappingRepresentation> socialMappings;
-    protected List<AuthenticationMappingRepresentation> authenticationMappings;
     protected List<ApplicationRepresentation> applications;
     protected List<OAuthClientRepresentation> oauthClients;
     protected Map<String, String> socialProviders;
@@ -181,18 +180,6 @@ public class RealmRepresentation {
         return mapping;
     }
 
-    public List<AuthenticationMappingRepresentation> getAuthenticationMappings() {
-        return authenticationMappings;
-    }
-
-    public AuthenticationMappingRepresentation authenticationMapping(String username) {
-        AuthenticationMappingRepresentation mapping = new AuthenticationMappingRepresentation();
-        mapping.setUsername(username);
-        if (authenticationMappings == null) authenticationMappings = new ArrayList<AuthenticationMappingRepresentation>();
-        authenticationMappings.add(mapping);
-        return mapping;
-    }
-
     public Set<String> getRequiredCredentials() {
         return requiredCredentials;
     }
diff --git a/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java
index 5fc55f0..43aa368 100755
--- a/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java
@@ -20,6 +20,7 @@ public class UserRepresentation {
     protected String firstName;
     protected String lastName;
     protected String email;
+    protected AuthenticationLinkRepresentation authenticationLink;
     protected Map<String, String> attributes;
     protected List<CredentialRepresentation> credentials;
     protected List<String> requiredActions;
@@ -96,6 +97,14 @@ public class UserRepresentation {
         this.emailVerified = emailVerified;
     }
 
+    public AuthenticationLinkRepresentation getAuthenticationLink() {
+        return authenticationLink;
+    }
+
+    public void setAuthenticationLink(AuthenticationLinkRepresentation authenticationLink) {
+        this.authenticationLink = authenticationLink;
+    }
+
     public Map<String, String> getAttributes() {
         return attributes;
     }
diff --git a/model/api/src/main/java/org/keycloak/models/RealmModel.java b/model/api/src/main/java/org/keycloak/models/RealmModel.java
index 827f628..1fd0843 100755
--- a/model/api/src/main/java/org/keycloak/models/RealmModel.java
+++ b/model/api/src/main/java/org/keycloak/models/RealmModel.java
@@ -134,11 +134,9 @@ public interface RealmModel extends RoleContainerModel, RoleMapperModel, ScopeMa
 
     boolean removeSocialLink(UserModel user, String socialProvider);
 
-    UserModel getUserByAuthenticationLink(AuthenticationLinkModel authenticationLink);
+    AuthenticationLinkModel getAuthenticationLink(UserModel user);
 
-    Set<AuthenticationLinkModel> getAuthenticationLinks(UserModel user);
-
-    void addAuthenticationLink(UserModel user, AuthenticationLinkModel authenticationLink);
+    void setAuthenticationLink(UserModel user, AuthenticationLinkModel authenticationLink);
 
     boolean isSocial();
 
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationLinkEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationLinkEntity.java
index 0eecfba..e6ae8d4 100644
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationLinkEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationLinkEntity.java
@@ -6,16 +6,13 @@ import javax.persistence.Id;
 import javax.persistence.ManyToOne;
 import javax.persistence.NamedQueries;
 import javax.persistence.NamedQuery;
+import javax.persistence.OneToOne;
 
 import org.hibernate.annotations.GenericGenerator;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
-@NamedQueries({
-        @NamedQuery(name="findAuthLinkByUser", query="select link from AuthenticationLinkEntity link where link.user = :user"),
-        @NamedQuery(name="findUserByAuthLinkAndRealm", query="select link.user from AuthenticationLinkEntity link where link.realm = :realm and link.authProvider = :authProvider and link.authUserId = :authUserId")
-})
 @Entity
 public class AuthenticationLinkEntity {
 
@@ -24,12 +21,6 @@ public class AuthenticationLinkEntity {
     @GeneratedValue(generator = "keycloak_generator")
     private String id;
 
-    @ManyToOne
-    private UserEntity user;
-
-    @ManyToOne
-    protected RealmEntity realm;
-
     protected String authProvider;
     protected String authUserId;
 
@@ -41,22 +32,6 @@ public class AuthenticationLinkEntity {
         this.id = id;
     }
 
-    public UserEntity getUser() {
-        return user;
-    }
-
-    public void setUser(UserEntity user) {
-        this.user = user;
-    }
-
-    public RealmEntity getRealm() {
-        return realm;
-    }
-
-    public void setRealm(RealmEntity realm) {
-        this.realm = realm;
-    }
-
     public String getAuthProvider() {
         return authProvider;
     }
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java
index ab25a64..9d8435d 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java
@@ -15,6 +15,8 @@ import javax.persistence.MapKeyColumn;
 import javax.persistence.NamedQueries;
 import javax.persistence.NamedQuery;
 import javax.persistence.OneToMany;
+import javax.persistence.OneToOne;
+
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
@@ -64,6 +66,9 @@ public class UserEntity {
     @OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true)
     protected Collection<CredentialEntity> credentials = new ArrayList<CredentialEntity>();
 
+    @OneToOne(cascade = CascadeType.REMOVE, orphanRemoval = true)
+    protected AuthenticationLinkEntity authenticationLink;
+
     public String getId() {
         return id;
     }
@@ -160,6 +165,14 @@ public class UserEntity {
         this.credentials = credentials;
     }
 
+    public AuthenticationLinkEntity getAuthenticationLink() {
+        return authenticationLink;
+    }
+
+    public void setAuthenticationLink(AuthenticationLinkEntity authenticationLink) {
+        this.authenticationLink = authenticationLink;
+    }
+
     public int getNotBefore() {
         return notBefore;
     }
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
index 08e6f39..c0483af 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
@@ -395,7 +395,9 @@ public class RealmAdapter implements RealmModel {
     private void removeUser(UserEntity user) {
         em.createQuery("delete from " + UserRoleMappingEntity.class.getSimpleName() + " where user = :user").setParameter("user", user).executeUpdate();
         em.createQuery("delete from " + SocialLinkEntity.class.getSimpleName() + " where user = :user").setParameter("user", user).executeUpdate();
-        em.createQuery("delete from " + AuthenticationLinkEntity.class.getSimpleName() + " where user = :user").setParameter("user", user).executeUpdate();
+        if (user.getAuthenticationLink() != null) {
+            em.remove(user.getAuthenticationLink());
+        }
         em.remove(user);
     }
 
@@ -614,43 +616,22 @@ public class RealmAdapter implements RealmModel {
     }
 
     @Override
-    public UserModel getUserByAuthenticationLink(AuthenticationLinkModel authenticationLink) {
-        TypedQuery<UserEntity> query = em.createNamedQuery("findUserByAuthLinkAndRealm", UserEntity.class);
-        query.setParameter("realm", realm);
-        query.setParameter("authProvider", authenticationLink.getAuthProvider());
-        query.setParameter("authUserId", authenticationLink.getAuthUserId());
-        List<UserEntity> results = query.getResultList();
-        if (results.isEmpty()) {
-            return null;
-        } else if (results.size() > 1) {
-            throw new IllegalStateException("More results found for authenticationProvider=" + authenticationLink.getAuthProvider() +
-                    ", authUserId=" + authenticationLink.getAuthUserId() + ", results=" + results);
-        } else {
-            UserEntity user = results.get(0);
-            return new UserAdapter(user);
-        }
-    }
-
-    @Override
-    public Set<AuthenticationLinkModel> getAuthenticationLinks(UserModel user) {
-        TypedQuery<AuthenticationLinkEntity> query = em.createNamedQuery("findAuthLinkByUser", AuthenticationLinkEntity.class);
-        query.setParameter("user", ((UserAdapter) user).getUser());
-        List<AuthenticationLinkEntity> results = query.getResultList();
-        Set<AuthenticationLinkModel> set = new HashSet<AuthenticationLinkModel>();
-        for (AuthenticationLinkEntity entity : results) {
-            set.add(new AuthenticationLinkModel(entity.getAuthProvider(), entity.getAuthUserId()));
-        }
-        return set;
+    public AuthenticationLinkModel getAuthenticationLink(UserModel user) {
+        UserEntity userEntity = ((UserAdapter) user).getUser();
+        AuthenticationLinkEntity authLinkEntity = userEntity.getAuthenticationLink();
+        return authLinkEntity == null ? null : new AuthenticationLinkModel(authLinkEntity.getAuthProvider(), authLinkEntity.getAuthUserId());
     }
 
     @Override
-    public void addAuthenticationLink(UserModel user, AuthenticationLinkModel authenticationLink) {
+    public void setAuthenticationLink(UserModel user, AuthenticationLinkModel authenticationLink) {
         AuthenticationLinkEntity entity = new AuthenticationLinkEntity();
-        entity.setRealm(realm);
         entity.setAuthProvider(authenticationLink.getAuthProvider());
         entity.setAuthUserId(authenticationLink.getAuthUserId());
-        entity.setUser(((UserAdapter) user).getUser());
+
+        UserEntity userEntity = ((UserAdapter) user).getUser();
+        userEntity.setAuthenticationLink(entity);
         em.persist(entity);
+        em.persist(userEntity);
         em.flush();
     }
 
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
index fb9619a..e5f6696 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
@@ -927,41 +927,26 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
     }
 
     @Override
-    public UserModel getUserByAuthenticationLink(AuthenticationLinkModel authenticationLink) {
-        DBObject query = new QueryBuilder()
-                .and("authenticationLinks.authProvider").is(authenticationLink.getAuthProvider())
-                .and("authenticationLinks.authUserId").is(authenticationLink.getAuthUserId())
-                .and("realmId").is(getId())
-                .get();
-        UserEntity userEntity = getMongoStore().loadSingleEntity(UserEntity.class, query, invocationContext);
-        return userEntity==null ? null : new UserAdapter(userEntity, invocationContext);
-    }
-
-    @Override
-    public Set<AuthenticationLinkModel> getAuthenticationLinks(UserModel user) {
+    public AuthenticationLinkModel getAuthenticationLink(UserModel user) {
         UserEntity userEntity = ((UserAdapter)user).getUser();
-        List<AuthenticationLinkEntity> linkEntities = userEntity.getAuthenticationLinks();
+        AuthenticationLinkEntity authLinkEntity = userEntity.getAuthenticationLink();
 
-        if (linkEntities == null) {
-            return Collections.EMPTY_SET;
-        }
-
-        Set<AuthenticationLinkModel> result = new HashSet<AuthenticationLinkModel>();
-        for (AuthenticationLinkEntity authLinkEntity : linkEntities) {
-            AuthenticationLinkModel model = new AuthenticationLinkModel(authLinkEntity.getAuthProvider(), authLinkEntity.getAuthUserId());
-            result.add(model);
+        if (authLinkEntity == null) {
+            return null;
+        }  else {
+            return new AuthenticationLinkModel(authLinkEntity.getAuthProvider(), authLinkEntity.getAuthUserId());
         }
-        return result;
     }
 
     @Override
-    public void addAuthenticationLink(UserModel user, AuthenticationLinkModel authenticationLink) {
+    public void setAuthenticationLink(UserModel user, AuthenticationLinkModel authenticationLink) {
         UserEntity userEntity = ((UserAdapter)user).getUser();
         AuthenticationLinkEntity authLinkEntity = new AuthenticationLinkEntity();
         authLinkEntity.setAuthProvider(authenticationLink.getAuthProvider());
         authLinkEntity.setAuthUserId(authenticationLink.getAuthUserId());
+        userEntity.setAuthenticationLink(authLinkEntity);
 
-        getMongoStore().pushItemToList(userEntity, "authenticationLinks", authLinkEntity, true, invocationContext);
+        getMongoStore().updateEntity(userEntity, invocationContext);
     }
 
     protected void updateRealm() {
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/UserEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/UserEntity.java
index f0539df..96b8791 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/UserEntity.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/UserEntity.java
@@ -33,7 +33,7 @@ public class UserEntity extends AbstractMongoIdentifiableEntity implements Mongo
     private List<UserModel.RequiredAction> requiredActions;
     private List<CredentialEntity> credentials = new ArrayList<CredentialEntity>();
     private List<SocialLinkEntity> socialLinks;
-    private List<AuthenticationLinkEntity> authenticationLinks;
+    private AuthenticationLinkEntity authenticationLink;
 
     @MongoField
     public String getLoginName() {
@@ -163,11 +163,11 @@ public class UserEntity extends AbstractMongoIdentifiableEntity implements Mongo
     }
 
     @MongoField
-    public List<AuthenticationLinkEntity> getAuthenticationLinks() {
-        return authenticationLinks;
+    public AuthenticationLinkEntity getAuthenticationLink() {
+        return authenticationLink;
     }
 
-    public void setAuthenticationLinks(List<AuthenticationLinkEntity> authenticationLinks) {
-        this.authenticationLinks = authenticationLinks;
+    public void setAuthenticationLink(AuthenticationLinkEntity authenticationLink) {
+        this.authenticationLink = authenticationLink;
     }
 }
diff --git a/model/tests/src/test/java/org/keycloak/model/test/AuthProvidersExternalModelTest.java b/model/tests/src/test/java/org/keycloak/model/test/AuthProvidersExternalModelTest.java
index 3f57de3..e365d1e 100644
--- a/model/tests/src/test/java/org/keycloak/model/test/AuthProvidersExternalModelTest.java
+++ b/model/tests/src/test/java/org/keycloak/model/test/AuthProvidersExternalModelTest.java
@@ -95,10 +95,10 @@ public class AuthProvidersExternalModelTest extends AbstractModelTest {
             Assert.assertEquals("john@email.org", john2.getEmail());
 
             // Verify link exists
-            Set<AuthenticationLinkModel> authLinks = realm2.getAuthenticationLinks(john2);
-            Assert.assertEquals(1, authLinks.size());
-            AuthenticationLinkModel authLink = authLinks.iterator().next();
+            AuthenticationLinkModel authLink = realm2.getAuthenticationLink(john2);
+            Assert.assertNotNull(authLink);
             Assert.assertEquals(authLink.getAuthProvider(), AuthProviderConstants.PROVIDER_NAME_EXTERNAL_MODEL);
+            Assert.assertEquals(authLink.getAuthUserId(), realm1.getUser("john").getId());
         } finally {
             ResteasyProviderFactory.clearContextData();
         }
@@ -110,14 +110,19 @@ public class AuthProvidersExternalModelTest extends AbstractModelTest {
         // Add externalModel authenticationProvider into realm2 and point to realm1
         setupAuthenticationProviders();
 
+        // Add john to realm2 and set authentication link
+        UserModel john = realm2.addUser("john");
+        john.setEnabled(true);
+        realm2.setAuthenticationLink(john, new AuthenticationLinkModel(AuthProviderConstants.PROVIDER_NAME_EXTERNAL_MODEL, realm1.getUser("john").getId()));
+
         try {
             // this is needed for externalModel provider
             ResteasyProviderFactory.pushContext(KeycloakSession.class, identitySession);
 
-            // Change credential via realm2 and validate that they are changed in both realms
+            // Change credential via realm2 and validate that they are changed also in realm1
             AuthenticationProviderManager authProviderManager = AuthenticationProviderManager.getManager(realm2);
             try {
-                authProviderManager.updatePassword("john", "password-updated");
+                Assert.assertTrue(authProviderManager.updatePassword(john, "password-updated"));
             } catch (AuthenticationProviderException ape) {
                 ape.printStackTrace();
                 Assert.fail("Error not expected");
@@ -132,14 +137,14 @@ public class AuthProvidersExternalModelTest extends AbstractModelTest {
 
             // Change credential and validate that password is updated just for realm2
             try {
-                authProviderManager.updatePassword("john", "password-updated2");
+                Assert.assertFalse(authProviderManager.updatePassword(john, "password-updated2"));
             } catch (AuthenticationProviderException ape) {
                 ape.printStackTrace();
                 Assert.fail("Error not expected");
             }
             formData = createFormData("john", "password-updated2");
             Assert.assertEquals(AuthenticationManager.AuthenticationStatus.INVALID_CREDENTIALS, am.authenticateForm(realm1, formData));
-            Assert.assertEquals(AuthenticationManager.AuthenticationStatus.SUCCESS, am.authenticateForm(realm2, formData));
+            Assert.assertEquals(AuthenticationManager.AuthenticationStatus.INVALID_CREDENTIALS, am.authenticateForm(realm2, formData));
 
 
             // Allow passwordUpdate propagation again
@@ -148,7 +153,7 @@ public class AuthProvidersExternalModelTest extends AbstractModelTest {
             // Set passwordPolicy for realm1 and verify that password update fail
             realm1.setPasswordPolicy(new PasswordPolicy("length(8)"));
             try {
-                authProviderManager.updatePassword("john", "passw");
+                authProviderManager.updatePassword(john, "passw");
                 Assert.fail("Update not expected to pass");
             } catch (AuthenticationProviderException ape) {
 
diff --git a/model/tests/src/test/java/org/keycloak/model/test/AuthProvidersLDAPTest.java b/model/tests/src/test/java/org/keycloak/model/test/AuthProvidersLDAPTest.java
index 96157fe..1711d22 100644
--- a/model/tests/src/test/java/org/keycloak/model/test/AuthProvidersLDAPTest.java
+++ b/model/tests/src/test/java/org/keycloak/model/test/AuthProvidersLDAPTest.java
@@ -95,9 +95,8 @@ public class AuthProvidersLDAPTest extends AbstractModelTest {
             Assert.assertEquals("john@email.org", john.getEmail());
 
             // Verify link exists
-            Set<AuthenticationLinkModel> authLinks = realm.getAuthenticationLinks(john);
-            Assert.assertEquals(1, authLinks.size());
-            AuthenticationLinkModel authLink = authLinks.iterator().next();
+            AuthenticationLinkModel authLink = realm.getAuthenticationLink(john);
+            Assert.assertNotNull(authLink);
             Assert.assertEquals(authLink.getAuthProvider(), AuthProviderConstants.PROVIDER_NAME_PICKETLINK);
         } finally {
             ResteasyProviderFactory.clearContextData();
@@ -114,6 +113,7 @@ public class AuthProvidersLDAPTest extends AbstractModelTest {
 
             // Add some user and password to realm
             UserModel realmUser = realm.addUser("realmUser");
+            realmUser.setEnabled(true);
             UserCredentialModel credential = new UserCredentialModel();
             credential.setType(CredentialRepresentation.PASSWORD);
             credential.setValue("pass");
@@ -135,6 +135,11 @@ public class AuthProvidersLDAPTest extends AbstractModelTest {
             realmUser.setEnabled(false);
             formData = AuthProvidersExternalModelTest.createFormData("realmUser", "pass");
             Assert.assertEquals(AuthenticationManager.AuthenticationStatus.ACCOUNT_DISABLED, am.authenticateForm(realm, formData));
+
+            // Successful authentication
+            realmUser.setEnabled(true);
+            formData = AuthProvidersExternalModelTest.createFormData("realmUser", "pass");
+            Assert.assertEquals(AuthenticationManager.AuthenticationStatus.SUCCESS, am.authenticateForm(realm, formData));
         } finally {
             ResteasyProviderFactory.clearContextData();
         }
@@ -149,26 +154,34 @@ public class AuthProvidersLDAPTest extends AbstractModelTest {
             // this is needed for ldap provider
             ResteasyProviderFactory.pushContext(KeycloakRegistry.class, new KeycloakRegistry());
 
+            LdapTestUtils.setLdapPassword(realm, "john", "password");
+
+            // First authenticate successfully to sync john into realm
+            MultivaluedMap<String, String> formData = AuthProvidersExternalModelTest.createFormData("john", "password");
+            Assert.assertEquals(AuthenticationManager.AuthenticationStatus.SUCCESS, am.authenticateForm(realm, formData));
+
             // Change credential and validate that user can authenticate
             AuthenticationProviderManager authProviderManager = AuthenticationProviderManager.getManager(realm);
+
+            UserModel john = realm.getUser("john");
             try {
-                authProviderManager.updatePassword("john", "password-updated");
+                Assert.assertTrue(authProviderManager.updatePassword(john, "password-updated"));
             } catch (AuthenticationProviderException ape) {
                 ape.printStackTrace();
                 Assert.fail("Error not expected");
             }
-            MultivaluedMap<String, String> formData = AuthProvidersExternalModelTest.createFormData("john", "password-updated");
+            formData = AuthProvidersExternalModelTest.createFormData("john", "password-updated");
             Assert.assertEquals(AuthenticationManager.AuthenticationStatus.SUCCESS, am.authenticateForm(realm, formData));
 
             // Password updated just in LDAP, so validating directly in realm should fail
-            Assert.assertFalse(realm.validatePassword(realm.getUser("john"), "password-updated"));
+            Assert.assertFalse(realm.validatePassword(john, "password-updated"));
 
             // Switch to not allow updating passwords in ldap
             AuthProvidersExternalModelTest.setPasswordUpdateForProvider(false, AuthProviderConstants.PROVIDER_NAME_PICKETLINK, realm);
 
             // Change credential and validate that password is not updated
             try {
-                authProviderManager.updatePassword("john", "password-updated2");
+                Assert.assertFalse(authProviderManager.updatePassword(john, "password-updated2"));
             } catch (AuthenticationProviderException ape) {
                 ape.printStackTrace();
                 Assert.fail("Error not expected");
diff --git a/model/tests/src/test/java/org/keycloak/model/test/ImportTest.java b/model/tests/src/test/java/org/keycloak/model/test/ImportTest.java
index ce4ca53..fb8ebaa 100755
--- a/model/tests/src/test/java/org/keycloak/model/test/ImportTest.java
+++ b/model/tests/src/test/java/org/keycloak/model/test/ImportTest.java
@@ -208,24 +208,9 @@ public class ImportTest extends AbstractModelTest {
         Assert.assertTrue(authProv3.isPasswordUpdateSupported());
 
         // Test authentication linking
-        Set<AuthenticationLinkModel> authLinks = realm.getAuthenticationLinks(socialUser);
-        Assert.assertEquals(2, authLinks.size());
-        boolean plFound = false;
-        boolean extFound = false;
-        for (AuthenticationLinkModel authLinkModel : authLinks) {
-            if (AuthProviderConstants.PROVIDER_NAME_PICKETLINK.equals(authLinkModel.getAuthProvider())) {
-                plFound = true;
-                Assert.assertEquals(authLinkModel.getAuthUserId(), "myUser1");
-            } else if (AuthProviderConstants.PROVIDER_NAME_EXTERNAL_MODEL.equals(authLinkModel.getAuthProvider())) {
-                extFound = true;
-                Assert.assertEquals(authLinkModel.getAuthUserId(), "myUser11");
-            }
-        }
-        Assert.assertTrue(plFound && extFound);
-
-        UserModel foundAuthUser = realm.getUserByAuthenticationLink(new AuthenticationLinkModel(AuthProviderConstants.PROVIDER_NAME_PICKETLINK, "myUser1"));
-        Assert.assertEquals(foundAuthUser.getLoginName(), socialUser.getLoginName());
-        Assert.assertNull(realm.getUserByAuthenticationLink(new AuthenticationLinkModel(AuthProviderConstants.PROVIDER_NAME_PICKETLINK, "not-existing")));
+        AuthenticationLinkModel authLink = realm.getAuthenticationLink(socialUser);
+        Assert.assertEquals(AuthProviderConstants.PROVIDER_NAME_PICKETLINK, authLink.getAuthProvider());
+        Assert.assertEquals("myUser1", authLink.getAuthUserId());
 
         commit();
 
diff --git a/model/tests/src/test/resources/testrealm.json b/model/tests/src/test/resources/testrealm.json
index b28087d..56bf23e 100755
--- a/model/tests/src/test/resources/testrealm.json
+++ b/model/tests/src/test/resources/testrealm.json
@@ -75,7 +75,11 @@
         },
         {
             "username": "mySocialUser",
-            "enabled": true
+            "enabled": true,
+            "authenticationLink": {
+                "authProvider": "picketlink",
+                "authUserId": "myUser1"
+            }
         }
     ],
     "socialMappings": [
@@ -100,21 +104,6 @@
             ]
         }
     ],
-    "authenticationMappings": [
-        {
-            "username": "mySocialUser",
-            "authenticationLinks": [
-                {
-                    "authProvider": "picketlink",
-                    "authUserId": "myUser1"
-                },
-                {
-                    "authProvider": "externalModel",
-                    "authUserId": "myUser11"
-                }
-            ]
-        }
-    ],
     "applications": [
         {
             "name": "Application",
diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
index fe86e5b..ff6c612 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -16,8 +16,7 @@ import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.services.resources.RealmsResource;
 import org.keycloak.spi.authentication.AuthProviderStatus;
-import org.keycloak.spi.authentication.AuthResult;
-import org.keycloak.spi.authentication.AuthenticatedUser;
+import org.keycloak.spi.authentication.AuthUser;
 import org.keycloak.spi.authentication.AuthenticationProviderManager;
 import org.keycloak.util.Time;
 
@@ -188,6 +187,25 @@ public class AuthenticationManager {
         }
 
         UserModel user = KeycloakModelUtils.findUserByNameOrEmail(realm, username);
+        if (user == null) {
+            AuthUser authUser = AuthenticationProviderManager.getManager(realm).getUser(username);
+            if (authUser != null) {
+                // Create new user and link him with authentication provider
+                user = realm.addUser(authUser.getUsername());
+                user.setEnabled(true);
+                user.setFirstName(authUser.getFirstName());
+                user.setLastName(authUser.getLastName());
+                user.setEmail(authUser.getEmail());
+                realm.setAuthenticationLink(user, new AuthenticationLinkModel(authUser.getProviderName(), authUser.getId()));
+            } else {
+                logger.warn("User " + username + " not found");
+                return AuthenticationStatus.INVALID_USER;
+            }
+        }
+
+        if (!checkEnabled(user)) {
+            return AuthenticationStatus.ACCOUNT_DISABLED;
+        }
 
         Set<String> types = new HashSet<String>();
 
@@ -202,20 +220,13 @@ public class AuthenticationManager {
                 return AuthenticationStatus.MISSING_PASSWORD;
             }
 
-            if (user == null && types.contains(CredentialRepresentation.TOTP)) {
-                logger.warn("User doesn't exists and TOTP is required for the realm");
-                return AuthenticationStatus.INVALID_USER;
-            }
-
-            if (user != null && user.isTotp()) {
+            if (user.isTotp()) {
                 String token = formData.getFirst(CredentialRepresentation.TOTP);
                 if (token == null) {
                     logger.warn("TOTP token not provided");
                     return AuthenticationStatus.MISSING_TOTP;
                 }
-                if (!checkEnabled(user)) {
-                    return AuthenticationStatus.ACCOUNT_DISABLED;
-                }
+
                 logger.debug("validating TOTP");
                 if (!realm.validateTOTP(user, password, token)) {
                     return AuthenticationStatus.INVALID_CREDENTIALS;
@@ -223,58 +234,12 @@ public class AuthenticationManager {
             } else {
                 logger.debug("validating password for user: " + username);
 
-                AuthResult authResult = AuthenticationProviderManager.getManager(realm).validatePassword(username, password);
-                if (authResult.getAuthProviderStatus() == AuthProviderStatus.INVALID_CREDENTIALS) {
+                AuthProviderStatus authStatus = AuthenticationProviderManager.getManager(realm).validatePassword(user, password);
+                if (authStatus == AuthProviderStatus.INVALID_CREDENTIALS) {
                     logger.debug("invalid password for user: " + username);
                     return AuthenticationStatus.INVALID_CREDENTIALS;
-                } else if (authResult.getAuthProviderStatus() == AuthProviderStatus.USER_NOT_FOUND) {
-                    logger.debug("User " + username + " not found in any Authentication provider");
-                    return AuthenticationStatus.INVALID_USER;
-                }
-
-                if (authResult.getAuthenticatedUser() != null) {
-                    AuthenticatedUser authUser = authResult.getAuthenticatedUser();
-                    AuthenticationLinkModel authLink = new AuthenticationLinkModel(authResult.getProviderName(), authUser.getId());
-                    user = realm.getUserByAuthenticationLink(authLink);
-                    if (user == null) {
-                        user = KeycloakModelUtils.findUserByNameOrEmail(realm, username);
-                        if (user != null) {
-                            // Case when we already have user with the same username like authenticated, but he is not yet linked to current provider.
-                            // TODO: Revisit if it's ok to link if we allow to change username. Maybe ask user?
-                            // TODO: Update of existing account?
-                            realm.addAuthenticationLink(user, authLink);
-                            logger.info("User " + authUser.getUsername() + " successfully authenticated and linked with provider " + authResult.getProviderName());
-                        }  else {
-                            // Create new user, which has been successfully authenticated and link him with authentication provider
-                            user = realm.addUser(authUser.getUsername());
-                            user.setEnabled(true);
-                            user.setFirstName(authUser.getFirstName());
-                            user.setLastName(authUser.getLastName());
-                            user.setEmail(authUser.getEmail());
-
-                            realm.addAuthenticationLink(user, authLink);
-                            logger.info("User " + username + " successfully authenticated and created based on provider " + authResult.getProviderName());
-                        }
-                    } else {
-                        // Existing and linked user has been authenticated TODO: Update of existing account?
-                    }
-
-                    // Authenticated username could be different from the "form" username. In this case, we will change it
-                    if (!username.equals(user.getLoginName())) {
-                        formData.putSingle(FORM_USERNAME, user.getLoginName());
-                        logger.debug("Existing user " + user.getLoginName() + " successfully authenticated");
-                    }
-
-                } else {
-                    // Authentication provider didn't send AuthenticatedUser. Using already retrieved user based on username from "form"
-                    if (user == null) {
-                        logger.warn("User '" + username + "' successfully authenticated, but he doesn't exists and don't know how to create him");
-                        return AuthenticationStatus.INVALID_USER;
-                    }
-                }
-
-                if (!checkEnabled(user)) {
-                    return AuthenticationStatus.ACCOUNT_DISABLED;
+                } else if (authStatus == AuthProviderStatus.FAILED) {
+                    return AuthenticationStatus.FAILED;
                 }
             }
 
diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
index 5b4c735..d9d40d2 100755
--- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
@@ -21,7 +21,6 @@ import org.keycloak.models.UserModel.RequiredAction;
 import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.representations.idm.ApplicationRepresentation;
 import org.keycloak.representations.idm.AuthenticationLinkRepresentation;
-import org.keycloak.representations.idm.AuthenticationMappingRepresentation;
 import org.keycloak.representations.idm.AuthenticationProviderRepresentation;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.representations.idm.OAuthClientRepresentation;
@@ -388,15 +387,6 @@ public class RealmManager {
                 }
             }
         }
-        if (rep.getAuthenticationMappings() != null) {
-            for (AuthenticationMappingRepresentation authMapping : rep.getAuthenticationMappings()) {
-                UserModel user = userMap.get(authMapping.getUsername());
-                for (AuthenticationLinkRepresentation link : authMapping.getAuthenticationLinks()) {
-                    AuthenticationLinkModel mappingModel = new AuthenticationLinkModel(link.getAuthProvider(), link.getAuthUserId());
-                    newRealm.addAuthenticationLink(user, mappingModel);
-                }
-            }
-        }
 
         if (rep.getSmtpServer() != null) {
             newRealm.setSmtpConfig(new HashMap(rep.getSmtpServer()));
@@ -478,6 +468,11 @@ public class RealmManager {
                 newRealm.updateCredential(user, credential);
             }
         }
+        if (userRep.getAuthenticationLink() != null) {
+            AuthenticationLinkRepresentation link = userRep.getAuthenticationLink();
+            AuthenticationLinkModel authLink = new AuthenticationLinkModel(link.getAuthProvider(), link.getAuthUserId());
+            newRealm.setAuthenticationLink(user, authLink);
+        }
         return user;
     }
 
diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java
index 4ac9a08..b937674 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -264,13 +264,15 @@ public class AccountService {
         AuthenticationProviderManager authProviderManager = AuthenticationProviderManager.getManager(realm);
         if (Validation.isEmpty(password)) {
             return account.setError(Messages.MISSING_PASSWORD).createResponse(AccountPages.PASSWORD);
-            // TODO: This may not work in some cases. For example if ldap username is "foo" but actual loginName of user is "bar", which could theoretically happen...
-        } else if (authProviderManager.validatePassword(user.getLoginName(), password).getAuthProviderStatus() != AuthProviderStatus.SUCCESS) {
+        } else if (authProviderManager.validatePassword(user, password) != AuthProviderStatus.SUCCESS) {
             return account.setError(Messages.INVALID_PASSWORD_EXISTING).createResponse(AccountPages.PASSWORD);
         }
 
         try {
-            authProviderManager.updatePassword(user.getLoginName(), passwordNew);
+            boolean passwordUpdateSuccess = authProviderManager.updatePassword(user, passwordNew);
+            if (!passwordUpdateSuccess) {
+                return account.setError("Password update failed").createResponse(AccountPages.PASSWORD);
+            }
         } catch (AuthenticationProviderException ape) {
             return account.setError(ape.getMessage()).createResponse(AccountPages.PASSWORD);
         }
diff --git a/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java b/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java
index c6b0a6c..d77ba7e 100755
--- a/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java
@@ -200,7 +200,10 @@ public class RequiredActionsService {
         }
 
         try {
-            AuthenticationProviderManager.getManager(realm).updatePassword(user.getLoginName(), passwordNew);
+            boolean updateSuccessful = AuthenticationProviderManager.getManager(realm).updatePassword(user, passwordNew);
+            if (!updateSuccessful) {
+                return loginForms.setError("Password update failed").createResponse(RequiredAction.UPDATE_PASSWORD);
+            }
         } catch (AuthenticationProviderException ape) {
             return loginForms.setError(ape.getMessage()).createResponse(RequiredAction.UPDATE_PASSWORD);
         }
diff --git a/services/src/main/java/org/keycloak/services/resources/TokenService.java b/services/src/main/java/org/keycloak/services/resources/TokenService.java
index 00fbbb4..7a91471 100755
--- a/services/src/main/java/org/keycloak/services/resources/TokenService.java
+++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java
@@ -397,12 +397,21 @@ public class TokenService {
             UserCredentialModel credentials = new UserCredentialModel();
             credentials.setType(CredentialRepresentation.PASSWORD);
             credentials.setValue(formData.getFirst("password"));
+
+            boolean passwordUpdateSuccessful;
+            String passwordUpdateError = null;
             try {
-                AuthenticationProviderManager.getManager(realm).updatePassword(username, formData.getFirst("password"));
+                passwordUpdateSuccessful = AuthenticationProviderManager.getManager(realm).updatePassword(user, formData.getFirst("password"));
+                passwordUpdateError = "Password update failed";
             } catch (AuthenticationProviderException ape) {
-                // User already registered, but force him to update password
+                passwordUpdateSuccessful = false;
+                passwordUpdateError = ape.getMessage();
+            }
+
+            // User already registered, but force him to update password
+            if (!passwordUpdateSuccessful) {
                 user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
-                return Flows.forms(realm, request, uriInfo).setError(ape.getMessage()).createResponse(UserModel.RequiredAction.UPDATE_PASSWORD);
+                return Flows.forms(realm, request, uriInfo).setError(passwordUpdateError).createResponse(UserModel.RequiredAction.UPDATE_PASSWORD);
             }
         }
 
diff --git a/spi/authentication-model/src/main/java/org/keycloak/spi/authentication/model/AbstractModelAuthenticationProvider.java b/spi/authentication-model/src/main/java/org/keycloak/spi/authentication/model/AbstractModelAuthenticationProvider.java
index 58bf423..66166ab 100644
--- a/spi/authentication-model/src/main/java/org/keycloak/spi/authentication/model/AbstractModelAuthenticationProvider.java
+++ b/spi/authentication-model/src/main/java/org/keycloak/spi/authentication/model/AbstractModelAuthenticationProvider.java
@@ -9,8 +9,7 @@ import org.keycloak.models.UserModel;
 import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.spi.authentication.AuthProviderStatus;
-import org.keycloak.spi.authentication.AuthResult;
-import org.keycloak.spi.authentication.AuthenticatedUser;
+import org.keycloak.spi.authentication.AuthUser;
 import org.keycloak.spi.authentication.AuthenticationProvider;
 import org.keycloak.spi.authentication.AuthenticationProviderException;
 
@@ -24,22 +23,19 @@ public abstract class AbstractModelAuthenticationProvider implements Authenticat
     private static final Logger logger = Logger.getLogger(AbstractModelAuthenticationProvider.class);
 
     @Override
-    public AuthResult validatePassword(RealmModel currentRealm, Map<String, String> config, String username, String password) throws AuthenticationProviderException {
+    public AuthUser getUser(RealmModel currentRealm, Map<String, String> config, String username) throws AuthenticationProviderException {
         RealmModel realm = getRealm(currentRealm, config);
-
         UserModel user = KeycloakModelUtils.findUserByNameOrEmail(realm, username);
+        return user == null ? null : createAuthenticatedUserInstance(user);
+    }
 
-        if (user == null) {
-            return new AuthResult(AuthProviderStatus.USER_NOT_FOUND);
-        }
+    @Override
+    public AuthProviderStatus validatePassword(RealmModel currentRealm, Map<String, String> config, String username, String password) throws AuthenticationProviderException {
+        RealmModel realm = getRealm(currentRealm, config);
+        UserModel user = KeycloakModelUtils.findUserByNameOrEmail(realm, username);
 
         boolean result = realm.validatePassword(user, password);
-        if (!result) {
-            return  new AuthResult(AuthProviderStatus.INVALID_CREDENTIALS);
-        }
-
-        AuthenticatedUser authUser = createAuthenticatedUserInstance(user);
-        return new AuthResult(AuthProviderStatus.SUCCESS).setProviderName(getName()).setUser(authUser);
+        return result ? AuthProviderStatus.SUCCESS : AuthProviderStatus.INVALID_CREDENTIALS;
     }
 
     @Override
@@ -54,7 +50,7 @@ public abstract class AbstractModelAuthenticationProvider implements Authenticat
 
         UserModel user = realm.getUser(username);
         if (user == null) {
-            logger.debugf("User '%s' doesn't exists. Skip password update", username);
+            logger.warnf("User '%s' doesn't exists. Skip password update", username);
             return false;
         }
 
@@ -68,5 +64,9 @@ public abstract class AbstractModelAuthenticationProvider implements Authenticat
 
     protected abstract RealmModel getRealm(RealmModel currentRealm, Map<String, String> config) throws AuthenticationProviderException;
 
-    protected abstract AuthenticatedUser createAuthenticatedUserInstance(UserModel user);
+    protected AuthUser createAuthenticatedUserInstance(UserModel user) {
+        return new AuthUser(user.getId(), user.getLoginName(), getName())
+                .setName(user.getFirstName(), user.getLastName())
+                .setEmail(user.getEmail());
+    }
 }
diff --git a/spi/authentication-model/src/main/java/org/keycloak/spi/authentication/model/ExternalModelAuthenticationProvider.java b/spi/authentication-model/src/main/java/org/keycloak/spi/authentication/model/ExternalModelAuthenticationProvider.java
index a24a495..a4d129b 100644
--- a/spi/authentication-model/src/main/java/org/keycloak/spi/authentication/model/ExternalModelAuthenticationProvider.java
+++ b/spi/authentication-model/src/main/java/org/keycloak/spi/authentication/model/ExternalModelAuthenticationProvider.java
@@ -7,7 +7,7 @@ import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.spi.authentication.AuthProviderConstants;
-import org.keycloak.spi.authentication.AuthenticatedUser;
+import org.keycloak.spi.authentication.AuthUser;
 import org.keycloak.spi.authentication.AuthenticationProviderException;
 
 /**
@@ -40,11 +40,4 @@ public class ExternalModelAuthenticationProvider extends AbstractModelAuthentica
         }
         return realm;
     }
-
-    @Override
-    protected AuthenticatedUser createAuthenticatedUserInstance(UserModel user) {
-        return new AuthenticatedUser(user.getId(), user.getLoginName())
-                .setName(user.getFirstName(), user.getLastName())
-                .setEmail(user.getEmail());
-    }
 }
diff --git a/spi/authentication-model/src/main/java/org/keycloak/spi/authentication/model/ModelAuthenticationProvider.java b/spi/authentication-model/src/main/java/org/keycloak/spi/authentication/model/ModelAuthenticationProvider.java
index 5169489..29ab43a 100644
--- a/spi/authentication-model/src/main/java/org/keycloak/spi/authentication/model/ModelAuthenticationProvider.java
+++ b/spi/authentication-model/src/main/java/org/keycloak/spi/authentication/model/ModelAuthenticationProvider.java
@@ -5,7 +5,7 @@ import java.util.Map;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.spi.authentication.AuthProviderConstants;
-import org.keycloak.spi.authentication.AuthenticatedUser;
+import org.keycloak.spi.authentication.AuthUser;
 
 /**
  * AbstractModelAuthenticationProvider, which uses current realm to call operations on
@@ -23,10 +23,4 @@ public class ModelAuthenticationProvider extends AbstractModelAuthenticationProv
     protected RealmModel getRealm(RealmModel currentRealm, Map<String, String> config) {
         return currentRealm;
     }
-
-    @Override
-    protected AuthenticatedUser createAuthenticatedUserInstance(UserModel user) {
-        // We don't want AuthenticatedUser instance. Auto-registration won't never happen with this provider
-        return null;
-    }
 }
diff --git a/spi/authentication-picketlink/src/main/java/org/keycloak/spi/authentication/picketlink/PicketlinkAuthenticationProvider.java b/spi/authentication-picketlink/src/main/java/org/keycloak/spi/authentication/picketlink/PicketlinkAuthenticationProvider.java
index 9d87e76..ffc1ab6 100644
--- a/spi/authentication-picketlink/src/main/java/org/keycloak/spi/authentication/picketlink/PicketlinkAuthenticationProvider.java
+++ b/spi/authentication-picketlink/src/main/java/org/keycloak/spi/authentication/picketlink/PicketlinkAuthenticationProvider.java
@@ -6,9 +6,8 @@ import org.jboss.logging.Logger;
 import org.jboss.resteasy.spi.ResteasyProviderFactory;
 import org.keycloak.models.RealmModel;
 import org.keycloak.spi.authentication.AuthProviderStatus;
-import org.keycloak.spi.authentication.AuthResult;
 import org.keycloak.spi.authentication.AuthProviderConstants;
-import org.keycloak.spi.authentication.AuthenticatedUser;
+import org.keycloak.spi.authentication.AuthUser;
 import org.keycloak.spi.authentication.AuthenticationProvider;
 import org.keycloak.spi.authentication.AuthenticationProviderException;
 import org.keycloak.spi.picketlink.PartitionManagerProvider;
@@ -36,28 +35,27 @@ public class PicketlinkAuthenticationProvider implements AuthenticationProvider 
     }
 
     @Override
-    public AuthResult validatePassword(RealmModel realm, Map<String, String> configuration, String username, String password) throws AuthenticationProviderException {
+    public AuthUser getUser(RealmModel realm, Map<String, String> configuration, String username) throws AuthenticationProviderException {
         IdentityManager identityManager = getIdentityManager(realm);
-
         User picketlinkUser = BasicModel.getUser(identityManager, username);
-        if (picketlinkUser == null) {
-            return new AuthResult(AuthProviderStatus.USER_NOT_FOUND);
-        }
+        return picketlinkUser == null ? null : new AuthUser(picketlinkUser.getId(), picketlinkUser.getLoginName(), getName())
+                .setName(picketlinkUser.getFirstName(), picketlinkUser.getLastName())
+                .setEmail(picketlinkUser.getEmail())
+                .setProviderName(getName());
+    }
+
+    @Override
+    public AuthProviderStatus validatePassword(RealmModel realm, Map<String, String> configuration, String username, String password) throws AuthenticationProviderException {
+        IdentityManager identityManager = getIdentityManager(realm);
 
         UsernamePasswordCredentials credential = new UsernamePasswordCredentials();
         credential.setUsername(username);
         credential.setPassword(new Password(password.toCharArray()));
         identityManager.validateCredentials(credential);
         if (credential.getStatus() == Credentials.Status.VALID) {
-            AuthResult result = new AuthResult(AuthProviderStatus.SUCCESS);
-
-            AuthenticatedUser authenticatedUser = new AuthenticatedUser(picketlinkUser.getId(), picketlinkUser.getLoginName())
-                    .setName(picketlinkUser.getFirstName(), picketlinkUser.getLastName())
-                    .setEmail(picketlinkUser.getEmail());
-            result.setUser(authenticatedUser).setProviderName(getName());
-            return result;
+            return AuthProviderStatus.SUCCESS;
         } else {
-            return new AuthResult(AuthProviderStatus.INVALID_CREDENTIALS);
+            return AuthProviderStatus.INVALID_CREDENTIALS;
         }
     }
 
diff --git a/spi/authentication-spi/src/main/java/org/keycloak/spi/authentication/AuthenticationProvider.java b/spi/authentication-spi/src/main/java/org/keycloak/spi/authentication/AuthenticationProvider.java
index 1ab1836..014d432 100644
--- a/spi/authentication-spi/src/main/java/org/keycloak/spi/authentication/AuthenticationProvider.java
+++ b/spi/authentication-spi/src/main/java/org/keycloak/spi/authentication/AuthenticationProvider.java
@@ -12,13 +12,24 @@ public interface AuthenticationProvider {
     String getName();
 
     /**
+     * Get user by given username or email. Return user instance or null if user doesn't exists in this authentication provider
+     *
+     * @param realm
+     * @param configuration
+     * @param username or email
+     * @return found user or null if user with given username doesn't exists
+     * @throws AuthenticationProviderException
+     */
+    AuthUser getUser(RealmModel realm, Map<String, String> configuration, String username) throws AuthenticationProviderException;
+
+    /**
      * Standard Authentication flow
      *
      * @param username
      * @param password
      * @return result of authentication, which might eventually encapsulate info about authenticated user and provider which successfully authenticated
      */
-    AuthResult validatePassword(RealmModel realm, Map<String, String> configuration, String username, String password) throws AuthenticationProviderException;
+    AuthProviderStatus validatePassword(RealmModel realm, Map<String, String> configuration, String username, String password) throws AuthenticationProviderException;
 
 
     /**
diff --git a/spi/authentication-spi/src/main/java/org/keycloak/spi/authentication/AuthenticationProviderManager.java b/spi/authentication-spi/src/main/java/org/keycloak/spi/authentication/AuthenticationProviderManager.java
index 8c6a6b8..c6a59a1 100644
--- a/spi/authentication-spi/src/main/java/org/keycloak/spi/authentication/AuthenticationProviderManager.java
+++ b/spi/authentication-spi/src/main/java/org/keycloak/spi/authentication/AuthenticationProviderManager.java
@@ -1,14 +1,15 @@
 package org.keycloak.spi.authentication;
 
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
 import org.jboss.logging.Logger;
+import org.keycloak.models.AuthenticationLinkModel;
 import org.keycloak.models.AuthenticationProviderModel;
 import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
 import org.keycloak.util.ProviderLoader;
 
 /**
@@ -46,77 +47,107 @@ public class AuthenticationProviderManager {
         this.delegates = delegates;
     }
 
-    public AuthResult validatePassword(String username, String password) {
-        List<AuthenticationProviderModel> configuredProviders = getConfiguredProviders(realm);
-        boolean userExists = false;
-
-        for (AuthenticationProviderModel authProviderConfig : configuredProviders) {
-            String providerName = authProviderConfig.getProviderName();
-
-            AuthenticationProvider delegate = getDelegate(providerName);
+    public AuthUser getUser(String username) {
+        List<AuthenticationProviderModel> authProviderModels = getConfiguredProviderModels(realm);
+        for (AuthenticationProviderModel providerModel : authProviderModels) {
+            AuthenticationProvider delegate = getProvider(providerModel.getProviderName());
             if (delegate == null) {
                 continue;
             }
 
             try {
-                AuthResult currentResult = delegate.validatePassword(realm, authProviderConfig.getConfig(), username, password);
-                logger.debugf("Authentication provider '%s' finished with '%s' for authentication of '%s'", delegate.getName(), currentResult.getAuthProviderStatus().toString(), username);
-
-                if (currentResult.getAuthProviderStatus() == AuthProviderStatus.SUCCESS) {
-                    return currentResult;
-                } else if (currentResult.getAuthProviderStatus() == AuthProviderStatus.INVALID_CREDENTIALS) {
-                    userExists = true;
+                AuthUser authUser = delegate.getUser(realm, providerModel.getConfig(), username);
+                if (authUser != null) {
+                    logger.debugf("User '%s' found with provider '%s'", username, providerModel.getProviderName());
+                    return authUser;
                 }
             } catch (AuthenticationProviderException ape) {
                 logger.warn(ape.getMessage(), ape);
             }
         }
 
-        AuthProviderStatus status = userExists ? AuthProviderStatus.INVALID_CREDENTIALS : AuthProviderStatus.USER_NOT_FOUND;
-        logger.debugf("Not able to authenticate '%s' with any authentication provider. Status: '%s'", username, status.toString());
+        logger.debugf("User '%s' not found with any provider", username);
+        return null;
+    }
+
+    public AuthProviderStatus validatePassword(UserModel user, String password) {
+        AuthenticationLinkModel authLink = realm.getAuthenticationLink(user);
+        if (authLink == null) {
+            authLink = new AuthenticationLinkModel(AuthenticationProviderModel.DEFAULT_PROVIDER.getProviderName(), user.getId());
+        }
+
+        String providerName = authLink.getAuthProvider();
 
-        return new AuthResult(status);
+        AuthenticationProviderModel providerModel = getConfiguredProviderModel(realm, providerName);
+        AuthenticationProvider delegate = getProvider(providerName);
+        if (delegate == null || providerModel == null) {
+            return AuthProviderStatus.FAILED;
+        }
+
+        try {
+            checkCorrectAuthLink(delegate, providerModel, authLink, user.getLoginName());
+
+            AuthProviderStatus currentResult = delegate.validatePassword(realm, providerModel.getConfig(), user.getLoginName(), password);
+            logger.debugf("Authentication provider '%s' finished with '%s' for authentication of '%s'", delegate.getName(), currentResult.toString(), user.getLoginName());
+            return currentResult;
+        } catch (AuthenticationProviderException ape) {
+            logger.warn(ape.getMessage(), ape);
+            return AuthProviderStatus.FAILED;
+        }
     }
 
-    public void updatePassword(String username, String password) throws AuthenticationProviderException {
-        List<AuthenticationProviderModel> configuredProviders = getConfiguredProviders(realm);
+    public boolean updatePassword(UserModel user, String password) throws AuthenticationProviderException {
+        AuthenticationLinkModel authLink = realm.getAuthenticationLink(user);
+        if (authLink == null) {
+            authLink = new AuthenticationLinkModel(AuthenticationProviderModel.DEFAULT_PROVIDER.getProviderName(), user.getId());
+        }
+
+        String providerName = authLink.getAuthProvider();
 
-        for (AuthenticationProviderModel authProviderConfig : configuredProviders) {
+        AuthenticationProviderModel providerModel = getConfiguredProviderModel(realm, providerName);
+        if (providerModel == null) {
+            return false;
+        }
+
+        String username = user.getLoginName();
 
-            // Update just those, which support password update
-            if (authProviderConfig.isPasswordUpdateSupported()) {
-                String providerName = authProviderConfig.getProviderName();
-                AuthenticationProvider delegate = getDelegate(providerName);
+        // Update just those, which support password update
+        if (providerModel.isPasswordUpdateSupported()) {
+            try {
+                AuthenticationProvider delegate = getProvider(providerName);
                 if (delegate == null) {
-                    continue;
+                    return false;
                 }
 
-                try {
-                    if (delegate.updateCredential(realm, authProviderConfig.getConfig(), username, password)) {
-                        logger.debugf("Updated password in authentication provider '%s' for user '%s'", delegate.getName(), username);
-                    } else {
-                        logger.debugf("Password not updated in authentication provider '%s' for user '%s'", delegate.getName(), username);
-                    }
-                } catch (AuthenticationProviderException ape) {
-                    // Rethrow it to upper layer
-                    logger.warn("Failed to update password: " + ape.getMessage());
-                    throw ape;
+                checkCorrectAuthLink(delegate, providerModel, authLink, username);
+
+                if (delegate.updateCredential(realm,providerModel.getConfig(), user.getLoginName(), password)) {
+                    logger.debugf("Updated password in authentication provider '%s' for user '%s'", providerName, username);
+                    return true;
+                } else {
+                    logger.warnf("Password not updated in authentication provider '%s' for user '%s'", providerName, username);
+                    return false;
                 }
-            } else {
-                logger.debugf("Skip password update for authentication provider '%s' for user '%s'", authProviderConfig.getProviderName(), username);
+            } catch (AuthenticationProviderException ape) {
+                // Rethrow it to upper layer
+                logger.warn("Failed to update password: " + ape.getMessage());
+                throw ape;
             }
+        } else {
+            logger.warnf("Skip password update for authentication provider '%s' for user '%s'", providerName, username);
+            return false;
         }
     }
 
-    private AuthenticationProvider getDelegate(String providerName) {
+    private AuthenticationProvider getProvider(String providerName) {
         AuthenticationProvider delegate = delegates.get(providerName);
         if (delegate == null) {
-            logger.warnf("Configured provider with name '%s' not found", providerName);
+            logger.warnf("Provider '%s' not available on classpath", providerName);
         }
         return delegate;
     }
 
-    private List<AuthenticationProviderModel> getConfiguredProviders(RealmModel realm) {
+    private List<AuthenticationProviderModel> getConfiguredProviderModels(RealmModel realm) {
         List<AuthenticationProviderModel> configuredProviders = realm.getAuthenticationProviders();
 
         // Use model based authentication of current realm by default
@@ -127,4 +158,27 @@ public class AuthenticationProviderManager {
 
         return configuredProviders;
     }
+
+    private AuthenticationProviderModel getConfiguredProviderModel(RealmModel realm, String providerName) {
+        List<AuthenticationProviderModel> providers = getConfiguredProviderModels(realm);
+        for (AuthenticationProviderModel provider : providers) {
+            if (providerName.equals(provider.getProviderName())) {
+                return provider;
+            }
+        }
+
+        logger.warnf("Provider '%s' not configured in realm", providerName);
+        return null;
+    }
+
+    // Check if ID of linked AuthUser is same as expected ID from authenticationLink . It should catch the case when for example user "john" was deleted in LDAP
+    // and then user "john" has been created again, but it's actually different user with different ID
+    private void checkCorrectAuthLink(AuthenticationProvider authProvider, AuthenticationProviderModel providerModel,
+                                      AuthenticationLinkModel authLinkModel, String username) throws AuthenticationProviderException {
+        AuthUser authUser = authProvider.getUser(realm, providerModel.getConfig(), username);
+        String userExternalId = authUser.getId();
+        if (!userExternalId.equals(authLinkModel.getAuthUserId())) {
+            throw new AuthenticationProviderException("ID did not match! ID from provider: " + userExternalId + ", ID from authentication link: " + authLinkModel.getAuthUserId());
+        }
+    }
 }
diff --git a/spi/authentication-spi/src/main/java/org/keycloak/spi/authentication/AuthProviderStatus.java b/spi/authentication-spi/src/main/java/org/keycloak/spi/authentication/AuthProviderStatus.java
index 56d05d0..c782dfc 100644
--- a/spi/authentication-spi/src/main/java/org/keycloak/spi/authentication/AuthProviderStatus.java
+++ b/spi/authentication-spi/src/main/java/org/keycloak/spi/authentication/AuthProviderStatus.java
@@ -7,6 +7,6 @@ package org.keycloak.spi.authentication;
  */
 public enum AuthProviderStatus {
 
-    SUCCESS, INVALID_CREDENTIALS, USER_NOT_FOUND
+    SUCCESS, INVALID_CREDENTIALS, FAILED
 
 }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AuthProvidersIntegrationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AuthProvidersIntegrationTest.java
index c34e77e..37cde85 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AuthProvidersIntegrationTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AuthProvidersIntegrationTest.java
@@ -47,7 +47,7 @@ public class AuthProvidersIntegrationTest {
         @Override
         public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
             addUser(appRealm, "mary", "mary@test.com", "password-app");
-            addUser(adminstrationRealm, "mary", "mary@admin.com", "password-admin");
+            addUser(adminstrationRealm, "mary-admin", "mary@admin.com", "password-admin");
 
             AuthenticationProviderModel modelProvider = new AuthenticationProviderModel(AuthProviderConstants.PROVIDER_NAME_MODEL, false, Collections.EMPTY_MAP);
             AuthenticationProviderModel picketlinkProvider = new AuthenticationProviderModel(AuthProviderConstants.PROVIDER_NAME_PICKETLINK, true, Collections.EMPTY_MAP);
@@ -116,7 +116,7 @@ public class AuthProvidersIntegrationTest {
     @Test
     public void loginExternalModel() {
         loginPage.open();
-        loginPage.login("mary", "password-admin");
+        loginPage.login("mary-admin", "password-admin");
 
         Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
         Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
@@ -148,18 +148,18 @@ public class AuthProvidersIntegrationTest {
 
         try {
             changePasswordPage.open();
-            loginPage.login("mary", "password-admin");
+            loginPage.login("mary-admin", "password-admin");
 
             // Can't update to "pass" due to passwordPolicy
             changePasswordPage.changePassword("password-admin", "pass", "pass");
             Assert.assertEquals("Invalid password: minimum length 6", profilePage.getError());
 
-            changePasswordPage.changePassword("password-app", "password-updated", "password-updated");
+            changePasswordPage.changePassword("password-admin", "password-updated", "password-updated");
             Assert.assertEquals("Your password has been updated", profilePage.getSuccess());
             changePasswordPage.logout();
 
             loginPage.open();
-            loginPage.login("mary", "password-updated");
+            loginPage.login("mary-admin", "password-updated");
             Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
 
         } finally {