Multiple LoginFormsProviderFactory implementations

Hi,
Probably, some of you already tried to do something similar but I wasn’t able to find any valuable response. The problem is:
There is a need to have multiple (currently 2) themes deployed in a Keycloak server instance, different themes are used by different Realms. Those themes are trying to change the default login theme behavior. I’m implementing LoginFormsProviderFactory in order to provide my own LoginFormsProvider that extends FreeMarkerLoginFormsProvider. But the problem is, that only 1 is visible to the Keycloak, the other one is ignored, unless, I uninstall one theme from Keycloak.
There is no possibility of having multiple implementations of LoginFormsProviderFactory (one per theme/jar) deployed at the same time in the Keycloak. It seems that Keycloak is specifically asking for getId() method from Factory to be exactly like this:

    @Override
    public String getId() {
        return "freemarker";
    }

If anything else, is returned, the Keycloak does not see it, the login page rendering fails and an error is thrown

ERROR [org.keycloak.services.error.KeycloakErrorHandler] (executor-thread-2) Uncaught server error: java.lang.NullPointerException: Cannot invoke "org.keycloak.forms.login.LoginFormsProvider.setAuthenticationSession(org.keycloak.sessions.AuthenticationSessionModel)" because the return value of "org.keycloak.models.KeycloakSession.getProvider(java.lang.Class)" is null

Does anyone have any idea how to overcome this problem?
I’m using Keycloak 21.0.2 at the moment.

Regards

Try to implement a kind of “composite” factory, which delivers a proper provider depending on the current realm. Set this provider factory as the default:

public class CompositeLoginFormsProviderFactory implements LoginFormsProviderFactory {
	
	public static final String PROVIDER_ID = "composite";
	
	@Override
	public LoginFormsProvider create(KeycloakSession session) {
		if (session.getContext().getRealm().getName().equals("myRealm")) {
			return new CustomLoginFormsProvider(session);
		} else {
			return new FreeMarkerLoginFormsProvider(session);
		}
	}

	@Override
	public void init(Config.Scope config) {
	}

	@Override
	public void postInit(KeycloakSessionFactory factory) {
	}

	@Override
	public void close() {
	}

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

Configure as default provider:

spi-login-provider=composite

Hi @dasniko,
Thanks for the answer.
Well, the problem is a bit deeper than that.
The problem is that 2 themes are deployed within 2 different jars. For example, within jarA there is a ThemeA, and login forms provider should do one thing, and within jarB, there is a ThemeB which does a different thing.
Obviously, I can’t create 1 class like you shared, since I can’t “call” jarB from jarA and the way around.

I would expect that If I set ThemeA as the login theme for RealmA, the login forms provider from ThemeA should be invoked, the same when talking about RealmB.
The case here is that even if I set ThemeB for the RealmB login theme, ThemeA will be invoked, and the login forms provider from ThemeA will be invoked, even though, I try login into RealmB.
Probably, there is some logic behind that decides which forms provider should be invoked, since, obviously, the required one is not invoked.

Is there any propagation, like: you provided 2 login forms providers with the same id=freemarker, I’ll invoke firstly the one from ThemeA, and if you return the default one FreeMarkerLoginFormsProvider, I’ll call then ThemeB?

Detailed Example:
I try to log in into RealmB which uses ThemeB, and Keycloak, for some reason, invokes login forms provider from ThemeA, where I make something similar to what you shared in the code example above, and since the check said that ThemeA forms provider should not be used, and I provide the default one like you did, will then Keycloak call again forms provider from the ThemeB?

If what I described above is possible, then, probably somewhere I should add all those custom providers, right?

Hi , did you get a solution for this issue.

Hi,
There is no solution to using it in the described way.
Keycloak can’t handle multiple jars with the same/similar implementation since it’s not “smart” enough to check the name of the Theme when it should invoke a lib. This is very poorly solved.
I solved this by merging 2 libs into one, making a “parent” Factory that does the check to where the request should go, and creating an instance of the required class.
So now, each time the check is performed.