Skip to content

Lock brute force attacks by user properties - #3

Draft
Hockenba wants to merge 12 commits into
mainfrom
independent-auth-brute-force-protection
Draft

Hockenba wants to merge 12 commits into
mainfrom
independent-auth-brute-force-protection

Conversation

@Hockenba

@Hockenba Hockenba commented Sep 24, 2026 •

Copy link
Copy Markdown
Collaborator

Brute force detection currently keys every failure on the user id, so five failed attempts against one identifier lock the whole account. This adds an optional policy that keys failures on the identifier the user actually submitted.

A new realm Lock policy selects the behavior:

  • This user only — the existing per-user counter. Default, unchanged.
  • Shared properties only — increment only the selected user property whose value matches the submitted identifier.

Properties can be built-in (username, email) or any custom user profile attribute, configured in Lock by user properties under Realm settings > Security defenses. Reaching the threshold for one identifier blocks only that identifier, so a locked email still leaves username and phone number usable.

Identifier lockouts are enforced across the login form, direct grant, CIBA login_hint, and SAML ECP, and the Admin Console users list and user details report them. Unlocking a user clears the identifier counters for that user.

Because a property counter belongs to the property value rather than to an account, the documentation calls out that users sharing a value share the counter, and that changing a value does not migrate it.

When contributing this upstream, add Closes #<issue> here (Keycloak requires a GitHub issue per PR).

Locking a property no longer disables the whole account: the counter for
that identifier is blocked while every other identifier keeps working, so
a user locked out by email can still authenticate by phone number.

Resolve the attempted identifier through the protected properties as well,
otherwise a login by phone number could not be matched to a user, and clear
the per-identifier and per-channel counters on success.

Signed-off-by: Hockenba <github@opalpine.com>
A property counter blocks a single identifier and deliberately leaves the
user record enabled, so neither isEnabled() nor the account-wide
isTemporarilyDisabled() check in updateUser() noticed it. The Admin Console
unlock switch therefore reported success without clearing the counter.

Also drops the per-property lookup helpers left unused by the admin API.
Temporary locks already surfaced through failedLoginNotBefore; this
asserts ui-ext reports disabled for permanent identifier lockouts.
@Hockenba Hockenba changed the title Independent auth brute force protection Lock brute force attacks by user properties Sep 24, 2026
- Fall back to the username counter when no protected property matches the
  attempted identifier, and always protect the username under the properties policy
- Look up at most two users per protected property and support several users
  sharing a value when recording failures
- Use the identifier-aware lockout checks in IdentityBrokerService and DefaultUserProfile
- Remove property counters when a user is deleted or a protected attribute value
  changes, unless the value is still used
@Hockenba
Hockenba force-pushed the independent-auth-brute-force-protection branch 3 times, most recently from da765e4 to f8f74bd Compare October 16, 2026 15:58
# Conflicts:
#	testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/CIBATest.java
@Hockenba
Hockenba force-pushed the independent-auth-brute-force-protection branch from 3c4f652 to 6b29bf7 Compare October 19, 2026 18:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant