aws-sdk
Version:
AWS SDK for JavaScript
220 lines (204 loc) • 8.01 kB
JavaScript
var AWS = require('./core');
/**
* Represents AWS token object, which contains {token}, and optional
* {expireTime}.
* Creating a `Token` object allows you to pass around your
* token to configuration and service objects.
*
* Note that this class typically does not need to be constructed manually,
* as the {AWS.Config} and {AWS.Service} classes both accept simple
* options hashes with the two keys. The token from this object will be used
* automatically in operations which require them.
*
* ## Expiring and Refreshing Token
*
* Occasionally token can expire in the middle of a long-running
* application. In this case, the SDK will automatically attempt to
* refresh the token from the storage location if the Token
* class implements the {refresh} method.
*
* If you are implementing a token storage location, you
* will want to create a subclass of the `Token` class and
* override the {refresh} method. This method allows token to be
* retrieved from the backing store, be it a file system, database, or
* some network storage. The method should reset the token attributes
* on the object.
*
* @!attribute token
* @return [String] represents the literal token string. This will typically
* be a base64 encoded string.
* @!attribute expireTime
* @return [Date] a time when token should be considered expired. Used
* in conjunction with {expired}.
* @!attribute expired
* @return [Boolean] whether the token is expired and require a refresh. Used
* in conjunction with {expireTime}.
*/
AWS.Token = AWS.util.inherit({
/**
* Creates a Token object with a given set of information in options hash.
* @option options token [String] represents the literal token string.
* @option options expireTime [Date] field representing the time at which
* the token expires.
* @example Create a token object
* var token = new AWS.Token({ token: 'token' });
*/
constructor: function Token(options) {
// hide token from being displayed with util.inspect
AWS.util.hideProperties(this, ['token']);
this.expired = false;
this.expireTime = null;
this.refreshCallbacks = [];
if (arguments.length === 1) {
var options = arguments[0];
this.token = options.token;
this.expireTime = options.expireTime;
}
},
/**
* @return [Integer] the number of seconds before {expireTime} during which
* the token will be considered expired.
*/
expiryWindow: 15,
/**
* @return [Boolean] whether the Token object should call {refresh}
* @note Subclasses should override this method to provide custom refresh
* logic.
*/
needsRefresh: function needsRefresh() {
var currentTime = AWS.util.date.getDate().getTime();
var adjustedTime = new Date(currentTime + this.expiryWindow * 1000);
if (this.expireTime && adjustedTime > this.expireTime)
return true;
return this.expired || !this.token;
},
/**
* Gets the existing token, refreshing them if they are not yet loaded
* or have expired. Users should call this method before using {refresh},
* as this will not attempt to reload token when they are already
* loaded into the object.
*
* @callback callback function(err)
* When this callback is called with no error, it means either token
* do not need to be refreshed or refreshed token information has
* been loaded into the object (as the `token` property).
* @param err [Error] if an error occurred, this value will be filled
*/
get: function get(callback) {
var self = this;
if (this.needsRefresh()) {
this.refresh(function(err) {
if (!err) self.expired = false; // reset expired flag
if (callback) callback(err);
});
} else if (callback) {
callback();
}
},
/**
* @!method getPromise()
* Returns a 'thenable' promise.
* Gets the existing token, refreshing it if it's not yet loaded
* or have expired. Users should call this method before using {refresh},
* as this will not attempt to reload token when it's already
* loaded into the object.
*
* Two callbacks can be provided to the `then` method on the returned promise.
* The first callback will be called if the promise is fulfilled, and the second
* callback will be called if the promise is rejected.
* @callback fulfilledCallback function()
* Called if the promise is fulfilled. When this callback is called, it means
* either token does not need to be refreshed or refreshed token information
* has been loaded into the object (as the `token` property).
* @callback rejectedCallback function(err)
* Called if the promise is rejected.
* @param err [Error] if an error occurred, this value will be filled.
* @return [Promise] A promise that represents the state of the `get` call.
* @example Calling the `getPromise` method.
* var promise = tokenProvider.getPromise();
* promise.then(function() { ... }, function(err) { ... });
*/
/**
* @!method refreshPromise()
* Returns a 'thenable' promise.
* Refreshes the token. Users should call {get} before attempting
* to forcibly refresh token.
*
* Two callbacks can be provided to the `then` method on the returned promise.
* The first callback will be called if the promise is fulfilled, and the second
* callback will be called if the promise is rejected.
* @callback fulfilledCallback function()
* Called if the promise is fulfilled. When this callback is called, it
* means refreshed token information has been loaded into the object
* (as the `token` property).
* @callback rejectedCallback function(err)
* Called if the promise is rejected.
* @param err [Error] if an error occurred, this value will be filled.
* @return [Promise] A promise that represents the state of the `refresh` call.
* @example Calling the `refreshPromise` method.
* var promise = tokenProvider.refreshPromise();
* promise.then(function() { ... }, function(err) { ... });
*/
/**
* Refreshes the token. Users should call {get} before attempting
* to forcibly refresh token.
*
* @callback callback function(err)
* When this callback is called with no error, it means refreshed
* token information has been loaded into the object (as the
* `token` property).
* @param err [Error] if an error occurred, this value will be filled
* @note Subclasses should override this class to reset the
* {token} on the token object and then call the callback with
* any error information.
* @see get
*/
refresh: function refresh(callback) {
this.expired = false;
callback();
},
/**
* @api private
* @param callback
*/
coalesceRefresh: function coalesceRefresh(callback, sync) {
var self = this;
if (self.refreshCallbacks.push(callback) === 1) {
self.load(function onLoad(err) {
AWS.util.arrayEach(self.refreshCallbacks, function(callback) {
if (sync) {
callback(err);
} else {
// callback could throw, so defer to ensure all callbacks are notified
AWS.util.defer(function () {
callback(err);
});
}
});
self.refreshCallbacks.length = 0;
});
}
},
/**
* @api private
* @param callback
*/
load: function load(callback) {
callback();
}
});
/**
* @api private
*/
AWS.Token.addPromisesToClass = function addPromisesToClass(PromiseDependency) {
this.prototype.getPromise = AWS.util.promisifyMethod('get', PromiseDependency);
this.prototype.refreshPromise = AWS.util.promisifyMethod('refresh', PromiseDependency);
};
/**
* @api private
*/
AWS.Token.deletePromisesFromClass = function deletePromisesFromClass() {
delete this.prototype.getPromise;
delete this.prototype.refreshPromise;
};
AWS.util.addPromises(AWS.Token);