diff --git a/backend/internal/auth/handler.go b/backend/internal/auth/handler.go index 5cc2826..84d3e12 100644 --- a/backend/internal/auth/handler.go +++ b/backend/internal/auth/handler.go @@ -24,7 +24,7 @@ func LoginHandler(c *gin.Context) { } // Systemnutzer - if req.Email == "systemuser@example.com" { + if req.Email == "test@localhost.de" { token, err := CreateJWT("system-user-id", req.Email, "admin", time.Hour*24*7) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Token error"}) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 7748518..15b9ed7 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -17,6 +17,7 @@ "@types/react": "^19.1.4", "@types/react-dom": "^19.1.5", "axios": "^1.9.0", + "jwt-decode": "^4.0.0", "react": "^19.1.0", "react-dom": "^19.1.0", "react-router-dom": "^7.6.0", @@ -10257,6 +10258,14 @@ "node": ">=4.0" } }, + "node_modules/jwt-decode": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", + "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", + "engines": { + "node": ">=18" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", diff --git a/frontend/package.json b/frontend/package.json index 22e8ff3..b8253df 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,6 +12,7 @@ "@types/react": "^19.1.4", "@types/react-dom": "^19.1.5", "axios": "^1.9.0", + "jwt-decode": "^4.0.0", "react": "^19.1.0", "react-dom": "^19.1.0", "react-router-dom": "^7.6.0", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index f30996b..d4a3bcc 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -8,6 +8,7 @@ import Players from './pages/Players'; import Navigation from './pages/Navigation'; function App() { + return ( diff --git a/frontend/src/pages/AuthContext.tsx b/frontend/src/pages/AuthContext.tsx index 945499a..16ac1a7 100644 --- a/frontend/src/pages/AuthContext.tsx +++ b/frontend/src/pages/AuthContext.tsx @@ -3,7 +3,7 @@ import React, { createContext, useContext, useState, useEffect } from 'react'; interface AuthContextType { token: string | null; userId: string | null; - login: (token: string, userId: string) => void; + login: (token: string, userId: string, role: string) => void; logout: () => void; } @@ -12,6 +12,7 @@ const AuthContext = createContext(undefined); export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { const [token, setToken] = useState(null); const [userId, setUserId] = useState(null); + const [role, setRole] = useState(null); useEffect(() => { const storedToken = localStorage.getItem('token'); @@ -22,18 +23,22 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children } }, []); - const login = (token: string, userId: string) => { + const login = (token: string, userId: string, role: string) => { setToken(token); setUserId(userId); + setRole(role); localStorage.setItem('token', token); localStorage.setItem('userId', userId); + localStorage.setItem('role', role); }; const logout = () => { setToken(null); setUserId(null); + setRole(null); localStorage.removeItem('token'); localStorage.removeItem('userId'); + localStorage.removeItem('role'); }; return ( @@ -43,6 +48,15 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children ); }; +/** + * Custom React hook to access the authentication context. + * + * @returns {AuthContextType} The current authentication context value. + * @throws {Error} If used outside of an `AuthProvider`. + * + * @example + * const { user, login, logout } = useAuth(); + */ export function useAuth() { const context = useContext(AuthContext); if (!context) throw new Error('useAuth must be used within AuthProvider'); diff --git a/frontend/src/pages/LoginPage.tsx b/frontend/src/pages/LoginPage.tsx index 746ff78..5a982b5 100644 --- a/frontend/src/pages/LoginPage.tsx +++ b/frontend/src/pages/LoginPage.tsx @@ -1,22 +1,35 @@ import { useState } from 'react'; import { useAuth } from './AuthContext'; import { login as apiLogin } from './api'; +import { jwtDecode } from 'jwt-decode'; +import { useNavigate } from 'react-router-dom'; export default function LoginPage() { const { login } = useAuth(); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [error, setError] = useState(null); + const navigate = useNavigate(); // ← Navigation-Hook + async function handleSubmit(e: React.FormEvent) { e.preventDefault(); try { const data = await apiLogin(email, password); - console.log(data); // Token aus JWT extrahieren (hier: UserID im Token Payload) // Für Demo: Einfach Dummy UserID setzen, oder später JWT decode implementieren - login(data.token, 'user-id-from-token'); - } catch { + type MyJwtPayload = { + userId: string + email: string; + role: string; + } & object; + const decodedData = jwtDecode(data.token); + login(data.token, decodedData.userId, decodedData.role); + // Nach dem Login zur Dashboard-Seite navigieren + setTimeout(() => { + navigate('/'); + }, 1000); + } catch { setError('Login fehlgeschlagen'); } }