From 715d07309e114599ab4219df363e74423dbf1124 Mon Sep 17 00:00:00 2001 From: gregfromstl Date: Tue, 12 Nov 2024 16:11:15 -0800 Subject: [PATCH 1/2] refactor(wallet-ui): redirect to ecosystem subroute once --- .../src/app/[ecosystem]/(authed)/layout.tsx | 2 +- .../src/app/[ecosystem]/login/layout.tsx | 6 ++- apps/wallet-ui/src/app/[ecosystem]/page.tsx | 13 +++--- .../wallet-ui/src/app/[ecosystem]/wc/page.tsx | 12 ++--- .../src/components/ConnectButton.tsx | 4 ++ .../wallet-ui/src/components/ConnectEmbed.tsx | 2 +- apps/wallet-ui/src/lib/auth.ts | 9 ++-- apps/wallet-ui/src/middleware.ts | 45 +++++++------------ 8 files changed, 48 insertions(+), 45 deletions(-) diff --git a/apps/wallet-ui/src/app/[ecosystem]/(authed)/layout.tsx b/apps/wallet-ui/src/app/[ecosystem]/(authed)/layout.tsx index ff3f5ae029d..35a465796ff 100644 --- a/apps/wallet-ui/src/app/[ecosystem]/(authed)/layout.tsx +++ b/apps/wallet-ui/src/app/[ecosystem]/(authed)/layout.tsx @@ -12,7 +12,7 @@ export default async function Layout(props: { const { children } = props; - await authedOnly(); + await authedOnly(params.ecosystem); const ecosystem = await getEcosystemInfo(params.ecosystem); return (
diff --git a/apps/wallet-ui/src/app/[ecosystem]/login/layout.tsx b/apps/wallet-ui/src/app/[ecosystem]/login/layout.tsx index c28be22248f..8a8d8e5940a 100644 --- a/apps/wallet-ui/src/app/[ecosystem]/login/layout.tsx +++ b/apps/wallet-ui/src/app/[ecosystem]/login/layout.tsx @@ -3,10 +3,12 @@ import { getCurrentUser } from "../../../lib/auth"; export default async function Layout({ children, -}: { children: React.ReactNode }) { + params, +}: { children: React.ReactNode; params: Promise<{ ecosystem: string }> }) { + const { ecosystem } = await params; const userAddress = await getCurrentUser(); if (userAddress) { - redirect(`/wallet/${userAddress}`); + redirect(`${ecosystem}/wallet/${userAddress}`); } return ( diff --git a/apps/wallet-ui/src/app/[ecosystem]/page.tsx b/apps/wallet-ui/src/app/[ecosystem]/page.tsx index 4ca2814303d..a1f379703e4 100644 --- a/apps/wallet-ui/src/app/[ecosystem]/page.tsx +++ b/apps/wallet-ui/src/app/[ecosystem]/page.tsx @@ -1,10 +1,13 @@ import { getCurrentUser } from "@/lib/auth"; import { redirect } from "next/navigation"; -export default async function Page() { - const user = await getCurrentUser(); - if (user) { - redirect("/wallet/${user}"); +export default async function Page({ + params, +}: { params: Promise<{ ecosystem: string }> }) { + const { ecosystem } = await params; + const address = await getCurrentUser(); + if (address) { + redirect(`${ecosystem}/wallet/${address}`); } - redirect("/login"); + redirect(`${ecosystem}/login`); } diff --git a/apps/wallet-ui/src/app/[ecosystem]/wc/page.tsx b/apps/wallet-ui/src/app/[ecosystem]/wc/page.tsx index b83388f18c5..530027461a5 100644 --- a/apps/wallet-ui/src/app/[ecosystem]/wc/page.tsx +++ b/apps/wallet-ui/src/app/[ecosystem]/wc/page.tsx @@ -4,16 +4,18 @@ import { redirect } from "next/navigation"; export default async function Page(props: { searchParams: Promise<{ uri: string }>; + params: Promise<{ ecosystem: string }>; }) { - const searchParams = await props.searchParams; - - const { uri } = searchParams; + const [{ uri }, { ecosystem }] = await Promise.all([ + props.searchParams, + props.params, + ]); const currentUser = await getCurrentUser(); if (!currentUser) { - redirect(`/login?uri=${encodeURIComponent(uri)}`); + redirect(`${ecosystem}/login?uri=${encodeURIComponent(uri)}`); } - redirect(`/wallet/${currentUser}?uri=${encodeURIComponent(uri)}`); + redirect(`${ecosystem}/wallet/${currentUser}?uri=${encodeURIComponent(uri)}`); } diff --git a/apps/wallet-ui/src/components/ConnectButton.tsx b/apps/wallet-ui/src/components/ConnectButton.tsx index e5cb72e0338..1fd911a268e 100644 --- a/apps/wallet-ui/src/components/ConnectButton.tsx +++ b/apps/wallet-ui/src/components/ConnectButton.tsx @@ -1,4 +1,5 @@ "use client"; +import { logout } from "@/lib/auth"; import { client } from "@/lib/client"; import { useTheme } from "next-themes"; import { @@ -23,6 +24,9 @@ export default function ConnectButton({ wallets={[ecosystemWallet(ecosystem)]} client={client} theme={theme === "light" ? "light" : "dark"} + onDisconnect={() => { + logout(); + }} /> ); } diff --git a/apps/wallet-ui/src/components/ConnectEmbed.tsx b/apps/wallet-ui/src/components/ConnectEmbed.tsx index e40a2031004..d2882d69be8 100644 --- a/apps/wallet-ui/src/components/ConnectEmbed.tsx +++ b/apps/wallet-ui/src/components/ConnectEmbed.tsx @@ -25,7 +25,7 @@ export function ConnectEmbed() { const success = await login(loginParams); if (success) { router.push( - `/wallet/${loginParams.payload.address}?${searchParams.toString()}`, + `/${params.ecosystem}/wallet/${loginParams.payload.address}?${searchParams.toString()}`, ); } }, diff --git a/apps/wallet-ui/src/lib/auth.ts b/apps/wallet-ui/src/lib/auth.ts index 7b1bdd933a6..28f6d5b8821 100644 --- a/apps/wallet-ui/src/lib/auth.ts +++ b/apps/wallet-ui/src/lib/auth.ts @@ -41,7 +41,10 @@ export async function login(payload: VerifyLoginPayloadParams) { secure: process.env.NODE_ENV !== "development", sameSite: "strict", maxAge: 3600, - domain: process.env.NEXT_PUBLIC_ROOT_DOMAIN, + domain: + process.env.NODE_ENV === "development" + ? "localhost" + : process.env.NEXT_PUBLIC_ROOT_DOMAIN, path: "/", }); return true; @@ -49,12 +52,12 @@ export async function login(payload: VerifyLoginPayloadParams) { return false; } -export async function authedOnly() { +export async function authedOnly(ecosystem?: string) { const loggedIn = await getCurrentUser(); if (loggedIn) { return; } - redirect("/login"); + redirect(`/${ecosystem || ""}/login`); } export async function isLoggedIn(): Promise { diff --git a/apps/wallet-ui/src/middleware.ts b/apps/wallet-ui/src/middleware.ts index 04e74265ff8..36e9c78b86e 100644 --- a/apps/wallet-ui/src/middleware.ts +++ b/apps/wallet-ui/src/middleware.ts @@ -1,36 +1,25 @@ import { type NextRequest, NextResponse } from "next/server"; -export const config = { - matcher: [ - /* - * Match all paths except for: - * 1. /api routes - * 2. /_next (Next.js internals) - * 3. /_static (inside /public) - * 4. all root files inside /public (e.g. /favicon.ico) - */ - "/((?!api/|_next/|_static/|_vercel|[\\w-]+\\.\\w+).*)", - ], -}; +export const config = { matcher: "/((?!.*\\.).*)" }; -const ROOT_DOMAIN = process.env.NEXT_PUBLIC_ROOT_DOMAIN; -export default async function middleware(req: NextRequest) { - // Get the request hostname (e.g. demo.thirdweb.com) - const hostname = req.headers.get("host"); +export function middleware(request: NextRequest) { + const url = request.nextUrl; + const hostname = request.headers.get("host") || ""; - const searchParams = req.nextUrl.searchParams.toString(); - const url = req.nextUrl; - const path = `${url.pathname}${ - searchParams.length > 0 ? `?${searchParams}` : "" - }`; + // Match pattern: something.ecosystem.domain.tld + const match = hostname.match(/^([^.]+)\.ecosystem\.([^.]+\.[^.]+)$/); - // keep root application at `/` - if (hostname === ROOT_DOMAIN || hostname === null) { - return NextResponse.next(); - } + if (match) { + const [_, subdomain, primaryDomain] = match; + + // Redirect to ecosystem.domain.tld/subdomain + const newUrl = new URL(`${url.protocol}//ecosystem.${primaryDomain}`); + newUrl.pathname = `/${subdomain}${url.pathname}`; + newUrl.search = url.search; - // rewrite everything else to `/[ecosystem]/... dynamic route - const ecosystem = hostname.split(".")[0]; + // 308 is permanent redirect, preserves request method + return NextResponse.redirect(newUrl, 308); + } - return NextResponse.rewrite(new URL(`/${ecosystem}${path}`, req.url)); + return NextResponse.next(); } From f054842c5cbaacf06bd8bbd408a0ca48d5216f70 Mon Sep 17 00:00:00 2001 From: "gregfromstl (aider)" Date: Tue, 12 Nov 2024 16:29:07 -0800 Subject: [PATCH 2/2] fix: Simplify cookie settings to use current domain automatically --- apps/wallet-ui/src/lib/auth.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/apps/wallet-ui/src/lib/auth.ts b/apps/wallet-ui/src/lib/auth.ts index 28f6d5b8821..412afe5d55a 100644 --- a/apps/wallet-ui/src/lib/auth.ts +++ b/apps/wallet-ui/src/lib/auth.ts @@ -38,13 +38,9 @@ export async function login(payload: VerifyLoginPayloadParams) { name: "jwt", value: jwt, httpOnly: true, - secure: process.env.NODE_ENV !== "development", + secure: process.env.NODE_ENV === "production", sameSite: "strict", maxAge: 3600, - domain: - process.env.NODE_ENV === "development" - ? "localhost" - : process.env.NEXT_PUBLIC_ROOT_DOMAIN, path: "/", }); return true;