Captcha
Endpoints for generating and verifying captcha challenges.
GET /api/captcha
Generate a captcha image.
Authentication: Not required
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
theme | string | light | Theme: light or dark |
format | string | — | json returns { svg, token } instead of raw SVG (for cross-origin clients like Informer GO; pass the token back on verify) |
Response:
SVG image (Content-Type: image/svg+xml) by default, or
{ "svg": "<svg ...>", "token": "<encrypted answer>" } with format=json.
Cookie Set:
In the default (cookie) mode, sets an encrypted captcha cookie
containing the expected answer. The format=json mode sets no cookie;
the encrypted answer travels as token instead.
Example Request:
curl https://app.example.com/api/captcha?theme=dark
Response:
<svg xmlns="http://www.w3.org/2000/svg" width="160" height="55">
<!-- SVG captcha image -->
</svg>
Captcha Configuration:
- Size: 4 characters
- Ignored Characters:
0oO1iIlLqQgG9S5sZz2(to avoid confusion) - Noise: Level 1
- Color: Yes (colored characters)
- Background:
- Light theme:
#fff - Dark theme:
#202125
- Light theme:
- Font Size: 45px
- Dimensions: 160x55 pixels
POST /api/captcha/_verify
Verify a captcha response.
Authentication: Not required
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
captcha | string | Yes | User's captcha answer |
token | string | No | The token from a format=json captcha (cookie mode omits this) |
Example Request:
{
"captcha": "ABC7"
}
Response (Success):
{
"success": true
}
Response (Failure): 400 Bad Request with message Invalid captcha
(there is no valid: false body).
Validation:
- Compares user input against the decrypted
tokenpayload field, falling back to thecaptchacookie - Case-insensitive comparison
- Clears the captcha cookie after verification (when one was used)
Captcha Flow
1. Request Captcha Image
GET /api/captcha?theme=light
Response: SVG image + encrypted cookie
2. Display Image to User
Render the SVG image in the UI and provide an input field.
3. Submit User Response
POST /api/captcha/_verify
{
"captcha": "ABC7"
}
Response: { "success": true }, or 400 Bad Request on a wrong answer.
4. Handle Result
- On
200, proceed with the protected action (e.g., login, registration) - On
400, request a new captcha and ask the user to try again
Integration Examples
Login Form with Captcha
// 1. Load captcha on page load
async function loadCaptcha() {
const response = await fetch('/api/captcha?theme=dark');
const svg = await response.text();
document.getElementById('captcha-image').innerHTML = svg;
}
// 2. Verify captcha before login
async function login(username, password, captchaResponse) {
// First verify captcha
const verification = await fetch('/api/captcha/_verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ captcha: captchaResponse })
});
if (!verification.ok) {
alert('Invalid captcha');
loadCaptcha(); // Reload captcha
return;
}
// Proceed with login
const loginResponse = await fetch('/api/login/local', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password })
});
// Handle login response...
}
Registration Form
async function register(userData, captchaResponse) {
// Verify captcha first
const verifyResp = await fetch('/api/captcha/_verify', {
method: 'POST',
body: JSON.stringify({ captcha: captchaResponse })
});
if (!verifyResp.ok) {
throw new Error('Invalid captcha');
}
// Proceed with registration...
}
Use Cases
Prevent Automated Attacks
- Brute Force Login - Rate limit login attempts with captcha
- Account Enumeration - Prevent automated user discovery
- Spam Registration - Block automated account creation
Security Triggers
Captcha can be selectively required:
- After N failed login attempts
- For registration from untrusted IPs
- For password reset requests
- When unusual activity is detected
Security Considerations
Cookie Encryption
The captcha answer is encrypted before being stored in the cookie to prevent:
- Client-side tampering
- Cookie inspection revealing the answer
Single-Use
Captcha verification consumes the cookie. Each captcha image can only be verified once.
Case Insensitivity
Verification is case-insensitive to improve usability while maintaining security.
Accessibility
Consider providing:
- Audio captcha alternative
- Option to refresh captcha
- Clear error messages
Only require captcha when necessary (e.g., after failed attempts) to maintain good UX while preventing abuse.
The character set excludes commonly confused characters (0oO1iIlLqQgG9S5sZz2) to reduce user frustration.