Next.js
July 20, 2026 · View on GitHub
The Next.js plugin simplifies integrating ALTCHA into your application. It provides handlers and middleware for challenge generation and verification.
Handlers
challengeHandler— Generates a new challenge. Configure this endpoint as the widget’schallengeURL.verifyHandler— Optional handler for server-side verification. Useful when external services need to verify a payload.
Middleware
The middleware automatically handles verification during requests.
Features:
- Validates the payload from the request body or a configured cookie.
- Sets
req.__altcha: AltchaResult, which includes the challenge data and verification result.
Options:
throwOnFailure: boolean— Whether to throw an error on verification failure (default:true).
Store
The store marks challenges as used to prevent reuse.
See /docs/store.md for details.
Remote verification (Sentinel)
Pass a verifyServer option to verify Sentinel-issued payloads remotely via Sentinel's
/v1/verify/signature API instead of locally with hmacSignatureSecret.
See /docs/server-signatures.md#remote-verification-sentinel-api for details.
Usage
1. Create the altcha instance
File: lib/altcha.ts
import { create, deriveHmacKeySecret, randomInt, CappedMap } from 'altcha-lib/frameworks/nextjs';
import { deriveKey } from 'altcha-lib/algorithms/pbkdf2';
// Define your HMAC secret
const HMAC_SECRET = 'secret.key';
export const altcha = create({
// Verification HMAC secrets
hmacSignatureSecret: HMAC_SECRET,
hmacKeySignatureSecret: await deriveHmacKeySecret(HMAC_SECRET),
// Adjust challenge parameters
createChallengeParameters: () => {
return {
algorithm: 'PBKDF2/SHA-256',
// Adjust cost and counter depending on the algorithm
cost: 5_000,
counter: randomInt(5_000, 10_000),
expiresAt: new Date(Date.now() + 600_000), // 10 minutes
};
},
// Key derivation function for the selected algorithm
deriveKey,
// Use a cookie instead of form data to send the payload
setCookie: {
name: 'altcha',
path: '/',
},
// In distributed environments, use Redis or another shared store
store: new CappedMap<string, boolean>({
maxSize: 1_000,
}),
});
2. Challenge endpoint
File: app/altcha/challenge/route.ts
import { altcha } from '@/src/altcha';
export const GET = altcha.challengeHandler;
3. Standalone verify endpoint
File: app/altcha/verify/route.ts
import { altcha } from '@/src/altcha';
export const POST = altcha.verifyHandler;
4a. Protect a route handler with withMiddleware
File: app/protected/route.ts
import { NextResponse } from 'next/server';
import { altcha } from '@/src/altcha';
import type { AltchaResult } from '@/src/altcha';
export const POST = altcha.withMiddleware(async (req: Request) => {
return NextResponse.json({
altcha: (req as { __altcha: AltchaResult }).__altcha,
});
});
4b. Use middleware in Next.js middleware.ts
File: middleware.ts
import { NextRequest, NextResponse } from 'next/server';
import { altcha } from '@/src/altcha';
export async function middleware(req: NextRequest) {
if (req.method === 'POST') {
const result = await altcha.middleware(req);
if (result instanceof Response) {
return result; // verification failed — 403 response
}
}
return NextResponse.next();
}
export const config = {
matcher: ['/api/protected/:path*'],
};