Oauth 2.0 Connection: Slack - Victorianuonuo/nudge_bot GitHub Wiki

Prerequisites

  1. Slack ID and Slack secret: More reference here
  2. npm install passport-slack-oauth2 to integrate Slack authentication in the chatbot application.

Access process: OAuth 2.0

After the user connects their account via Oauth2, an access token will be created. That access token must be included in each API request. The user can revoke access to a service at any time.

Trigger

There should be a starting point to allow users to begin the process. For our Slack chatbot, simply give users a button to click on to start the process.

function authenSlack(slackID){
    bot.postMessage(slackID, 'Please click the following button to add Slack as a data source.' , {
    as_user:true,
    "attachments": [
        {
            "fallback": "activate",
            "actions": [
                {
                    "type": "button",
                    "text": "connect",
                    "url": process.env.DOMAIN + '/apikey/slack/oauth?auth_id='+slackID
                }
            ]
        }
    ]
    });
}

This is a function to generate a link button in Slack chatbot. The Slack button format could be seen here. In order to map each user's access token to their Slack IDs (slackID), we will add a new query after the url.

Configure Slack strategy with Passport

The Slack authentication strategy authenticates users using a Slack account and OAuth 2.0 tokens. The strategy requires a verify callback, which accepts these credentials and calls done providing a user, as well as options specifying a client ID, client secret, and callback URL.

passport.use(new SlackStrategy({
    clientID: process.env.SLACK_ID,
    clientSecret: process.env.SLACK_SECRET,
    callbackURL: process.env.DOMAIN+"/apikey/slack/callback",
    skipUserProfile: false, // default
    passReqToCallback: true,
    scope: ['users:read', 'identity.basic']
  },
  (req, accessToken, refreshToken, profile, done) => {
    // save user data into the database
    SlackKey.findOne({slackID: req.query.state}).exec(function(err, apikey){
        if(err){
            done(err, apikey);
        } else {
            if(apikey){
                var newApikey = apikey;
                newApikey.access_token = accessToken;
            }else{
                var newApikey = new SlackKey({
                    slackID: req.query.state,
                    access_token: accessToken,
                });
            }
            newApikey.save()
            .then( () => {
                done(err, newApikey);
            })
            .catch((err) => {
                done(err, newApikey);
            });
        }
    });
  }
));

Authenticate Requests

Use passport.authenticate(), specifying the 'rescuetime' strategy, to authenticate requests.

router.get('/slack/oauth', function(req, res, next) {
    var slackID = req.query.auth_id;
    passport.authenticate('Slack', {state: slackID})(req, res, next);
});
router.get('/slack/callback',
  passport.authenticate('Slack', { failureRedirect: '/apikey/slack/callback', session: false }),
  function(req, res) {
    var slackID = req.query.state;
    // Successful authentication, redirect home.
    if(slackID){
        bot.postMessage(slackID, "Congratulations! You successfully add slack.", {as_user:true});
        res.status(200).send("Your account was successfully authenticated");
    }else{
        bot.postMessage(slackID, "Ooops! Something went wrong! Please try again!", {as_user:true});
        res.status(400).send("Your slackID is missing when call back!");
    }
});