diff --git a/web/src/context/AuthContext.tsx b/web/src/context/AuthContext.tsx
index 9f312ad..dbdc177 100644
--- a/web/src/context/AuthContext.tsx
+++ b/web/src/context/AuthContext.tsx
@@ -3,6 +3,11 @@ import { createContext } from "react";
interface AuthContextType {
isLoggedIn: boolean | undefined;
setIsLoggedIn: (state: boolean) => void;
+ getJwt: () => string;
}
-export const AuthContext = createContext
({ isLoggedIn: undefined, setIsLoggedIn: () => { return } });
+export const AuthContext = createContext({
+ isLoggedIn: undefined,
+ setIsLoggedIn: () => { return },
+ getJwt: () => ""
+});
diff --git a/web/src/context/AuthProvider.tsx b/web/src/context/AuthProvider.tsx
index 17922be..e983087 100644
--- a/web/src/context/AuthProvider.tsx
+++ b/web/src/context/AuthProvider.tsx
@@ -4,10 +4,19 @@ import { useCookies } from 'react-cookie';
// BUG: The rerender issue is ridiclious, and needs to be updated. Maybe using a global
// state management tool instead of a context
+//
+// BUG: We do not want to have access to these cookies in the UI, for security reasons.
+// Instead, we should implement an api `/auth/status` can be requested to validate
+// a token. Not sure how often this should get called, but it should be implemented.
export function AuthProvider({ children }: { children: ReactNode }) {
const [cookies] = useCookies(["jwt_token"]);
const [isLoggedIn, setIsLoggedIn] = useState(cookies.jwt_token !== undefined);
+ const getJwt = (): string => {
+ if (!cookies.jwt_token) return "";
+ return cookies.jwt_token as string;
+ }
+
useEffect(() => {
setIsLoggedIn(cookies.jwt_token !== undefined);
}, [cookies]);
@@ -18,7 +27,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
// }
return (
-
+
{children}
);
diff --git a/web/src/context/ProtectedRoute.tsx b/web/src/context/ProtectedRoute.tsx
new file mode 100644
index 0000000..a4f9c23
--- /dev/null
+++ b/web/src/context/ProtectedRoute.tsx
@@ -0,0 +1,2 @@
+
+export function ProtectedRoute
diff --git a/web/src/pages/Home.tsx b/web/src/pages/Home.tsx
index 9f10c80..3430f7a 100644
--- a/web/src/pages/Home.tsx
+++ b/web/src/pages/Home.tsx
@@ -10,7 +10,6 @@ import ContentCardSmall from "../components/cards/ContentCardSmall";
import RecipeSearchBar from "../components/inputs/RecipeSearchBar";
import { GetRecipeOfTheWeek } from "../services/RecipeService";
import { isApiError, type ApiError } from "../types/api/error";
-import axios from "axios";
import { AuthContext } from "../context/AuthContext";
export default function Home() {
@@ -113,6 +112,8 @@ export default function Home() {
setViewedRecipes(recipes);
}, []);
+ // TODO: Fetch other items when needed
+ // Fetch the recipe of the week
useEffect(() => {
async function fetch() {
const result: Recipe | ApiError = await GetRecipeOfTheWeek();
@@ -131,13 +132,6 @@ export default function Home() {
console.error(error);
}, [error]);
- // BUG: This is useless, just for testing
- useEffect(() => {
- // NOTE: Be sure to call this WITH CREDENTIALS
- void axios.get("http://localhost:3000/v2/api/protected", { withCredentials: true });
- }, []);
-
-
return (
<>
{/* Intro Section */}
diff --git a/web/src/pages/Profile.tsx b/web/src/pages/Profile.tsx
index a4d27c6..c773ba1 100644
--- a/web/src/pages/Profile.tsx
+++ b/web/src/pages/Profile.tsx
@@ -1,16 +1,29 @@
-import { useEffect, useState } from "react";
+import { use, useEffect, useState } from "react";
import type { User } from "../types/user";
import type { Recipe } from "../types/recipe";
import RecipeListItem from "../components/results/RecipeListItem";
import type { Engagement } from "../types/engagement";
import ActivityListItem from "../components/results/ActivityListItem";
+import { AuthContext } from "../context/AuthContext";
+import { GetAuthenticatedUser } from "../services/UserService";
+import { isApiError, type ApiError } from "../types/api/error";
+import { Logout } from "../services/AuthService";
+import { useNavigate } from "react-router-dom";
export default function Profile() {
+ // Context
+ const { getJwt } = use(AuthContext);
+ const navigate = useNavigate();
+
+ // Page state
+ const [error, setError] = useState("");
const [user, setUser] = useState(null);
const [recipes, setRecipes] = useState([]);
const [favorites, setFavorites] = useState([]);
const [activity, setActivity] = useState([]);
+ const [jwt, setJwt] = useState("");
+ // BUG: Remove this, used for testing
useEffect(() => {
const recipe: Recipe = {
Id: 1,
@@ -99,23 +112,43 @@ export default function Profile() {
Created: new Date(),
};
- const user: User = {
- Id: 1,
- GoogleId: "a",
- Name: "Hayden Hargreaves",
- Email: "hhargreaves2006@gmail.com",
- ImageUrl: "https://lh3.googleusercontent.com/a/ACg8ocLeT6ltjQIkiBy1MgMJDbQxtBfMVfn8sP4e1t7d0bCJeHFdpcea=s96-c",
- GoogleRefreshToken: "a",
- Created: new Date(),
- };
-
- setUser(user);
-
- setRecipes([recipe, recipe2, recipe, recipe, recipe, recipe, recipe]);
+ setRecipes([recipe, recipe2]);
setFavorites([recipe, recipe2]);
setActivity([eng]);
}, []);
+ // Log the user out and direct to the home page
+ const logoutHandler = (): void => {
+ void Logout();
+ void navigate("/v2/web/home");
+ }
+
+ // Get the JWT from the cookies
+ useEffect(() => {
+ setJwt(getJwt());
+ }, [getJwt]);
+
+ // Get the user when the JWTS change
+ useEffect(() => {
+ // No jwt, we can't get a user
+ if (!jwt) return;
+
+ async function fetch() {
+ const result: User | ApiError = await GetAuthenticatedUser();
+ if (isApiError(result)) {
+ setError(result.message);
+ return;
+ }
+ setUser(result);
+ }
+ void fetch();
+ }, [jwt]);
+
+ useEffect(() => {
+ if (error)
+ console.log("@error", error);
+ }, [error]);
+
return (
<>
{/* User Details Section */}
@@ -195,9 +228,9 @@ export default function Profile() {
{/* Logout Section TODO: Click event*/}
>
);
diff --git a/web/src/services/AuthService.ts b/web/src/services/AuthService.ts
index c7afff4..d66643f 100644
--- a/web/src/services/AuthService.ts
+++ b/web/src/services/AuthService.ts
@@ -1,5 +1,5 @@
import axios from "axios";
-import type { GetGoogleAuthUrlResponse } from "../types/api/auth";
+import type { GetGoogleAuthUrlResponse, LogoutResponse } from "../types/api/auth";
import type { ApiError } from "../types/api/error";
@@ -16,3 +16,11 @@ export async function GetGoogleAuthUrl (): Promise {
return response.data.url;
}
+
+export async function Logout (): Promise {
+ const response = await axios.get("http://localhost:3000/v2/api/auth/logout");
+
+ // This should never happen
+ if (response.status !== 204)
+ console.error("LOGOUT FAILED");
+}
diff --git a/web/src/services/UserService.ts b/web/src/services/UserService.ts
new file mode 100644
index 0000000..e33d992
--- /dev/null
+++ b/web/src/services/UserService.ts
@@ -0,0 +1,19 @@
+import axios from "axios";
+import type { ApiError } from "../types/api/error";
+import type { User } from "../types/user";
+import type { GetAuthenticateUserResponse } from "../types/api/user";
+
+
+export async function GetAuthenticatedUser(): Promise {
+ const response = await axios.get("http://localhost:3000/v2/api/user", { withCredentials: true });
+
+ if (response.data.status !== 200 || response.data.user === undefined){
+ const err: ApiError = {
+ status: response.data.status,
+ message: response.data.message
+ };
+ return err;
+ }
+
+ return response.data.user;
+}
diff --git a/web/src/types/api/auth.ts b/web/src/types/api/auth.ts
index 8acc4c6..fbd3e14 100644
--- a/web/src/types/api/auth.ts
+++ b/web/src/types/api/auth.ts
@@ -4,3 +4,7 @@ export interface GetGoogleAuthUrlResponse {
message: string;
url: string;
}
+
+export interface LogoutResponse {
+ stauts: number;
+}
diff --git a/web/src/types/api/user.ts b/web/src/types/api/user.ts
new file mode 100644
index 0000000..8720013
--- /dev/null
+++ b/web/src/types/api/user.ts
@@ -0,0 +1,7 @@
+import type { User } from "../user";
+
+export interface GetAuthenticateUserResponse {
+ status: number;
+ message: string;
+ user?: User;
+}