External custom user storage but all credentials in Keycloak

I am trying to integrate Keycloak with my legacy applications, with minimal additional development. My use case would go as follows:

  • User identities (login, email, first name, last name) would be stored in an external database and managed (created, updated, deleted) outside of Keycloak.
  • User credentials (password, OTP, MFA and so on) would be stored and managed by Keycloak, without relying on the external database.

I have read through the User Storage SPI documentation page, but it is not clear to me whether the case I describe above is possible.

The situation is that I already have an external database of user identities, and I need to keep it. Of course, I could enrich it to also store credentials and so on, and then write all the SPIs to integrate these custom storage in Keycloak but that seems like redundant work, since Keycloak is already designed to handle credentials.

So my question is: can I delegate user identities storage to an external database, while keeping all user credentials storage inside Keycloak, and how should I proceed? Many thanks!

1 Like

Yes, that’s possible. Just don’t implement the interfaces CredentialInputUpdaterand CredentialInputValidator. Then, all credential handling will be done in Keycloak itself and credentials will be saved in the Keycloak DB.

2 Likes

Hi,

For this purpose, we implemented a provider as :
public class OurUserStorageProvider implements UserLookupProvider, UserStorageProvider, UserQueryProvider {

To keep the credentials in the Keycloak database. We activiated the reset password but when tring to reset, we got error :
Uncaught server error: org.keycloak.storage.ReadOnlyException: user is read only for this update

Seems the password is considerated being in the Identies Storage.

How can we keep password, OTP info and so on in the keycloak database ?

Many Thanks !

Detailed error :
ERROR [org.keycloak.services.error.KeycloakErrorHandler] (executor-thread-3) Uncaught server error: org.keycloak.storage.ReadOnlyException: user is read only for this update
at org.keycloak.storage.adapter.AbstractUserAdapter.removeRequiredAction(AbstractUserAdapter.java:84)
at org.keycloak.services.resources.LoginActionsService.processRequireAction(LoginActionsService.java:1102)
at org.keycloak.services.resources.LoginActionsService.requiredActionPOST(LoginActionsService.java:1025)
at org.keycloak.services.resources.LoginActionsService$quarkusrestinvoker$requiredActionPOST_677a8efd4e80bfe1b3aa5a0d6fca2043252c9624.invoke(Unknown Source)
at org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
at io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141)
at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:145)
at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:576)
at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2513)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1538)
at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:840)

Hi again,

I added to my customUserAdapter this override :
@Override
public void removeRequiredAction(String action) {
log.info(“removeRequiredAction({})”, action);
if (action == “UPDATE_PASSWORD”) {
return;
}

    throw new ReadOnlyException("user is read only for this update");
}

Is this the good way just to say we allow this action ?

Regards,

mplasse

Hey!

I want to achieve the same as you, did you solve it?
Currently, I have developed a Provider that implements the UserStorageProvider, UserLookupProvider, and UserQueryProvider interfaces, like @dasniko said.
Yet, I’m encountering an issue: whenever I attempt to assign credentials to users via the Keycloak admin console, I receive an error accompanied by the following log message: [org.keycloak.services.error.KeycloakErrorHandler] (executor-thread-15) Uncaught server error: org.keycloak.storage.ReadOnlyException: user is read only for this update.

See my answer here:

1 Like