Skip to main content
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.
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.

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.
Do not invent files, functions, or evidence. If an artifact cannot be located, the reviewer must explicitly state “insufficient context” for that check.

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.
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:
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:

Password hashing on registration and login

Use async bcrypt hashing and comparison:

Brute force protection (rate limiter example)

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

Stronger password validation

Example using express-validator:

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:

Account enumeration & timing defense

Return uniform messages for auth failures:
Add small random jitter on failures to mitigate precise timing analysis.

SQL/NoSQL injection defenses

  • SQL: use parameterized queries (example with pg):
  • NoSQL: avoid allowing user-controlled operators in filters. Sanitize inputs and use allowlists.
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

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:

Example remediation: rate limiting and login validation (consolidated)

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)

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.
A screenshot of a dark-themed GitHub repository page titled "Claude-Code-Reviewing-Prompts" 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.

Watch Video