JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is used as the payload of a JSON Web Signature (JWS) structure or as the plaintext of a JSON Web Encryption (JWE) structure, enabling the claims to be digitally signed or integrity protected with a Message Authentication Code (MAC) and/or encrypted.
A JWT is an encoded string of characters which is safe to send between two computers if they both have HTTPS. The token represents a value that is accessible only by the computer that has access to the secret key with which it was encrypted. Simple enough, right?
RESTful API design with Node.js
The demo is on GitHub:
git clone https://github.com/CristinaVolk/Node-JS-ResfulAPI-init
Let’s take a look at the folder structure:
> database.js
- authQueries.js
- app.js
- server.js
- package.json
Important: Have in mind, under no circumstances should you ever, (EVER!) have your secret key publicly visible like this. Always put all of your keys in environment variables! I’m only writing it like this for demo purposes.
// config.js
module.exports = {
secret: 'supersecret',
};
// AuthController.js
const express = require('express');
const router = express.Router();
const bodyParser = require('body-parser');
const db = require('../database.js/authQueries');
router.use(bodyParser.urlencoded({ extended: false }));
router.use(bodyParser.json());
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const { v4: uuidv4 } = require('uuid');
const config = require('../config');
npm install jsonwebtoken --save
npm install bcryptjs --save
npm install uuid --save
router.post('/user', async (req, res) => {
try {
const hashedPassword = bcrypt.hashSync(req.body.password, 8);
const userCreated = await db.addUser({
id: uuidv4(),
name: req.body.name,
email: req.body.email,
password: hashedPassword,
});
const token = jwt.sign({ id: userCreated.id }, config.secret, {
expiresIn: 86400, // expires in 24 hours
});
if (userCreated) res.status(200).send({ auth: true, token });
} catch (error) {
console.error(error);
res.sendStatus(500);
}
});
// add this to the bottom of AuthController.js
module.exports = router;
// app.js
const AuthController = require(__root + 'auth/AuthController');
app.use('/api/auth', AuthController);
module.exports = app;
// add this to the bottom of AuthController.js
router.get('/me', (req, res) => {
var token = req.headers['x-access-token'];
if (!token) return res.status(401).send({ auth: false, message: 'No token provided.' });
jwt.verify(token, config.secret, async (req, decoded) => {
const user = await db.findUserById(decoded.id);
if (!user) return res.status(404).send('No user found.');
res.status(200).send(decoded);
});
});
router.post('/login', async function (req, res) {
const user = await db.findUserByEmail(req.body.email);
if (!user) return res.status(404).send('No user found.');
const passwordIsValid = bcrypt.compareSync(req.body.password, user.password);
if (!passwordIsValid) return res.status(401).send({ auth: false, token: null });
const token = jwt.sign({ id: user.id }, config.secret, {
expiresIn: 86400, // expires in 24 hours
});
res.status(200).send({ auth: true, token: token });
});
// AuthController.js
router.get('/logout', function (req, res) {
res.status(200).send({ auth: false, token: null });
});
Middleware is a piece of code, a function in Node.js, that acts as a bridge between some parts of your code.
Middleware functions are functions that have access to the request object (req), the response object (res), and the next function in the application’s request-response cycle.
router.get('/me', (req, res, next) => {
var token = req.headers['x-access-token'];
if (!token) return res.status(401).send({ auth: false, message: 'No token provided.' });
jwt.verify(token, config.secret, async (req, decoded) => {
const user = await db.findUserById(decoded.id);
if (!user) return res.status(404).send('No user found.');
next(user);
});
});
// add the middleware function
router.use(function (user, req, res, next) {
res.status(200).send(user);
});
Let’s take this same logic and apply it to create a middleware function to check the validity of tokens.
Important: Go ahead and delete this sample before we continue as it is only used for demonstrating the logic of using next().
const jwt = require('jsonwebtoken');
const config = require('../config');
function verifyToken(req, res, next) {
const token = req.headers['x-access-token'];
if (!token) return res.status(403).send({ auth: false, message: 'No token provided.' });
jwt.verify(token, config.secret, function (err, decoded) {
if (err) return res.status(500).send({ auth: false, message: 'Failed to authenticate token.' });
// if everything good, save to request for use in other routes
req.userId = decoded.id;
next();
});
}
module.exports = verifyToken;
// AuthController.js
const VerifyToken = require('./VerifyToken');
// ...
router.get('/me', VerifyToken, async (req, res, next) => {
const user = await db.findUserById(req.userId);
if (!user) return res.status(404).send('No user found.');
res.status(200).send(user);
});
Wrapping your head around everything.
Thank y’all for the attention!
be curious and have fun!