> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication Flow Review

> Practical checklist and audit workflow for securing authentication flows in Express.js apps, covering JWTs, sessions, refresh tokens, password resets, rate limiting, and remediation

In this lesson we review a comprehensive authentication security checklist and an example security-audit workflow applied to an Express.js login demo. The content below restructures and clarifies the original material, preserves the original diagram(s), and improves technical accuracy and example code so you can produce actionable findings, evidence, and prioritized remediation for authentication-related risks.

<Callout icon="lightbulb" color="#1CB2FE">
  This guide shows practical checks, examples, and remediation snippets for common authentication flows (JWT, sessions, refresh tokens, password resets, rate limiting, etc.). Use it as a framework for security audits and for creating automated tests that validate each control.
</Callout>

## Overview

* We used a detailed structured prompt to drive an automated security review of an Express app. The prompt enumerated specific checks and required the reviewer not to invent files or claims when context was missing.
* Goal: produce a concrete authentication security audit with structured findings (title, severity, evidence, remediation), tests, and minimally invasive fixes prioritized by impact.

## Prompt context (excerpt)

The automated reviewer was instructed to "conduct a comprehensive authentication security review" and to check the controls summarized below. If the codebase lacks evidence for a claim, the reviewer must report "insufficient context" rather than fabricate files or behavior.

<Callout icon="warning" color="#FF6B6B">
  Do not invent files, functions, or evidence. If an artifact cannot be located, the reviewer must explicitly state "insufficient context" for that check.
</Callout>

## Authentication & session controls checklist

1. Password hashing
   * Use bcrypt with salt rounds (recommendation: 12).
   * Use async APIs (`bcrypt.hash`, not sync variants); avoid double-hashing on updates.
   * Use `bcrypt.compare` for login verification.

2. Secret management (JWT and other keys)
   * Never hard-code secrets; load them from environment variables or a secret manager.
   * Use separate keys for access vs refresh tokens.
   * Prefer high-entropy symmetric keys or an asymmetric key pair with rotation.

3. Token configuration
   * Access token TTL: short (5–15 minutes recommended).
   * Refresh token TTL: moderate (7–30 days recommended).
   * Consider sliding sessions capped by a maximum session age.

4. Refresh-token implementation
   * Rotate refresh tokens on each use: issue a new token and mark the previous as revoked.
   * Detect refresh-token reuse and invalidate the associated session family.
   * Store refresh tokens hashed at rest (e.g., SHA-256).

5. Session invalidation
   * Invalidate issued tokens on password reset/change or explicit logout.

6. Brute force protection
   * Rate-limit login, reset, and verification endpoints.
   * Use progressive backoff per (username + IP) in a fast store (Redis recommended).
   * Optionally require CAPTCHA after thresholds.

7. Account enumeration defenses
   * Return generic errors and equal timing for "user not found" vs "bad password".
   * Add small jitter on failures to make timing attacks harder.

8. Password reset flow
   * Use `crypto.randomBytes(32)` (or equivalent) for reset tokens; store the hash and set a short TTL (≤ 15–30 minutes).
   * Tokens must be single-use and invalidated after successful reset.
   * Throttle reset email sending and avoid logging the raw tokens.

9. Email verification
   * Use server-side verification only with short-TTL one-time tokens.
   * Do not allow verification state to be set through untrusted inputs (prevent mass assignment).

10. Injection and auth bypass
    * Avoid user-controlled operators in NoSQL filters; sanitize queries.
    * Use parameterized queries for SQL to prevent injection.

11. Authorization (AuthZ) integrity
    * Deny-by-default access controls; load roles/permissions server-side.
    * For sensitive operations, revalidate permissions against the database rather than relying only on JWT claims.

12. Cookie & CSRF configuration (if cookies used)
    * Set `HttpOnly`, `Secure`, `SameSite` (`Lax`/`Strict` as appropriate), explicit `Max-Age`, and narrow `path`/`domain`.
    * Protect state-changing endpoints and token refresh routes from CSRF (double-submit token or ensure SameSite strategy).

