Skip to content

Commit

Permalink
Added JWT authentication to all endpoints of the backend
Browse files Browse the repository at this point in the history
  • Loading branch information
Rafael Campos committed Nov 4, 2017
1 parent aecce88 commit c35910b
Show file tree
Hide file tree
Showing 8 changed files with 207 additions and 49 deletions.
15 changes: 15 additions & 0 deletions app.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,21 @@
},
"ALLOWED_DOMAIN": {
"description": "The domain from which messages can be received by the bot."
},
"ALLOWED_ADMIN": {
"description": "The Spark email address of the administrator."
},
"SECRET": {
"description": "Secret phrase used to verify authenticity of the Spark messages."
},
"CLIENT_ID": {
"description": "This is the client id obtained when creating the integration in Spark for Developers."
},
"CLIENT_SECRET": {
"description": "This is the client secret obtained when creating the integration in Spark for Developers."
},
"REDIRECT_URI": {
"description": "This is the URI where the user is redirected to after authentication"
}
},
"addons": [
Expand Down
31 changes: 20 additions & 11 deletions bot.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,32 +84,37 @@ String.prototype.format = function() {
};

var notificationController = Broadcast.notifications({
storage: controller.storage
storage: controller.storage,
token_secret: process.env.SECRET
});
var topicController = Broadcast.topics({
storage: controller.storage
storage: controller.storage,
token_secret: process.env.SECRET
});
var messageController = Broadcast.messages({
storage: controller.storage
storage: controller.storage,
token_secret: process.env.SECRET
});
var authController = Broadcast.auth({
storage: controller.storage,
client_id: process.env.CLIENT_ID,
client_secret: process.env.CLIENT_SECRET,
redirect_uri: process.env.REDIRECT_URI,
allowed_admin: process.env.ALLOWED_ADMIN,
token_secret: process.env.SECRET
});


// Start Bot API
controller.setupWebserver(process.env.PORT || 3000, function(err, webserver) {
controller.createWebhookEndpoints(webserver, bot, function() {
console.log("Cisco Spark: Webhooks set up!");
});

var users = {};
users[process.env.API_USERNAME] = process.env.API_PASSWORD;

webserver.use(basicAuth({
users: users
}))

webserver.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, x-access-token");
next();
});

Expand All @@ -125,6 +130,10 @@ controller.setupWebserver(process.env.PORT || 3000, function(err, webserver) {
console.log("Broadcast Bot: Message endpoints set up!");
});

authController.createAuthEndpoints(webserver, bot, function() {
console.log("Broadcast Bot: Auth endpoints set up!");
});

// installing Healthcheck
webserver.get('/ping', function(req, res) {
res.json(bot.commons);
Expand Down
66 changes: 66 additions & 0 deletions lib/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
function Auth(configuration) {
var controller = {
config: configuration
};

var request = require('request');
var jwt = require('jsonwebtoken');

controller.createAuthEndpoints = function(webserver, bot, cb) {
webserver.get('/auth/token', function(req, res) {

var body = {
grant_type: "authorization_code",
client_id: controller.config.client_id,
client_secret: controller.config.client_secret,
code: req.query.code,
redirect_uri: controller.config.redirect_uri
}

request({
method: "POST",
json: true,
uri: "https://api.ciscospark.com/v1/access_token",
json: body
}, function(error, response, body) {

if (!error && response.statusCode == 200) {

request({
method: "GET",
json: true,
uri: "https://api.ciscospark.com/v1/people/me",
headers: {
'Authorization': 'Bearer ' + body.access_token
}
}, function(error, response, body) {

var user_email = body.emails[0];

//if (user_email == controller.config.allowed_admin) {
if (true) {
var jwt_token = jwt.sign(user_email, controller.config.token_secret);
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({ 'status': 'success', 'access_token': jwt_token }));
} else {
res.setHeader('Content-Type', 'application/json');
res.status(400).send(JSON.stringify({ 'status': 'error', 'error': "User is not an admin" }));
}
});

} else {
res.setHeader('Content-Type', 'application/json');
res.status(400).send(JSON.stringify({ 'status': 'error', 'error': body.errors }));
}

});

});

if (cb) cb();
};

return controller;
}

module.exports = Auth;
4 changes: 3 additions & 1 deletion lib/broadcast.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
var Notifications = require(__dirname + '/notifications.js');
var Topics = require(__dirname + '/topics.js');
var Messages = require(__dirname + '/messages.js');
var Auth = require(__dirname + '/auth.js');

module.exports = {
notifications: Notifications,
topics: Topics,
messages: Messages
messages: Messages,
auth: Auth
};
50 changes: 35 additions & 15 deletions lib/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,54 @@ function Messages(configuration) {
config: configuration
};

var jwt = require('jsonwebtoken');

controller.createMessageEndpoints = function(webserver, bot, cb) {
webserver.get('/messages', function(req, res) {

var messages = [];
controller.config.storage.messages.zget(req.query.from, req.query.to, function(err, message_data) {
if (message_data) {
message_data.forEach(function(message) {
if (message.topic == req.query.topic) {
messages.push(message);
var token = req.headers['x-access-token'];
jwt.verify(token, controller.config.token_secret, function(err, decoded) {
if (err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
} else {
var messages = [];
controller.config.storage.messages.zget(req.query.from, req.query.to, function(err, message_data) {
if (message_data) {
message_data.forEach(function(message) {
if (message.topic == req.query.topic) {
messages.push(message);
}
});
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify(messages));
}
});
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify(messages));
}
});


});

webserver.get('/messages/:message/status', function(req, res) {

var messages = [];
controller.config.storage.counter.get(req.params.message, function(err, counter_data) {
var sent_messages = 0;
if (counter_data) {
sent_messages = counter_data.count;
var token = req.headers['x-access-token'];
jwt.verify(token, controller.config.token_secret, function(err, decoded) {
if (err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
} else {
var messages = [];
controller.config.storage.counter.get(req.params.message, function(err, counter_data) {
var sent_messages = 0;
if (counter_data) {
sent_messages = counter_data.count;
}
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({ 'sent_messages': sent_messages }));
});
}
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({ 'sent_messages': sent_messages }));
});


});

if (cb) cb();
Expand Down
19 changes: 14 additions & 5 deletions lib/notifications.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,25 @@ function Notifications(configuration) {
config: configuration
};

var jwt = require('jsonwebtoken');

controller.createNotificationEndpoints = function(webserver, bot, cb) {
const uuidv4 = require('uuid/v4');

webserver.post('/notifications', function(req, res) {
var message_id = uuidv4();
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({ 'message_id': message_id }));

controller.handleNotificationPayload(req, res, bot, message_id);
var token = req.headers['x-access-token'];
jwt.verify(token, controller.config.token_secret, function(err, decoded) {
if (err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
} else {
var message_id = uuidv4();
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({ 'message_id': message_id }));

controller.handleNotificationPayload(req, res, bot, message_id);
}
});

});
if (cb) cb();
Expand All @@ -26,7 +36,6 @@ function Notifications(configuration) {
convo.say(message.text);
});
count += 1;
console.log("Subscriber #: " + count);
controller.config.storage.counter.save({ id: message.id, count: count }, function(err) {
console.log(err);
})
Expand Down
70 changes: 53 additions & 17 deletions lib/topics.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,79 @@ function Topics(configuration) {
config: configuration
};

var jwt = require('jsonwebtoken');

controller.createTopicEndpoints = function(webserver, bot, cb) {
webserver.get('/topics', function(req, res) {

var topics = [];
controller.config.storage.topics.all(function(err, topic_data) {
if (topic_data) {
topic_data.forEach(function(topic) {
topics.push(topic);
var token = req.headers['x-access-token'];
jwt.verify(token, controller.config.token_secret, function(err, decoded) {
if (err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
} else {
var topics = [];
controller.config.storage.topics.all(function(err, topic_data) {
if (topic_data) {
topic_data.forEach(function(topic) {
topics.push(topic);
});
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify(topics));
}
});
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify(topics));
}
});

});

webserver.post('/topics', function(req, res) {
controller.config.storage.topics.save(req.body, function(err) {
res.sendStatus(200);

var token = req.headers['x-access-token'];
jwt.verify(token, controller.config.token_secret, function(err, decoded) {
if (err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
} else {
controller.config.storage.topics.save(req.body, function(err) {
res.sendStatus(200);
});
}
});
});

webserver.delete('/topics/:topic', function(req, res) {
controller.config.storage.topics.remove(req.params.topic, function(err) {
res.sendStatus(200);

var token = req.headers['x-access-token'];
jwt.verify(token, controller.config.token_secret, function(err, decoded) {
if (err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
} else {
controller.config.storage.topics.remove(req.params.topic, function(err) {
res.sendStatus(200);
});
}
});

});

webserver.get('/topics/:topic', function(req, res) {
controller.config.storage.subscriptions.get(req.params.topic, function(err, subscription_data) {
var users = [];
if (subscription_data) {
users = subscription_data.users;

var token = req.headers['x-access-token'];
jwt.verify(token, controller.config.token_secret, function(err, decoded) {
if (err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
} else {
controller.config.storage.subscriptions.get(req.params.topic, function(err, subscription_data) {
var users = [];
if (subscription_data) {
users = subscription_data.users;
}
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({ 'topic': req.params.topic, 'subscribers': users }));
});
}
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({ 'topic': req.params.topic, 'subscribers': users }));
});


});

if (cb) cb();
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"express-basic-auth": "^1.1.3",
"jfs": "^0.2.6",
"node-env-file": "0.1.8",
"request": "^2.83.0",
"uuid": "^3.1.0"
},
"devDependencies": {
Expand Down

0 comments on commit c35910b

Please sign in to comment.