Skip to content

feat(recaptcha): support reCAPTCHA v3 in adaptive forms (+ XFA no-iframe embed fixes) - #1977

Open
kartikey19427 wants to merge 3 commits into
masterfrom
feature/recaptcha-v3-support
Open

feat(recaptcha): support reCAPTCHA v3 in adaptive forms (+ XFA no-iframe embed fixes)#1977
kartikey19427 wants to merge 3 commits into
masterfrom
feature/recaptcha-v3-support

Conversation

@kartikey19427

Copy link
Copy Markdown

Summary

Adds reCAPTCHA v3 support to the adaptive forms recaptcha component, plus two fixes that make auto-fetch captchas work on XFA-backed forms embedded without an iframe.

reCAPTCHA v3 (feature)

  • Client-side widget rendering and edit-dialog logic treat v3 the same as an invisible/score-based key (v3 has no visible challenge).
  • customFunctions.js fetches the v3 token locally (classic, non-enterprise grecaptcha namespace), since 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 dispatchEvent(field, 'custom:setProperty', {value}) instead of a direct field.value = assignment, to work with af-core-xfa's get-only rule-node proxy.

XFA no-iframe embed fixes

While validating v3 on an embedded form, two pre-existing bugs surfaced that broke auto-fetch captcha (invisible v2, enterprise-score, turnstile, and v3) on XFA-backed forms embedded without an iframe:

  1. Register custom functions into the XFA runtime at bundle load (ui.frontend/src/index.js) — the runtime ships two bundles (non-XFA @aemforms/af-core and XFA @aemforms/af-core-xfa), each with its own FunctionRuntime. Custom functions were registered only into the non-XFA runtime via setupFormContainer (through the single window.FormView), so the XFA form's rule engine never received fetchCaptchaToken and its built-in submitForm failed with fetchCaptchaToken is not defined. Registering at bundle load populates each bundle's own runtime before the form model is built. registerFunctions is idempotent, so the existing registration is unaffected.
  2. Guard container-view submit/save handlers against a missing state.events (formcontainerview.js) — the XFA runtime's getState() omits events, which crashed the submit/save success/error handlers (Cannot read properties of undefined) once submit progressed. Guarded with optional chaining; a missing events object means no rule is configured, so the default handler runs. No-op for the non-XFA runtime.

Testing

Verified end-to-end on a local AEM instance with an enterprise-score reCAPTCHA on an XFA form embedded no-iframe: the XFA rule engine exposes fetchCaptchaToken, the token is fetched and set on submit, POST /adobe/forms/af/submit/... -> 200 OK, and the thank-you / success handling runs. No fetchCaptchaToken is not defined and no submitSuccess handler crash. Frontend unit tests pass.

🤖 Generated with Claude Code

Kartikey Gupta and others added 3 commits August 28, 2026 11:23
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>
@adobe-bot

Copy link
Copy Markdown

Accessibility Violations Found

Id Impact
aria-required-attr critical
empty-heading minor
label-title-only serious
target-size serious

1 similar comment
@adobe-bot

Copy link
Copy Markdown

Accessibility Violations Found

Id Impact
aria-required-attr critical
empty-heading minor
label-title-only serious
target-size serious

* the framework always appends last.
* @returns {object} - Empty object, matching the original's return value.
*/
function submitForm() {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

submitForm should not be overridden, we should use a pre-submit hook if required ? Also, can you check old captcha implementation

return a.valueOf();
}

function toStringOrEmpty(a) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

These functions are not required, make them inline or private

* @param {object} globals - An object containing read-only form instance, read-only target field instance and methods for form modifications.
* @returns {string} - The captcha token.
*/
function fetchCaptchaToken(globals) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We should put this af-core, so that even headless/EDS works

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.

3 participants