mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-20 14:34:25 +00:00
# Which Problems Are Solved - Logo and heading overlap on login pages due to a -mb-4 negative margin on the logo container - The /login route returns a 500 when `listSessions` fails (e.g. stale session cookies), because loadSessions has no error handling. - The proxy logs `"fetch() returned undefined"` because `fetchIframeOrigins` returns undefined, which lru-cache's forceFetch treats as a fetch failure. # How the Problems Are Solved - Remove `-mb-4` from the logo container in DynamicTheme. - Wrap `loadSessions` in a try/catch — failures fall through as empty sessions instead of crashing. - Return `null` instead of `undefined` from `fetchIframeOrigins` so lru-cache caches it normally. --------- Co-authored-by: Livio Spring <[email protected]> Co-authored-by: conblem <[email protected]>
282 lines
9.6 KiB
TypeScript
282 lines
9.6 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
|
import { getInstanceHost, getPublicHost, getPublicHostWithProtocol } from "./host";
|
|
|
|
describe("Host utility functions", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
describe("getInstanceHost", () => {
|
|
test("should use x-zitadel-instance-host when available", () => {
|
|
const mockHeaders = {
|
|
get: vi.fn((key: string) => {
|
|
if (key === "x-zitadel-instance-host") return "instance.zitadel.cloud";
|
|
if (key === "x-zitadel-forward-host") return "forward.zitadel.cloud";
|
|
return null;
|
|
}),
|
|
} as any;
|
|
|
|
const result = getInstanceHost(mockHeaders);
|
|
expect(result).toBe("instance.zitadel.cloud");
|
|
expect(mockHeaders.get).toHaveBeenCalledWith("x-zitadel-instance-host");
|
|
});
|
|
|
|
test("should use x-zitadel-forward-host when x-zitadel-instance-host is not available", () => {
|
|
const mockHeaders = {
|
|
get: vi.fn((key: string) => {
|
|
if (key === "x-zitadel-instance-host") return null;
|
|
if (key === "x-zitadel-forward-host") return "forward.zitadel.cloud";
|
|
return null;
|
|
}),
|
|
} as any;
|
|
|
|
const result = getInstanceHost(mockHeaders);
|
|
expect(result).toBe("forward.zitadel.cloud");
|
|
expect(mockHeaders.get).toHaveBeenCalledWith("x-zitadel-instance-host");
|
|
expect(mockHeaders.get).toHaveBeenCalledWith("x-zitadel-forward-host");
|
|
});
|
|
});
|
|
|
|
describe("getPublicHostWithProtocol", () => {
|
|
test("should return https for production domain", () => {
|
|
const mockHeaders = {
|
|
get: vi.fn(() => "zitadel.com"),
|
|
} as any;
|
|
|
|
const result = getPublicHostWithProtocol(mockHeaders);
|
|
expect(result).toBe("https://zitadel.com");
|
|
});
|
|
|
|
test("should return http for localhost", () => {
|
|
const mockHeaders = {
|
|
get: vi.fn(() => "localhost:3000"),
|
|
} as any;
|
|
|
|
const result = getPublicHostWithProtocol(mockHeaders);
|
|
expect(result).toBe("http://localhost:3000");
|
|
});
|
|
|
|
test("should return http for localhost without port", () => {
|
|
const mockHeaders = {
|
|
get: vi.fn(() => "localhost"),
|
|
} as any;
|
|
|
|
const result = getPublicHostWithProtocol(mockHeaders);
|
|
expect(result).toBe("http://localhost");
|
|
});
|
|
|
|
test("should return https for custom domain", () => {
|
|
const mockHeaders = {
|
|
get: vi.fn(() => "auth.company.com"),
|
|
} as any;
|
|
|
|
const result = getPublicHostWithProtocol(mockHeaders);
|
|
expect(result).toBe("https://auth.company.com");
|
|
});
|
|
});
|
|
|
|
describe("Real-world scenarios", () => {
|
|
test("should handle Vercel rewrite scenario", () => {
|
|
const mockHeaders = {
|
|
get: vi.fn((key: string) => {
|
|
// Simulate Vercel rewrite: zitadel.com/login -> login-zitadel-qa.vercel.app
|
|
if (key === "x-forwarded-host") return "zitadel.com";
|
|
if (key === "host") return "login-zitadel-qa.vercel.app";
|
|
return null;
|
|
}),
|
|
} as any;
|
|
|
|
const result = getPublicHostWithProtocol(mockHeaders);
|
|
expect(result).toBe("https://zitadel.com");
|
|
});
|
|
|
|
test("should handle CloudFlare proxy scenario", () => {
|
|
const mockHeaders = {
|
|
get: vi.fn((key: string) => {
|
|
if (key === "x-forwarded-host") return "auth.company.com";
|
|
if (key === "x-original-host") return null;
|
|
if (key === "host") return "cloudflare-worker.workers.dev";
|
|
return null;
|
|
}),
|
|
} as any;
|
|
|
|
const result = getPublicHost(mockHeaders);
|
|
expect(result).toBe("auth.company.com");
|
|
});
|
|
|
|
test("should handle development environment", () => {
|
|
const mockHeaders = {
|
|
get: vi.fn((key: string) => {
|
|
if (key === "host") return "localhost:3000";
|
|
return null;
|
|
}),
|
|
} as any;
|
|
|
|
const result = getPublicHostWithProtocol(mockHeaders);
|
|
expect(result).toBe("http://localhost:3000");
|
|
});
|
|
|
|
test("should handle staging environment with subdomain", () => {
|
|
const mockHeaders = {
|
|
get: vi.fn((key: string) => {
|
|
if (key === "x-forwarded-host") return "staging-auth.company.com";
|
|
if (key === "host") return "staging-internal.vercel.app";
|
|
return null;
|
|
}),
|
|
} as any;
|
|
|
|
const result = getPublicHostWithProtocol(mockHeaders);
|
|
expect(result).toBe("https://staging-auth.company.com");
|
|
});
|
|
|
|
test("should prioritize x-zitadel-instance-host in multi-tenant scenario", () => {
|
|
const mockHeaders = {
|
|
get: vi.fn((key: string) => {
|
|
if (key === "x-zitadel-instance-host") return "customer.zitadel.cloud";
|
|
if (key === "x-forwarded-host") return "accounts.company.com";
|
|
if (key === "host") return "internal.vercel.app";
|
|
return null;
|
|
}),
|
|
} as any;
|
|
|
|
const result = getInstanceHost(mockHeaders);
|
|
expect(result).toBe("customer.zitadel.cloud");
|
|
});
|
|
});
|
|
|
|
describe("Edge cases", () => {
|
|
test("should handle IPv4 addresses", () => {
|
|
const mockHeaders = {
|
|
get: vi.fn(() => "192.168.1.100:3000"),
|
|
} as any;
|
|
|
|
const result = getPublicHostWithProtocol(mockHeaders);
|
|
expect(result).toBe("https://192.168.1.100:3000");
|
|
});
|
|
|
|
test("should handle IPv6 addresses", () => {
|
|
const mockHeaders = {
|
|
get: vi.fn(() => "[::1]:3000"),
|
|
} as any;
|
|
|
|
const result = getPublicHostWithProtocol(mockHeaders);
|
|
expect(result).toBe("https://[::1]:3000");
|
|
});
|
|
|
|
test("should handle hosts with ports", () => {
|
|
const mockHeaders = {
|
|
get: vi.fn(() => "zitadel.com:8080"),
|
|
} as any;
|
|
|
|
const result = getPublicHostWithProtocol(mockHeaders);
|
|
expect(result).toBe("https://zitadel.com:8080");
|
|
});
|
|
|
|
test("should handle localhost with different ports", () => {
|
|
const mockHeaders = {
|
|
get: vi.fn(() => "localhost:8080"),
|
|
} as any;
|
|
|
|
const result = getPublicHostWithProtocol(mockHeaders);
|
|
expect(result).toBe("http://localhost:8080");
|
|
});
|
|
});
|
|
|
|
describe("getPublicHost", () => {
|
|
test("should use x-zitadel-public-host when available", () => {
|
|
const mockHeaders = {
|
|
get: vi.fn((key: string) => {
|
|
if (key === "x-zitadel-public-host") return "public.zitadel.cloud";
|
|
if (key === "x-forwarded-host") return "accounts.company.com";
|
|
return null;
|
|
}),
|
|
} as any;
|
|
|
|
const result = getPublicHost(mockHeaders);
|
|
expect(result).toBe("public.zitadel.cloud");
|
|
expect(mockHeaders.get).toHaveBeenCalledWith("x-zitadel-public-host");
|
|
});
|
|
|
|
test("should use x-zitadel-forward-host when x-zitadel-public-host is not available", () => {
|
|
const mockHeaders = {
|
|
get: vi.fn((key: string) => {
|
|
if (key === "x-zitadel-public-host") return null;
|
|
if (key === "x-zitadel-forward-host") return "forward.zitadel.cloud";
|
|
if (key === "x-forwarded-host") return "accounts.company.com";
|
|
return null;
|
|
}),
|
|
} as any;
|
|
|
|
const result = getPublicHost(mockHeaders);
|
|
expect(result).toBe("forward.zitadel.cloud");
|
|
expect(mockHeaders.get).toHaveBeenCalledWith("x-zitadel-public-host");
|
|
expect(mockHeaders.get).toHaveBeenCalledWith("x-zitadel-forward-host");
|
|
});
|
|
|
|
test("should use x-forwarded-host when neither x-zitadel-public-host nor x-zitadel-forward-host is available", () => {
|
|
const mockHeaders = {
|
|
get: vi.fn((key: string) => {
|
|
if (key === "x-zitadel-public-host") return null;
|
|
if (key === "x-zitadel-forward-host") return null;
|
|
if (key === "x-forwarded-host") return "accounts.company.com";
|
|
if (key === "host") return "internal.server";
|
|
return null;
|
|
}),
|
|
} as any;
|
|
|
|
const result = getPublicHost(mockHeaders);
|
|
expect(result).toBe("accounts.company.com");
|
|
expect(mockHeaders.get).toHaveBeenCalledWith("x-zitadel-public-host");
|
|
expect(mockHeaders.get).toHaveBeenCalledWith("x-zitadel-forward-host");
|
|
expect(mockHeaders.get).toHaveBeenCalledWith("x-forwarded-host");
|
|
});
|
|
|
|
test("should fall back to host when x-forwarded-host is not available", () => {
|
|
const mockHeaders = {
|
|
get: vi.fn((key: string) => {
|
|
if (key === "x-zitadel-public-host") return null;
|
|
if (key === "x-zitadel-forward-host") return null;
|
|
if (key === "x-forwarded-host") return null;
|
|
if (key === "host") return "localhost:3000";
|
|
return null;
|
|
}),
|
|
} as any;
|
|
|
|
const result = getPublicHost(mockHeaders);
|
|
expect(result).toBe("localhost:3000");
|
|
expect(mockHeaders.get).toHaveBeenCalledWith("x-forwarded-host");
|
|
expect(mockHeaders.get).toHaveBeenCalledWith("host");
|
|
});
|
|
|
|
test("should throw error when no host is found", () => {
|
|
const mockHeaders = {
|
|
get: vi.fn(() => null),
|
|
} as any;
|
|
|
|
expect(() => getPublicHost(mockHeaders)).toThrow("No host found in headers");
|
|
});
|
|
|
|
test("should differ from getInstanceHost when x-zitadel-instance-host is present", () => {
|
|
const mockHeaders = {
|
|
get: vi.fn((key: string) => {
|
|
if (key === "x-zitadel-instance-host") return "instance.zitadel.cloud";
|
|
if (key === "x-forwarded-host") return "accounts.company.com";
|
|
if (key === "host") return "internal.server";
|
|
return null;
|
|
}),
|
|
} as any;
|
|
|
|
const instanceHost = getInstanceHost(mockHeaders);
|
|
const publicHost = getPublicHost(mockHeaders);
|
|
|
|
expect(instanceHost).toBe("instance.zitadel.cloud");
|
|
expect(publicHost).toBe("accounts.company.com");
|
|
expect(instanceHost).not.toBe(publicHost);
|
|
});
|
|
});
|
|
});
|