mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-10-07 02:58:11 +00:00
fix: trying mcp auth with resource (#840)
This commit is contained in:
parent
543c45a626
commit
8f1e8afaf9
3 changed files with 61 additions and 5 deletions
|
|
@ -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",
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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<URLSearchParams, "get">): boolean {
|
||||
return sp.get("response_type") === "code" && Boolean(sp.get("client_id"))
|
||||
}
|
||||
|
||||
function buildMcpAuthorizeResumeUrl(
|
||||
sp: Pick<URLSearchParams, "toString">,
|
||||
): 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 (
|
||||
<div className="fixed inset-0 z-0 overflow-hidden">
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,26 @@
|
|||
import { redirect } from "next/navigation"
|
||||
|
||||
export default function Page() {
|
||||
redirect("/login/new")
|
||||
function serializeSearchParams(
|
||||
sp: Record<string, string | string[] | undefined>,
|
||||
): 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<Record<string, string | string[] | undefined>>
|
||||
}) {
|
||||
const sp = await searchParams
|
||||
const query = serializeSearchParams(sp)
|
||||
redirect(query ? `/login/new?${query}` : "/login/new")
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue