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..412afe5d55a 100644
--- a/apps/wallet-ui/src/lib/auth.ts
+++ b/apps/wallet-ui/src/lib/auth.ts
@@ -38,10 +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.NEXT_PUBLIC_ROOT_DOMAIN,
path: "/",
});
return true;
@@ -49,12 +48,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();
}