Merge pull request #1430 from suryo12/refactor/1362-oauth-typed-contract

refactor(web): centralize OAuth callback cookie contract (fixes #1362)
This commit is contained in:
Rohan Verma 2026-05-23 15:53:51 -07:00 committed by GitHub
commit d53866d87d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 39 additions and 18 deletions

View file

@ -1,6 +1,5 @@
import { type NextRequest, NextResponse } from "next/server"; import { type NextRequest, NextResponse } from "next/server";
import { OAUTH_RESULT_COOKIE, type OAuthCallbackResult } from "@/contracts/types/oauth.types";
const OAUTH_RESULT_COOKIE = "connector_oauth_result";
export async function GET( export async function GET(
request: NextRequest, request: NextRequest,
@ -9,12 +8,13 @@ export async function GET(
const { search_space_id } = await params; const { search_space_id } = await params;
const searchParams = request.nextUrl.searchParams; const searchParams = request.nextUrl.searchParams;
const result = JSON.stringify({ const payload: OAuthCallbackResult = {
success: searchParams.get("success"), success: searchParams.get("success"),
error: searchParams.get("error"), error: searchParams.get("error"),
connector: searchParams.get("connector"), connector: searchParams.get("connector"),
connectorId: searchParams.get("connectorId"), connectorId: searchParams.get("connectorId"),
}); };
const result = JSON.stringify(payload);
const redirectUrl = new URL(`/dashboard/${search_space_id}/new-chat`, request.url); const redirectUrl = new URL(`/dashboard/${search_space_id}/new-chat`, request.url);

View file

@ -14,7 +14,9 @@ import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-quer
import { EnumConnectorName } from "@/contracts/enums/connector"; import { EnumConnectorName } from "@/contracts/enums/connector";
import type { SearchSourceConnector } from "@/contracts/types/connector.types"; import type { SearchSourceConnector } from "@/contracts/types/connector.types";
import { searchSourceConnector } from "@/contracts/types/connector.types"; import { searchSourceConnector } from "@/contracts/types/connector.types";
import { OAUTH_RESULT_COOKIE, parseOAuthCallbackResult } from "@/contracts/types/oauth.types";
import { authenticatedFetch } from "@/lib/auth-utils"; import { authenticatedFetch } from "@/lib/auth-utils";
import { BACKEND_URL } from "@/lib/env-config";
import { import {
trackConnectorConnected, trackConnectorConnected,
trackConnectorDeleted, trackConnectorDeleted,
@ -36,15 +38,12 @@ import {
OAUTH_CONNECTORS, OAUTH_CONNECTORS,
OTHER_CONNECTORS, OTHER_CONNECTORS,
} from "../constants/connector-constants"; } from "../constants/connector-constants";
import { import {
dateRangeSchema, dateRangeSchema,
frequencyMinutesSchema, frequencyMinutesSchema,
parseOAuthAuthResponse, parseOAuthAuthResponse,
validateIndexingConfigState, validateIndexingConfigState,
} from "../constants/connector-popup.schemas"; } from "../constants/connector-popup.schemas";
import { BACKEND_URL } from "@/lib/env-config";
const OAUTH_RESULT_COOKIE = "connector_oauth_result";
function readOAuthResultCookie(): string | null { function readOAuthResultCookie(): string | null {
const match = document.cookie const match = document.cookie
@ -211,17 +210,8 @@ export const useConnectorDialog = () => {
if (!raw || !searchSpaceId) return; if (!raw || !searchSpaceId) return;
clearOAuthResultCookie(); clearOAuthResultCookie();
let result: { const result = parseOAuthCallbackResult(raw);
success: string | null; if (!result) return;
error: string | null;
connector: string | null;
connectorId: string | null;
};
try {
result = JSON.parse(raw);
} catch {
return;
}
if (result.error) { if (result.error) {
const oauthConnector = result.connector const oauthConnector = result.connector

View file

@ -0,0 +1,31 @@
import { z } from "zod";
export const OAUTH_RESULT_COOKIE = "connector_oauth_result";
/**
* Schema for the payload written to the `connector_oauth_result` cookie by the
* OAuth callback route and read back by the connector dialog hook.
*/
export const oauthCallbackResultSchema = z.object({
success: z.string().nullable(),
error: z.string().nullable(),
connector: z.string().nullable(),
connectorId: z.string().nullable(),
});
export type OAuthCallbackResult = z.infer<typeof oauthCallbackResultSchema>;
/**
* Safely decode and validate the OAuth callback cookie value. Returns `null`
* when the value is not valid JSON or does not match the expected shape.
*/
export function parseOAuthCallbackResult(raw: string): OAuthCallbackResult | null {
let parsed: unknown;
try {
parsed = JSON.parse(raw);
} catch {
return null;
}
const result = oauthCallbackResultSchema.safeParse(parsed);
return result.success ? result.data : null;
}