13. Input validation & normalization
    * Normalize emails/usernames; enforce length and charset checks.
    * Enforce a strong password policy.
    * Use schema validation libraries such as zod, joi, or celebrate.

14. Mass assignment risks
    * Whitelist updateable fields; disallow clients from writing `role`, `emailVerified`, `passwordResetToken`, etc.

15. JWT misuse
    * Never use `jwt.decode()` for authorization decisions. Always use `jwt.verify()` with explicit `algorithms`.
    * Prefer reputable libraries like `jsonwebtoken` for signing and verifying.

16. Logging & telemetry
    * Do not log passwords, tokens, reset links, or other secrets. Use structured logs with redaction/allowlists.

17. Dependency & crypto hygiene
    * Keep crypto/auth libraries up to date; avoid weak hashes (MD5, SHA1) for password storage.
    * Use Node's `crypto` correctly as per Node.js docs.

18. Transport & CORS
    * Enforce HTTPS; restrict CORS to trusted origins (do not use wildcard credentials).

19. Open redirect / `next` param
    * Restrict post-login redirection to vetted paths or origins; default to an approved fallback.

20. Operational controls
    * Secret rotation procedures, environment separation, monitoring for suspicious auth activity, and alerts for refresh-token reuse.

## Report format (recommended)

For each finding, provide:

* Title
* Severity (Critical/High/Medium/Low)
* CWE mapping (if applicable)
* Evidence (file / function / line ranges)
* Why it matters (short)
* Exploitability notes (safe PoC where possible)
* Remediation: precise code-level fix or config change (include code snippets)
* Defense-in-depth guidance
* Summary risk score (0–10) and prioritized fixes

## Example findings and remediation snippets

The automated analysis typically flags common issues in sample projects: placeholder JWT secret, missing password hashing on registration, excessive access-token TTLs, absent refresh-token logic, missing brute force protection, and insufficient validation.

### Weak JWT secret (example)

Evidence:

```javascript theme={null}
// .env
JWT_SECRET=your_jwt_secret_key_here

// routes/auth.js (usage)
jwt.sign(payload, process.env.JWT_SECRET, { algorithm: 'HS256', expiresIn: '24h' });
```

Why it matters: placeholder secrets can be guessed or leaked; attackers can forge tokens and impersonate users.

Fix: use a high-entropy secret or an asymmetric key pair managed via a secret manager. Provide the secret via environment variables and verify tokens with explicit algorithm restrictions.

Example:

```javascript theme={null}
const jwt = require('jsonwebtoken');

const token = jwt.sign(payload, process.env.JWT_SECRET, {
  algorithm: 'HS256',
  expiresIn: '15m'
});

try {
  const decoded = jwt.verify(token, process.env.JWT_SECRET, {
    algorithms: ['HS256']
  });
  // proceed
} catch (err) {
  // handle invalid token
}
```

### Password hashing on registration and login

Use async bcrypt hashing and comparison:

```javascript theme={null}
const bcrypt = require('bcrypt');

async function registerUser(email, plainPassword) {
  const saltRounds = 12;
  const passwordHash = await bcrypt.hash(plainPassword, saltRounds);
  // store passwordHash in DB, never store plainPassword
}

async function verifyLogin(plainPassword, passwordHash) {
  return bcrypt.compare(plainPassword, passwordHash);
}
```

### Brute force protection (rate limiter example)

Use an IP + username keyed fast store (Redis recommended). Example using express-rate-limit:

```javascript theme={null}
const rateLimit = require('express-rate-limit');

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

// Apply to login route
router.post('/login', loginLimiter, validationMiddleware, loginHandler);
```

### Stronger password validation

Example using express-validator:

