0

I'm trying to decrypt some environment variables, using AWS Key Management Service (KMS), from an AWS Lambda function, and then posting a tweet using the decrypted credentials. However, the environment variables are not being decrypted before I utilize the Twitter object. This is causing authentication to fail.

How can I make sure that the Twitter object, in the code below, is fully instantiated / initialized, before calling its member functions? Should I be using promises instead?

var AWS = require('aws-sdk');
var Twitter = require('twitter');
var s3 = new AWS.S3();
var kms = new AWS.KMS();

function DecryptEnvironmentVariable(creds, varname) {
    console.log(`Decrypting environment variable named ${varname}`);
    console.log(process.env[varname]);

    params = {
        CiphertextBlob: process.env[varname]
    }
    kms.decrypt(params, function (err, data) {
        if (err) {
            console.log(err);
        }
        else {
            console.log("Successfully decrypted envrionment variable.");
            return data.Plaintext;
        }
    });
}

exports.tweet = function (event, context) {
    // Instantiate the Twitter object
    var twitterclient = new Twitter({
        consumer_key: DecryptEnvironmentVariable('TWITTER_CONSUMER_KEY'),
        consumer_secret: DecryptEnvironmentVariable('TWITTER_CONSUMER_SECRET'),
        access_token_key: DecryptEnvironmentVariable('TWITTER_ACCESS_TOKEN_KEY'),
        access_token_secret: DecryptEnvironmentVariable('TWITTER_ACCESS_TOKEN_SECRET'),
    })

    // Post a new tweet
    twitterclient.post('statuses/update', { status: "messagegoeshere" })
    .then(function(tweet) {
        console.log("Tweet was successfully posted!");
    })
    .catch(function(error) {
        console.log("Error occurred while posting tweet. :(");
        console.log(error);
    });
}

2 Answers 2

1

DecryptEnvironmentVariable doesn't actually return anything, so you can't really use it the way you're trying to.

Since you need to get the results of multiple async operations, probably the most straightforward way of doing that will be with Promise.all (assuming you're using a current version of nodejs):

var AWS = require('aws-sdk');
var Twitter = require('twitter');
var s3 = new AWS.S3();
var kms = new AWS.KMS();

function DecryptEnvironmentVariable(creds, varname) {
  return new Promise(function (resolve, reject) {
    console.log(`Decrypting environment variable named ${varname}`);
    console.log(process.env[varname]);

    params = {
      CiphertextBlob: process.env[varname]
    }
    kms.decrypt(params, function (err, data) {
      if (err) {
        console.log(err);
        reject(err);
      }
      else {
        console.log("Successfully decrypted envrionment variable.");
        resolve(data.Plaintext);
      }
    });
  });
}

exports.tweet = function (event, context) {
  // Instantiate the Twitter object
  var tasks = [
    DecryptEnvironmentVariable('TWITTER_CONSUMER_KEY'),
    DecryptEnvironmentVariable('TWITTER_CONSUMER_SECRET'),
    DecryptEnvironmentVariable('TWITTER_ACCESS_TOKEN_KEY'),
    DecryptEnvironmentVariable('TWITTER_ACCESS_TOKEN_SECRET')
  ];

  return Promise.all(tasks).then(function (keys) {

    var twitterclient = new Twitter({
      consumer_key: keys[0],
      consumer_secret: keys[1],
      access_token_key: keys[2],
      access_token_secret: keys[3]
    });

    // Post a new tweet
    return twitterclient.post('statuses/update', { status: "messagegoeshere" });
  })
  .then(function(tweet) {
    console.log("Tweet was successfully posted!");
  })
  .catch(function(error) {
    console.log("Error occurred while posting tweet. :(");
    console.log(error);
  });
}
Sign up to request clarification or add additional context in comments.

3 Comments

FYI the AWS SDK supports promises now so you could just do something like return kms.decrypt(params).promise() in the DecryptEnvironmentVariable function instead of wrapping the whole thing in a new Promise.
Nice, I didnt see that when I checked the docs, but then again I didn't look too hard
The documentation is a little hidden. Essentially, KMS.decrypt() returns an AWS.Request instance which in turn has a promise() method.
0

AWS Lambda supports environment variables natively. So there's really no need for you to encrypt/decrypt them yourself with KMS. Using the built-in environment variables would allow you to use them much easier and synchronously just be accessing them where they would normally be: process.env.TWITTER_CONSUMER_KEY.

Comments