1/** 2 * TASK-0107 §6(c) — the reCAPTCHA verification HOOK POINT (stub). 3 * 4 * Phase 10 ships the seam, not the integration: `verifyRecaptcha` returns 5 * `{ skipped: true }` while the provider is unconfigured (the real Google 6 * verification lands in Phase 13+ against `RECAPTCHA_SECRET`). The public 7 * page renders the placeholder widget div and the submit payload carries 8 * the `recaptchaToken` slot when `config.recaptcha.enabled` — both flow 9 * through this function so the Phase-13 swap is a single-file change. 10 */ 11import { loadEnv } from "../../config/env.js"; 12import { logger } from "../../common/logger.js"; 13 14export interface RecaptchaResult { 15 /** True when no verification ran (provider unconfigured / disabled). */ 16 skipped: boolean; 17 /** The verified end-user IP (set by the real provider in Phase 13+). */ 18 success: boolean; 19 ip?: string; 20} 21 22/** 23 * Verify a reCAPTCHA token. STUB behavior (documented): 24 * - provider not configured (`RECAPTCHA_SECRET` empty) → `{ skipped: true, success: true }` 25 * + a one-per-request WARN log ("recaptcha verification skipped (stub)"); 26 * - provider configured → the Phase-13 implementation site (throws 27 * `RECAPTCHA_NOT_CONFIGURED` for now so a half-configured deployment 28 * fails LOUD instead of silently open). 29 */ 30export async function verifyRecaptcha(_token: string | undefined, _ip: string): Promise<RecaptchaResult> { 31 const env = loadEnv(); 32 if (!env.RECAPTCHA_SECRET) { 33 logger.warn("recaptcha verification skipped (stub — RECAPTCHA_SECRET not configured)"); 34 return { skipped: true, success: true }; 35 } 36 // Phase 13: POST https://www.google.com/recaptcha/api/siteverify 37 throw new Error("recaptcha provider not implemented (Phase 13)"); 38} 39