auth-yes/sdk/hono.test.ts

141 lines
4.1 KiB
TypeScript

import { Hono } from "jsr:@hono/hono@4";
import { assertEquals } from "jsr:@std/assert";
import { createAuthMiddleware, requireScope } from "./hono.ts";
import { stub } from "jsr:@std/testing/mock";
import { AuthSdk } from "./mod.ts";
type Variables = {
userId: string;
scopes: string[];
};
Deno.test("AuthMiddleware - Authorized request sets userId and scopes", async () => {
const sdk = new AuthSdk({ authApiUrl: "http://localhost" });
const validateSessionStub = stub(
sdk,
"validateSession",
() =>
Promise.resolve({
valid: true,
uuid: "test-user-id",
scopes: ["commander", "viewer"],
}),
);
const app = new Hono<{ Variables: Variables }>();
app.use("*", createAuthMiddleware(sdk));
app.get(
"/",
(c) => c.json({ userId: c.get("userId"), scopes: c.get("scopes") }),
);
const req = new Request("http://localhost/", {
headers: {
"Authorization": "Bearer valid-token",
},
});
const res = await app.request(req);
assertEquals(res.status, 200);
const json = await res.json();
assertEquals(json, {
userId: "test-user-id",
scopes: ["commander", "viewer"],
});
validateSessionStub.restore();
});
Deno.test("AuthMiddleware - Unauthorized request (missing token)", async () => {
const sdk = new AuthSdk({ authApiUrl: "http://localhost" });
const app = new Hono<{ Variables: Variables }>();
app.use("*", createAuthMiddleware(sdk));
app.get("/", (c) => c.text(`User: ${c.get("userId")}`));
const req = new Request("http://localhost/");
const res = await app.request(req);
assertEquals(res.status, 401);
const json = await res.json();
assertEquals(json, { error: "Unauthorized: Missing session token" });
});
Deno.test("AuthMiddleware - Unauthorized request (invalid token)", async () => {
const sdk = new AuthSdk({ authApiUrl: "http://localhost" });
const validateSessionStub = stub(
sdk,
"validateSession",
() => Promise.resolve({ valid: false, error: "Invalid token" }),
);
const app = new Hono<{ Variables: Variables }>();
app.use("*", createAuthMiddleware(sdk));
app.get("/", (c) => c.text(`User: ${c.get("userId")}`));
const req = new Request("http://localhost/", {
headers: {
"Authorization": "Bearer invalid-token",
},
});
const res = await app.request(req);
assertEquals(res.status, 401);
const json = await res.json();
assertEquals(json, { error: "Invalid token" });
validateSessionStub.restore();
});
Deno.test("requireScope - allows user when scope is present", async () => {
const app = new Hono<{ Variables: Variables }>();
app.use("*", (c, next) => {
c.set("userId", "test-user");
c.set("scopes", ["commander", "viewer"]);
return next();
});
app.get("/command", requireScope("commander"), (c) => c.text("Command OK"));
const req = new Request("http://localhost/command");
const res = await app.request(req);
assertEquals(res.status, 200);
assertEquals(await res.text(), "Command OK");
});
Deno.test("requireScope - allows user when global admin scope is present", async () => {
const app = new Hono<{ Variables: Variables }>();
app.use("*", (c, next) => {
c.set("userId", "admin-user");
c.set("scopes", ["admin"]);
return next();
});
app.get(
"/restricted",
requireScope("special-scope"),
(c) => c.text("Admin Override OK"),
);
const req = new Request("http://localhost/restricted");
const res = await app.request(req);
assertEquals(res.status, 200);
assertEquals(await res.text(), "Admin Override OK");
});
Deno.test("requireScope - rejects user when scope is missing", async () => {
const app = new Hono<{ Variables: Variables }>();
app.use("*", (c, next) => {
c.set("userId", "test-user");
c.set("scopes", ["viewer"]);
return next();
});
app.get("/command", requireScope("commander"), (c) => c.text("Command OK"));
const req = new Request("http://localhost/command");
const res = await app.request(req);
assertEquals(res.status, 403);
const json = await res.json();
assertEquals(json, {
error: "Forbidden: Required scope 'commander' missing.",
});
});