diff --git a/src/lib/api/user-session.ts b/src/lib/api/user-session.ts index e79a3657..910c118e 100644 --- a/src/lib/api/user-session.ts +++ b/src/lib/api/user-session.ts @@ -20,7 +20,7 @@ export const loginUser = async ( var userSession: UserSession = defaultUserSession(); var err: string = ""; - const data = await axios + await axios .post( `${siteConfig.env.backendServiceURL}/login`, { @@ -45,8 +45,8 @@ export const loginUser = async ( // deserialize successfully, then downstream operations will see the default // userSessionData in state and we will experience subtle Bugs. We should consider // how best we want to handle this. Ex. clear auth cookie? - console.debug("userSessionData: ", userSessionData) - throw { message: 'Login Failed to produce valid User Session data' } + console.debug("userSessionData: ", userSessionData); + throw { message: "Login Failed to produce valid User Session data" }; } }) .catch(function (error: AxiosError) { @@ -73,7 +73,7 @@ export const logoutUser = async (): Promise => { const data = await axios .get(`${siteConfig.env.backendServiceURL}/logout`, { withCredentials: true, - setTimeout: 5000, // 5 seconds before timing out trying to log in with the backend + setTimeout: 5000, // 5 seconds before timing out trying to log out with the backend }) .then(function (response: AxiosResponse) { // handle success diff --git a/src/lib/providers/auth-store-provider.tsx b/src/lib/providers/auth-store-provider.tsx index 7aa4d1d6..84917048 100644 --- a/src/lib/providers/auth-store-provider.tsx +++ b/src/lib/providers/auth-store-provider.tsx @@ -1,8 +1,16 @@ +"use client"; // The purpose of this provider is to provide compatibility with // Next.js re-rendering and component caching -"use client"; -import { type ReactNode, createContext, useRef, useContext } from "react"; +import { + type ReactNode, + createContext, + useRef, + useContext, + useMemo, + useEffect, + useState, +} from "react"; import { type StoreApi, useStore } from "zustand"; import { type AuthStore, createAuthStore } from "@/lib/stores/auth-store"; @@ -15,9 +23,22 @@ export interface AuthStoreProviderProps { } export const AuthStoreProvider = ({ children }: AuthStoreProviderProps) => { - const storeRef = useRef>(undefined); - if (!storeRef.current) { - storeRef.current = createAuthStore(); + const storeRef = useRef | null>(null); + const [isInitialized, setIsInitialized] = useState(false); + + useEffect(() => { + if (typeof window !== "undefined") { + // Now safe to access localStorage + const storedValue = localStorage.getItem("auth-store"); + const initialState = storedValue ? JSON.parse(storedValue).state : null; + storeRef.current = createAuthStore(initialState); + setIsInitialized(true); + } + }, []); + + // Ensure store is initialized before rendering the provider + if (!isInitialized) { + return null; // or return a loading component } return ( diff --git a/src/lib/stores/auth-store.ts b/src/lib/stores/auth-store.ts index cb0471b0..68797c40 100644 --- a/src/lib/stores/auth-store.ts +++ b/src/lib/stores/auth-store.ts @@ -50,7 +50,7 @@ export const createAuthStore = (initState: AuthState = defaultInitState) => { }), { name: "auth-store", - storage: createJSONStorage(() => sessionStorage), + storage: createJSONStorage(() => localStorage), } ) ) diff --git a/src/middleware.ts b/src/middleware.ts new file mode 100644 index 00000000..983f80b7 --- /dev/null +++ b/src/middleware.ts @@ -0,0 +1,45 @@ +import { NextRequest, NextResponse } from "next/server"; + +// 1. Specify protected and public routes +const protectedRoutes = [ + "/dashboard", + "/coaching-sessions", + "/settings", + "/profile", +]; +const publicRoutes = ["/"]; + +export default async function middleware(req: NextRequest) { + // 2. Check if the current route is protected or public + const path = req.nextUrl.pathname; + const isProtectedRoute = protectedRoutes.some((route) => + path.startsWith(route) + ); + const isPublicRoute = publicRoutes.some((route) => path === route); + + // 3. Get the session from the cookie + const sessionCookie = req.cookies.get("id"); + const session = sessionCookie?.value; + + // 4. Redirect to / if the user is not authenticated + // 4b. TODO: Check session validity/expiration? + if (isProtectedRoute && !session) { + return NextResponse.redirect(new URL("/", req.nextUrl)); + } + + // 5. Redirect to /dashboard if the user is authenticated + if ( + isPublicRoute && + session && + !req.nextUrl.pathname.startsWith("/dashboard") + ) { + return NextResponse.redirect(new URL("/dashboard", req.nextUrl)); + } + + return NextResponse.next(); +} + +// Routes Middleware should not run on +export const config = { + matcher: ["/((?!api|_next/static|_next/image|favicon.ico|.*\\.png$).*)"], +};