Authentication in Node.js
Authentication plays an important role in the security of the application. If you don’t have the right authentication and authorization when you share your resources via routes to your users your application’s security is compromised. You can give your users access based on their rights and user type. The usernames and encrypted passwords are stored in a password file on the server side. This password and username are asked when a user tries to access the resource and if its valid user is taken to the page he/she requested. Let’s create a folder nodejs-authentication and set up the app template.
mkdir nodejs-authentication
cd nodejs-authentication
npm init --yes
For retrieving jwtPrivatekey we use Config, joi is used for validation, express for the web framework, mongoose to create models and schema, jsonwebtoken to generate and verify JWt and bcrypt for hashing password. Install all these dependencies.
npm i config joi express mongoose jsonwebtoken bcrypt
Create the files and folders needed for the application. Add the following lines of code in
default.json.
{
"myprivatekey": "myprivatekey"
}
Set an environmental variable myprivatekey to use in our application.
const config = require('config');
const jwt = require('jsonwebtoken');
const Joi = require('joi');
const mongoose = require('mongoose');
//simple schema
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true,
minlength: 3,
maxlength: 50
},
email: {
type: String,
required: true,
minlength: 5,
maxlength: 255,
unique: true
},
password: {
type: String,
required: true,
minlength: 3,
maxlength: 255
},
//give different access rights if admin or not
isAdmin: Boolean
});
//custom method to generate authToken
UserSchema.methods.generateAuthToken = function() {
const token = jwt.sign({ _id: this._id, isAdmin: this.isAdmin }, config.get('myprivatekey')); //get the private key from the config file -> environment variable
return token;
}
const User = mongoose.model('User', UserSchema);
//function to validate user
function validateUser(user) {
const schema = {
name: Joi.string().min(3).max(50).required(),
email: Joi.string().min(5).max(255).required().email(),
password: Joi.string().min(3).max(255).required()
};
return Joi.validate(user, schema);
}
exports.User = User;
exports.validate = validateUser;
Our schema has a name, email, and password. To generate an authentication token we have added a method. We have exported validateUser. It will be used to check if all required fields are filled.
const jwt = require("jsonwebtoken");
const config = require("config");
module.exports = function(req, res, next) {
//get the token from the header if present
const token = req.headers["x-access-token"] || req.headers["authorization"];
//if no token found, return response (without going to the next middleware)
if (!token) return res.status(401).send("Access denied. No token provided.");
try {
//if can verify the token, set req.user and pass to next middleware
const decoded = jwt.verify(token, config.get("myprivatekey"));
req.user = decoded;
next();
} catch (ex) {
//if invalid token
res.status(400).send("Invalid token.");
}
};
This is the custom middleware we will use to check if there is valid JWT in request headers.
In our application we have two routes defined. One to get the current user, second one to register the user. To hash the password we will use bcrypt.
Index.js
const config = require("config");
const mongoose = require("mongoose");
const usersRoute = require("./routes/user.route");
const express = require("express");
const app = express();
//use config module to get the privatekey, if no private key set, end the application
if (!config.get("myprivatekey")) {
console.error("FATAL ERROR: myprivatekey is not defined.");
process.exit(1);
}
//connect to mongodb
mongoose
.connect("mongodb://localhost/nodejsauth", { useNewUrlParser: true })
.then(() => console.log("Connected to MongoDB..."))
.catch(err => console.error("Could not connect to MongoDB..."));
app.use(express.json());
//use users route for api/users
app.use("/api/users", usersRoute);
const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Listening on port ${port}...`));
Run the index.js file. We will get an error first that is because we haven’t set the environment variable to sign our tokens.
$env:myprivatekey = "myprivatekey"
node index.js
Now you can run the app. It will print the following.
Listening on port 3000...
Connected to MongoDB…
Comments
0 comments
Please Sign in or Create an account to Post Comments