Filter Login Events?

I want to create a plugin similar to GitHub - ThoreKr/keycloak-last-login-event-listener: Automatically sets a last-login attribute on a user., by capturing the EventType.LOGIN and LOGIN_ERROR events.

But I only want to record logins and login_errors when users are entering usernames and passwords. For example, I have site1, site2, and site3 all secured by keycloak SSO. Now a user

  1. initially logins to www.site1.com with username and password
  2. accesses www.site2.com without having to re-enter password (due to SSO)
  3. accesses www.site3.com without having to re-enter password (due to SSO)
  4. accesses www.site1.com again 8 hour later with a token refresh

I only want (1) to appear in the log, not the subsequent (2)-(4). How can this be done?

I’m also interested in this topic, i have a similar situation where im listening on login events but i’m only interested in the event where the user inputs their username and password, im not interested in the event where the user is already authenticated and logs in to a different client.

Have you figured out a way to separate the 2 situations?

I’ve found that when the event is coming from an already authenticated user, the following 2 event.details are set, but they are not present when it’s the first login:

|Details||
|response_type|code|
|response_mode|fragment|

Maybe a coincidence.

I think @cedricguindon 's solution is a good one. Look at the payload and details for the events you are interested in (and the ones you want to ignore) and see what is different about them. As you have found, there is not really fine-grained detail in the event type.

Another approach I have taken is to add an Authenticator step for the flows I am interested in that simply sets a User Session Note. That way I can check for that note in the session inside my event listener. It’s a bit of a hack, but if you are trying to identify a specific flow, it can be helpful.

If you solve it in a different way, please share it here.

I might have found something. In the same vein of different details from one entry to another, i found out that the sessionId might be the best way to figure out if it’s a new login or an already authenticated user.

The sessionId will be the same from one entry to another if you’re already login, or will be different/new if it’s a new login.

I came up with this (the LOGIN event needs to be enabled in the events tab for it to work):

@Override
	public void onEvent(Event event) {
		try {
			if (EventType.LOGIN.equals(event.getType())) {
				if (session.users() != null) {
					String userId = event.getUserId();
					String realmId = event.getRealmId();

					// Getting current session ID.
					String currentSessionId = event.getSessionId();

					// Getting previous session ID.
					EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
					EventQuery query = eventStore.createQuery().realm(realmId);
					query.type(EventType.LOGIN);
					query.user(userId);
					query.firstResult(1);
					query.maxResults(1);

					String previousSessionId = query.getResultStream().findFirst()
							.map(ModelToRepresentation::toRepresentation).map(EventRepresentation::getSessionId)
							.orElse(null);

					if (previousSessionId == null || !currentSessionId.equals(previousSessionId)) {
						// Current session ID does not match previous session ID, new authentication.
						
					}
				}
			}
		} catch (Exception e) {
			log.error("Exception in LoginEventListenerProvider::Exception: ", e);
		}
	}

The above is listening on the LOGIN event, once a user logs in, it compares the currentSessionId, the one you got from that login event, to the one that was previously saved in the realm event, filtered by the same user and only the LOGIN event, returns only 1 results, the second last (it starts at 0).

If the previousSessionId is null, then it’s a brand new login.
If the previousSessionId does not match the current one, it’s a new login.
If the previousSessionId match the current, it means the user is already authenticated.

From there you can do whatever you want.

1 Like