From ca06bc50ed428c5c6632a7998b2429474d6b2bd8 Mon Sep 17 00:00:00 2001 From: lk316 <51822928+lk316@users.noreply.github.com> Date: Wed, 22 Jul 2026 14:34:24 +0300 Subject: [PATCH] Updated demo apps to be defaults aware --- .../app/views/pages/demo.liquid | 60 +++++++++---- .../app/views/pages/verify.liquid | 16 ++-- .../app/views/pages/demo.liquid | 61 +++++++++---- .../app/views/pages/verify.liquid | 14 +-- .../app/views/pages/demo.liquid | 40 ++++++--- .../app/views/pages/verify.liquid | 14 ++- .../app/views/pages/demo.liquid | 60 +++++++++---- .../app/views/pages/verify.liquid | 15 ++-- .../app/views/pages/captcha-demo.liquid | 88 +++++++++++++------ .../app/views/pages/captcha-verify.liquid | 23 ++--- 10 files changed, 271 insertions(+), 120 deletions(-) diff --git a/pos-module-captchas-hcaptcha/app/views/pages/demo.liquid b/pos-module-captchas-hcaptcha/app/views/pages/demo.liquid index 680bd59..c398f04 100644 --- a/pos-module-captchas-hcaptcha/app/views/pages/demo.liquid +++ b/pos-module-captchas-hcaptcha/app/views/pages/demo.liquid @@ -1,25 +1,53 @@ {% comment %} Example-app demo for the captchas_hcaptcha provider (NOT part of the distributed module). - GET /demo — renders a form with the hCaptcha widget using hCaptcha's public always-pass - TEST keys. Submitting POSTs to /verify, which verifies the token server-side and shows the - normalized result. Swap the keys for real ones (constants) to see a live challenge. + GET /demo — demonstrates the DEFAULT-KEYS best practice: the site key is resolved from the + CAPTCHA_DEFAULT_SITE_KEY constant, so the widget is rendered with NO site_key argument (and + /verify reads the secret from CAPTCHA_DEFAULT_SECRET). This exercises the captchas + abstraction's helpers/default_site_key and helpers/default_secret. The provider is pinned to + 'hcaptcha' here; only the keys come from constants. + + hCaptcha publishes public always-pass TEST keys, so set the two constants to those and reload + (see the setup box). Swap in your real keys the same way for a live challenge. {% endcomment %} +{%- liquid + assign site_key = context.constants.CAPTCHA_DEFAULT_SITE_KEY + assign secret = context.constants.CAPTCHA_DEFAULT_SECRET + assign configured = false + if site_key != blank and secret != blank + assign configured = true + endif +-%}

Captcha demo — hCaptcha

-

- This form uses hCaptcha's public test site key, which always passes. - Submitting posts the token to /verify, which calls hCaptcha's siteverify - endpoint server-side and shows the result. -

- -
- +{% unless configured %} +

+ ⚠️ This demo resolves the site key and secret from the CAPTCHA_DEFAULT_SITE_KEY + and CAPTCHA_DEFAULT_SECRET constants — set both, then reload. hCaptcha's public + always-pass test keys work out of the box: +

+
pos-cli constants set <env> --name CAPTCHA_DEFAULT_SITE_KEY --value "10000000-ffff-ffff-ffff-000000000001"
+pos-cli constants set <env> --name CAPTCHA_DEFAULT_SECRET   --value "0x0000000000000000000000000000000000000000"

-
- + Currently set — site key: {{ site_key | default: '(unset)' }}, + secret: {% if secret != blank %}(set){% else %}(unset){% endif %}.

+{% else %} +

+ The site key comes from CAPTCHA_DEFAULT_SITE_KEY, so the widget below passes no + site_key. Submitting posts the token to /verify, which reads the + secret from CAPTCHA_DEFAULT_SECRET and binds the token to that site key via + expected_sitekey (hCaptcha secrets are account-wide). +

