Fixed multiple bugs

This commit is contained in:
Torsten Schulz
2025-07-17 13:56:34 +02:00
parent 353b8386ee
commit e827964688
7 changed files with 105 additions and 67 deletions

View File

@@ -23,26 +23,24 @@ const activate = async (req, res, next) => {
}
};
const loginUser = async (req, res) => {
const { username, password } = req.body;
const user = await User.findOne({ where: { username } });
if (!user || !(await user.validatePassword(password))) {
return res.status(401).json({ message: 'Ungültige Anmeldedaten' });
const loginUser = async (req, res, next) => {
try {
const { email, password } = req.body;
const result = await login(email, password);
res.status(200).json(result);
} catch (error) {
next(error);
}
const token = jwt.sign({ userId: user.id }, process.env.JWT_SECRET, { expiresIn: '1h' });
await UserToken.create({
userId: user.id,
token,
expiresAt: new Date(Date.now() + 3600 * 1000),
});
res.json({ token });
};
const logoutUser = async (req, res) => {
const token = req.headers['authorization']?.split(' ')[1];
if (!token) return res.status(400).json({ message: 'Token fehlt' });
await UserToken.destroy({ where: { token } });
res.json({ message: 'Logout erfolgreich' });
const logoutUser = async (req, res, next) => {
try {
const token = req.headers['authorization']?.split(' ')[1];
const result = await logout(token);
res.status(200).json(result);
} catch (error) {
next(error);
}
};
export { registerUser, activate, loginUser, logoutUser };

View File

@@ -1,18 +1,23 @@
import User from '../models/User.js';
import jwt from 'jsonwebtoken';
import UserToken from '../models/UserToken.js';
export const authenticate = async (req, res, next) => {
try {
const { userid: userId, authcode: authCode } = req.headers;
if (!userId || !authCode) {
return res.status(401).json({ error: 'Unauthorized: Missing credentials' });
}
const user = await User.findOne({ where: { email: userId, authCode: authCode } });
if (!user) {
return res.status(401).json({ error: 'Unauthorized: Invalid credentials' });
}
next();
} catch(error) {
console.log(error);
return res.status(500).json({ error: 'Internal Server Error at auth' });
let token = req.headers['authorization']?.split(' ')[1];
if (!token) {
token = req.headers['authcode'];
}
if (!token) {
return res.status(401).json({ error: 'Unauthorized: Token fehlt' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
const tokenRecord = await UserToken.findOne({ where: { token } });
if (!tokenRecord || tokenRecord.expiresAt < new Date()) {
return res.status(401).json({ error: 'Unauthorized: Invalid credentials' });
}
req.user = { id: decoded.userId };
next();
} catch (err) {
return res.status(401).json({ error: 'Unauthorized: Invalid credentials' });
}
};

View File

@@ -63,4 +63,8 @@ const User = sequelize.define('User', {
},
});
User.prototype.validatePassword = function(password) {
return bcrypt.compare(password, this.password);
};
export default User;

View File

@@ -27,6 +27,7 @@ import TournamentMember from './TournamentMember.js';
import TournamentMatch from './TournamentMatch.js';
import TournamentResult from './TournamentResult.js';
import Accident from './Accident.js';
import UserToken from './UserToken.js';
User.hasMany(Log, { foreignKey: 'userId' });
Log.belongsTo(User, { foreignKey: 'userId' });
@@ -203,4 +204,5 @@ export {
TournamentMatch,
TournamentResult,
Accident,
UserToken,
};

View File

@@ -8,7 +8,7 @@ import {
DiaryNote, DiaryTag, MemberDiaryTag, DiaryDateTag, DiaryMemberNote, DiaryMemberTag,
PredefinedActivity, DiaryDateActivity, Match, League, Team, Group,
GroupActivity, Tournament, TournamentGroup, TournamentMatch, TournamentResult,
TournamentMember, Accident
TournamentMember, Accident, UserToken
} from './models/index.js';
import authRoutes from './routes/authRoutes.js';
import clubRoutes from './routes/clubRoutes.js';
@@ -99,6 +99,7 @@ app.get('*', (req, res) => {
await TournamentMatch.sync({ alter: true });
await TournamentResult.sync({ alter: true });
await Accident.sync({ alter: true });
await UserToken.sync({ alter: true });
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);

View File

@@ -1,6 +1,7 @@
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import User from '../models/User.js';
import UserToken from '../models/UserToken.js';
import { sendActivationEmail } from './emailService.js';
const register = async (email, password) => {
@@ -25,22 +26,28 @@ const activateUser = async (activationCode) => {
};
const login = async (email, password) => {
if (!email || !password) {
throw { status: 400, message: 'Email und Passwort sind erforderlich.' };
}
const user = await User.findOne({ where: { email } });
if (!user || !user.isActive) throw new Error('Invalid email or password.');
const isPasswordValid = await bcrypt.compare(password, user.password);
if (!isPasswordValid) throw new Error('Invalid email or password!');
const token = jwt.sign({ userId: user.hashedId }, process.env.JWT_SECRET, { expiresIn: '1h' });
user.authCode = token;
await user.save();
if (!user || !(await bcrypt.compare(password, user.password))) {
throw { status: 401, message: 'Ungültige Anmeldedaten' };
}
const token = jwt.sign({ userId: user.id }, process.env.JWT_SECRET, { expiresIn: '1h' });
await UserToken.create({
userId: user.id,
token,
expiresAt: new Date(Date.now() + 3600 * 1000),
});
return { token };
};
const logout = async(userId, authToken) => {
const user = await User.findOne({ where: { id: userId, authToken: authToken }});
if (!user) {
throw new Error('not found');
const logout = async (token) => {
if (!token) {
throw { status: 400, message: 'Token fehlt' };
}
user.update({ authToken: null });
}
await UserToken.destroy({ where: { token } });
return { message: 'Logout erfolgreich' };
};
export { register, activateUser, login, logout };

View File

@@ -1,24 +1,45 @@
import User from '../models/User.js'
import UserClub from '../models/UserClub.js';
import jwt from 'jsonwebtoken';
import { Op } from 'sequelize';
import User from '../models/User.js';
import UserToken from '../models/UserToken.js';
import UserClub from '../models/UserClub.js'; // <-- hier hinzufügen
import HttpError from '../exceptions/HttpError.js';
import { config } from 'dotenv';
config(); // sorgt dafür, dass process.env.JWT_SECRET geladen wird
export const getUserByToken = async(token) => {
export const getUserByToken = async (token) => {
try {
const user = await User.findOne({
where: [
{auth_code: token}
]
});
return user;
} catch (error) {
console.log(error);
const err = new HttpError('noaccess', 403);
throw err;
}
}
// 1. JWT validieren
const payload = jwt.verify(token, process.env.JWT_SECRET);
export const hasUserClubAccess = async(userId, clubId) => {
// 2. Token-Eintrag prüfen (existiert und nicht abgelaufen)
const stored = await UserToken.findOne({
where: {
token,
expiresAt: { [Op.gt]: new Date() }
}
});
if (!stored) {
throw new HttpError('Token abgelaufen oder ungültig', 401);
}
// 3. User laden
const user = await User.findByPk(payload.userId);
if (!user) {
throw new HttpError('Benutzer nicht gefunden', 404);
}
return user;
} catch (err) {
console.error(err);
// Falls es ein HttpError ist, einfach weiterwerfen
if (err instanceof HttpError) throw err;
// ansonsten pauschal „noaccess“
throw new HttpError('noaccess', 403);
}
};
export const hasUserClubAccess = async (userId, clubId) => {
try {
console.log('[hasUserClubAccess]');
const userClub = await UserClub.findOne({
@@ -29,23 +50,23 @@ export const hasUserClubAccess = async(userId, clubId) => {
}
});
return userClub !== null;
} catch(error) {
} catch (error) {
console.log(error);
throw new HttpError('notfound', 500);
}
console.log('---- no user found');
}
export const checkAccess = async(userToken, clubId) => {
export const checkAccess = async (userToken, clubId) => {
try {
const user = await getUserByToken(userToken);
if (!await hasUserClubAccess(user.id, clubId)) {
const hasAccess = await hasUserClubAccess(user.id, clubId);
if (!hasAccess) {
console.log('no club access');
const err = new HttpError('noaccess', 403);
throw err;
throw new HttpError('noaccess', 403);
}
} catch (error) {
console.log(error);
throw error;
throw error;
}
}
};