Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

functions/slack: message verification - tokens -> signatures #1562

Merged
merged 8 commits into from
Jan 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion functions/slack/config.default.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"SLACK_TOKEN": "[YOUR_SLACK_TOKEN]",
"SLACK_SECRET": "[YOUR_SLACK_SIGNING_SECRET]",
"KG_API_KEY": "[YOUR_KG_API_KEY]"
}
26 changes: 16 additions & 10 deletions functions/slack/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
// [START functions_slack_setup]
const config = require('./config.json');
const {google} = require('googleapis');
const {verifyRequestSignature} = require('@slack/events-api');

// Get a reference to the Knowledge Graph Search component
const kgsearch = google.kgsearch('v1');
Expand Down Expand Up @@ -87,11 +88,19 @@ const formatSlackMessage = (query, response) => {
/**
* Verify that the webhook request came from Slack.
*
* @param {object} body The body of the request.
* @param {string} body.token The Slack token to be verified.
* @param {object} req Cloud Function request object.
* @param {string} req.headers Headers Slack SDK uses to authenticate request.
* @param {string} req.rawBody Raw body of webhook request to check signature against.
*/
const verifyWebhook = body => {
if (!body || body.token !== config.SLACK_TOKEN) {
const verifyWebhook = req => {
const signature = {
signingSecret: config.SLACK_SECRET,
requestSignature: req.headers['x-slack-signature'],
requestTimestamp: req.headers['x-slack-request-timestamp'],
body: req.rawBody,
};

if (!verifyRequestSignature(signature)) {
const error = new Error('Invalid credentials');
error.code = 401;
throw error;
Expand Down Expand Up @@ -132,15 +141,12 @@ const makeSearchRequest = query => {
/**
* Receive a Slash Command request from Slack.
*
* Trigger this function by making a POST request with a payload to:
* Trigger this function by creating a Slack slash command with this URL:
* https://[YOUR_REGION].[YOUR_PROJECT_ID].cloudfunctions.net/kgsearch
*
* @example
* curl -X POST "https://us-central1.your-project-id.cloudfunctions.net/kgSearch" --data '{"token":"[YOUR_SLACK_TOKEN]","text":"giraffe"}'
*
* @param {object} req Cloud Function request object.
* @param {object} req.body The request payload.
* @param {string} req.body.token Slack's verification token.
* @param {string} req.rawBody Raw request payload used to validate Slack's message signature.
* @param {string} req.body.text The user's search query.
* @param {object} res Cloud Function response object.
*/
Expand All @@ -153,7 +159,7 @@ exports.kgSearch = async (req, res) => {
}

// Verify that this request came from Slack
verifyWebhook(req.body);
verifyWebhook(req);

// Make the request to the Knowledge Graph Search API
const response = await makeSearchRequest(req.body.text);
Expand Down
1 change: 1 addition & 0 deletions functions/slack/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"test": "mocha test/*.test.js --timeout=20000"
},
"dependencies": {
"@slack/events-api": "^2.3.0",
"googleapis": "^46.0.0"
},
"devDependencies": {
Expand Down
7 changes: 6 additions & 1 deletion functions/slack/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,21 @@ const getSample = () => {
const googleapis = {
kgsearch: sinon.stub().returns(kgsearch),
};
const eventsApi = {
verifyRequestSignature: sinon.stub().returns(true),
};

return {
program: proxyquire('../', {
googleapis: {google: googleapis},
'./config.json': config,
'@slack/events-api': eventsApi,
}),
mocks: {
googleapis: googleapis,
kgsearch: kgsearch,
config: config,
eventsApi: eventsApi,
},
};
};
Expand Down Expand Up @@ -115,7 +120,7 @@ describe('functions_slack_search functions_verify_webhook', () => {
const sample = getSample();

mocks.req.method = method;
mocks.req.body.token = 'wrong';
sample.mocks.eventsApi.verifyRequestSignature = sinon.stub().returns(false);

try {
await sample.program.kgSearch(mocks.req, mocks.res);
Expand Down