import { describe, expect, it } from "vitest"; import { getAuthRedirectFromSearch, getCurrentAuthRedirectFromHref, getOAuthAuthorizeRedirectFromSearch, getOAuthSignedQuery, getSignInHref, getVerifyEmailSearch, normalizeAuthRedirect, } from "./auth-redirect"; const oauthSearch = new URLSearchParams({ response_type: "code", client_id: "claude-client", redirect_uri: "https://claude.ai/api/mcp/auth_callback", scope: "offline_access mcp", state: "state-123", code_challenge: "challenge-123", code_challenge_method: "S256", resource: "https://app.openseo.so/mcp", exp: "1778271800", sig: "signed-value", }).toString(); describe("auth redirect helpers", () => { it("defaults unsafe or missing redirects to the app root", () => { expect(normalizeAuthRedirect(undefined)).toBe("/"); expect(normalizeAuthRedirect("https://evil.example/app")).toBe("/"); expect(normalizeAuthRedirect("//evil.example/app")).toBe("/"); }); it("keeps same-origin relative redirects", () => { expect( normalizeAuthRedirect("/api/auth/oauth2/authorize?client_id=abc"), ).toBe("/api/auth/oauth2/authorize?client_id=abc"); }); it("rejects external and protocol-relative redirects", () => { expect(normalizeAuthRedirect("https://evil.test")).toBe("/"); expect(normalizeAuthRedirect("//evil.test")).toBe("/"); }); it("builds sign-in links with the redirect query only when needed", () => { expect(getSignInHref("/")).toBe("/sign-in"); expect(getSignInHref("/oauth-consent?client_id=abc")).toBe( "/sign-in?redirect=%2Foauth-consent%3Fclient_id%3Dabc", ); }); it("builds verify-email search params without a root redirect", () => { expect(getVerifyEmailSearch("ben@example.com", "/")).toEqual({ email: "ben@example.com", }); expect( getVerifyEmailSearch("ben@example.com", "/onboarding?step=0"), ).toEqual({ email: "ben@example.com", redirect: "/onboarding?step=0", }); }); it("extracts the current path, query, and hash from hrefs", () => { expect( getCurrentAuthRedirectFromHref( "https://open-seo.test/projects?tab=keywords#top", ), ).toBe("/projects?tab=keywords#top"); }); it("preserves safe internal redirects", () => { expect(getAuthRedirectFromSearch("", "/app")).toBe("/app"); }); it("extracts Better Auth signed OAuth query parameters through sig", () => { const signedQuery = getOAuthSignedQuery( `${oauthSearch}&ignored_after_sig=true`, ); expect(signedQuery).toBe(oauthSearch); }); it("builds an internal OAuth authorize continuation redirect", () => { const redirect = getOAuthAuthorizeRedirectFromSearch(oauthSearch); expect(redirect).toBe(`/api/auth/oauth2/authorize?${oauthSearch}`); expect(redirect).toContain("client_id=claude-client"); expect(redirect).toContain( "redirect_uri=https%3A%2F%2Fclaude.ai%2Fapi%2Fmcp%2Fauth_callback", ); expect(redirect).toContain("scope=offline_access+mcp"); expect(redirect).toContain("state=state-123"); expect(redirect).toContain("code_challenge=challenge-123"); expect(redirect).toContain("code_challenge_method=S256"); expect(redirect).toContain("resource=https%3A%2F%2Fapp.openseo.so%2Fmcp"); expect(redirect).toContain("exp=1778271800"); expect(redirect).toContain("sig=signed-value"); }); it("prefers OAuth continuation over a generic redirect", () => { expect(getAuthRedirectFromSearch(oauthSearch, "/app")).toBe( `/api/auth/oauth2/authorize?${oauthSearch}`, ); }); });