How to do it...

Let's look at how we can add authentication. To work with JWT, we'll be using jsonwebtoken from https://github.com/auth0/node-jsonwebtoken. Install it with the help of the following command:

npm install jsonwebtoken --save

Our code example for JWT will be larger than in previous examples, and it should be separated into many files. However, I avoided doing this in order to make it clearer. First, we'll need to make some declarations, and the key lines are in bold:

// Source file: src/jwt_server.js

/* @flow */
"use strict";

const express = require("express");
const app = express();
const jwt = require("jsonwebtoken");
const bodyParser = require("body-parser");

const validateUser = require("./validate_user.js");

const SECRET_JWT_KEY = "modernJSbook";

app.use(bodyParser.urlencoded({ extended: false }));

Almost everything is standard, except for the validateUser() function and the SECRET_JWT_KEY string. The latter will be used to sign the tokens, and most definitely shouldn't be in the code itself! (If somebody could hack their way into the source code, your secret would be out; rather, set the key in an environment variable, and get the value from there.)

As for the function, checking if a user exists and if their password is correct is simple to do, and can be achieved in many ways, such as by accessing a database, active directory, service, and so on. Here, we'll just make do with a hardcoded version, which accepts only a single user. The validate_user.js source code is, then, quite simple:

// Source file: src/validate_user.js

/* @flow */
"use strict";

/*
In real life, validateUser could check a database,
look into an Active Directory, call another service,
etc. -- but for this demo, let's keep it quite
simple and only accept a single, hardcoded user.
*/

const validateUser = (
userName: string,
password: string,
callback: (?string, ?string) => void) => {
if (!userName || !password) {
callback("Missing user/password", null);
} else if (userName === "fkereki" && password === "modernjsbook") {
callback(null, "fkereki"); // OK, send userName back
} else {
callback("Not valid user", null);
}
};

module.exports = validateUser;

Let's get back to our server. After the initial definitions, we can place the routes that need no tokens. Let's have a /public route, and also a /gettoken route to get a JWT for later. In the latter, we'll see whether the POST included user and password values in its body, and if they are a valid user by means of the validateUser() function we showed in the preceding code. Any problems will mean a 401 status will be sent, while if the user is correct, a token will be created, expiring in one hour's time:

// Source file: src/jwt_server.js

app.get("/public", (req, res) => {
res.send("the /public endpoint needs no token!");
});

app.post("/gettoken", (req, res) => {
validateUser(req.body.user, req.body.password, (idErr, userid) => {
if (idErr !== null) {
res.status(401).send(idErr);
} else {
jwt.sign(
{ userid },
SECRET_JWT_KEY,
{ algorithm: "HS256", expiresIn: "1h" },
(err, token) => res.status(200).send(token)
);
}
});
});

Now that the unprotected routes are out of the way, let's add some middleware to verify that a token is present. We expect, according to the JWT RFC, to have an Authorization: Bearer somejwttoken header included, and it must be accepted. If no such header is present, or if it's not in the right format, a 401 status will be sent. If the token is present, but it's expired or has any other problem, a 403 status will be sent. Finally, if there's nothing wrong, the userid field will be extracted from the payload, and attached to the request object so that future code will be able to use it:

// Source file: src/jwt_server.js

app.use((req, res, next) => {
// First check for the Authorization header
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith("Bearer ")) {
return res.status(401).send("No token specified");
}

// Now validate the token itself
const token = authHeader.split(" ")[1];
jwt.verify(token, SECRET_JWT_KEY, (err, decoded) => {
if (err) {
// Token bad formed, or expired, or other problem
return res.status(403).send("Token expired or not valid");
} else {
// Token OK; get the user id from it
req.userid = decoded.userid;
// Keep processing the request
next();
}
});
});

Now, let's have some protected routes (in fact, a single one, /private, just for this example), followed by error checking and setting up the whole server:

// Source file: src/jwt_server.js

app.get("/private", (req, res) => {

res.send("the /private endpoint needs JWT, but it was provided: OK!");
});

// eslint-disable-next-line no-unused-vars
app.use((err, req, res, next) => {
console.error("Error....", err.message);
res.status(500).send("INTERNAL SERVER ERROR");
});

app.listen(8080, () =>
console.log("Mini JWT server ready, at http://localhost:8080/!")
);

We're done! Let's see how this all comes together.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.131.168