Initial commit: teaching institution management API.
Express/MongoDB backend with JWT auth, RBAC, S3 uploads, notifications, and scheduled jobs.
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
// /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 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) {
|
||||
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) {
|
||||
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
|
||||
};
|
||||
Reference in New Issue
Block a user