All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 50s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
81 lines
2.6 KiB
TypeScript
81 lines
2.6 KiB
TypeScript
import "../setup.ts";
|
|
import {
|
|
assert,
|
|
assertEquals,
|
|
assertFalse,
|
|
assertNotEquals,
|
|
} from "@std/assert";
|
|
import { SignJWT } from "@panva/jose";
|
|
import {
|
|
createJWT,
|
|
createMediaToken,
|
|
createPasswordResetToken,
|
|
hashPassword,
|
|
verifyJWT,
|
|
verifyMediaToken,
|
|
verifyPassword,
|
|
verifyPasswordResetToken,
|
|
} from "../../lib/jwt.ts";
|
|
|
|
const KEY = new TextEncoder().encode("test-secret-test-secret-test-secret");
|
|
|
|
Deno.test("hashPassword/verifyPassword round-trip with a random salt", async () => {
|
|
const a = await hashPassword("hunter22");
|
|
const b = await hashPassword("hunter22");
|
|
assertNotEquals(a, b);
|
|
assert(await verifyPassword("hunter22", a));
|
|
assertFalse(await verifyPassword("hunter23", a));
|
|
// A malformed stored hash never verifies.
|
|
assertFalse(await verifyPassword("hunter22", "not-a-hash"));
|
|
assertFalse(await verifyPassword("hunter22", ""));
|
|
});
|
|
|
|
Deno.test("session, media and reset tokens don't stand in for each other", async () => {
|
|
const userId = crypto.randomUUID();
|
|
const session = await createJWT({ userId, username: "u", role: "user" });
|
|
const media = await createMediaToken(userId);
|
|
const reset = await createPasswordResetToken(userId);
|
|
|
|
assertEquals((await verifyJWT(session))?.userId, userId);
|
|
assertEquals(await verifyJWT(media), null);
|
|
assertEquals(await verifyJWT(reset), null);
|
|
|
|
assertEquals((await verifyMediaToken(media))?.userId, userId);
|
|
assertEquals(await verifyMediaToken(session), null);
|
|
assertEquals(await verifyMediaToken(reset), null);
|
|
|
|
assertEquals((await verifyPasswordResetToken(reset))?.userId, userId);
|
|
assertEquals(await verifyPasswordResetToken(session), null);
|
|
assertEquals(await verifyPasswordResetToken(media), null);
|
|
});
|
|
|
|
Deno.test("verifyJWT: refuses a bad signature, an expired token, garbage", async () => {
|
|
const forged = await new SignJWT({
|
|
userId: "x",
|
|
username: "x",
|
|
role: "admin",
|
|
})
|
|
.setProtectedHeader({ alg: "HS256" })
|
|
.setExpirationTime("1h")
|
|
.sign(new TextEncoder().encode("some-other-secret"));
|
|
assertEquals(await verifyJWT(forged), null);
|
|
|
|
const expired = await new SignJWT({
|
|
userId: "x",
|
|
username: "x",
|
|
role: "user",
|
|
})
|
|
.setProtectedHeader({ alg: "HS256" })
|
|
.setExpirationTime(Math.floor(Date.now() / 1000) - 10)
|
|
.sign(KEY);
|
|
assertEquals(await verifyJWT(expired), null);
|
|
|
|
const badRole = await new SignJWT({ userId: "x", username: "x", role: "god" })
|
|
.setProtectedHeader({ alg: "HS256" })
|
|
.setExpirationTime("1h")
|
|
.sign(KEY);
|
|
assertEquals(await verifyJWT(badRole), null);
|
|
|
|
assertEquals(await verifyJWT("not.a.jwt"), null);
|
|
});
|