vymalo / keycloak-webhook

Event-based Webhook plugin for Keycloak
https://blog.ssegning.com
MIT License
29 stars 6 forks source link

Is there a way to attach more data on registration/update event? #14

Open alolis opened 12 months ago

alolis commented 12 months ago

Hello,

So, I have been using the webhook to synchronize user registration data with my database and I was wondering if there is some way to include in the event object more data, like client roles, or user attributes.

I understand that I can fetch the extra data i need via keycloak REST API but if the event object can carry them it would definitely be helpful.

Thank you for your time.

stephane-segning commented 11 months ago

🤔 Sure. But it's an event. It'll mean fetching those data directly in the keycloak and potentially increasing the payload. The part in the code responsible of it is here. What other data would be need in that case?

alolis commented 11 months ago

Thanks for writing back @stephane-segning

I understand that I can fetch the extra data i need via keycloak REST API but if the event object can carry them it would definitely be helpful.

So, it seems that you can not do that inside the register event :) The reason is, from what I understand and please someone correct me if I am wrong, that the events are called synchronously from Keycloak. That means, that when the register event is fired, the user is not actually in the keycloak database yet and therefore you can not make API calls to fetch associated information. Some related information can be found here.

Implementors can leverage the fact that the onEvent and onAdminEvent are run within a running transaction. 
Hence, if the event processing uses JPA, it can insert event details into a table, and the whole transaction 
including the event is either committed or rolled back. However if transaction processing is not an option, 
e.g. in the case of log files, it is recommended to hook onto transaction after the commit is complete via the
[KeycloakTransactionManager.enlistAfterCompletion(KeycloakTransaction)](https://www.keycloak.org/docs-api/22.0.2/javadocs/org/keycloak/models/KeycloakTransactionManager.html#enlistAfterCompletion(org.keycloak.models.KeycloakTransaction))
method, so that the events are stacked in memory and only written to the file after the original transaction completes successfully.

If the above means what I think it means, then attaching data to the event will definitely be useful if your synchronization user flows depends on some user attribute or some role that is assigned during registration and I believe that it will be done more cleanly in the hook than waiting for the user to actually login to your system and then make the synchronization if you detect that you do not have the user data in your database yet.

But it's an event. It'll mean fetching those data directly in the keycloak and potentially increasing the payload.

The first thing I checked was if there was some kind of option to "attach token mapper to event" from the keycloak UI like you do with token mappers and "add to id token/add to access token/add to user info". That would definitely help controlling the payload size easier and it would be friendlier in general for more customization. I understand that this is not in the scope of this library I am just saying that it would have been nice :)

What other data would be need in that case?

I guess that this depends. Personally (for my case at least) I find realm roles/client roles and user attributes useful to include and if you are worried about the payload they can only be included in the register event that happens once instead of including them in events that happen very often (e.g. token refresh).

Also there are some other cases that I've noticed that you could have an extra field or two in the payload. For example, the CLIENT_ROLE_MAPPING-CREATE does not include the role name, just the id. Wouldn't it make sense to include it instead of doing an API call to get it from Keycloak?

Just throwing my thoughts out there :)

alolis commented 11 months ago

[...] from what I understand and please someone correct me if I am wrong, that the events are called synchronously from Keycloak

I would also like to say here that if the above is indeed true, and the hooks are called synchronously, then it would be very helpful if keycloak-webhook took under consideration the response http status from the remote it's calling.

For example, if a non 2xx response is returned (or a network failure maybe), the plugin code could throw an error and force a rollback of the keycloak transaction as a whole. Very useful in cases like registration (or any event really) in which the http remote fails to handle the incoming event payload (e.g. to sync it with own database) but the user is written in keycloak database. Now you have a user in keycloak, but not on your side, and you have to make extra checks to actually sync data from keycloak to your database.

I understand that this might not be the behavior everyone wants so an extra setting for the plugin might be helpful.

alolis commented 11 months ago

@stephane-segning ,I've been playing around some more with the plugin and the admin events also need some more data in order to make sense. For example, I delete a user from the Keycloak UI, and my backend receives a USER-DELETE event like the following:

{"clientId" => "7a568878-e210-4bb8-b30b-6c5afad58c7e", "id" => "28cc0396-ebf9-4362-bb73-1a130082d380", "ipAddress" => "192.168.65.1", "realmId" => "b1d47d38-238d-433e-9ea0-1e217c6b94e1", "time" => 1700049436966, "type" => "USER-DELETE", "userId" => "1bb1679d-07ac-4234-9cd6-b0992ead7d73"}

At first, I thought that the userId key contains the user id that this event is about. Unfortunately it's not. It's the id of the user who triggered this event. In my backend I want to delete from my database the user who just got deleted on the keycloak side but I have no idea what to delete based on the payload I got.

In the Keycloak UI, you can see the admin events like so:

Screenshot 2023-11-15 at 2 20 10 PM

Notice in the screenshot above the resourcePath. It contains the resource endpoint that this event is about. Now that's something that I could use for example and can easily be added in the admin event that keycloak-webhook sends. There is also another key in the event (if I am not mistaken) called representation. Not sure what that contains, I just noticed it in the UI somewhere. Might have more useful info in there that worth sending with the event.

In general, like I have mentioned in my previous posts in this issue, I think that a bit more data on which the receiving end can act upon, could be added to the event payload.

Edit: Plenty of useful stuff in the admin event payload from what I see here.

valid-var commented 8 months ago

Hello,

So, I have been using the webhook to synchronize user registration data with my database and I was wondering if there is some way to include in the event object more data, like client roles, or user attributes.

I understand that I can fetch the extra data i need via keycloak REST API but if the event object can carry them it would definitely be helpful.

Thank you for your time.

I did what you asked for here https://github.com/vymalo/keycloak-webhook/pull/19

alolis commented 7 months ago

@valid-var cheers! I will give it a try very soon :)