mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-08-28 05:25:33 +00:00
fix(web): portless dev auth redirects and session cookies (#981)
Middleware now reconstructs public URLs from portless forwarded headers and recognizes better-auth-dev session cookies so OAuth sign-in works with local API + worktree dev URLs.
This commit is contained in:
parent
41a9001660
commit
53ae4af5ab
3 changed files with 78 additions and 19 deletions
|
|
@ -17,6 +17,7 @@ import { motion } from "motion/react"
|
|||
import { dmSansClassName } from "@/lib/fonts"
|
||||
import { cn } from "@lib/utils"
|
||||
import { Logo } from "@ui/assets/Logo"
|
||||
import { resolveAuthRedirectUrl } from "@/lib/url-helpers"
|
||||
|
||||
function isMcpOAuthAuthorizeContext(sp: Pick<URLSearchParams, "get">): boolean {
|
||||
return sp.get("response_type") === "code" && Boolean(sp.get("client_id"))
|
||||
|
|
@ -113,9 +114,25 @@ export default function LoginPage() {
|
|||
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])
|
||||
if (isMcpOAuthAuthorizeContext(sp)) {
|
||||
window.location.assign(buildMcpAuthorizeResumeUrl(sp))
|
||||
return
|
||||
}
|
||||
const redirectUrl = params.get("redirect")
|
||||
if (redirectUrl) {
|
||||
window.location.assign(
|
||||
resolveAuthRedirectUrl(redirectUrl, window.location.origin).toString(),
|
||||
)
|
||||
return
|
||||
}
|
||||
router.replace("/")
|
||||
}, [
|
||||
sessionPending,
|
||||
sessionData?.session,
|
||||
oauthQueryForResume,
|
||||
params,
|
||||
router,
|
||||
])
|
||||
|
||||
// Get redirect URL from query params
|
||||
const redirectUrl = params.get("redirect")
|
||||
|
|
@ -128,17 +145,7 @@ export default function LoginPage() {
|
|||
return buildMcpAuthorizeResumeUrl(params)
|
||||
}
|
||||
|
||||
let finalUrl: URL
|
||||
|
||||
if (redirectUrl) {
|
||||
try {
|
||||
finalUrl = new URL(redirectUrl, origin)
|
||||
} catch {
|
||||
finalUrl = new URL(origin)
|
||||
}
|
||||
} else {
|
||||
finalUrl = new URL(origin)
|
||||
}
|
||||
const finalUrl = resolveAuthRedirectUrl(redirectUrl, origin)
|
||||
|
||||
finalUrl.searchParams.set("extension-auth-success", "true")
|
||||
return finalUrl.toString()
|
||||
|
|
|
|||
|
|
@ -1,3 +1,47 @@
|
|||
const PROXY_LOCAL_HOSTS = new Set(["localhost", "127.0.0.1", "::1"])
|
||||
|
||||
/** Reconstruct the browser-facing URL when running behind portless (or similar). */
|
||||
export function getPublicRequestUrl(request: Request): URL {
|
||||
const internal = new URL(request.url)
|
||||
const forwardedHost = request.headers
|
||||
.get("x-forwarded-host")
|
||||
?.split(",")[0]
|
||||
?.trim()
|
||||
if (forwardedHost) {
|
||||
const proto = request.headers.get("x-forwarded-proto") || "https"
|
||||
return new URL(
|
||||
`${proto}://${forwardedHost}${internal.pathname}${internal.search}`,
|
||||
)
|
||||
}
|
||||
const portlessUrl = process.env.PORTLESS_URL
|
||||
if (portlessUrl) {
|
||||
try {
|
||||
const base = new URL(portlessUrl)
|
||||
return new URL(`${base.origin}${internal.pathname}${internal.search}`)
|
||||
} catch {}
|
||||
}
|
||||
return internal
|
||||
}
|
||||
|
||||
/** Map portless proxy localhost redirects back to the current public origin. */
|
||||
export function resolveAuthRedirectUrl(
|
||||
redirectUrl: string | null,
|
||||
origin: string,
|
||||
): URL {
|
||||
const fallback = new URL(origin)
|
||||
if (!redirectUrl) return fallback
|
||||
try {
|
||||
const target = new URL(redirectUrl)
|
||||
if (PROXY_LOCAL_HOSTS.has(target.hostname)) {
|
||||
return new URL(`${target.pathname}${target.search}`, origin)
|
||||
}
|
||||
if (target.origin === origin) return target
|
||||
return fallback
|
||||
} catch {
|
||||
return fallback
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if a string is a valid URL.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,14 +1,22 @@
|
|||
import { getSessionCookie } from "better-auth/cookies"
|
||||
import { NextResponse } from "next/server"
|
||||
import { getPublicRequestUrl } from "@/lib/url-helpers"
|
||||
|
||||
function getAuthSessionCookie(request: Request): string | null {
|
||||
return (
|
||||
getSessionCookie(request) ??
|
||||
getSessionCookie(request, { cookiePrefix: "better-auth-dev" })
|
||||
)
|
||||
}
|
||||
|
||||
export default async function proxy(request: Request) {
|
||||
console.debug("[PROXY] === PROXY START ===")
|
||||
const url = new URL(request.url)
|
||||
const url = getPublicRequestUrl(request)
|
||||
|
||||
console.debug("[PROXY] Path:", url.pathname)
|
||||
console.debug("[PROXY] Method:", request.method)
|
||||
|
||||
const sessionCookie = getSessionCookie(request)
|
||||
const sessionCookie = getAuthSessionCookie(request)
|
||||
console.debug("[PROXY] Session cookie exists:", !!sessionCookie)
|
||||
|
||||
// Always allow access to login and waitlist pages
|
||||
|
|
@ -40,9 +48,9 @@ export default async function proxy(request: Request) {
|
|||
console.debug(
|
||||
"[PROXY] No session cookie and not on public path, redirecting to /login",
|
||||
)
|
||||
const url = new URL("/login", request.url)
|
||||
url.searchParams.set("redirect", request.url)
|
||||
return NextResponse.redirect(url)
|
||||
const loginUrl = new URL("/login", url.origin)
|
||||
loginUrl.searchParams.set("redirect", url.toString())
|
||||
return NextResponse.redirect(loginUrl)
|
||||
}
|
||||
|
||||
// TEMPORARILY DISABLED: Waitlist check
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue