Skip to main content
verifySolution() is called server-side in your verify endpoint to check that the browser correctly solved all challenge tokens. It checks the JWT signature, token expiry, the SHA-256 hash proof, and (by default) prevents token reuse.

Import

import { verifySolution } from 'ribaunt';

Signature

function verifySolution(
  token: ChallengeToken | ChallengeToken[],
  nonce: number | string | Array<number | string> | ChallengeSolution | ChallengeSolution[],
  options?: VerifySolutionOptions
): Promise<boolean>

Parameters

token
ChallengeToken | ChallengeToken[]
required
The original JWT token(s) returned by createChallenge(). Pass the same tokens your challenge endpoint issued.
nonce
number | string | Array<number | string> | ChallengeSolution | ChallengeSolution[]
required
The solution(s) submitted by the browser. Can be:
  • A single nonce string
  • An array of nonce strings
  • A ChallengeSolution object { nonce: string; hash: string }
  • An array of ChallengeSolution objects (what the widget sends as solutions)
options
VerifySolutionOptions
Optional configuration object. See the options table below.

Options

OptionTypeDefaultDescription
replayPrevention'local' | 'remote' | 'disabled''local'Controls how token reuse is prevented
replayStoreReplayStoreundefinedRequired when replayPrevention is 'remote'. Implements the consume() method.
debugbooleanauto (true in dev)Log verification warnings to console
onWarning(warning: VerifyWarning) => voidundefinedCallback for structured warning events

Return value

Promise<boolean>. Returns true only if ALL tokens have valid solutions and pass replay checks. Returns false for any failure without throwing.

Examples

Basic usage:
const { tokens, solutions } = req.body;
const valid = await verifySolution(tokens, solutions);
With structured warning telemetry:
const valid = await verifySolution(tokens, solutions, {
  onWarning: (warning) => {
    // warning.reason: 'invalid-token' | 'expired-token' | 'invalid-solution' | 'replay-detected' | 'configuration-error'
    console.log('captcha warning', warning.reason, warning.message);
  },
});
With remote replay store:
const valid = await verifySolution(tokens, solutions, {
  replayPrevention: 'remote',
  replayStore: {
    consume: async (jti, expiresAt) => {
      // Atomic set-if-not-exists with TTL (e.g. Redis SET NX EX)
      return await redis.set(jti, '1', 'NX', 'EXAT', expiresAt) !== null;
    },
  },
});

Warning reasons

ReasonDescription
invalid-tokenJWT signature is invalid or malformed
expired-tokenChallenge TTL has passed
invalid-solutionThe nonce does not produce a valid hash
replay-detectedToken was already consumed
configuration-errorreplayPrevention is 'remote' but no replayStore provided