```javascript theme={null}
const { body } = require('express-validator');

const registrationValidation = [
  body('email')
    .isEmail()
    .isLength({ max: 254 })
    .normalizeEmail({ gmail_remove_dots: false }),

  body('password')
    .isLength({ min: 8, max: 128 })
    .matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&]).{8,128}$/)
    .withMessage('Password must contain uppercase, lowercase, number, and special character')
];
```

### Refresh token rotation & storage (high level)

Model refresh tokens in a database table with: hashed token, user\_id, expiry, and session\_id/family\_id.

On refresh:

* Verify presented refresh token by comparing hashed value.
* If valid and unused: issue a new refresh token (rotation) and mark old as revoked.
* If reuse is detected: invalidate the entire session family and require re-authentication.

### Password reset tokens

Create a cryptographically random token, store the hash at rest, set a short TTL, and invalidate after use:

```javascript theme={null}
const crypto = require('crypto');

function createResetToken() {
  const token = crypto.randomBytes(32).toString('hex');
  const tokenHash = crypto.createHash('sha256').update(token).digest('hex');
  // store tokenHash with expiry in DB and send raw token to user's email
  return { token, tokenHash };
}
```

### Account enumeration & timing defense

Return uniform messages for auth failures:

```javascript theme={null}
// Always return same status and message to avoid distinction
res.status(401).json({ error: 'Invalid credentials' });
```

Add small random jitter on failures to mitigate precise timing analysis.

### SQL/NoSQL injection defenses

* SQL: use parameterized queries (example with pg):

```javascript theme={null}
const result = await pool.query('SELECT id, password_hash FROM users WHERE email = $1', [email]);
```

* NoSQL: avoid allowing user-controlled operators in filters. Sanitize inputs and use allowlists.

### Cookie & CSRF configuration (if using cookies)

Recommended cookie flags:

* `HttpOnly: true`
* `Secure: true`
* `SameSite: 'Lax'` or `'Strict'` depending on app flow
* Explicit `maxAge`, narrow `path` and `domain`

