feat(components): frontend captcha gate — <zl-captcha> atom, patcher injection, mock verification - #562
Draft
fforootd wants to merge 1 commit into
Draft
feat(components): frontend captcha gate — <zl-captcha> atom, patcher injection, mock verification#562fforootd wants to merge 1 commit into
<zl-captcha> atom, patcher injection, mock verification#562fforootd wants to merge 1 commit into
Conversation
…, patcher injection, mock verification Implements the frontend half of ADR 019 on current main, replacing stale PR #159 (pre-rewrite history, dead template surface): - New invisible light-DOM <zl-captcha> atom: Altcha proof-of-work (Web Worker fast path, abortable main-thread fallback) and Turnstile / hCaptcha / reCAPTCHA widgets; emits string proofs (base64 Altcha payload / vendor token) per the existing gate_proofs contract — no OpenAPI change. - mandatory-gates patcher now injects <zl-captcha> for any step gate without a consumer (gates first, so solving starts on mount); the default template needs no gate markup. - Orchestrator collects proofs, sends gate_proofs on submit, clears them per step, and surfaces error.gate_failed (en/de/it) with the same anti-remount-loop guard as passkey errors. - Invisible atoms are uniformly null-safe: auto-start moves to the first update cycle and late-arriving options/config activate it; explicit start calls without data still error. - api-mock mints a real Altcha challenge on the identifier step and, with verifyGates: true (standalone server), verifies proofs and re-renders with a fresh challenge on failure. - jsdom 29 lacks crypto.subtle — unit project now backs it with Node's WebCrypto via vitest.setup.unit.ts.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
🦋 Changeset detectedLatest commit: 486a21b The changes in this PR will be included in the next version bump. This PR includes changesets to release 19 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements the frontend half of ADR 019 (Captcha Gate Contract & Bot-Detection Signals), replacing stale #159 — that branch predates the June history rewrite (no common ancestor with main) and patches per-step Liquid templates that were since consolidated into
default.liquid, so this is a fresh implementation on current main that ports its good parts.<zl-captcha>atom (light DOM): reads the gate config from attributes, dispatches onkind+provider— built-in Altcha proof-of-work (Web Worker fast path, abortable main-thread fallback) or a third-party widget (Turnstile / hCaptcha / reCAPTCHA, mounted in light DOM because reCAPTCHA/hCaptcha break in shadow roots) — and emits the proof viazl-captcha-result.flow-submit-request.yamlcontract: base64-encoded JSON solution payload for Altcha (the standard Altcha wire format), the token for vendors. Zero OpenAPI/codegen changes — the spec's<zl-captcha>references and string-valuedgate_proofswere already correct as written.mandatory-gatespatcher now does what its docstring always claimed: anystep.gatesentry without a matching<zl-captcha>in the rendered template gets one injected (gates first, so solving starts on mount).default.liquidneeds no gate markup at all — custom branding authors never have to know invisible atoms exist.gate_proofson submit, clears them per step, and surfaceserror.gate_failed(new locale key in en/de/it) with the same anti-remount-loop guard as passkey errors. Submits are not blocked client-side; enforcement is the server's job (ADR 019 re-renders with a fresh challenge on failure).<zl-passkey>and<zl-captcha>auto-start from the first update cycle and also activate when options/config arrive late; mounting without data idles silently. Deliberate deviation from feat(components):<zl-gate>atom, null-safe invisible atoms, mock gate verification #159: an explicitstartCeremony()/startSolve()without data still emits an error event (the suite pins that contract, and a loud failure on a consumer bug is correct).identifierStepnow mints a real Altcha challenge for abot_checkgate on every render;setupMockHandlers({ verifyGates: true })(the standalone dev server opts in) verifies submitted proofs and re-renders witherror.gate_failed+ a fresh challenge on a missing or tampered proof. Unit tests keep verification off by default.cryptowithoutsubtle; the components unit project now backs it with Node's WebCrypto viavitest.setup.unit.ts.Validation
moon run components:test— 366/366 (includes the end-to-end path: withverifyGates: truethe patcher injects the atom, it solves the PoW, the submit carriesgate_proofs, the mock verifies, and the flow reaches "You're signed in"; plus the anti-loop banner test)moon run api-mock:test— 47/47 (mint/verify round-trip, missing-proof and tampered-proof rejection with fresh challenge, spec-conformance of the gated fixture)moon run components:typecheck api-mock:typecheck— cleanmoon run components:lint api-mock:lint— 0 errors, no new warningsmoon run components:build api-mock:build— clean*.browser.spec.ts) projects not run locally; CI covers them.Release notes / changeset
.changeset/frontend-captcha-gate.md— minor for@zitadel/components(@zitadel/api-mockis changeset-ignored).Notes
<zl-gate>atom, null-safe invisible atoms, mock gate verification #159 (recommend closing it once this is reviewed — its branch can't merge: disjoint history after the rewrite, and its template edits target files that no longer exist).FlowStep.gatesandProcess()still returnsErrUnsupportedforgate_proofs— the api-mock is the only end-to-end consumer until that lands.<zl-captcha>naming this PR required no ADR/doc edits.refactor-liquid-fields, stale draft): onlymandatory-gates.ts+ registration/exports — this PR deliberately avoidsdefault.liquidand the field filters. When refactor(components): extract field rendering into _fields.liquid partial and fix select enum mapping #398 revives it rebases over this; itsmandatory-gates → field-patcherrename is worth reconsidering there since the patcher now handles gates too.zl-passkeytests neededupdateCompleteawaits: the auto-start moved fromconnectedCallback(synchronous on attach) to the first update cycle.<zl-gate>atom, null-safe invisible atoms, mock gate verification #159.