Skip to content

Restrict marking email as verified to explicit email verification flow#50946

Open
gaoyikeshuer wants to merge 1 commit into
keycloak:mainfrom
gaoyikeshuer:restrict-email-verify-50622
Open

Restrict marking email as verified to explicit email verification flow#50946
gaoyikeshuer wants to merge 1 commit into
keycloak:mainfrom
gaoyikeshuer:restrict-email-verify-50622

Conversation

@gaoyikeshuer

@gaoyikeshuer gaoyikeshuer commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

The weakness is the emailVerified couldd transition to true as a side effect of flows which purpose is not email verification. With this PR, the email of a user is marked as verified only by an explicit email verification

after the fix, the toggle will only be turned on after user finished the verification step:

Screen.Recording.2026-07-17.at.14.43.00.mov

master branch:

Screen.Recording.2026-07-20.at.13.08.08.mov

the difference between master branch and fix is: on master branch, the user just click confirm linking to the current exist account, but the verified Email toggle is on as a side effect, which is an vulnerability. the fix has an extra step, once you land on a Verify email page, a second email arrives, email verified stays OFF until user click the second link. also the page now says "Account linking confirmed" instead of the false "verified" claim.

Closes #50622

Copilot AI review requested due to automatic review settings July 16, 2026 12:00
@gaoyikeshuer
gaoyikeshuer requested review from a team as code owners July 16, 2026 12:00

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.

Pull request overview

Restricts implicit email verification during reset, account linking, and execute-actions flows.

Changes:

  • Makes reset-flow verification opt-in.
  • Separates broker-link confirmation from explicit email verification.
  • Adds coverage for reset and execute-actions behavior.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
ResetPasswordTest.java Tests reset verification behavior.
AbstractFirstBrokerLoginTest.java Tests broker-link verification flows.
UserEmailTest.java Tests execute-actions email verification.
ResetCredentialEmail.java Adds configurable reset verification.
IdpEmailVerificationAuthenticator.java Separates broker and verification email notes.
IdpVerifyAccountLinkActionTokenHandler.java Stops link confirmation from verifying email.
ExecuteActionsActionTokenHandler.java Verifies only when explicitly requested.

Copilot AI review requested due to automatic review settings July 16, 2026 12:30
@gaoyikeshuer
gaoyikeshuer force-pushed the restrict-email-verify-50622 branch from 2848be0 to ed7dff6 Compare July 16, 2026 12:30

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.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.

@gaoyikeshuer gaoyikeshuer changed the title Fix verify email at multiple places. Restrict marking email as verified to explicit email verification flow Jul 16, 2026
Copilot AI review requested due to automatic review settings July 16, 2026 13:44
@gaoyikeshuer
gaoyikeshuer force-pushed the restrict-email-verify-50622 branch from ed7dff6 to 4480dc6 Compare July 16, 2026 13:44

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.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

@gaoyikeshuer
gaoyikeshuer force-pushed the restrict-email-verify-50622 branch from 4480dc6 to 3a812a5 Compare July 16, 2026 14:15
Copilot AI review requested due to automatic review settings July 17, 2026 08:51
@gaoyikeshuer
gaoyikeshuer force-pushed the restrict-email-verify-50622 branch from 3a812a5 to 072d8c6 Compare July 17, 2026 08:51

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.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Comment thread docs/documentation/upgrading/topics/changes/changes-26_8_0.adoc Outdated
Comment thread docs/documentation/upgrading/topics/changes/changes-26_8_0.adoc Outdated
Copilot AI review requested due to automatic review settings July 17, 2026 09:05
@gaoyikeshuer
gaoyikeshuer force-pushed the restrict-email-verify-50622 branch from 072d8c6 to 9e5ba4c Compare July 17, 2026 09: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.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated no new comments.

Copilot AI review requested due to automatic review settings July 17, 2026 09:12
@gaoyikeshuer
gaoyikeshuer force-pushed the restrict-email-verify-50622 branch from 9e5ba4c to 5816484 Compare July 17, 2026 09: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.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.


Previously, some flows marked the email of a user as verified even though their purpose was not email verification.
This could make an account registered by a different party than the owner of the email address look trusted.
An email is now marked as verified only when the user completes an email verification.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@gaoyikeshuer Seems worth to check

@gaoyikeshuer gaoyikeshuer Jul 17, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks @mabartos ! The issue is about the flows toggled the emailVerified by accident as a side effect nobody asked for them. but these two spots are the opposite, that they only run when an admin turned on Trust Email, which is off by default. I will add a line to the upgrading note to not overpromise causing misunderstanding.

