150 lines
4.6 KiB
TypeScript
150 lines
4.6 KiB
TypeScript
import { describe, expect, it } from "@suaveplan/testing/runner";
|
|
import {
|
|
AUTH_ROLLED_CODES,
|
|
AuthRolledError,
|
|
} from "../error-codes/error-codes.js";
|
|
import {
|
|
DEFAULT_COOKIE_NAME,
|
|
DEFAULT_MIN_PASSWORD_STRENGTH,
|
|
DEFAULT_REFRESH_TTL_SEC,
|
|
DEFAULT_ROTATION_PERIOD_MS,
|
|
DEFAULT_ROUTE_PREFIX,
|
|
DEFAULT_SESSION_TTL_SEC,
|
|
parseRolledAuthOptions,
|
|
} from "./options.js";
|
|
|
|
const baseRaw = (): Record<string, unknown> => ({
|
|
store: { __mark: "store" },
|
|
keys: [
|
|
{
|
|
kid: "k1",
|
|
privateJwk: { kty: "RSA" },
|
|
publicJwk: { kty: "RSA" },
|
|
active: true,
|
|
},
|
|
],
|
|
issuer: "https://api.example.com",
|
|
audience: "front",
|
|
});
|
|
|
|
describe("parseRolledAuthOptions", () => {
|
|
it("accepts a minimal valid options bag", { timeout: 5000 }, () => {
|
|
const opts = parseRolledAuthOptions(baseRaw());
|
|
expect(opts.issuer).toBe("https://api.example.com");
|
|
expect(opts.audience).toBe("front");
|
|
expect(opts.keys).toHaveLength(1);
|
|
});
|
|
|
|
it("throws DATABASE_REQUIRED when raw is not an object", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
for (const bad of [null, undefined, 42, "hi", [1]]) {
|
|
try {
|
|
parseRolledAuthOptions(bad);
|
|
throw new Error("did not throw");
|
|
} catch (err) {
|
|
expect(err).toBeInstanceOf(AuthRolledError);
|
|
expect((err as AuthRolledError).code).toBe(
|
|
AUTH_ROLLED_CODES.DATABASE_REQUIRED
|
|
);
|
|
}
|
|
}
|
|
});
|
|
|
|
it("throws DATABASE_REQUIRED when store is missing", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
const raw = baseRaw();
|
|
delete raw["store"];
|
|
try {
|
|
parseRolledAuthOptions(raw);
|
|
throw new Error("did not throw");
|
|
} catch (err) {
|
|
expect(err).toBeInstanceOf(AuthRolledError);
|
|
expect((err as AuthRolledError).code).toBe(
|
|
AUTH_ROLLED_CODES.DATABASE_REQUIRED
|
|
);
|
|
}
|
|
});
|
|
|
|
it("throws DATABASE_REQUIRED when store is null", { timeout: 5000 }, () => {
|
|
const raw = baseRaw();
|
|
raw["store"] = null;
|
|
expect(() => parseRolledAuthOptions(raw)).toThrow(AuthRolledError);
|
|
});
|
|
|
|
it("throws SIGNING_KEY_NOT_CONFIGURED on schema failure (zero keys)", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
const raw = baseRaw();
|
|
raw["keys"] = [];
|
|
try {
|
|
parseRolledAuthOptions(raw);
|
|
throw new Error("did not throw");
|
|
} catch (err) {
|
|
expect(err).toBeInstanceOf(AuthRolledError);
|
|
expect((err as AuthRolledError).code).toBe(
|
|
AUTH_ROLLED_CODES.SIGNING_KEY_NOT_CONFIGURED
|
|
);
|
|
}
|
|
});
|
|
|
|
it("throws SIGNING_KEY_NOT_CONFIGURED when more than one key is active", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
const raw = baseRaw();
|
|
raw["keys"] = [
|
|
{
|
|
kid: "k1",
|
|
privateJwk: { kty: "RSA" },
|
|
publicJwk: { kty: "RSA" },
|
|
active: true,
|
|
},
|
|
{
|
|
kid: "k2",
|
|
privateJwk: { kty: "RSA" },
|
|
publicJwk: { kty: "RSA" },
|
|
active: true,
|
|
},
|
|
];
|
|
expect(() => parseRolledAuthOptions(raw)).toThrow(AuthRolledError);
|
|
});
|
|
|
|
it("throws SIGNING_KEY_NOT_CONFIGURED when active key is revoked", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
const raw = baseRaw();
|
|
raw["keys"] = [
|
|
{
|
|
kid: "k1",
|
|
privateJwk: { kty: "RSA" },
|
|
publicJwk: { kty: "RSA" },
|
|
active: true,
|
|
revokedAt: 1,
|
|
},
|
|
];
|
|
expect(() => parseRolledAuthOptions(raw)).toThrow(AuthRolledError);
|
|
});
|
|
|
|
it("rejects non-slash routePrefix", { timeout: 5000 }, () => {
|
|
const raw = baseRaw();
|
|
raw["routePrefix"] = "auth";
|
|
expect(() => parseRolledAuthOptions(raw)).toThrow(AuthRolledError);
|
|
});
|
|
|
|
it("rejects unknown keys (strictObject)", { timeout: 5000 }, () => {
|
|
const raw = baseRaw();
|
|
(raw as Record<string, unknown>)["unknownField"] = 1;
|
|
expect(() => parseRolledAuthOptions(raw)).toThrow(AuthRolledError);
|
|
});
|
|
|
|
it("exposes named defaults", { timeout: 5000 }, () => {
|
|
expect(DEFAULT_COOKIE_NAME).toBe("suaveplan.session");
|
|
expect(DEFAULT_ROUTE_PREFIX).toBe("/auth");
|
|
expect(DEFAULT_SESSION_TTL_SEC).toBe(3600);
|
|
expect(DEFAULT_REFRESH_TTL_SEC).toBe(2592000);
|
|
expect(DEFAULT_ROTATION_PERIOD_MS).toBe(86_400_000);
|
|
expect(DEFAULT_MIN_PASSWORD_STRENGTH).toBe(3);
|
|
});
|
|
});
|