feat: Implement authentication flow with token verification and protected routes

This commit is contained in:
2025-10-24 20:45:37 +02:00
parent b99f52f09a
commit 960e91c38a
4 changed files with 50 additions and 42 deletions

View File

@@ -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 />}>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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" />;
}; };