Broken Authentication Explained: When Login Security Fails

TL;DR

Broken authentication covers any weakness in login and session management. The two problems we see most in scans: no rate limiting on the login endpoint, and passwords stored as plain text or hashed with MD5. Use an established auth provider if you can (Clerk, Auth0, Supabase Auth). If you're building your own, bcrypt for passwords and a rate limiter on every login endpoint are non-negotiable. Make sure logout actually invalidates sessions server-side too.

What Is Broken Authentication?

Broken authentication is a category of vulnerabilities in how users log in and stay logged in. When it's broken, attackers can:

  • Guess or brute-force passwords without rate limiting
  • Steal or hijack session tokens
  • Bypass authentication entirely
  • Take over accounts through password reset flaws
  • Access accounts through credential stuffing (using leaked passwords)

Common Authentication Vulnerabilities

1. Weak Password Storage

DANGEROUS: Storing passwords wrong
// CRITICAL VULNERABILITY: Plain text passwords
const user = await db.user.create({
  data: { email, password }  // Password stored as-is!
});

// WRONG: Using weak hashing
const hash = crypto.createHash('md5').update(password).digest('hex');

// WRONG: Missing salt
const hash = crypto.createHash('sha256').update(password).digest('hex');
SECURE: Proper password hashing
import bcrypt from 'bcrypt';

// Hashing a password (on signup)
const saltRounds = 12;
const hashedPassword = await bcrypt.hash(password, saltRounds);

// Verifying a password (on login)
const isValid = await bcrypt.compare(password, user.hashedPassword);

2. No Rate Limiting

Without rate limiting, attackers can try thousands of passwords:

Vulnerable endpoint
// Attacker can send unlimited login attempts
app.post('/api/login', async (req, res) => {
  const { email, password } = req.body;
  const user = await authenticate(email, password);
  // No rate limiting = brute force attacks work
});
Protected with rate limiting
import rateLimit from 'express-rate-limit';

const loginLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 5, // 5 attempts per window
  message: 'Too many login attempts, please try again later',
  standardHeaders: true,
  legacyHeaders: false,
});

app.post('/api/login', loginLimiter, async (req, res) => {
  // Now protected against brute force
});

3. Insecure Session Management

VulnerabilityProblemFix
Predictable session IDsAttackers can guess valid sessionsUse cryptographically random IDs
Session in URLToken leaked in browser history, referrerUse HttpOnly cookies
No session expirationStolen sessions work foreverSet reasonable expiration times
Session not invalidated on logoutOld sessions still workDestroy session server-side on logout

4. Insecure Password Reset

Vulnerable password reset
// VULNERABLE: Predictable reset tokens
const resetToken = Date.now().toString();
const resetUrl = `/reset?token=${resetToken}`;

// VULNERABLE: No expiration
await db.passwordReset.create({
  data: { userId: user.id, token: resetToken }
  // Missing: expiresAt field
});

// VULNERABLE: Token not invalidated after use
// User could reuse the same reset link
Secure password reset
import crypto from 'crypto';

// Generate secure random token
const resetToken = crypto.randomBytes(32).toString('hex');
const tokenHash = crypto.createHash('sha256').update(resetToken).digest('hex');

// Store hashed token with expiration
await db.passwordReset.create({
  data: {
    userId: user.id,
    tokenHash,
    expiresAt: new Date(Date.now() + 3600000) // 1 hour
  }
});

// After password reset, delete the token
await db.passwordReset.delete({ where: { tokenHash } });

Authentication in AI-Generated Code

AI-generated auth code tends to repeat the same mistakes:

Common AI auth mistakes: Plain-text password storage, missing rate limiting, and JWT tokens with no expiration. Hardcoded secrets and missing CSRF protection show up constantly too. These aren't edge cases in vibe-coded apps.

What AI might generate
// AI often generates vulnerable auth patterns
app.post('/login', async (req, res) => {
  const user = await db.user.findFirst({
    where: {
      email: req.body.email,
      password: req.body.password  // Plain text comparison!
    }
  });

  if (user) {
    res.cookie('userId', user.id);  // Insecure cookie!
    res.json({ success: true });
  }
});

Better Approach: Use Auth Providers

In most cases, an established auth service beats building your own:

ProviderBest ForFeatures
ClerkNext.js apps, fast setupPre-built components, MFA, social login
Auth0Enterprise, complex requirementsSSO, compliance features, custom rules
Supabase AuthFull-stack with SupabaseRow-level security integration, social login
NextAuth.jsSelf-hosted, flexibleMany providers, database sessions

Auth providers handle: Password hashing, rate limiting, secure sessions, MFA, and password reset flows. All the things that are easy to get slightly wrong. You focus on your app.

What is broken authentication?

Broken authentication refers to weaknesses in login and session management that let attackers compromise passwords, keys, or session tokens. This can lead to account takeover, unauthorized access, or identity impersonation.

Should I build my own authentication system?

For most apps, no. Established providers like Clerk, Auth0, or NextAuth handle password hashing, session management, and brute-force protection. Rolling your own is only worth it if you have specific constraints those services can't meet.

Is storing passwords in plain text really that bad?

Yes, it's critical. If your database is breached, attackers get everyone's passwords immediately. Since users reuse passwords, this often leads to account compromises on other sites. Always hash passwords with bcrypt, argon2, or scrypt.

What's the difference between authentication and authorization?

Authentication verifies who you are (login). Authorization determines what you can do (permissions). Both are important: broken authentication lets attackers log in; broken authorization lets logged-in users access things they shouldn't.

Check Your Authentication Security

Our scanner tests your login system for common vulnerabilities.

Vulnerability Guides

Broken Authentication Explained: When Login Security Fails