Oauth 2.0 Connection: RescueTime - Victorianuonuo/nudge_bot GitHub Wiki

Prerequisites

  1. RescueTime ID and ResueTime secret: Contact RescueTime to set up an Oauth2 application. You will have to send them your domain name. After a few weekdays, they will give you the secret and id for Oauth2 connection.
  2. npm install Passport-RescueTime to integrate RescueTime 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 authenResuetime(slackID){
    bot.postMessage(slackID, 'Please click the following button to add rescuetime as a data source.' , {
    as_user:true,
    "attachments": [
        {
            "fallback": "activate",
            "actions": [
                {
                    "type": "button",
                    "text": "connect",
                    "url": process.env.DOMAIN + '/apikey/rescuetime/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 RescueTime strategy with Passport

The RescueTime authentication strategy authenticates users using a RescueTime 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 RescueTimeStrategy({
    clientID: process.env.RESCUETIME_ID,
    clientSecret: process.env.RESCUETIME_SECRET,
    callbackURL: process.env.DOMAIN+"/apikey/rescuetime/callback",
    passReqToCallback: true,
    scope: ['time_data', 'category_data', 'productivity_data', 'alert_data', 'highlight_data', 'focustime_data']
  },
  function(req, accessToken, refreshToken, profile, done) {
    // save user data into the database
    Apikey.findOne({slackID: req.query.state}).exec(function(err, apikey){
        if(err){
            done(err, apikey);
        } else {
            if(apikey){
                var newApikey = apikey;
                newApikey.rescuetime_key = accessToken;
            }else{
                var newApikey = new Apikey({
                    slackID: req.query.state,
                    rescuetime_key: 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('/rescuetime/oauth', function(req, res, next) {
    var slackID = req.query.auth_id;
    passport.authenticate('rescuetime', {state: slackID})(req, res, next);
});
router.get('/rescuetime/callback',
  passport.authenticate('rescuetime', { failureRedirect: '/apikey/rescuetime/callback', session: false }),
  function(req, res) {
    var slackID = req.query.state;
    // Successful authentication, redirect home.
    if(slackID){
        bot.postMessage(slackID, "Congratulations! You successfully add rescuetime.", {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!");
    }
  });