UNPKG

myex-cli

Version:

Opinionated Express.js framework with CLI tools

262 lines (233 loc) 6.88 kB
import jwt from 'jsonwebtoken'; import bcrypt from 'bcryptjs'; import crypto from 'crypto'; import { User } from '../models/user.model.js'; import { Token } from '../models/token.model.js'; import { logger } from '../utils/logger.js'; export const authService = { /** * Find a user by email * @param {string} email - User email * @returns {Promise<Object|null>} User object or null */ findUserByEmail: async (email) => { try { return await User.findOne({ email }); } catch (error) { logger.error(`Error finding user by email: ${error.message}`); throw error; } }, /** * Create a new user * @param {Object} userData - User data * @returns {Promise<Object>} Created user */ createUser: async (userData) => { try { // Hash password const salt = await bcrypt.genSalt(10); const hashedPassword = await bcrypt.hash(userData.password, salt); // Create new user const user = new User({ email: userData.email, password: hashedPassword, name: userData.name, role: userData.role || 'user', }); // Save user to database await user.save(); return user; } catch (error) { logger.error(`Error creating user: ${error.message}`); throw error; } }, /** * Generate access token for a user * @param {Object} user - User object * @returns {string} JWT access token */ generateAccessToken: (user) => { return jwt.sign( { id: user._id, email: user.email, role: user.role, }, process.env.JWT_SECRET, { expiresIn: process.env.JWT_EXPIRES_IN || '1d' } ); }, /** * Generate refresh token for a user * @param {Object} user - User object * @returns {string} JWT refresh token */ generateRefreshToken: (user) => { return jwt.sign( { id: user._id, }, process.env.JWT_SECRET, { expiresIn: '7d' } ); }, /** * Save refresh token to database * @param {string} userId - User ID * @param {string} refreshToken - Refresh token * @returns {Promise<void>} */ saveRefreshToken: async (userId, refreshToken) => { try { // Remove existing refresh tokens for the user await Token.deleteMany({ userId, type: 'refresh' }); // Create new refresh token record const token = new Token({ userId, token: refreshToken, type: 'refresh', expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days }); await token.save(); } catch (error) { logger.error(`Error saving refresh token: ${error.message}`); throw error; } }, /** * Verify a refresh token and return the associated user * @param {string} refreshToken - Refresh token * @returns {Promise<Object|null>} User object or null */ verifyRefreshToken: async (refreshToken) => { try { // Verify JWT token const decoded = jwt.verify(refreshToken, process.env.JWT_SECRET); // Check if token exists in database const tokenDoc = await Token.findOne({ userId: decoded.id, token: refreshToken, type: 'refresh', expiresAt: { $gt: new Date() }, }); if (!tokenDoc) { return null; } // Get user const user = await User.findById(decoded.id); return user; } catch (error) { logger.error(`Error verifying refresh token: ${error.message}`); return null; } }, /** * Remove a refresh token from the database * @param {string} refreshToken - Refresh token * @returns {Promise<void>} */ removeRefreshToken: async (refreshToken) => { try { await Token.deleteOne({ token: refreshToken, type: 'refresh' }); } catch (error) { logger.error(`Error removing refresh token: ${error.message}`); throw error; } }, /** * Generate a password reset token for a user * @param {string} userId - User ID * @returns {Promise<string>} Reset token */ generatePasswordResetToken: async (userId) => { try { // Generate random token const resetToken = crypto.randomBytes(32).toString('hex'); // Hash token const hashedToken = crypto .createHash('sha256') .update(resetToken) .digest('hex'); // Remove existing reset tokens for the user await Token.deleteMany({ userId, type: 'reset' }); // Create new reset token record const token = new Token({ userId, token: hashedToken, type: 'reset', expiresAt: new Date(Date.now() + 1 * 60 * 60 * 1000), // 1 hour }); await token.save(); return resetToken; } catch (error) { logger.error(`Error generating password reset token: ${error.message}`); throw error; } }, /** * Verify a password reset token * @param {string} resetToken - Reset token * @returns {Promise<string|null>} User ID or null */ verifyPasswordResetToken: async (resetToken) => { try { // Hash token const hashedToken = crypto .createHash('sha256') .update(resetToken) .digest('hex'); // Find token in database const tokenDoc = await Token.findOne({ token: hashedToken, type: 'reset', expiresAt: { $gt: new Date() }, }); if (!tokenDoc) { return null; } return tokenDoc.userId; } catch (error) { logger.error(`Error verifying password reset token: ${error.message}`); return null; } }, /** * Update a user's password * @param {string} userId - User ID * @param {string} newPassword - New password * @returns {Promise<void>} */ updatePassword: async (userId, newPassword) => { try { // Hash new password const salt = await bcrypt.genSalt(10); const hashedPassword = await bcrypt.hash(newPassword, salt); // Update user password await User.findByIdAndUpdate(userId, { password: hashedPassword }); } catch (error) { logger.error(`Error updating password: ${error.message}`); throw error; } }, /** * Invalidate a password reset token * @param {string} resetToken - Reset token * @returns {Promise<void>} */ invalidatePasswordResetToken: async (resetToken) => { try { // Hash token const hashedToken = crypto .createHash('sha256') .update(resetToken) .digest('hex'); // Delete token from database await Token.deleteOne({ token: hashedToken, type: 'reset' }); } catch (error) { logger.error(`Error invalidating password reset token: ${error.message}`); throw error; } }, };