fix(web): validate proxy sessions

This commit is contained in:
GautamSharma99 2026-07-13 03:39:07 +05:30
parent 2cebe81512
commit 43a9ebd7d6
2 changed files with 122 additions and 6 deletions

View file

@ -0,0 +1,95 @@
import { beforeEach, describe, expect, it, mock } from "bun:test"
const getSession = mock()
mock.module("@lib/auth.middleware", () => ({
middlewareAuthClient: { getSession },
}))
const { default: proxy } = await import("./middleware")
beforeEach(() => {
getSession.mockReset()
})
describe("auth proxy", () => {
it("rejects API requests with a forged session cookie", async () => {
getSession.mockResolvedValue(null)
const response = await proxy(
new Request("https://app.supermemory.ai/api/onboarding/research", {
headers: {
cookie: "better-auth.session_token=forged",
},
}),
)
expect(response.status).toBe(401)
expect(await response.json()).toEqual({ error: "Unauthorized" })
expect(getSession).toHaveBeenCalledWith({
fetchOptions: {
headers: {
cookie: "better-auth.session_token=forged",
},
},
})
})
it("does not validate API requests without a session cookie", async () => {
const response = await proxy(
new Request("https://app.supermemory.ai/api/onboarding/research"),
)
expect(response.status).toBe(401)
expect(getSession).not.toHaveBeenCalled()
})
it("redirects page requests with an expired session cookie", async () => {
getSession.mockResolvedValue(null)
const response = await proxy(
new Request("https://app.supermemory.ai/settings", {
headers: {
cookie: "better-auth.session_token=expired",
},
}),
)
expect(response.status).toBe(307)
expect(response.headers.get("location")).toBe(
"https://app.supermemory.ai/login?redirect=https%3A%2F%2Fapp.supermemory.ai%2Fsettings",
)
})
it("allows API requests with a valid session", async () => {
getSession.mockResolvedValue({
session: {},
user: {},
})
const response = await proxy(
new Request("https://app.supermemory.ai/api/onboarding/research", {
headers: {
cookie: "better-auth.session_token=valid",
},
}),
)
expect(response.status).toBe(200)
expect(response.headers.get("x-middleware-next")).toBe("1")
})
it("fails closed when session validation is unavailable", async () => {
getSession.mockRejectedValue(new Error("backend unavailable"))
const response = await proxy(
new Request("https://app.supermemory.ai/api/onboarding/research", {
headers: {
cookie: "better-auth.session_token=unknown",
},
}),
)
expect(response.status).toBe(401)
})
})

View file

@ -1,5 +1,6 @@
import { getSessionCookie } from "better-auth/cookies"
import { NextResponse } from "next/server"
import { middlewareAuthClient } from "@lib/auth.middleware"
import { getPublicRequestUrl } from "@/lib/url-helpers"
const LOCAL_DEV_HOSTS = new Set(["localhost", "127.0.0.1", "::1"])
@ -11,6 +12,22 @@ function getAuthSessionCookie(request: Request): string | null {
)
}
async function hasValidSession(request: Request): Promise<boolean> {
try {
const session = await middlewareAuthClient.getSession({
fetchOptions: {
headers: {
cookie: request.headers.get("cookie") ?? "",
},
},
})
return Boolean(session?.session && session.user)
} catch (error) {
console.error("[PROXY] Failed to validate session", error)
return false
}
}
export default async function proxy(request: Request) {
console.debug("[PROXY] === PROXY START ===")
const url = getPublicRequestUrl(request)
@ -58,22 +75,26 @@ export default async function proxy(request: Request) {
return NextResponse.next()
}
const validSession = sessionCookie ? await hasValidSession(request) : false
if (url.pathname.startsWith("/api/")) {
if (!sessionCookie) {
console.debug("[MIDDLEWARE] API route without session, returning 401")
if (!validSession) {
console.debug(
"[MIDDLEWARE] API route without valid session, returning 401",
)
return new Response(JSON.stringify({ error: "Unauthorized" }), {
status: 401,
headers: { "Content-Type": "application/json" },
})
}
console.debug("[MIDDLEWARE] API route with session, allowing access")
console.debug("[MIDDLEWARE] API route with valid session, allowing access")
return NextResponse.next()
}
// If no session cookie and not on a public path, redirect to login
if (!sessionCookie) {
// If no valid session and not on a public path, redirect to login
if (!validSession) {
console.debug(
"[PROXY] No session cookie and not on public path, redirecting to /login",
"[PROXY] No valid session and not on public path, redirecting to /login",
)
const loginUrl = new URL("/login", url.origin)
loginUrl.searchParams.set("redirect", url.toString())