How can I make sure any user can have only one active session?

2,353 views
Skip to first unread message

DP

unread,
Apr 4, 2023, 3:54:30 AM4/4/23
to Keycloak User
Hello!

I want to implement a "one user -- one session" policy, that is, any single user can be logged in in one session at any time.

What is the proper way to do this in Keycloak 20+?

I found the thread (see ref. 1 below)  which suggests creating an event listener and removing all the sessions there.

However, on StackOverflow (ref. 2) I read that the "one user -- one session" limit was implemented in Keycloak 17.X.

But I cannot find the place in the GUI where I can set this limit.

Questions

1. Is it indeed possible to limit the number of active user sessions to 1 without programming?

2. If yes, how can I configure this (make sure that no user can be logged more than once simultaneously)?

References

Ref. 1: https://groups.google.com/g/keycloak-user/c/V_WvT6tRGKQ

Ref. 2: https://stackoverflow.com/a/60197078

Thanks in advance

sebastian.schuster

unread,
Apr 4, 2023, 4:29:34 AM4/4/23
to Keycloak User

DP

unread,
Apr 5, 2023, 8:00:47 AM4/5/23
to Keycloak User
Hi!

Thanks.

I tried adding "User Session Count Limiter" to my authentication flow, but it did not seem to help. I was still able to keep two active sessions on two different devices.

Thereafter I modified my custom authenticator as follows.

I added the following code at the place where all normal checks (password and OTP verification) were succesful:

final List<UserSessionModel> activeSessions = lockUserSessionsForModification(session, () -> session.sessions().getUserSessionsStream(context.getRealm(), context.getUser()).collect(Collectors.toList()));

if (!activeSessions.isEmpty()) {
    logoutOldestSession(activeSessions);
}

context.success();

logoutOldestSession looks like this:

private void logoutOldestSession(List<UserSessionModel> sessions) {
    final Optional<UserSessionModel> oldest = sessions.stream().sorted(comparingInt(UserSessionModel::getLastSessionRefresh)).findFirst();
    oldest.ifPresent(userSession -> backchannelLogout(session, userSession, true));
}


This kind of works. If I

1. log in under user A on device 1,
2. log in under user A on dervice 2,
3. refresh the web application in the browser on device 1,

then, on device 1 I get redirected to the login page.

What I want is different in two ways:

1. I want the user on device 1 to be redirected to the login page more or less immediately (ideally -- even if they don't do anything in the browser).

2. The login page on device 1 should contain a message telling the user that they were logged out because of a login on another device.

Are these things achievable?

If yes, how?

Thanks

DP

unread,
Apr 5, 2023, 10:35:29 AM4/5/23
to Keycloak User
Hi!

I managed to fix problem 1 (user is kicked out of the active session immediately, if they login on another device) by adding the following code to my frontend based on Vue:

  setInterval(() => {
    keycloak.updateToken(70).then((refreshed) => {
      if (refreshed) {
        Vue.$log.info('Token refreshed' + refreshed);
      } else {
        Vue.$log.warn('Token not refreshed, valid for '
          + Math.round(keycloak.tokenParsed.exp + keycloak.timeSkew - new Date().getTime() / 1000) + ' seconds');
      }
    }).catch(() => {
      Vue.$log.error('Failed to refresh token');
    });
  }, 6000)

Source: https://github.com/keycloak/keycloak-quickstarts/blob/latest/applications/app-vue/src/main.js

But I am struggling with the second requirement: Output of a message like "you were logged out because of a login on another device" on the login page.

Is there a way to display such message after a user's session is terminated?

Thanks in advance
Reply all
Reply to author
Forward
0 new messages