From 8f1e8afaf9e8f6e7dba39f7d8722aacba69fc0fd Mon Sep 17 00:00:00 2001 From: MaheshtheDev <38828053+MaheshtheDev@users.noreply.github.com> Date: Thu, 9 Apr 2026 08:21:06 +0000 Subject: [PATCH] fix: trying mcp auth with resource (#840) --- apps/mcp/src/index.ts | 8 +++++-- apps/web/app/(auth)/login/new/page.tsx | 33 +++++++++++++++++++++++++- apps/web/app/(auth)/login/page.tsx | 25 +++++++++++++++++-- 3 files changed, 61 insertions(+), 5 deletions(-) diff --git a/apps/mcp/src/index.ts b/apps/mcp/src/index.ts index 0586492f..8ca711e4 100644 --- a/apps/mcp/src/index.ts +++ b/apps/mcp/src/index.ts @@ -105,12 +105,16 @@ app.all("/mcp/*", async (c) => { const token = authHeader?.replace(/^Bearer\s+/i, "") const containerTag = c.req.header("x-sm-project") const apiUrl = c.env.API_URL || DEFAULT_API_URL + const mcpURL = + c.env.API_URL === "http://localhost:8787" + ? "http://localhost:8788" + : "https://mcp.supermemory.ai" if (!token) { return new Response("Unauthorized", { status: 401, headers: { - "WWW-Authenticate": `Bearer resource_metadata="/.well-known/oauth-protected-resource"`, + "WWW-Authenticate": `Bearer resource_metadata="${mcpURL}/.well-known/oauth-protected-resource"`, "Access-Control-Expose-Headers": "WWW-Authenticate", }, }) @@ -149,7 +153,7 @@ app.all("/mcp/*", async (c) => { status: 401, headers: { "Content-Type": "application/json", - "WWW-Authenticate": `Bearer error="invalid_token", resource_metadata="/.well-known/oauth-protected-resource"`, + "WWW-Authenticate": `Bearer error="invalid_token", resource_metadata="${mcpURL}/.well-known/oauth-protected-resource"`, "Access-Control-Expose-Headers": "WWW-Authenticate", }, }, diff --git a/apps/web/app/(auth)/login/new/page.tsx b/apps/web/app/(auth)/login/new/page.tsx index e4e4a67a..e3d2a1c5 100644 --- a/apps/web/app/(auth)/login/new/page.tsx +++ b/apps/web/app/(auth)/login/new/page.tsx @@ -1,6 +1,6 @@ "use client" -import { signIn } from "@lib/auth" +import { signIn, useSession } from "@lib/auth" import { usePostHog } from "@lib/posthog" import { TextSeparator } from "@ui/components/text-separator" import { ExternalAuthButton } from "@ui/button/external-auth" @@ -18,6 +18,21 @@ import { dmSansClassName } from "@/lib/fonts" import { cn } from "@lib/utils" import { Logo } from "@ui/assets/Logo" +function isMcpOAuthAuthorizeContext(sp: Pick): boolean { + return sp.get("response_type") === "code" && Boolean(sp.get("client_id")) +} + +function buildMcpAuthorizeResumeUrl( + sp: Pick, +): string { + const backend = + process.env.NEXT_PUBLIC_BACKEND_URL ?? "https://api.supermemory.ai" + const p = new URLSearchParams(sp.toString()) + p.delete("redirect") + p.delete("error") + return `${backend}/api/auth/mcp/authorize?${p.toString()}` +} + function AnimatedGradientBackground() { return (
@@ -90,6 +105,17 @@ export default function LoginPage() { const posthog = usePostHog() const params = useSearchParams() + const { data: sessionData, isPending: sessionPending } = useSession() + + const oauthQueryForResume = params.toString() + + useEffect(() => { + if (sessionPending) return + if (!sessionData?.session) return + const sp = new URLSearchParams(oauthQueryForResume) + if (!isMcpOAuthAuthorizeContext(sp)) return + window.location.assign(buildMcpAuthorizeResumeUrl(sp)) + }, [sessionPending, sessionData?.session, oauthQueryForResume]) // Get redirect URL from query params const redirectUrl = params.get("redirect") @@ -97,6 +123,11 @@ export default function LoginPage() { // Create callback URL that includes redirect parameter if provided const getCallbackURL = () => { const origin = window.location.origin + + if (isMcpOAuthAuthorizeContext(params)) { + return buildMcpAuthorizeResumeUrl(params) + } + let finalUrl: URL if (redirectUrl) { diff --git a/apps/web/app/(auth)/login/page.tsx b/apps/web/app/(auth)/login/page.tsx index a7e44a6b..a73f47ab 100644 --- a/apps/web/app/(auth)/login/page.tsx +++ b/apps/web/app/(auth)/login/page.tsx @@ -1,5 +1,26 @@ import { redirect } from "next/navigation" -export default function Page() { - redirect("/login/new") +function serializeSearchParams( + sp: Record, +): string { + const q = new URLSearchParams() + for (const [key, value] of Object.entries(sp)) { + if (value === undefined) continue + if (Array.isArray(value)) { + for (const v of value) q.append(key, v) + } else { + q.set(key, value) + } + } + return q.toString() +} + +export default async function Page({ + searchParams, +}: { + searchParams: Promise> +}) { + const sp = await searchParams + const query = serializeSearchParams(sp) + redirect(query ? `/login/new?${query}` : "/login/new") }