feat: Implement authentication flow with token verification and protected routes
This commit is contained in:
@@ -3,6 +3,10 @@ import { LoginPage } from "@/pages/LoginPage";
|
|||||||
import { BrowserRouter, Route, Routes } from "react-router-dom";
|
import { BrowserRouter, Route, Routes } from "react-router-dom";
|
||||||
import { HomePage } from "@/pages/HomePage";
|
import { HomePage } from "@/pages/HomePage";
|
||||||
import { ProtectedRoutes } from "./utils/ProtectedRoutes";
|
import { ProtectedRoutes } from "./utils/ProtectedRoutes";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import Cookies from "js-cookie";
|
||||||
|
import { useAtom } from "jotai";
|
||||||
|
import { setIsLoggedInAtom } from "@/states/Atoms";
|
||||||
|
|
||||||
const API_BASE =
|
const API_BASE =
|
||||||
(import.meta as any).env?.VITE_BACKEND_URL ||
|
(import.meta as any).env?.VITE_BACKEND_URL ||
|
||||||
@@ -10,9 +14,30 @@ const API_BASE =
|
|||||||
"http://localhost:8002";
|
"http://localhost:8002";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
const [, setIsLoggedIn] = useAtom(setIsLoggedInAtom);
|
||||||
|
useEffect(() => {
|
||||||
|
if (Cookies.get("token")) {
|
||||||
|
const verifyToken = async () => {
|
||||||
|
const response = await fetch(`${API_BASE}/api/verifyToken`, {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${Cookies.get("token")}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (response.ok) {
|
||||||
|
setIsLoggedIn(true);
|
||||||
|
} else {
|
||||||
|
Cookies.remove("token");
|
||||||
|
setIsLoggedIn(false);
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
verifyToken();
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div>Layout Component</div>
|
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route element={<ProtectedRoutes />}>
|
<Route element={<ProtectedRoutes />}>
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
|
import { setIsLoggedInAtom } from "@/states/Atoms";
|
||||||
|
import { useAtom } from "jotai";
|
||||||
|
|
||||||
export const HomePage = () => {
|
export const HomePage = () => {
|
||||||
|
const [isLoggedIn] = useAtom(setIsLoggedInAtom);
|
||||||
|
console.log(isLoggedIn);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h1>Home Page</h1>
|
<h1>Home Page</h1>
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { useState } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import MyAlert from "../components/myChakra/MyAlert";
|
import MyAlert from "../components/myChakra/MyAlert";
|
||||||
import { Button, Card, Field, Input, Stack } from "@chakra-ui/react";
|
import { Button, Card, Field, Input, Stack } from "@chakra-ui/react";
|
||||||
import { setIsLoggedInAtom } from "@/states/Atoms";
|
import { setIsLoggedInAtom } from "@/states/Atoms";
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import { useEffect } from "react";
|
|
||||||
import Cookies from "js-cookie";
|
import Cookies from "js-cookie";
|
||||||
|
import { Navigate, useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
const API_BASE =
|
const API_BASE =
|
||||||
(import.meta as any).env?.VITE_BACKEND_URL ||
|
(import.meta as any).env?.VITE_BACKEND_URL ||
|
||||||
@@ -13,29 +13,29 @@ const API_BASE =
|
|||||||
|
|
||||||
export const LoginPage = () => {
|
export const LoginPage = () => {
|
||||||
const [isLoggedIn, setIsLoggedIn] = useAtom(setIsLoggedInAtom);
|
const [isLoggedIn, setIsLoggedIn] = useAtom(setIsLoggedInAtom);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
console.log(isLoggedIn);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isLoggedIn) {
|
if (isLoggedIn) {
|
||||||
window.location.href = "/";
|
navigate("/", { replace: true });
|
||||||
}
|
}
|
||||||
}, [isLoggedIn]);
|
}, [isLoggedIn, navigate]);
|
||||||
|
|
||||||
const loginFnc = async (username: string, password: string) => {
|
const loginFnc = async (username: string, password: string) => {
|
||||||
const response = await fetch(`${API_BASE}/api/login`, {
|
const response = await fetch(`${API_BASE}/api/login`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: { "Content-Type": "application/json" },
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ username, password }),
|
body: JSON.stringify({ username, password }),
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorData = await response.json();
|
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: errorData.message,
|
message: data.message ?? "Login fehlgeschlagen",
|
||||||
description: errorData.description,
|
description: data.description ?? "",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,8 +58,13 @@ export const LoginPage = () => {
|
|||||||
setIsError(true);
|
setIsError(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
navigate("/", { replace: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (isLoggedIn) {
|
||||||
|
return <Navigate to="/" replace />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex items-center justify-center p-4">
|
<div className="min-h-screen flex items-center justify-center p-4">
|
||||||
<form onSubmit={(e) => e.preventDefault()}>
|
<form onSubmit={(e) => e.preventDefault()}>
|
||||||
@@ -94,7 +99,7 @@ export const LoginPage = () => {
|
|||||||
<MyAlert status="error" title={errorMsg} description={errorDsc} />
|
<MyAlert status="error" title={errorMsg} description={errorDsc} />
|
||||||
)}
|
)}
|
||||||
<Button type="submit" onClick={() => handleLogin()} variant="solid">
|
<Button type="submit" onClick={() => handleLogin()} variant="solid">
|
||||||
Login
|
Login <p>{isLoggedIn}</p>
|
||||||
</Button>
|
</Button>
|
||||||
</Card.Footer>
|
</Card.Footer>
|
||||||
</Card.Root>
|
</Card.Root>
|
||||||
|
|||||||
@@ -1,37 +1,9 @@
|
|||||||
import { Outlet, Navigate } from "react-router-dom";
|
import { Navigate, Outlet } from "react-router-dom";
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import { setIsLoggedInAtom } from "@/states/Atoms";
|
import { setIsLoggedInAtom } from "@/states/Atoms";
|
||||||
import { useEffect } from "react";
|
|
||||||
import Cookies from "js-cookie";
|
|
||||||
|
|
||||||
const API_BASE =
|
|
||||||
(import.meta as any).env?.VITE_BACKEND_URL ||
|
|
||||||
import.meta.env.VITE_BACKEND_URL ||
|
|
||||||
"http://localhost:8002";
|
|
||||||
|
|
||||||
export const ProtectedRoutes = () => {
|
export const ProtectedRoutes = () => {
|
||||||
const [isLoggedIn, setIsLoggedIn] = useAtom(setIsLoggedInAtom);
|
const [isLoggedIn] = useAtom(setIsLoggedInAtom);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (Cookies.get("token")) {
|
|
||||||
const verifyToken = async () => {
|
|
||||||
const response = await fetch(`${API_BASE}/api/verifyToken`, {
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${Cookies.get("token")}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (response.ok) {
|
|
||||||
setIsLoggedIn(true);
|
|
||||||
} else {
|
|
||||||
Cookies.remove("token");
|
|
||||||
setIsLoggedIn(false);
|
|
||||||
window.location.reload();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
verifyToken();
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return isLoggedIn ? <Outlet /> : <Navigate to="/login" />;
|
return isLoggedIn ? <Outlet /> : <Navigate to="/login" />;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user