Files
cfw-auth/tests/auth-config.test.ts

203 lines
6.5 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
booleanEnv,
csvEnv,
optionalEnv,
requiredEnv,
trustedOrigins,
type Env,
} from "../src/env";
import { buildAuthEmail, sendEmail } from "../src/email";
import { createGenericOAuthProviders, createSocialProviders } from "../src/oauth";
import { createAuthPlugins } from "../src/plugins";
const env: Env = {
BETTER_AUTH_URL: "http://localhost:8788",
TRUSTED_ORIGINS: " http://localhost:8787, https://app.example.com ",
};
describe("auth env helpers", () => {
it("parses trusted origins from comma-separated env", () => {
expect(trustedOrigins(env)).toEqual(["http://localhost:8787", "https://app.example.com"]);
});
it("parses optional csv values", () => {
expect(csvEnv(" google, github ,, ")).toEqual(["google", "github"]);
});
it("parses boolean env values", () => {
expect(booleanEnv("true")).toBe(true);
expect(booleanEnv("1")).toBe(true);
expect(booleanEnv("false")).toBe(false);
expect(booleanEnv(undefined)).toBe(false);
});
it("throws for missing required env", () => {
expect(() => requiredEnv({}, "MAIL_FROM")).toThrow("Missing required env: MAIL_FROM");
});
it("returns optional env values", () => {
expect(optionalEnv({ MAIL_FROM: "noreply@example.com" }, "MAIL_FROM")).toBe(
"noreply@example.com",
);
expect(optionalEnv({}, "MAIL_FROM")).toBeUndefined();
});
});
describe("auth email adapter", () => {
it("builds a verification email", () => {
const message = buildAuthEmail({
kind: "verify-email",
to: "user@example.com",
url: "https://auth.example.com/verify",
});
expect(message.subject).toBe("Verify your email");
expect(message.to).toBe("user@example.com");
expect(message.text).toContain("https://auth.example.com/verify");
});
it("fails clearly when no mail provider is configured", async () => {
await expect(
sendEmail(
{
BETTER_AUTH_URL: "http://localhost:8788",
TRUSTED_ORIGINS: "http://localhost:8787",
},
{
to: "user@example.com",
subject: "Test",
text: "Test",
},
),
).rejects.toThrow("MAIL_PROVIDER is not configured");
});
});
describe("auth plugins", () => {
it("does not enable captcha without provider config", () => {
const plugins = createAuthPlugins({
BETTER_AUTH_URL: "http://localhost:8788",
TRUSTED_ORIGINS: "http://localhost:8787",
});
expect(plugins.map((plugin) => plugin.id)).not.toContain("captcha");
});
it("enables captcha when Cloudflare Turnstile config is present", () => {
const plugins = createAuthPlugins({
BETTER_AUTH_URL: "http://localhost:8788",
TRUSTED_ORIGINS: "http://localhost:8787",
CAPTCHA_PROVIDER: "cloudflare-turnstile",
CAPTCHA_SECRET_KEY: "secret",
});
expect(plugins.map((plugin) => plugin.id)).toContain("captcha");
});
it("enables email otp plugin by default", () => {
const plugins = createAuthPlugins({
BETTER_AUTH_URL: "http://localhost:8788",
TRUSTED_ORIGINS: "http://localhost:8787",
});
expect(plugins.map((plugin) => plugin.id)).toContain("email-otp");
});
it("enables account security and admin plugins", () => {
const plugins = createAuthPlugins({
BETTER_AUTH_URL: "http://localhost:8788",
TRUSTED_ORIGINS: "http://localhost:8787",
});
expect(plugins.map((plugin) => plugin.id)).toEqual(
expect.arrayContaining(["two-factor", "multi-session", "last-login-method", "admin"]),
);
});
it("enables jwt and bearer only when requested", () => {
const disabled = createAuthPlugins({
BETTER_AUTH_URL: "http://localhost:8788",
TRUSTED_ORIGINS: "http://localhost:8787",
});
expect(disabled.map((plugin) => plugin.id)).not.toContain("jwt");
expect(disabled.map((plugin) => plugin.id)).not.toContain("bearer");
const enabled = createAuthPlugins({
BETTER_AUTH_URL: "http://localhost:8788",
TRUSTED_ORIGINS: "http://localhost:8787",
ENABLE_JWT: "true",
ENABLE_BEARER: "true",
});
expect(enabled.map((plugin) => plugin.id)).toEqual(expect.arrayContaining(["jwt", "bearer"]));
});
});
describe("oauth config", () => {
it("creates built-in social providers from env", () => {
const providers = createSocialProviders({
BETTER_AUTH_URL: "http://localhost:8788",
TRUSTED_ORIGINS: "http://localhost:8787",
GOOGLE_CLIENT_ID: "google-id",
GOOGLE_CLIENT_SECRET: "google-secret",
GITHUB_CLIENT_ID: "github-id",
GITHUB_CLIENT_SECRET: "github-secret",
});
expect(Object.keys(providers)).toEqual(["google", "github"]);
});
it("creates generic oauth providers from env", () => {
const providers = createGenericOAuthProviders({
BETTER_AUTH_URL: "http://localhost:8788",
TRUSTED_ORIGINS: "http://localhost:8787",
GENERIC_OAUTH_PROVIDER_ID: "workos",
GENERIC_OAUTH_DISCOVERY_URL: "https://example.com/.well-known/openid-configuration",
GENERIC_OAUTH_CLIENT_ID: "client-id",
GENERIC_OAUTH_CLIENT_SECRET: "client-secret",
});
expect(providers).toEqual([
{
providerId: "workos",
discoveryUrl: "https://example.com/.well-known/openid-configuration",
clientId: "client-id",
clientSecret: "client-secret",
scopes: ["openid", "email", "profile"],
},
]);
});
});
describe("passkey config", () => {
it("enables passkey when rp config is present", () => {
const plugins = createAuthPlugins({
BETTER_AUTH_URL: "http://localhost:8788",
TRUSTED_ORIGINS: "http://localhost:8787",
PASSKEY_RP_ID: "localhost",
PASSKEY_RP_NAME: "cfw-auth",
PASSKEY_ORIGIN: "http://localhost:8788",
});
expect(plugins.map((plugin) => plugin.id)).toContain("passkey");
});
});
describe("Better Auth migration config", () => {
it("exports a static auth instance for the Better Auth CLI", async () => {
const module = await import("../src/auth.migration");
expect(module.auth).toBeDefined();
expect(module.default).toBe(module.auth);
expect(typeof module.auth.handler).toBe("function");
});
it("keeps schema-affecting plugins enabled for generated migrations", async () => {
const { migrationPlugins } = await import("../src/auth.migration");
const pluginIds = migrationPlugins.map((plugin) => plugin.id);
expect(pluginIds).toEqual(
expect.arrayContaining([
"open-api",
"email-otp",
"two-factor",
"multi-session",
"last-login-method",
"admin",
"passkey",
]),
);
});
});