A Keycloak event listener which publishes events to NATS or NATS Streaming (STAN)
The layout of this project is inspired by aznamier/keycloak-event-listener-rabbitmq.
JDK 21 is required to build the project locally. This matches the Java version used in the Docker image (openjdk:21-slim) and is what Gradle will use from your PATH when running ./gradlew shadowJar.
- Download the source code (for example via Git)
- Build the project (
./gradlew shadowJar) - Move the built jar (located in
build/libsand ends with-all.jar) into the$KEYCLOAK_HOME/standalone/deploymentsdirectory - Keycloak supports hot reload, however, you may need to restart it
- Log into the admin console, select the realm you want to enable NATS event adapting for, head over to 'Events >
Config' and add
keycloak-nats-adapterto the list of active event listeners
Configuration is done via environment variables. I read something about some included XML configuration solution and decided not to use it because we don't live in the stone age anymore.
| Environment Variable | Data Type | Description | Default Value |
|---|---|---|---|
KEYCLOAK_NATS_JETSTREAM |
boolean | Whether or not to use NATS JetStream; plain NATS is used otherwise | false |
KEYCLOAK_NATS_JETSTREAM_MANAGE_STREAMS |
boolean | Whether or not to let the plugin create and manage streams | true |
KEYCLOAK_NATS_URL |
string | The NATS URL to connect to; may contain authentication details | nats://localhost:4222 |
KEYCLOAK_NATS_NKEY_SEED |
string | NKey seed for authentication (optional); when omitted, no NKey auth is used | |
KEYCLOAK_NATS_ENRICHED_EVENTS |
boolean | Whether or not to include enriched session data in client events | false |
JETSTREAM_ADMIN_SIZE |
string | Admin stream size when JetStream is used | 1 MB |
JETSTREAM_CLIENT_SIZE |
string | Client stream size when JetStream is used | 1 MB |
The channel name or subject structure depends on the event type:
- Admin Event:
keycloak.event.admin.<realm>.<success/error>.<resourceType>.<operation>(for examplekeycloak.event.admin.master.success.user.update) - Client Event:
keycloak.event.client.<realm>.<success/error>.<clientId>.<type>(for examplekeycloak.event.client.master.success.security-admin-console.refresh_token)
The resourceType, operation and type parts are always forced to be lowercase even though Keycloak fires them
in an uppercase format.
Spaces get replaced with underscores.
Channel keycloak.event.admin.ebbot.success.user.update
{
"id": "5ee1b9ee-426d-4f69-9877-b3a96b54da35",
"time": 1628089918834,
"realmId": "ebbot",
"authDetails": {
"realmId": "master",
"clientId": "56f3e1c8-1a94-4373-9173-90ed06ee9c83",
"userId": "96087d0a-8334-4305-be07-72166ca937a6",
"ipAddress": "172.30.0.1"
},
"resourceType": "USER",
"operationType": "UPDATE",
"resourcePath": "users/db85bef5-f1b8-462d-a563-7de86cf7a2da",
"representation": "{\"id\":\"db85bef5-f1b8-462d-a563-7de86cf7a2da\",\"createdTimestamp\":1628088891982,\"username\":\"johnny\",\"enabled\":true,\"totp\":false,\"emailVerified\":false,\"firstName\":\"john\",\"lastName\":\"doe\",\"email\":\"[email protected]\",\"attributes\":{},\"disableableCredentialTypes\":[],\"requiredActions\":[],\"notBefore\":0,\"access\":{\"manageGroupMembership\":true,\"view\":true,\"mapRoles\":true,\"impersonate\":true,\"manage\":true}}",
"error": null,
"resourceTypeAsString": "USER"
}Channel keycloak.event.client.ebbot.success.account-console.update_password
{
"id": "a1e97bf5-f1ac-477e-beb4-e8be6775f857",
"time": 1628091019154,
"type": "UPDATE_PASSWORD",
"realmId": "ebbot",
"clientId": "account-console",
"userId": "db85bef5-f1b8-462d-a563-7de86cf7a2da",
"sessionId": null,
"ipAddress": "172.30.0.1",
"error": null,
"details": {
"auth_method": "openid-connect",
"custom_required_action": "UPDATE_PASSWORD",
"response_type": "code",
"redirect_uri": "http://localhost:8080/auth/realms/ebbot/account/?referrer=security-admin-console&referrer_uri=http%3A%2F%2Flocalhost%3A8080%2Fauth%2Fadmin%2Fmaster%2Fconsole%2F%23%2Frealms%2Febbot%2Fusers%2Fdb85bef5-f1b8-462d-a563-7de86cf7a2da%2Fuser-credentials#/security/signingin",
"remember_me": "false",
"code_id": "594a0a98-1889-45c9-a6b9-58b731cfa13f",
"response_mode": "fragment",
"username": "johnny"
}
}