Does custom provider support user info update?

I implemented a custom provider for our Oracle database following the user storage spi guide for our Keycloak 23 instance. It does work for searching users, updating password and login.

However it doesn’t work when we try to update user info such as first name, last name and e-mail. It used to on Keycloak 8, but now on KC 23 it doesn’t even reach setFirstName, setLastName and setEmail methods when I set debug breakpoints.

The documentation doesn’t mention this use case, so I’m unsure if it’s supported or if I’m missing something.

Could someone provide an answer if KC support updating these user info when using custom providers?

Yes, updating the data is supported, of course.
Unfortunately, it depends on different implementations, whether the distinct setter methods are called, or the setAttribute method is called.
See e.g. here: keycloak-extensions-demo/flintstones-userprovider/src/main/java/dasniko/keycloak/user/flintstones/FlintstoneUserAdapter.java at main · dasniko/keycloak-extensions-demo · GitHub

1 Like

Thank you.

I wasn’t overriding the methods bellow. Now it’s working as expected.

@Override
    public void setAttribute(String name, List<String> values) {
        String value = values != null && !values.isEmpty() ? values.get(0) : null;
        switch (name) {
            case UserModel.USERNAME -> setUsername(value);
            case UserModel.LAST_NAME -> setLastName(value);
            case UserModel.FIRST_NAME -> setFirstName(value);
            case UserModel.EMAIL -> setEmail(value);
            default -> super.setAttribute(name, values);
        }
    }

    @Override
    public String getFirstAttribute(String name) {
        return switch (name) {
            case UserModel.USERNAME -> getUsername();
            case UserModel.LAST_NAME -> getLastName();
            case UserModel.FIRST_NAME -> getFirstName();
//            case UserModel.EMAIL -> getEmail();
            default -> super.getFirstAttribute(name);
        };
    }

    @Override
    public Stream<String> getAttributeStream(String name) {
        return switch (name) {
            case UserModel.USERNAME -> Stream.of(getUsername());
            case UserModel.LAST_NAME -> Stream.of(getLastName());
            case UserModel.FIRST_NAME -> Stream.of(getFirstName());
            case UserModel.EMAIL -> Stream.of(getEmail());
            default -> super.getAttributeStream(name);
        };
    }
    @Override
    public Map<String, List<String>> getAttributes() {
        MultivaluedHashMap<String, String> attributes = getFederatedStorage().getAttributes(realm, this.getId());
        if (attributes == null) {
            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;
    }

    @Override
    public void setSingleAttribute(String name, String value) {
        switch (name) {
            case UserModel.USERNAME -> setUsername(value);
            case UserModel.LAST_NAME -> setLastName(value);
            case UserModel.FIRST_NAME -> setFirstName(value);
            case UserModel.EMAIL -> setEmail(value);
            default -> super.setSingleAttribute(name, value);
        }
    }