123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- var AWS = require('../core');
- var path = require('path');
- var crypto = require('crypto');
- var iniLoader = AWS.util.iniLoader;
- /**
- * Represents credentials from sso.getRoleCredentials API for
- * `sso_*` values defined in shared credentials file.
- *
- * ## Using SSO credentials
- *
- * The credentials file must specify the information below to use sso:
- *
- * [profile sso-profile]
- * sso_account_id = 012345678901
- * sso_region = **-****-*
- * sso_role_name = SampleRole
- * sso_start_url = https://d-******.awsapps.com/start
- *
- * or using the session format:
- *
- * [profile sso-token]
- * sso_session = prod
- * sso_account_id = 012345678901
- * sso_role_name = SampleRole
- *
- * [sso-session prod]
- * sso_region = **-****-*
- * sso_start_url = https://d-******.awsapps.com/start
- *
- * This information will be automatically added to your shared credentials file by running
- * `aws configure sso`.
- *
- * ## Using custom profiles
- *
- * The SDK supports loading credentials for separate profiles. This can be done
- * in two ways:
- *
- * 1. Set the `AWS_PROFILE` environment variable in your process prior to
- * loading the SDK.
- * 2. Directly load the AWS.SsoCredentials provider:
- *
- * ```javascript
- * var creds = new AWS.SsoCredentials({profile: 'myprofile'});
- * AWS.config.credentials = creds;
- * ```
- *
- * @!macro nobrowser
- */
- AWS.SsoCredentials = AWS.util.inherit(AWS.Credentials, {
- /**
- * Creates a new SsoCredentials object.
- *
- * @param options [map] a set of options
- * @option options profile [String] (AWS_PROFILE env var or 'default')
- * the name of the profile to load.
- * @option options filename [String] ('~/.aws/credentials' or defined by
- * AWS_SHARED_CREDENTIALS_FILE process env var)
- * the filename to use when loading credentials.
- * @option options callback [Function] (err) Credentials are eagerly loaded
- * by the constructor. When the callback is called with no error, the
- * credentials have been loaded successfully.
- */
- constructor: function SsoCredentials(options) {
- AWS.Credentials.call(this);
- options = options || {};
- this.errorCode = 'SsoCredentialsProviderFailure';
- this.expired = true;
- this.filename = options.filename;
- this.profile = options.profile || process.env.AWS_PROFILE || AWS.util.defaultProfile;
- this.service = options.ssoClient;
- this.httpOptions = options.httpOptions || null;
- this.get(options.callback || AWS.util.fn.noop);
- },
- /**
- * @api private
- */
- load: function load(callback) {
- var self = this;
- try {
- var profiles = AWS.util.getProfilesFromSharedConfig(iniLoader, this.filename);
- var profile = profiles[this.profile] || {};
- if (Object.keys(profile).length === 0) {
- throw AWS.util.error(
- new Error('Profile ' + this.profile + ' not found'),
- { code: self.errorCode }
- );
- }
- if (profile.sso_session) {
- if (!profile.sso_account_id || !profile.sso_role_name) {
- throw AWS.util.error(
- new Error('Profile ' + this.profile + ' with session ' + profile.sso_session +
- ' does not have valid SSO credentials. Required parameters "sso_account_id", "sso_session", ' +
- '"sso_role_name". Reference: https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-sso.html'),
- { code: self.errorCode }
- );
- }
- } else {
- if (!profile.sso_start_url || !profile.sso_account_id || !profile.sso_region || !profile.sso_role_name) {
- throw AWS.util.error(
- new Error('Profile ' + this.profile + ' does not have valid SSO credentials. Required parameters "sso_account_id", "sso_region", ' +
- '"sso_role_name", "sso_start_url". Reference: https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-sso.html'),
- { code: self.errorCode }
- );
- }
- }
- this.getToken(this.profile, profile, function (err, token) {
- if (err) {
- return callback(err);
- }
- var request = {
- accessToken: token,
- accountId: profile.sso_account_id,
- roleName: profile.sso_role_name,
- };
- if (!self.service || self.service.config.region !== profile.sso_region) {
- self.service = new AWS.SSO({
- region: profile.sso_region,
- httpOptions: self.httpOptions,
- });
- }
- self.service.getRoleCredentials(request, function(err, data) {
- if (err || !data || !data.roleCredentials) {
- callback(AWS.util.error(
- err || new Error('Please log in using "aws sso login"'),
- { code: self.errorCode }
- ), null);
- } else if (!data.roleCredentials.accessKeyId || !data.roleCredentials.secretAccessKey || !data.roleCredentials.sessionToken || !data.roleCredentials.expiration) {
- throw AWS.util.error(new Error(
- 'SSO returns an invalid temporary credential.'
- ));
- } else {
- self.expired = false;
- self.accessKeyId = data.roleCredentials.accessKeyId;
- self.secretAccessKey = data.roleCredentials.secretAccessKey;
- self.sessionToken = data.roleCredentials.sessionToken;
- self.expireTime = new Date(data.roleCredentials.expiration);
- callback(null);
- }
- });
- });
- } catch (err) {
- callback(err);
- }
- },
- /**
- * @private
- * Uses legacy file system retrieval or if sso-session is set,
- * use the SSOTokenProvider.
- *
- * @param {string} profileName - name of the profile.
- * @param {object} profile - profile data containing sso_session or sso_start_url etc.
- * @param {function} callback - called with (err, (string) token).
- *
- * @returns {void}
- */
- getToken: function getToken(profileName, profile, callback) {
- var self = this;
- if (profile.sso_session) {
- var _iniLoader = AWS.util.iniLoader;
- var ssoSessions = _iniLoader.loadSsoSessionsFrom();
- var ssoSession = ssoSessions[profile.sso_session];
- Object.assign(profile, ssoSession);
- var ssoTokenProvider = new AWS.SSOTokenProvider({
- profile: profileName,
- });
- ssoTokenProvider.load(function (err) {
- if (err) {
- return callback(err);
- }
- return callback(null, ssoTokenProvider.token);
- });
- return;
- }
- try {
- /**
- * The time window (15 mins) that SDK will treat the SSO token expires in before the defined expiration date in token.
- * This is needed because server side may have invalidated the token before the defined expiration date.
- */
- var EXPIRE_WINDOW_MS = 15 * 60 * 1000;
- var hasher = crypto.createHash('sha1');
- var fileName = hasher.update(profile.sso_start_url).digest('hex') + '.json';
- var cachePath = path.join(
- iniLoader.getHomeDir(),
- '.aws',
- 'sso',
- 'cache',
- fileName
- );
- var cacheFile = AWS.util.readFileSync(cachePath);
- var cacheContent = null;
- if (cacheFile) {
- cacheContent = JSON.parse(cacheFile);
- }
- if (!cacheContent) {
- throw AWS.util.error(
- new Error('Cached credentials not found under ' + this.profile + ' profile. Please make sure you log in with aws sso login first'),
- { code: self.errorCode }
- );
- }
- if (!cacheContent.startUrl || !cacheContent.region || !cacheContent.accessToken || !cacheContent.expiresAt) {
- throw AWS.util.error(
- new Error('Cached credentials are missing required properties. Try running aws sso login.')
- );
- }
- if (new Date(cacheContent.expiresAt).getTime() - Date.now() <= EXPIRE_WINDOW_MS) {
- throw AWS.util.error(new Error(
- 'The SSO session associated with this profile has expired. To refresh this SSO session run aws sso login with the corresponding profile.'
- ));
- }
- return callback(null, cacheContent.accessToken);
- } catch (err) {
- return callback(err, null);
- }
- },
- /**
- * Loads the credentials from the AWS SSO process
- *
- * @callback callback function(err)
- * Called after the AWS SSO process has been executed. When this
- * callback is called with no error, it means that the credentials
- * information has been loaded into the object (as the `accessKeyId`,
- * `secretAccessKey`, and `sessionToken` properties).
- * @param err [Error] if an error occurred, this value will be filled
- * @see get
- */
- refresh: function refresh(callback) {
- iniLoader.clearCachedFiles();
- this.coalesceRefresh(callback || AWS.util.fn.callback);
- },
- });
|