8000 Reset email background fallback by gaoyikeshuer · Pull Request #53991 · keycloak/keycloak · GitHub
[go: up one dir, main page]

Skip to content

Reset email background fallback - #53991

Merged
rmartinc merged 1 commit into
keycloak:mainfrom
gaoyikeshuer:reset-email-background-fallback
Nov 11, 2026
Merged

rmartinc merged 1 commit into
keycloak:mainfrom
gaoyikeshuer:reset-email-background-fallback

Conversation

@gaoyikeshuer
@gaoyikeshuer gaoyikeshuer commented Oct 14, 2026 •
Copy link
Copy Markdown
Contributor

Closes #26625.

the problem: when someone types a username on the fogot password page, KC answers "You should receive and email shortly" no matter what. but the response time it took to answer was not the same. for the existed username and has email. KC will build the email, connected to the mail server, and wait for the answer, only after then reply to the browser. for the non existed username, KC reply right away.

So the main fix idea is to stop waiting to the mail server. the page will answers first , adn the email is sent later by a small pool of backrgound threads (KC's exisiting ExecutorProvider, which has 4 threads by default)

now the KC will build email for the unknown users as well. so known and unknown usernames walk the same path.

background sending needs a limit or a flood of requests could pile up emails in memeory. in this pr the limit is simple at most one email per backgroudn thread. exactly like our current main branch no email got dropped.

the trade off of this PR is :

When all 4 background threads are busy, requests send the email themselves, so under heavy load the response waits for the mail server again, as it does today. Someone with a few valid accounts could keep the threads busy on purpose and then measure other usernames. This is never worse than today's behaviour, and the proper fix for load is rate limiting reset requests, which is a separate change. I'd like team to view on whether this is the right way and the alternatives are an unbounded queue, or dropping extra emails with an error event.

after the fix:

no matter known user or unknown user they got the reply same time

Screen.Recording.2026-10-15.at.13.41.08.mov

updated:

apply the fix for comments. now the form no longer waits for the mail server. The email is rendered during the request and delivered in the background by the existing ExecutorsProvider, using a new email executor.and only after the request’s transaction commits, so nothing is sent if the request rolls back.

Backpressure: the email executor has a bounded queue (4–64 threads, queue of 128 by default). When more than 64+128 emails are pending, the request fails wit 8000 h an EmailException and records SEND_RESET_PASSWORD_ERROR . emails never pile up in memory and there is no synchronous fallback. Admins can tune spi-executors--default--email-min/-max/-queue; the new queue option works for every executor type, and the existing types stay unbounded by default.

@gaoyikeshuer
gaoyikeshuer force-pushed the reset-email-background-fallback branch 5 times, most recently from 95dc7b3 to 42cf2d8 Compare October 15, 2026 10:44
@keycloak-github-bot keycloak-github-bot Bot left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Unreported flaky test detected, please review

@keycloak-github-bot
Copy link
Copy Markdown

Unreported flaky test detected

If the flaky tests below are affected by the changes, please review and update the changes accordingly. Otherwise, a maintainer should report the flaky tests prior to merging the PR.

org.keycloak.testsuite.oauth.ClientAuthSignedJWTTest#testUploadCertificatePemEcdsa

Keycloak CI - FIPS IT (non-strict)

java.lang.RuntimeException: jakarta.ws.rs.InternalServerErrorException: HTTP 500 Internal Server Error
	at org.keycloak.testsuite.AbstractKeycloakTest.afterAbstractKeycloakTest(AbstractKeycloakTest.java:269)
	at java.base/java.lang.reflect.Method.invoke(Method.java:565)
	at org.jboss.arquillian.junit.Arquillian$StatementLifecycleExecutor.invoke(Arquillian.java:372)
	at org.jboss.arquillian.container.test.impl.execution.ClientBeforeAfterLifecycleEventExecuter.execute(ClientBeforeAfterLifecycleEventExecuter.java:88)
...

Report flaky test

org.keycloak.testsuite.oauth.ClientAuthSignedJWTTest#testUploadKeystoreBCFKS

Keycloak CI - FIPS IT (non-strict)

jakarta.ws.rs.WebApplicationException: Create method returned status Conflict (Code: 409); expected status: Created (201). Response body: {"errorMessage":"Client client3 already exists"}
	at org.keycloak.testsuite.admin.ApiUtil.getCreatedId(ApiUtil.java:38)
	at org.keycloak.testsuite.oauth.AbstractClientAuthSignedJWTTest.recreateApp3(AbstractClientAuthSignedJWTTest.java:236)
	at java.base/java.lang.reflect.Method.invoke(Method.java:565)
	at org.jboss.arquillian.junit.Arquillian$StatementLifecycleExecutor.invoke(Arquillian.java:372)
...

Report flaky test

org.keycloak.testsuite.oauth.ClientAuthSignedJWTTest#testAssertionMissingIssuer

Keycloak CI - FIPS IT (non-strict)

jakarta.ws.rs.WebApplicationException: Create method returned status Conflict (Code: 409); expected status: Created (201). Response body: {"errorMessage":"Client client3 already exists"}
	at org.keycloak.testsuite.admin.ApiUtil.getCreatedId(ApiUtil.java:38)
	at org.keycloak.testsuite.oauth.AbstractClientAuthSignedJWTTest.recreateApp3(AbstractClientAuthSignedJWTTest.java:236)
	at java.base/java.lang.reflect.Method.invoke(Method.java:565)
	at org.jboss.arquillian.junit.Arquillian$StatementLifecycleExecutor.invoke(Arquillian.java:372)
...

Report flaky test

org.keycloak.testsuite.oauth.ClientAuthSignedJWTTest#testWithClientAndMissingClientAssertionType

Keycloak CI - FIPS IT (non-strict)

jakarta.ws.rs.WebApplicationException: Create method returned status Conflict (Code: 409); expected status: Created (201). Response body: {"errorMessage":"Client client3 already exists"}
	at org.keycloak.testsuite.admin.ApiUtil.getCreatedId(ApiUtil.java:38)
	at org.keycloak.testsuite.oauth.AbstractClientAuthSignedJWTTest.recreateApp3(AbstractClientAuthSignedJWTTest.java:236)
	at java.base/java.lang.reflect.Method.invoke(Method.java:565)
	at org.jboss.arquillian.junit.Arquillian$StatementLifecycleExecutor.invoke(Arquillian.java:372)
...

Report flaky test

org.keycloak.testsuite.oauth.ClientAuthSignedJWTTest#testMissingIssuerClaim

Keycloak CI - FIPS IT (non-strict)

jakarta.ws.rs.WebApplicationException: Create method returned status Conflict (Code: 409); expected status: Created (201). Response body: {"errorMessage":"Client client3 already exists"}
	at org.keycloak.testsuite.admin.ApiUtil.getCreatedId(ApiUtil.java:38)
	at org.keycloak.testsuite.oauth.AbstractClientAuthSignedJWTTest.recreateApp3(AbstractClientAuthSignedJWTTest.java:236)
	at java.base/java.lang.reflect.Method.invoke(Method.java:565)
	at org.jboss.arquillian.junit.Arquillian$StatementLifecycleExecutor.invoke(Arquillian.java:372)
...

Report flaky test

org.keycloak.testsuite.oauth.ClientAuthSignedJWTTest#testAssertionExpired

Keycloak CI - FIPS IT (non-strict)

jakarta.ws.rs.WebApplicationException: Create method returned status Conflict (Code: 409); expected status: Created (201). Response body: {"errorMessage":"Client client3 already exists"}
	at org.keycloak.testsuite.admin.ApiUtil.getCreatedId(ApiUtil.java:38)
	at org.keycloak.testsuite.oauth.AbstractClientAuthSignedJWTTest.recreateApp3(AbstractClientAuthSignedJWTTest.java:236)
	at java.base/java.lang.reflect.Method.invoke(Method.java:565)
	at org.jboss.arquillian.junit.Arquillian$StatementLifecycleExecutor.invoke(Arquillian.java:372)
...

Report flaky test

org.keycloak.testsuite.oauth.ClientAuthSignedJWTTest#testCodeToTokenRequestSuccessRS256usingJwks

Keycloak CI - FIPS IT (non-strict)

jakarta.ws.rs.WebApplicationException: Create method returned status Conflict (Code: 409); expected status: Created (201). Response body: {"errorMessage":"Client client3 already exists"}
	at org.keycloak.testsuite.admin.ApiUtil.getCreatedId(ApiUtil.java:38)
	at org.keycloak.testsuite.oauth.AbstractClientAuthSignedJWTTest.recreateApp3(AbstractClientAuthSignedJWTTest.java:236)
	at java.base/java.lang.reflect.Method.invoke(Method.java:565)
	at org.jboss.arquillian.junit.Arquillian$StatementLifecycleExecutor.invoke(Arquillian.java:372)
...

Report flaky test

org.keycloak.testsuite.oauth.ClientAuthSignedJWTTest#testCodeToTokenRequestSuccessPS384

Keycloak CI - FIPS IT (non-strict)

jakarta.ws.rs.WebApplicationException: Create method returned status Conflict (Code: 409); expected status: Created (201). Response body: {"errorMessage":"Client client3 already exists"}
	at org.keycloak.testsuite.admin.ApiUtil.getCreatedId(ApiUtil.java:38)
	at org.keycloak.testsuite.oauth.AbstractClientAuthSignedJWTTest.recreateApp3(AbstractClientAuthSignedJWTTest.java:236)
	at java.base/java.lang.reflect.Method.invoke(Method.java:565)
	at org.jboss.arquillian.junit.Arquillian$StatementLifecycleExecutor.invoke(Arquillian.java:372)
...

Report flaky test

org.keycloak.testsuite.oauth.ClientAuthSignedJWTTest#testCodeToTokenRequestSuccessPS512

Keycloak CI - FIPS IT (non-strict)

jakarta.ws.rs.WebApplicationException: Create method returned status Conflict (Code: 409); expected status: Created (201). Response body: {"errorMessage":"Client client3 already exists"}
	at org.keycloak.testsuite.admin.ApiUtil.getCreatedId(ApiUtil.java:38)
	at org.keycloak.testsuite.oauth.AbstractClientAuthSignedJWTTest.recreateApp3(AbstractClientAuthSignedJWTTest.java:236)
	at java.base/java.lang.reflect.Method.invoke(Method.java:565)
	at org.jboss.arquillian.junit.Arquillian$StatementLifecycleExecutor.invoke(Arquillian.java:372)
...

Report flaky test

org.keycloak.testsuite.oauth.ClientAuthSignedJWTTest#testECDSASignature

Keycloak CI - FIPS IT (non-strict)

jakarta.ws.rs.WebApplicationException: Create method returned status Conflict (Code: 409); expected status: Created (201). Response body: {"errorMessage":"Client client3 already exists"}
	at org.keycloak.testsuite.admin.ApiUtil.getCreatedId(ApiUtil.java:38)
	at org.keycloak.testsuite.oauth.AbstractClientAuthSignedJWTTest.recreateApp3(AbstractClientAuthSignedJWTTest.java:236)
	at java.base/java.lang.reflect.Method.invoke(Method.java:565)
	at org.jboss.arquillian.junit.Arquillian$StatementLifecycleExecutor.invoke(Arquillian.java:372)
...

Report flaky test

org.keycloak.testsuite.oauth.ClientAuthSignedJWTTest#testSuccessDefaultAlgWhenNoAlgSetInJWK

Keycloak CI - FIPS IT (non-strict)

jakarta.ws.rs.WebApplicationException: Create method returned status Conflict (Code: 409); expected status: Created (201). Response body: {"errorMessage":"Client client3 already exists"}
	at org.keycloak.testsuite.admin.ApiUtil.getCreatedId(ApiUtil.java:38)
	at org.keycloak.testsuite.oauth.AbstractClientAuthSignedJWTTest.recreateApp3(AbstractClientAuthSignedJWTTest.java:236)
	at java.base/java.lang.reflect.Method.invoke(Method.java:565)
	at org.jboss.arquillian.junit.Arquillian$StatementLifecycleExecutor.invoke(Arquillian.java:372)
...

Report flaky test

org.keycloak.testsuite.oauth.ClientAuthSignedJWTTest#testAuthenticationFailsWhenClientSecretJWTAuthenticatorSet

Keycloak CI - FIPS IT (non-strict)

jakarta.ws.rs.WebApplicationException: Create method returned status Conflict (Code: 409); expected status: Created (201). Response body: {"errorMessage":"Client client3 already exists"}
	at org.keycloak.testsuite.admin.ApiUtil.getCreatedId(ApiUtil.java:38)
	at org.keycloak.testsuite.oauth.AbstractClientAuthSignedJWTTest.recreateApp3(AbstractClientAuthSignedJWTTest.java:236)
	at java.base/java.lang.reflect.Method.invoke(Method.java:565)
	at org.jboss.arquillian.junit.Arquillian$StatementLifecycleExecutor.invoke(Arquillian.java:372)
...

Report flaky test

org.keycloak.testsuite.oauth.ClientAuthSignedJWTTest#testParEndpointAsAudience

Keycloak CI - FIPS IT (non-strict)

jakarta.ws.rs.WebApplicationException: Create method returned status Conflict (Code: 409); expected status: Created (201). Response body: {"errorMessage":"Client client3 already exists"}
	at org.keycloak.testsuite.admin.ApiUtil.getCreatedId(ApiUtil.java:38)
	at org.keycloak.testsuite.oauth.AbstractClientAuthSignedJWTTest.recreateApp3(AbstractClientAuthSignedJWTTest.java:236)
	at java.base/java.lang.reflect.Method.invoke(Method.java:565)
	at org.jboss.arquillian.junit.Arquillian$StatementLifecycleExecutor.invoke(Arquillian.java:372)
...

Report flaky test

org.keycloak.testsuite.oauth.ClientAuthSignedJWTTest#testInvalidClientAssertionType

Keycloak CI - FIPS IT (non-strict)

jakarta.ws.rs.WebApplicationException: Create method returned status Conflict (Code: 409); expected status: Created (201). Response body: {"errorMessage":"Client client3 already exists"}
	at org.keycloak.testsuite.admin.ApiUtil.getCreatedId(ApiUtil.java:38)
	at org.keycloak.testsuite.oauth.AbstractClientAuthSignedJWTTest.recreateApp3(AbstractClientAuthSignedJWTTest.java:236)
	at java.base/java.lang.reflect.Method.invoke(Method.java:565)
	at org.jboss.arquillian.junit.Arquillian$StatementLifecycleExecutor.invoke(Arquillian.java:372)
...

Report flaky test

org.keycloak.testsuite.oauth.ClientAuthSignedJWTTest#testCodeToTokenRequestFailureRS256

Keycloak CI - FIPS IT (non-strict)

jakarta.ws.rs.WebApplicationException: Create method returned status Conflict (Code: 409); expected status: Created (201). Response body: {"errorMessage":"Client client3 already exists"}
	at org.keycloak.testsuite.admin.ApiUtil.getCreatedId(ApiUtil.java:38)
	at org.keycloak.testsuite.oauth.AbstractClientAuthSignedJWTTest.recreateApp3(AbstractClientAuthSignedJWTTest.java:236)
	at java.base/java.lang.reflect.Method.invoke(Method.java:565)
	at org.jboss.arquillian.junit.Arquillian$StatementLifecycleExecutor.invoke(Arquillian.java:372)
...

Report flaky test

org.keycloak.testsuite.oauth.ClientAuthSignedJWTTest#testDirectGrantRequestSuccess

Keycloak CI - FIPS IT (non-strict)

jakarta.ws.rs.WebApplicationException: Create method returned status Conflict (Code: 409); expected status: Created (201). Response body: {"errorMessage":"Client client3 already exists"}
	at org.keycloak.testsuite.admin.ApiUtil.getCreatedId(ApiUtil.java:38)
	at org.keycloak.testsuite.oauth.AbstractClientAuthSignedJWTTest.recreateApp3(AbstractClientAuthSignedJWTTest.java:236)
	at java.base/java.lang.reflect.Method.invoke(Method.java:565)
	at org.jboss.arquillian.junit.Arquillian$StatementLifecycleExecutor.invoke(Arquillian.java:372)
...

Report flaky test

org.keycloak.testsuite.oauth.ClientAuthSignedJWTTest#testUploadKeystorePKCS12

Keycloak CI - FIPS IT (non-strict)

jakarta.ws.rs.WebApplicationException: Create method returned status Conflict (Code: 409); expected status: Created (201). Response body: {"errorMessage":"Client client3 already exists"}
	at org.keycloak.testsuite.admin.ApiUtil.getCreatedId(ApiUtil.java:38)
	at org.keycloak.testsuite.oauth.AbstractClientAuthSignedJWTTest.recreateApp3(AbstractClientAuthSignedJWTTest.java:236)
	at java.base/java.lang.reflect.Method.invoke(Method.java:565)
	at org.jboss.arquillian.junit.Arquillian$StatementLifecycleExecutor.invoke(Arquillian.java:372)
...

Report flaky test

org.keycloak.testsuite.oauth.ClientAuthSignedJWTTest#testMissingIssuedAtClaim

Keycloak CI - FIPS IT (non-strict)

jakarta.ws.rs.WebApplicationException: Create method returned status Conflict (Code: 409); expected status: Created (201). Response body: {"errorMessage":"Client client3 already exists"}
	at org.keycloak.testsuite.admin.ApiUtil.getCreatedId(ApiUtil.java:38)
	at org.keycloak.testsuite.oauth.AbstractClientAuthSignedJWTTest.recreateApp3(AbstractClientAuthSignedJWTTest.java:236)
	at java.base/java.lang.reflect.Method.invoke(Method.java:565)
	at org.jboss.arquillian.junit.Arquillian$StatementLifecycleExecutor.invoke(Arquillian.java:372)
...

Report flaky test

org.keycloak.testsuite.oauth.ClientAuthSignedJWTTest#testSuccessWhenNoAlgSetInJWK

Keycloak CI - FIPS IT (non-strict)

jakarta.ws.rs.WebApplicationException: Create method returned status Conflict (Code: 409); expected status: Created (201). Response body: {"errorMessage":"Client client3 already exists"}
	at org.keycloak.testsuite.admin.ApiUtil.getCreatedId(ApiUtil.java:38)
	at org.keycloak.testsuite.oauth.AbstractClientAuthSignedJWTTest.recreateApp3(AbstractClientAuthSignedJWTTest.java:236)
	at java.base/java.lang.reflect.Method.invoke(Method.java:565)
	at org.jboss.arquillian.junit.Arquillian$StatementLifecycleExecutor.invoke(Arquillian.java:372)
...

Report flaky test

org.keycloak.testsuite.oauth.ClientAuthSignedJWTTest#testMissingClientAssertionType

Keycloak CI - FIPS IT (non-strict)

jakarta.ws.rs.WebApplicationException: Create method returned status Conflict (Code: 409); expected status: Created (201). Response body: {"errorMessage":"Client client3 already exists"}
	at org.keycloak.testsuite.admin.ApiUtil.getCreatedId(ApiUtil.java:38)
	at org.keycloak.testsuite.oauth.AbstractClientAuthSignedJWTTest.recreateApp3(AbstractClientAuthSignedJWTTest.java:236)
	at java.base/java.lang.reflect.Method.invoke(Method.java:565)
	at org.jboss.arquillian.junit.Arquillian$StatementLifecycleExecutor.invoke(Arquillian.java:372)
...

Report flaky test

org.keycloak.testsuite.oauth.ClientAuthSignedJWTTest#testMultipleAudiencesRejected

Keycloak CI - FIPS IT (non-strict)

jakarta.ws.rs.WebApplicationException: Create method returned status Conflict (Code: 409); expected status: Created (201). Response body: {"errorMessage":"Client client3 already exists"}
	at org.keycloak.testsuite.admin.ApiUtil.getCreatedId(ApiUtil.java:38)
	at org.keycloak.testsuite.oauth.AbstractClientAuthSignedJWTTest.recreateApp3(AbstractClientAuthSignedJWTTest.java:236)
	at java.base/java.lang.reflect.Method.invoke(Method.java:565)
	at org.jboss.arquillian.junit.Arquillian$StatementLifecycleExecutor.invoke(Arquillian.java:372)
...

Report flaky test

@gaoyikeshuer
gaoyikeshuer requested a balanced review from Copilot October 15, 2026 13:05
Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@gaoyikeshuer
gaoyikeshuer force-pushed the reset-email-background-fallback branch from 42cf2d8 to e11f58a Compare October 15, 2026 13:19
@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.

Thankd @gaoyikeshuer! I think we have no other way of doing this. But I don't like the idea os checking the threads and run normally if they are busy. I prefer to use normal ThreadPoolExecutor with a limited blocking queue (exception if queue is full). Just maintain unbounded for current types and set a default for the email (use a map or similar for the defaults, admins will be able to configure now min, max and queue size for the different types.

WDYT?

@gaoyikeshuer
gaoyikeshuer force-pushed the reset-email-background-fallback branch 5 times, most recently from 5e897ba to 9636e28 Compare November 9, 2026 14:22
@gaoyikeshuer
gaoyikeshuer marked this pull request as ready for review November 9, 2026 14:22
@gaoyikeshuer
gaoyikeshuer requested review from a team as code owners November 9, 2026 14:22
Copilot AI balanced review requested due to automatic review settings November 9, 2026 14:22
Copilot AI 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.

🟡 Changes recommended

Queued emails can send invalid links to changed addresses, documented executor settings are ignored, and the stalled-SMTP regression test never runs because it lacks @Test.

3 open issues

Review effort: Balanced

@gaoyikeshuer
gaoyikeshuer force-pushed the reset-email-background-fallback branch from 9636e28 to d99fc80 Compare November 9, 2026 15:38
Copilot AI balanced review requested due to automatic review settings November 9, 2026 15:38
Copilot AI 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.

🟡 Changes recommended

Email executor shutdown neither cancels queued deliveries nor awaits in-flight work, allowing SMTP tasks to use providers after they have been closed.

1 open issue
3 resolved since last review

Review effort: Balanced

Copilot AI balanced review requested due to automatic review settings November 10, 2026 09:52
@gaoyikeshuer
gaoyikeshuer force-pushed the reset-email-background-fallback branch from d99fc80 to a019183 Compare November 10, 2026 09:52
Copilot AI 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.

🔵 Needs a closer look

The security-sensitive reset-email changes require maintainer review of delivery policy and validation of response timing and delivery under delayed SMTP, concurrent resets, and queue saturation.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

@gaoyikeshuer
gaoyikeshuer force-pushed the reset-email-background-fallback branch from a019183 to 5ae89da Compare November 10, 2026 12:48
Copilot AI balanced review requested due to automatic review settings November 10, 2026 12:48
Copilot AI 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.

🔵 Needs a closer look

The timing mitigation needs runtime validation beyond recording-executor unit tests to assess whether usernames remain distinguishable under realistic SMTP/database latency and executor saturation.

No open issues 🎉

Review effort: Balanced

@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 @gaoyikeshuer! Some final nit-picks, mainly for the test part. See if they make sense to you.

@gaoyikeshuer
gaoyikeshuer force-pushed the reset-email-background-fallback branch from 5ae89da to 03ad327 Compare November 11, 2026 14:12
Copilot AI balanced review requested due to automatic review settings November 11, 2026 14:12
Copilot AI 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.

🔵 Needs a closer look

Concurrent password-reset email delivery and transaction coordination require maintainer review and runtime validation under delayed SMTP and executor saturation to verify enumeration resistance, authentication availability, and commit/rollback behavior.

No open issues 🎉

Review effort: Balanced

Closes keycloak#26625

Signed-off-by: Yike Gao <yikegao8@gmail.com>
@gaoyikeshuer
gaoyikeshuer force-pushed the reset-email-background-fallback branch from 03ad327 to 9d0bf33 Compare November 11, 2026 14:58
@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 for the changes @gaoyikeshuer!

@rmartinc
rmartinc merged commit 46a401d into keycloak:main Nov 11, 2026
107 of 116 checks passed
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.

Manual user enumeration via password reset endpoint

3 participants

0