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

Testing Security, Not Just Functionality

The gap in most test suites

Most apps have tests like: “User can sign up,” “User can log in,” “User can create a note.” These are functional tests — they verify the happy path works.

But auth security is about the unhappy path. The important questions are: “Can a user see another user’s notes?” “Does rate limiting actually kick in after 20 attempts?” “Does a revoked token actually get rejected?” “If I send user_id: 'admin-1' in the request body, does the server ignore it?”

These are security tests. They verify that defenses work, not just that features work. And they are almost always missing.

Why security tests matter more

A failing functional test means a user cannot do something they should be able to do. Annoying, but you will hear about it quickly (the user complains).

A failing security test means a user can do something they should not be able to do. Dangerous, and you will not hear about it until it is exploited. The user who benefits from the bug (the attacker) is not going to file a support ticket.

Security bugs are silent. Tests are how you find them before attackers do.

What security tests look like

A functional test for login:

it("valid credentials return a session", async () => {
  const res = await login("[email protected]", "password123");
  assert.strictEqual(res.status, 200);
  assert.ok(res.headers.get("set-cookie"));
});

Security tests for the same endpoint:

it("wrong password returns 401", async () => {
  const res = await login("[email protected]", "wrongpassword");
  assert.strictEqual(res.status, 401);
});

it("non-existent email returns 401 (not 404)", async () => {
  const res = await login("[email protected]", "anything");
  assert.strictEqual(res.status, 401); // Same as wrong password — no enumeration
});

it("response time is similar for existing and non-existing users", async () => {
  const t1 = Date.now();
  await login("[email protected]", "wrong");
  const d1 = Date.now() - t1;

  const t2 = Date.now();
  await login("[email protected]", "wrong");
  const d2 = Date.now() - t2;

  // Both should take roughly the same time (dummy hash)
  assert.ok(Math.abs(d1 - d2) < 100, "Timing difference too large");
});

The functional test verifies one thing. The security tests verify three things the endpoint must not do: reveal whether an email exists, leak timing information, or return a session for wrong credentials.

The testing mindset

For every feature, ask:

  • What should happen when the input is valid? (Functional test)
  • What should happen when the input is invalid? (Security test)
  • What should happen when the user is not who they claim to be? (Security test)
  • What should happen when the user tries to access something they should not? (Security test)
  • What should happen under abuse conditions (many requests, large payloads)? (Security test)

This course teaches you to write all five categories.

Why are security bugs harder to catch than functional bugs?

Project Setup →

© 2026 hectoday. All rights reserved.