// /components/auth/authService.js const jwt = require('jsonwebtoken'); const bcrypt = require('bcryptjs'); const User = require('../users/userModel'); const config = require('../../config/config'); const AppError = require('../../utils/AppError'); const { isBootstrapSuperAdminDisabled } = require('../../utils/superAdmin'); const generateTokens = (user) => { const payload = { id: user._id, username: user.username, role: user.role?._id || user.role }; const accessToken = jwt.sign(payload, config.JWT_ACCESS_SECRET, { expiresIn: config.JWT_ACCESS_EXPIRES_IN }); const refreshToken = jwt.sign({ id: user._id }, config.JWT_REFRESH_SECRET, { expiresIn: config.JWT_REFRESH_EXPIRES_IN }); return { accessToken, refreshToken }; }; const login = async (username, password) => { const user = await User.findOne({ username }).populate('role'); if (!user || !user.isActive || isBootstrapSuperAdminDisabled(user)) { throw new AppError('INVALID_CREDENTIALS'); } const isMatch = await bcrypt.compare(password, user.passwordHash); if (!isMatch) { throw new AppError('INVALID_CREDENTIALS'); } const { accessToken, refreshToken: refreshTokenString } = generateTokens(user); const expiresAt = new Date(); expiresAt.setDate(expiresAt.getDate() + 7); user.refreshTokens.push({ token: refreshTokenString, expiresAt }); await user.save(); const userObject = user.toObject(); delete userObject.passwordHash; delete userObject.refreshTokens; return { user: userObject, accessToken, refreshToken: refreshTokenString }; }; const refreshToken = async (refreshTokenString) => { let decoded; try { decoded = jwt.verify(refreshTokenString, config.JWT_REFRESH_SECRET); } catch (err) { throw new AppError('INVALID_REFRESH_TOKEN'); } const user = await User.findById(decoded.id).populate('role'); if (!user || !user.isActive || isBootstrapSuperAdminDisabled(user)) { throw new AppError('INVALID_REFRESH_TOKEN'); } const tokenIndex = user.refreshTokens.findIndex(rt => rt.token === refreshTokenString); if (tokenIndex === -1) { throw new AppError('INVALID_REFRESH_TOKEN'); } if (new Date() > new Date(user.refreshTokens[tokenIndex].expiresAt)) { user.refreshTokens.splice(tokenIndex, 1); await user.save(); throw new AppError('TOKEN_EXPIRED'); } user.refreshTokens.splice(tokenIndex, 1); const { accessToken, refreshToken: newRefreshToken } = generateTokens(user); const expiresAt = new Date(); expiresAt.setDate(expiresAt.getDate() + 7); user.refreshTokens.push({ token: newRefreshToken, expiresAt }); await user.save(); return { accessToken, refreshToken: newRefreshToken }; }; const logout = async (userId, refreshTokenString) => { const user = await User.findById(userId); if (user) { user.refreshTokens = user.refreshTokens.filter(rt => rt.token !== refreshTokenString); await user.save(); } return true; }; module.exports = { login, refreshToken, logout };