authentication-context.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. /*
  2. * @copyright
  3. * Copyright © Microsoft Open Technologies, Inc.
  4. *
  5. * All Rights Reserved
  6. *
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http: *www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
  14. * OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
  15. * ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A
  16. * PARTICULAR PURPOSE, MERCHANTABILITY OR NON-INFRINGEMENT.
  17. *
  18. * See the Apache License, Version 2.0 for the specific language
  19. * governing permissions and limitations under the License.
  20. */
  21. 'use strict';
  22. var argument = require('./argument');
  23. var Authority = require('./authority').Authority;
  24. var TokenRequest = require('./token-request');
  25. var CodeRequest = require('./code-request');
  26. var createLogContext = require('./log').createLogContext;
  27. var MemoryCache = require('./memory-cache');
  28. var util = require('./util');
  29. var constants = require('./constants');
  30. var globalADALOptions = {};
  31. var globalCache = new MemoryCache();
  32. /**
  33. * This function is used to add or remove entries from a TokenCache
  34. * @typedef {function} ModifyCacheFunction
  35. * @param {Array} entries An array of entries to either add or remove from the TokenCache
  36. * @param {function} callback A callback function to call when the add or remove operation is complete.
  37. * This function can take a single error argument.
  38. */
  39. /**
  40. * This function is called by a TokenCache when a find operation completes.
  41. * @callback TokenCacheFindCallback
  42. * @param {Error} [err] If an error occurred during the find operation then it should be passed here.
  43. * @param {Array} [entries] If the find operation was succesful then the matched entries should be returned here.
  44. */
  45. /**
  46. * This function is called by ADAL to query a TokenCache. The query parameter is
  47. * a flat object which must be compared against entries in the cache. An entry
  48. * matches if it has all of the fields in the query and the values of those fields match
  49. * the values in the query. A matched object may have more fields than the query object.
  50. * @typedef {function} FindCacheFunction
  51. * @param {object} query This object should be compared to cache entries and matches should be returned.
  52. * @param {TokenCacheFindCallback} callback This callback should be called when the find operation is complete.
  53. */
  54. /**
  55. * This is an interface that can be implemented to provide custom token cache persistence.
  56. * @public
  57. * @class TokenCache
  58. * @property {ModifyCacheFunction} add Called by ADAL when entries should be added to the cache.
  59. * @property {ModifyCacheFunction} remove Called by ADAL when entries should be removed from the cache.
  60. * @property {FindCacheFunction} find Called when ADAL needs to find entries in the cache.
  61. */
  62. /**
  63. * Creates a new AuthenticationContext object. By default the authority will be checked against
  64. * a list of known Azure Active Directory authorities. If the authority is not recognized as
  65. * one of these well known authorities then token acquisition will fail. This behavior can be
  66. * turned off via the validateAuthority parameter below.
  67. * @constructor
  68. * @param {string} authority A URL that identifies a token authority.
  69. * @param {bool} [validateAuthority] Turns authority validation on or off. This parameter default to true.
  70. * @param {TokenCache} [cache] Sets the token cache used by this AuthenticationContext instance. If this parameter is not set
  71. * then a default, in memory cache is used. The default in memory cache is global to the process and is
  72. * shared by all AuthenticationContexts that are created with an empty cache parameter. To control the
  73. * scope and lifetime of a cache you can either create a {@link MemoryCache} instance and pass it when
  74. * constructing an AuthenticationContext or implement a custom {@link TokenCache} and pass that. Cache
  75. * instances passed at AuthenticationContext construction time are only used by that instance of
  76. * the AuthenticationContext and are not shared unless it has been manually passed during the
  77. * construction of other AuthenticationContexts.
  78. *
  79. */
  80. function AuthenticationContext(authority, validateAuthority, cache) {
  81. var validate = (validateAuthority === undefined || validateAuthority === null || validateAuthority);
  82. this._authority = new Authority(authority, validate);
  83. this._oauth2client = null;
  84. this._correlationId = null;
  85. this._callContext = { options : globalADALOptions };
  86. this._cache = cache || globalCache;
  87. this._tokenRequestWithUserCode = {};
  88. }
  89. /**
  90. * Gets the authority url this AuthenticationContext was constructed with.
  91. * @instance
  92. * @memberOf AuthenticationContext
  93. * @type {string}
  94. * @name authority
  95. */
  96. Object.defineProperty(AuthenticationContext.prototype, 'authority', {
  97. get: function () {
  98. return this._authority.url;
  99. }
  100. });
  101. /**
  102. * Gets/Sets the correlation id that will be used for the next acquireToken request.
  103. * @instance
  104. * @memberOf AuthenticationContext
  105. * @type {string}
  106. * @name correlationId
  107. */
  108. Object.defineProperty(AuthenticationContext.prototype, 'correlationId', {
  109. get: function () {
  110. return this._correlationId;
  111. },
  112. set: function (id) {
  113. this._correlationId = id;
  114. }
  115. });
  116. /**
  117. * Get/Sets options that are applied to requests generated by this AuthenticationContext instance.
  118. * @instance
  119. * @memberOf AuthenticationContext
  120. * @type {object}
  121. * @name options
  122. */
  123. Object.defineProperty(AuthenticationContext.prototype, 'options', {
  124. get: function() {
  125. return this._callContext.options;
  126. },
  127. set: function (value) {
  128. this._callContext.options = value;
  129. }
  130. });
  131. /**
  132. * Get the token cache used by this AuthenticationContext instance.
  133. * @instance
  134. * @memberOf AuthenticationContext
  135. * @type {object}
  136. * @name cache
  137. */
  138. Object.defineProperty(AuthenticationContext.prototype, 'cache', {
  139. get: function() {
  140. return this._cache;
  141. },
  142. });
  143. /**
  144. * This will be returned in case the OAuth 2 service returns an error.
  145. * @typedef ErrorResponse
  146. * @property {string} [error] A server error.
  147. * @property {string} [errorDescription] A description of the error returned.
  148. */
  149. /**
  150. * Contains tokens and metadata upon successful completion of an acquireToken call.
  151. * @typedef TokenResponse
  152. * @property {string} tokenType The type of token returned.
  153. * @property {string} accessToken The returned access token.
  154. * @property {string} [refreshToken] A refresh token.
  155. * @property {Date} [createdOn] The date on which the access token was created.
  156. * @property {Date} expiresOn The Date on which the access token expires.
  157. * @property {int} expiresIn The amount of time, in seconds, for which the token is valid.
  158. * @property {string} [userId] An id for the user. May be a displayable value if is_user_id_displayable is true.
  159. * @property {bool} [isUserIdDisplayable] Indicates whether the user_id property will be meaningful if displayed to a user.
  160. * @property {string} [tenantId] The identifier of the tenant under which the access token was issued.
  161. * @property {string} [givenName] The given name of the principal represented by the access token.
  162. * @property {string} [familyName] The family name of the principal represented by the access token.
  163. * @property {string} [identityProvider] Identifies the identity provider that issued the access token.
  164. */
  165. /**
  166. * This is the callback that is passed to all acquireToken variants below.
  167. * @callback AcquireTokenCallback
  168. * @param {Error} [error] If the request fails this parameter will contain an Error object.
  169. * @param {TokenResponse|ErrorResponse} [response] On a succesful request returns a {@link TokenResposne}.
  170. */
  171. /**
  172. * This function implements code that is common to all acquireToken flows.
  173. * @private
  174. * @param {AcquireTokenCallback} callback
  175. * @param {Function} tokenFunction This is the function to call to actually acquire the token after common flow has completed.
  176. */
  177. AuthenticationContext.prototype._acquireToken = function(callback, tokenFunction) {
  178. var self = this;
  179. this._callContext._logContext = createLogContext(this.correlationId);
  180. this._authority.validate(this._callContext, function(err) {
  181. if (err) {
  182. callback(err);
  183. return;
  184. }
  185. tokenFunction.call(self);
  186. });
  187. };
  188. AuthenticationContext.prototype._acquireUserCode = function (callback, codeFunction) {
  189. var self = this;
  190. this._callContext._logContext = createLogContext(this.correlationId);
  191. this._authority.validate(this._callContext, function (err) {
  192. if (err) {
  193. callback(err);
  194. return;
  195. }
  196. codeFunction.call(self);
  197. });
  198. };
  199. /**
  200. * Gets a token for a given resource.
  201. * @param {string} resource A URI that identifies the resource for which the token is valid.
  202. * @param {string} [userId] The username of the user on behalf this application is authenticating.
  203. * @param {string} [clientId] The OAuth client id of the calling application.
  204. * @param {AcquireTokenCallback} callback The callback function.
  205. */
  206. AuthenticationContext.prototype.acquireToken = function(resource, userId, clientId, callback) {
  207. argument.validateCallbackType(callback);
  208. try {
  209. argument.validateStringParameter(resource, 'resource');
  210. argument.validateStringParameter(clientId, 'clientId');
  211. } catch(err) {
  212. callback(err);
  213. return;
  214. }
  215. this._acquireToken(callback, function() {
  216. var tokenRequest = new TokenRequest(this._callContext, this, clientId, resource);
  217. tokenRequest.getTokenFromCacheWithRefresh(userId, callback);
  218. });
  219. };
  220. /**
  221. * Gets a token for a given resource.
  222. * @param {string} resource A URI that identifies the resource for which the token is valid.
  223. * @param {string} username The username of the user on behalf this application is authenticating.
  224. * @param {string} password The password of the user named in the username parameter.
  225. * @param {string} clientId The OAuth client id of the calling application.
  226. * @param {AcquireTokenCallback} callback The callback function.
  227. */
  228. AuthenticationContext.prototype.acquireTokenWithUsernamePassword = function(resource, username, password, clientId, callback) {
  229. argument.validateCallbackType(callback);
  230. try {
  231. argument.validateStringParameter(resource, 'resource');
  232. argument.validateStringParameter(username, 'username');
  233. argument.validateStringParameter(password, 'password');
  234. argument.validateStringParameter(clientId, 'clientId');
  235. } catch(err) {
  236. callback(err);
  237. return;
  238. }
  239. this._acquireToken(callback, function() {
  240. var tokenRequest = new TokenRequest(this._callContext, this, clientId, resource);
  241. tokenRequest.getTokenWithUsernamePassword(username, password, callback);
  242. });
  243. };
  244. /**
  245. * Gets a token for a given resource.
  246. * @param {string} resource A URI that identifies the resource for which the token is valid.
  247. * @param {string} clientId The OAuth client id of the calling application.
  248. * @param {string} clientSecret The OAuth client secret of the calling application.
  249. * @param {AcquireTokenCallback} callback The callback function.
  250. */
  251. AuthenticationContext.prototype.acquireTokenWithClientCredentials = function(resource, clientId, clientSecret, callback) {
  252. argument.validateCallbackType(callback);
  253. try {
  254. argument.validateStringParameter(resource, 'resource');
  255. argument.validateStringParameter(clientId, 'clientId');
  256. argument.validateStringParameter(clientSecret, 'clientSecret');
  257. } catch (err) {
  258. callback(err);
  259. return;
  260. }
  261. this._acquireToken(callback, function() {
  262. var tokenRequest = new TokenRequest(this._callContext, this, clientId, resource);
  263. tokenRequest.getTokenWithClientCredentials(clientSecret, callback);
  264. });
  265. };
  266. /**
  267. * Gets a token for a given resource.
  268. * @param {string} authorizationCode An authorization code returned from a client.
  269. * @param {string} redirectUri The redirect uri that was used in the authorize call.
  270. * @param {string} resource A URI that identifies the resource for which the token is valid.
  271. * @param {string} clientId The OAuth client id of the calling application.
  272. * @param {string} clientSecret The OAuth client secret of the calling application.
  273. * @param {AcquireTokenCallback} callback The callback function.
  274. */
  275. AuthenticationContext.prototype.acquireTokenWithAuthorizationCode = function(authorizationCode, redirectUri, resource, clientId, clientSecret, callback) {
  276. argument.validateCallbackType(callback);
  277. try {
  278. argument.validateStringParameter(resource, 'resource');
  279. argument.validateStringParameter(authorizationCode, 'authorizationCode');
  280. argument.validateStringParameter(redirectUri, 'redirectUri');
  281. argument.validateStringParameter(clientId, 'clientId');
  282. } catch(err) {
  283. callback(err);
  284. return;
  285. }
  286. this._acquireToken(callback, function() {
  287. var tokenRequest = new TokenRequest(this._callContext, this, clientId, resource, redirectUri);
  288. tokenRequest.getTokenWithAuthorizationCode(authorizationCode, clientSecret, callback);
  289. });
  290. };
  291. /**
  292. * Gets a new access token via a previously issued refresh token.
  293. * @param {string} refreshToken A refresh token returned in a tokne response from a previous invocation of acquireToken.
  294. * @param {string} clientId The OAuth client id of the calling application.
  295. * @param {string} [clientSecret] The OAuth client secret of the calling application. (Note: this parameter is a late addition.
  296. * This parameter may be ommitted entirely so that applications built before this change will continue
  297. * to work unchanged.)
  298. * @param {string} resource The OAuth resource for which a token is being request. This parameter is optional and can be set to null.
  299. * @param {AcquireTokenCallback} callback The callback function.
  300. */
  301. AuthenticationContext.prototype.acquireTokenWithRefreshToken = function(refreshToken, clientId, clientSecret, resource, callback) {
  302. // Fix up the arguments. Older clients may pass fewer arguments as the clientSecret paramter did not always exist.
  303. // The code needs to make adjustments for those clients.
  304. var clientSecretPresent = (5 === arguments.length);
  305. var actualClientSecret = clientSecretPresent ? clientSecret : null;
  306. var actualCallback = clientSecretPresent ? arguments[4] : arguments[3];
  307. var actualResource = clientSecretPresent ? arguments[3] : arguments[2];
  308. argument.validateCallbackType(actualCallback);
  309. try {
  310. argument.validateStringParameter(refreshToken, 'refreshToken');
  311. argument.validateStringParameter(clientId, 'clientId');
  312. } catch(err) {
  313. callback(err);
  314. return;
  315. }
  316. this._acquireToken(callback, function() {
  317. var tokenRequest = new TokenRequest(this._callContext, this, clientId, actualResource);
  318. tokenRequest.getTokenWithRefreshToken(refreshToken, actualClientSecret, actualCallback);
  319. });
  320. };
  321. /**
  322. * Gets a new access token using via a certificate credential.
  323. * @param {string} resource A URI that identifies the resource for which the token is valid.
  324. * @param {string} clientId The OAuth client id of the calling application.
  325. * @param {string} certificate A PEM encoded certificate private key.
  326. * @param {string} thumbprint A hex encoded thumbprint of the certificate.
  327. * @param {AcquireTokenCallback} callback The callback function.
  328. */
  329. AuthenticationContext.prototype.acquireTokenWithClientCertificate = function(resource, clientId, certificate, thumbprint, callback) {
  330. argument.validateCallbackType(callback);
  331. try {
  332. argument.validateStringParameter(resource, 'resource');
  333. argument.validateStringParameter(certificate, 'certificate');
  334. argument.validateStringParameter(thumbprint, 'thumbprint');
  335. } catch(err) {
  336. callback(err);
  337. return;
  338. }
  339. this._acquireToken(callback, function() {
  340. var tokenRequest = new TokenRequest(this._callContext, this, clientId, resource);
  341. tokenRequest.getTokenWithCertificate(certificate, thumbprint, callback);
  342. });
  343. };
  344. /**
  345. * Gets the userCodeInfo which contains user_code, device_code for authenticating user on device.
  346. * @param {string} resource A URI that identifies the resource for which the device_code and user_code is valid for.
  347. * @param {string} clientId The OAuth client id of the calling application.
  348. * @param {string} language The language code specifying how the message should be localized to.
  349. * @param {AcquireTokenCallback} callback The callback function.
  350. */
  351. AuthenticationContext.prototype.acquireUserCode = function(resource, clientId, language, callback) {
  352. argument.validateCallbackType(callback);
  353. try {
  354. argument.validateStringParameter(resource, 'resource');
  355. argument.validateStringParameter(clientId, 'clientId');
  356. } catch (err) {
  357. callback(err);
  358. return;
  359. }
  360. this._acquireUserCode(callback, function () {
  361. var codeRequest = new CodeRequest(this._callContext, this, clientId, resource);
  362. codeRequest.getUserCodeInfo(language, callback);
  363. });
  364. };
  365. /**
  366. * Gets a new access token using via a device code.
  367. * @note This method doesn't look up the cache, it only stores the returned token into cache. To look up cache before making a new request,
  368. * please use acquireToken.
  369. * @param {string} clientId The OAuth client id of the calling application.
  370. * @param {object} userCodeInfo Contains device_code, retry interval, and expire time for the request for get the token.
  371. * @param {AcquireTokenCallback} callback The callback function.
  372. */
  373. AuthenticationContext.prototype.acquireTokenWithDeviceCode = function(resource, clientId, userCodeInfo, callback){
  374. argument.validateCallbackType(callback);
  375. try{
  376. argument.validateUserCodeInfo(userCodeInfo);
  377. } catch (err) {
  378. callback(err);
  379. return;
  380. }
  381. var self = this;
  382. this._acquireToken(callback, function() {
  383. var tokenRequest = new TokenRequest(this._callContext, this, clientId, resource, null);
  384. self._tokenRequestWithUserCode[userCodeInfo[constants.UserCodeResponseFields.DEVICE_CODE]] = tokenRequest;
  385. tokenRequest.getTokenWithDeviceCode(userCodeInfo, callback);
  386. })
  387. };
  388. /**
  389. * Cancels the polling request to get token with device code.
  390. * @param {object} userCodeInfo Contains device_code, retry interval, and expire time for the request for get the token.
  391. * @param {AcquireTokenCallback} callback The callback function.
  392. */
  393. AuthenticationContext.prototype.cancelRequestToGetTokenWithDeviceCode = function (userCodeInfo, callback) {
  394. argument.validateCallbackType(callback);
  395. try {
  396. argument.validateUserCodeInfo(userCodeInfo);
  397. } catch (err) {
  398. callback(err);
  399. return;
  400. }
  401. if (!this._tokenRequestWithUserCode || !this._tokenRequestWithUserCode[userCodeInfo[constants.UserCodeResponseFields.DEVICE_CODE]]) {
  402. callback(new Error('No acquireTokenWithDeviceCodeRequest existed to be cancelled'));
  403. return;
  404. }
  405. var tokenRequestToBeCancelled = this._tokenRequestWithUserCode[userCodeInfo[constants.UserCodeResponseFields.DEVICE_CODE]];
  406. tokenRequestToBeCancelled.cancelTokenRequestWithDeviceCode();
  407. delete this._tokenRequestWithUserCode[constants.UserCodeResponseFields.DEVICE_CODE];
  408. };
  409. var exports = {
  410. AuthenticationContext : AuthenticationContext,
  411. setGlobalADALOptions : function(options) {
  412. globalADALOptions = options;
  413. },
  414. getGlobalADALOptions : function() {
  415. return globalADALOptions;
  416. }
  417. };
  418. util.adalInit();
  419. module.exports = exports;