API Security Best Practices: Authentication, Rate Limiting, and Input Validation

APIs are the backbone of modern web applications, connecting frontend clients, third-party integrations, microservices, and internal tools. Each exposed endpoint is a potential entry point for attackers, and API breaches now account for the majority of data exposure incidents.
API Key Lifecycle Management
Static API keys are the simplest authentication mechanism but require careful lifecycle management. Keys should be generated using cryptographically secure randomness, stored as hashed values in the database, and never logged or included in source code.
import crypto from 'crypto';
function createApiKey(): { prefix: string; hashedKey: string; rawKey: string } {
const rawKey = `sn_${crypto.randomBytes(32).toString('hex')}`;
const prefix = rawKey.substring(0, 8); // Store prefix for identification
const hashedKey = crypto
.createHash('sha256')
.update(rawKey)
.digest('hex');
return { prefix, hashedKey, rawKey };
}
Return the full key only once during creation. Allow multiple active keys per user or service so expiration can happen without disruption. Implement key rotation policies — revoke old keys after 90 days and force regeneration.
OAuth 2.0 Token Validation for APIs
When accepting OAuth 2.0 bearer tokens, the API must validate the token on every request. For self-issued JWTs, verify the signature using the issuer's public key, check the exp claim, and validate the aud (audience) against the expected resource server.
import jwt from 'jsonwebtoken';
import jwksClient from 'jwks-rsa';
const client = jwksClient({ jwksUri: 'https://auth.example.com/.well-known/jwks.json' });
async function verifyToken(token: string): Promise<JwtPayload> {
const getKey = (header: jwt.JwtHeader, cb: jwt.SigningKeyCallback) => {
client.getSigningKey(header.kid!, (err, key) => {
cb(err, key?.getPublicKey());
});
};
return new Promise((resolve, reject) => {
jwt.verify(token, getKey, {
algorithms: ['RS256'],
audience: 'api.soninow.com',
issuer: 'https://auth.soninow.com',
}, (err, decoded) => {
if (err) reject(err);
else resolve(decoded as JwtPayload);
});
});
}
For third-party token validation (Google, GitHub, Microsoft), use the provider's token introspection endpoint rather than reimplementing JWKS fetching. Provider-issued tokens have different claim structures and key rotation schedules.
Rate Limiting Strategies
Rate limiting protects APIs from abuse, brute force attacks, and unintentional traffic spikes. Implement multiple tiers of rate limiting — global, per-endpoint, and per-user — with decreasing thresholds.
import rateLimit from 'express-rate-limit';
import RedisStore from 'rate-limit-redis';
const apiLimiter = rateLimit({
store: new RedisStore({ client: redisClient }),
windowMs: 60 * 1000, // 1 minute
max: 100,
standardHeaders: true,
legacyHeaders: false,
message: { error: 'Too many requests', retryAfter: 60 },
});
app.use('/api/', apiLimiter);
// Stricter limit for auth endpoints
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5,
skipSuccessfulRequests: true,
});
app.use('/api/auth/login', authLimiter);
Return 429 Too Many Requests with a Retry-After header. Use Redis or another distributed store for rate limit state so limits apply consistently across all application instances. Consider token bucket or sliding window algorithms over fixed window counters to avoid burst traffic at window boundaries.
Input Validation at the API Gateway
Validate every field of every request before it reaches your application logic. Define schemas for all endpoints and reject malformed payloads at the earliest possible point.
import { z } from 'zod';
const CreateUserSchema = z.object({
email: z.string().email().max(255),
name: z.string().min(1).max(100).trim(),
role: z.enum(['user', 'admin', 'viewer']).default('user'),
metadata: z.record(z.string(), z.unknown()).optional(),
});
app.post('/api/users', (req, res) => {
const result = CreateUserSchema.safeParse(req.body);
if (!result.success) {
return res.status(422).json({
error: 'Validation failed',
details: result.error.flatten().fieldErrors,
});
}
// Process validated data
});
Reject unexpected fields, enforce length limits on all string inputs, and validate numeric ranges. SQL injection and NoSQL injection are prevented at this layer by rejecting input types that don't match the schema.
CORS Configuration
Cross-Origin Resource Sharing is frequently misconfigured. Restrict Access-Control-Allow-Origin to specific origins rather than using * for credentialed requests. Validate the Origin header against a whitelist rather than echoing it back.
const corsOptions = {
origin: [
'https://app.soninow.com',
'https://admin.soninow.com',
],
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
allowedHeaders: ['Content-Type', 'Authorization', 'X-Request-Id'],
exposedHeaders: ['X-RateLimit-Remaining'],
credentials: true,
maxAge: 86400,
};
Request Signing for Server-to-Server Communication
For internal microservice communication, HMAC-based request signing provides integrity and authenticity. The sender signs the request body plus headers using a shared secret, and the receiver verifies the signature before processing.
function signRequest(payload: string, secret: string): string {
return crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
}
Include a timestamp in the signature and reject requests with more than five minutes of clock skew to prevent replay attacks.
APIs are the most attacked surface area in modern applications. Our <a href="/services/web-development">API development services</a> embed security into every endpoint from design through deployment. Contact SoniNow to audit your API security posture.
Related Insights

API Rate Limiting Strategies: Token Bucket, Leaky Bucket, and Sliding Window
A guide to implementing API rate limiting including token bucket, leaky bucket, sliding window, and distributed rate limiting with Redis for production APIs.

Authentication Patterns in Modern Web Apps: JWT, OAuth, and Session Management
A guide to authentication patterns for web applications including JWT implementation, OAuth 2.0 flows, refresh tokens, session management, and secure storage.

Authentication Patterns in Modern Web Apps: JWT, Sessions, and Passkeys
A guide to modern authentication patterns comparing JWT, session-based auth, and passkeys including implementation strategies, security considerations, and user experience.