Appearance
Testing
The repository uses Vitest for tests and Supertest for Express integration tests.
Run the test suite
bash
npm testUseful commands:
bash
npm run test:watch # rerun tests while editing
npm run test:coverage # generate coverage reports
npm run typecheck # validate TypeScript without emitting filesTest a protected route
Keep the app in memory and call it with Supertest. Do not start a network listener in the test file.
ts
import express from "express";
import request from "supertest";
import { createAuth } from "@0-auth/zero-auth";
const auth = createAuth({
accessSecret: "test-access-secret-at-least-32-characters",
refreshSecret: "test-refresh-secret-at-least-32-characters",
});
const app = express();
app.get("/profile", auth.protect(), (req, res) => {
res.json({ user: req.user });
});
app.use(auth.errorHandler);
it("rejects a request without a token", async () => {
const response = await request(app).get("/profile");
expect(response.status).toBe(401);
expect(response.body.error.code).toBe("AUTH_TOKEN_MISSING");
});
it("accepts a valid access token", async () => {
const { accessToken } = await auth.generateTokenPair({
id: "user-1",
role: "user",
});
const response = await request(app)
.get("/profile")
.set("Authorization", `Bearer ${accessToken}`);
expect(response.status).toBe(200);
expect(response.body.user.id).toBe("user-1");
});Test roles
Test both sides of an authorization boundary:
ts
app.get(
"/admin",
auth.protect(),
auth.authorize(["admin"]),
(_req, res) => res.json({ ok: true }),
);
const { accessToken } = await auth.generateTokenPair({
id: "user-1",
role: "user",
});
const response = await request(app)
.get("/admin")
.set("Authorization", `Bearer ${accessToken}`);
expect(response.status).toBe(403);
expect(response.body.error.code).toBe("AUTH_FORBIDDEN");Test expiry and refresh
Use a short-lived token or sign a token with an expiration in the past, then assert the exact error code:
ts
await expect(auth.verifyToken(expiredToken)).rejects.toMatchObject({
code: "AUTH_TOKEN_EXPIRED",
statusCode: 401,
});For refresh rotation, test both the first refresh and replay of the old refresh token. The replay should return AUTH_TOKEN_INVALID and call your onRefreshReuse callback.
What to test in an application
- Run the complete release checks before deployment:
bash
npm run format:check
npm run lint
npm run typecheck
npm run test:coverage
npm run build
npm run docs:build
npm pack --dry-run- Missing, malformed, expired, and tampered tokens
- Access tokens rejected where refresh tokens are expected
- Users with allowed and disallowed roles
- Users with all required permissions and users missing one permission
- Cookie flags and logout behavior
- Missing, mismatched, tampered, and valid CSRF tokens
- Refresh-token replay and family revocation
- Error responses without leaking secrets or raw token contents
Test refresh concurrency
The rotation store must allow exactly one request to consume a refresh token:
bash
npm test -- --run tests/security/concurrency-and-rotation.test.tsFor an application-level stress check, send the same refresh token from many concurrent clients and verify one success followed by rejected replays. Do not use this test against a production user's session.
Test the examples
The stateless example needs only Node.js:
bash
cd examples/express-rest-api
npm install
npm testThe cookie example needs Redis:
bash
cd examples/express-cookies-redis
docker compose up -d
npm install
npm test
docker compose downThe cookie test covers health, registration, login, CSRF setup, missing-CSRF rejection, protected cookies, rotation, and logout.
Test cURL behavior
For bearer clients, verify success and the expected unauthenticated response:
bash
curl http://localhost:3000/profile \
-H "Authorization: Bearer <access-token>"
curl -i http://localhost:3000/profileFor cookie clients, preserve cookies with -c cookies.txt and -b cookies.txt. Obtain the CSRF token before refresh, logout, or other state-changing requests. See the cookie + Redis example README and the client guide.