TokenManager.js 5.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. /* eslint complexity:[0,8] max-statements:[0,21] */
  2. var util = require('./util');
  3. var AuthSdkError = require('./errors/AuthSdkError');
  4. var storageUtil = require('./storageUtil');
  5. var Q = require('q');
  6. var Emitter = require('tiny-emitter');
  7. var config = require('./config');
  8. var storageBuilder = require('./storageBuilder');
  9. function emitExpired(tokenMgmtRef, key, token) {
  10. tokenMgmtRef.emitter.emit('expired', key, token);
  11. }
  12. function clearRefreshTimeout(tokenMgmtRef, key) {
  13. clearTimeout(tokenMgmtRef.refreshTimeouts[key]);
  14. delete tokenMgmtRef.refreshTimeouts[key];
  15. }
  16. function clearRefreshTimeoutAll(tokenMgmtRef) {
  17. var refreshTimeouts = tokenMgmtRef.refreshTimeouts;
  18. for(var key in refreshTimeouts) {
  19. if (!refreshTimeouts.hasOwnProperty(key)) {
  20. continue;
  21. }
  22. clearRefreshTimeout(tokenMgmtRef, key);
  23. }
  24. tokenMgmtRef.refreshTimeouts = {};
  25. }
  26. function setRefreshTimeout(sdk, tokenMgmtRef, storage, key, token) {
  27. var refreshWait = (token.expiresAt * 1000) - Date.now();
  28. if (refreshWait < 0) {
  29. // Already expired
  30. refreshWait = 0;
  31. }
  32. var refreshTimeout = setTimeout(function() {
  33. if (tokenMgmtRef.autoRefresh) {
  34. return refresh(sdk, tokenMgmtRef, storage, key);
  35. } else if (token.expiresAt * 1000 <= Date.now()) {
  36. remove(tokenMgmtRef, storage, key);
  37. emitExpired(tokenMgmtRef, key, token);
  38. }
  39. }, refreshWait);
  40. // Clear any existing timeout
  41. clearRefreshTimeout(tokenMgmtRef, key);
  42. // Add a new timeout
  43. tokenMgmtRef.refreshTimeouts[key] = refreshTimeout;
  44. }
  45. function setRefreshTimeoutAll(sdk, tokenMgmtRef, storage) {
  46. try {
  47. var tokenStorage = storage.getStorage();
  48. } catch(e) {
  49. // Any errors thrown on instantiation will not be caught,
  50. // because there are no listeners yet
  51. tokenMgmtRef.emitter.emit('error', e);
  52. return;
  53. }
  54. for(var key in tokenStorage) {
  55. if (!tokenStorage.hasOwnProperty(key)) {
  56. continue;
  57. }
  58. var token = tokenStorage[key];
  59. setRefreshTimeout(sdk, tokenMgmtRef, storage, key, token);
  60. }
  61. }
  62. function add(sdk, tokenMgmtRef, storage, key, token) {
  63. var tokenStorage = storage.getStorage();
  64. if (!util.isObject(token) ||
  65. !token.scopes ||
  66. (!token.expiresAt && token.expiresAt !== 0) ||
  67. (!token.idToken && !token.accessToken)) {
  68. throw new AuthSdkError('Token must be an Object with scopes, expiresAt, and an idToken or accessToken properties');
  69. }
  70. tokenStorage[key] = token;
  71. storage.setStorage(tokenStorage);
  72. setRefreshTimeout(sdk, tokenMgmtRef, storage, key, token);
  73. }
  74. function get(storage, key) {
  75. var tokenStorage = storage.getStorage();
  76. return tokenStorage[key];
  77. }
  78. function remove(tokenMgmtRef, storage, key) {
  79. // Clear any listener for this token
  80. clearRefreshTimeout(tokenMgmtRef, key);
  81. // Remove it from storage
  82. var tokenStorage = storage.getStorage();
  83. delete tokenStorage[key];
  84. storage.setStorage(tokenStorage);
  85. }
  86. function refresh(sdk, tokenMgmtRef, storage, key) {
  87. try {
  88. var token = get(storage, key);
  89. if (!token) {
  90. throw new AuthSdkError('The tokenManager has no token for the key: ' + key);
  91. }
  92. } catch (e) {
  93. return Q.reject(e);
  94. }
  95. // Remove existing autoRefresh timeout for this key
  96. clearRefreshTimeout(tokenMgmtRef, key);
  97. return sdk.token.refresh(token)
  98. .then(function(freshToken) {
  99. add(sdk, tokenMgmtRef, storage, key, freshToken);
  100. tokenMgmtRef.emitter.emit('refreshed', key, freshToken, token);
  101. return freshToken;
  102. })
  103. .fail(function(err) {
  104. if (err.name === 'OAuthError') {
  105. remove(tokenMgmtRef, storage, key);
  106. emitExpired(tokenMgmtRef, key, token);
  107. }
  108. throw err;
  109. });
  110. }
  111. function clear(tokenMgmtRef, storage) {
  112. clearRefreshTimeoutAll(tokenMgmtRef);
  113. storage.clearStorage();
  114. }
  115. function TokenManager(sdk, options) {
  116. options = options || {};
  117. options.storage = options.storage || 'localStorage';
  118. if (!options.autoRefresh && options.autoRefresh !== false) {
  119. options.autoRefresh = true;
  120. }
  121. if (options.storage === 'localStorage' && !storageUtil.browserHasLocalStorage()) {
  122. util.warn('This browser doesn\'t support localStorage. Switching to sessionStorage.');
  123. options.storage = 'sessionStorage';
  124. }
  125. if (options.storage === 'sessionStorage' && !storageUtil.browserHasSessionStorage()) {
  126. util.warn('This browser doesn\'t support sessionStorage. Switching to cookie-based storage.');
  127. options.storage = 'cookie';
  128. }
  129. var storage;
  130. switch(options.storage) {
  131. case 'localStorage':
  132. storage = storageBuilder(localStorage, config.TOKEN_STORAGE_NAME);
  133. break;
  134. case 'sessionStorage':
  135. storage = storageBuilder(sessionStorage, config.TOKEN_STORAGE_NAME);
  136. break;
  137. case 'cookie':
  138. storage = storageBuilder(storageUtil.getCookieStorage(), config.TOKEN_STORAGE_NAME);
  139. break;
  140. default:
  141. throw new AuthSdkError('Unrecognized storage option');
  142. }
  143. var tokenMgmtRef = {
  144. emitter: new Emitter(),
  145. autoRefresh: options.autoRefresh,
  146. refreshTimeouts: {}
  147. };
  148. this.add = util.bind(add, this, sdk, tokenMgmtRef, storage);
  149. this.get = util.bind(get, this, storage);
  150. this.remove = util.bind(remove, this, tokenMgmtRef, storage);
  151. this.clear = util.bind(clear, this, tokenMgmtRef, storage);
  152. this.refresh = util.bind(refresh, this, sdk, tokenMgmtRef, storage);
  153. this.on = util.bind(tokenMgmtRef.emitter.on, tokenMgmtRef.emitter);
  154. this.off = util.bind(tokenMgmtRef.emitter.off, tokenMgmtRef.emitter);
  155. setRefreshTimeoutAll(sdk, tokenMgmtRef, storage);
  156. }
  157. module.exports = TokenManager;