Sane
Sane

Reputation: 594

Send REST calls from Node server to third party application using OAuth

enter image description here

I'm implementing a server that handles chat messages. In some cases I want to access data from a JIRA instance. I'm using passport-atlassian-oauth strategy for authenticating with JIRA and BearerStrategy for requests, but my issue is that the authentication is only valid in the browser after a user has given "My Server" read and write access to JIRA. In many guides they just call res.redirect('/successfulLogin') or something similar after a successful authentication, but I would instead like to do a rest call to JIRA, process the data and send it to my connected client application.

How do I do that?

I'm completely new to all this and everything just spins around in my head. I save and have access to the token used for authentication and when I for instance navigate to .../test/access_token=?[token] in my browser it works.

passport.use(new BearerStrategy(
    function(token, done) {
      // Find user by token
      client.smembers('access_token:' + token, function(err, replies) {
        if (err) {
          return done(err);
        }
        // if user found
        // TODO: yet again, hard coded for one
        if (replies.length > 0) {
          console.log('SHOULD BE 1:', replies[0]);
          client.hgetall('users:' + replies[0], function(err, user) {
            if (err) {
              return done(err);
            }
            if (!user) {
              return done(null, false);
            }
            return done(null, user, {scope: 'all'});
          });
        }
      });
    }
  ));

As you can see it's hard coded for just one user and I'm using Redis as a "database".

  passport.use(new AtlassianOAuthStrategy({
      applicationURL: 'http://localhost:2990/jira',
      callbackURL: '/auth/atlassian-oauth/callback',
      consumerKey: RsaPublicKey,
      consumerSecret: rsaPrivateKey,
      clientId: 'MyBot'
    },
    function(accessToken, tokenSecret, profile, done) {
      // Find user
      client.hgetall('users:1', function(err, user) {
        if(err) {
          return done(err);
        }
        // user not found
        if(!user) {
          // create new user, no worries!
          // TODO: HARD CODED FOR ONE USER
          client.hmset('users:1', 'id', profile.id, 'access_token', accessToken, function(err, res) {
            client.sadd('id:admin', '1');
            client.sadd('access_token:'+ accessToken, '1');
            client.hgetall(profile.id, function(err, user) {
              return done(null, user);
            });
          });
        } else {
          // Update access token!
          client.hmset(profile.id, 'access_token', accessToken, function() {
            client.sadd('access_token:' + accessToken, '1', function() {
              client.hgetall(profile.id, function(err, result) {
                return done(null, user);
              });
            });
          });

        }
      });
    }
  ));

Here's the rest

  app.get('/auth/atlassian-oauth',
    passport.authenticate('atlassian-oauth', {session: false, scope: []}),
    function(req, res) {
      console.log('- Function: /auth/atlassian-oauth - should not be called)');
    });

  app.get('/auth/atlassian-oauth/callback',
    passport.authenticate('atlassian-oauth', {session: false, failureRedirect: '/login'}),
    function(req, res) {
      console.log('- Function: /auth/atlassian-oauth/callback - Authentication successful!', req.user.access_token);
      // Update access token!
      // Should I even do this? Shouldn't I already have the correct token?
      client.hmset('users:1', 'access_token', req.user.access_token, function() {
        client.sadd('access_token:' + req.user.access_token, '1', function() {
          client.hgetall('users:1', function(err, result) {
            res.redirect('/test?access_token=' + req.user.access_token);
          });
        });
      });
    });

So now that you've seen some relevant (just tell me and I'll post more) code, how do I send a rest call to JIRA without getting a 401? :)

EDIT: Any help appreciated! You would make me really happy if you just can point me into the right direction!

Upvotes: 3

Views: 1042

Answers (1)

Sane
Sane

Reputation: 594

Ok. I figured it out! First of all you want to save both you access token and token secret to you db in AtlassianOAuthStrategy. Second, in order to send a REST call to a third party service you can just use http request with OAuth:

var request = require('request');

var oauth = {
  signature_method : 'RSA-SHA1',
  consumer_key : RsaPublicKey,
  private_key : rsaPrivateKey,
  token : [get access_token from you db],
  token_secret : [get token_secret from you db]'
};
var url = 'http://localhost:2990/jira/rest/api/2/issue/' + id;
request.get({url:url, oauth:oauth, json:true}, function (e, r, issue) {
  console.log(issue)
});

Now that everything is working I'm going to start refactoring and reading some more documentation in order to make the design prettier and figure out how to use Redis properly :)

Upvotes: 1

Related Questions