feat(recaptcha): support reCAPTCHA v3 in adaptive forms (+ XFA no-iframe embed fixes) - #1986
Conversation
Adds handling for the v3 site-key type across the recaptcha component:
- Client-side widget rendering and edit-dialog logic treat v3 the same
as an invisible/score-based key, since v3 has no visible challenge.
- customFunctions.js fetches the v3 token locally (classic, non-enterprise
grecaptcha namespace) since af-core's upstream fetchCaptchaToken only
handles turnstile and Enterprise score-based keys.
- submitForm() is overridden to auto-fetch the captcha token before
submit for v3 as well, matching the existing turnstile/Enterprise
behavior.
- For XFA-rendered forms, the token is applied via
globals.functions.dispatchEvent(field, 'custom:setProperty', {value})
instead of a direct field.value assignment: @aemforms/af-core-xfa's
rule-node proxy only implements a get trap (unlike af-core's, which
also implements set), so a direct assignment invokes the real value
setter with `this` bound to the proxy and crashes on internal
`this.parent` access. Dispatching the update looks the field up by id
on the raw form and applies it directly, avoiding the proxy entirely.
…load Auto-fetch captcha (invisible reCAPTCHA v2, enterprise-score, and v3) failed on XFA-backed adaptive forms embedded without an iframe: clicking Submit logged "fetchCaptchaToken is not defined" and no token was fetched. The frontend ships two runtime bundles - non-XFA (@aemforms/af-core) and XFA (@aemforms/af-core-xfa) - each with its own FunctionRuntime. customFunctions were only registered via setupFormContainer against the non-XFA runtime (window.FormView), while the XFA form's rule engine runs in af-core-xfa and never received fetchCaptchaToken (af-core-xfa ships no default for it), so its built-in submitForm errored. Register customFunctions at bundle load in the shared entry so each bundle populates its own FunctionRuntime - in main-xfa.js this happens before the XFA form instance (and its function-table snapshot) is created. registerFunctions is idempotent, so the later setupFormContainer registration is unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ts under XFA The container view's submitSuccess/submitError/saveSuccess/saveError handlers read action.target.getState().events, but the XFA runtime's getState() omits `events`, so the handlers threw "Cannot read properties of undefined" on submit/save. This was previously masked by an earlier captcha error and surfaced once captcha submit succeeds on no-iframe XFA embeds. Guard the access with optional chaining; a missing events object means no rule is configured, so the default handler runs (thank-you / error UX). No-op for the non-XFA runtime where events is always present. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ode + tests Address review feedback on reCAPTCHA v3 support: - RecaptchaImpl.getCaptchaDisplayMode() returns "invisible" for v3 so af-core's built-in submitForm auto-fetches the token natively (both af-core and af-core-xfa runtimes), removing the need to override submitForm. - Remove the customFunctions.js submitForm override and its helpers (isArrayValue/valueOf/toStringOrEmpty), which only existed to support it. - Add Cypress authoring + runtime specs for v3 and the v3 IT fixtures (cloud config + sample page). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…unctions Collapse the local fetchCaptchaToken wrapper to a plain re-export of cf.fetchCaptchaToken now that v3 support lives upstream in @aemforms/af-custom-functions, removing the duplicated v3 grecaptcha.execute logic. Requires an @aemforms/af-custom-functions release that includes v3 support plus a matching dependency bump; until that lands, v3 relies on a local symlink to the updated package. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add "submission should pass for reCAPTCHA v3" runtime test plus the updateRecaptchaV3Config helper (secret injected via the RECAPTCHA_V3_API_KEY env var; low threshold for headless reliability). - Set the real v3 site key in the v3 cloud config fixture (site keys are public; the secret stays out of git). - Gitignore ui.tests/test-module/cypress.env.json so the local secret is never committed. Verified end-to-end locally: v3 form submit -> POST /adobe/forms/af/submit -> 200. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Accessibility Violations Found
|
…container
On the addon build CI runs, the Threshold Score field is nested inside the
enterprise-fields container which is display:none for a v3 config, so Cypress
refused to clear/type into it ("element is not visible"), failing the v3
submission test. Use {force: true} — a hidden input is still submitted on save —
so the threshold is applied and the v3 submit flow runs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Accessibility Violations Found
|
3 similar comments
Accessibility Violations Found
|
Accessibility Violations Found
|
Accessibility Violations Found
|
Accessibility Violations Found
|
Accessibility Violations Found
|
…test
The v3 threshold field is a coral-numberinput nested in the hidden
enterprise-fields container, so the wrapper is never upgraded to a
clearable element and cy.clear() fails even with {force:true}. Target
the inner native input, which is always clearable regardless of the
wrapper's visibility/upgrade state, and syncs to the field on save.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…HA v3 1.0.18 (published to npm) contains the v3 token-fetch fix that routes v3 site keys through the classic grecaptcha namespace. customFunctions.js already delegates to cf.fetchCaptchaToken, so this makes the branch CI-safe now that the version is available in the registry. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Accessibility Violations Found
|
1 similar comment
Accessibility Violations Found
|
Switch the v3 site/secret key to Google's published test pair, which always returns a constant score of 0.9 server-side regardless of domain. This makes the submission test deterministic and removes the retry-until-success loop and the RECAPTCHA_V3_API_KEY dependency that were needed to work around real scoring variability in headless CI browsers. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Accessibility Violations Found
|
Accessibility Violations Found
|
Accessibility Violations Found
|
| // the XFA rule engine would otherwise see only its built-in defaults and have no fetchCaptchaToken | ||
| // (breaking auto-fetch captcha - invisible/enterprise-score/v3 - on no-iframe XFA embeds). | ||
| // registerFunctions is idempotent, so the later setupFormContainer registration is unaffected. | ||
| FunctionRuntime.registerFunctions(customFunctions); |
There was a problem hiding this comment.
This is not required, this already happens today, why did you add this ?
There was a problem hiding this comment.
On a no-iframe XFA form the rule engine runs on af-core-xfa's FunctionRuntime, which ships no default fetchCaptchaToken, so auto-fetch captcha (invisible v2 / enterprise-score / v3) failed at submit with fetchCaptchaToken is not defined.
I tested the no iframe case for recaptcha v3 and enterprise without this change. Both were failing with no submit action. This line was actually needed.
There was a problem hiding this comment.
This already happens here, it is redundant check,
This should not happen at multiple places, check the existing occurence and add it correctly
There was a problem hiding this comment.
You're right that registration shouldn't be scattered — but these two calls target different FunctionRuntime instances, so the index.js one isn't redundant. The -xfa build aliases @aemforms/af-core → @aemforms/af-core-xfa (webpack.common.cjs), so main.js and main-xfa.js each carry their own runtime. utils.js:312 runs inside setupFormContainer, invoked only via the global window.FormView (formcontainerview.js) — the non-XFA bundle — so it only ever populates af-core's runtime. The XFA rule engine runs on af-core-xfa, which never received customFunctions, so no-iframe XFA captcha submit failed with fetchCaptchaToken is not defined (verified: removing the index.js line reproduces it).
The clean consolidation would be to make index.js (the shared per-bundle entry) the single registration point and drop utils.js:312 — but that line predates this PR and sits in the universal form-bootstrap path (FormView.Utils.setupFormContainer is public API), so removing it inside a reCAPTCHA fix is a wider, cross-cutting change than I'd want to bundle here without full regression testing. I'd prefer to keep this PR minimal (index.js only) and do the consolidation as a follow-up — happy to raise a ticket.
I feel claude is right here and removing any of them effects either the captcha or have cross repo implications(public API).
There was a problem hiding this comment.
Got the issue, this is specific to XFA. Can you move the registration here and remove it from utils.js
Google does not publish an official test key pair for v3 score-based verification (the public 6LeIx... pair is documented for v2 checkbox only and does not reliably render the v3 invisible badge). Restore the real site key in the v3 cloud config fixture and the RECAPTCHA_V3_API_KEY-based secret in the Cypress test, and drop the threshold to 0 so a real headless- browser score still passes verification. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Accessibility Violations Found
|
1 similar comment
Accessibility Violations Found
|
| }); | ||
| }) | ||
|
|
||
| it("submission should pass for reCAPTCHA v3", () => { |
There was a problem hiding this comment.
can we add a test case for reacaptcha v3failure too ?
There was a problem hiding this comment.
Done- added a v3 failure test case with threshold score 1. Same as enterprise.
There was a problem hiding this comment.
The newly added test case for failure is failing on the PR, please check it
Mirror the enterprise-score failure test for v3: set the threshold to 1.0 (above any real headless-browser score) so server-side verification always rejects the token, deterministically asserting the 400 + CAPTCHA-validation error. Gated on isLatestAddon like the other failure tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Accessibility Violations Found
|
1 similar comment
Accessibility Violations Found
|
|
https://github.com/adobe/aem-core-forms-components/tree/master/ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/recaptcha/v1/recaptcha This readme needs to be updated to tell about v3 support |
Reverts the index.js bundle-load registerFunctions call added for reCAPTCHA v3. The `fetchCaptchaToken is not defined` failure it worked around was traced to the XFA runtime clientlib (core.forms.components.runtime.all.xfa) being loaded on pages that have no XFA form, which creates a second FunctionRuntime singleton that setupFormContainer never populates. The proper fix is to stop importing the XFA clientlib when no XFA form is present, which makes this bundle-load registration unnecessary. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Accessibility Violations Found
|
1 similar comment
Accessibility Violations Found
|
The reCAPTCHA v3 test cases are gated behind FT_FORMS-27648. Add it to the IT DynamicToggleProviderImpl enabledToggles so the toggle is on when the Cypress v3 submission tests (success and failure) run on CI. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
reCAPTCHA v3 is gated behind FT_FORMS-27648, so the v3 test cases (render, submission pass, submission fail) should only run when that toggle is on and the latest addon is in use. Fetch the enabled toggles in before() and guard each v3 test with cy.af.isLatestAddon() && toggle_array.includes(FT_RECAPTCHA_V3), matching the existing hCaptcha pattern. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Accessibility Violations Found
|
1 similar comment
Accessibility Violations Found
|
updated |
Add reCAPTCHA v3 to the supported-versions list (invisible/badge-only, token auto-fetched at submit, gated behind FT_FORMS-27648) and note that recaptchaSize does not apply to v3. Addresses PR review feedback. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Accessibility Violations Found
|
1 similar comment
Accessibility Violations Found
|
Accessibility Violations Found
|
Accessibility Violations Found
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Summary
Adds reCAPTCHA v3 support to the adaptive forms recaptcha component, targeting the
devbranch. Same change set as #1977 (which targetsmaster) — cherry-picked ontodev, net diff is identical.RecaptchaImpl.getCaptchaDisplayMode()returnsinvisiblefor v3, so af-core's built-insubmitFormauto-fetches the token natively (both af-core and af-core-xfa runtimes) — nosubmitFormoverride.fetchCaptchaTokenis a plain re-export ofcf.fetchCaptchaToken; v3 support lands upstream in@aemforms/af-custom-functions(covers headless/EDS). Requires that release + a dependency bump before it is CI-green.state.events.Testing
getCaptchaDisplayMode+ updated exporter fixture.submission should pass for reCAPTCHA v3test).POST /adobe/forms/af/submit/... → 200.🤖 Generated with Claude Code