I've developed a bot and it works fine on its home workspace. Now I need to make oauth work so it can be installed on other workspaces. How can I do that? Does Bold provide anything by default?
x in one of the [ ])x in each of the [ ])At the moment, Bolt doesn't have a feature to handle OAuth flow as a part of the framework. Bolt users need to implement https://api.slack.com/docs/oauth on their own.
After implementing the OAuth flow, only the thing to do is to implement the authorize function: https://slack.dev/bolt/concepts#authorization
@kroltan To help you on your way, this is my boilerplate of the oauth flow, working alongside Bolt. It also uses the express instance of Bolt, rather than a new one. It might need some tweaks here and there, but it should be useful. Don't forget to implement the state mechanism though, it's very important.
Side note: it's not using oauth.v2.access method because it doesn't seem to be available in Bolt yet either.
```
const { App, ExpressReceiver } = require('@slack/bolt');
const expressReceiver = new ExpressReceiver({
signingSecret:
endpoints: '/events'
});
//TODO: implement this function see Bolt documentation under "Authorization"
const app = new App({
authorize: authorizeFn,
receiver: expressReceiver
});
const app_express = expressReceiver.app;
/* Page with add button, can be implemented in website instead /
app_express.get('/auth/add', (req, res, next) => {
//TODO: fill in correct Embeddable slack button
res.write('[ Slack API > your app > Manage Distribution > Embeddable slack button ]');
res.end();
});
/ Support for Direct Install */
app_express.get('/auth/direct', (req, res, next) => {
//TODO: fill in correct Sharable URL
res.redirect('[ Slack API > your app > Manage Distribution > Sharable URL ]);
res.end();
});
/* oauth callback function */
app_express.get('/auth/callback', (req, res, next) => {
let code = req.param("code");
// TODO: add state to your oauth url in /auth/add (button) and /auth/direct (direct install) and check it here to make sure there is no XSRF attack going on.
let state = req.param("state");
return app.client.oauth.access({
client_id: <client_id>,
client_secret: <client_secret>,
code: code
}).then(async (result) => {
// save result of oauth.access call somewhere, like in a database.
// redirect user afterwards with res.redirect to an url that will say something like "Thanks for installing!" perhaps.
}).catch((error) => {
throw error;
});
});
Folks who are still stuck on this, you might want to check out this project: https://github.com/asopinka/bolt-oauth
I haven't used it myself yet, but it seems to solve this problem in a reusable way.
I have a question about:
const app = new App({
authorize: authorizeFn,
receiver: expressReceiver
});
Does app get reinitialized every time a request comes in? Like when I deploy my bolt code to Heroku or whatever, and users from multiple slack teams hit the express endpoints, how does an app get properly initialized for all those teams? I think I'm just confused about the lifecycle of the node.js code?
The question is a bit naive but I'd appreciate any help 馃檹
@jacklein
Does app get reinitialized every time a request comes in?
No, it doesn't.
how does an app get properly initialized for all those teams?
Every time App receives a request from Slack, App runs authorizeFn to check if the app is installed to the workspace. If the team_id (plus optionally enterprise_id) is absent in the app's database (the one authorizeFn checks), the request will be automatically denied by Bolt. Otherwise, the authorized data (botToken etc) will be given as part of context arg for listeners. Checking the framework code may be easier to understand the flow:
https://github.com/slackapi/bolt/blob/%40slack/bolt%401.4.1/src/App.ts#L385-L393
At the moment, Bolt doesn't offer the features to manage your database. So, as the examples above do, your app needs to insert/update/delete rows in the database when users install/uninstall your app.
Does this explanation make sense to you?
@seratch ahhhhh authorizeFn gets called on every incoming request. That makes sense. One other quick question I have is if the workspace is not an enterprise workspace, will enterpriseId just be nil? In which case we will just search for the teamId in our db?
Thanks!!
Edit: I now see your comment about enterpriseId. This all makes sense, thank you again for all the help.
Happy to be of help to you! 馃槂
@kroltan To help you on your way, this is my boilerplate of the oauth flow, working alongside Bolt. It also uses the express instance of Bolt, rather than a new one. It might need some tweaks here and there, but it should be useful. Don't forget to implement the state mechanism though, it's very important.
Side note: it's not using oauth.v2.access method because it doesn't seem to be available in Bolt yet either.
const { App, ExpressReceiver } = require('@slack/bolt'); const expressReceiver = new ExpressReceiver({ signingSecret: <signing secret>, endpoints: '/events' }); //TODO: implement this function see Bolt documentation under "Authorization" const app = new App({ authorize: authorizeFn, receiver: expressReceiver }); const app_express = expressReceiver.app; /* Page with add button, can be implemented in website instead */ app_express.get('/auth/add', (req, res, next) => { //TODO: fill in correct Embeddable slack button res.write('[ Slack API > your app > Manage Distribution > Embeddable slack button ]'); res.end(); }); /* Support for Direct Install */ app_express.get('/auth/direct', (req, res, next) => { //TODO: fill in correct Sharable URL res.redirect('[ Slack API > your app > Manage Distribution > Sharable URL ]); res.end(); }); /* oauth callback function */ app_express.get('/auth/callback', (req, res, next) => { let code = req.param("code"); // TODO: add state to your oauth url in /auth/add (button) and /auth/direct (direct install) and check it here to make sure there is no XSRF attack going on. let state = req.param("state"); return app.client.oauth.access({ client_id: <client_id>, client_secret: <client_secret>, code: code }).then(async (result) => { // save result of oauth.access call somewhere, like in a database. // redirect user afterwards with res.redirect to an url that will say something like "Thanks for installing!" perhaps. }).catch((error) => { throw error; }); });
Is the oAuthV2 not available still?
Thanks @ErwinAI this is the closest I cam to get this running.
I had to make a few changes.
1) In express 4 it's
let code = req.query.code;
instead of
let code = req.param("code");
2) With oauth v2
return app.client.oauth.v2.access({
instead of
return app.client.oauth.access({
And be careful with
const expressReceiver = new ExpressReceiver({
signingSecret: process.env.SLACK_SIGNING_SECRET,
endpoints: "/events"
});
its probably
const expressReceiver = new ExpressReceiver({
signingSecret: process.env.SLACK_SIGNING_SECRET,
endpoints: "/slack/events"
});
took me a while to figure it out
I noticed that Bolt has some of the oauth support enabled. Would it be possible to see an example using the bolt API for configuring this system? So far, I have seen the (somewhat outdated) bolt-oauth library on npm, but it looks to be using an old version of the API.
@jparr721 I'm not sure I understand what your asking? Want to open a new issue for this? bolt-oauth isn't necessary since OAuth is built into bolt-js now. See https://slack.dev/bolt-js/concepts#authenticating-oauth
Sure, thanks @stevengill
Most helpful comment
@kroltan To help you on your way, this is my boilerplate of the oauth flow, working alongside Bolt. It also uses the express instance of Bolt, rather than a new one. It might need some tweaks here and there, but it should be useful. Don't forget to implement the state mechanism though, it's very important.
Side note: it's not using oauth.v2.access method because it doesn't seem to be available in Bolt yet either.
```,
const { App, ExpressReceiver } = require('@slack/bolt');
const expressReceiver = new ExpressReceiver({
signingSecret:
endpoints: '/events'
});
//TODO: implement this function see Bolt documentation under "Authorization"
const app = new App({
authorize: authorizeFn,
receiver: expressReceiver
});
const app_express = expressReceiver.app;
/* Page with add button, can be implemented in website instead /
app_express.get('/auth/add', (req, res, next) => {
//TODO: fill in correct Embeddable slack button
res.write('[ Slack API > your app > Manage Distribution > Embeddable slack button ]');
res.end();
});
/ Support for Direct Install */
app_express.get('/auth/direct', (req, res, next) => {
//TODO: fill in correct Sharable URL
res.redirect('[ Slack API > your app > Manage Distribution > Sharable URL ]);
res.end();
});
/* oauth callback function */
app_express.get('/auth/callback', (req, res, next) => {
let code = req.param("code");
});