fix(#414): extract trustedOrigins to getTrustedOrigins() with env vars
All checks were successful
ci/woodpecker/push/api Pipeline was successful
All checks were successful
ci/woodpecker/push/api Pipeline was successful
Replace hardcoded production URLs with environment-driven config. Reads NEXT_PUBLIC_APP_URL, NEXT_PUBLIC_API_URL, TRUSTED_ORIGINS. Localhost fallbacks only in development mode. Refs #414 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -18,12 +18,7 @@ vi.mock("better-auth/adapters/prisma", () => ({
|
||||
prismaAdapter: (...args: unknown[]) => mockPrismaAdapter(...args),
|
||||
}));
|
||||
|
||||
import {
|
||||
isOidcEnabled,
|
||||
validateOidcConfig,
|
||||
createAuth,
|
||||
getTrustedOrigins,
|
||||
} from "./auth.config";
|
||||
import { isOidcEnabled, validateOidcConfig, createAuth, getTrustedOrigins } from "./auth.config";
|
||||
|
||||
describe("auth.config", () => {
|
||||
// Store original env vars to restore after each test
|
||||
@@ -290,6 +285,127 @@ describe("auth.config", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("getTrustedOrigins", () => {
|
||||
it("should return localhost URLs when NODE_ENV is not production", () => {
|
||||
process.env.NODE_ENV = "development";
|
||||
|
||||
const origins = getTrustedOrigins();
|
||||
|
||||
expect(origins).toContain("http://localhost:3000");
|
||||
expect(origins).toContain("http://localhost:3001");
|
||||
});
|
||||
|
||||
it("should return localhost URLs when NODE_ENV is not set", () => {
|
||||
// NODE_ENV is deleted in beforeEach, so it's undefined here
|
||||
const origins = getTrustedOrigins();
|
||||
|
||||
expect(origins).toContain("http://localhost:3000");
|
||||
expect(origins).toContain("http://localhost:3001");
|
||||
});
|
||||
|
||||
it("should exclude localhost URLs in production", () => {
|
||||
process.env.NODE_ENV = "production";
|
||||
|
||||
const origins = getTrustedOrigins();
|
||||
|
||||
expect(origins).not.toContain("http://localhost:3000");
|
||||
expect(origins).not.toContain("http://localhost:3001");
|
||||
});
|
||||
|
||||
it("should parse TRUSTED_ORIGINS comma-separated values", () => {
|
||||
process.env.TRUSTED_ORIGINS =
|
||||
"https://app.mosaicstack.dev,https://api.mosaicstack.dev";
|
||||
|
||||
const origins = getTrustedOrigins();
|
||||
|
||||
expect(origins).toContain("https://app.mosaicstack.dev");
|
||||
expect(origins).toContain("https://api.mosaicstack.dev");
|
||||
});
|
||||
|
||||
it("should trim whitespace from TRUSTED_ORIGINS entries", () => {
|
||||
process.env.TRUSTED_ORIGINS =
|
||||
" https://app.mosaicstack.dev , https://api.mosaicstack.dev ";
|
||||
|
||||
const origins = getTrustedOrigins();
|
||||
|
||||
expect(origins).toContain("https://app.mosaicstack.dev");
|
||||
expect(origins).toContain("https://api.mosaicstack.dev");
|
||||
});
|
||||
|
||||
it("should filter out empty strings from TRUSTED_ORIGINS", () => {
|
||||
process.env.TRUSTED_ORIGINS = "https://app.mosaicstack.dev,,, ,";
|
||||
|
||||
const origins = getTrustedOrigins();
|
||||
|
||||
expect(origins).toContain("https://app.mosaicstack.dev");
|
||||
// No empty strings in the result
|
||||
origins.forEach((o) => expect(o).not.toBe(""));
|
||||
});
|
||||
|
||||
it("should include NEXT_PUBLIC_APP_URL", () => {
|
||||
process.env.NEXT_PUBLIC_APP_URL = "https://my-app.example.com";
|
||||
|
||||
const origins = getTrustedOrigins();
|
||||
|
||||
expect(origins).toContain("https://my-app.example.com");
|
||||
});
|
||||
|
||||
it("should include NEXT_PUBLIC_API_URL", () => {
|
||||
process.env.NEXT_PUBLIC_API_URL = "https://my-api.example.com";
|
||||
|
||||
const origins = getTrustedOrigins();
|
||||
|
||||
expect(origins).toContain("https://my-api.example.com");
|
||||
});
|
||||
|
||||
it("should deduplicate origins", () => {
|
||||
process.env.NEXT_PUBLIC_APP_URL = "http://localhost:3000";
|
||||
process.env.TRUSTED_ORIGINS = "http://localhost:3000,http://localhost:3001";
|
||||
// NODE_ENV not set, so localhost fallbacks are also added
|
||||
|
||||
const origins = getTrustedOrigins();
|
||||
|
||||
const countLocalhost3000 = origins.filter((o) => o === "http://localhost:3000").length;
|
||||
const countLocalhost3001 = origins.filter((o) => o === "http://localhost:3001").length;
|
||||
expect(countLocalhost3000).toBe(1);
|
||||
expect(countLocalhost3001).toBe(1);
|
||||
});
|
||||
|
||||
it("should handle all env vars missing gracefully", () => {
|
||||
// All env vars deleted in beforeEach; NODE_ENV is also deleted (not production)
|
||||
const origins = getTrustedOrigins();
|
||||
|
||||
// Should still return localhost fallbacks since not in production
|
||||
expect(origins).toContain("http://localhost:3000");
|
||||
expect(origins).toContain("http://localhost:3001");
|
||||
expect(origins).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("should return empty array when all env vars missing in production", () => {
|
||||
process.env.NODE_ENV = "production";
|
||||
|
||||
const origins = getTrustedOrigins();
|
||||
|
||||
expect(origins).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("should combine all sources correctly", () => {
|
||||
process.env.NEXT_PUBLIC_APP_URL = "https://app.mosaicstack.dev";
|
||||
process.env.NEXT_PUBLIC_API_URL = "https://api.mosaicstack.dev";
|
||||
process.env.TRUSTED_ORIGINS = "https://extra.example.com";
|
||||
process.env.NODE_ENV = "development";
|
||||
|
||||
const origins = getTrustedOrigins();
|
||||
|
||||
expect(origins).toContain("https://app.mosaicstack.dev");
|
||||
expect(origins).toContain("https://api.mosaicstack.dev");
|
||||
expect(origins).toContain("https://extra.example.com");
|
||||
expect(origins).toContain("http://localhost:3000");
|
||||
expect(origins).toContain("http://localhost:3001");
|
||||
expect(origins).toHaveLength(5);
|
||||
});
|
||||
});
|
||||
|
||||
describe("createAuth - session and cookie configuration", () => {
|
||||
beforeEach(() => {
|
||||
mockGenericOAuth.mockClear();
|
||||
|
||||
Reference in New Issue
Block a user