Index: frontend/src/App.tsx
===================================================================
--- frontend/src/App.tsx	(revision 15ec67f0faa204c0c5d0e1f4d4979ee8ff4fcd4a)
+++ frontend/src/App.tsx	(revision 0c86e77a055e354a71be0ff7c053051ca180993b)
@@ -1,19 +1,41 @@
-import { useEffect } from "react";
+import { createBrowserRouter, Outlet, RouterProvider } from "react-router-dom";
+import LandingPage from "./LandingPage";
+import Login from "./Login";
+import Register from "./Register";
+
+const Layout = () => {
+	return (
+		<div className="flex flex-col min-h-screen">
+			{/* <Nav /> */}
+			<main className="grow">
+				<Outlet />
+			</main>
+		</div>
+	);
+};
+
+const router = createBrowserRouter([
+	{
+		path: "",
+		element: <Layout />,
+		children: [
+			{
+				path: "/",
+				element: <LandingPage />,
+			},
+			{
+				path: "/login",
+				element: <Login />,
+			},
+			{
+				path: "/register",
+				element: <Register />,
+			},
+		],
+	},
+]);
 
 function App() {
-	useEffect(() => {
-		const fetchData = async () => {
-			const response = await fetch("http://localhost:8080/");
-			const data = await response.json();
-			console.log(data);
-		};
-		fetchData();
-	});
-
-	return (
-		<>
-			<p className="text-red-500">ok</p>
-		</>
-	);
+	return <RouterProvider router={router} />;
 }
 