Protect refresh and state-changing endpoints from CSRF (double-submit tokens or SameSite enforcement). See OWASP on CSRF: [https://owasp.org/www-community/attacks/csrf](https://owasp.org/www-community/attacks/csrf)

### JWT advice recap

* Always call `jwt.verify()` with explicit algorithms. Do not rely on `jwt.decode()` for authorization.
* For critical authorization decisions, revalidate claims against server-side state (database) rather than trusting claims alone.

### Logging and telemetry

* Redact or avoid logging PII, passwords, tokens, and reset links. Use structured logs and allowlists for safe fields.

### Dependency & crypto hygiene

* Keep libraries up to date.
* Avoid custom crypto implementations for auth. Use Node's `crypto` and vetted libraries.

## Operational testing suggestions

Automate tests for:

* Brute force protection (verify rate-limiter triggers).
* Refresh-token rotation and reuse detection (session revocation on reuse).
* Password reset token expiry and single-use behavior.
* JWT verification algorithm enforcement.

Example test stubs:

```javascript theme={null}
// Refresh token reuse detection (pseudo-test)
describe('Refresh Token Security', () => {
  it('should invalidate session family on token reuse', async () => {
    // 1) Obtain refresh token A
    // 2) Use A to obtain new refresh token B (rotation)
    // 3) Re-use A -> server detects reuse and invalidates session family
    // Assert: both requests after reuse fail and user sessions invalidated
  });
});

// Brute force protection
describe('Brute Force Protection', () => {
  it('should rate limit after 5 failed attempts', async () => {
    // Make 6 failing login attempts
    // Assert 6th returns 429
  });
});
```

## Example remediation: rate limiting and login validation (consolidated)

```javascript theme={null}
// routes/auth.js
const rateLimit = require('express-rate-limit');
const { body } = require('express-validator');

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

const loginValidation = [
  body('email').isEmail().normalizeEmail(),
  body('password').isLength({ min: 8, max: 128 })
];

router.post('/login', loginLimiter, loginValidation, async (req, res) => {
  // login logic
});
```

## Not applicable or missing features (sample project)

* Password reset flow: Not implemented in the sample.
* Email verification: Not implemented.
* CSRF protection: Not applicable for APIs that do not use cookies, but must be considered if cookies are later introduced.
* CORS: No explicit configuration in sample; must be locked down for production.

## Sample security checklist results (condensed)

| Control | Status | Notes |
| - | -: | - |
| `bcrypt.compare()` usage | PASS | Properly used where present |
| Password hashing on registration | FAIL | No registration endpoint or missing hashing |
| JWT secret strength | FAIL | Placeholder secret in `.env` |
| Short access token TTL | FAIL | `24h` is excessive for access tokens |
| Refresh token implementation | FAIL | Not implemented |
| Session invalidation | FAIL | No mechanism for invalidating old sessions |
| Brute force protection | FAIL | No rate limiting present |
| Account enumeration defenses | PARTIAL | Generic error messages needed |
| Password reset / Email verification | N/A | Not implemented in sample |

Notes:

* Any examples in this table that contain code-like values (e.g., `.env`, `24h`, `bcrypt.compare()`) are shown in inline code to avoid parsing issues.

## Operational remediation timeline (example)

* Immediately (48 hours): Replace JWT secret, implement bcrypt hashing on registration, add rate limiting for auth endpoints, reduce access-token TTL.
* Short term (1 week): Add refresh token rotation, detect token reuse, implement session invalidation.
* Medium term (1 month): Add password reset flow with secure tokens, email verification, CORS/HTTPS enforcement, and automated tests.

## Executive summary (example output from automated audit)

* Risk Score: 8.5/10 (High Risk)
* Critical findings: weak JWT secret management, missing password hashing on registration, excessive access-token TTLs, missing refresh-token implementation, and no brute force protection.
* Prioritized immediate actions:
  1. Replace hard-coded JWT secret with a strong, rotated secret stored in a secret manager.
  2. Add password hashing on registration using bcrypt (async, adequate salt rounds).
  3. Implement rate-limiting for auth endpoints (login, reset).
  4. Reduce access token lifetime and implement refresh-token rotation with reuse detection.
  5. Add logging redaction and robust input validation.

## Common gotchas for automated reviewers

* Do not invent files or functions. If context is missing, report "insufficient context".
* Prefer to point to exact files/line ranges when making evidence-based findings.
* Always provide minimal reproducible remediation examples and tests.

Final notes:

* The structured prompt yields a concrete remediation plan and prioritized checklist. Immediate mitigations with the best ROI are secret/key hygiene, password hashing, short-lived access tokens + refresh token rotation, and brute force protections.

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/Xs8Ruv7b0QCZf7iM/images/Claude-Code-for-Beginners/Security-Auditing-with-Claude-Code/Authentication-Flow-Review/claude-code-reviewing-prompts-github-screenshot.jpg?fit=max&auto=format&n=Xs8Ruv7b0QCZf7iM&q=85&s=e39eeeffd3dbe3b5230be9db753c1ad0" alt="A screenshot of a dark-themed GitHub repository page titled &#x22;Claude-Code-Reviewing-Prompts&#x22; showing a list of markdown files and repo details in a browser window. The window is over a code editor/IDE background with project files and a terminal visible." width="1920" height="1080" data-path="images/Claude-Code-for-Beginners/Security-Auditing-with-Claude-Code/Authentication-Flow-Review/claude-code-reviewing-prompts-github-screenshot.jpg" />
</Frame>

<CardGroup>
  <Card title="Watch Video" icon="video" cta="Learn more" href="https://learn.kodekloud.com/user/courses/claude-code-for-beginners/module/eaf75a67-f28f-4217-a3f9-92d411403129/lesson/1f44be11-76ce-4dd1-b847-b3c54ffd49e4" />
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.