> ## Documentation Index
> Fetch the complete documentation index at: https://captcha.ribaunt.com/llms.txt
> Use this file to discover all available pages before exploring further.

# verifySolution

> verifySolution() validates JWT signatures, hash proofs, expiry, replay protection, and optional context binding. Returns a structured result object.

`verifySolution()` is called server-side in your verify endpoint to check that the browser correctly solved all challenge tokens. It validates the JWT signature, token expiry, the SHA-256 hash proof, replay state, and optional context binding.

## Import

```ts theme={null}
import { verifySolution } from 'ribaunt';
```

## Signature

```ts theme={null}
function verifySolution(
  token: ChallengeToken | ChallengeToken[],
  nonce: number | string | Array<number | string> | ChallengeSolution | ChallengeSolution[],
  options?: VerifySolutionOptions
): Promise<VerifySolutionResult>
```

## Parameters

<ParamField path="token" type="ChallengeToken | ChallengeToken[]" required>
  The original JWT token(s) returned by `createChallenge()`. Pass the same tokens your challenge endpoint issued.
</ParamField>

<ParamField path="nonce" type="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`)
</ParamField>

<ParamField path="options" type="VerifySolutionOptions">
  Optional configuration object. See the options table below.
</ParamField>

## Options

| Option             | Type                                | Default                    | Description                                                                 |
| ------------------ | ----------------------------------- | -------------------------- | --------------------------------------------------------------------------- |
| `replayPrevention` | `'local' \| 'remote' \| 'disabled'` | `'local'`                  | Controls how token reuse is prevented                                       |
| `replayStore`      | `ReplayStore`                       | `undefined`                | Required when `replayPrevention` is `'remote'`                              |
| `context`          | `string`                            | `undefined`                | Requires the same context value that was used when the challenge was issued |
| `debug`            | `boolean`                           | auto (true in development) | Logs verification warnings to the console                                   |
| `onWarning`        | `(warning: VerifyWarning) => void`  | `undefined`                | Callback for structured warning events                                      |

## Return value

`Promise<VerifySolutionResult>`. The function returns either:

```ts theme={null}
{ valid: true }
```

or

```ts theme={null}
{ valid: false, reason, message }
```

## Examples

Basic usage:

```ts theme={null}
const { tokens, solutions } = req.body;
const result = await verifySolution(tokens, solutions);

if (!result.valid) {
  return res.status(400).json({ success: false, error: result.reason });
}
```

With structured warning telemetry:

```ts theme={null}
const result = await verifySolution(tokens, solutions, {
  context: 'signup',
  onWarning: (warning) => {
    // warning.reason: 'invalid-token' | 'expired-token' | 'invalid-solution' | 'context-mismatch' | 'replay-detected' | 'configuration-error'
    console.log('captcha warning', warning.reason, warning.message);
  },
});
```

With a remote replay store:

```ts theme={null}
const result = await verifySolution(tokens, solutions, {
  replayPrevention: 'remote',
  replayStore: {
    consume: async (jti, expiresAt) => {
      // Atomic set-if-not-exists with TTL (e.g. Redis SET NX EXAT)
      return await redis.set(jti, '1', 'NX', 'EXAT', expiresAt) !== null;
    },
  },
});
```

## Warning reasons

| Reason                | Description                                                        |
| --------------------- | ------------------------------------------------------------------ |
| `invalid-token`       | JWT signature is invalid or malformed                              |
| `expired-token`       | Challenge TTL has passed                                           |
| `invalid-solution`    | The nonce does not produce a valid hash                            |
| `context-mismatch`    | The supplied context does not match the token's bound context      |
| `replay-detected`     | Token was already consumed                                         |
| `configuration-error` | `replayPrevention` is `'remote'` but no `replayStore` was provided |
