94 lines
3.2 KiB
TypeScript
94 lines
3.2 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
import {
|
|
codeHmac,
|
|
demoSmsCodeEnabled,
|
|
phoneHmac,
|
|
resetAuthEnvironmentForTests,
|
|
validateAuthEnvironment,
|
|
webOrigin,
|
|
} from "./auth.config.js";
|
|
|
|
const valid = {
|
|
PHONE_ENCRYPTION_KEY: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
|
|
PHONE_HMAC_KEY: "phone-hmac-key-that-is-at-least-32-bytes",
|
|
VERIFICATION_CODE_HMAC_KEY: "code-hmac-key-that-is-at-least-32-bytes!",
|
|
REFRESH_TOKEN_HMAC_KEY: "refresh-key-that-is-at-least-thirty-two-bytes",
|
|
JWT_SECRET: "jwt-secret-that-is-at-least-thirty-two-bytes",
|
|
WEB_ORIGIN: "http://localhost:3000",
|
|
};
|
|
|
|
describe("auth environment", () => {
|
|
const original = { ...process.env };
|
|
beforeEach(() => {
|
|
process.env.NODE_ENV = "test";
|
|
Object.assign(process.env, valid);
|
|
resetAuthEnvironmentForTests();
|
|
});
|
|
afterEach(() => {
|
|
process.env = { ...original, NODE_ENV: "test" };
|
|
resetAuthEnvironmentForTests();
|
|
});
|
|
|
|
it.each(Object.keys(valid))("fails fast when %s is missing", (name) => {
|
|
delete process.env[name];
|
|
expect(() => validateAuthEnvironment()).toThrow(`${name} is required`);
|
|
});
|
|
|
|
it.each(Object.keys(valid))("fails fast when %s is too short", (name) => {
|
|
process.env[name] = "short";
|
|
expect(() => validateAuthEnvironment()).toThrow();
|
|
});
|
|
|
|
it("rejects duplicate keys including the verification-code key", () => {
|
|
process.env.VERIFICATION_CODE_HMAC_KEY = process.env.PHONE_HMAC_KEY;
|
|
expect(() => validateAuthEnvironment()).toThrow(
|
|
"Auth secrets must be independent",
|
|
);
|
|
});
|
|
|
|
it("uses a verification-code key independent from the phone key", () => {
|
|
validateAuthEnvironment();
|
|
const phoneDigest = phoneHmac("+8613800138000");
|
|
const first = codeHmac(phoneDigest, "123456");
|
|
resetAuthEnvironmentForTests();
|
|
process.env.VERIFICATION_CODE_HMAC_KEY =
|
|
"different-code-key-that-is-at-least-32-bytes";
|
|
validateAuthEnvironment();
|
|
expect(phoneHmac("+8613800138000")).toBe(phoneDigest);
|
|
expect(codeHmac(phoneDigest, "123456")).not.toBe(first);
|
|
});
|
|
|
|
it("fails closed when WEB_ORIGIN is missing or is not one absolute HTTP origin", () => {
|
|
for (const value of [
|
|
undefined,
|
|
"localhost:3000",
|
|
"https://a.test/path",
|
|
"https://u:p@a.test",
|
|
"ftp://a.test",
|
|
]) {
|
|
resetAuthEnvironmentForTests();
|
|
if (value === undefined) delete process.env.WEB_ORIGIN;
|
|
else process.env.WEB_ORIGIN = value;
|
|
expect(() => validateAuthEnvironment()).toThrow(/WEB_ORIGIN/);
|
|
}
|
|
});
|
|
|
|
it("caches and exposes the validated web origin", () => {
|
|
validateAuthEnvironment();
|
|
expect(webOrigin()).toBe("http://localhost:3000");
|
|
});
|
|
|
|
it("returns demo codes only in test or explicitly enabled local development", () => {
|
|
delete process.env.DEMO_SMS_CODE_ENABLED;
|
|
expect(demoSmsCodeEnabled()).toBe(false);
|
|
process.env.DEMO_SMS_CODE_ENABLED = "true";
|
|
expect(demoSmsCodeEnabled()).toBe(true);
|
|
process.env.NODE_ENV = "staging";
|
|
expect(demoSmsCodeEnabled()).toBe(false);
|
|
process.env.NODE_ENV = "development";
|
|
expect(demoSmsCodeEnabled()).toBe(true);
|
|
process.env.NODE_ENV = "production";
|
|
expect(demoSmsCodeEnabled()).toBe(false);
|
|
});
|
|
});
|