diff --git a/apps/frontend/components/WalletContext.tsx b/apps/frontend/components/WalletContext.tsx
index a50e7e5a..f7ab94fa 100644
--- a/apps/frontend/components/WalletContext.tsx
+++ b/apps/frontend/components/WalletContext.tsx
@@ -9,6 +9,8 @@ type WalletState = {
targetNetwork: string;
isConnecting: boolean;
error: string | null;
+ /** Whether the server detected an auth-token cookie during SSR. */
+ hasToken: boolean;
/** Monotonically increasing version. Increments on every connect and
* disconnect so dashboard components can key their data-fetches on it
* and automatically refetch after a wallet change. */
@@ -39,12 +41,22 @@ async function loadFreighter(): Promise {
return import("@stellar/freighter-api");
}
-export function WalletProvider({ children }: { children: React.ReactNode }) {
+export function WalletProvider({
+ children,
+ initialHasToken = false,
+}: {
+ children: React.ReactNode;
+ /** Whether the server detected an auth-token cookie during SSR. */
+ initialHasToken?: boolean;
+}) {
const [publicKey, setPublicKey] = useState(null);
const [freighterNetwork, setFreighterNetwork] = useState(null);
const [isConnecting, setIsConnecting] = useState(false);
const [error, setError] = useState(null);
const [dashboardVersion, setDashboardVersion] = useState(0);
+ // Use the server-hydrated value so the first paint shows the correct
+ // state — no "Connect wallet to view" flash when a token exists.
+ const [hasToken, setHasToken] = useState(initialHasToken);
const targetNetwork = normalizeNetwork(process.env.NEXT_PUBLIC_STELLAR_NETWORK);
@@ -120,11 +132,12 @@ export function WalletProvider({ children }: { children: React.ReactNode }) {
targetNetwork,
isConnecting,
error,
+ hasToken,
dashboardVersion,
connect,
disconnect,
}),
- [connect, disconnect, dashboardVersion, error, freighterNetwork, isConnecting, publicKey, targetNetwork],
+ [connect, disconnect, dashboardVersion, error, freighterNetwork, hasToken, isConnecting, publicKey, targetNetwork],
);
return {children};
diff --git a/apps/frontend/lib/api.ts b/apps/frontend/lib/api.ts
index 40934453..cfb5fc4d 100644
--- a/apps/frontend/lib/api.ts
+++ b/apps/frontend/lib/api.ts
@@ -14,6 +14,20 @@ type JwtPayload = {
sub?: unknown;
};
+const AUTH_TOKEN_COOKIE = "auth-token";
+
+function setAuthCookie(token: string): void {
+ if (typeof document === "undefined") return;
+ // Set a non-HttpOnly cookie so the server can read it via `cookies()`.
+ // Path=/ ensures it's sent on every request.
+ document.cookie = `${AUTH_TOKEN_COOKIE}=${encodeURIComponent(token)}; path=/; max-age=${60 * 60 * 24 * 7}; SameSite=Lax`;
+}
+
+export function clearAuthCookie(): void {
+ if (typeof document === "undefined") return;
+ document.cookie = `${AUTH_TOKEN_COOKIE}=; path=/; max-age=0; SameSite=Lax`;
+}
+
export async function getAccessToken(publicKey: string): Promise {
const savedToken =
typeof window !== "undefined" ? window.localStorage.getItem(TOKEN_STORAGE_KEY) : null;
@@ -63,6 +77,7 @@ export async function getAccessToken(publicKey: string): Promise {
}
window.localStorage.setItem(TOKEN_STORAGE_KEY, accessToken);
+ setAuthCookie(accessToken);
return accessToken;
}
@@ -72,6 +87,7 @@ export function clearAuthToken(): void {
}
window.localStorage.removeItem(TOKEN_STORAGE_KEY);
+ clearAuthCookie();
}
/**
diff --git a/apps/frontend/lib/get-auth-status.server.ts b/apps/frontend/lib/get-auth-status.server.ts
new file mode 100644
index 00000000..220e1934
--- /dev/null
+++ b/apps/frontend/lib/get-auth-status.server.ts
@@ -0,0 +1,26 @@
+/**
+ * Server-only utility that reads the `auth-token` cookie so the initial
+ * render can show the correct connection state instead of a flash of
+ * "Connect wallet to view".
+ *
+ * This file uses Next.js `cookies()` — it MUST only be imported from
+ * Server Components (app directory), never from client components.
+ */
+import { cookies } from "next/headers";
+
+export type AuthStatus = {
+ /** Whether a valid auth-token cookie was found */
+ hasToken: boolean;
+ /** The raw token value, if present */
+ token: string | null;
+};
+
+export function getAuthStatusFromCookie(): AuthStatus {
+ const cookieStore = cookies();
+ const token = cookieStore.get("auth-token")?.value ?? null;
+
+ return {
+ hasToken: token !== null,
+ token,
+ };
+}
\ No newline at end of file