The captcha a site's sign-in, registration, forgot-password and sign-in-link forms ask, through the provider the site picked.
Each scope picks a provider on the settings page's Security tab (security.captcha.provider) and
sets that provider's keys there, for itself alone (resolve: :own): Global is the masters' own
sign-in. Providers are mishka_installer plugins (MishkaCmsCore.Captcha.Provider), the core's
own — Cloudflare Turnstile, Google reCAPTCHA and hCaptcha — included. A scope with no provider
(None) or with one missing a key asks nothing. A scope whose chosen provider does not answer — its
plugin is gone, raised, or cannot run (MishkaCmsCore.ProviderRegistry) — refuses every check
until it chooses another or None.
Where
A form draws the check with <.captcha> (MishkaCmsCore.Captcha.Widget), which browser/3
describes, and its action checks the token (MishkaCmsCore.Accounts.CaptchaCheck), so the sign-in
pages and the /api/auth routes ask alike. An extension's form asks the same captcha under a form
of its own, an atom its action and its <.captcha for={…}> both name, and may ask it only once the
form has been sent (deferred, see MishkaCmsCore.Captcha.Widget). The security headers let the
browser load the provider on the sites that use it (csp_sources/1).
One check
check/4 refuses a missing token, and a token it saw accepted in the last five minutes — each is
good for one check — without asking anybody, then asks the provider, then holds its answer to the
site's own address (security.captcha.check_domain). A provider that cannot be asked — down, slow,
refusing the keys — is answered {:error, {:unavailable, detail}}, and the action lets the form
through: its rate limits still hold.
Summary
Functions
How the browser draws site_id's check for form in language — the provider's
MishkaCmsCore.Captcha.Provider.browser/3 with its :provider id — or nil when it asks none.
Checks token, sent with form to site_id: :ok when it passed or the scope asks none;
{:error, :missing} without one; {:error, {:failed, detail}} when it was used before, the
provider refused it, it was solved on another address, or the provider the scope chose does not
answer; {:error, {:unavailable, detail}} when the provider could not be asked.
The origins, by CSP directive, the browser loads the captcha of any of site_ids from — none for
a scope that asks none. :all names every offered provider's, for the settings page, where any of
them may be tried.
Whether site_id's forms ask a captcha now.
The provider site_id checks with, ready to check: {:ok, module}, or {:error, reason} —
:off (none chosen), :unknown_provider (chosen, but its plugin is gone), :cannot_run (chosen,
while the providers' plugins cannot run) or :not_ready (chosen, but missing a key).
The settings the Security tab shows under "Captcha": which provider a scope uses, whether its
answer is held to the site's address, then every offered provider's own — each drawn only while
its provider is the one chosen (MishkaCmsCore.Settings.shown_when/3). Part of
MishkaCmsCore.Settings' core definitions.
The settings page's test of site_id's keys with a token solved on it: what to tell the admin,
{:ok, text} or {:error, text}. The same check a form gets, so a passed test spends the token.
Types
@type form() :: MishkaCmsCore.Captcha.Provider.form()
@type request() :: %{optional(:remote_ip) => String.t() | nil}
@type site_id() :: String.t() | nil
Functions
How the browser draws site_id's check for form in language — the provider's
MishkaCmsCore.Captcha.Provider.browser/3 with its :provider id — or nil when it asks none.
Checks token, sent with form to site_id: :ok when it passed or the scope asks none;
{:error, :missing} without one; {:error, {:failed, detail}} when it was used before, the
provider refused it, it was solved on another address, or the provider the scope chose does not
answer; {:error, {:unavailable, detail}} when the provider could not be asked.
The origins, by CSP directive, the browser loads the captcha of any of site_ids from — none for
a scope that asks none. :all names every offered provider's, for the settings page, where any of
them may be tried.
Whether site_id's forms ask a captcha now.
@spec provider(site_id()) :: {:ok, module()} | {:error, :off | :unknown_provider | :cannot_run | :not_ready}
The provider site_id checks with, ready to check: {:ok, module}, or {:error, reason} —
:off (none chosen), :unknown_provider (chosen, but its plugin is gone), :cannot_run (chosen,
while the providers' plugins cannot run) or :not_ready (chosen, but missing a key).
@spec settings() :: [MishkaCmsCore.Settings.definition()]
The settings the Security tab shows under "Captcha": which provider a scope uses, whether its
answer is held to the site's address, then every offered provider's own — each drawn only while
its provider is the one chosen (MishkaCmsCore.Settings.shown_when/3). Part of
MishkaCmsCore.Settings' core definitions.
The settings page's test of site_id's keys with a token solved on it: what to tell the admin,
{:ok, text} or {:error, text}. The same check a form gets, so a passed test spends the token.