Bot Protection
Primus Auth Broker includes a built-in tiered rate limiter that defends local login endpoints against credential-stuffing and brute-force attacks. Optionally, it can require a CAPTCHA token after a configurable number of failed attempts.
Enable Bot Protection
Add a BotProtection section to your PrimusAuth configuration:
{
"PrimusAuth": {
"BotProtection": {
"Enabled": true,
"MaxAttemptsPerMinute": 5,
"MaxAttemptsPerHour": 30,
"ChallengeAfterFailures": 3
}
}
}
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
Enabled | bool | false | true | false | Activates bot protection on the local login endpoint. |
MaxAttemptsPerMinute | int | 5 | Positive integer | Max login attempts from one IP per 60-second window before HTTP 429. |
MaxAttemptsPerHour | int | 30 | Positive integer | Max login attempts from one IP per 60-minute window before HTTP 429. |
ChallengeAfterFailures | int | 3 | Positive integer, or 0 to disable CAPTCHA | Number of consecutive failed attempts from the same IP before a CAPTCHA token is required. Set to 0 to disable the CAPTCHA gate entirely. |
Environment Variable Override
ASP.NET Core maps __ to : so all config keys work as environment variables (e.g. PrimusAuth__BotProtection__Enabled=true, PrimusAuth__BotProtection__MaxAttemptsPerMinute=5).
How It Works
The rate limiter is keyed by the client's IP address and operates across two independent windows:
- Per-minute window — blocks the request immediately if the IP has exceeded
MaxAttemptsPerMinutein the current 60-second period. - Per-hour window — blocks the request if the IP has exceeded
MaxAttemptsPerHourin the current 60-minute period.
When either limit is exceeded the endpoint returns HTTP 429 Too Many Requests.
After ChallengeAfterFailures consecutive authentication failures from the same IP, subsequent requests must include a valid captchaToken in the login request body. The CAPTCHA requirement is cleared automatically when a login succeeds from that IP.
CAPTCHA Integration
The SDK ships built-in validators for the four most common CAPTCHA providers. Register one before AddPrimusAuthBroker — no custom implementation required:
// Google reCAPTCHA v2 ("I'm not a robot" checkbox)
builder.Services.AddPrimusRecaptchaV2(secretKey: "your-recaptcha-v2-secret");
// Google reCAPTCHA v3 (invisible, score-based — default min score: 0.5)
builder.Services.AddPrimusRecaptchaV3(secretKey: "your-recaptcha-v3-secret", minScore: 0.5);
// hCaptcha
builder.Services.AddPrimusHCaptcha(secretKey: "your-hcaptcha-secret");
// Cloudflare Turnstile
builder.Services.AddPrimusTurnstile(secretKey: "your-turnstile-secret");
builder.Services.AddPrimusAuthBroker(builder.Configuration, builder.Environment.IsDevelopment());
Store the secret key in your CI/CD secret store or dotnet user-secrets — never in source-controlled config files.
Login Request with CAPTCHA Token
When CAPTCHA is required, pass the token your frontend received from the CAPTCHA widget in the request body alongside the credentials:
{
"email": "alice@acme.com",
"password": "...",
"captchaToken": "<recaptcha-or-turnstile-response-token>"
}
The captchaToken field is ignored when the CAPTCHA threshold has not yet been reached.
Frontend Integration
After ChallengeAfterFailures consecutive failures, the login endpoint returns HTTP 403:
{
"error": "CAPTCHA required.",
"captchaRequired": true
}
Your frontend should detect captchaRequired: true and render the CAPTCHA widget before re-submitting.
Behaviour Notes
- Rate limiting runs in-memory using
ConcurrentDictionary. It is per-process — if you run multiple app instances, each instance maintains its own counters. For distributed rate limiting, replace the defaultTieredInMemoryRateLimiterwith a Redis-backed implementation. - Failure counters are reset automatically after a successful login from the same IP.
- Only the local login endpoint (
POST /api/auth/login) is subject to bot protection. OIDC and SAML flows are handled by the external identity provider.
Next Steps
- Integration Guide — complete setup walkthrough from install to first authenticated request
- Advanced Configuration — swap in
RedisRateLimiterfor distributed, multi-instance deployments - Endpoint Reference — complete endpoint documentation with request and response shapes
- Database Setup — persist user lockout state with EF Core