UNPKG

@kapvm/create-express-app

Version:

A CLI tool to scaffold an Express.js boilerplate project

238 lines (197 loc) 7.42 kB
const { promisify } = require('util'); const jwt = require('jsonwebtoken'); const crypto = require('crypto'); const User = require('./../models/userModel'); const catchAsync = require('./../utils/catchAsync'); const AppError = require('./../utils/appError'); //Label // utility methods //creating a token [JWT Token] const getJWTToken = (id) => { return jwt.sign({ id }, process.env.JWT_SECRET, { expiresIn: process.env.JWT_EXPIRE, }); }; const createSendToken = (user, statusCode, req, res) => { const token = getJWTToken(user._id); const cookieOptions = { expires: new Date( Date.now() + process.env.JWT_COOKIE_EXPIRE * 24 * 60 * 60 * 1000, ), httpOnly: true, secure: req.secure || req.headers['x-forwarded-proto'] === 'https', }; //send the cookie with 'res' object res.cookie('jwt', token, cookieOptions); res.status(statusCode).json({ status: 'success', token, user, }); }; //Label // Main handlers //=> // Signing up the user (first-time) exports.signUp = catchAsync(async (req, res, next) => { const newUser = await User.create(req.body); createSendToken(newUser, 201, req, res); }); //=> // logging in user and send jwt token exports.login = catchAsync(async (req, res, next) => { const { email, password } = req.body; //1. check if password and email exists in req.body or not if (!email || !password) return next(new AppError('Please provide email and password both', 400)); //2. check if user exist, and check if password is correct or not const user = await User.findOne({ email }).select('+password'); if (!user || !(await user.checkPassword(password, user.password))) return next(new AppError('Email or Password incorrect', 401)); //3. provide the jwt token createSendToken(user, 200, req, res); }); //=> // Protect some content from unauthorized access (simple checks if user logged in or not) exports.protect = catchAsync(async (req, res, next) => { //1. Get the token and check if its there (from client, means in request) let token; if (req.cookies.jwt) token = req.cookies.jwt; else if (req.headers.authorization) token = req.headers.authorization.split(' ')[1]; // if (!token) return next(new AppError("User belong to this Token is no longer exist, please log in again", 401)) //2. Verification of token const decoded = await promisify(jwt.verify)(token, process.env.JWT_SECRET); //3. check if user still exist const currentUser = await User.findById(decoded.id); if (!currentUser) return next( new AppError( 'User belong to this Token is no longer exist, please log in again', 401, ), ); //4. check if user changed the password after the token was issued if (currentUser.isPasswordChangedAfter(decoded.iat)) return next( new AppError('User Changed the password, please log in again', 401), ); //Grant access to the protected content now req.user = currentUser; res.locals.user = currentUser; next(); }); //=> // Check if user is logged in or not (no errors if not) exports.isLoggedIn = async (req, res, next) => { try { if (req.cookies.jwt) { //1. Get the token and check if its there (from client, means in request) // 2. Verification of token const decoded = await promisify(jwt.verify)( req.cookies.jwt, process.env.JWT_SECRET, ); //3. check if user still exist const currentUser = await User.findById(decoded.id); if (!currentUser) return next(); //4. check if user changed the password after the token was issued if (currentUser.isPasswordChangedAfter(decoded.iat)) return next(); //Grant access to the protected content now res.locals.user = currentUser; return next(); } return next(); } catch (e) { return next(); } }; //=> // Adding the restriction to the specific routes, we can use this on any route to restrict it from certain users exports.restrictTo = (...roles) => { return (req, res, next) => { if (!roles.includes(req.user.role)) return next( new AppError('You are not authorized to use this resource', 403), ); next(); }; }; //=> // Forgot password module, used to send the request email to user for password reset link [send token for password reset] exports.forgotPassword = catchAsync(async (req, res, next) => { //1. Get the user based on the email provided from POST method const user = await User.findOne({ email: req.body.email }); if (!user) return next(new AppError('There is no user with that email', 404)); //2. Generate the password reset token const passwordResetToken = user.getPasswordResetToken(); await user.save({ validateBeforeSave: false }); //3.send the token to user's email try { res.status(200).json({ status: 'success', message: 'Token is sent through email, check your inbox and also spam folder', passwordResetToken, }); } catch (e) { user.passwordResetExpires = undefined; user.passwordResetToken = undefined; await user.save({ validateBeforeSave: false }); return next( new AppError( 'There was an error sending the email, please try again later', 500, ), ); } }); //=> // reset password module, which actually resets the password using a resetToken exports.resetPassword = catchAsync(async (req, res, next) => { //1. check if user available based on token const HashedResetToken = crypto .createHash('sha256') .update(req.params.token) .digest('hex'); const user = await User.findOne({ passwordResetToken: HashedResetToken, passwordResetExpires: { $gt: Date.now() }, }); if (!user) return next(new AppError('Token is Expired or invalid', 400)); //2. if token is valid and there is a user, set the new password user.password = req.body.password; user.passwordConfirm = req.body.passwordConfirm; user.passwordResetToken = undefined; user.passwordResetExpires = undefined; await user.save(); //3. update the passwordChangedAt property [must be done on model side] //4. Log in the user with JWT createSendToken(user, 200, req, res); }); //=> // Update the user password without prompting forgot password [for logged in users] exports.updatePassword = catchAsync(async (req, res, next) => { //1. get the user from collection const user = await User.findById(req.user._id).select('+password'); if (!user) return next(new AppError('Invalid JWT token, please log in again', 400)); //2. check if password is correct or not if (!user.checkPassword(req.body.currentPassword, user.password)) return next(new AppError('Your current password is invalid', 400)); //3. if yes, then change the password user.password = req.body.newPassword; user.passwordConfirm = req.body.NewPasswordConfirm; user.save(); //4. log user in again with JWT createSendToken(user, 200, req, res); }); //=> // Logout the user exports.logout = (req, res) => { res.cookie('jwt', 'LoggedOut', { expires: new Date(Date.now() + 5 * 1000), httpOnly: true, }); res.status(200).json({ status: 'success' }); };