Node.js and Express.js part 7

working with user authentication using JWT

npm i jsonwebtoken bcryptjs
const mongoose = require('mongoose');const UserSchema = new mongoose.Schema({
name: {
type: String,
required: [true, 'Please add a name']
},
email: {
type: String,
required: [true, 'Please add an email'],
unique: true,
match: [
/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/,
'Please add a valid email'
]
},
role: {
type: String,
enum: ['user', 'publisher'],
default: 'user'
},
password: {
type: String,
required: [true, 'Please add a password'],
minlength: 6,
select: false
},
resetPasswordToken: String,
resetPasswordExpire: Date,
createdAt: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model('User', UserSchema);
const ErrorResponse = require('../utils/errorResponse');
const User = require('../models/User');
const asyncHandler = require('../middleware/async')
exports.register = asyncHandler(async(req,res,next) => {
res.status(200).json({success:true});
});
const express = require('express');
const {register} = require('../controllers/auth');
const router = express.Router();router.post('/register',register);module.exports = router;
const auth = require('./routes/auth');
app.use('/api/v1/auth',auth);
{{URL}}/api/v1/auth/register 

now let's register the user and hash the password

const ErrorResponse = require('../utils/errorResponse');
const User = require('../models/User');
const asyncHandler = require('../middleware/async')
exports.register = asyncHandler(async(req,res,next) => {
const { name, email, password, role } = req.body;
// Create user
const user = await User.create({
name,
email,
password,
role
});
res.status(200).json({success:true});
//sendTokenResponse(user, 200, res);
});
const bcrypt = require('bcryptjs');UserSchema.pre('save', async function(next) {  const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt);
});

get JSON web token

JWT_SECRET = ugff7ds6f7dsfsd9fdsgfdfdfJWT_EXPIRE=30d
UserSchema.methods.getSignedJwtToken = function() {
return jwt.sign({ id: this._id }, process.env.JWT_SECRET, {
expiresIn: process.env.JWT_EXPIRE
});
};
exports.register = asyncHandler(async(req,res,next) => {
const { name, email, password, role } = req.body;
// Create user
const user = await User.create({
name,
email,
password,
role
});
const token = user.getSignedJwtToken();
res.status(200).json({success:true,token:token});
//sendTokenResponse(user, 200, res);
});
{"name":"john doe","email":"j@gmail.com","password":"123456","role":"publisher"}
exports.login = asyncHandler(async (req, res, next) => {
const { email, password } = req.body;

// Validate emil & password
if (!email || !password) {
return next(new ErrorResponse('Please provide an email and password', 400));
}

// Check for user
const user = await User.findOne({ email }).select('+password');

if (!user) {
return next(new ErrorResponse('Invalid credentials', 401));
}

// Check if password matches
const isMatch = await user.matchPassword(password);

if (!isMatch) {
return next(new ErrorResponse('Invalid credentials', 401));
}

const token = user.getSignedJwtToken();
res.status(200).json({success:true,token:token});
});
UserSchema.methods.matchPassword = async function(enteredPassword) {
return await bcrypt.compare(enteredPassword, this.password);
};
const express = require('express');
const {register,login} = require('../controllers/auth');
const router = express.Router();router.post('/register',register).post('/login',login);module.exports = router;
{"email":"j@gmail.com","password":"123456"}

SENDING JWT IN A COOKIE

npm i cookie-parser
const ErrorResponse = require('../utils/errorResponse');
const User = require('../models/User');
const asyncHandler = require('../middleware/async')
exports.register = asyncHandler(async(req,res,next) => {
const { name, email, password, role } = req.body;
// Create user
const user = await User.create({
name,
email,
password,
role
});
sendTokenResponse(user, 200, res);
});
exports.login = asyncHandler(async (req, res, next) => {
const { email, password } = req.body;

// Validate emil & password
if (!email || !password) {
return next(new ErrorResponse('Please provide an email and password', 400));
}

// Check for user
const user = await User.findOne({ email }).select('+password');

if (!user) {
return next(new ErrorResponse('Invalid credentials', 401));
}

// Check if password matches
const isMatch = await user.matchPassword(password);

if (!isMatch) {
return next(new ErrorResponse('Invalid credentials', 401));
}

sendTokenResponse(user, 200, res);
});

// Get token from model, create cookie and send response
const sendTokenResponse = (user, statusCode, res) => {
// Create token
const token = user.getSignedJwtToken();

const options = {
expires: new Date(
Date.now() + process.env.JWT_COOKIE_EXPIRE * 24 * 60 * 60 * 1000
),
httpOnly: true
};

if (process.env.NODE_ENV === 'production') {
options.secure = true;
}

res
.status(statusCode)
.cookie('token', token, options)
.json({
success: true,
token
});
};
const cookieParser = require('cookie-parser');
//cookie parser
app.use(cookieParser());

AUTH PROTECT MIDDLEWARE

const jwt = require('jsonwebtoken');
const asyncHandler = require('./async');
const ErrorResponse = require('../utils/errorResponse');
const User = require('../models/User');
// Protect routes
exports.protect = asyncHandler(async (req, res, next) => {
let token;
if (
req.headers.authorization &&
req.headers.authorization.startsWith('Bearer')
) {
// Set token from Bearer token in header
token = req.headers.authorization.split(' ')[1];
// Set token from cookie
}
// else if (req.cookies.token) {
// token = req.cookies.token;
// }
// Make sure token exists
if (!token) {
return next(new ErrorResponse('Not authorized to access this route', 401));
}
try {
// Verify token
const decoded = jwt.verify(token, process.env.JWT_SECRET);
console.log(decoded);
req.user = await User.findById(decoded.id);
next();
} catch (err) {
return next(new ErrorResponse('Not authorized to access this route', 401));
}
});
exports.getMe = asyncHandler(async (req, res, next) => {
const user = await User.findById(req.user.id);

res.status(200).json({
success: true,
data: user
});
});
const express = require('express');
const {register,login,getMe} = require('../controllers/auth');
const router = express.Router();const { protect } = require('../middleware/auth');router.post('/register',register).post('/login',login).get('/me',protect,getMe);module.exports = router;
const express = require('express');
const {
getBootcamps,
getBootcamp,
createBootcamp,
updateBootcamp,
deleteBootcamp,
getBootcampsInRadius,
bootcampPhotoUpload,
} = require('../controllers/bootcamps');
const Bootcamp = require('../models/Bootcamp');
const advancedResults = require('../middleware/advancedResults');
//include other resourse router
const courseRouter = require('./courses');
const router = express.Router();const { protect } = require('../middleware/auth');router.use('/:bootcampId/courses', courseRouter);router.route('/radius/:zipcode/:distance').get(getBootcampsInRadius);router
.route('/')
.get(advancedResults(Bootcamp, 'courses'), getBootcamps)
.post(protect, createBootcamp);
router.route('/:id/photo').put(protect, bootcampPhotoUpload);router
.route('/:id')
.get(getBootcamp)
.put(protect, updateBootcamp)
.delete(protect, deleteBootcamp);
module.exports = router;
const express = require('express');
const {
getCourses,
getCourse,
addCourse,
updateCourse,
deleteCourse,
} = require('../controllers/courses');
const Course = require('../models/Course');
const advancedResults = require('../middleware/advancedResults');
const router = express.Router({ mergeParams: true });const { protect } = require('../middleware/auth');router
.route('/')
.get(
advancedResults(Course, {
path: 'bootcamp',
select: 'name description',
}),
getCourses
)
.post(protect, addCourse);
router
.route('/:id')
.get(getCourse)
.put(protect, updateCourse)
.delete(protect, deleteCourse);
module.exports = router;
key:Authorization
Value: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVmN2ExMWUxODU3YTU1OGMwMjJlYTNmYiIsImlhd

ROLE AUTHORIZATION

// Grant access to specific roles
exports.authorize = (...roles) => {
return (req, res, next) => {
if (!roles.includes(req.user.role)) {
return next(
new ErrorResponse(
`User role ${req.user.role} is not authorized to access this route`,
403
)
);
}
next();
};
};
const express = require('express');
const {
getBootcamps,
getBootcamp,
createBootcamp,
updateBootcamp,
deleteBootcamp,
getBootcampsInRadius,
bootcampPhotoUpload,
} = require('../controllers/bootcamps');
const Bootcamp = require('../models/Bootcamp');
const advancedResults = require('../middleware/advancedResults');
//include other resourse router
const courseRouter = require('./courses');
const router = express.Router();const { protect,authorize } = require('../middleware/auth');router.use('/:bootcampId/courses', courseRouter);router.route('/radius/:zipcode/:distance').get(getBootcampsInRadius);router
.route('/')
.get(advancedResults(Bootcamp, 'courses'), getBootcamps)
.post(protect,authorize('publisher','admin'), createBootcamp);
router.route('/:id/photo').put(protect, authorize('publisher','admin'),bootcampPhotoUpload);router
.route('/:id')
.get(getBootcamp)
.put(protect, authorize('publisher','admin'),updateBootcamp)
.delete(protect, authorize('publisher','admin'),deleteBootcamp);
module.exports = router;
const express = require('express');
const {
getCourses,
getCourse,
addCourse,
updateCourse,
deleteCourse,
} = require('../controllers/courses');
const Course = require('../models/Course');
const advancedResults = require('../middleware/advancedResults');
const router = express.Router({ mergeParams: true });const { protect,authorize } = require('../middleware/auth');router
.route('/')
.get(
advancedResults(Course, {
path: 'bootcamp',
select: 'name description',
}),
getCourses
)
.post(protect,authorize('publisher','admin'), addCourse);
router
.route('/:id')
.get(getCourse)
.put(protect, authorize('publisher','admin'),updateCourse)
.delete(protect,authorize('publisher','admin'), deleteCourse);
module.exports = router;

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store