Keycloak, Tutorial, Java, Custom Event Listener

Keycloak - Custom Event Listener

Tutorial about how to create and deploy a custom event listener for Keycloak.

Author Martin
Martin23rd November 2020

This tutorial shows how you can create your very specific event listener in Keycloak. Keycloak let's you hook into almost 100 different event types. The example we're building in this tutorial will send an email to an admin on every new registration. In this way somebody of the company will be notified whenever a new user signs up. The repository of the example can be found here.

How to create the listener?

In order to implement a custom event listener you need to impement the org.keycloak.events.EventListenerProvider interface. The function onEvent will be called on every event that happens in Keycloak. In this example we check for the REGISTER event and send an email to an admin user. The class AdminUser (see here) implements the Keycloak user model to provide a hard coded email to which the event listener sends the notification.

1@Override
2public void onEvent(Event event) {
3
4    if (EventType.REGISTER.equals(event.getType())) {
5        log.infof("## NEW %s EVENT", event.getType());
6        log.info("-----------------------------------------------------------");
7
8        RealmModel realm = this.model.getRealm(event.getRealmId());
9        UserModel newRegisteredUser = this.session.users().getUserById(event.getUserId(), realm);
10
11        String emailPlainContent = "New user registration\n\n" +
12                "Email: " + newRegisteredUser.getEmail() + "\n" +
13                "Username: " + newRegisteredUser.getUsername() + "\n" +
14                "Client: " + event.getClientId();
15
16        String emailHtmlContent = "<h1>New user registration</h1>" +
17                "<ul>" +
18                "<li>Email: " + newRegisteredUser.getEmail() + "</li>" +
19                "<li>Username: " + newRegisteredUser.getUsername() + "</li>" +
20                "<li>Client: " + event.getClientId() + "</li>" +
21                "</ul>";
22
23        DefaultEmailSenderProvider senderProvider = new DefaultEmailSenderProvider(session);
24
25        try {
26            senderProvider.send(session.getContext().getRealm().getSmtpConfig(), new AdminUser(), "Keycloak - New Registration", emailPlainContent, emailHtmlContent);
27        } catch (EmailException e) {
28            log.error("Failed to send email", e);
29        }
30        log.info("-----------------------------------------------------------");
31    }
32}

In order to be able to register this custom event listener class in Keycloak you will need to implement org.keycloak.events.EventListenerProviderFactory interface and return the newly created class in the create method.

1public class CustomEventListenerProviderFactory implements EventListenerProviderFactory {
2    @Override
3    public EventListenerProvider create(KeycloakSession keycloakSession) {
4        return new CustomEventListenerProvider(keycloakSession);
5    }
6
7    @Override
8    public void init(Config.Scope scope) {
9
10    }
11
12    @Override
13    public void postInit(KeycloakSessionFactory keycloakSessionFactory) {
14
15    }
16
17    @Override
18    public void close() {
19
20    }
21
22    @Override
23    public String getId() {
24        return "custom-event-listener";
25    }
26}

The final step before we can package our listener is to create the following file src/main/resources/META-INF/services/org.keycloak.events.EventListenerProviderFactory. This will make sure that Keycloak can discover the new Listener.

1it.aboutbits.CustomEventListenerProviderFactory

You can find the file in the example repository here.

Bonus: Using keycloak services within listener

If you would like to use services from keycloak within your listener, like the email service in the example, then you have to add this file src/main/resources/META-INF/jboss-deployment-structure.xml.

1<?xml version="1.0" encoding="UTF-8"?>
2<jboss-deployment-structure>
3    <deployment>
4        <dependencies>
5            <module name="org.keycloak.keycloak-services" />
6        </dependencies>
7    </deployment>
8</jboss-deployment-structure>

How to deploy the artifact?

Once you created the artifact you have to copy the jar into your keycloak installation ($KEYCLOAK_DIR/standalone/deployments/). If you are using docker to run your Keycloak instance you can do the following:

1version: "3.7"
2
3services:
4  keycloak:
5    image: jboss/keycloak:11.0.3
6    environment:
7      DB_VENDOR: POSTGRES
8      DB_ADDR: postgres
9      DB_DATABASE: keycloak
10      DB_SCHEMA: public
11      DB_USER: keycloak
12      DB_PASSWORD: password
13      KEYCLOAK_USER: admin
14      KEYCLOAK_PASSWORD: password
15    volumes:
16      - ./custom-event-listener/target/custom-event-listener-0.0.1-SNAPSHOT.jar://opt/jboss/keycloak/standalone/deployments/custom-event-listener-0.0.1-SNAPSHOT.jar
17    depends_on:
18      - postgres
19    ports:
20      - 8080:8080
21
22  postgres:
23    image: postgres:12
24    environment:
25      POSTGRES_DB: keycloak
26      POSTGRES_USER: keycloak
27      POSTGRES_PASSWORD: password
28    ports:
29      - 5432:5432
30
31  mailhog:
32    image: mailhog/mailhog
33    ports:
34      - 8025:8025

How to enable the listener?

You're almost done. The listener should now be available in the list of event listeners. All you have to do now is to select the listener from the dropdown of available event listeners and save.

Register the event listener

To see a running example checkout the repository and follow the instructions in the readme.