Skip to content

Stop cookie-based SSO re-auth from resetting brute-force counter - #49996

Merged
rmartinc merged 1 commit into
keycloak:mainfrom
niels-van-nerum:fix-sso-reauth-resetting-brute-force-detection
Jun 16, 2026
Merged

rmartinc merged 1 commit into
keycloak:mainfrom
niels-van-nerum:fix-sso-reauth-resetting-brute-force-detection

Conversation

@niels-van-nerum

Copy link
Copy Markdown
Contributor

Cookie-based SSO re-authentication (e.g. a page reload in an already-authenticated
browser) was reusing the browser success path in
AuthenticationManager.redirectAfterSuccessfulFlow, which calls logSuccess and
clears the user's accumulated brute-force login failures.

This let an attacker continue password-guessing against a locked account as long as
a legitimate user with an active session triggered an SSO re-auth, the lockout
counter and "Temporarily locked" state were reset without any credential being
verified.

The fix gates the logSuccess call on !isSSOAuthentication, so the brute-force
counter is only reset by genuine credential-based authentication. SSO re-auth is now
neutral with respect to lockout state, while existing sessions remain active.

The OAuth refresh_token grant was investigated and is unaffected, it never reaches
logSuccess, which is covered by a regression test.

Added two tests to BruteForceTest:

  • testCookieSsoReauthenticationDoesNotResetLockout -> reproduces the bug (failed before the fix)
  • testAuthenticatedSessionRefreshDoesNotResetLockout -> confirms the token-refresh path is unaffected

Claude Code was used for understanding and discovering the codebase, and as a rubber-duck.

Closes #49960

@rmartinc rmartinc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot @niels-van-nerum! But I prefer to just reset the counter if any of the allowed credentials are used. This would cover other scenarios that can appear later (for example if someone says that using webauthn credential should not reset the brute force counter).

@rmartinc rmartinc self-assigned this Jun 15, 2026
@niels-van-nerum
niels-van-nerum requested a review from rmartinc June 15, 2026 16:41

@rmartinc rmartinc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @niels-van-nerum! We are almost there. 😄

Just a few comments. With this I also detected that the ResetPassword should pass the credential to perform the reset, which I think it is OK because this means we are strict now.

diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetPassword.java b/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetPassword.java
index 5ad402390d..6c943b709f 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetPassword.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetPassword.java
@@ -37,7 +37,8 @@ public class ResetPassword extends AbstractSetRequiredActionAuthenticator {
                         configuredFor(context))) {
             context.getAuthenticationSession().addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
         }
-        context.success();
+        // send password category to force brute-force reset for temporary lockout
+        context.success(PasswordCredentialModel.TYPE);
     }

I tested with this commit rmartinc@2673597 and I think it is OK.
With these changes everything is OK to me.

@niels-van-nerum

Copy link
Copy Markdown
Contributor Author

Should be good now! I'll squash when you give the OK.

@rmartinc rmartinc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @niels-van-nerum! LGTM! Squash it if you want and I'll start the tests.

This commit fixes an issue where a SSO re-auth was calling a reset on
the brute-force counter, and clearing lockout, allowing a malicious
actor to continue brute-forcing as long as the user was logged in and
active. Brute-force reset is now only called on a successful login that
wasn't a SSO re-auth.

It also resets the brute-force counter based on credential categories:
only categories defined in the BruteForceProtector under
ALLOWED_AUTHENTICATION_CATEGORIES are processed, allowing flexibility on
what counts as a valid login attempt. Null-handling in
BruteForceProtector was tightened for success and loosened for failure.

Closes keycloak#49960

Signed-off-by: Niels Van Nerum <vannerumniels@icloud.com>
@niels-van-nerum
niels-van-nerum force-pushed the fix-sso-reauth-resetting-brute-force-detection branch from f997416 to ee66646 Compare June 16, 2026 12:36
@niels-van-nerum

Copy link
Copy Markdown
Contributor Author

@rmartinc I am done squashing, thanks for the awesome guidance!

@rmartinc

Copy link
Copy Markdown
Contributor

Thanks @niels-van-nerum! I'll provide the backport to 26.6.

@rmartinc
rmartinc merged commit ed270d9 into keycloak:main Jun 16, 2026
88 checks passed
rmartinc pushed a commit to rmartinc/keycloak that referenced this pull request Jun 16, 2026
…cloak#49996)

This commit fixes an issue where a SSO re-auth was calling a reset on
the brute-force counter, and clearing lockout, allowing a malicious
actor to continue brute-forcing as long as the user was logged in and
active. Brute-force reset is now only called on a successful login that
wasn't a SSO re-auth.

It also resets the brute-force counter based on credential categories:
only categories defined in the BruteForceProtector under
ALLOWED_AUTHENTICATION_CATEGORIES are processed, allowing flexibility on
what counts as a valid login attempt. Null-handling in
BruteForceProtector was tightened for success and loosened for failure.

Closes keycloak#49960

Signed-off-by: Niels Van Nerum <vannerumniels@icloud.com>
(cherry picked from commit ed270d9)

test
ahus1 pushed a commit that referenced this pull request Jun 29, 2026
) (#50047)

This commit fixes an issue where a SSO re-auth was calling a reset on
the brute-force counter, and clearing lockout, allowing a malicious
actor to continue brute-forcing as long as the user was logged in and
active. Brute-force reset is now only called on a successful login that
wasn't a SSO re-auth.

It also resets the brute-force counter based on credential categories:
only categories defined in the BruteForceProtector under
ALLOWED_AUTHENTICATION_CATEGORIES are processed, allowing flexibility on
what counts as a valid login attempt. Null-handling in
BruteForceProtector was tightened for success and loosened for failure.

Closes #49960

Signed-off-by: Niels Van Nerum <vannerumniels@icloud.com>
(cherry picked from commit ed270d9)

test

Co-authored-by: Niels Van Nerum <vannerumniels@gmail.com>
sashyo pushed a commit to tide-foundation/keycloak-IGA that referenced this pull request Jul 20, 2026
…cloak#49996)

This commit fixes an issue where a SSO re-auth was calling a reset on
the brute-force counter, and clearing lockout, allowing a malicious
actor to continue brute-forcing as long as the user was logged in and
active. Brute-force reset is now only called on a successful login that
wasn't a SSO re-auth.

It also resets the brute-force counter based on credential categories:
only categories defined in the BruteForceProtector under
ALLOWED_AUTHENTICATION_CATEGORIES are processed, allowing flexibility on
what counts as a valid login attempt. Null-handling in
BruteForceProtector was tightened for success and loosened for failure.

Closes keycloak#49960

Signed-off-by: Niels Van Nerum <vannerumniels@icloud.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Brute-force detection: Authenticated session token refresh resets lockout counter

2 participants