/** * Authorization Tests * Tests for access control, privilege escalation prevention, and admin access */ import { describe, it, expect } from "bun:test"; import { getUserID, getPrivilegeLevel } from "~/server/auth"; import { createMockEvent, createTestJWT } from "./test-utils"; import { env } from "~/env/server"; describe("Authorization", () => { describe("Admin Access Control", () => { it("should grant admin access to configured admin user", async () => { const adminToken = await createTestJWT(env.ADMIN_ID); const event = createMockEvent({ cookies: { userIDToken: adminToken } }); const privilege = await getPrivilegeLevel(event); expect(privilege).toBe("admin"); }); it("should deny admin access to regular users", async () => { const userToken = await createTestJWT("regular-user-123"); const event = createMockEvent({ cookies: { userIDToken: userToken } }); const privilege = await getPrivilegeLevel(event); expect(privilege).toBe("user"); expect(privilege).not.toBe("admin"); }); it("should deny admin access to anonymous users", async () => { const event = createMockEvent({}); const privilege = await getPrivilegeLevel(event); expect(privilege).toBe("anonymous"); expect(privilege).not.toBe("admin"); }); it("should not allow privilege escalation through token tampering", async () => { // Create a regular user token const regularToken = await createTestJWT("regular-user-123"); // Attacker tries to modify token to include admin ID // This should fail signature verification const parts = regularToken.split("."); const fakeAdminPayload = Buffer.from( JSON.stringify({ id: env.ADMIN_ID }) ).toString("base64url"); const tamperedToken = `${parts[0]}.${fakeAdminPayload}.${parts[2]}`; const event = createMockEvent({ cookies: { userIDToken: tamperedToken } }); const privilege = await getPrivilegeLevel(event); expect(privilege).toBe("anonymous"); // Invalid token = anonymous }); it("should handle malformed admin ID gracefully", async () => { const invalidIds = ["", null, undefined, " ", "admin'--"]; for (const invalidId of invalidIds) { const token = await createTestJWT(invalidId as string); const event = createMockEvent({ cookies: { userIDToken: token } }); const privilege = await getPrivilegeLevel(event); // Should not grant admin access for invalid IDs expect(privilege).not.toBe("admin"); } }); }); describe("User Access Control", () => { it("should grant user access to authenticated users", async () => { const userToken = await createTestJWT("user-123"); const event = createMockEvent({ cookies: { userIDToken: userToken } }); const privilege = await getPrivilegeLevel(event); expect(privilege).toBe("user"); }); it("should deny user access to anonymous requests", async () => { const event = createMockEvent({}); const privilege = await getPrivilegeLevel(event); expect(privilege).toBe("anonymous"); expect(privilege).not.toBe("user"); }); it("should maintain user access with valid token", async () => { const userToken = await createTestJWT("user-456"); const event = createMockEvent({ cookies: { userIDToken: userToken } }); const userId = await getUserID(event); expect(userId).toBe("user-456"); const privilege = await getPrivilegeLevel(event); expect(privilege).toBe("user"); }); }); describe("Privilege Escalation Prevention", () => { it("should prevent horizontal privilege escalation", async () => { const user1Token = await createTestJWT("user-1"); const user2Token = await createTestJWT("user-2"); const event1 = createMockEvent({ cookies: { userIDToken: user1Token } }); const event2 = createMockEvent({ cookies: { userIDToken: user2Token } }); const user1Id = await getUserID(event1); const user2Id = await getUserID(event2); expect(user1Id).toBe("user-1"); expect(user2Id).toBe("user-2"); expect(user1Id).not.toBe(user2Id); }); it("should prevent vertical privilege escalation", async () => { // Regular user should not be able to become admin const userToken = await createTestJWT("regular-user"); const event = createMockEvent({ cookies: { userIDToken: userToken } }); const privilege = await getPrivilegeLevel(event); expect(privilege).toBe("user"); // Even with multiple checks, privilege should remain the same const privilege2 = await getPrivilegeLevel(event); expect(privilege2).toBe("user"); }); it("should not allow session hijacking through token reuse", async () => { const user1Token = await createTestJWT("user-1"); // User 1's token should always return user 1's ID const event1 = createMockEvent({ cookies: { userIDToken: user1Token } }); const id1 = await getUserID(event1); // Even if attacker captures token, it still identifies as user 1 const event2 = createMockEvent({ cookies: { userIDToken: user1Token } }); const id2 = await getUserID(event2); expect(id1).toBe("user-1"); expect(id2).toBe("user-1"); }); it("should prevent privilege escalation via race conditions", async () => { const userToken = await createTestJWT("concurrent-user"); const event = createMockEvent({ cookies: { userIDToken: userToken } }); // Simulate concurrent privilege checks const results = await Promise.all([ getPrivilegeLevel(event), getPrivilegeLevel(event), getPrivilegeLevel(event), getPrivilegeLevel(event), getPrivilegeLevel(event) ]); // All results should be the same expect(results.every((r) => r === "user")).toBe(true); }); }); describe("Anonymous Access", () => { it("should handle missing authentication token", async () => { const event = createMockEvent({}); const privilege = await getPrivilegeLevel(event); expect(privilege).toBe("anonymous"); }); it("should handle empty authentication token", async () => { const event = createMockEvent({ cookies: { userIDToken: "" } }); const privilege = await getPrivilegeLevel(event); expect(privilege).toBe("anonymous"); }); it("should handle invalid token format", async () => { const event = createMockEvent({ cookies: { userIDToken: "not-a-jwt-token" } }); const privilege = await getPrivilegeLevel(event); expect(privilege).toBe("anonymous"); }); it("should return null user ID for anonymous users", async () => { const event = createMockEvent({}); const userId = await getUserID(event); expect(userId).toBeNull(); }); }); describe("Access Control Edge Cases", () => { it("should handle user ID with special characters", async () => { const specialUserId = "user-with-special-!@#$%"; const token = await createTestJWT(specialUserId); const event = createMockEvent({ cookies: { userIDToken: token } }); const userId = await getUserID(event); expect(userId).toBe(specialUserId); }); it("should handle very long user IDs", async () => { const longUserId = "user-" + "x".repeat(1000); const token = await createTestJWT(longUserId); const event = createMockEvent({ cookies: { userIDToken: token } }); const userId = await getUserID(event); expect(userId).toBe(longUserId); }); it("should handle user ID with unicode characters", async () => { const unicodeUserId = "user-with-unicode-🔐"; const token = await createTestJWT(unicodeUserId); const event = createMockEvent({ cookies: { userIDToken: token } }); const userId = await getUserID(event); expect(userId).toBe(unicodeUserId); }); it("should handle admin ID case sensitivity", async () => { const adminId = env.ADMIN_ID; const wrongCaseId = adminId.toUpperCase(); // Exact match required const correctToken = await createTestJWT(adminId); const wrongCaseToken = await createTestJWT(wrongCaseId); const correctEvent = createMockEvent({ cookies: { userIDToken: correctToken } }); const wrongCaseEvent = createMockEvent({ cookies: { userIDToken: wrongCaseToken } }); const correctPrivilege = await getPrivilegeLevel(correctEvent); const wrongCasePrivilege = await getPrivilegeLevel(wrongCaseEvent); expect(correctPrivilege).toBe("admin"); // Wrong case should not get admin access (unless IDs match) if (adminId !== wrongCaseId) { expect(wrongCasePrivilege).toBe("user"); } }); }); describe("Authorization Attack Scenarios", () => { it("should prevent session fixation attacks", async () => { // Attacker cannot predict or fix session tokens const token1 = await createTestJWT("user-1"); const token2 = await createTestJWT("user-1"); // Tokens should be different even for same user // (Due to different timestamps, though payload is same) expect(token1).toBeDefined(); expect(token2).toBeDefined(); }); it("should prevent parameter pollution attacks", async () => { // Multiple cookie values should not cause confusion const token1 = await createTestJWT("user-1"); const token2 = await createTestJWT("user-2"); // Only first cookie should be used const event = createMockEvent({ cookies: { userIDToken: token1 // In practice, duplicate cookies are handled by the framework } }); const userId = await getUserID(event); expect(userId).toBe("user-1"); }); it("should prevent token substitution attacks", async () => { const legitimateToken = await createTestJWT("victim-user"); const attackerToken = await createTestJWT("attacker-user"); // Each token should only authenticate its respective user const legitimateEvent = createMockEvent({ cookies: { userIDToken: legitimateToken } }); const attackerEvent = createMockEvent({ cookies: { userIDToken: attackerToken } }); const legitimateId = await getUserID(legitimateEvent); const attackerId = await getUserID(attackerEvent); expect(legitimateId).toBe("victim-user"); expect(attackerId).toBe("attacker-user"); expect(legitimateId).not.toBe(attackerId); }); it("should prevent authorization bypass through empty checks", async () => { const emptyChecks = [null, undefined, "", " ", "null", "undefined"]; for (const check of emptyChecks) { const event = createMockEvent({ cookies: { userIDToken: check as string } }); const privilege = await getPrivilegeLevel(event); expect(privilege).toBe("anonymous"); } }); }); describe("Multi-User Scenarios", () => { it("should handle multiple concurrent user sessions", async () => { const users = ["user-1", "user-2", "user-3", "user-4", "user-5"]; const tokens = await Promise.all(users.map((u) => createTestJWT(u))); const events = tokens.map((token) => createMockEvent({ cookies: { userIDToken: token } }) ); const userIds = await Promise.all(events.map(getUserID)); // All users should be correctly identified expect(userIds).toEqual(users); }); it("should maintain separate privileges for different users", async () => { const adminToken = await createTestJWT(env.ADMIN_ID); const user1Token = await createTestJWT("user-1"); const user2Token = await createTestJWT("user-2"); const adminEvent = createMockEvent({ cookies: { userIDToken: adminToken } }); const user1Event = createMockEvent({ cookies: { userIDToken: user1Token } }); const user2Event = createMockEvent({ cookies: { userIDToken: user2Token } }); const [adminPriv, user1Priv, user2Priv] = await Promise.all([ getPrivilegeLevel(adminEvent), getPrivilegeLevel(user1Event), getPrivilegeLevel(user2Event) ]); expect(adminPriv).toBe("admin"); expect(user1Priv).toBe("user"); expect(user2Priv).toBe("user"); }); }); describe("Performance", () => { it("should check privileges efficiently", async () => { const userToken = await createTestJWT("perf-test-user"); const event = createMockEvent({ cookies: { userIDToken: userToken } }); const start = performance.now(); for (let i = 0; i < 1000; i++) { await getPrivilegeLevel(event); } const duration = performance.now() - start; // Should complete 1000 checks in less than 100ms expect(duration).toBeLessThan(100); }); it("should extract user IDs efficiently", async () => { const userToken = await createTestJWT("perf-test-user"); const event = createMockEvent({ cookies: { userIDToken: userToken } }); const start = performance.now(); for (let i = 0; i < 1000; i++) { await getUserID(event); } const duration = performance.now() - start; // Should complete 1000 extractions in less than 100ms expect(duration).toBeLessThan(100); }); }); });