Skip to content
This repository was archived by the owner on May 27, 2025. It is now read-only.

Commit fd053e3

Browse files
committed
Initial commit for the workspace app update
1 parent a87e191 commit fd053e3

File tree

8 files changed

+134
-41
lines changed

8 files changed

+134
-41
lines changed

.env.sample

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
1-
SLACK_ACCESS_TOKEN=''
2-
PORT=3000
3-
SLACK_VERIFICATION_TOKEN=''
1+
SLACK_ACCESS_TOKEN=
2+
SIGNING_SECRET=

.gitignore

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
node_modules
2+
npm-debug.log
3+
package-lock.json
4+
test
5+
temp*
6+
7+
.env
8+
9+
### https://raw.github.com/github/gitignore/b304edf487ce607174e188712225b5269d43f279/Global/OSX.gitignore
10+
11+
.DS_Store
12+
.AppleDouble
13+
.LSOverride
14+
15+
### IDE Settings (EditorConfig/Sublime)
16+
.editorconfig
17+
18+
### IDE Settings (VSCode)
19+
.vscode

DIFF.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#Updates from the previous version
2+
3+
Now all the Blueprints examples have updated using workspace token model from the previous user-centric model. So what are the diffs in this version?
4+
5+
## Creating the app
6+
7+
Instead of using the previous App creation page, the new app is creates one at [https://api.slack.com/apps?new_app_token=1](https://api.slack.com/apps?new_app_token=1).
8+
9+
## Scopes
10+
11+
Some scopes are no longer valid with the new workspace apps.
12+
13+
In previous version, the app required:
14+
* `commands`
15+
* `users:read`
16+
* `users:read.email`
17+
* `chat:write:bot`
18+
19+
In the new version:
20+
* `commands`
21+
* `users:read`
22+
* `users:read.email`
23+
* `chat:write`
24+
25+
You can learn more about scopes at [https://api.slack.com/scopes](https://api.slack.com/scopes)
26+
27+
## Installation
28+
29+
When you install the app, you will be ask to choose which channel(s) to install it. Once you install, on your Slack client, you see a message that tells you the app was adding to certain channel(s). Also the app name appears under "Apps". This is your `app_home`.
30+
31+
## OAuth
32+
33+
Your OAuth access token should begins with `-xoxa`, instead of `-xoxp`.
34+
35+
## Sigining secret
36+
37+
You used to verify if a request was coming from a reliable source (well, from us Slack!) by checking with the legacy *verificatin token*. Now you have more secure [sigining secret](https://api.slack.com/docs/verifying-requests-from-slack).
38+
39+
Basically, you need to compare the value of the `X-Slack-Signature`, the HMAC-SHA256 keyed hash of the raw request payload, with a hashed string containing your Slack signin secret code, combined with the version and `X-Slack-Request-Timestamp`.

README.md

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Slash Command and Dialogs blueprint
22

3+
*Updated August 2018: As we have introduced the workspace app (currently in beta), this tutorial and the code samples have been updated using the new token model! All the changes from the previous version of this example, read the [DIFF.md](diff.md)*
4+
5+
*Learn more about the workspace app at the [Slack API doc(https://api.slack.com/workspace-apps-preview).]*
6+
37
## Creating a helpdesk ticket using a Slash Command and a Dialog
48

59
Use a slash command and a dialog to create a helpdesk ticket in a 3rd-party system. Once it has been created, send a message to the user with information about their ticket.
@@ -10,25 +14,15 @@ Use a slash command and a dialog to create a helpdesk ticket in a 3rd-party syst
1014

1115
#### Create a Slack app
1216

13-
1. Create an app at api.slack.com/apps
14-
1. Navigate to the OAuth & Permissions page and add the following scopes:
17+
1. Create a *workspace app* at [https://api.slack.com/apps?new_app_token=1](https://api.slack.com/apps?new_app_token=1)
18+
2. Add a Slash command (See *Add a Slash Command* section below)
19+
3. Enable Interactive components (See *Enable Interactive Components* below)
20+
4. Navigate to the **OAuth & Permissions** page and add the following scopes:
1521
* `commands`
1622
* `users:read`
1723
* `users:read.email`
18-
* `chat:write:bot`
19-
1. Click 'Save Changes' and install the app
20-
21-
#### Run locally or [![Remix on Glitch](https://cdn.glitch.com/2703baf2-b643-4da7-ab91-7ee2a2d00b5b%2Fremix-button.svg)](https://glitch.com/edit/#!/remix/slack-slash-command-and-dialogs-blueprint)
22-
1. Get the code
23-
* Either clone this repo and run `npm install`
24-
* Or visit https://glitch.com/edit/#!/remix/slack-slash-command-and-dialogs-blueprint
25-
1. Set the following environment variables to `.env` (see `.env.sample`):
26-
* `SLACK_ACCESS_TOKEN`: Your app's `xoxp-` token (available on the Install App page)
27-
* `PORT`: The port that you want to run the web server on
28-
* `SLACK_VERIFICATION_TOKEN`: Your app's Verification Token (available on the Basic Information page)
29-
1. If you're running the app locally:
30-
1. Start the app (`npm start`)
31-
1. In another window, start ngrok on the same port as your webserver (`ngrok http $PORT`)
24+
* `chat:write`
25+
5. Click 'Save Changes' and install the app (You should get an OAuth access token after the installation)
3226

3327
#### Add a Slash Command
3428
1. Go back to the app settings and click on Slash Commands.
@@ -41,4 +35,13 @@ Use a slash command and a dialog to create a helpdesk ticket in a 3rd-party syst
4135

4236
#### Enable Interactive Components
4337
1. Go back to the app settings and click on Interactive Components.
44-
1. Set the Request URL to your ngrok or Glitch URL + /interactive-component
38+
1. Set the Request URL to your server or Glitch URL + /interactive-component
39+
40+
#### Run the app locally or [![Remix on Glitch](https://cdn.glitch.com/2703baf2-b643-4da7-ab91-7ee2a2d00b5b%2Fremix-button.svg)](https://glitch.com/edit/#!/remix/slack-slash-command-and-dialogs-blueprint)
41+
1. Get the code
42+
* Either clone this repo and run `npm install`
43+
* Or visit https://glitch.com/edit/#!/remix/slack-slash-command-and-dialogs-blueprint
44+
2. Set the following environment variables to `.env` (see `.env.sample`):
45+
* `SLACK_ACCESS_TOKEN`: Your app's `xoxa-` token (available on the Install App page)
46+
* `SLACK_SIGNING_SECRET`: Your app's Signing Secret (available on the **Basic Information** page)
47+
3. If you're running the app locally, run the app (`npm start`)

package-lock.json

Lines changed: 10 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"test": "echo \"Error: no test specified\" && exit 1",
88
"start": "DEBUG=slash-command-template node src/index.js"
99
},
10-
"author": "Sachin Ranchod <sranchod@slack-corp.com>",
10+
"author": "Sachin Ranchod <sranchod@slack-corp.com> and Tomomi Imura <timura@slack-corp.com>",
1111
"engines": {
1212
"node": ">=4.2.0",
1313
"npm": ">=2.14.7"

src/index.js

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,27 @@ const express = require('express');
55
const bodyParser = require('body-parser');
66
const qs = require('querystring');
77
const ticket = require('./ticket');
8+
const signature = require('./verifySignature');
89
const debug = require('debug')('slash-command-template:index');
910

11+
const apiUrl = 'https://slack.com/api';
12+
1013
const app = express();
1114

1215
/*
1316
* Parse application/x-www-form-urlencoded && application/json
17+
* Use body-parser's `verify` callback to export a parsed raw body
18+
* that you need to use to verify the signature
1419
*/
15-
app.use(bodyParser.urlencoded({ extended: true }));
16-
app.use(bodyParser.json());
20+
21+
const rawBodyBuffer = (req, res, buf, encoding) => {
22+
if (buf && buf.length) {
23+
req.rawBody = buf.toString(encoding || 'utf8');
24+
}
25+
};
26+
27+
app.use(bodyParser.urlencoded({verify: rawBodyBuffer, extended: true }));
28+
app.use(bodyParser.json({ verify: rawBodyBuffer }));
1729

1830
app.get('/', (req, res) => {
1931
res.send('<h2>The Slash Command and Dialog app is running</h2> <p>Follow the' +
@@ -27,10 +39,10 @@ app.get('/', (req, res) => {
2739
app.post('/commands', (req, res) => {
2840
// extract the verification token, slash command text,
2941
// and trigger ID from payload
30-
const { token, text, trigger_id } = req.body;
42+
const { text, trigger_id } = req.body;
3143

3244
// check that the verification token matches expected value
33-
if (token === process.env.SLACK_VERIFICATION_TOKEN) {
45+
if (signature.isVerified(req)) {
3446
// create the dialog payload - includes the dialog structure, Slack API token,
3547
// and trigger ID
3648
const dialog = {
@@ -69,17 +81,18 @@ app.post('/commands', (req, res) => {
6981
};
7082

7183
// open the dialog by calling dialogs.open method and sending the payload
72-
axios.post('https://slack.com/api/dialog.open', qs.stringify(dialog))
84+
axios.post(`${apiUrl}/dialog.open`, qs.stringify(dialog))
7385
.then((result) => {
7486
debug('dialog.open: %o', result.data);
87+
console.log(result.data)
7588
res.send('');
7689
}).catch((err) => {
7790
debug('dialog.open call failed: %o', err);
7891
res.sendStatus(500);
7992
});
8093
} else {
8194
debug('Verification token mismatch');
82-
res.sendStatus(500);
95+
res.sendStatus(403);
8396
}
8497
});
8598

@@ -91,7 +104,7 @@ app.post('/interactive-component', (req, res) => {
91104
const body = JSON.parse(req.body.payload);
92105

93106
// check that the verification token matches expected value
94-
if (body.token === process.env.SLACK_VERIFICATION_TOKEN) {
107+
if (signature.isVerified(req)) {
95108
debug(`Form submission received: ${body.submission.trigger_id}`);
96109

97110
// immediately respond with a empty 200 response to let
@@ -102,10 +115,10 @@ app.post('/interactive-component', (req, res) => {
102115
ticket.create(body.user.id, body.submission);
103116
} else {
104117
debug('Token mismatch');
105-
res.sendStatus(500);
118+
res.sendStatus(403);
106119
}
107120
});
108121

109-
app.listen(process.env.PORT, () => {
110-
console.log(`App listening on port ${process.env.PORT}!`);
122+
const server = app.listen(process.env.PORT || 5000, () => {
123+
console.log('Express server listening on port %d in %s mode', server.address().port, app.settings.env);
111124
});

src/verifySignature.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
const crypto = require('crypto');
2+
3+
const isVerified = (req) => {
4+
const signature = req.headers['x-slack-signature'];
5+
const timestamp = req.headers['x-slack-request-timestamp'];
6+
const hmac = crypto.createHmac('sha256', process.env.SLACK_SIGNING_SECRET);
7+
const [version, hash] = signature.split('=');
8+
9+
// Check if the timestamp is too old
10+
const fiveMinutesAgo = ~~(Date.now() / 1000) - (60 * 5);
11+
if (timestamp < fiveMinutesAgo) return false;
12+
13+
hmac.update(`${version}:${timestamp}:${req.rawBody}`);
14+
15+
// check that the request signature matches expected value
16+
return hmac.digest('hex') === hash;
17+
};
18+
19+
module.exports = { isVerified };
20+

0 commit comments

Comments
 (0)