You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airavata.apache.org by ya...@apache.org on 2026/10/18 03:54:30 UTC

(airavata-custos) 01/11: block off-site end-session callback redirects

This is an automated email from the ASF dual-hosted git repository.

yasithdev pushed a commit to branch stack-bugfixes
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git

commit cfb83c35fa16f710f2c7ccec0ce980c793760d3b
Author: Yasith Jayawardana <ya...@icloud.com>
AuthorDate: Sat Oct 17 22:51:49 2026 -0400

    block off-site end-session callback redirects
---
 web/src/app/api/auth/end-session/__tests__/route.test.ts | 12 ++++++++++++
 web/src/app/api/auth/end-session/route.ts                |  4 +++-
 2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/web/src/app/api/auth/end-session/__tests__/route.test.ts b/web/src/app/api/auth/end-session/__tests__/route.test.ts
index 64e9e0860..4420014cd 100644
--- a/web/src/app/api/auth/end-session/__tests__/route.test.ts
+++ b/web/src/app/api/auth/end-session/__tests__/route.test.ts
@@ -75,6 +75,18 @@ describe("end-session route", () => {
     expect(res.headers.get("location")).toBe("https://portal.example.org/");
   });
 
+  it("falls back to the deployment root for off-site callbacks", async () => {
+    for (const callbackUrl of ["//evil.example", "https://evil.example/x"]) {
+      fetchMock.mockResolvedValueOnce(discoveryResponse({}));
+      const res = await GET(
+        new NextRequest(
+          `http://localhost:3000/api/auth/end-session?callbackUrl=${encodeURIComponent(callbackUrl)}`,
+        ),
+      );
+      expect(res.headers.get("location")).toBe("https://portal.example.org/");
+    }
+  });
+
   it("redirects locally when discovery fails", async () => {
     fetchMock.mockRejectedValueOnce(new Error("network"));
     const res = await GET(request());
diff --git a/web/src/app/api/auth/end-session/route.ts b/web/src/app/api/auth/end-session/route.ts
index 37281efd8..68e703d5a 100644
--- a/web/src/app/api/auth/end-session/route.ts
+++ b/web/src/app/api/auth/end-session/route.ts
@@ -64,7 +64,9 @@ export async function GET(request: NextRequest) {
   const callbackUrl = request.nextUrl.searchParams.get("callbackUrl") ?? "/";
   // Behind a reverse proxy request.nextUrl.origin is the internal address.
   const origin = serverEnv.NEXTAUTH_URL ?? request.nextUrl.origin;
-  const postLogout = new URL(callbackUrl, origin).toString();
+  const resolved = new URL(callbackUrl, origin);
+  const base = new URL("/", origin);
+  const postLogout = (resolved.origin === base.origin ? resolved : base).toString();
 
   const session = await auth();
   const idToken = session?.idToken ?? null;