Index: frontend/src/LandingPage.tsx
===================================================================
--- frontend/src/LandingPage.tsx	(revision 0c86e77a055e354a71be0ff7c053051ca180993b)
+++ frontend/src/LandingPage.tsx	(revision 0c86e77a055e354a71be0ff7c053051ca180993b)
@@ -0,0 +1,5 @@
+const LandingPage = () => {
+	return <div>landing page</div>;
+};
+
+export default LandingPage;
Index: frontend/src/Login.tsx
===================================================================
--- frontend/src/Login.tsx	(revision 0c86e77a055e354a71be0ff7c053051ca180993b)
+++ frontend/src/Login.tsx	(revision 0c86e77a055e354a71be0ff7c053051ca180993b)
@@ -0,0 +1,5 @@
+const Login = () => {
+	return <div>login</div>;
+};
+
+export default Login;
Index: frontend/src/Register.tsx
===================================================================
--- frontend/src/Register.tsx	(revision 0c86e77a055e354a71be0ff7c053051ca180993b)
+++ frontend/src/Register.tsx	(revision 0c86e77a055e354a71be0ff7c053051ca180993b)
@@ -0,0 +1,123 @@
+import { useState } from "react";
+import { useNavigate } from "react-router";
+import axiosInstance, { scheduleTokenRefresh } from "./api/axiosInstance";
+import { useAuth } from "./context/authContext";
+import type { User, UserResponse } from "./types";
+
+const Register = () => {
+	const { setUser, user } = useAuth();
+	const [username, setUsername] = useState("");
+	const [password, setPassword] = useState("");
+	const [fullname, setFullname] = useState("");
+	const [email, setEmail] = useState("");
+	const [profilePhoto, setProfilePhoto] = useState<string | null>(null);
+
+	const navigate = useNavigate();
+
+	const handleRegister = async () => {
+		const newUser: User = {
+			username,
+			fullName: fullname,
+			email: email,
+			profilePhoto: profilePhoto,
+		};
+		try {
+			const response = await axiosInstance.post<{
+				user: UserResponse;
+				tokenExpiresIn: number;
+			}>("/auth/register", {
+				username,
+				fullname,
+				email,
+				password,
+				profilePhoto,
+			});
+			scheduleTokenRefresh(response.data.tokenExpiresIn);
+			setUser(newUser);
+			// setUser(response.data.user);
+			navigate("/");
+		} catch (error) {
+			console.error("Registration failed:", error);
+		}
+	};
+
+	return (
+		<div className="flex flex-col items-center justify-center min-h-screen bg-gray-100">
+			<h2 className="text-2xl mb-4">Register</h2>
+			<form className="bg-white p-6 rounded shadow-md w-80">
+				<div className="mb-4">
+					<label className="block text-gray-700 mb-2" htmlFor="username">
+						Username
+					</label>
+					<input
+						type="text"
+						id="username"
+						className="w-full p-2 border border-gray-300 rounded"
+						value={username}
+						onChange={(e) => setUsername(e.target.value)}
+					/>
+				</div>
+				<div className="mb-4">
+					<label className="block text-gray-700 mb-2" htmlFor="password">
+						Password
+					</label>
+					<input
+						type="password"
+						id="password"
+						className="w-full p-2 border border-gray-300 rounded"
+						value={password}
+						onChange={(e) => setPassword(e.target.value)}
+					/>
+				</div>
+				<div className="mb-4">
+					<label className="block text-gray-700 mb-2" htmlFor="fullName">
+						Full Name
+					</label>
+					<input
+						type="text"
+						id="fullName"
+						className="w-full p-2 border border-gray-300 rounded"
+						value={fullname}
+						onChange={(e) => setFullname(e.target.value)}
+					/>
+				</div>
+				<div className="mb-4">
+					<label className="block text-gray-700 mb-2" htmlFor="email">
+						Email
+					</label>
+					<input
+						type="email"
+						id="email"
+						className="w-full p-2 border border-gray-300 rounded"
+						value={email}
+						onChange={(e) => setEmail(e.target.value)}
+					/>
+				</div>
+				<div className="mb-4">
+					<label className="block text-gray-700 mb-2" htmlFor="profilePhoto">
+						Profile Photo URL
+					</label>
+					<input
+						type="text"
+						id="profilePhoto"
+						className="w-full p-2 border border-gray-300 rounded"
+						value={profilePhoto || ""}
+						onChange={(e) => setProfilePhoto(e.target.value)}
+					/>
+				</div>
+				<button
+					type="submit"
+					className="w-full bg-blue-500 text-white p-2 rounded hover:bg-blue-600"
+					onClick={(e) => {
+						e.preventDefault();
+						handleRegister();
+					}}
+				>
+					Register
+				</button>
+			</form>
+		</div>
+	);
+};
+
+export default Register;
Index: frontend/src/api/axiosInstance.ts
===================================================================
--- frontend/src/api/axiosInstance.ts	(revision 0c86e77a055e354a71be0ff7c053051ca180993b)
+++ frontend/src/api/axiosInstance.ts	(revision 0c86e77a055e354a71be0ff7c053051ca180993b)
@@ -0,0 +1,78 @@
+import axios from "axios";
+
+export const baseURL = import.meta.env.VITE_API_BASE_URL;
+
+export const axiosInstance = axios.create({
+	baseURL,
+	headers: {
+		"Content-Type": "application/json",
+	},
+	withCredentials: true,
+});
+
+let refreshPromise: Promise<void> | null = null;
+let refreshTimeout: number | null = null;
+
+export const clearRefreshTimeout = () => {
+	if (refreshTimeout) {
+		clearTimeout(refreshTimeout);
+		refreshTimeout = null;
+	}
+};
+
+export const scheduleTokenRefresh = (expiryTimeSeconds: number) => {
+	clearRefreshTimeout();
+
+	const refreshTime = (expiryTimeSeconds - 60) * 1000;
+	if (refreshTime <= 0) {
+		refreshTokenMethod();
+	} else {
+		refreshTimeout = setTimeout(refreshTokenMethod, refreshTime);
+	}
+};
+
+export const refreshTokenMethod = async (): Promise<void | null> => {
+	if (refreshPromise) {
+		return refreshPromise;
+	}
+
+	refreshPromise = (async () => {
+		try {
+			const refreshResponse = await axiosInstance.post<{
+				tokenExpiresIn: number;
+			}>("/auth/refresh");
+			scheduleTokenRefresh(refreshResponse.data.tokenExpiresIn);
+		} catch (err) {
+			console.error(err);
+			throw err;
+		} finally {
+			refreshPromise = null;
+		}
+	})();
+	return refreshPromise;
+};
+
+axios.interceptors.response.use(
+	(response) => response,
+	async (error) => {
+		const originalConfig = error.config;
+		if (
+			[401, 403].includes(error.response?.status) &&
+			!originalConfig._retry &&
+			originalConfig.url !== "/auth/refresh"
+		) {
+			originalConfig._retry = true;
+
+			try {
+				await refreshTokenMethod();
+				return axiosInstance(originalConfig);
+			} catch (err) {
+				clearRefreshTimeout();
+				return Promise.reject(err);
+			}
+		}
+		return Promise.reject(error);
+	},
+);
+
+export default axiosInstance;
Index: frontend/src/context/authContext.tsx
===================================================================
--- frontend/src/context/authContext.tsx	(revision 0c86e77a055e354a71be0ff7c053051ca180993b)
+++ frontend/src/context/authContext.tsx	(revision 0c86e77a055e354a71be0ff7c053051ca180993b)
@@ -0,0 +1,63 @@
+import {
+	createContext,
+	useContext,
+	useEffect,
+	useState,
+	type Dispatch,
+	type ReactNode,
+	type SetStateAction,
+} from "react";
+import axiosInstance, {
+	refreshTokenMethod,
+	scheduleTokenRefresh,
+} from "../api/axiosInstance";
+import { type User } from "../types";
+
+interface AuthContextType {
+	user: User | undefined;
+	setUser: Dispatch<SetStateAction<User | undefined>>;
+	isAuthLoading: boolean;
+}
+
+const AuthContext = createContext<AuthContextType>({
+	user: undefined,
+	setUser: () => {},
+	isAuthLoading: false,
+});
+
+interface AuthProviderProps {
+	children: ReactNode;
+}
+
+export const AuthProvider = ({ children }: AuthProviderProps) => {
+	const [user, setUser] = useState<User | undefined>(undefined);
+	const [isAuthLoading, setIsAuthLoading] = useState(true);
+
+	useEffect(() => {
+		const fetchUserDetails = async () => {
+			try {
+				await refreshTokenMethod();
+				const response = await axiosInstance.get<{
+					tokenExpiresIn: number;
+					user: User;
+				}>("/user");
+				setUser(response.data.user);
+				scheduleTokenRefresh(response.data.tokenExpiresIn);
+			} catch (error) {
+				setUser(undefined);
+			} finally {
+				setIsAuthLoading(false);
+			}
+		};
+
+		fetchUserDetails();
+	}, []);
+
+	return (
+		<AuthContext.Provider value={{ user, setUser, isAuthLoading }}>
+			{children}
+		</AuthContext.Provider>
+	);
+};
+
+export const useAuth = () => useContext(AuthContext);
Index: frontend/src/main.tsx
===================================================================
--- frontend/src/main.tsx	(revision 15ec67f0faa204c0c5d0e1f4d4979ee8ff4fcd4a)
+++ frontend/src/main.tsx	(revision 0c86e77a055e354a71be0ff7c053051ca180993b)
@@ -2,9 +2,12 @@
 import { createRoot } from "react-dom/client";
 import App from "./App.tsx";
+import { AuthProvider } from "./context/authContext.tsx";
 import "./index.css";
 
 createRoot(document.getElementById("root")!).render(
 	<StrictMode>
-		<App />
+		<AuthProvider>
+			<App />
+		</AuthProvider>
 	</StrictMode>,
 );
Index: frontend/src/types.ts
===================================================================
--- frontend/src/types.ts	(revision 0c86e77a055e354a71be0ff7c053051ca180993b)
+++ frontend/src/types.ts	(revision 0c86e77a055e354a71be0ff7c053051ca180993b)
@@ -0,0 +1,12 @@
+export interface UserResponse {
+	username: string;
+	role: "ADMIN" | "NONADMIN";
+}
+
+export interface User {
+	username: string;
+	fullName: string;
+	email: string;
+	profilePhoto?: string | null;
+	role?: "ADMIN" | "NONADMIN";
+}
