Keycloak not using custom storage provider when storing user retrieved from external idp like google

I am using custom user provider spi . my use case is i want to store user autheticated/retrieved from external idp to my custom user provider spi or my custom DB and
I basically want ‘first broker login’ auth flow to use my user provider spi everytime instead of using keycloak user store .

I integrated custom user spi and checked a user login via external idp but found that it is not calling my custom user provider spi to store user info instead i think it is using keycloak user store.

How can i do so ?

@dasniko @thomasdarimont @mposolda
Can you please check .

My custom provider template is implementing UserRegistrationProvider interface as well .

Thanks in Advance .

I have followed the read only example on keycloak documentation as here

i have added UserRegistrationProvider interface as well to it .
I checked the keycloak source code and found that the UserStoreManager.java itself checks for registered providers and calls them if they have the desired capability interface .

I have also enabled and configured the spi during build process as mentioned here .

i am using the social login flow . While testing i am assuming any of my custom storage provider overridden methods should get called , but same is not happening , keycloak is not calling any method on my custom storage .

Also in the admin console i have configured my custom storage as user federation.

@Bill @dasniko @thomasdarimont @mposolda or Anyone .
Can you guys please confirm if am missing anything here .

Any help is much appreciated .
Thanks !!

You could take a look AbstractIdentityProviderMapper and implement a custom Identity Provider mapper to dynamically handle new users from IdPs.

okay Thanks @thomasdarimont .

I have implemented a custom idp class extending AbstractIdentityProviderMapper, i hope that’s correct .
But now i am getting below error while building and i am using keycloak 17 :

kc.bat build
Updating the configuration and installing your custom providers, if any. Please wait.
2022-12-19 17:06:19,976 WARN  [org.keycloak.services] (build-1) KC-SERVICES0047: null (com.keycloakSPI.storage.provider.CustomIdentityProviderMapper) is implementing the internal SPI identity-provider-mapper. This SPI is internal and may change without notice
2022-12-19 17:06:20,512 INFO  [com.keycloakSPI.storage.provider.CustomUserStorageProviderFactory] (build-1) [I24] CustomUserStorageProviderFactory created
2022-12-19 17:06:20,531 INFO  [com.keycloakSPI.storage.provider.CustomUserStorageProviderFactory] (build-1) [I69] getId()
2022-12-19 17:06:20,532 INFO  [com.keycloakSPI.storage.provider.CustomUserStorageProviderFactory] (build-1) [I69] getId()
2022-12-19 17:06:20,532 INFO  [com.keycloakSPI.storage.provider.CustomUserStorageProviderFactory] (build-1) [I69] getId()
2022-12-19 17:06:20,756 INFO  [com.keycloakSPI.storage.provider.CustomUserStorageProviderFactory] (build-1) [I69] getId()
ERROR: Failed to run 'build' command.
ERROR: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
        [error]: Build step io.quarkus.deployment.steps.MainClassBuildStep#build threw an exception: java.lang.RuntimeException: Failed to record call to method public void org.keycloak.quarkus.runtime.KeycloakRecorder.configSessionFactory(java.util.Map,java.util.Map,java.util.Map,java.lang.Boolean)
        at io.quarkus.deployment.recording.BytecodeRecorderImpl.writeBytecode(BytecodeRecorderImpl.java:509)
        at io.quarkus.deployment.steps.MainClassBuildStep.writeRecordedBytecode(MainClassBuildStep.java:444)
        at io.quarkus.deployment.steps.MainClassBuildStep.build(MainClassBuildStep.java:255)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:567)
        at io.quarkus.deployment.ExtensionLoader$2.execute(ExtensionLoader.java:882)
        at io.quarkus.builder.BuildContext.run(BuildContext.java:277)
        at org.jboss.threads.ContextHandler$1.runWith(ContextHandler.java:18)
        at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1478)
        at java.base/java.lang.Thread.run(Thread.java:835)
        at org.jboss.threads.JBossThread.run(JBossThread.java:501)
