feat: add helpers and improve workspace auth flows
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
import { expect, test } from "bun:test";
|
||||
import { createContext } from "@wrnexus/core";
|
||||
import {
|
||||
getOriginalRequestMethod,
|
||||
getOriginalRequestOrigin,
|
||||
getOriginalRequestPath,
|
||||
getOriginalRequestUrl,
|
||||
redirectToLogin,
|
||||
} from "../src/index.ts";
|
||||
|
||||
function context(url: string, headers: HeadersInit = {}) {
|
||||
const parsed = new URL(url);
|
||||
return createContext(new Request(parsed, { headers }), parsed);
|
||||
}
|
||||
|
||||
test("uses the direct context URL when no gateway headers exist", () => {
|
||||
const ctx = context("https://app.example.test/account?tab=security");
|
||||
|
||||
expect(getOriginalRequestUrl(ctx).href).toBe("https://app.example.test/account?tab=security");
|
||||
expect(getOriginalRequestOrigin(ctx)).toBe("https://app.example.test");
|
||||
expect(getOriginalRequestPath(ctx)).toBe("/account?tab=security");
|
||||
expect(getOriginalRequestMethod(ctx)).toBe("GET");
|
||||
});
|
||||
|
||||
test("reconstructs an allowed original gateway URL", () => {
|
||||
const ctx = context("http://sso.localhost:3000/api/verify", {
|
||||
"x-forwarded-host": "admin.localhost:3000",
|
||||
"x-forwarded-proto": "http",
|
||||
"x-original-method": "GET",
|
||||
"x-original-uri": "/users?page=2",
|
||||
});
|
||||
|
||||
const url = getOriginalRequestUrl(ctx, { allowedHosts: ["admin.localhost:3000"] });
|
||||
expect(url.href).toBe("http://admin.localhost:3000/users?page=2");
|
||||
expect(getOriginalRequestMethod(ctx)).toBe("GET");
|
||||
});
|
||||
|
||||
test("rejects untrusted hosts and unsafe request paths", () => {
|
||||
const untrusted = context("http://sso.localhost/api/verify", {
|
||||
"x-forwarded-host": "evil.example",
|
||||
"x-original-uri": "/",
|
||||
});
|
||||
const unsafePath = context("http://sso.localhost/api/verify", {
|
||||
"x-forwarded-host": "admin.localhost",
|
||||
"x-original-uri": "//evil.example/steal",
|
||||
});
|
||||
|
||||
expect(() => getOriginalRequestUrl(untrusted)).toThrow("Untrusted forwarded host");
|
||||
expect(() => getOriginalRequestUrl(unsafePath, { allowedHosts: ["admin.localhost"] })).toThrow(
|
||||
"absolute request path",
|
||||
);
|
||||
});
|
||||
|
||||
test("creates a safe login redirect with an encoded returnTo URL", () => {
|
||||
const ctx = context("http://sso.localhost:3000/api/verify", {
|
||||
"x-forwarded-host": "admin.localhost:3000",
|
||||
"x-forwarded-proto": "http",
|
||||
"x-original-uri": "/reports?range=week",
|
||||
});
|
||||
|
||||
const response = redirectToLogin(ctx, "/login", {
|
||||
allowedHosts: new Set(["admin.localhost:3000"]),
|
||||
});
|
||||
const location = new URL(response.headers.get("location")!);
|
||||
|
||||
expect(response.status).toBe(302);
|
||||
expect(location.origin).toBe("http://sso.localhost:3000");
|
||||
expect(location.pathname).toBe("/login");
|
||||
expect(location.searchParams.get("returnTo")).toBe(
|
||||
"http://admin.localhost:3000/reports?range=week",
|
||||
);
|
||||
});
|
||||
|
||||
test("supports an allowed-host callback and custom response options", () => {
|
||||
const ctx = context("https://login.example.test/api/verify", {
|
||||
"x-forwarded-host": "reports.example.test",
|
||||
"x-forwarded-proto": "https",
|
||||
"x-original-uri": "/",
|
||||
});
|
||||
const response = redirectToLogin(ctx, "https://login.example.test/sign-in?tenant=acme", {
|
||||
allowedHosts: (host) => host.endsWith(".example.test"),
|
||||
returnToParam: "next",
|
||||
status: 303,
|
||||
});
|
||||
const location = new URL(response.headers.get("location")!);
|
||||
|
||||
expect(response.status).toBe(303);
|
||||
expect(location.searchParams.get("tenant")).toBe("acme");
|
||||
expect(location.searchParams.get("next")).toBe("https://reports.example.test/");
|
||||
});
|
||||
Reference in New Issue
Block a user