Keycloak custom user storage not displaying attributes

Viewed 2292

I have implemented a custom user storage spi in keycloak for comunicating with a mysql db. It works all in the correct way except for a prticular.

When I go to the users page in the admin console and I click view all users the retrived users are correctly displayed with all the fields ( username, email, lastname, firstname )

enter image description here

From the upper image the firt two users were created directly from keycloak, the last two were retrived from the mysql db.

instead when i click on the single user in the opened page all the fields except for the user id and the username are empty.

enter image description here

I don't understand why in the view all page this datas are displayed correctly and in the single user page not. I suppose that is because kecloak tries to read the datas from it's own storage because

  • if i set this for ex the email field in the single user page with custom value and save it
  • happens that in the view all page the email has the value in the image (m.c...@hotmail.it )
  • and in the single user page it has another value ( the custom one that i saved ).

As storage is use this one https://github.com/Linch1/mysql-keycloak-storage-spi wrote by me ( the main login is in the jar-module folder). I think that the protagonist of this behaviour is the UserAdapter.java file, in mine i wrote this:

package mysql.storage.main;

import org.keycloak.component.ComponentModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.storage.StorageId;
import org.keycloak.storage.adapter.AbstractUserAdapterFederatedStorage;

import lombok.extern.jbosslog.JBossLog;
import mysql.storage.entity.User;

@JBossLog
public class UserAdapter extends AbstractUserAdapterFederatedStorage {

  private final User user;
  private final String keycloakId;
  

  
  public UserAdapter(KeycloakSession session, RealmModel realm, ComponentModel model, User user) {
    super(session, realm, model);
    this.user = user;
    this.EMAIL_ATTRIBUTE = user.getEmail();
    this.keycloakId = StorageId.keycloakId(model, String.valueOf(user.getId()) ); 
  }

  @Override
  public String getId() {
    return keycloakId;
  }

  @Override
  public String getUsername() {
      log.infov("\n UserAdapter > getting username: " + user.getNickName() );
    return user.getNickName();
  }

  @Override
  public void setUsername(String username) {
    user.setNickName(username);
  }

  @Override
  public String getEmail() {
      log.infov("\n UserAdapter > getting email: " + user.getEmail() );
    return user.getEmail();
  }

  @Override
  public void setEmail(String email) {
    user.setEmail(email);
  }

  @Override
  public String getFirstName() {
    return user.getFirstName();
  }

  @Override
  public void setFirstName(String firstName) {
    user.setFirstName(firstName);
  }

  @Override
  public String getLastName() {
    return user.getLastName();
  }

  @Override
  public void setLastName(String lastName) {
    user.setLastName(lastName);
  }
}

I read the doc and searched a while but found nothing usefull. Any ideas?

3 Answers

I think it might be related to your provider's user lookup priority and cache.

Try and edit your provider's configuration (admin console > user federation > your provider) priority to a value lower than 0 (mind other providers config and priority), so that it looks it up first in your custom provider.

Also you might want to check the cache policy value, try to set it to NO_CACHE or set a value for the MAX_LIFESPAN combo box.

I solved this by updating the user values with the keycloak client. I called this method meanwhile that the user was being saved to the db so that also keycloak has this values inside of it's db.

import java.util.List;
import java.util.stream.Collectors;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.KeycloakBuilder;
import org.keycloak.admin.client.resource.UserResource;
import org.keycloak.admin.client.resource.UsersResource;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.representations.idm.UserRepresentation;

public class KeycloakRepo {
    
    private String SERVER_URL = "http://localhost:8180/auth";
    private String REALM = "master";
    private String REALM_USE = "myRelam";
    private String USERNAME = "admin_name";
    private String PASSWORD = "admin_pwd";
    private String CLIENT_ID = "admin-cli";
    private String CLIENT_SECRET = "73920be5-388b-48d9-bbfc-eb1786f5fcb0";
    
    private Keycloak keycloak = Keycloak.getInstance(
            SERVER_URL,
            REALM,
            USERNAME,
            PASSWORD,
            CLIENT_ID
            );
    
    
    public void KeycloakRepo() {}
    
    public void updateUser(String emailSearch, String email, String firstName, String Lastname) {
        
        RealmResource realmResource = keycloak.realm(REALM_USE);
        UsersResource usersRessource = realmResource.users();

        List<UserRepresentation> users = usersRessource.list().stream()
              .filter(user ->  user.getUsername().contains(emailSearch))
              .collect(Collectors.toList());
        UserRepresentation user_ = users.get(0);
        
        UserResource userResource = usersRessource.get(user_.getId());
        UserRepresentation consumerUser = userResource.toRepresentation();
        
        consumerUser.setEmail(email);
        consumerUser.setFirstName(firstName);
        consumerUser.setLastName(Lastname);
        
        userResource.update(consumerUser);
        
        System.out.println("found:" + users.size() + " " + user_.getId() + " " + user_.getFirstName() + " " + user_.getEmail());

    }
}

You need to override getAttributes() in your UserAdapter:

@Override
public Map<String, List<String>> getAttributes() {
    MultivaluedHashMap<String, String> attributes = new MultivaluedHashMap<>();
    attributes.add(UserModel.USERNAME, getUsername());
    attributes.add(UserModel.EMAIL,getEmail());
    attributes.add(UserModel.FIRST_NAME,getFirstName());
    attributes.add(UserModel.LAST_NAME,getLastName());
    return attributes;
}
Related