godcrm/backend/services/verification/methods/captcha.js
GOD CRM Release f89e074dd1
Some checks failed
CI / Lint / Typecheck / Test / Build (push) Has been cancelled
CI / PostgreSQL Integration Tests (push) Has been cancelled
GOD CRM — public scrubbed snapshot
Governed substrate for autonomous agents: scoped identity (passports),
audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
2026-08-10 04:01:45 +03:00

60 lines
2.1 KiB
JavaScript

// ADR-0011 · Phase C · CAPTCHA method plugin (hCaptcha).
//
// Requires HCAPTCHA_SECRET env. When unset → returns captcha_not_configured
// (the verify endpoint treats it as method-level failure, which bubbles up to
// 401 if required_methods is not met).
//
// Submission: { token: 'hcap-<...>' } (client-side hCaptcha widget token).
// Server posts to hCaptcha siteverify; on {success:true} → ok.
import { apiLogger } from '../../../utils/logger.js';
import { getSecret } from '../../secrets/getSecret.js';
import { hashCode } from './hash.js';
const HCAPTCHA_VERIFY_URL = 'https://hcaptcha.com/siteverify';
export const captchaMethod = {
name: 'captcha',
async verify({ context, submission }) {
const token = submission?.token;
if (!token || typeof token !== 'string') {
return { ok: false, code: 'captcha_token_missing', message: 'Captcha token is required' };
}
// ADR-0040: vault first, env fallback during transition.
const secret = await getSecret('hcaptcha_secret', 'HCAPTCHA_SECRET');
if (!secret) {
return {
ok: false,
code: 'captcha_not_configured',
message: 'Captcha is not configured (hcaptcha_secret missing in vault)',
};
}
try {
const body = new URLSearchParams({ secret, response: token });
const resp = await fetch(HCAPTCHA_VERIFY_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: body.toString(),
});
const json = await resp.json();
if (!json?.success) {
apiLogger.warn({ errors: json?.['error-codes'] }, 'hCaptcha verify failed');
return { ok: false, code: 'captcha_invalid', message: 'Captcha verification failed' };
}
} catch (err) {
apiLogger.error({ err: err.message }, 'hCaptcha verify request failed');
return { ok: false, code: 'captcha_network_error', message: 'Captcha verification request failed' };
}
const at = new Date().toISOString();
return {
ok: true,
at,
// Token is one-shot (hCaptcha invalidates on verify) — hash for audit only.
code_hash: hashCode(token, context.rowId, at),
};
},
};