token.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. var AWS = require('./core');
  2. /**
  3. * Represents AWS token object, which contains {token}, and optional
  4. * {expireTime}.
  5. * Creating a `Token` object allows you to pass around your
  6. * token to configuration and service objects.
  7. *
  8. * Note that this class typically does not need to be constructed manually,
  9. * as the {AWS.Config} and {AWS.Service} classes both accept simple
  10. * options hashes with the two keys. The token from this object will be used
  11. * automatically in operations which require them.
  12. *
  13. * ## Expiring and Refreshing Token
  14. *
  15. * Occasionally token can expire in the middle of a long-running
  16. * application. In this case, the SDK will automatically attempt to
  17. * refresh the token from the storage location if the Token
  18. * class implements the {refresh} method.
  19. *
  20. * If you are implementing a token storage location, you
  21. * will want to create a subclass of the `Token` class and
  22. * override the {refresh} method. This method allows token to be
  23. * retrieved from the backing store, be it a file system, database, or
  24. * some network storage. The method should reset the token attributes
  25. * on the object.
  26. *
  27. * @!attribute token
  28. * @return [String] represents the literal token string. This will typically
  29. * be a base64 encoded string.
  30. * @!attribute expireTime
  31. * @return [Date] a time when token should be considered expired. Used
  32. * in conjunction with {expired}.
  33. * @!attribute expired
  34. * @return [Boolean] whether the token is expired and require a refresh. Used
  35. * in conjunction with {expireTime}.
  36. */
  37. AWS.Token = AWS.util.inherit({
  38. /**
  39. * Creates a Token object with a given set of information in options hash.
  40. * @option options token [String] represents the literal token string.
  41. * @option options expireTime [Date] field representing the time at which
  42. * the token expires.
  43. * @example Create a token object
  44. * var token = new AWS.Token({ token: 'token' });
  45. */
  46. constructor: function Token(options) {
  47. // hide token from being displayed with util.inspect
  48. AWS.util.hideProperties(this, ['token']);
  49. this.expired = false;
  50. this.expireTime = null;
  51. this.refreshCallbacks = [];
  52. if (arguments.length === 1) {
  53. var options = arguments[0];
  54. this.token = options.token;
  55. this.expireTime = options.expireTime;
  56. }
  57. },
  58. /**
  59. * @return [Integer] the number of seconds before {expireTime} during which
  60. * the token will be considered expired.
  61. */
  62. expiryWindow: 15,
  63. /**
  64. * @return [Boolean] whether the Token object should call {refresh}
  65. * @note Subclasses should override this method to provide custom refresh
  66. * logic.
  67. */
  68. needsRefresh: function needsRefresh() {
  69. var currentTime = AWS.util.date.getDate().getTime();
  70. var adjustedTime = new Date(currentTime + this.expiryWindow * 1000);
  71. if (this.expireTime && adjustedTime > this.expireTime)
  72. return true;
  73. return this.expired || !this.token;
  74. },
  75. /**
  76. * Gets the existing token, refreshing them if they are not yet loaded
  77. * or have expired. Users should call this method before using {refresh},
  78. * as this will not attempt to reload token when they are already
  79. * loaded into the object.
  80. *
  81. * @callback callback function(err)
  82. * When this callback is called with no error, it means either token
  83. * do not need to be refreshed or refreshed token information has
  84. * been loaded into the object (as the `token` property).
  85. * @param err [Error] if an error occurred, this value will be filled
  86. */
  87. get: function get(callback) {
  88. var self = this;
  89. if (this.needsRefresh()) {
  90. this.refresh(function(err) {
  91. if (!err) self.expired = false; // reset expired flag
  92. if (callback) callback(err);
  93. });
  94. } else if (callback) {
  95. callback();
  96. }
  97. },
  98. /**
  99. * @!method getPromise()
  100. * Returns a 'thenable' promise.
  101. * Gets the existing token, refreshing it if it's not yet loaded
  102. * or have expired. Users should call this method before using {refresh},
  103. * as this will not attempt to reload token when it's already
  104. * loaded into the object.
  105. *
  106. * Two callbacks can be provided to the `then` method on the returned promise.
  107. * The first callback will be called if the promise is fulfilled, and the second
  108. * callback will be called if the promise is rejected.
  109. * @callback fulfilledCallback function()
  110. * Called if the promise is fulfilled. When this callback is called, it means
  111. * either token does not need to be refreshed or refreshed token information
  112. * has been loaded into the object (as the `token` property).
  113. * @callback rejectedCallback function(err)
  114. * Called if the promise is rejected.
  115. * @param err [Error] if an error occurred, this value will be filled.
  116. * @return [Promise] A promise that represents the state of the `get` call.
  117. * @example Calling the `getPromise` method.
  118. * var promise = tokenProvider.getPromise();
  119. * promise.then(function() { ... }, function(err) { ... });
  120. */
  121. /**
  122. * @!method refreshPromise()
  123. * Returns a 'thenable' promise.
  124. * Refreshes the token. Users should call {get} before attempting
  125. * to forcibly refresh token.
  126. *
  127. * Two callbacks can be provided to the `then` method on the returned promise.
  128. * The first callback will be called if the promise is fulfilled, and the second
  129. * callback will be called if the promise is rejected.
  130. * @callback fulfilledCallback function()
  131. * Called if the promise is fulfilled. When this callback is called, it
  132. * means refreshed token information has been loaded into the object
  133. * (as the `token` property).
  134. * @callback rejectedCallback function(err)
  135. * Called if the promise is rejected.
  136. * @param err [Error] if an error occurred, this value will be filled.
  137. * @return [Promise] A promise that represents the state of the `refresh` call.
  138. * @example Calling the `refreshPromise` method.
  139. * var promise = tokenProvider.refreshPromise();
  140. * promise.then(function() { ... }, function(err) { ... });
  141. */
  142. /**
  143. * Refreshes the token. Users should call {get} before attempting
  144. * to forcibly refresh token.
  145. *
  146. * @callback callback function(err)
  147. * When this callback is called with no error, it means refreshed
  148. * token information has been loaded into the object (as the
  149. * `token` property).
  150. * @param err [Error] if an error occurred, this value will be filled
  151. * @note Subclasses should override this class to reset the
  152. * {token} on the token object and then call the callback with
  153. * any error information.
  154. * @see get
  155. */
  156. refresh: function refresh(callback) {
  157. this.expired = false;
  158. callback();
  159. },
  160. /**
  161. * @api private
  162. * @param callback
  163. */
  164. coalesceRefresh: function coalesceRefresh(callback, sync) {
  165. var self = this;
  166. if (self.refreshCallbacks.push(callback) === 1) {
  167. self.load(function onLoad(err) {
  168. AWS.util.arrayEach(self.refreshCallbacks, function(callback) {
  169. if (sync) {
  170. callback(err);
  171. } else {
  172. // callback could throw, so defer to ensure all callbacks are notified
  173. AWS.util.defer(function () {
  174. callback(err);
  175. });
  176. }
  177. });
  178. self.refreshCallbacks.length = 0;
  179. });
  180. }
  181. },
  182. /**
  183. * @api private
  184. * @param callback
  185. */
  186. load: function load(callback) {
  187. callback();
  188. }
  189. });
  190. /**
  191. * @api private
  192. */
  193. AWS.Token.addPromisesToClass = function addPromisesToClass(PromiseDependency) {
  194. this.prototype.getPromise = AWS.util.promisifyMethod('get', PromiseDependency);
  195. this.prototype.refreshPromise = AWS.util.promisifyMethod('refresh', PromiseDependency);
  196. };
  197. /**
  198. * @api private
  199. */
  200. AWS.Token.deletePromisesFromClass = function deletePromisesFromClass() {
  201. delete this.prototype.getPromise;
  202. delete this.prototype.refreshPromise;
  203. };
  204. AWS.util.addPromises(AWS.Token);