Syncing User Data Between Keycloak and Local Database
2024-01-31 · 3 min · Willy Setiawan
Table of Contents
One of the most common use cases before migrating to Keycloak is syncing data between Keycloak and existing user data. Here, we are going to implement a custom event listener to address the issue.
I recommend to implement the event listener in a separate module or project.
We need to create new classes that implement the interfaces provided by Keycloak, EventListenerProvider and EventListenerProviderFactory, in order to have a custom event listener. EventListenerProviderFactory includes functions to help identify the event listener ID and instantiate an EventListenerProvider class, whereas EventListenerProvider includes functions for handling events that pass through Keycloak. We use keycloak-dependencies-server-all library as a dependency.
Let’s see the implementation of the EventListenerProviderFactory class:
There are two functions used here: create and getId, which functions as their name implies. create function creates a new ExternalDbSyncProvider object, where data synchronization occurs.
ExternalDbSyncProvider class is a custom event listener class that implements EventListenerProvider.
privatefinalStringREALM_ID=System.getenv("TABUNGAN_REALM_ID");privatefinalStringDB_URL=MessageFormat.format("{0}/{1}",System.getenv("JDBC_DB_URL"),System.getenv("TABUNGAN_DB_NAME"));privatefinalStringDB_USER=System.getenv("JDBC_DB_USERNAME");privatefinalStringDB_PASSWORD=System.getenv("JDBC_DB_PASSWORD");privatefinalStringDB_SQL="INSERT INTO users (user_id, first_name, last_name, email, username) VALUES (?,?,?,?,?)";publicExternalDbSyncProvider(){};@OverridepublicvoidonEvent(Eventevent){if(event.getRealmId().equals(REALM_ID)&&event.getType()==EventType.REGISTER){try(Connectionconn=DbConnect();varpstmt=conn.prepareStatement(DB_SQL)){pstmt.setObject(1,UUID.fromString(event.getUserId()));pstmt.setString(2,event.getDetails().get("first_name"));pstmt.setString(3,event.getDetails().get("last_name"));pstmt.setString(4,event.getDetails().get("email"));pstmt.setString(5,event.getDetails().get("username"));intaffectedRows=pstmt.executeUpdate();}catch(SQLExceptione){System.out.println(e.getMessage());thrownewRuntimeException(e);}}}@OverridepublicvoidonEvent(AdminEventadminEvent,booleanb){}@Overridepublicvoidclose(){}privateConnectionDbConnect(){Connectionconn=null;try{conn=DriverManager.getConnection(DB_URL,DB_USER,DB_PASSWORD);}catch(SQLExceptione){System.out.println(e.getMessage());}returnconn;}
The snippet above is the implementation of ExternalDbSyncProvider class. onEvent method is where the execution takes place. It checks whether a user registration event originates from a specific realm ID. If this condition is true, it will insert a new user data into the existing database. Meanwhile, DbConnect is used to establish a connection between Keycloak and the user’s database.
Before packaging the classes into a JAR, we need to add a new file at src/main/resources/META-INF/services/org.keycloak.events.EventListenerProviderFactory with the value com.tabunganku.sync.ExternalDbSyncProviderFactory, which is the full class name of the class that implements the EventListenerProviderFactory interface. The JAR file can be created by running the mvn clean install command.