THREESCALE-14842: Add recaptcha to reset password screen - #4359
Conversation
The admin portal Forgot Password page lacked bot protection while the Sign In page already had reCAPTCHA v3. This creates an inconsistency and exposes the password reset endpoint to automated attacks. Add reCAPTCHA v3 protection using the existing admin_bot_protection_level setting (same as the login page). The controller includes BotProtection::Controller, overrides bot_protection_level to read the admin setting, and checks the token in the destroy action. The view passes recaptcha config to the React component, which conditionally renders ReCaptchaV3. Assisted-by: Claude Code
Verify that the reCAPTCHA check in the destroy action correctly blocks requests when verification fails (renders the reset page with an error message) and allows them through when it passes or when bot protection is disabled. Assisted-by: Claude Code
Add end-to-end scenarios covering the reCAPTCHA protection on the admin Forgot Password page: captcha presence when enabled/disabled, bot rejection, and successful submission. Also update the bot protection step definition to stub at the Recaptcha module level (skip_env?, invalid_response?, verify_via_api_call) instead of stubbing ApplicationController#verify_recaptcha. This lets the full controller flow run and produces the gem's real flash message. Assisted-by: Claude Code
Add tests to RequestPasswordForm verifying that the ReCaptchaV3 input is present when enabled and absent when disabled. Also added similar lacking tests to LoginForm. Update defaultProps in RequestPasswordForm and RequestPasswordPage specs to include the new required recaptcha prop, and regenerate the RequestPasswordForm snapshot to reflect it. Assisted-by: Claude Code
ESLint react/prefer-read-only-props requires props that are never mutated to be declared as readonly. The recaptcha prop in RequestPasswordForm and RequestPasswordPage fits this rule. Assisted-by: Claude Code
| ApplicationController.any_instance.stubs(:verify_recaptcha).returns(!bot) | ||
| Recaptcha.stubs(:skip_env?).returns(false) | ||
| Recaptcha.stubs(:invalid_response?).returns(false) | ||
| Recaptcha.stubs(:verify_via_api_call).returns([!bot, {}]) |
There was a problem hiding this comment.
The step definition stubs changed from stubbing the public API (ApplicationController.any_instance.stubs(:verify_recaptcha)) to stubbing gem internals (Recaptcha.stubs(:skip_env?), Recaptcha.stubs(:invalid_response?), Recaptcha.stubs(:verify_via_api_call)). This couples the tests to the recaptcha gem's internal implementation — a gem version bump could silently break these stubs without any app code change.
Was there a specific reason the previous approach didn't work here? If so, it might be worth adding a comment explaining why, so future maintainers don't revert it.
akostadinov
left a comment
There was a problem hiding this comment.
looks good, if you want to take into concern akostadinov-bot's comment or not, that's fine with me
Gem internals are stubbed instead of our :verify_recaptcha method so that the flash message is set by the gem itself, matching production behavior. Add a comment explaining this non-obvious choice.
What this PR does / why we need it:
This adds the recaptcha challenge to the "Forgot password" screen and creates the corresponding tests.
Additionally, some recaptcha tests has been added for the
LoginFormcomponent. Not directly related to the PR but the component was lacking such tests and it was straightforward to add them.Which issue(s) this PR fixes
https://redhat.atlassian.net/browse/THREESCALE-14842
Verification steps
Enable recaptcha on Account Settings -> Integrate -> Security
Go to the reset password screen
Recaptcha notice "This site is protected by reCAPTCHA..." should appear
Intriduce an email and submit the form
RECAPTCHA_MIN_BOT_SCORE=0.99recpatcha challenge should failRECAPTCHA_MIN_BOT_SCORE=0.5recpatcha challenge should pass