{"action":"created","issue":{"url":"https://api.github.com/repos/aws/aws-sdk-js/issues/1664","repository_url":"https://api.github.com/repos/aws/aws-sdk-js","labels_url":"https://api.github.com/repos/aws/aws-sdk-js/issues/1664/labels{/name}","comments_url":"https://api.github.com/repos/aws/aws-sdk-js/issues/1664/comments","events_url":"https://api.github.com/repos/aws/aws-sdk-js/issues/1664/events","html_url":"https://github.com/aws/aws-sdk-js/issues/1664","id":248068827,"number":1664,"title":"TemporaryCredentials Does Not Work With MFA When Invoked in Parallel","user":{"login":"jthomerson","id":171934,"avatar_url":"https://avatars3.githubusercontent.com/u/171934?v=4","gravatar_id":"","url":"https://api.github.com/users/jthomerson","html_url":"https://github.com/jthomerson","followers_url":"https://api.github.com/users/jthomerson/followers","following_url":"https://api.github.com/users/jthomerson/following{/other_user}","gists_url":"https://api.github.com/users/jthomerson/gists{/gist_id}","starred_url":"https://api.github.com/users/jthomerson/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jthomerson/subscriptions","organizations_url":"https://api.github.com/users/jthomerson/orgs","repos_url":"https://api.github.com/users/jthomerson/repos","events_url":"https://api.github.com/users/jthomerson/events{/privacy}","received_events_url":"https://api.github.com/users/jthomerson/received_events","type":"User","site_admin":false},"labels":[{"id":13276308,"url":"https://api.github.com/repos/aws/aws-sdk-js/labels/Enhancement","name":"Enhancement","color":"84b6eb","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":2,"created_at":"2017-08-04T17:28:21Z","updated_at":"2017-12-24T15:54:39Z","closed_at":null,"author_association":"NONE","body":"If your SDK is configured to use the `TemporaryCredentials` provider and is using MFA to assume a role and your app has parallel processes calling the SDK, it will result in a credentials error.\r\n\r\nThis happens because `TemporaryCredentials` lazily-loads credentials in its `refresh` method. Unfortunately, that method does not have a lock around it, so if more than one asynchronous operation ends up making a call, `TemporaryCredentials` will call `refresh` for each call, and use the MFA token you passed it more than once. Thus, only the first call to refresh will work because the MFA token is a one-time-use token.\r\n\r\nHere is some example code that demonstrates the problem. You can download it, `npm install underscore q aws-sdk minimist` and run it, passing it the `--role-arn`, `--mfa-serial`, `--mfa-token`, and optionally `--profile` arguments (if you need to use a profile from your shared INI credentials to assume the role). Pass `--refresh-first` to \"fix\" (workaround) the bug by calling refresh and waiting for it to succeed before your parallel operations start.\r\n\r\n```js\r\n#!/usr/bin/env node\r\n'use strict';\r\n\r\nvar _ = require('underscore'),\r\n    Q = require('q'),\r\n    AWS = require('aws-sdk'),\r\n    minimist = require('minimist'),\r\n    argvOpts, argv;\r\n\r\nargvOpts = {\r\n   string: [ 'profile', 'role-arn', 'mfa-serial', 'mfa-token' ],\r\n   'boolean': [ 'refresh-first' ],\r\n};\r\n\r\nargv = minimist(process.argv.slice(2), argvOpts);\r\n\r\nif (!_.isEmpty(argv.profile)) {\r\n   console.log('Setting AWS credentials provider to use profile %s', argv.profile);\r\n   AWS.config.credentials = new AWS.SharedIniFileCredentials({ profile: argv.profile });\r\n}\r\n\r\nif (!_.isEmpty(argv['role-arn'])) {\r\n   (function() {\r\n      var params = { RoleArn: argv['role-arn'] };\r\n\r\n      if (_.isEmpty(argv['mfa-serial'])) {\r\n         console.log('Assuming role %s', params.RoleArn);\r\n      } else {\r\n         params.SerialNumber = argv['mfa-serial'];\r\n         params.TokenCode = argv['mfa-token'];\r\n         console.log('Assuming role %s with MFA %s (%s)', params.RoleArn, params.SerialNumber, params.TokenCode);\r\n      }\r\n\r\n      AWS.config.credentials = new AWS.TemporaryCredentials(params);\r\n   }());\r\n}\r\n\r\n\r\nfunction startParallelProcesses() {\r\n   return Q.all(_.map([ 'us-east-1', 'us-west-2', 'eu-west-1' ], function(region) {\r\n      var s3 = new AWS.S3({ region: region });\r\n\r\n      return Q.ninvoke(s3, 'listBuckets')\r\n         .then(function(resp) {\r\n            _.each(resp.Buckets, function(bucket) {\r\n               console.log('Region \"%s\", bucket \"%s\" was created \"%s\"', region, bucket.Name, bucket.CreationDate);\r\n            });\r\n         });\r\n   }))\r\n   .then(function() {\r\n      console.log('All done');\r\n   });\r\n}\r\n\r\n\r\nif (argv['refresh-first']) {\r\n   // If we manually call refresh on the temporary credentials first, we can avoid the\r\n   // problem described below.\r\n   Q.ninvoke(AWS.config.credentials, 'refresh')\r\n      .then(function() {\r\n         return startParallelProcesses();\r\n      })\r\n      .done();\r\n} else {\r\n   // Here is where the real issue is:\r\n   // If we start a bunch of parallel processes simultaneously, they will all end up\r\n   // resulting in the `refresh` function getting called on the temporary credentials,\r\n   // which will result in our MFA code getting used more than once, which doesn't work\r\n   // since it's a one-time-use code.\r\n   startParallelProcesses().done();\r\n}\r\n```"},"comment":{"url":"https://api.github.com/repos/aws/aws-sdk-js/issues/comments/353791402","html_url":"https://github.com/aws/aws-sdk-js/issues/1664#issuecomment-353791402","issue_url":"https://api.github.com/repos/aws/aws-sdk-js/issues/1664","id":353791402,"user":{"login":"jthomerson","id":171934,"avatar_url":"https://avatars3.githubusercontent.com/u/171934?v=4","gravatar_id":"","url":"https://api.github.com/users/jthomerson","html_url":"https://github.com/jthomerson","followers_url":"https://api.github.com/users/jthomerson/followers","following_url":"https://api.github.com/users/jthomerson/following{/other_user}","gists_url":"https://api.github.com/users/jthomerson/gists{/gist_id}","starred_url":"https://api.github.com/users/jthomerson/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jthomerson/subscriptions","organizations_url":"https://api.github.com/users/jthomerson/orgs","repos_url":"https://api.github.com/users/jthomerson/repos","events_url":"https://api.github.com/users/jthomerson/events{/privacy}","received_events_url":"https://api.github.com/users/jthomerson/received_events","type":"User","site_admin":false},"created_at":"2017-12-24T15:54:39Z","updated_at":"2017-12-24T15:54:39Z","author_association":"NONE","body":"Any update on this issue? @jeskew?"}}
{"actor":{"display_login":"jthomerson"}}