Revoke refresh tokens after password reset

Hello everyone :slight_smile:
We are experiencing something (I think) weird and we need to know how to solve this with Keycloak.
We have an application which stores the refresh token for users. These are offline tokens.
When one of these users resets his password, the refresh token stored into our application is still valid and still issues new access tokens, despite of the fact that the password has been reset.

Is it a security hole? If someone stoles the refresh token, he can still obtain the access token despite the reset of the password.
How can the user behave if he feels that his password (and refresh token) has been stolen?

Is there any Keycloak configuration to mitigate (or resolve) this problem?

Thanks

from the docs on offline access tokens (Server Administration Guide):

Also if you enable the option Revoke refresh tokens , then each offline token can be used just once. So after refresh, you always need to store the new offline token from refresh response into your DB instead of the previous one.

If you use that option, then it will mitigate the use of old offline access tokens, because they will be invalidated every time you do a refresh.

Another option could be revoking the consent to the client (using the user account console). This also should invalidate refresh tokens.

regards,
Matthias

Thank you for your suggestions.

@xgp As for the revoke, I can’t figure how it could solve my problem: if the user who stole my refresh token uses it to obtain new couple access-refresh token, he could use every refresh token only once but every time he can obtain a new one; so the problem remains.

@mbonn So there is no way to trigger this after a password reset?

Correct, assuming the attacker uses the refresh token before the user does, he could do so indefinitely.

To trigger on a password reset, you can write a custom EventListenerProvider that listens for the UPDATE_PASSWORD event and then invalidates all the user’s refresh tokens.

@xgp thanks :slight_smile: I figured out how to implement a EventListenerProvider (with this guide: Building an Event Listener SPI (Plugin) for KeyCloak | by Adwait Thattey | Medium) but still I can’t find out how can I invalidate user’s refresh tokens programmatically.

Do you have any pointer for me?

If you’re trying to revoke an offline access token, you can call revoke consent:

In your EventListenerProvider, you can call:

    keycloakSession.users().revokeConsentForClient(realm, userId, clientInternalId);

https://www.keycloak.org/docs-api/15.0/javadocs/org/keycloak/models/UserProvider.html#revokeConsentForClient-org.keycloak.models.RealmModel-java.lang.String-java.lang.String-

@xgp you saved my day :slight_smile:
The instruction you gave me seems not to invalidate user’s refresh tokens, but pointed me on the right way.

This seems to do the magic:
keycloakSession.sessions().removeUserSessions(realm, user)

1 Like

Great! Thanks for sharing the solution that worked for you.

@xgp I’m here back to you :frowning:

The plugin seems working, but in production emerged the need to revoke also offline tokens, and that instruction did not work.

Does anyone know if and how could I revoke offline token consent of a specific user in an event listener?

See my other post here: Revoke offline token on logout - #2 by xgp

thanks for your answer :slight_smile: The post you linked me, unfortunately, indicated to call a REST API as a Keycloak admin.
My problem is that I am inside an event listener, so it have not access to admin credentials.

Earlier in this topic you indicated me this instruction:
keycloakSession.users().revokeConsentForClient(realm, userId, clientInternalId);, but it seems that does not work :frowning:

Also if I call keycloakSession.users().getConsents() on the user, is returned an empty list, but in the User -> Consents tab I can see the offline token consent for that user.

Take a look in the code at how those APIs work.

  1. get sessions keycloak/SessionResource.java at master · keycloak/keycloak · GitHub
  2. delete session keycloak/SessionResource.java at master · keycloak/keycloak · GitHub

from a quick look, it appears keycloakSession.session()... is what you need.