From 95f11521c32595efecdb568998e1a14816974c0e Mon Sep 17 00:00:00 2001
From: Anish Sarkar <104695310+AnishSarkar22@users.noreply.github.com>
Date: Wed, 18 Mar 2026 17:00:54 +0530
Subject: [PATCH] refactor: migrate OAuth result handling from sessionStorage
to cookies and update connector callback implementation
- Removed the ConnectorCallbackPage component and replaced it with a route handler that sets the OAuth result in a cookie.
- Updated the useConnectorDialog hook to read from the cookie instead of sessionStorage, ensuring a more consistent state management approach.
---
.../connectors/callback/page.tsx | 34 -------------------
.../connectors/callback/route.ts | 33 ++++++++++++++++++
.../hooks/use-connector-dialog.ts | 23 ++++++++++---
3 files changed, 52 insertions(+), 38 deletions(-)
delete mode 100644 surfsense_web/app/dashboard/[search_space_id]/connectors/callback/page.tsx
create mode 100644 surfsense_web/app/dashboard/[search_space_id]/connectors/callback/route.ts
diff --git a/surfsense_web/app/dashboard/[search_space_id]/connectors/callback/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/connectors/callback/page.tsx
deleted file mode 100644
index ef5be4640..000000000
--- a/surfsense_web/app/dashboard/[search_space_id]/connectors/callback/page.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-"use client";
-
-import { useEffect } from "react";
-import { useRouter, useSearchParams } from "next/navigation";
-import { Spinner } from "@/components/ui/spinner";
-
-const OAUTH_RESULT_KEY = "connector_oauth_result";
-
-export default function ConnectorCallbackPage({
- params,
-}: {
- params: { search_space_id: string };
-}) {
- const router = useRouter();
- const searchParams = useSearchParams();
-
- useEffect(() => {
- const result = {
- success: searchParams.get("success"),
- error: searchParams.get("error"),
- connector: searchParams.get("connector"),
- connectorId: searchParams.get("connectorId"),
- };
-
- sessionStorage.setItem(OAUTH_RESULT_KEY, JSON.stringify(result));
- router.replace(`/dashboard/${params.search_space_id}/new-chat`);
- }, [searchParams, router, params.search_space_id]);
-
- return (
-
-
-
- );
-}
diff --git a/surfsense_web/app/dashboard/[search_space_id]/connectors/callback/route.ts b/surfsense_web/app/dashboard/[search_space_id]/connectors/callback/route.ts
new file mode 100644
index 000000000..ca55913f0
--- /dev/null
+++ b/surfsense_web/app/dashboard/[search_space_id]/connectors/callback/route.ts
@@ -0,0 +1,33 @@
+import { type NextRequest, NextResponse } from "next/server";
+
+const OAUTH_RESULT_COOKIE = "connector_oauth_result";
+
+export async function GET(
+ request: NextRequest,
+ { params }: { params: Promise<{ search_space_id: string }> }
+) {
+ const { search_space_id } = await params;
+ const searchParams = request.nextUrl.searchParams;
+
+ const result = JSON.stringify({
+ success: searchParams.get("success"),
+ error: searchParams.get("error"),
+ connector: searchParams.get("connector"),
+ connectorId: searchParams.get("connectorId"),
+ });
+
+ const redirectUrl = new URL(
+ `/dashboard/${search_space_id}/new-chat`,
+ request.url
+ );
+
+ const response = NextResponse.redirect(redirectUrl, { status: 302 });
+ response.cookies.set(OAUTH_RESULT_COOKIE, result, {
+ path: "/",
+ maxAge: 60,
+ httpOnly: false,
+ sameSite: "lax",
+ });
+
+ return response;
+}
diff --git a/surfsense_web/components/assistant-ui/connector-popup/hooks/use-connector-dialog.ts b/surfsense_web/components/assistant-ui/connector-popup/hooks/use-connector-dialog.ts
index 74cea667c..a79e15c7f 100644
--- a/surfsense_web/components/assistant-ui/connector-popup/hooks/use-connector-dialog.ts
+++ b/surfsense_web/components/assistant-ui/connector-popup/hooks/use-connector-dialog.ts
@@ -40,7 +40,19 @@ import {
validateIndexingConfigState,
} from "../constants/connector-popup.schemas";
-const OAUTH_RESULT_KEY = "connector_oauth_result";
+const OAUTH_RESULT_COOKIE = "connector_oauth_result";
+
+function readOAuthResultCookie(): string | null {
+ const match = document.cookie
+ .split("; ")
+ .find((row) => row.startsWith(`${OAUTH_RESULT_COOKIE}=`));
+ return match ? decodeURIComponent(match.split("=").slice(1).join("=")) : null;
+}
+
+function clearOAuthResultCookie(): void {
+ // biome-ignore lint: only standard way to expire a cookie
+ document.cookie = `${OAUTH_RESULT_COOKIE}=; path=/; max-age=0`;
+}
export const useConnectorDialog = () => {
const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom);
@@ -188,11 +200,11 @@ export const useConnectorDialog = () => {
// Track whether the current indexing config came from an OAuth redirect
const [isFromOAuth, setIsFromOAuth] = useState(false);
- // Consume OAuth result from sessionStorage (set by /connectors/callback page)
+ // Consume OAuth result from cookie (set by /connectors/callback route handler)
useEffect(() => {
- const raw = sessionStorage.getItem(OAUTH_RESULT_KEY);
+ const raw = readOAuthResultCookie();
if (!raw || !searchSpaceId) return;
- sessionStorage.removeItem(OAUTH_RESULT_KEY);
+ clearOAuthResultCookie();
let result: {
success: string | null;
@@ -1188,6 +1200,9 @@ export const useConnectorDialog = () => {
setConnectorName(null);
setConnectorConfig(null);
} else {
+ setEditingConnector(null);
+ setConnectorName(null);
+ setConnectorConfig(null);
setIsOpen(false);
}