Zuplo
API Authentication

Securing APIs Against Broken Authentication (OWASP API2)

Nate TottenNate Totten
April 1, 2025
10 min read

Learn how to prevent broken authentication vulnerabilities (OWASP API2) with JWT validation, rate limiting, MFA, and session management best practices.

Broken authentication is the second most critical API vulnerability in the OWASP API Security Top 10 (2023), and for good reason. When authentication fails, attackers walk through your front door using stolen credentials, forged tokens, or exploited implementation flaws. The consequences are severe: complete account takeover, personal data theft, and unauthorized actions that are indistinguishable from legitimate user behavior.

API authentication endpoints are uniquely exposed because they’re public by design — anyone can reach your /login or /token endpoint. That makes them prime targets for credential stuffing, brute force attacks, and token manipulation. In this guide, you’ll learn exactly how broken authentication vulnerabilities work, the most common mistakes developers make, and the concrete steps you can take to secure your APIs.

What Is Broken Authentication (OWASP API2)?

OWASP classifies broken authentication as any weakness in your API’s authentication mechanisms that allows attackers to compromise user identities. This includes flawed login flows, weak token handling, missing rate limits on authentication endpoints, and poor session management.

The key distinction from authorization vulnerabilities is scope: authentication verifies who a user is, while authorization determines what they can access. Broken authentication means an attacker can impersonate a legitimate user entirely.

OWASP describes the attack vector as easily accessible since authentication mechanisms are exposed to everyone, notes that authentication issues are prevalent due to implementation complexity, and states that detection methodologies are available and straightforward. This combination makes it one of the most actively exploited vulnerability categories in production APIs.

Why APIs Are Especially Vulnerable

Traditional web applications benefit from browser-based protections like CSRF tokens and cookie policies. APIs, on the other hand, rely entirely on tokens, keys, and headers for authentication — mechanisms that are easier to intercept, replay, and forge if improperly implemented.

Microservices architectures amplify the risk. Each service may implement its own authentication logic, creating inconsistencies that attackers exploit. A single service with a weak authentication check can compromise your entire system.

Common Mistakes That Lead to Broken Authentication

These are the most frequently exploited authentication flaws in production APIs. If any of these patterns exist in your codebase, they need immediate attention.

1. Using jwt.decode() Instead of jwt.verify()

This is the single most dangerous JWT mistake. Calling decode() only parses the token payload — it never checks the signature. An attacker can craft any payload they want, and your API will trust it.

TypeScripttypescript
// VULNERABLE: Decodes without verifying the signature
function authenticateRequest(token: string) {
  const decoded = jwt.decode(token);
  if (decoded) {
    return { userId: decoded.sub }; // Attacker controls this value
  }
  return null;
}

// SECURE: Verifies signature and enforces algorithm
function authenticateRequest(token: string) {
  try {
    const decoded = jwt.verify(token, publicKey, {
      algorithms: ["RS256"], // Explicitly allow only RS256
      audience: "https://api.example.com",
      issuer: "https://auth.example.com",
    });
    return { userId: decoded.sub };
  } catch (err) {
    return null; // Invalid token — reject the request
  }
}

The none algorithm attack is a variant of this mistake. If your JWT library accepts alg: "none" in the token header, an attacker can strip the signature entirely and forge arbitrary tokens.

With an API gateway like Zuplo, you can avoid implementing JWT validation in your application code entirely. Zuplo’s JWT Authentication policy validates token signatures, checks expiration, and verifies claims at the gateway level — before requests ever reach your backend.

2. No Rate Limiting on Authentication Endpoints

Without rate limiting on /login, /token, or /password-reset endpoints, attackers can run credential stuffing attacks at scale. They’ll try thousands of stolen username/password combinations per minute until they find valid credentials.

OWASP specifically calls out that anti-brute-force mechanisms on authentication endpoints should be stricter than general API rate limiting.

TypeScripttypescript
// Example: Rate limiting configuration for auth endpoints
// In Zuplo, you can apply rate limiting per-route with different limits
{
  "name": "auth-rate-limit",
  "policyType": "rate-limit-inbound",
  "handler": {
    "export": "RateLimitInboundPolicy",
    "module": "$import(@zuplo/runtime)",
    "options": {
      "rateLimitBy": "ip",
      "requestsAllowed": 10,
      "timeWindowMinutes": 5
    }
  }
}

Zuplo’s rate limiting uses a sliding window algorithm enforced globally across all edge locations, so attackers can’t bypass limits by distributing requests across regions.

3. Weak Password Reset Flows

Password reset endpoints are authentication endpoints — they deserve the same security scrutiny as login. Common flaws include:

  • Predictable reset tokens: Using sequential IDs or timestamps instead of cryptographically random values
  • No expiration on reset tokens: Tokens that remain valid indefinitely give attackers unlimited time
  • Missing identity verification: Allowing email changes or password resets without confirming the current password
  • No rate limiting: Letting attackers enumerate reset tokens by brute force
