AbsoluteJS

Bot & Abuse Protection

An allow / challenge / deny pipeline over IP allow-deny lists, a CAPTCHA hook, and a bot classifier — the self-hosted framework half of WorkOS "Radar" (which is hosted-only). Composes with the adaptive risk engine.

#The guard

createAbuseGuard owns the decision pipeline; you supply the signals:

ipDeny / ipAllowIP lists, exact or IPv4 CIDR.
verifyCaptchaA hook wrapping your CAPTCHA provider.
classifyBotThe built-in UA heuristic, or your own AI-agent detector.
Honest scope: fingerprint-grade detection needs a data network — this is the framework and hooks.
TS
import {
  createAbuseGuard,
  defaultBotClassifier,
  verifyTurnstile
} from '@absolutejs/auth';

// The decision pipeline: IP allow/deny (exact or IPv4 CIDR) + a CAPTCHA hook +
// a bot classifier -> allow / challenge / deny. The framework half of WorkOS
// "Radar" (which is hosted-only); you supply the signals.
export const abuse = createAbuseGuard({
  // a built-in adapter (Turnstile/reCAPTCHA/hCaptcha) or your own (token) => boolean
  verifyCaptcha: verifyTurnstile({ secret: process.env.TURNSTILE_SECRET }),
  // built-in User-Agent heuristic, or pass your own (e.g. an AI-agent detector)
  classifyBot: defaultBotClassifier,
  ipDeny: ['203.0.113.0/24'],
  captchaAction: 'challenge' // default 'deny'
});

#CAPTCHA adapters

Built-in verifyTurnstile, verifyRecaptcha (with an optional v3 minScore), and verifyHcaptcha wrap each provider's siteverify call (the client IP is taken from the assess context). Drop one into verifyCaptcha — no extra plumbing.

TS
import {
  createAbuseGuard,
  verifyHcaptcha,
  verifyRecaptcha,
  verifyTurnstile
} from '@absolutejs/auth';

// Built-in CAPTCHA adapters call the provider's siteverify for you (remoteip is
// taken from the assess context). All three share the same shape; reCAPTCHA v3
// also takes a minScore.
const turnstile = verifyTurnstile({ secret: process.env.TURNSTILE_SECRET });
const recaptcha = verifyRecaptcha({ minScore: 0.5, secret: process.env.RECAPTCHA_SECRET });
const hcaptcha = verifyHcaptcha({ secret: process.env.HCAPTCHA_SECRET });

const abuse = createAbuseGuard({ verifyCaptcha: turnstile, captchaAction: 'deny' });

#Wire into login

Call assess at the top of register / login with the request context. The result is the most severe action that fired plus every reason; an allow-listed IP short-circuits.

TS
// Run it at the top of register / login, where the request context is available.
// Pairs with the adaptive risk engine — run both.
const { action, reasons } = await abuse.assess({
  captchaToken: body.captchaToken,
  ip: request.headers.get('x-forwarded-for') ?? undefined,
  userAgent: request.headers.get('user-agent') ?? undefined
});

if (action === 'deny') return status('Forbidden', { reasons });
if (action === 'challenge') return status('Bad Request', 'captcha_required');
// action === 'allow' -> proceed with the credential work