Caused by: java.lang.NullPointerException
        at io.quarkus.deployment.recording.BytecodeRecorderImpl.loadComplexObject(BytecodeRecorderImpl.java:1117)
        at io.quarkus.deployment.recording.BytecodeRecorderImpl.loadObjectInstanceImpl(BytecodeRecorderImpl.java:938)
        at io.quarkus.deployment.recording.BytecodeRecorderImpl.loadObjectInstance(BytecodeRecorderImpl.java:610)
        at io.quarkus.deployment.recording.BytecodeRecorderImpl.loadComplexObject(BytecodeRecorderImpl.java:1119)
        at io.quarkus.deployment.recording.BytecodeRecorderImpl.loadObjectInstanceImpl(BytecodeRecorderImpl.java:938)
        at io.quarkus.deployment.recording.BytecodeRecorderImpl.loadObjectInstance(BytecodeRecorderImpl.java:610)
        at io.quarkus.deployment.recording.BytecodeRecorderImpl.loadComplexObject(BytecodeRecorderImpl.java:1119)
        at io.quarkus.deployment.recording.BytecodeRecorderImpl.loadObjectInstanceImpl(BytecodeRecorderImpl.java:938)
        at io.quarkus.deployment.recording.BytecodeRecorderImpl.loadObjectInstance(BytecodeRecorderImpl.java:610)
        at io.quarkus.deployment.recording.BytecodeRecorderImpl.writeBytecode(BytecodeRecorderImpl.java:504)
        ... 13 more

and the custom class is as below :

public class CustomIdentityProviderMapper extends AbstractIdentityProviderMapper {

    private Logger log = LoggerFactory.getLogger(CustomIdentityProviderMapper.class);

    private KeycloakSession session;
    private IdentityProviderModel identityProviderModel;

    public CustomIdentityProviderMapper(KeycloakSession session, IdentityProviderModel identityProviderModel){
        this.session = session;
        this.identityProviderModel = identityProviderModel;
    }

    public CustomIdentityProviderMapper(){}

    public CustomIdentityProviderMapper(KeycloakSession session){
        this.session = session;
    }

    @Override
    public IdentityProviderMapper create(KeycloakSession session) {
        return new CustomIdentityProviderMapper(session) ;
    }

    @Override
    public void close() {

    }

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



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

    }

    @Override
    public void postInit(KeycloakSessionFactory factory) {

    }

    @Override
    public String[] getCompatibleProviders() {
        return new String[0];
    }

    @Override
    public String getDisplayCategory() {
        return null;
    }

    @Override
    public String getDisplayType() {
        return null;
    }

    @Override
    public void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
            log.info("CALLED BEFORE FIRST BROKER LOGIN**********************");
    }

    @Override
    public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
        log.info("CALLED AFTER FIRST BROKER LOGIN**********************");
    }

    @Override
    public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {

    }

    @Override
    public void updateBrokeredUserLegacy(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
        updateBrokeredUser(session, realm, user, mapperModel, context);
    }

    @Override
    public String getHelpText() {
        return null;
    }

    @Override
    public List<ProviderConfigProperty> getConfigProperties() {
        return null;
    }
}

Could you please check what correction it needs .
Thanks !!

I am still not been able to figure out what’s causing this error .

@dasniko @thomasdarimont

@vijender9423 I had also similar case where I was testing custom IdentityProviderMapper and I managed to make it work. From what I see in your case - you just need to make sure all methods return sensible defaults and not null.

Thanks @darzieba
I took a bit help earlier from GitHub - BenjaminFavre/keycloak-apple-social-identity-provider: Apple Social Identity Provider for Keycloak and was able to mitigate this build error somehow .
Thanks again for your reply .

it is building fine but now my i am assuming below 2 methods should get called which is not happening .

Is there anything i need to configure on keycloak console .I doubt if i have some misunderstanding about this functionality .
Do you have a sample code for what you did and any clarification on how i should proceed further .

I am assuming on using above CustomMapper in provider’s directory of keycloak . Upon social login via google these 2 above methods will get called itself before and after first broker login flow as mentioned under comments here

@vijender9423 I am no KC expert, just reached similar topics ;). After installing the plugin into KC I needed to configure it in Mappers tab of desired Identity Provider. Then you should see those methods being called, and how you handle those data would depend on your needs I guess.