+ + + +

+
+ +

- {% render 'modules/captchas/widget', provider: 'hcaptcha', site_key: '10000000-ffff-ffff-ffff-000000000001' %} + {% render 'modules/captchas/widget', provider: 'hcaptcha' %} -

-
+

+ +{% endunless %} diff --git a/pos-module-captchas-hcaptcha/app/views/pages/verify.liquid b/pos-module-captchas-hcaptcha/app/views/pages/verify.liquid index d1713b9..4802697 100644 --- a/pos-module-captchas-hcaptcha/app/views/pages/verify.liquid +++ b/pos-module-captchas-hcaptcha/app/views/pages/verify.liquid @@ -4,17 +4,21 @@ method: post --- {% comment %} Example-app POST handler for the hCaptcha demo (NOT part of the distributed module). - Uses hCaptcha's public TEST secret, and binds the token to the test sitekey via - expected_sitekey — hCaptcha secrets are account-wide, so a real integration should - always pass it: + Verifies with the secret resolved from CAPTCHA_DEFAULT_SECRET — no secret argument passed — + which exercises the abstraction's helpers/default_secret. expected_sitekey is still passed + explicitly (it is a policy param, not a key): hCaptcha secrets are account-wide, so the token + must be bound to the expected site key — here the same CAPTCHA_DEFAULT_SITE_KEY the widget + used. The provider is pinned server-side; a real integration is identical: function result = 'modules/captchas/commands/captcha/verify', provider: 'hcaptcha', - secret: context.constants.CAPTCHA_HCAPTCHA_SECRET, - expected_sitekey: context.constants.CAPTCHA_HCAPTCHA_SITE_KEY + expected_sitekey: context.constants.CAPTCHA_DEFAULT_SITE_KEY + + Set CAPTCHA_DEFAULT_SECRET (hCaptcha's test secret 0x0000... works). With it unset, verify + fails closed with secret_missing and the error renders below. {% endcomment %} {% liquid - function result = 'modules/captchas/commands/captcha/verify', provider: 'hcaptcha', secret: '0x0000000000000000000000000000000000000000', expected_sitekey: '10000000-ffff-ffff-ffff-000000000001' + function result = 'modules/captchas/commands/captcha/verify', provider: 'hcaptcha', expected_sitekey: context.constants.CAPTCHA_DEFAULT_SITE_KEY %} {% if result.valid %}

✅ Captcha passed ({{ result.provider }})

diff --git a/pos-module-captchas-recaptcha/app/views/pages/demo.liquid b/pos-module-captchas-recaptcha/app/views/pages/demo.liquid index d54f899..22a8c39 100644 --- a/pos-module-captchas-recaptcha/app/views/pages/demo.liquid +++ b/pos-module-captchas-recaptcha/app/views/pages/demo.liquid @@ -1,25 +1,54 @@ {% comment %} Example-app demo for the captchas_recaptcha provider (NOT part of the distributed module). - GET /demo — renders a form with the reCAPTCHA v2 widget using Google's public always-pass - automated-testing keys (the widget shows a warning banner). Submitting POSTs to /verify, - which verifies the token server-side and shows the normalized result. + GET /demo — demonstrates the DEFAULT-KEYS best practice: the site key is resolved from the + CAPTCHA_DEFAULT_SITE_KEY constant, so the widget is rendered with NO site_key argument (and + /verify reads the secret from CAPTCHA_DEFAULT_SECRET). This exercises the captchas + abstraction's helpers/default_site_key and helpers/default_secret. The provider is pinned to + 'recaptcha' here; only the keys come from constants. + + Google publishes public always-pass automated-testing keys (the widget shows a warning + banner), so set the two constants to those and reload (see the setup box). Swap in your real + keys the same way for a live challenge. {% endcomment %} +{%- liquid + assign site_key = context.constants.CAPTCHA_DEFAULT_SITE_KEY + assign secret = context.constants.CAPTCHA_DEFAULT_SECRET + assign configured = false + if site_key != blank and secret != blank + assign configured = true + endif +-%}

Captcha demo — Google reCAPTCHA v2

-

- This form uses Google's automated-testing site key, which always passes (the widget shows - a warning banner). Submitting posts the token to /verify, which calls Google's - siteverify endpoint server-side and shows the result. -

- -
- +{% unless configured %} +

+ ⚠️ This demo resolves the site key and secret from the CAPTCHA_DEFAULT_SITE_KEY + and CAPTCHA_DEFAULT_SECRET constants — set both, then reload. Google's public + automated-testing keys work out of the box (the widget shows a warning banner): +

+
pos-cli constants set <env> --name CAPTCHA_DEFAULT_SITE_KEY --value "6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI"
+pos-cli constants set <env> --name CAPTCHA_DEFAULT_SECRET   --value "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe"

-
- + Currently set — site key: {{ site_key | default: '(unset)' }}, + secret: {% if secret != blank %}(set){% else %}(unset){% endif %}.

+{% else %} +

+ The site key comes from CAPTCHA_DEFAULT_SITE_KEY, so the widget below passes no + site_key. Submitting posts the token to /verify, which likewise + reads the secret from CAPTCHA_DEFAULT_SECRET — Google's siteverify is called + server-side and the normalized result shown. +

+ + + +

+
+ +

- {% render 'modules/captchas/widget', provider: 'recaptcha', site_key: '6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI' %} + {% render 'modules/captchas/widget', provider: 'recaptcha' %} -

-
+

+ +{% endunless %} diff --git a/pos-module-captchas-recaptcha/app/views/pages/verify.liquid b/pos-module-captchas-recaptcha/app/views/pages/verify.liquid index 9f15560..5bab788 100644 --- a/pos-module-captchas-recaptcha/app/views/pages/verify.liquid +++ b/pos-module-captchas-recaptcha/app/views/pages/verify.liquid @@ -4,15 +4,17 @@ method: post --- {% comment %} Example-app POST handler for the reCAPTCHA v2 demo (NOT part of the distributed module). - Uses Google's public automated-testing secret. A real integration is identical, with - the secret read from a constant: + Verifies with the secret resolved from CAPTCHA_DEFAULT_SECRET — no secret argument passed — + which exercises the abstraction's helpers/default_secret. The provider is pinned server-side + (never read from client input); a real integration is identical: - function result = 'modules/captchas/commands/captcha/verify', - provider: 'recaptcha', - secret: context.constants.CAPTCHA_RECAPTCHA_SECRET + function result = 'modules/captchas/commands/captcha/verify', provider: 'recaptcha' + + Set CAPTCHA_DEFAULT_SECRET (Google's automated-testing secret 6LeIxAcT...WifJWe works). With + it unset, verify fails closed with secret_missing and the error renders below. {% endcomment %} {% liquid - function result = 'modules/captchas/commands/captcha/verify', provider: 'recaptcha', secret: '6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe' + function result = 'modules/captchas/commands/captcha/verify', provider: 'recaptcha' %} {% if result.valid %}

✅ Captcha passed ({{ result.provider }})

diff --git a/pos-module-captchas-recaptcha3/app/views/pages/demo.liquid b/pos-module-captchas-recaptcha3/app/views/pages/demo.liquid index 3785a91..910faae 100644 --- a/pos-module-captchas-recaptcha3/app/views/pages/demo.liquid +++ b/pos-module-captchas-recaptcha3/app/views/pages/demo.liquid @@ -1,14 +1,23 @@ {% comment %} Example-app demo for the captchas_recaptcha3 provider (NOT part of the distributed module). - GET /demo — reCAPTCHA v3 has NO public test keys, so this reads real keys from the - CAPTCHA_RECAPTCHA3_SITE_KEY / CAPTCHA_RECAPTCHA3_SECRET constants and shows setup - instructions when they are unset. v3 is invisible: submitting runs the challenge, injects - the token, and POSTs to /verify, which thresholds the returned score. + GET /demo — demonstrates the DEFAULT-KEYS best practice: the site key is resolved from the + CAPTCHA_DEFAULT_SITE_KEY constant, so the widget is rendered with NO site_key argument (and + /verify reads the secret from CAPTCHA_DEFAULT_SECRET). This exercises the captchas + abstraction's helpers/default_site_key and helpers/default_secret. The provider is pinned to + 'recaptcha3', and the action/score policy is still passed explicitly (not part of the keys + feature). reCAPTCHA v3 has NO public test keys, so this shows setup instructions when the + constants are unset. v3 is invisible: submitting runs the challenge, injects the token, and + POSTs to /verify, which thresholds the returned score. {% endcomment %} -{% liquid - assign site_key = context.constants.CAPTCHA_RECAPTCHA3_SITE_KEY +{%- liquid + assign site_key = context.constants.CAPTCHA_DEFAULT_SITE_KEY + assign secret = context.constants.CAPTCHA_DEFAULT_SECRET assign widget_options = '{"action": "demo_submit"}' | parse_json -%} + assign configured = false + if site_key != blank and secret != blank + assign configured = true + endif +-%}

Captcha demo — Google reCAPTCHA v3

@@ -16,11 +25,14 @@ a score. The verifier here requires a score ≥ 0.5 and action demo_submit.

-{% if site_key == blank %} -

⚠️ CAPTCHA_RECAPTCHA3_SITE_KEY is not set. v3 has no public test keys — - register a v3 site in the reCAPTCHA admin console (tied to this domain) and set both constants:

-
pos-cli constants set <env> --name CAPTCHA_RECAPTCHA3_SITE_KEY --value "6Le..."
-pos-cli constants set <env> --name CAPTCHA_RECAPTCHA3_SECRET --value "6Le..."
+{% unless configured %} +

+ ⚠️ CAPTCHA_DEFAULT_SITE_KEY / CAPTCHA_DEFAULT_SECRET are not both + set. v3 has no public test keys — register a v3 site in the reCAPTCHA admin console (tied to + this domain) and set both constants, then reload: +

+
pos-cli constants set <env> --name CAPTCHA_DEFAULT_SITE_KEY --value "6Le..."
+pos-cli constants set <env> --name CAPTCHA_DEFAULT_SECRET   --value "6Le..."
{% else %}
@@ -29,8 +41,8 @@ pos-cli constants set <env> --name CAPTCHA_RECAPTCHA3_SECRET --value "6Le.

- {% render 'modules/captchas/widget', provider: 'recaptcha3', site_key: site_key, options: widget_options %} + {% render 'modules/captchas/widget', provider: 'recaptcha3', options: widget_options %}

-{% endif %} +{% endunless %} diff --git a/pos-module-captchas-recaptcha3/app/views/pages/verify.liquid b/pos-module-captchas-recaptcha3/app/views/pages/verify.liquid index 5b9c405..3af583c 100644 --- a/pos-module-captchas-recaptcha3/app/views/pages/verify.liquid +++ b/pos-module-captchas-recaptcha3/app/views/pages/verify.liquid @@ -4,11 +4,19 @@ method: post --- {% comment %} Example-app POST handler for the reCAPTCHA v3 demo (NOT part of the distributed module). - Reads the real secret from a constant (v3 has no public test keys) and pairs the widget's - action with expected_action. + Verifies with the secret resolved from CAPTCHA_DEFAULT_SECRET — no secret argument passed — + which exercises the abstraction's helpers/default_secret. min_score and expected_action are + still passed explicitly (v3 score policy, not keys) and expected_action pairs with the + widget's action. The provider is pinned server-side; a real integration is identical: + + function result = 'modules/captchas/commands/captcha/verify', + provider: 'recaptcha3', min_score: 0.5, expected_action: 'demo_submit' + + Set CAPTCHA_DEFAULT_SECRET to your v3 secret (v3 has no public test keys). With it unset, + verify fails closed with secret_missing and the error renders below. {% endcomment %} {% liquid - function result = 'modules/captchas/commands/captcha/verify', provider: 'recaptcha3', secret: context.constants.CAPTCHA_RECAPTCHA3_SECRET, min_score: 0.5, expected_action: 'demo_submit' + function result = 'modules/captchas/commands/captcha/verify', provider: 'recaptcha3', min_score: 0.5, expected_action: 'demo_submit' %} {% if result.valid %}

✅ Captcha passed ({{ result.provider }})

diff --git a/pos-module-captchas-turnstile/app/views/pages/demo.liquid b/pos-module-captchas-turnstile/app/views/pages/demo.liquid index 4f8e8a3..9eb519e 100644 --- a/pos-module-captchas-turnstile/app/views/pages/demo.liquid +++ b/pos-module-captchas-turnstile/app/views/pages/demo.liquid @@ -1,25 +1,53 @@ {% comment %} Example-app demo for the captchas_turnstile provider (NOT part of the distributed module). - GET /demo — renders a form with the Turnstile widget using Cloudflare's public always-pass - TEST keys. Submitting POSTs to /verify, which verifies the token server-side and shows the - normalized result. Swap the keys for real ones (constants) to see a live challenge. + GET /demo — demonstrates the DEFAULT-KEYS best practice: the site key is resolved from the + CAPTCHA_DEFAULT_SITE_KEY constant, so the widget is rendered with NO site_key argument (and + /verify reads the secret from CAPTCHA_DEFAULT_SECRET). This exercises the captchas + abstraction's helpers/default_site_key and helpers/default_secret. The provider is pinned to + 'turnstile' here; only the keys come from constants. + + Cloudflare publishes public always-pass TEST keys, so set the two constants to those and + reload (see the setup box). Swap in your real keys the same way for a live challenge. {% endcomment %} +{%- liquid + assign site_key = context.constants.CAPTCHA_DEFAULT_SITE_KEY + assign secret = context.constants.CAPTCHA_DEFAULT_SECRET + assign configured = false + if site_key != blank and secret != blank + assign configured = true + endif +-%}

Captcha demo — Cloudflare Turnstile

-

- This form uses Turnstile's public test site key, which always passes. - Submitting posts the token to /verify, which calls Cloudflare's siteverify - endpoint server-side and shows the result. -

- -
- +{% unless configured %} +

+ ⚠️ This demo resolves the site key and secret from the CAPTCHA_DEFAULT_SITE_KEY + and CAPTCHA_DEFAULT_SECRET constants — set both, then reload. Cloudflare's + public always-pass test keys work out of the box: +

+
pos-cli constants set <env> --name CAPTCHA_DEFAULT_SITE_KEY --value "1x00000000000000000000AA"
+pos-cli constants set <env> --name CAPTCHA_DEFAULT_SECRET   --value "1x0000000000000000000000000000000AA"

-
- + Currently set — site key: {{ site_key | default: '(unset)' }}, + secret: {% if secret != blank %}(set){% else %}(unset){% endif %}.

+{% else %} +

+ The site key comes from CAPTCHA_DEFAULT_SITE_KEY, so the widget below passes no + site_key. Submitting posts the token to /verify, which likewise + reads the secret from CAPTCHA_DEFAULT_SECRET — Cloudflare's siteverify is + called server-side and the normalized result shown. +

+ + + +

+
+ +

- {% render 'modules/captchas/widget', provider: 'turnstile', site_key: '1x00000000000000000000AA' %} + {% render 'modules/captchas/widget', provider: 'turnstile' %} -

-
+

+ +{% endunless %} diff --git a/pos-module-captchas-turnstile/app/views/pages/verify.liquid b/pos-module-captchas-turnstile/app/views/pages/verify.liquid index 2c7b3d6..47a1436 100644 --- a/pos-module-captchas-turnstile/app/views/pages/verify.liquid +++ b/pos-module-captchas-turnstile/app/views/pages/verify.liquid @@ -4,15 +4,18 @@ method: post --- {% comment %} Example-app POST handler for the Turnstile demo (NOT part of the distributed module). - Uses Cloudflare's public always-pass TEST secret. A real integration is identical, with - the secret read from a constant: + Verifies with the secret resolved from CAPTCHA_DEFAULT_SECRET — no secret argument passed — + which exercises the abstraction's helpers/default_secret. The provider is pinned server-side + (never read from client input); a real integration is identical: - function result = 'modules/captchas/commands/captcha/verify', - provider: 'turnstile', - secret: context.constants.CAPTCHA_TURNSTILE_SECRET + function result = 'modules/captchas/commands/captcha/verify', provider: 'turnstile' + + Set CAPTCHA_DEFAULT_SECRET (Cloudflare's always-pass test secret 1x0000...AA works). With it + unset, verify fails closed with secret_missing and the error renders below — that is the + fail-closed behavior, not a bug. {% endcomment %} {% liquid - function result = 'modules/captchas/commands/captcha/verify', provider: 'turnstile', secret: '1x0000000000000000000000000000000AA' + function result = 'modules/captchas/commands/captcha/verify', provider: 'turnstile' %} {% if result.valid %}

✅ Captcha passed ({{ result.provider }})

diff --git a/pos-module-captchas/app/views/pages/captcha-demo.liquid b/pos-module-captchas/app/views/pages/captcha-demo.liquid index ab2f374..4701e81 100644 --- a/pos-module-captchas/app/views/pages/captcha-demo.liquid +++ b/pos-module-captchas/app/views/pages/captcha-demo.liquid @@ -1,36 +1,70 @@ {% comment %} Example-app demo for pos-module-captchas (NOT part of the distributed module). - GET /captcha-demo — renders a form protected by the FAKE test provider that ships with this - repo's test app (app/modules/captchas_test): its widget injects a deterministic token and its - verify command passes token 'pass' and fails anything else, without any network call. This - demonstrates the abstraction only — widget dispatch, verify dispatch, normalized results. - Real providers are separate modules (captchas_turnstile, captchas_hcaptcha, - captchas_recaptcha, captchas_recaptcha3), each with its own demo app in its own repo. -{% endcomment %} -

Captcha demo — Test provider

+ GET /captcha-demo — demonstrates the DEFAULT-CONSTANT best practice from the module README: + the provider, site key, and secret are all resolved from instance constants + (CAPTCHA_DEFAULT_PROVIDER, CAPTCHA_DEFAULT_SITE_KEY, CAPTCHA_DEFAULT_SECRET), so the widget + below is rendered — and the token verified in /captcha-verify — with NO + provider/site_key/secret arguments at the call site. This is what exercises + helpers/default_site_key and helpers/default_secret (the default-keys feature). -

- This form uses the repo's fake test provider (app/modules/captchas_test): - the widget below injects a token that always verifies, with no network call. Submitting posts - to /captcha-verify, which dispatches to the provider module server-side and shows - the normalized result. Install a real provider module (e.g. captchas_turnstile) - and use its demo app to see a real challenge. -

+ It runs against the FAKE test provider that ships with this repo's example app + (app/modules/captchas_test): its widget injects a deterministic token and its verify command + passes token 'pass' and fails anything else, with no network call. Point the constants at the + 'test' provider (see the setup box below) to try it — any non-blank site key / secret works, + since the fake provider ignores their values. Real providers are separate modules + (captchas_turnstile, captchas_hcaptcha, captchas_recaptcha, captchas_recaptcha3), each with + its own demo app in its own repo. +{% endcomment %} +{%- liquid + assign default_provider = context.constants.CAPTCHA_DEFAULT_PROVIDER + assign default_site_key = context.constants.CAPTCHA_DEFAULT_SITE_KEY + assign default_secret = context.constants.CAPTCHA_DEFAULT_SECRET + assign configured = false + if default_provider != blank and default_site_key != blank and default_secret != blank + assign configured = true + endif +-%} +

Captcha demo — default constants

-
- +{% unless configured %} +

+ ⚠️ This demo resolves the provider, site key, and secret entirely from instance constants — + the best-practice setup from the module README. Set all three, then reload this page. Any + non-blank site key / secret works for the fake test provider, which ignores + their values: +

+
pos-cli constants set <env> --name CAPTCHA_DEFAULT_PROVIDER --value "test"
+pos-cli constants set <env> --name CAPTCHA_DEFAULT_SITE_KEY --value "demo-site-key"
+pos-cli constants set <env> --name CAPTCHA_DEFAULT_SECRET   --value "demo-secret"

-
- + Currently set — provider: {{ default_provider | default: '(unset)' }}, + site key: {{ default_site_key | default: '(unset)' }}, + secret: {% if default_secret != blank %}(set){% else %}(unset){% endif %}.

+{% else %} +

+ Provider, site key, and secret all come from constants + (CAPTCHA_DEFAULT_PROVIDER = {{ default_provider }}), so the widget below and the + verify in /captcha-verify pass no + provider/site_key/secret arguments — they resolve via + helpers/default_site_key and helpers/default_secret. +

+ + + +

+
+ +

- {% render 'modules/captchas/widget', provider: 'test', site_key: 'demo-site-key' %} + {% render 'modules/captchas/widget' %} -

-
+

+ -
- - -

-
+
+ + +

+
+{% endunless %} diff --git a/pos-module-captchas/app/views/pages/captcha-verify.liquid b/pos-module-captchas/app/views/pages/captcha-verify.liquid index 585de7a..28c73ea 100644 --- a/pos-module-captchas/app/views/pages/captcha-verify.liquid +++ b/pos-module-captchas/app/views/pages/captcha-verify.liquid @@ -4,20 +4,23 @@ method: post --- {% comment %} Example-app POST handler for the captcha demo (NOT part of the distributed module). - The provider is fixed SERVER-SIDE — never read the provider (and therefore the secret) - from client input: an attacker who controls `provider` could steer verification to a - weaker path and bypass the captcha. A real integration looks the same, with a real - provider module installed and the secret read from a constant: + Verifies with NO arguments: the provider resolves from CAPTCHA_DEFAULT_PROVIDER and the secret + from CAPTCHA_DEFAULT_SECRET (both server-side constants). This is the module README's + best-practice call shape and is what exercises helpers/default_secret. - function result = 'modules/captchas/commands/captcha/verify', - provider: 'turnstile', - secret: context.constants.CAPTCHA_TURNSTILE_SECRET + Keeping the provider in a server-side constant — never read from client input — is deliberate: + an attacker who controls `provider`, and therefore which secret verifies, could steer + verification to a weaker path and bypass the captcha. A real integration is identical; just set + the CAPTCHA_DEFAULT_* constants for your provider (see /captcha-demo) and call: - Here the fake test provider verifies deterministically (token 'pass' passes, anything - else fails), so any non-blank secret works and no network call is made. + function result = 'modules/captchas/commands/captcha/verify' + + With the constants unset, verify fails closed (secret_missing / unsupported_provider) and those + errors render below — that is the fail-closed behavior, not a bug. Against the fake test + provider, token 'pass' passes and anything else fails, with no network call. {% endcomment %} {% liquid - function result = 'modules/captchas/commands/captcha/verify', provider: 'test', secret: 'demo-secret' + function result = 'modules/captchas/commands/captcha/verify' %} {% if result.valid %}

✅ Captcha passed ({{ result.provider }})