Files
complete-node-bootcamp/4-natours/after-section-14/controllers/authController.js
Jonas Schmedtmann f6252a6759 Fixed minor bug
2019-06-14 16:52:42 +01:00

242 lines
6.5 KiB
JavaScript

const crypto = require('crypto');
const { promisify } = require('util');
const jwt = require('jsonwebtoken');
const User = require('./../models/userModel');
const catchAsync = require('./../utils/catchAsync');
const AppError = require('./../utils/appError');
const Email = require('./../utils/email');
const signToken = id => {
return jwt.sign({ id }, process.env.JWT_SECRET, {
expiresIn: process.env.JWT_EXPIRES_IN
});
};
const createSendToken = (user, statusCode, req, res) => {
const token = signToken(user._id);
res.cookie('jwt', token, {
expires: new Date(
Date.now() + process.env.JWT_COOKIE_EXPIRES_IN * 24 * 60 * 60 * 1000
),
httpOnly: true,
secure: req.secure || req.headers['x-forwarded-proto'] === 'https'
});
// Remove password from output
user.password = undefined;
res.status(statusCode).json({
status: 'success',
token,
data: {
user
}
});
};
exports.signup = catchAsync(async (req, res, next) => {
const newUser = await User.create(req.body);
const url = `${req.protocol}://${req.get('host')}/me`;
// console.log(url);
await new Email(newUser, url).sendWelcome();
createSendToken(newUser, 201, req, res);
});
exports.login = catchAsync(async (req, res, next) => {
const { email, password } = req.body;
// 1) Check if email and password exist
if (!email || !password) {
return next(new AppError('Please provide email and password!', 400));
}
// 2) Check if user exists && password is correct
const user = await User.findOne({ email }).select('+password');
if (!user || !(await user.correctPassword(password, user.password))) {
return next(new AppError('Incorrect email or password', 401));
}
// 3) If everything ok, send token to client
createSendToken(user, 200, req, res);
});
exports.logout = (req, res) => {
res.cookie('jwt', 'loggedout', {
expires: new Date(Date.now() + 10 * 1000),
httpOnly: true
});
res.status(200).json({ status: 'success' });
};
exports.protect = catchAsync(async (req, res, next) => {
// 1) Getting token and check of it's there
let token;
if (
req.headers.authorization &&
req.headers.authorization.startsWith('Bearer')
) {
token = req.headers.authorization.split(' ')[1];
} else if (req.cookies.jwt) {
token = req.cookies.jwt;
}
if (!token) {
return next(
new AppError('You are not logged in! Please log in to get access.', 401)
);
}
// 2) Verification token
const decoded = await promisify(jwt.verify)(token, process.env.JWT_SECRET);
// 3) Check if user still exists
const currentUser = await User.findById(decoded.id);
if (!currentUser) {
return next(
new AppError(
'The user belonging to this token does no longer exist.',
401
)
);
}
// 4) Check if user changed password after the token was issued
if (currentUser.changedPasswordAfter(decoded.iat)) {
return next(
new AppError('User recently changed password! Please log in again.', 401)
);
}
// GRANT ACCESS TO PROTECTED ROUTE
req.user = currentUser;
res.locals.user = currentUser;
next();
});
// Only for rendered pages, no errors!
exports.isLoggedIn = async (req, res, next) => {
if (req.cookies.jwt) {
try {
// 1) verify token
const decoded = await promisify(jwt.verify)(
req.cookies.jwt,
process.env.JWT_SECRET
);
// 2) Check if user still exists
const currentUser = await User.findById(decoded.id);
if (!currentUser) {
return next();
}
// 3) Check if user changed password after the token was issued
if (currentUser.changedPasswordAfter(decoded.iat)) {
return next();
}
// THERE IS A LOGGED IN USER
res.locals.user = currentUser;
return next();
} catch (err) {
return next();
}
}
next();
};
exports.restrictTo = (...roles) => {
return (req, res, next) => {
// roles ['admin', 'lead-guide']. role='user'
if (!roles.includes(req.user.role)) {
return next(
new AppError('You do not have permission to perform this action', 403)
);
}
next();
};
};
exports.forgotPassword = catchAsync(async (req, res, next) => {
// 1) Get user based on POSTed email
const user = await User.findOne({ email: req.body.email });
if (!user) {
return next(new AppError('There is no user with email address.', 404));
}
// 2) Generate the random reset token
const resetToken = user.createPasswordResetToken();
await user.save({ validateBeforeSave: false });
// 3) Send it to user's email
try {
const resetURL = `${req.protocol}://${req.get(
'host'
)}/api/v1/users/resetPassword/${resetToken}`;
await new Email(user, resetURL).sendPasswordReset();
res.status(200).json({
status: 'success',
message: 'Token sent to email!'
});
} catch (err) {
user.passwordResetToken = undefined;
user.passwordResetExpires = undefined;
await user.save({ validateBeforeSave: false });
return next(
new AppError('There was an error sending the email. Try again later!'),
500
);
}
});
exports.resetPassword = catchAsync(async (req, res, next) => {
// 1) Get user based on the token
const hashedToken = crypto
.createHash('sha256')
.update(req.params.token)
.digest('hex');
const user = await User.findOne({
passwordResetToken: hashedToken,
passwordResetExpires: { $gt: Date.now() }
});
// 2) If token has not expired, and there is user, set the new password
if (!user) {
return next(new AppError('Token is invalid or has expired', 400));
}
user.password = req.body.password;
user.passwordConfirm = req.body.passwordConfirm;
user.passwordResetToken = undefined;
user.passwordResetExpires = undefined;
await user.save();
// 3) Update changedPasswordAt property for the user
// 4) Log the user in, send JWT
createSendToken(user, 200, req, res);
});
exports.updatePassword = catchAsync(async (req, res, next) => {
// 1) Get user from collection
const user = await User.findById(req.user.id).select('+password');
// 2) Check if POSTed current password is correct
if (!(await user.correctPassword(req.body.passwordCurrent, user.password))) {
return next(new AppError('Your current password is wrong.', 401));
}
// 3) If so, update password
user.password = req.body.password;
user.passwordConfirm = req.body.passwordConfirm;
await user.save();
// User.findByIdAndUpdate will NOT work as intended!
// 4) Log user in, send JWT
createSendToken(user, 200, req, res);
});