ref – https://scotch.io/tutorials/authenticate-a-node-js-api-with-json-web-tokens
In part 1, we set up a simple project where we let a client create a user through a public URL /setup, then display all the users via /api/users.
Next, let’s make sure that we can authenticate a user and then protect those routes using Express route middleware and requiring a token.
Authenticating and Creating a Token
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
// route to authenticate a user (POST http://localhost:8080/api/authenticate) apiRoutes.post('/authenticate', function(req, res) { console.log('(POST http://localhost:8080/api/authenticate)'); //User is model object made by mongoose // mongoose's findOne function to find the user User.findOne({ name: req.body.name }, function(err, user) { if (err) throw err; if (!user) { res.json({ success: false, message: 'Authentication failed. User not found.' }); } else if (user) { //user with that name is returned...we just have to check if the pwds match // check if password matches if (user.password != req.body.password) { res.json({ success: false, message: 'Authentication failed. Wrong password.' }); } else { console.log('user found, and password is correct'); // if user is found and password is right // create a token, jsonwebtoken to create the token. var token = jwt.sign(user, app.get('superSecret'), { expiresInMinutes: 1440 // expires in 24 hours }); // return the information including token as JSON res.json({ success: true, message: 'Enjoy your token!', token: token }); } } }); }); |
We then use Postman to query it like so
why – form-urlencoded vs form-data
To summarize, form-urlencoded has a low fixed cost, but high variable for special characters, so application/x-www-form-urlencoded is good for short messages like those found in most basic web forms. On the other hand, form-data has a high fixed costs due to the additional MIME headers prepended to each message chunk, but low variable cost for special characters, so multipart/form-data is good for long messages such images or large files.
Now, copy that whole token string somewhere to be used later.
Protecting URLS
Now let’s protect /api and /api/users by forcing clients to provide the token whenever they need to access these urls.
So for our var apiRoutes that we get from express.Routes(), we have that routing variable use a function to process requests.
We make it run a middleware function by using .use on it. We basically check the requests’ headers to see if the key x-access-token has a valid value. That value should be the token value we received earlier. If the token is valid, everything checks through via the next(), and we run to the apiRoutes.get(‘/users’, function(req, res) {}); function definitions which gets the user from the database and gives it back as a json response.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
// route middleware to verify a token apiRoutes.use(function(req, res, next) { console.log('apiRoutes.use function'); //X-ACCESS-TOKEN is the key, we need to give it value of token //so it can authenticate us console.log("req.body.token: " + req.body.token); console.log("req.query.token: " + req.query.token); console.log("req.headers[x-access-token]: " + req.headers['x-access-token']); // check header or url parameters or post parameters for token var token = req.body.token || req.query.token || req.headers['x-access-token']; // decode token if (token) { // verifies secret and checks exp jwt.verify(token, app.get('superSecret'), function(err, decoded) { if (err) { return res.json({ success: false, message: 'Failed to authenticate token.' }); } else { // if everything is good, save to request for use in other routes req.decoded = decoded; next(); } }); } else { // if there is no token // return an error return res.status(403).send({ success: false, message: 'No token provided.' }); } }); |
For example, if you were to hit http://localhost:8080/api/ or http://localhost:8080/api/ it will run this function definition first….see that x-access-token has not been defined in the headers and return you an error json message.
When the tokens are valid, you’ll see the valid json data returned. If you are to use POSTman, here’s what it should look like: