From 31e000fd2b9848fdede40a33239f220bf74211d9 Mon Sep 17 00:00:00 2001 From: Mathias Oterhals Myklebust Date: Tue, 10 Sep 2024 10:37:57 +0200 Subject: [PATCH] fix(redirect): fetch redirect data via API route to avoid token leaking to client (even though middleware should run on server...) --- src/middleware.ts | 39 +++++++++++++++++++++++---------- studio/lib/payloads/redirect.ts | 8 ++----- studio/lib/queries/redirects.ts | 10 +++++++++ 3 files changed, 40 insertions(+), 17 deletions(-) create mode 100644 studio/lib/queries/redirects.ts diff --git a/src/middleware.ts b/src/middleware.ts index b2b3aad82..4ccf01da1 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -1,20 +1,37 @@ import { NextRequest, NextResponse } from "next/server"; -import { client } from "../studio/lib/client"; -import { RedirectSparsePage } from "../studio/lib/payloads/redirect"; import { HTTP_STATUSES } from "./utils/http"; +import { RedirectDestinationSlugPage } from "studio/lib/payloads/redirect"; +import { REDIRECT_BY_SOURCE_SLUG_QUERY } from "../studio/lib/queries/redirects"; export async function middleware(request: NextRequest) { const slug = request.nextUrl.pathname; - const redirect = await client.fetch( - `*[_type == "redirect" && source.current == "${slug}"][0]{ - "destination":destination.current - }`, + const slugQueryParam = slug.replace(/^\/+/, ""); + /* + fetching redirect data via API route to avoid token leaking to client + (middleware should run on server, but `experimental_taintUniqueValue` begs to differ...) + */ + const redirectRes = await fetch( + new URL("/api/fetchData", process.env.NEXT_PUBLIC_URL), + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + query: REDIRECT_BY_SOURCE_SLUG_QUERY, + params: { slug: slugQueryParam }, + }), + }, ); - if (redirect !== null) { - return NextResponse.redirect( - new URL(redirect.destination, request.url), - HTTP_STATUSES.TEMPORARY_REDIRECT, - ); + if (redirectRes.ok) { + const redirect: RedirectDestinationSlugPage | null = + await redirectRes.json(); + if (redirect?.destinationSlug) { + return NextResponse.redirect( + new URL(redirect.destinationSlug, request.url), + HTTP_STATUSES.TEMPORARY_REDIRECT, + ); + } } } diff --git a/studio/lib/payloads/redirect.ts b/studio/lib/payloads/redirect.ts index 1d904f0df..e54442eab 100644 --- a/studio/lib/payloads/redirect.ts +++ b/studio/lib/payloads/redirect.ts @@ -1,7 +1,3 @@ -export interface RedirectPage extends RedirectSparsePage { - source: string; -} - -export interface RedirectSparsePage { - destination: string; +export interface RedirectDestinationSlugPage { + destinationSlug: string; } diff --git a/studio/lib/queries/redirects.ts b/studio/lib/queries/redirects.ts new file mode 100644 index 000000000..ecc0362f1 --- /dev/null +++ b/studio/lib/queries/redirects.ts @@ -0,0 +1,10 @@ +import { groq } from "next-sanity"; + +export const REDIRECT_BY_SOURCE_SLUG_QUERY = groq` + *[_type == "redirect" && source.current == $slug][0]{ + "destinationSlug": select( + destination.type == "slug" => destination.slug.current, + destination.type == "reference" => destination.reference->slug.current + ) + } +`;