Keycloak 26 – How to permanently store invalid login attempts instead of cache?

Hi everyone,

I am using Keycloak 26.3.0 and I have enabled Brute Force Detection.

My configuration:

  • Max Login Failures = 3
  • Lockout permanently enabled
  • I am using Direct Grant (token endpoint API) for login, not browser login

Problem

I noticed that Keycloak stores invalid login attempts in its internal cache (Infinispan). Because of this:

  • The invalid attempt counter resets automatically after some time (for example after a few days)
  • Even if the user is not yet permanently locked, the count goes back to zero

This behavior makes it hard to track the full history of invalid login attempts, especially for security/audit purposes.

Requirement

I need this behavior:

  • Invalid login attempts should be stored permanently in the database
  • Failure counter should not reset automatically
  • User should be locked when failures reach the threshold
  • User stays locked according to Keycloak settings
  • Must work with token endpoint (/protocol/openid-connect/token)

Essentially, I want both the invalid attempt count and lock state to be persistent, not temporary cache-based.

Question

Is there any built-in configuration or recommended approach in Keycloak to persist login failure attempts permanently?

Or is a custom SPI/extension required for this use case?

Thanks for your guidance.

AFAIK there is no built-in way to do this. You would have to implement the BruteForceProtector SPI, or perhaps the UserLoginFailureProvider SPI which is the storage mechanism for the failures that the BruteForceProtector uses.

1 Like

Another, “integrated” way would be to use the user events in Keycloak. Enable to save the user events for your realm (realm settings → events → user events), select only the relevant event types (for invalid login attempts, only the LOGIN_ERROR event types are of interest).
This way you would get all the login errors which occur over time for each user. Kind of permanent storage of all the failed login attempts.
If you really want to have a non-resetting failure counter, you will have to implement some kind of extension, as @xgp mentioned.

2 Likes

For security-related reasons and best practices, please don’t use Direct Grant (also known as ROPC). Also, move toward a phishing-resistant authenticator so you don’t have to worry about brute-force attacks.

3 Likes