Skip to content
5 changes: 5 additions & 0 deletions .changes/next-release/feature-Credentials-1a22eb00.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"type": "feature",
"category": "Credentials",
"description": "enables use of credentials_process for sourcing credentials from an external process https://docs.aws.amazon.com/cli/latest/topic/config-vars.html#sourcing-credentials-from-external-processes"
}
63 changes: 58 additions & 5 deletions lib/credentials/shared_ini_file_credentials.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
var AWS = require('../core');
var STS = require('../../clients/sts');
var proc = require('child_process');
var iniLoader = AWS.util.iniLoader;

/**
Expand Down Expand Up @@ -158,23 +159,75 @@ AWS.SharedIniFileCredentials = AWS.util.inherit(AWS.Credentials, {
return;
}

this.accessKeyId = profile['aws_access_key_id'];
this.secretAccessKey = profile['aws_secret_access_key'];
this.sessionToken = profile['aws_session_token'];
if (profile['aws_access_key_id'] || profile['aws_secret_access_key']) {
this.accessKeyId = profile['aws_access_key_id'];
this.secretAccessKey = profile['aws_secret_access_key'];
this.sessionToken = profile['aws_session_token'];
} else if (profile['credential_process']) {
this.loadViaCredentialProcess(profile, function(err, data) {
if (err) {
callback(err);
} else {
self.expired = false;
self.accessKeyId = data.AccessKeyId;
self.secretAccessKey = data.SecretAccessKey;
self.sessionToken = data.SessionToken;
if (data.Expiration) {
self.expireTime = new Date(data.Expiration);
}
callback(null);
}
});
}

if (!this.accessKeyId || !this.secretAccessKey) {
if ((!this.accessKeyId || !this.secretAccessKey) && !profile['credential_process']) {
throw AWS.util.error(
new Error('Credentials not set for profile ' + this.profile),
{ code: 'SharedIniFileCredentialsProviderFailure' }
);
}
this.expired = false;
callback(null);
if (this.accessKeyId || this.secretAccessKey) {
callback(null);
}
} catch (err) {
callback(err);
}
},

/**
* Executes the credential_process and retrieves
* credentials from the output
* @api private
* @param profile [map] credentials profile
* @throws SharedIniFileCredentialsProviderFailure
*/
loadViaCredentialProcess: function loadViaCredentialProcess(profile, callback) {
proc.exec(profile['credential_process'], function(err,stdOut, stdErr) {
if (err) {
callback(err, null);
} else {
var credData = JSON.parse(stdOut);
if (credData.Expiration) {
var currentTime = AWS.util.date.getDate();
var expireTime = new Date(credData.Expiration);
if (expireTime < currentTime) {
err = AWS.util.error(
new Error('credential_process returned expired credentials'),
{ code: 'SharedIniFileCredentialsProviderFailure' }
);
}
} else if (credData.Version !== 1) {
err = AWS.util.error(
new Error('credential_process does not return Version == 1'),
{ code: 'SharedIniFileCredentialsProviderFailure' }
);
}
callback(err, credData);
}
});
},

/**
* Loads the credentials from the shared credentials file
*
Expand Down
120 changes: 119 additions & 1 deletion test/credentials.spec.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.