hectoday
DocsCoursesChangelog GitHub
DocsCoursesChangelog GitHub

Access Required

Enter your access code to view courses.

Invalid code

← All courses Testing Auth and Security with @hectoday/http

Why Auth Tests Are Different

  • Testing Security, Not Just Functionality
  • Project Setup

Testing Authentication

  • Testing Login Flows
  • Testing Sessions and Cookies
  • Testing 2FA Flows

Testing Authorization

  • Testing Access Boundaries
  • Testing API Keys and Scopes

Testing Security Properties

  • Testing Rate Limiting and Lockout
  • Testing Token Security
  • Testing Input Handling

Putting It Together

  • A Security Test Suite

A Security Test Suite

Organizing tests by threat

Functional tests are organized by feature: “login tests,” “notes tests,” “settings tests.” Security tests are better organized by threat: “what could go wrong?”

tests/
  auth.test.ts            # Login, sessions, 2FA — authentication threats
  authz.test.ts           # IDOR, roles, permissions — authorization threats
  security.test.ts        # Injection, XSS, traversal — input handling threats
  abuse.test.ts           # Rate limiting, lockout — abuse protection
  tokens.test.ts          # Rotation, reuse, deny list — token threats
  helpers.ts              # Login, request, cookie extraction
  payloads.ts             # Standard attack payloads

Each file answers one question: “Is this category of threat handled?” When a test fails, the filename tells you what type of vulnerability was introduced.

The minimum test set

Not every app needs hundreds of security tests. But every app with auth needs these:

Authentication (must have)

  • Wrong password returns 401
  • Non-existent email returns 401 (same as wrong password)
  • Session cookie has HttpOnly and SameSite attributes
  • Invalid session ID returns 401
  • Logout invalidates the session

Authorization (must have)

  • User A cannot access user B’s data (IDOR)
  • Non-member gets 404 for org resources (not 403)
  • Viewer cannot create/delete (role enforcement)
  • Extra fields in request body are ignored (mass assignment)

Abuse protection (should have)

  • Rate limiting triggers at the configured threshold
  • Lockout triggers after N failures
  • Correct password fails during lockout

Token security (should have if using JWTs)

  • Expired access token is rejected
  • Used refresh token is rejected
  • Refresh token reuse invalidates the family

Input handling (should have)

  • SQL injection payloads do not leak data
  • XSS payloads are encoded in HTML responses

This is 17 tests. They cover the highest-impact threats. Add more as your app grows.

Running on CI

Add security tests to your CI pipeline:

# .github/workflows/test.yml
name: Tests
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20 }
      - run: npm ci
      - run: npx tsx --test tests/*.test.ts

Security tests should run on every pull request. A failing security test blocks the merge.

When security tests save you

Scenario 1: A developer refactors the notes route and forgets to include org_id in the query. The IDOR test fails. The PR is blocked. The developer adds the org_id check. Bug never ships.

Scenario 2: A developer adds a new field to the note creation body and uses body.user_id ?? user.id (copied from old code). The mass assignment test fails. The developer switches to explicit field picking. Bug never ships.

Scenario 3: A developer changes the session cookie helper and removes SameSite to fix a cross-origin issue. The cookie attribute test fails. The developer finds a different fix. CSRF vulnerability never ships.

In each case, the test caught a security regression introduced by a well-intentioned code change. This is the core value of security tests: they catch mistakes by people who are not thinking about security at that moment.

What we covered in this course

CategoryWhat we testedWhy it matters
Login flowsValid, invalid, missing, timingCatches enumeration and timing leaks
SessionsCookie attrs, lifecycle, rotationCatches XSS and CSRF enablers
2FAPending sessions, codes, recoveryCatches 2FA bypass
Access boundariesIDOR, roles, permissions, org scopingCatches unauthorized access
API keysScopes, org binding, escalationCatches privilege escalation
Rate limitingThresholds, lockout, headersCatches brute-force enablers
TokensRotation, reuse, deny listCatches token theft impacts
Input handlingInjection, XSS, traversal, assignmentCatches input-based attacks

Every test answers: “Does this defense still work after the latest code change?”

Exercises

Exercise 1: Create the minimum test set (17 tests from the checklist above). Run them against your app. How many pass?

Exercise 2: Add the tests to a CI pipeline. Make a PR that breaks one test. Verify the pipeline catches it.

Exercise 3: Review your app’s routes. Are there any security properties that are not covered by your tests? Add tests for them.

Exercise 4: Break a defense intentionally (remove an org_id check, remove HttpOnly from a cookie, remove the timing dummy hash). Run the tests. The relevant test should fail. Fix it and verify the test passes again.

When is the most valuable time for a security test to fail?

← Testing Input Handling Back to course →

© 2026 hectoday. All rights reserved.