token-request.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607
  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 constants = require('./constants');
  23. var CacheDriver = require('./cache-driver');
  24. var Logger = require('./log').Logger;
  25. var Mex = require('./mex');
  26. var OAuth2Client = require('./oauth2client');
  27. var SelfSignedJwt = require('./self-signed-jwt');
  28. var UserRealm = require('./user-realm');
  29. var WSTrustRequest = require('./wstrust-request');
  30. var OAuth2Parameters = constants.OAuth2.Parameters;
  31. var TokenResponseFields = constants.TokenResponseFields;
  32. var OAuth2GrantType = constants.OAuth2.GrantType;
  33. var OAuth2Scope = constants.OAuth2.Scope;
  34. var Saml = constants.Saml;
  35. var AccountType = constants.UserRealm.AccountType;
  36. var WSTrustVersion = constants.WSTrustVersion;
  37. var DeviceCodeResponseParameters = constants.UserCodeResponseFields;
  38. /**
  39. * Constructs a new TokenRequest object.
  40. * @constructor
  41. * @private
  42. * @param {object} callContext Contains any context information that applies to the request.
  43. * @param {AuthenticationContext} authenticationContext
  44. * @param {string} resource
  45. * @param {string} clientId
  46. * @param {string} redirectUri
  47. */
  48. function TokenRequest(callContext, authenticationContext, clientId, resource, redirectUri) {
  49. this._log = new Logger('TokenRequest', callContext._logContext);
  50. this._callContext = callContext;
  51. this._authenticationContext = authenticationContext;
  52. this._resource = resource;
  53. this._clientId = clientId;
  54. this._redirectUri = redirectUri;
  55. // This should be set at the beginning of getToken
  56. // functions that have a userId.
  57. this._userId = null;
  58. this._userRealm = null;
  59. this._pollingClient = {};
  60. }
  61. TokenRequest.prototype._createUserRealmRequest = function(username) {
  62. return new UserRealm(this._callContext, username, this._authenticationContext.authority);
  63. };
  64. TokenRequest.prototype._createMex = function(mexEndpoint) {
  65. return new Mex(this._callContext, mexEndpoint);
  66. };
  67. TokenRequest.prototype._createWSTrustRequest = function(wstrustEndpoint, appliesTo, wstrustEndpointVersion) {
  68. return new WSTrustRequest(this._callContext, wstrustEndpoint, appliesTo, wstrustEndpointVersion);
  69. };
  70. TokenRequest.prototype._createOAuth2Client = function() {
  71. return new OAuth2Client(this._callContext, this._authenticationContext._authority);
  72. };
  73. TokenRequest.prototype._createSelfSignedJwt = function() {
  74. return new SelfSignedJwt(this._callContext, this._authenticationContext._authority, this._clientId);
  75. };
  76. TokenRequest.prototype._oauthGetToken = function(oauthParameters, callback) {
  77. var client = this._createOAuth2Client();
  78. client.getToken(oauthParameters, callback);
  79. };
  80. TokenRequest.prototype._oauthGetTokenByPolling = function(oauthParameters, refresh_interval, expires_in, callback){
  81. var client = this._createOAuth2Client();
  82. client.getTokenWithPolling(oauthParameters, refresh_interval, expires_in, callback);
  83. this._pollingClient = client;
  84. }
  85. TokenRequest.prototype._createCacheDriver = function() {
  86. return new CacheDriver(
  87. this._callContext,
  88. this._authenticationContext.authority,
  89. this._resource,
  90. this._clientId,
  91. this._authenticationContext.cache,
  92. this._getTokenWithTokenResponse.bind(this)
  93. );
  94. };
  95. /**
  96. * Used by the cache driver to refresh tokens.
  97. * @param {TokenResponse} entry A token response to refresh.
  98. * @param {string} resource The resource for which to get the token.
  99. * @param {AcquireTokenCallback} callback
  100. */
  101. TokenRequest.prototype._getTokenWithTokenResponse = function(entry, resource, callback) {
  102. this._log.verbose('Called to refresh a token from the cache.');
  103. var refreshToken = entry[TokenResponseFields.REFRESH_TOKEN];
  104. this._getTokenWithRefreshToken(refreshToken, resource, null, callback);
  105. };
  106. TokenRequest.prototype._createCacheQuery = function() {
  107. var query = {
  108. clientId : this._clientId
  109. };
  110. if (this._userId) {
  111. query.userId = this._userId;
  112. } else {
  113. this._log.verbose('No userId passed for cache query.');
  114. }
  115. return query;
  116. };
  117. TokenRequest.prototype._getTokenWithCacheWrapper = function(callback, getTokenFunc) {
  118. var self = this;
  119. this._cacheDriver = this._createCacheDriver();
  120. var cacheQuery = this._createCacheQuery();
  121. this._cacheDriver.find(cacheQuery, function(err, token) {
  122. if (err) {
  123. self._log.warn('Attempt to look for token in cahce resulted in Error');
  124. self._log.warn('Attempt to look for token in cache resulted in Error: ' + err.stack, true);
  125. }
  126. if (!token) {
  127. self._log.verbose('No appropriate cached token found.');
  128. getTokenFunc.call(self, function(err, tokenResponse) {
  129. if (err) {
  130. self._log.verbose('getTokenFunc returned with err');
  131. callback(err, tokenResponse);
  132. return;
  133. }
  134. self._log.verbose('Successfully retrieved token from authority');
  135. self._cacheDriver.add(tokenResponse, function() {
  136. callback(null, tokenResponse);
  137. });
  138. });
  139. } else {
  140. self._log.info('Returning cached token.');
  141. callback(err, token);
  142. }
  143. });
  144. };
  145. /**
  146. * Store token into cache.
  147. * @param {object} tokenResponse Token response to be added into the cache.
  148. */
  149. TokenRequest.prototype._addTokenIntoCache = function(tokenResponse, callback) {
  150. this._cacheDriver = this._createCacheDriver();
  151. this._log.verbose('Storing retrieved token into cache');
  152. this._cacheDriver.add(tokenResponse, function(err) {
  153. callback(err, tokenResponse);
  154. });
  155. };
  156. /**
  157. * Adds an OAuth parameter to the paramters object if the parameter is
  158. * not null or undefined.
  159. * @private
  160. * @param {object} parameters OAuth parameters object.
  161. * @param {string} key A member of the OAuth2Parameters constants.
  162. * @param {object} value
  163. */
  164. function _addParameterIfAvailable(parameters, key, value) {
  165. if (value) {
  166. parameters[key] = value;
  167. }
  168. }
  169. /**
  170. * Creates a set of basic, common, OAuthParameters based on values that the TokenRequest
  171. * was created with.
  172. * @private
  173. * @param {string} grantType A member of the OAuth2GrantType constants.
  174. * @return {object}
  175. */
  176. TokenRequest.prototype._createOAuthParameters = function(grantType) {
  177. var oauthParameters = {};
  178. oauthParameters[OAuth2Parameters.GRANT_TYPE] = grantType;
  179. if (OAuth2GrantType.AUTHORIZATION_CODE !== grantType &&
  180. OAuth2GrantType.CLIENT_CREDENTIALS !== grantType &&
  181. OAuth2GrantType.DEVICE_CODE != grantType) {
  182. oauthParameters[OAuth2Parameters.SCOPE] = OAuth2Scope.OPENID;
  183. }
  184. _addParameterIfAvailable(oauthParameters, OAuth2Parameters.CLIENT_ID, this._clientId);
  185. _addParameterIfAvailable(oauthParameters, OAuth2Parameters.RESOURCE, this._resource);
  186. _addParameterIfAvailable(oauthParameters, OAuth2Parameters.REDIRECT_URI, this._redirectUri);
  187. return oauthParameters;
  188. };
  189. /**
  190. * Get's a token from AAD using a username and password
  191. * @private
  192. * @param {string} username
  193. * @param {string} password
  194. * @param {AcquireTokenCallback} callback
  195. */
  196. TokenRequest.prototype._getTokenUsernamePasswordManaged = function(username, password, callback) {
  197. this._log.verbose('Acquiring token with username password for managed user');
  198. var oauthParameters = this._createOAuthParameters(OAuth2GrantType.PASSWORD);
  199. oauthParameters[OAuth2Parameters.PASSWORD] = password;
  200. oauthParameters[OAuth2Parameters.USERNAME] = username;
  201. this._oauthGetToken(oauthParameters, callback);
  202. };
  203. /**
  204. * Determines the OAuth SAML grant type to use based on the passed in TokenType
  205. * that was returned from a RSTR.
  206. * @param {string} wstrustResponse RSTR token type.
  207. * @return {string} An OAuth grant type.
  208. */
  209. TokenRequest.prototype._getSamlGrantType = function(wstrustResponse) {
  210. var tokenType = wstrustResponse.tokenType;
  211. switch (tokenType) {
  212. case Saml.TokenTypeV1:
  213. return OAuth2GrantType.SAML1;
  214. case Saml.TokenTypeV2:
  215. return OAuth2GrantType.SAML2;
  216. default:
  217. throw this._log.createError('RSTR returned unknown token type: ' + tokenType);
  218. }
  219. };
  220. /**
  221. * Performs an OAuth SAML Assertion grant type exchange. Uses a SAML token as the credential for getting
  222. * an OAuth access token.
  223. * @param {WSTrustResponse} wstrustResponse A response from a WSTrustRequest
  224. * @param {AcquireTokenCallback} callback callback
  225. */
  226. TokenRequest.prototype._performWSTrustAssertionOAuthExchange = function(wstrustResponse, callback) {
  227. this._log.verbose('Performing OAuth assertion grant type exchange.');
  228. var oauthParameters;
  229. try {
  230. var grantType = this._getSamlGrantType(wstrustResponse);
  231. var assertion = new Buffer(wstrustResponse.token).toString('base64');
  232. oauthParameters = this._createOAuthParameters(grantType);
  233. oauthParameters[OAuth2Parameters.ASSERTION] = assertion;
  234. } catch (err) {
  235. callback(err);
  236. return;
  237. }
  238. this._oauthGetToken(oauthParameters, callback);
  239. };
  240. /**
  241. * Exchange a username and password for a SAML token from an ADFS instance via WSTrust.
  242. * @param {string} wstrustEndpoint An url of an ADFS WSTrust endpoint.
  243. * @param {string} wstrustEndpointVersion The version of the wstrust endpoint.
  244. * @param {string} username username
  245. * @param {string} password password
  246. * @param {AcquireTokenCallback} callback callback
  247. */
  248. TokenRequest.prototype._performWSTrustExchange = function(wstrustEndpoint, wstrustEndpointVersion, username, password, callback) {
  249. var self = this;
  250. var wstrust = this._createWSTrustRequest(wstrustEndpoint, 'urn:federation:MicrosoftOnline', wstrustEndpointVersion);
  251. wstrust.acquireToken(username, password, function(rstErr, response) {
  252. if (rstErr) {
  253. callback(rstErr);
  254. return;
  255. }
  256. if (!response.token) {
  257. var rstrErr = self._log.createError('Unsucessful RSTR.\n\terror code: ' + response.errorCode + '\n\tfaultMessage: ' + response.faultMessage, true);
  258. callback(rstrErr);
  259. return;
  260. }
  261. callback(null, response);
  262. });
  263. };
  264. /**
  265. * Given a username and password this method invokes a WSTrust and OAuth exchange to get an access token.
  266. * @param {string} wstrustEndpoint An url of an ADFS WSTrust endpoint.
  267. * @param {string} username username
  268. * @param {string} password password
  269. * @param {AcquireTokenCallback} callback callback
  270. */
  271. TokenRequest.prototype._performUsernamePasswordForAccessTokenExchange = function(wstrustEndpoint, wstrustEndpointVersion, username, password, callback) {
  272. var self = this;
  273. this._performWSTrustExchange(wstrustEndpoint, wstrustEndpointVersion, username, password, function(err, wstrustResponse) {
  274. if (err) {
  275. callback(err);
  276. return;
  277. }
  278. self._performWSTrustAssertionOAuthExchange(wstrustResponse, callback);
  279. });
  280. };
  281. /**
  282. * Returns an Error object indicating that AAD did not return a WSTrust endpoint.
  283. * @return {Error}
  284. */
  285. TokenRequest.prototype._createADWSTrustEndpointError = function() {
  286. return this._log.createError('AAD did not return a WSTrust endpoint. Unable to proceed.');
  287. };
  288. /**
  289. * Gets an OAuth access token using a username and password via a federated ADFS instance.
  290. * @param {string} username username
  291. * @param {string} password password
  292. * @param {AcquireTokenCallback} callback callback
  293. */
  294. TokenRequest.prototype._getTokenUsernamePasswordFederated = function(username, password, callback) {
  295. this._log.verbose('Acquiring token with username password for federated user');
  296. var self = this;
  297. if (!this._userRealm.federationMetadataUrl) {
  298. this._log.warn('Unable to retrieve federationMetadataUrl from AAD. Attempting fallback to AAD supplied endpoint.');
  299. if (!this._userRealm.federationActiveAuthUrl) {
  300. callback(this._createADWSTrustEndpointError());
  301. return;
  302. }
  303. var wstrustVersion = this._parseWStrustVersionFromFederationActiveAuthUrl(this._userRealm.federationActiveAuthUrl);
  304. this._log.verbose('Wstrust endpoint version is: ' + wstrustVersion);
  305. this._performUsernamePasswordForAccessTokenExchange(this._userRealm.federationActiveAuthUrl, wstrustVersion, username, password, callback);
  306. return;
  307. } else {
  308. var mexEndpoint = this._userRealm.federationMetadataUrl;
  309. this._log.verbose('Attempting mex');
  310. this._log.verbose('Attempting mex at: ' + mexEndpoint, true);
  311. var mex = this._createMex(mexEndpoint);
  312. mex.discover(function(mexErr) {
  313. var wstrustEndpoint;
  314. wstrustVersion = WSTrustVersion.UNDEFINED;
  315. if (mexErr) {
  316. self._log.warn('MEX exchange failed. Attempting fallback to AAD supplied endpoint.');
  317. wstrustEndpoint = self._userRealm.federationActiveAuthUrl;
  318. wstrustVersion = self._parseWStrustVersionFromFederationActiveAuthUrl(self._userRealm.federationActiveAuthUrl);
  319. if (!wstrustEndpoint) {
  320. callback(self._createADWSTrustEndpointError());
  321. return;
  322. }
  323. } else {
  324. wstrustEndpoint = mex.usernamePasswordPolicy.url;
  325. wstrustVersion = mex.usernamePasswordPolicy.version;
  326. }
  327. self._performUsernamePasswordForAccessTokenExchange(wstrustEndpoint, wstrustVersion, username, password, callback);
  328. return;
  329. });
  330. }
  331. };
  332. /**
  333. * Gets wstrust endpoint version from the federation active auth url.
  334. * @private
  335. * @param {string} federationActiveAuthUrl federationActiveAuthUrl
  336. * @return {object} The wstrust endpoint version.
  337. */
  338. TokenRequest.prototype._parseWStrustVersionFromFederationActiveAuthUrl = function(federationActiveAuthUrl) {
  339. var wstrust2005Regex = /[/trust]?[2005][/usernamemixed]?/;
  340. var wstrust13Regex = /[/trust]?[13][/usernamemixed]?/;
  341. if (wstrust2005Regex.exec(federationActiveAuthUrl)) {
  342. return WSTrustVersion.WSTRUST2005;
  343. }
  344. else if (wstrust13Regex.exec(federationActiveAuthUrl)) {
  345. return WSTrustVersion.WSTRUST13;
  346. }
  347. return WSTrustVersion.UNDEFINED;
  348. };
  349. /**
  350. * Decides whether the username represents a managed or a federated user and then
  351. * obtains a token using the appropriate protocol flow.
  352. * @private
  353. * @param {string} username
  354. * @param {string} password
  355. * @param {AcquireTokenCallback} callback
  356. */
  357. TokenRequest.prototype.getTokenWithUsernamePassword = function(username, password, callback) {
  358. this._log.info('Acquiring token with username password');
  359. this._userId = username;
  360. this._getTokenWithCacheWrapper(callback, function(getTokenCompleteCallback) {
  361. var self = this;
  362. if(this._authenticationContext._authority._isAdfsAuthority) {
  363. this._log.info('Skipping user realm discovery for ADFS authority');
  364. self._getTokenUsernamePasswordManaged(username, password, getTokenCompleteCallback);
  365. return;
  366. }
  367. this._userRealm = this._createUserRealmRequest(username);
  368. this._userRealm.discover(function(err) {
  369. if (err) {
  370. getTokenCompleteCallback(err);
  371. return;
  372. }
  373. switch(self._userRealm.accountType) {
  374. case AccountType.Managed:
  375. self._getTokenUsernamePasswordManaged(username, password, getTokenCompleteCallback);
  376. return;
  377. case AccountType.Federated:
  378. self._getTokenUsernamePasswordFederated(username, password, getTokenCompleteCallback);
  379. return;
  380. default:
  381. getTokenCompleteCallback(self._log.createError('Server returned an unknown AccountType: ' + self._userRealm.AccountType));
  382. }
  383. });
  384. });
  385. };
  386. /**
  387. * Obtains a token using client credentials
  388. * @private
  389. * @param {string} clientSecret
  390. * @param {AcquireTokenCallback} callback
  391. */
  392. TokenRequest.prototype.getTokenWithClientCredentials = function(clientSecret, callback) {
  393. this._log.info('Getting token with client credentials.');
  394. this._getTokenWithCacheWrapper(callback, function(getTokenCompleteCallback) {
  395. var oauthParameters = this._createOAuthParameters(OAuth2GrantType.CLIENT_CREDENTIALS);
  396. oauthParameters[OAuth2Parameters.CLIENT_SECRET] = clientSecret;
  397. this._oauthGetToken(oauthParameters, getTokenCompleteCallback);
  398. });
  399. };
  400. /**
  401. * Obtains a token using an authorization code.
  402. * @private
  403. * @param {string} authorizationCode
  404. * @param {string} clientSecret
  405. * @param {AcquireTokenCallback} callback
  406. */
  407. TokenRequest.prototype.getTokenWithAuthorizationCode = function(authorizationCode, clientSecret, callback) {
  408. this._log.info('Getting token with auth code.');
  409. var oauthParameters = this._createOAuthParameters(OAuth2GrantType.AUTHORIZATION_CODE);
  410. oauthParameters[OAuth2Parameters.CODE] = authorizationCode;
  411. oauthParameters[OAuth2Parameters.CLIENT_SECRET] = clientSecret;
  412. this._oauthGetToken(oauthParameters, callback);
  413. };
  414. /**
  415. * Obtains a token using a refresh token.
  416. * @param {string} refreshToken
  417. * @param {string} resource
  418. * @param {string} [clientSecret]
  419. * @param {AcquireTokenCallback} callback
  420. */
  421. TokenRequest.prototype._getTokenWithRefreshToken = function(refreshToken, resource, clientSecret, callback) {
  422. this._log.info('Getting a new token from a refresh token.');
  423. var oauthParameters = this._createOAuthParameters(OAuth2GrantType.REFRESH_TOKEN);
  424. if (resource) {
  425. oauthParameters[OAuth2Parameters.RESOURCE] = resource;
  426. }
  427. if (clientSecret) {
  428. oauthParameters[OAuth2Parameters.CLIENT_SECRET] = clientSecret;
  429. }
  430. oauthParameters[OAuth2Parameters.REFRESH_TOKEN] = refreshToken;
  431. this._oauthGetToken(oauthParameters, callback);
  432. };
  433. /**
  434. * Obtains a token using a refresh token.
  435. * @param {string} refreshToken
  436. * @param {string} [clientSecret]
  437. * @param {AcquireTokenCallback} callback
  438. */
  439. TokenRequest.prototype.getTokenWithRefreshToken = function(refreshToken, clientSecret, callback) {
  440. this._getTokenWithRefreshToken(refreshToken, null, clientSecret, callback);
  441. };
  442. /**
  443. * Obtains a token from the cache, refreshing it or using a MRRT if necessary.
  444. * @param {string} [userId] The user associated with the cached token.
  445. * @param {AcquireTokenCallback} callback
  446. */
  447. TokenRequest.prototype.getTokenFromCacheWithRefresh = function(userId, callback) {
  448. var self = this;
  449. this._log.info('Getting token from cache with refresh if necessary.');
  450. this._userId = userId;
  451. this._getTokenWithCacheWrapper(callback, function(getTokenCompleteCallback) {
  452. // If this method was called then no cached entry was found. Since
  453. // this particular version of acquireToken can only retrieve tokens
  454. // from the cache, return an error.
  455. getTokenCompleteCallback(self._log.createError('Entry not found in cache.'));
  456. });
  457. };
  458. /**
  459. * Creates a self signed jwt.
  460. * @param {string} authorityUrl
  461. * @param {string} certificate A PEM encoded certificate private key.
  462. * @param {string} thumbprint
  463. * @return {string} A self signed JWT
  464. */
  465. TokenRequest.prototype._createJwt = function(authorityUrl, certificate, thumbprint) {
  466. var jwt;
  467. var ssj = this._createSelfSignedJwt();
  468. jwt = ssj.create(certificate, thumbprint);
  469. if (!jwt) {
  470. throw this._log.createError('Failed to create JWT');
  471. }
  472. return jwt;
  473. };
  474. /**
  475. * Obtains a token via a certificate. The certificate is used to generate a self signed
  476. * JWT token that is passed as a client_assertion.
  477. * @param {string} certificate A PEM encoded certificate private key.
  478. * @param {string} thumbprint A hex encoded thumbprint of the certificate.
  479. * @param {AcquireTokenCallback} callback
  480. */
  481. TokenRequest.prototype.getTokenWithCertificate = function(certificate, thumbprint, callback) {
  482. this._log.info('Getting a token via certificate.');
  483. var authorityUrl = this._authenticationContext._authority;
  484. var jwt;
  485. try {
  486. jwt = this._createJwt(authorityUrl, certificate, thumbprint);
  487. } catch (err) {
  488. callback(err);
  489. return;
  490. }
  491. var oauthParameters = this._createOAuthParameters(OAuth2GrantType.CLIENT_CREDENTIALS);
  492. oauthParameters[OAuth2Parameters.CLIENT_ASSERTION_TYPE] = OAuth2GrantType.JWT_BEARER;
  493. oauthParameters[OAuth2Parameters.CLIENT_ASSERTION] = jwt;
  494. this._getTokenWithCacheWrapper(callback, function(getTokenCompleteCallback) {
  495. this._oauthGetToken(oauthParameters, getTokenCompleteCallback);
  496. });
  497. };
  498. TokenRequest.prototype.getTokenWithDeviceCode = function(userCodeInfo, callback) {
  499. this._log.info('Getting a token via device code');
  500. var self = this;
  501. var oauthParameters = this._createOAuthParameters(OAuth2GrantType.DEVICE_CODE);
  502. oauthParameters[OAuth2Parameters.CODE] = userCodeInfo[DeviceCodeResponseParameters.DEVICE_CODE];
  503. var interval = userCodeInfo[DeviceCodeResponseParameters.INTERVAL];
  504. var expires_in = userCodeInfo[DeviceCodeResponseParameters.EXPIRES_IN];
  505. if (interval <= 0) {
  506. callback(new Error('invalid refresh interval'));
  507. }
  508. this._oauthGetTokenByPolling(oauthParameters, interval, expires_in, function(err, tokenResponse) {
  509. if (err) {
  510. self._log.verbose('Token polling request returend with err.');
  511. callback(err, tokenResponse);
  512. }
  513. else {
  514. self._addTokenIntoCache(tokenResponse, callback);
  515. }
  516. });
  517. };
  518. TokenRequest.prototype.cancelTokenRequestWithDeviceCode = function() {
  519. this._pollingClient.cancelPollingRequest();
  520. };
  521. module.exports = TokenRequest;