"Expire Password" policy in Custom User Storage Provider

So, we’ve implemented our custom user storage provider based on a stateful EJB that interacts with a proprietary relational user database and implements all sort of interfaces (UserStorageProvider, UserLookupProvider, UserQueryProvider, CredentialInputValidator, CredentialInputUpdater). It integrates well with Keycloak so far. We’re able to sign-in, list and modify users, change passwords and verify configured password policies.

However, what does not work at the moment is the Expire Password policy, for which I’m confused what needs to be done to support this. I found out so far that o.k.a.r.UpdatePassword.evaluateTriggers() retrieves a PasswordCredentialProvider and calls getPassword() and that one either gets the password and created date from the cached user model or from a o.k.c.UserCredentialStore.

What exactly do I need to implement? Should my user storage provider implement UserCredentialStore? This is confusing since it has a lot of methods that overlap with CredentialInputValidator and CredentialInputUpdater. Or do I need my own CredentialProvider (and CredentialProviderFactory)?

Any pointers are appreciated!


Turns out to easier than expected. In the implemented CredentialInputValidator.isValid() just do

int daysToExpire = realm.getPasswordPolicy().getDaysToExpirePassword();

if (daysToExpire > 0) {
    if (isPasswordExpired(/*...*/)) {
1 Like

Thank you … that really helped me out.

Adding password expiry from our backend via custom UserStorageProvider essentially was a three-liner.

Since I implemented a custom UserAdapter extending AbstractUserAdapterFederatedStorage I implemented it there in the constructor:

public MyUserAdapter(KeycloakSession session, RealmModel realm, ComponentModel storageProviderModel, MyUser myUser) {
    super(session, realm, storageProviderModel);
    this.myUser = myUser;
    this.keycloakId = StorageId.keycloakId(storageProviderModel, myUser.getId());

    if (Boolean.TRUE.equals(myUser.getRequireUpdatePassword())) {