TypeScripttypescript
// VULNERABLE: Predictable reset token
function generateResetToken(userId: string) {
  return Buffer.from(`${userId}-${Date.now()}`).toString("base64");
  // Attacker can guess this pattern
}

// SECURE: Cryptographically random token with expiration
import { randomBytes } from "crypto";

function generateResetToken(userId: string) {
  const token = randomBytes(32).toString("hex");
  storeResetToken(userId, token, {
    expiresAt: Date.now() + 15 * 60 * 1000, // 15 minutes
    used: false,
  });
  return token;
}

4. Transmitting Credentials Over Unencrypted Channels

Sending API keys, passwords, or tokens over HTTP (without TLS) exposes them to anyone monitoring network traffic. This is especially dangerous for Basic Authentication, which Base64-encodes credentials in every request header.

TypeScripttypescript
// VULNERABLE: No TLS enforcement
app.get("/api/user", (req, res) => {
  const authHeader = req.headers.authorization;
  if (validateBasicAuth(authHeader)) {
    return res.json({ data: "sensitive information" });
  }
  res.status(401).send("Unauthorized");
});

// SECURE: Require HTTPS for all authenticated requests
app.use((req, res, next) => {
  if (!req.secure && req.headers["x-forwarded-proto"] !== "https") {
    return res.status(403).json({ error: "HTTPS required" });
  }
  next();
});

When you deploy your API behind Zuplo, all traffic is TLS-encrypted by default across every edge location — no additional configuration required.

5. Long-Lived Tokens Without Revocation

Access tokens with expiration times measured in days or weeks create a large attack window. If a token is compromised, the attacker has unrestricted access until it expires. The problem gets worse when there’s no revocation mechanism to invalidate a stolen token immediately.

Follow token expiry best practices by keeping access tokens short-lived (15–60 minutes) and implementing refresh token rotation.

6. Improper Session Invalidation

Clearing session data on the client side during logout isn’t enough. If the server-side session remains valid, an attacker with a captured session ID can continue accessing the API.

python
# VULNERABLE: Only clears client-side session
@app.route('/logout')
def logout():
    session.clear()
    return redirect(url_for('login'))

# SECURE: Invalidates server-side session, then clears client
@app.route('/logout')
def logout():
    session_id = request.cookies.get('session_id')
    invalidate_session_in_database(session_id)
    session.clear()
    response = redirect(url_for('login'))
    response.delete_cookie('session_id')
    return response

Building a Defense Against Broken Authentication

Fixing individual mistakes isn’t enough — you need a layered defense strategy that addresses authentication at every level.

Implement Multi-Factor Authentication

MFA is one of the most effective defenses against broken authentication. Even if an attacker obtains valid credentials through phishing or a data breach, they still need the second factor to authenticate. Implement MFA for all sensitive operations, administrative access, and high-risk actions like changing email addresses or resetting passwords.

For more on implementing MFA in your APIs, see our guide on securing APIs with two-factor authentication.

Use Standard Authentication Protocols

Don’t build custom authentication. Rely on battle-tested protocols:

  • OAuth 2.0 and OpenID Connect for delegated authorization and user authentication. See our guide on securing your API with OAuth
  • JWT with proper validation for stateless authentication — always verify signatures, check expiration, and validate issuer and audience claims. Our JWT authentication guide covers the implementation details
  • API keys for service-to-service authentication, not end-user authentication. Understand the tradeoffs in our API authentication methods comparison

Zuplo supports all of these methods through built-in authentication policies. The JWT Auth policy works with any OpenID-compliant identity provider — Auth0, Okta, Azure AD, Cognito, Firebase, Supabase, and more. For API key authentication, Zuplo provides built-in API key management with key rotation support and consumer-level metadata.

Enforce Strict Rate Limiting on Auth Endpoints

Authentication endpoints need tighter rate limits than your general API. Consider implementing:

  • IP-based rate limiting: Limit login attempts per IP address to prevent distributed brute force attacks
  • Account-based rate limiting: Track failed attempts per account and enforce progressive lockouts
  • CAPTCHA challenges: Add CAPTCHA after repeated failures to block automated attacks
  • Password reset protection: Apply the same rate limiting to /forgot-password and /reset-password endpoints

Zuplo’s rate limiting policies let you configure different limits per route, so your authentication endpoints can have stricter thresholds than your data endpoints. The complex rate limiting policy supports multiple named counters when you need fine-grained control.

For a deeper understanding of rate limiting strategies, read our API rate limiting guide.

Validate Tokens Comprehensively

