
Research
/Security News
Miasma Mini Shai-Hulud Hits ImmobiliareLabs npm Packages
Miasma Mini Shai-Hulud hits @immobiliarelabs Backstage plugins, targeting GitLab and LDAP auth packages on npm.
@captigo/nextjs
Advanced tools
Next.js App Router helpers for Captigo — request parsing, client IP, and route-handler verification
Next.js App Router helpers for Captigo — parse
Request, resolve client IP, verify with anyCaptchaAdapter.
Uses the Web Request API only (no runtime dependency on next). Pair with @captigo/react and an adapter such as @captigo/turnstile on the client.
npm install @captigo/nextjs @captigo/turnstile
@captigo/core is installed transitively. next ≥ 14 is an optional peer (declared for version clarity; helpers work anywhere Request is available).
The most common pattern: a POST endpoint that receives the token from a form and verifies it before taking any action.
// app/api/submit/route.ts
import { verifyCaptchaFromRequest, CaptchaError } from "@captigo/nextjs";
import { turnstile } from "@captigo/turnstile";
const adapter = turnstile({ siteKey: process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY! });
export async function POST(request: Request) {
let result;
try {
result = await verifyCaptchaFromRequest(
request,
adapter,
process.env.TURNSTILE_SECRET!,
);
} catch (err) {
if (err instanceof CaptchaError) {
// Token was missing from the body, or the provider network call failed.
return Response.json({ error: err.message }, { status: 400 });
}
throw err;
}
if (!result.success) {
return Response.json({ error: "CAPTCHA verification failed" }, { status: 400 });
}
// Token is verified — proceed with the protected action.
return Response.json({ ok: true });
}
Note on field names.
verifyCaptchaFromRequestlooks for a"token"field by default. If your form uses a different name (e.g."cf-turnstile-response"from Turnstile's auto-injected hidden input), pass it viaoptions.fieldName.
Use @captigo/react and @captigo/turnstile on the client as usual. No changes are needed — @captigo/nextjs is a server-only package.
// app/contact/page.tsx
"use client";
import { Captcha } from "@captigo/react";
import { turnstile } from "@captigo/turnstile";
import { useState } from "react";
const adapter = turnstile({ siteKey: process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY! });
export default function ContactPage() {
const [token, setToken] = useState<string | null>(null);
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
if (!token) return;
await fetch("/api/submit", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ token, message: "Hello" }),
});
}
return (
<form onSubmit={handleSubmit}>
<Captcha adapter={adapter} onSuccess={(t) => setToken(t.value)} />
<button type="submit" disabled={!token}>Send</button>
</form>
);
}
verifyCaptchaFromRequest(request, adapter, secretKey, options?)Extracts the CAPTCHA token from the request body, resolves the client IP from standard headers, and calls adapter.verify().
import { verifyCaptchaFromRequest } from "@captigo/nextjs";
const result = await verifyCaptchaFromRequest(request, adapter, secretKey, {
fieldName: "token", // default — the body field that holds the token
forwardIp: true, // default — passes client IP to the provider
});
Throws CaptchaError when:
Returns VerifyResult — check result.success to decide whether to accept the submission.
captchaTokenFromRequest(request, fieldName?)Extracts the raw token string from a request body. Tries JSON first, then FormData/URL-encoded. Returns null when the field is absent or empty — never throws on parse failure.
import { captchaTokenFromRequest } from "@captigo/nextjs";
// Use this when you need the token separately before calling adapter.verify().
const token = await captchaTokenFromRequest(request, "cf-turnstile-response");
if (!token) {
return Response.json({ error: "Missing CAPTCHA token" }, { status: 400 });
}
const result = await adapter.verify(token, process.env.TURNSTILE_SECRET!);
clientIpFromRequest(request)Resolves the client IP from standard CDN and proxy headers. Checks in order:
CF-Connecting-IP (Cloudflare)X-Forwarded-For (first address in the list)X-Real-IP (nginx)Returns undefined when none are present.
import { clientIpFromRequest } from "@captigo/nextjs";
const ip = clientIpFromRequest(request);
const result = await adapter.verify(token, secretKey, ip ? { remoteip: ip } : undefined);
verifyCaptchaFromRequest expects a Request object. Server Actions receive form data directly, not a Request, so use captchaTokenFromRequest with the raw FormData instead — or verify via a route handler.
// app/actions.ts
"use server";
import { CaptchaError } from "@captigo/nextjs";
import { turnstile } from "@captigo/turnstile";
const adapter = turnstile({ siteKey: process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY! });
export async function submitAction(formData: FormData) {
const token = formData.get("token");
if (typeof token !== "string" || !token) {
throw new Error("Missing CAPTCHA token");
}
const result = await adapter.verify(token, process.env.TURNSTILE_SECRET!);
if (!result.success) {
throw new Error("CAPTCHA verification failed");
}
// ... proceed
}
next runtime dependency. The helpers work with the standard Web Request API (available natively in Next.js App Router and Node.js 18+).adapter.verify() or expose your secret key on the client.turnstile(config), hcaptcha(config), recaptchaV2(config), etc.FAQs
Next.js App Router helpers for Captigo — request parsing, client IP, and route-handler verification
We found that @captigo/nextjs demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
/Security News
Miasma Mini Shai-Hulud hits @immobiliarelabs Backstage plugins, targeting GitLab and LDAP auth packages on npm.

Security News
Rolldown paused Rust React Compiler integration after a 5MB binary size increase raised concerns about shipping React-specific code to all Vite users.

Security News
/Research
Mini Shai-Hulud expands into the Go ecosystem after hitting LeoPlatform npm packages and targeting GitHub Actions workflows.