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 Sessions and Cookies

What to test about sessions

Sessions have both functional requirements (they work) and security requirements (they expire, they rotate, they have the right cookie attributes).

Cookie attribute tests

The session cookie must have specific security attributes. Test them:

describe("session cookies", () => {
  it("sets HttpOnly flag", async () => {
    const res = await login("[email protected]", "password123");
    const setCookie = res.headers.get("set-cookie") ?? "";
    assert.ok(setCookie.toLowerCase().includes("httponly"), "Cookie must be HttpOnly");
  });

  it("sets SameSite=Lax", async () => {
    const res = await login("[email protected]", "password123");
    const setCookie = res.headers.get("set-cookie") ?? "";
    assert.ok(setCookie.toLowerCase().includes("samesite=lax"), "Cookie must be SameSite=Lax");
  });

  it("sets Path=/", async () => {
    const res = await login("[email protected]", "password123");
    const setCookie = res.headers.get("set-cookie") ?? "";
    assert.ok(setCookie.includes("Path=/"), "Cookie must have Path=/");
  });

  it("sets Max-Age", async () => {
    const res = await login("[email protected]", "password123");
    const setCookie = res.headers.get("set-cookie") ?? "";
    assert.ok(setCookie.includes("Max-Age="), "Cookie must have Max-Age");
  });
});

These tests catch regressions where someone accidentally removes a cookie attribute. Without HttpOnly, XSS can steal sessions. Without SameSite, CSRF attacks are possible.

Session lifecycle tests

describe("session lifecycle", () => {
  it("valid session accesses protected routes", async () => {
    const cookie = await loginAs("[email protected]");
    const res = await authenticatedRequest("/me", cookie);
    assert.strictEqual(res.status, 200);
  });

  it("invalid session ID returns 401", async () => {
    const res = await authenticatedRequest("/me", "session=fake-id-12345");
    assert.strictEqual(res.status, 401);
  });

  it("empty cookie returns 401", async () => {
    const res = await authenticatedRequest("/me", "");
    assert.strictEqual(res.status, 401);
  });

  it("no cookie header returns 401", async () => {
    const res = await request("/me");
    assert.strictEqual(res.status, 401);
  });

  it("logout invalidates the session", async () => {
    const cookie = await loginAs("[email protected]");

    // Logout
    await authenticatedRequest("/logout", cookie, { method: "POST" });

    // Session should be invalid
    const res = await authenticatedRequest("/me", cookie);
    assert.strictEqual(res.status, 401);
  });
});

Session rotation tests

If your app rotates sessions after privilege changes (from the Production Auth course):

describe("session rotation", () => {
  it("password change invalidates old session", async () => {
    const cookie = await loginAs("[email protected]");

    // Change password (requires step-up in production, simplified here)
    const res = await authenticatedRequest("/me/password", cookie, {
      method: "PUT",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ newPassword: "newpass123" }),
    });

    // Response should include a new session cookie
    const newCookie = getCookie(res);
    assert.ok(newCookie, "Should set a new session cookie");
    assert.notStrictEqual(newCookie, cookie, "New cookie should differ from old");

    // Old session should be invalid
    const oldRes = await authenticatedRequest("/me", cookie);
    assert.strictEqual(oldRes.status, 401);

    // New session should work
    const newRes = await authenticatedRequest("/me", newCookie);
    assert.strictEqual(newRes.status, 200);
  });
});

This test catches a common regression: someone changes the password-update route and forgets to rotate the session. The old session remains valid, which means an attacker who captured it still has access.

Exercises

Exercise 1: Write the cookie attribute tests. Run them. If any fail, fix the cookie helper.

Exercise 2: Write the session lifecycle tests. Verify logout actually invalidates the session.

Exercise 3: Temporarily remove the HttpOnly attribute from your session cookie. Run the tests. The HttpOnly test should fail, catching the regression.

Why do we test cookie attributes like HttpOnly and SameSite?

← Testing Login Flows Testing 2FA Flows →

© 2026 hectoday. All rights reserved.