some improvements from claude
This commit is contained in:
@ -12,14 +12,37 @@ const JWT_SECRET = process.env.JWT_SECRET;
|
||||
const JWT_EXPIRES_IN = process.env.JWT_EXPIRES_IN || '1h';
|
||||
const SALT_ROUNDS = 10; // Cost factor for bcrypt hashing
|
||||
|
||||
// Simple rate limiting for login attempts
|
||||
const loginAttempts = {};
|
||||
const MAX_ATTEMPTS = 5;
|
||||
const LOCKOUT_TIME = 15 * 60 * 1000; // 15 minutes
|
||||
|
||||
// Password validation function
|
||||
const isPasswordValid = (password) => {
|
||||
// At least 8 characters, containing a number and a letter
|
||||
return password.length >= 8 &&
|
||||
/\d/.test(password) &&
|
||||
/[a-zA-Z]/.test(password);
|
||||
};
|
||||
|
||||
// POST /api/auth/register - User Registration
|
||||
router.post('/register', async (req, res) => {
|
||||
const { username, password } = req.body;
|
||||
|
||||
// Basic validation
|
||||
// Enhanced validation
|
||||
if (!username || !password) {
|
||||
return res.status(400).json({ message: 'Benutzername und Passwort sind erforderlich.' });
|
||||
}
|
||||
|
||||
if (username.length < 3 || username.length > 30) {
|
||||
return res.status(400).json({ message: 'Benutzername muss zwischen 3 und 30 Zeichen lang sein.' });
|
||||
}
|
||||
|
||||
if (!isPasswordValid(password)) {
|
||||
return res.status(400).json({
|
||||
message: 'Passwort muss mindestens 8 Zeichen lang sein und mindestens eine Zahl und einen Buchstaben enthalten.'
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
// Check if user already exists
|
||||
@ -51,6 +74,21 @@ router.post('/register', async (req, res) => {
|
||||
// POST /api/auth/login - User Login
|
||||
router.post('/login', async (req, res) => {
|
||||
const { username, password } = req.body;
|
||||
const ip = req.ip;
|
||||
|
||||
// Check for login attempts rate limiting
|
||||
if (loginAttempts[ip] && loginAttempts[ip].count >= MAX_ATTEMPTS) {
|
||||
const timeElapsed = Date.now() - loginAttempts[ip].timestamp;
|
||||
if (timeElapsed < LOCKOUT_TIME) {
|
||||
const minutesLeft = Math.ceil((LOCKOUT_TIME - timeElapsed) / 60000);
|
||||
return res.status(429).json({
|
||||
message: `Zu viele Anmeldeversuche. Bitte versuchen Sie es in ${minutesLeft} Minuten erneut.`
|
||||
});
|
||||
} else {
|
||||
// Reset attempts after lockout period
|
||||
delete loginAttempts[ip];
|
||||
}
|
||||
}
|
||||
|
||||
if (!username || !password) {
|
||||
return res.status(400).json({ message: 'Benutzername und Passwort sind erforderlich.' });
|
||||
@ -63,9 +101,19 @@ router.post('/login', async (req, res) => {
|
||||
|
||||
// Check if user exists and password is correct
|
||||
if (!user || !(await bcrypt.compare(password, user.password_hash))) {
|
||||
// Track failed login attempts
|
||||
if (!loginAttempts[ip]) {
|
||||
loginAttempts[ip] = { count: 0, timestamp: Date.now() };
|
||||
}
|
||||
loginAttempts[ip].count++;
|
||||
loginAttempts[ip].timestamp = Date.now();
|
||||
|
||||
return res.status(401).json({ message: 'Ungültiger Benutzername oder Passwort.' }); // 401 Unauthorized
|
||||
}
|
||||
|
||||
// Reset login attempts on successful login
|
||||
delete loginAttempts[ip];
|
||||
|
||||
// User authenticated successfully, create JWT payload
|
||||
const payload = {
|
||||
id: user.id,
|
||||
|
Reference in New Issue
Block a user