Every JWT validation check should verify:

  1. Signature: Confirm the token was signed by a trusted issuer
  2. Algorithm: Explicitly allow only expected algorithms (never accept none)
  3. Expiration (exp): Reject expired tokens
  4. Not Before (nbf): Reject tokens used before their valid period
  5. Issuer (iss): Confirm the token comes from your identity provider
  6. Audience (aud): Verify the token was intended for your API

Skipping any of these checks opens a vulnerability. Zuplo’s JWT Auth policy handles all of these validations automatically, so you don’t need to implement them in your application code.

Secure Token Storage and Transport

Where and how you store tokens matters as much as how you validate them:

  • Never store tokens in localStorage — it’s accessible to any JavaScript on the page via XSS attacks
  • Use HTTP-only cookies with Secure, SameSite, and appropriate Path flags for browser-based applications
  • Implement token binding — tie tokens to specific client contexts (IP ranges, device fingerprints) to prevent replay attacks
  • Rotate refresh tokens — issue a new refresh token with every use and invalidate the previous one

Monitor Authentication Activity

Detection is as important as prevention. Track and alert on:

  • Unusual spikes in failed authentication attempts
  • Successful logins from unexpected geolocations
  • Multiple accounts accessed from the same IP
  • Token usage patterns that suggest replay attacks

Authentication in Microservices Architectures

Microservices create unique authentication challenges. When your application is split across dozens of services, each with its own API, managing authentication consistently becomes significantly harder.

The Propagation Problem

In a monolithic application, authentication happens once at the front door. Microservices need authentication at every service boundary. The common approach is identity propagation — passing a validated JWT through the service chain so each service can verify the caller’s identity without requiring re-authentication.

The risk emerges when internal services skip authentication because they assume only trusted services will call them. A single compromised service then becomes a gateway to your entire system. Apply zero-trust principles to internal communication: every service interaction should require authentication, regardless of network location.

Centralized vs. Distributed Validation

You have two options for token validation in microservices:

  • Centralized: All services validate tokens against a central auth service. Simpler to manage but creates a single point of failure and adds latency
  • Distributed: Each service validates tokens locally using cached public keys. More resilient but requires consistent key distribution

An API gateway like Zuplo handles authentication at the edge before requests reach your services, combining the consistency of centralized validation with the performance of distributed execution. Since Zuplo runs at the edge globally, token validation happens close to the user with minimal latency.

Managing Service-to-Service Credentials

Internal services need their own authentication mechanism. Use mutual TLS (mTLS) for service-to-service communication to ensure both parties verify each other’s identity. Zuplo supports mTLS authentication for securing communication between your gateway and backend services.

Keep credentials out of your code. Use a secrets management platform like HashiCorp Vault or your cloud provider’s secrets manager to store service credentials with automatic rotation and strict access controls.

Edge Computing and Authentication

Deploying APIs on edge networks introduces additional authentication complexity. Edge nodes are distributed globally, often operate with limited resources, and may temporarily lose connectivity to central services.

Lightweight, self-contained tokens like JWTs are ideal for edge environments because they can be validated locally without round-trips to a central server. Combined with strategic authentication caching and techniques for optimizing API performance, you can maintain strong security without sacrificing latency.

An edge-native API gateway like Zuplo is purpose-built for this scenario — it validates authentication at the nearest edge location, keeping latency low while enforcing consistent security policies globally.

A Security Maturity Roadmap for API Authentication

Improving your authentication security is an incremental process. Use this roadmap to assess where you are and plan your next steps:

Foundation — Enforce HTTPS everywhere. Implement strong password policies. Store credentials using bcrypt or Argon2. Apply basic rate limiting to login endpoints.

Intermediate — Deploy OAuth 2.0 and OpenID Connect for user authentication. Implement JWTs with full signature and claims validation. Add MFA for administrative access. Implement API security best practices across all endpoints.

Advanced — Adopt context-aware authentication that adapts based on risk signals (device, location, behavior). Integrate security testing into your CI/CD pipeline. Implement token binding and refresh token rotation. Apply the complete OWASP API Security cheat sheet recommendations.

Leading — Deploy zero-trust architecture across all services. Implement real-time behavioral analytics for anomaly detection. Automate incident response for authentication-related alerts. Conduct regular API security audits.

Secure Your APIs with Zuplo

Broken authentication is preventable. The vulnerabilities outlined in OWASP API2 — weak JWT validation, missing rate limits, poor session management — all have concrete solutions. The challenge is implementing them consistently across every endpoint in your API.

Zuplo makes this straightforward. Built-in JWT authentication, rate limiting, and API key management policies deploy in minutes without writing custom authentication code. Every request is authenticated and rate-limited at the edge before it reaches your backend.

Sign up for a free Zuplo account and start securing your APIs against broken authentication today.