code-challenge.php
Code-Challenge library layer: trigger decision, transient-backed token codec, and random code generator. REST handlers and route registration live at the bottom of this file (added in Task 10).
Table of Contents
Functions
- creationell_captcha_should_issue_code_challenge() : bool
- Decides whether the current /challenge request should attach a code-challenge instruction. Returns true iff:
- creationell_captcha_code_charset() : string
- Returns the active charset string for the configured option.
- creationell_captcha_generate_code() : string
- Generates a fresh random code from the configured charset.
- creationell_captcha_code_token_issue() : string
- Issues a token backed by a server-side WP transient that stores the expected code for at most $expiry_seconds. Returns the opaque ID that `/code-image` and `/code-verify` use to look the code up again.
- creationell_captcha_code_token_verify() : string|null
- Looks up the code for a token and returns it, or null on: - malformed token (not 32 hex chars) - missing transient (expired or unknown)
- creationell_captcha_code_fail_key() : string
- Transient name of the per-token failed-attempt counter (CM-3).
- creationell_captcha_code_token_fail() : void
- Records one wrong image-code submission for a token and invalidates the token once its attempt budget is spent.
- creationell_captcha_rest_code_image() : WP_REST_Response
- Handles GET /code-image?t=<token>. Looks up the code from the token (server-side transient), renders the PNG, returns 410 on token failure.
- creationell_captcha_rest_code_verify() : WP_REST_Response
- Handles POST /code-verify. Two body shapes:
- creationell_captcha_register_code_challenge_routes() : void
- Registers the two code-challenge REST routes. The /challenge handler itself stays in includes/rest.php; Task 11 extends it with the codeChallenge field and the data.ccode embed.
Functions
creationell_captcha_should_issue_code_challenge()
Decides whether the current /challenge request should attach a code-challenge instruction. Returns true iff:
creationell_captcha_should_issue_code_challenge() : bool
- the master toggle is on, AND
- PHP-GD is available, AND
- at least one of the three trigger conditions matches (under-attack, ratelimit threshold, watch-list).
Return values
boolcreationell_captcha_code_charset()
Returns the active charset string for the configured option.
creationell_captcha_code_charset(string $charset_key) : string
Parameters
- $charset_key : string
-
One of: digits / alphanumeric / alphanumeric-no-confusing.
Return values
stringcreationell_captcha_generate_code()
Generates a fresh random code from the configured charset.
creationell_captcha_generate_code() : string
Return values
stringcreationell_captcha_code_token_issue()
Issues a token backed by a server-side WP transient that stores the expected code for at most $expiry_seconds. Returns the opaque ID that `/code-image` and `/code-verify` use to look the code up again.
creationell_captcha_code_token_issue(string $code, int $expiry_seconds) : string
The code is intentionally NOT encoded into the token itself — a base64 round-trip would leak the code to anyone who can read the network response (defeats the OCR-resistant captcha goal). Server state via WP transients is the accepted trade-off.
Parameters
- $code : string
-
The expected code (already from generator).
- $expiry_seconds : int
-
Seconds until the token expires.
Return values
stringcreationell_captcha_code_token_verify()
Looks up the code for a token and returns it, or null on: - malformed token (not 32 hex chars) - missing transient (expired or unknown)
creationell_captcha_code_token_verify(string $token[, bool $consume = false ]) : string|null
Parameters
- $token : string
-
The 32-hex-char ID from
code_token_issue. - $consume : bool = false
-
Delete the transient after successful lookup (single-use semantics). The /code-image handler passes false; /code-verify passes true ONLY after a successful code match so retries on wrong input still work.
Return values
string|nullcreationell_captcha_code_fail_key()
Transient name of the per-token failed-attempt counter (CM-3).
creationell_captcha_code_fail_key(string $token) : string
Parameters
- $token : string
-
The 32-hex-char ID from
code_token_issue.
Return values
stringcreationell_captcha_code_token_fail()
Records one wrong image-code submission for a token and invalidates the token once its attempt budget is spent.
creationell_captcha_code_token_fail(string $token) : void
Before this existed, a wrong code was completely free: /code-verify
returned 401 before the consume, nothing was counted, and the REST route
is not covered by the default rate-limit scopes (core/forms). A client
could therefore walk a token through the entire code space. The counter
lives exactly as long as the token it belongs to, so it cannot be reset by
waiting — only by fetching a fresh challenge, which also means a fresh PoW.
Parameters
- $token : string
-
The 32-hex-char ID from
code_token_issue.
creationell_captcha_rest_code_image()
Handles GET /code-image?t=<token>. Looks up the code from the token (server-side transient), renders the PNG, returns 410 on token failure.
creationell_captcha_rest_code_image(WP_REST_Request $request) : WP_REST_Response
Idempotent — the transient is NOT consumed here so the browser may reload the image.
Parameters
- $request : WP_REST_Request
-
The REST request. Required query parameter
t(opaque server-issued token).
Return values
WP_REST_Response —200 with image/png body on success; 410 when the
token is unknown/expired or PHP-GD is unavailable.
creationell_captcha_rest_code_verify()
Handles POST /code-verify. Two body shapes:
creationell_captcha_rest_code_verify(WP_REST_Request $request) : WP_REST_Response
-
Code-Challenge mode: { "code": "
", "payload": " " } The widget rendered a code-image because the /challenge response embedded data.ccode. Token lookup → case-insensitive match → single-use consume → "code stage passed" marker for this challenge. - Plain server-verify mode: { "payload": "
" } (no code) ALTCHA's widget posts here unconditionally whenever verifyUrl is set (see widget.js logic: verifyUrl ? _e() : verified()). Structural verify only — nothing is consumed, because there is nothing to protect: the endpoint hands back exactly what it was given.
- Plain server-verify mode: { "payload": "
Neither mode mints a payload any more (CM-5, Wurzel 3.1). The old handler
ended in Engine::issue_signed_payload(), which made the SERVER solve a
fresh PoW (solveChallenge()) and returned a ready-to-use payload — one
client-side solve was enough to drive an endless loop of server-side key
derivations. The response now echoes the SUBMITTED payload back verbatim,
so a caller never gains anything it did not already have.
Parameters
- $request : WP_REST_Request
-
The REST request with JSON body
{payload: string, code?: string}.
Return values
WP_REST_Response —200 on success ({payload, verified: true}); 400 on malformed body; 401 on wrong code; 410 on missing/expired/unsigned payload or token.
creationell_captcha_register_code_challenge_routes()
Registers the two code-challenge REST routes. The /challenge handler itself stays in includes/rest.php; Task 11 extends it with the codeChallenge field and the data.ccode embed.
creationell_captcha_register_code_challenge_routes() : void