Setup the REST Server
Create a new folder named myRESTApp. Inside that folder, create a folder named server. We will first create a new node project. Open a new terminal/prompt from inside the server folder and run
npm init
Fill it up as applicable. Now, we will add a few dependencies. Run
npm install --save express body-parser morgan jwt-simple
We are going to use Express as our server side framework, jwt-simple module to generate JSON Web Tokens. These tokens will be used as access tokens between the server and client.Update server.js as below
var express = require('express');
var path = require('path');
var logger = require('morgan');
var bodyParser = require('body-parser');
var app = express();
app.use(logger('dev'));
app.use(bodyParser.json());
app.all('/*', function(req, res, next) {
// CORS headers
res.header("Access-Control-Allow-Origin", "*"); // restrict it to the required domain
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
// Set custom headers for CORS
res.header('Access-Control-Allow-Headers', 'Content-type,Accept,X-Access-Token,X-Key');
if (req.method == 'OPTIONS') {
res.status(200).end();
} else {
next();
}
});
// Auth Middleware - This will check if the token is valid
// Only the requests that start with /api/v1/* will be checked for the token.
// Any URL's that do not follow the below pattern should be avoided unless you
// are sure that authentication is not needed
app.all('/api/v1/*', [require('./middlewares/validateRequest')]);
app.use('/', require('./routes'));
// If no route is matched by now, it must be a 404
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// Start the server
app.set('port', process.env.PORT || 3000);
var server = app.listen(app.get('port'), function() {
console.log('Express server listening on port ' + server.address().port);
});
https://www.blogger.com/blogger.g?blogID=3698077727790650810#editor/target=post;postID=6251057592472405429
Create a new folder named routes inside the server folder. Inside the routes folder create a new file named index.js. This file will hold all the routes needed for our app.
Updated index.js as below
http://thejackalofjavascript.com/architecting-a-restful-node-js-app/
Updated index.js as below
var express = require('express');
var router = express.Router();
var auth = require('./auth.js');
var products = require('./products.js');
var user = require('./users.js');
/*
* Routes that can be accessed by any one
*/
router.post('/login', auth.login);
/*
* Routes that can be accessed only by autheticated users
*/
router.get('/api/v1/products', products.getAll);
router.get('/api/v1/product/:id', products.getOne);
router.post('/api/v1/product/', products.create);
router.put('/api/v1/product/:id', products.update);
router.delete('/api/v1/product/:id', products.delete);
/*
* Routes that can be accessed only by authenticated & authorized users
*/
router.get('/api/v1/admin/users', user.getAll);
router.get('/api/v1/admin/user/:id', user.getOne);
router.post('/api/v1/admin/user/', user.create);
router.put('/api/v1/admin/user/:id', user.update);
router.delete('/api/v1/admin/user/:id', user.delete);
module.exports = router;
Update auth.js as below
server/routes/auth.js
var jwt = require('jwt-simple');
var auth = {
login: function(req, res) {
var username = req.body.username || '';
var password = req.body.password || '';
if (username == '' || password == '') {
res.status(401);
res.json({
"status": 401,
"message": "Invalid credentials"
});
return;
}
// Fire a query to your DB and check if the credentials are valid
var dbUserObj = auth.validate(username, password);
if (!dbUserObj) { // If authentication fails, we send a 401 back
res.status(401);
res.json({
"status": 401,
"message": "Invalid credentials"
});
return;
}
if (dbUserObj) {
// If authentication is success, we will generate a token
// and dispatch it to the client
res.json(genToken(dbUserObj));
}
},
validate: function(username, password) {
// spoofing the DB response for simplicity
var dbUserObj = { // spoofing a userobject from the DB.
name: 'arvind',
role: 'admin',
username: 'arvind@myapp.com'
};
return dbUserObj;
},
validateUser: function(username) {
// spoofing the DB response for simplicity
var dbUserObj = { // spoofing a userobject from the DB.
name: 'arvind',
role: 'admin',
username: 'arvind@myapp.com'
};
return dbUserObj;
},
}
// private method
function genToken(user) {
var expires = expiresIn(7); // 7 days
var token = jwt.encode({
exp: expires
}, require('../config/secret')());
return {
token: token,
expires: expires,
user: user
};
}
function expiresIn(numDays) {
var dateObj = new Date();
return dateObj.setDate(dateObj.getDate() + numDays);
}
module.exports = auth;
Now, we will create the secret. Create a new folder named config inside the server folder. Inside the config folder create a new file named secret.js and update it as
server/config/secret.js
module.exports = function() {
return 'super.super.secret.shhh';
}
Now, we will create the dummy API for products and users. Create a file named products.js inside the routes folder and update it as below
routes/products.js
var products = {
getAll: function(req, res) {
var allProducts = data; // Spoof a DB call
res.json(allProducts);
},
getOne: function(req, res) {
var id = req.params.id;
var product = data[0]; // Spoof a DB call
res.json(product);
},
create: function(req, res) {
var newProduct = req.body;
data.push(newProduct); // Spoof a DB call
res.json(newProduct);
},
update: function(req, res) {
var updateProduct = req.body;
var id = req.params.id;
data[id] = updateProduct // Spoof a DB call
res.json(updateProduct);
},
delete: function(req, res) {
var id = req.params.id;
data.splice(id, 1) // Spoof a DB call
res.json(true);
}
};
var data = [{
name: 'product 1',
id: '1'
}, {
name: 'product 2',
id: '2'
}, {
name: 'product 3',
id: '3'
}];
module.exports = products;
And create another file named users.js and update it as below
server/routes/users.js
var users = {
getAll: function(req, res) {
var allusers = data; // Spoof a DB call
res.json(allusers);
},
getOne: function(req, res) {
var id = req.params.id;
var user = data[0]; // Spoof a DB call
res.json(user);
},
create: function(req, res) {
var newuser = req.body;
data.push(newuser); // Spoof a DB call
res.json(newuser);
},
update: function(req, res) {
var updateuser = req.body;
var id = req.params.id;
data[id] = updateuser // Spoof a DB call
res.json(updateuser);
},
delete: function(req, res) {
var id = req.params.id;
data.splice(id, 1) // Spoof a DB call
res.json(true);
}
};
var data = [{
name: 'user 1',
id: '1'
}, {
name: 'user 2',
id: '2'
}, {
name: 'user 3',
id: '3'
}];
module.exports = users;
server/middlewares/validateRequest.js
var jwt = require('jwt-simple');
var validateUser = require('../routes/auth').validateUser;
module.exports = function(req, res, next) {
// When performing a cross domain request, you will recieve
// a preflighted request first. This is to check if our the app
// is safe.
// We skip the token outh for [OPTIONS] requests.
//if(req.method == 'OPTIONS') next();
var token = (req.body && req.body.access_token) || (req.query && req.query.access_token) || req.headers['x-access-token'];
var key = (req.body && req.body.x_key) || (req.query && req.query.x_key) || req.headers['x-key'];
if (token || key) {
try {
var decoded = jwt.decode(token, require('../config/secret.js')());
if (decoded.exp <= Date.now()) {
res.status(400);
res.json({
"status": 400,
"message": "Token Expired"
});
return;
}
// Authorize the user to see if s/he can access our resources
var dbUser = validateUser(key); // The key would be the logged in user's username
if (dbUser) {
if ((req.url.indexOf('admin') >= 0 && dbUser.role == 'admin') || (req.url.indexOf('admin') < 0 && req.url.indexOf('/api/v1/') >= 0)) {
next(); // To move to next middleware
} else {
res.status(403);
res.json({
"status": 403,
"message": "Not Authorized"
});
return;
}
} else {
// No user with this name exists, respond back with a 401
res.status(401);
res.json({
"status": 401,
"message": "Invalid User"
});
return;
}
} catch (err) {
res.status(500);
res.json({
"status": 500,
"message": "Oops something went wrong",
"error": err
});
}
} else {
res.status(401);
res.json({
"status": 401,
"message": "Invalid Token or Key"
});
return;
}
};
Start our server by running node server.js or nodemon server.js
curl http://localhost:3000/api/v1/products
curl –data “{\”username\” : \”arvind@myapp.com\”, \”password\” : \”pass123\”}” -H “content-type:application/json” http://localhost:3000/login
URL : curl -H “content-type:application/json” -H “x-access-token:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE0MTE5MjcyMDAzNjl9.cuUKFKsf2qhQJHToP-zBmObhMwi84rhnrhH03OdyzSA” -H “x-key:arvind@myapp.com” http://localhost:3000/api/v1/products
https://scotch.io/tutorials/authenticate-a-node-js-api-with-json-web-tokens
https://github.com/scotch-io/node-token-authentication
0 Comments