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:
MaheshtheDev 2026-05-20 23:38:07 +00:00
parent 41a9001660
commit 53ae4af5ab
3 changed files with 78 additions and 19 deletions

View file

@ -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()

View file

@ -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.
*/

View file

@ -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