Copilot AI review requested due to automatic review settings July 17, 2026 10:15

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.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Comment on lines +30 to +31
This changes the behavior of identity provider account linking by email, of admin-initiated action emails that do not include the *Verify Email* action,
and of the verifiable credential offer emails of the experimental OID4VCI feature.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Seems valid

Copilot AI review requested due to automatic review settings July 17, 2026 12:07
@gaoyikeshuer
gaoyikeshuer force-pushed the restrict-email-verify-50622 branch from 2986dd8 to 7df3183 Compare July 17, 2026 12:07
@gaoyikeshuer
gaoyikeshuer requested a review from a team as a code owner July 17, 2026 12:07

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.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.


driver2.navigate().to(url);
waitForPage(driver, "your email address has been verified already.", false);
waitForPage(driver, "has been confirmed already.", false);
@gaoyikeshuer

gaoyikeshuer commented Jul 17, 2026

Copy link
Copy Markdown
Contributor Author
image

Update the confim link page, previously the message was "You successfully verified your email", which could be misleading

@mabartos

Copy link
Copy Markdown
Member

@gaoyikeshuer Based on your screenshot, it seems that the title is the same as the description. I'd say it's a bug - the title should be brief and the details described in the sub-test(description). But it might be a follow-up issue.

Copilot AI review requested due to automatic review settings July 17, 2026 12:33
@gaoyikeshuer
gaoyikeshuer force-pushed the restrict-email-verify-50622 branch from 7df3183 to ea3f706 Compare July 17, 2026 12:33
@gaoyikeshuer

Copy link
Copy Markdown
Contributor Author

@gaoyikeshuer Based on your screenshot, it seems that the title is the same as the description. I'd say it's a bug - the title should be brief and the details described in the sub-test(description). But it might be a follow-up issue.

Thanks! I've fixed the description and title part

@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.adapter.servlet.SAMLServletAdapterTest#employeeSigPostNoIdpKeyTestNoKeyNameInKeyInfo

Keycloak CI - Adapter IT Strict Cookies

org.opentest4j.AssertionFailedError: expected: <true> but was: <false>
	at org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:151)
	at org.junit.jupiter.api.AssertionFailureBuilder.buildAndThrow(AssertionFailureBuilder.java:132)
	at org.junit.jupiter.api.AssertTrue.failNotTrue(AssertTrue.java:63)
	at org.junit.jupiter.api.AssertTrue.assertTrue(AssertTrue.java:36)
...

Report flaky test

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.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.

Comment thread docs/documentation/upgrading/topics/changes/changes-26_8_0.adoc
Copilot AI review requested due to automatic review settings July 17, 2026 13:39
@gaoyikeshuer
gaoyikeshuer force-pushed the restrict-email-verify-50622 branch from ea3f706 to 17bf1a6 Compare July 17, 2026 13:39

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.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.

Closes keycloak#50622

Signed-off-by: Yike Gao <yikegao8@gmail.com>
Copilot AI review requested due to automatic review settings July 20, 2026 12:27
@gaoyikeshuer
gaoyikeshuer force-pushed the restrict-email-verify-50622 branch from 17bf1a6 to e9e30fd Compare July 20, 2026 12:27

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.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (2)

testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractFirstBrokerLoginTest.java:1202

  • This assertion still waits on driver, which is already showing the expected page from the preceding navigation, so the driver2 replay is never validated. Wait on driver2 to cover the second-browser behavior.
        waitForPage(driver, "account linking already confirmed", false);

themes/src/main/resources/theme/base/login/messages/messages_en.properties:377

  • This existing key is translated in community bundles, and many translations still claim the email was verified (for example, messages_de.properties:309). Consequently the false verification claim remains for non-English users; use a new key with English fallback or update the translations.
identityProviderLinkSuccess=You successfully confirmed linking your account with {0} account {1}. Please go back to your original browser and continue there with the login.

Comment on lines +182 to +184
emailLinkIdp2=Haven''t received the email?
emailLinkIdp3=to re-send the email.
emailLinkIdp4=If you already verified the email in a different browser
emailLinkIdp4=If you already confirmed linking the account in a different browser
@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.ClientAuthPostMethodTest#testPostAuthenticationNotAllowedWhenBasicRequested

Keycloak CI - Base IT (6)

java.lang.NullPointerException: Cannot read field "features" because the return value of "org.keycloak.common.Profile.getInstance()" is null
	at org.keycloak.common.Profile.isFeatureEnabled(Profile.java:534)
	at org.keycloak.protocol.oidc.OIDCClientSecretConfigWrapper.<init>(OIDCClientSecretConfigWrapper.java:41)
	at org.keycloak.protocol.oidc.OIDCClientSecretConfigWrapper.fromClientRepresentation(OIDCClientSecretConfigWrapper.java:49)
	at org.keycloak.testsuite.oauth.ClientAuthPostMethodTest.testPostAuthenticationNotAllowedWhenBasicRequested(ClientAuthPostMethodTest.java:141)
...

Report flaky test

org.keycloak.testsuite.oauth.ClientAuthPostMethodTest#testBasicAuthenticationNotAllowedWhenPostRequested

Keycloak CI - Base IT (6)

java.lang.NullPointerException: Cannot read field "features" because the return value of "org.keycloak.common.Profile.getInstance()" is null
	at org.keycloak.common.Profile.isFeatureEnabled(Profile.java:534)
	at org.keycloak.protocol.oidc.OIDCClientSecretConfigWrapper.<init>(OIDCClientSecretConfigWrapper.java:41)
	at org.keycloak.protocol.oidc.OIDCClientSecretConfigWrapper.fromClientRepresentation(OIDCClientSecretConfigWrapper.java:49)
	at org.keycloak.testsuite.oauth.ClientAuthPostMethodTest.testBasicAuthenticationNotAllowedWhenPostRequested(ClientAuthPostMethodTest.java:112)
...

Report flaky test

org.keycloak.testsuite.forms.BrowserFlowTest#testUserWithOneAdditionalFactorOtpSuccess

Keycloak CI - Base IT (5)

org.opentest4j.AssertionFailedError: expected: <true> but was: <false>
	at org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:151)
	at org.junit.jupiter.api.AssertionFailureBuilder.buildAndThrow(AssertionFailureBuilder.java:132)
	at org.junit.jupiter.api.AssertTrue.failNotTrue(AssertTrue.java:63)
	at org.junit.jupiter.api.AssertTrue.assertTrue(AssertTrue.java:36)
...

Report flaky test

org.keycloak.testsuite.forms.MultipleTabsLoginTest#testEmptyBaseUrl

Keycloak CI - Forms IT (firefox)

org.opentest4j.AssertionFailedError: expected: <true> but was: <false>
	at org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:151)
	at org.junit.jupiter.api.AssertionFailureBuilder.buildAndThrow(AssertionFailureBuilder.java:132)
	at org.junit.jupiter.api.AssertTrue.failNotTrue(AssertTrue.java:63)
	at org.junit.jupiter.api.AssertTrue.assertTrue(AssertTrue.java:36)
...

Report flaky test

org.keycloak.testsuite.forms.MultipleTabsLoginTest#multipleTabsParallelLoginTest

Keycloak CI - Forms IT (firefox)

org.opentest4j.AssertionFailedError: expected: <true> but was: <false>
	at org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:151)
	at org.junit.jupiter.api.AssertionFailureBuilder.buildAndThrow(AssertionFailureBuilder.java:132)
	at org.junit.jupiter.api.AssertTrue.failNotTrue(AssertTrue.java:63)
	at org.junit.jupiter.api.AssertTrue.assertTrue(AssertTrue.java:36)
...
org.opentest4j.AssertionFailedError: expected: <true> but was: <false>
	at org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:151)
	at org.junit.jupiter.api.AssertionFailureBuilder.buildAndThrow(AssertionFailureBuilder.java:132)
	at org.junit.jupiter.api.AssertTrue.failNotTrue(AssertTrue.java:63)
	at org.junit.jupiter.api.AssertTrue.assertTrue(AssertTrue.java:36)
...

Report flaky test

@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

Comment on lines +30 to +31
This changes the behavior of identity provider account linking by email, of admin-initiated action emails that do not include the *Verify Email* action,
and of the verifiable credential offer emails of the experimental OID4VCI feature.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Seems valid

UserModel user = tokenContext.getAuthenticationSession().getAuthenticatedUser();
// verify user email as we know it is valid as this entry point would never have gotten here.
user.setEmailVerified(true);
if (token.getRequiredActions().contains(UserModel.RequiredAction.VERIFY_EMAIL.name())) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Even when this is in the list of required actions, it does not say it is completed, right? Shouldn't be really the place where it's set only after the email verification?

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.

sub-issue 2: Restrict implicit email verification to the explicit verify-email flow

3 participants