aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/server/routes/auth.test.ts
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2025-12-30 22:08:47 +0900
committernsfisis <nsfisis@gmail.com>2025-12-30 22:08:47 +0900
commitc2eb7513834eeb5adfa53fff897f585de87e4821 (patch)
tree9e914051ca67e2f9e1fa301119bdec398ec9e55f /src/server/routes/auth.test.ts
parentb839cae49efd4b9d35c2868a4137101a4d71bd7f (diff)
downloadkioku-c2eb7513834eeb5adfa53fff897f585de87e4821.tar.gz
kioku-c2eb7513834eeb5adfa53fff897f585de87e4821.tar.zst
kioku-c2eb7513834eeb5adfa53fff897f585de87e4821.zip
feat(security): add rate limiting and CORS middleware
- Add rate limiting to login endpoint (5 requests/minute per IP) - Configure CORS middleware with environment-based origin control - Expose rate limit headers in CORS for client visibility - Update hono to 4.11.3 for rate limiter peer dependency 🤖 Generated with [Claude Code](https://claude.ai/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'src/server/routes/auth.test.ts')
-rw-r--r--src/server/routes/auth.test.ts50
1 files changed, 50 insertions, 0 deletions
diff --git a/src/server/routes/auth.test.ts b/src/server/routes/auth.test.ts
index 5bf9f86..c3b0158 100644
--- a/src/server/routes/auth.test.ts
+++ b/src/server/routes/auth.test.ts
@@ -62,6 +62,56 @@ describe("POST /login", () => {
app.route("/api/auth", auth);
});
+ it("returns rate limit headers on login request", async () => {
+ vi.mocked(mockUserRepo.findByUsername).mockResolvedValue(undefined);
+
+ const res = await app.request("/api/auth/login", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ "X-Forwarded-For": "192.168.1.1",
+ },
+ body: JSON.stringify({
+ username: "testuser",
+ password: "somepassword",
+ }),
+ });
+
+ expect(res.headers.get("RateLimit-Limit")).toBe("5");
+ expect(res.headers.get("RateLimit-Remaining")).toBeDefined();
+ });
+
+ it("returns 429 after exceeding rate limit", async () => {
+ vi.mocked(mockUserRepo.findByUsername).mockResolvedValue(undefined);
+
+ const makeRequest = () =>
+ app.request("/api/auth/login", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ "X-Forwarded-For": "10.0.0.1",
+ },
+ body: JSON.stringify({
+ username: "testuser",
+ password: "wrongpassword",
+ }),
+ });
+
+ // Make 5 requests (the limit)
+ for (let i = 0; i < 5; i++) {
+ const res = await makeRequest();
+ expect(res.status).toBe(401);
+ }
+
+ // 6th request should be rate limited
+ const rateLimitedRes = await makeRequest();
+ expect(rateLimitedRes.status).toBe(429);
+ const body = (await rateLimitedRes.json()) as {
+ error?: { code: string; message: string };
+ };
+ expect(body.error?.code).toBe("RATE_LIMIT_EXCEEDED");
+ });
+
it("returns access token for valid credentials", async () => {
vi.mocked(mockUserRepo.findByUsername).mockResolvedValue({
id: "user-uuid-123",