Api Rate Abuse with Openid Connect
How Api Rate Abuse Manifests in Openid Connect
Rate abuse in OpenID Connect implementations often exploits the authentication and token exchange endpoints. The authorization endpoint (/authorize) becomes a primary target when attackers attempt credential stuffing or brute force attacks against user accounts. Since OpenID Connect uses the OAuth 2.0 authorization code flow, each failed authentication attempt still consumes server resources and may trigger rate limiting on the authorization server.
The token endpoint (/token) presents another critical attack surface. Attackers can flood this endpoint with invalid authorization codes, malformed client credentials, or excessive refresh token requests. Each of these attacks has distinct signatures: invalid authorization codes return invalid_grant errors, while malformed client credentials return invalid_client errors. The server still processes each request, consuming CPU cycles for validation, database lookups for client verification, and cryptographic operations for token generation.
OpenID Connect's discovery endpoint (/.well-known/openid-configuration) can also be abused. Since this endpoint returns server metadata including supported scopes, response types, and grant types, attackers can use it to map the authorization server's capabilities before launching targeted attacks. Without proper rate limiting, this endpoint can be used for reconnaissance at scale.
The userinfo endpoint (/userinfo) becomes vulnerable when rate limits are not applied consistently across the authentication flow. An attacker might successfully authenticate through the authorization code flow, then flood the userinfo endpoint with access tokens obtained from a single successful login. Since the userinfo endpoint validates access tokens and retrieves user claims, each request involves database queries and potentially expensive claims aggregation logic.
Code example showing vulnerable OpenID Connect implementation:
app.post('/token', async (req, res) => {
const { client_id, client_secret, code, grant_type } = req.body;
// NO RATE LIMITING - vulnerable to abuse
const client = await getClient(client_id);
if (!client || client.secret !== client_secret) {
return res.status(401).json({ error: 'invalid_client' });
}
if (grant_type === 'authorization_code') {
const authCode = await getAuthCode(code);
if (!authCode || authCode.expired) {
return res.status(400).json({ error: 'invalid_grant' });
}
const tokens = generateTokens(authCode.sub);
return res.json(tokens);
}
return res.status(400).json({ error: 'unsupported_grant_type' });
});Openid Connect-Specific Detection
Detecting rate abuse in OpenID Connect requires monitoring specific patterns across the authentication flow. The authorization endpoint shows distinct signatures when under attack: sudden spikes in login_required errors, increased invalid_request responses for malformed requests, and unusual geographic distribution of client IP addresses. Since OpenID Connect uses state parameters to prevent CSRF, monitoring for repeated state parameter reuse can indicate automated attacks.
The token endpoint exhibits characteristic patterns during rate abuse. Look for: sudden increases in invalid_grant errors (invalid authorization codes), invalid_client errors (bad client credentials), and unsupported_grant_type errors. The timing patterns matter too—legitimate clients typically space out token requests, while attackers often send bursts of requests in rapid succession.
OpenID Connect's introspection endpoint (/introspect) for token validation can also be abused. Without rate limiting, attackers can continuously validate stolen or guessed tokens to determine which ones are active. Monitoring the ratio of active versus inactive token introspections helps identify reconnaissance attempts.
middleBrick's OpenID Connect-specific scanning identifies rate abuse vulnerabilities by testing the unauthenticated attack surface. The scanner sends legitimate-looking OpenID Connect requests to your authorization and token endpoints, measuring response times, error rates, and identifying missing rate limiting. It specifically tests for:
- Authorization endpoint rate limiting (preventing credential stuffing)
- Token endpoint rate limiting (preventing code reuse attacks)
- Discovery endpoint rate limiting (preventing reconnaissance)
- Consistent rate limiting across all OpenID Connect endpoints
- IP-based rate limiting effectiveness
- Client ID-based rate limiting
Code example for implementing detection middleware:
const rateLimit = require('express-rate-limit');
// OpenID Connect-specific rate limiting
const oidcAuthLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // limit each IP to 5 auth requests
message: {
error: 'Too many authentication attempts',
openid_connect_error: 'too_many_attempts',
error_description: 'Too many authentication attempts from this IP'
},
standardHeaders: true,
legacyHeaders: false,
skipSuccessfulRequests: false // log successful attempts too
});
app.use('/authorize', oidcAuthLimiter);
app.use('/token', oidcAuthLimiter);
app.use('/introspect', oidcAuthLimiter);
app.use('/userinfo', oidcAuthLimiter);Openid Connect-Specific Remediation
Remediating rate abuse in OpenID Connect requires implementing defense-in-depth across all endpoints. Start with the authorization endpoint by implementing exponential backoff for failed authentications. After each failed attempt, increase the delay before allowing another attempt from the same IP or user account. OpenID Connect's state parameter should be validated against a server-side store, and repeated invalid state parameters should trigger temporary blocks.
For the token endpoint, implement client-specific rate limits based on the client_id. Different OAuth 2.0 client types (confidential vs public clients) should have different rate limits. Confidential clients with client secrets can have higher limits than public clients using PKCE. Store rate limit counters in a distributed cache like Redis to handle load-balanced deployments.
OpenID Connect's session management features can help mitigate rate abuse. Implement idle session timeouts and enforce re-authentication after configurable periods. When rate limits are triggered, return OpenID Connect-compliant error responses with appropriate error codes like too_many_attempts or access_denied with custom error descriptions.
Code example for comprehensive OpenID Connect rate limiting:
const RedisStore = require('rate-limit-redis');
const { Issuer, generators } = require('openid-client');
// OpenID Connect-aware rate limiting with client-specific policies
const clientRateLimiter = rateLimit({
store: new RedisStore({
client: redisClient,
prefix: 'oidc_client_'
}),
keyGenerator: (req) => req.body.client_id || req.ip,
windowMs: 60 * 1000, // 1 minute windows
max: (req) => {
const clientId = req.body.client_id;
const client = getClientFromRegistry(clientId);
if (!client) return 5; // unknown clients get minimal limits
// Different limits based on client type
if (client.client_type === 'confidential') {
return 100; // confidential clients get higher limits
} else if (client.client_type === 'public') {
return 20; // public clients get lower limits
}
return 10; // default
},
message: {
error: 'Rate limit exceeded',
openid_connect_error: 'rate_limit_exceeded',
error_description: 'Too many requests from this client'
},
standardHeaders: true,
legacyHeaders: false
});
// Apply to OpenID Connect endpoints
app.use('/authorize', clientRateLimiter);
app.use('/token', clientRateLimiter);
app.use('/introspect', clientRateLimiter);
app.use('/userinfo', clientRateLimiter);
// OpenID Connect-compliant error responses
app.use((err, req, res, next) => {
if (err instanceof RateLimit)
return res.status(429).json({
error: 'access_denied',
error_description: err.message,
state: req.query.state,
openid_connect_error: 'too_many_attempts'
});
next(err);
});Frequently Asked Questions
How does OpenID Connect's PKCE flow affect rate limiting strategies?
What OpenID Connect error codes should I return when rate limits are triggered?
access_denied with custom error descriptions for general rate limiting. For authentication-specific rate limiting, use login_required with appropriate error details. The OpenID Connect specification recommends returning JSON error responses with error, error_description, and optionally error_uri fields. Include state parameters in responses to maintain flow integrity.