Comparing version 0.1.16 to 0.1.17
@@ -0,1 +1,6 @@ | ||
Version 0.1.17 | ||
-------------- | ||
Release Date: 8 Oct 2015 | ||
* Add support for cross tenant refresh token | ||
Version 0.1.16 | ||
@@ -2,0 +7,0 @@ -------------- |
@@ -403,3 +403,5 @@ /* | ||
/** | ||
* Gets a new access token using via a device code. | ||
* Gets a new access token using via a device code. | ||
* @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, | ||
* please use acquireToken. | ||
* @param {string} clientId The OAuth client id of the calling application. | ||
@@ -422,3 +424,3 @@ * @param {object} userCodeInfo Contains device_code, retry interval, and expire time for the request for get the token. | ||
var tokenRequest = new TokenRequest(this._callContext, this, clientId, resource, null); | ||
self._tokenRequestWithUserCode[userCodeInfo[constants.OAuth2.DeviceCodeResponseParameters.DEVICE_CODE]] = tokenRequest; | ||
self._tokenRequestWithUserCode[userCodeInfo[constants.UserCodeResponseFields.DEVICE_CODE]] = tokenRequest; | ||
tokenRequest.getTokenWithDeviceCode(userCodeInfo, callback); | ||
@@ -443,3 +445,3 @@ }) | ||
if (!this._tokenRequestWithUserCode || !this._tokenRequestWithUserCode[userCodeInfo[constants.OAuth2.DeviceCodeResponseParameters.DEVICE_CODE]]) { | ||
if (!this._tokenRequestWithUserCode || !this._tokenRequestWithUserCode[userCodeInfo[constants.UserCodeResponseFields.DEVICE_CODE]]) { | ||
callback(new Error('No acquireTokenWithDeviceCodeRequest existed to be cancelled')); | ||
@@ -449,6 +451,6 @@ return; | ||
var tokenRequestToBeCancelled = this._tokenRequestWithUserCode[userCodeInfo[constants.OAuth2.DeviceCodeResponseParameters.DEVICE_CODE]]; | ||
var tokenRequestToBeCancelled = this._tokenRequestWithUserCode[userCodeInfo[constants.UserCodeResponseFields.DEVICE_CODE]]; | ||
tokenRequestToBeCancelled.cancelTokenRequestWithDeviceCode(); | ||
delete this._tokenRequestWithUserCode[constants.OAuth2.DeviceCodeResponseParameters.DEVICE_CODE]; | ||
delete this._tokenRequestWithUserCode[constants.UserCodeResponseFields.DEVICE_CODE]; | ||
}; | ||
@@ -455,0 +457,0 @@ |
@@ -140,6 +140,2 @@ /* | ||
CacheDriver.prototype._find = function(query, callback) { | ||
var authorityQuery = {}; | ||
authorityQuery[METADATA_AUTHORITY] = this._authority; | ||
_.extend(query, authorityQuery); | ||
this._cache.find(query, callback); | ||
@@ -175,6 +171,7 @@ }; | ||
* Finds all multi resource refresh tokens in the cache. | ||
* Refresh token is bound to userId, clientId. | ||
* @param {QueryCallback} callback | ||
*/ | ||
CacheDriver.prototype._findMRRTTokensForUser = function(user, callback) { | ||
this._find({ isMRRT : true, userId : user }, callback); | ||
this._find({ isMRRT : true, userId : user, _clientId : this._clientId}, callback); | ||
}; | ||
@@ -206,9 +203,8 @@ | ||
var returnVal; | ||
var isResourceSpecific; | ||
var isResourceTenantSpecific; | ||
if (potentialEntries && 0 < potentialEntries.length) { | ||
var resourceSpecificEntries; | ||
resourceSpecificEntries = _.where(potentialEntries, { resource : self._resource }); | ||
var resourceTenantSpecificEntries = _.where(potentialEntries, { resource : self._resource, _authority : self._authority }); | ||
if (!resourceSpecificEntries || 0 === resourceSpecificEntries.length) { | ||
if (!resourceTenantSpecificEntries || 0 === resourceTenantSpecificEntries.length) { | ||
self._log.verbose('No resource specific cache entries found.'); | ||
@@ -225,6 +221,6 @@ | ||
} else if (resourceSpecificEntries.length === 1) { | ||
} else if (resourceTenantSpecificEntries.length === 1) { | ||
self._log.verbose('Resource specific token found.'); | ||
returnVal = resourceSpecificEntries[0]; | ||
isResourceSpecific = true; | ||
returnVal = resourceTenantSpecificEntries[0]; | ||
isResourceTenantSpecific = true; | ||
}else { | ||
@@ -238,3 +234,3 @@ callback(self._log.createError('More than one token matches the criteria. The result is ambiguous.')); | ||
} | ||
callback(null, returnVal, isResourceSpecific); | ||
callback(null, returnVal, isResourceTenantSpecific); | ||
}); | ||
@@ -255,2 +251,7 @@ }; | ||
newEntry = _.extend(newEntry, refreshResponse); | ||
if (entry.isMRRT && this._authority !== entry[METADATA_AUTHORITY]) { | ||
newEntry[METADATA_AUTHORITY] = this._authority; | ||
} | ||
this._log.verbose('Created new cache entry from refresh response.'); | ||
@@ -357,3 +358,3 @@ return newEntry; | ||
this._log.verbose('finding with query:' + JSON.stringify(query)); | ||
this._loadSingleEntryFromCache(query, function(err, entry, isResourceSpecific) { | ||
this._loadSingleEntryFromCache(query, function(err, entry, isResourceTenantSpecific) { | ||
if (err) { | ||
@@ -369,3 +370,3 @@ callback(err); | ||
self._refreshEntryIfNecessary(entry, isResourceSpecific, function(err, newEntry) { | ||
self._refreshEntryIfNecessary(entry, isResourceTenantSpecific, function(err, newEntry) { | ||
callback(err, newEntry); | ||
@@ -372,0 +373,0 @@ return; |
@@ -106,10 +106,10 @@ /* | ||
UserCodeResponseFields : { | ||
USER_CODE : 'user_code', | ||
DEVICE_CODE: 'device_code', | ||
VERIFICATION_URL: 'verification_url', | ||
EXPIRES_IN: 'expires_in', | ||
USER_CODE : 'userCode', | ||
DEVICE_CODE: 'deviceCode', | ||
VERIFICATION_URL: 'verificationUrl', | ||
EXPIRES_IN: 'expiresIn', | ||
INTERVAL: 'interval', | ||
MESSAGE: 'message', | ||
ERROR: 'error', | ||
ERROR_DESCRIPTION: 'error_description' | ||
ERROR_DESCRIPTION: 'errorDescription' | ||
}, | ||
@@ -116,0 +116,0 @@ |
@@ -90,10 +90,13 @@ /* | ||
var parameters = {}; | ||
parameters.slice = 'testslice'; | ||
parameters[OAuth2Parameters.AAD_API_VERSION] = '1.0'; | ||
tokenUrl.search = querystring.stringify(parameters); | ||
return tokenUrl; | ||
}; | ||
/** | ||
* Constructs the user code info request url. | ||
* @private | ||
* @return {URL} | ||
*/ | ||
OAuth2Client.prototype._createDeviceCodeUrl = function () { | ||
@@ -103,3 +106,2 @@ var deviceCodeUrl = url.parse(this._deviceCodeEndpoint); | ||
var parameters = {}; | ||
parameters.slice = 'testslice'; | ||
parameters[OAuth2Parameters.AAD_API_VERSION] = '1.0'; | ||
@@ -438,3 +440,3 @@ | ||
{ | ||
'url' : postUrl, | ||
'url' : url.format(postUrl), | ||
body : urlEncodedRequestForm, | ||
@@ -441,0 +443,0 @@ headers: { |
@@ -39,3 +39,3 @@ /* | ||
var WSTrustVersion = constants.WSTrustVersion; | ||
var DeviceCodeResponseParameters = constants.OAuth2.DeviceCodeResponseParameters; | ||
var DeviceCodeResponseParameters = constants.UserCodeResponseFields; | ||
@@ -168,2 +168,14 @@ /** | ||
/** | ||
* Store token into cache. | ||
* @param {object} tokenResponse Token response to be added into the cache. | ||
*/ | ||
TokenRequest.prototype._addTokenIntoCache = function(tokenResponse, callback) { | ||
this._cacheDriver = this._createCacheDriver(); | ||
this._log.verbose('Storing retrieved token into cache'); | ||
this._cacheDriver.add(tokenResponse, function(err) { | ||
callback(err, tokenResponse); | ||
}); | ||
}; | ||
/** | ||
* Adds an OAuth parameter to the paramters object if the parameter is | ||
@@ -559,5 +571,6 @@ * not null or undefined. | ||
this._log.info('Getting a token via device code'); | ||
var self = this; | ||
var oauthParameters = this._createOAuthParameters(OAuth2GrantType.DEVICE_CODE); | ||
oauthParameters[OAuth2Parameters.CODE] = userCodeInfo[OAuth2Parameters.DEVICE_CODE]; | ||
oauthParameters[OAuth2Parameters.CODE] = userCodeInfo[DeviceCodeResponseParameters.DEVICE_CODE]; | ||
@@ -571,4 +584,10 @@ var interval = userCodeInfo[DeviceCodeResponseParameters.INTERVAL]; | ||
this._getTokenWithCacheWrapper(callback, function(getTokenCompleteCallback) { | ||
this._oauthGetTokenByPolling(oauthParameters, interval, expires_in, getTokenCompleteCallback); | ||
this._oauthGetTokenByPolling(oauthParameters, interval, expires_in, function(err, tokenResponse) { | ||
if (err) { | ||
self._log.verbose('Token polling request returend with err.'); | ||
callback(err, tokenResponse); | ||
} | ||
else { | ||
self._addTokenIntoCache(tokenResponse, callback); | ||
} | ||
}); | ||
@@ -575,0 +594,0 @@ }; |
@@ -18,3 +18,3 @@ { | ||
}, | ||
"version": "0.1.16", | ||
"version": "0.1.17", | ||
"description": "Windows Azure Active Directory Client Library for node", | ||
@@ -28,3 +28,3 @@ "keywords": [ "node", "azure", "AAD", "adal", "adfs", "oauth" ], | ||
"node-uuid": "1.4.1", | ||
"request": ">= 2.9.203", | ||
"request": ">= 2.52.0", | ||
"underscore": ">= 1.3.1", | ||
@@ -31,0 +31,0 @@ "xmldom": ">= 0.1.x", |
@@ -27,2 +27,3 @@ /* | ||
var url = require('url'); | ||
var MemoryCache = require('../lib/memory-cache'); | ||
@@ -71,5 +72,6 @@ var AuthenticationContext = adal.AuthenticationContext; | ||
sampleParameters = { | ||
tenant : '', | ||
authorityHostUrl : '', | ||
clientId : '' | ||
tenant : 'convergeTest.onmicrosoft.com', | ||
authorityHostUrl : 'https://login.microsoftonline.com', | ||
clientId : '', | ||
anothertenant: '' | ||
}; | ||
@@ -81,7 +83,9 @@ } | ||
var resource = '00000002-0000-0000-c000-000000000000'; | ||
var userId = ''; | ||
//turnOnLogging(); | ||
var context = new AuthenticationContext(authorityUrl); | ||
var cache = new MemoryCache(); | ||
var context = new AuthenticationContext(authorityUrl, null, cache); | ||
context.acquireUserCode(resource, sampleParameters.clientId, 'es-mx', function (err, response) { | ||
@@ -99,3 +103,14 @@ if (err) { | ||
else { | ||
console.log(tokenResponse); | ||
authorityUrl = sampleParameters.authorityHostUrl + '/' + sampleParameters.anothertenant; | ||
var context2 = new AuthenticationContext(authorityUrl, null, cache); | ||
context2.acquireToken(resource, userId, sampleParameters.clientId, function (err, tokenResponse) { | ||
if (err) { | ||
console.log('error happens when acquiring token with device code'); | ||
console.log(err); | ||
} | ||
else { | ||
console.log(tokenResponse); | ||
} | ||
}); | ||
} | ||
@@ -105,1 +120,2 @@ }); | ||
}); | ||
@@ -217,2 +217,3 @@ /* | ||
}; | ||
callback(err, testValues, finalRefreshToken); | ||
@@ -235,3 +236,3 @@ } | ||
var responseOptions = { authority : otherAuthority, mrrt : true, resource : responses[0].resource }; | ||
var differentAuthorityResponse = util.createResponse(responseOptions); | ||
var differentAuthorityResponse = util.createResponse(responseOptions, 21); | ||
delete responseOptions.authority; | ||
@@ -245,2 +246,3 @@ var extraMRRTResponse = util.createResponse(responseOptions, 21); | ||
// the refresh token of the entries with the same authority. | ||
// update: with mega refresh token(cross tenant RT), refresh token of the entry will be updated if there is a match with userId, clientId. | ||
var cacheDriver = new CacheDriver(fakeTokenRequest._callContext, otherAuthority, differentAuthorityResponse.resource, differentAuthorityResponse.clientId, memCache, unexpectedRefreshFunction); | ||
@@ -253,3 +255,2 @@ cacheDriver.add(differentAuthorityResponse.decodedResponse, function(err) { | ||
assert(!err2, 'Unexpected error adding second entry with previous authority.'); | ||
compareInputAndCache(responses, memCache, numMRRTTokens); | ||
@@ -403,2 +404,3 @@ // ensure that we only find the mrrt with the different authority. | ||
var refreshedRefreshToken = refreshedResponse.refreshToken; | ||
var refreshFunction = createRefreshFunction(finalMrrt, refreshedResponse.decodedResponse); | ||
@@ -405,0 +407,0 @@ |
@@ -30,2 +30,3 @@ /* | ||
var querystring = require('querystring'); | ||
var url = require('url'); | ||
@@ -37,2 +38,3 @@ var util = require('./util/util'); | ||
var adal = testRequire('adal'); | ||
var MemoryCache = testRequire('memory-cache'); | ||
var AuthenticationContext = adal.AuthenticationContext; | ||
@@ -46,3 +48,3 @@ | ||
function setupExpectedTokenRequestResponse(httpCode, returnDoc, authorityEndpoint) { | ||
function setupExpectedTokenRequestResponse(httpCode, returnDoc, authorityEndpoint, extraQP) { | ||
var authEndpoint = util.getNockAuthorityHost(authorityEndpoint); | ||
@@ -57,3 +59,3 @@ | ||
var query = querystring.stringify(queryParameters); | ||
var tokenRequest = nock(authEndpoint) | ||
@@ -70,3 +72,3 @@ .filteringRequestBody(function (body) { | ||
} | ||
test('happy-path-successOnFirstRequest', function (done) { | ||
@@ -76,3 +78,3 @@ var response = util.createResponse(); | ||
var userCodeInfo = { device_code: cp.deviceCode, interval: 1, expires_in: 1 }; | ||
var userCodeInfo = { deviceCode: cp.deviceCode, interval: 1, expiresIn: 1 }; | ||
var context = new AuthenticationContext(cp.authUrl); | ||
@@ -116,3 +118,3 @@ context.acquireTokenWithDeviceCode(cp.resource, cp.clientId, userCodeInfo, function (err, tokenResponse) { | ||
var userCodeInfo = { device_code: cp.deviceCode, interval: 1, expires_in: 200 }; | ||
var userCodeInfo = { deviceCode: cp.deviceCode, interval: 1, expiresIn: 200 }; | ||
var context = new AuthenticationContext(cp.authUrl); | ||
@@ -133,3 +135,3 @@ context.acquireTokenWithDeviceCode(cp.resource, cp.clientId, userCodeInfo, function (err, tokenResponse) { | ||
var userCodeInfo = { device_code: cp.deviceCode, interval: 1, expires_in: 200 }; | ||
var userCodeInfo = { deviceCode: cp.deviceCode, interval: 1, expiresIn: 200 }; | ||
var context = new AuthenticationContext(cp.authUrl); | ||
@@ -152,3 +154,3 @@ | ||
var userCodeInfo = { interval: 5, expires_in: 1000}; | ||
var userCodeInfo = { interval: 5, expiresIn: 1000}; | ||
context.acquireTokenWithDeviceCode(cp.resource, cp.clientId, userCodeInfo, function (err) { | ||
@@ -159,3 +161,3 @@ assert(err, 'Did not receive expected argument error'); | ||
userCodeInfo = { device_code: 'test_device_code', expires_in: 1000 }; | ||
userCodeInfo = { deviceCode: 'test_device_code', expiresIn: 1000 }; | ||
context.acquireTokenWithDeviceCode(cp.resource, cp.clientId, userCodeInfo, function (err) { | ||
@@ -166,3 +168,3 @@ assert(err, 'Did not receive expected argument error'); | ||
userCodeInfo = { device_code: 'test_device_code', interval: 5 }; | ||
userCodeInfo = { deviceCode: 'test_device_code', interval: 5 }; | ||
context.acquireTokenWithDeviceCode(cp.resource, cp.clientId, userCodeInfo, function (err) { | ||
@@ -179,3 +181,3 @@ assert(err, 'Did not receive expected argument error'); | ||
userCodeInfo = { device_code: 'test_device_code', interval: 5, expires_in: 1000 }; | ||
userCodeInfo = { deviceCode: 'test_device_code', interval: 5, expiresIn: 1000 }; | ||
try { | ||
@@ -188,3 +190,3 @@ context.acquireTokenWithDeviceCode(cp.resource, cp.clientId, userCodeInfo); | ||
userCodeInfo = { device_code: 'test_device_code', interval: 0, expires_in: 1000 }; | ||
userCodeInfo = { deviceCode: 'test_device_code', interval: 0, expiresIn: 1000 }; | ||
context.acquireTokenWithDeviceCode(cp.resource, cp.clientId, userCodeInfo, function (err) { | ||
@@ -201,3 +203,3 @@ assert(err, 'Did not receive expected error.'); | ||
var userCodeInfo = { interval: 5, expires_in: 1000 }; | ||
var userCodeInfo = { interval: 5, expiresIn: 1000 }; | ||
context.cancelRequestToGetTokenWithDeviceCode(userCodeInfo, function (err) { | ||
@@ -214,3 +216,3 @@ assert(err, 'Did not receive expected argument error'); | ||
userCodeInfo = { device_code: 'test_device_code', interval: 5, expires_in: 1000 }; | ||
userCodeInfo = { deviceCode: 'test_device_code', interval: 5, expiresIn: 1000 }; | ||
try { | ||
@@ -223,3 +225,3 @@ context.cancelRequestToGetTokenWithDeviceCode(userCodeInfo); | ||
userCodeInfo = { device_code: cp.deviceCode, interval: 1, expires_in: 200 }; | ||
userCodeInfo = { deviceCode: cp.deviceCode, interval: 1, expiresIn: 200 }; | ||
context.cancelRequestToGetTokenWithDeviceCode(userCodeInfo, function (err) { | ||
@@ -232,2 +234,42 @@ assert(err, 'Did not receive expected error. '); | ||
}); | ||
test('cross-tenant-refresh-token', function (done) { | ||
var memCache = new MemoryCache(); | ||
var response = util.createResponse({mrrt: true}); | ||
var tokenRequest = setupExpectedTokenRequestResponse(200, response.wireResponse); | ||
var userCodeInfo = { deviceCode: cp.deviceCode, interval: 1, expiresIn: 1 }; | ||
var context = new AuthenticationContext(cp.authUrl, false, memCache); | ||
context.acquireTokenWithDeviceCode(cp.resource, cp.clientId, userCodeInfo, function (err, tokenResponse) { | ||
assert(!err, 'Receive unexpected error'); | ||
var someOtherAuthority = url.parse(cp.evoEndpoint + '/' + 'anotherTenant'); | ||
var responseOptions = { refreshedRefresh : true, mrrt: true}; | ||
var response = util.createResponse(responseOptions); | ||
var wireResponse = response.wireResponse; | ||
//need to change tokenUrlPath for the different tenant token request, and make sure get it changed back to not affect other tests | ||
var tokenUrlPath = cp.tokenUrlPath; | ||
cp.tokenUrlPath = someOtherAuthority.pathname + cp.tokenPath + cp.extraQP; | ||
var refreshRequest = util.setupExpectedRefreshTokenRequestResponse(200, wireResponse, someOtherAuthority, response.resource); | ||
cp.tokenUrlPath = tokenUrlPath; | ||
var conextForAnotherAuthority = new AuthenticationContext(someOtherAuthority, false, memCache); | ||
conextForAnotherAuthority.acquireToken(response.resource, tokenResponse.userId, response.clientId, function (error, tokenResponseForAnotherAuthority) { | ||
assert(!error, 'Receive unexpected error'); | ||
assert(memCache._entries.length === 2, 'There should two cache entries in the cache'); | ||
memCache.find({userId: tokenResponse.userId, _clientId: response.clientId, _authority: cp.evoEndpoint + '/' + cp.tenant}, function (err, entry) { | ||
assert(!err, 'Unexpected error received'); | ||
assert(entry.length === 1, 'no result returned for given tenant.'); | ||
}); | ||
memCache.find({userId: tokenResponse.userId, _clientId: response.clientId, _authority: url.format(someOtherAuthority)}, function (err, entry) { | ||
assert(!err, 'unexpected error received'); | ||
assert(entry.length === 1, 'no result returned for given tenant.'); | ||
}); | ||
done(err); | ||
}); | ||
}); | ||
}); | ||
}); |
@@ -123,5 +123,6 @@ 'use strict'; | ||
parameters.tokenPath = '/oauth2/token'; | ||
parameters.tokenUrlPath = parameters.authUrl.pathname + parameters.tokenPath; | ||
parameters.extraQP = '?api-version=1.0'; | ||
parameters.tokenUrlPath = parameters.authUrl.pathname + parameters.tokenPath + parameters.extraQP; | ||
parameters.deviceCodePath = '/oauth2/devicecode' | ||
parameters.deviceCodeUrlPath = parameters.authUrl.pathname + parameters.deviceCodePath; | ||
parameters.deviceCodeUrlPath = parameters.authUrl.pathname + parameters.deviceCodePath + parameters.extraQP; | ||
parameters.authorizePath = '/oauth/authorize'; | ||
@@ -203,9 +204,9 @@ parameters.authorizeUrlPath = parameters.authUrl.pathname + parameters.authorizePath; | ||
var DEVICE_CODE_RESPONSE_MAP = {}; | ||
DEVICE_CODE_RESPONSE_MAP['device_code'] = 'device_code'; | ||
DEVICE_CODE_RESPONSE_MAP['user_code'] = 'user_code'; | ||
DEVICE_CODE_RESPONSE_MAP['verification_url'] = 'verification_url'; | ||
DEVICE_CODE_RESPONSE_MAP['device_code'] = 'deviceCode'; | ||
DEVICE_CODE_RESPONSE_MAP['user_code'] = 'userCode'; | ||
DEVICE_CODE_RESPONSE_MAP['verification_url'] = 'verificationUrl'; | ||
DEVICE_CODE_RESPONSE_MAP['interval'] = 'interval'; | ||
DEVICE_CODE_RESPONSE_MAP['expires_in'] = 'expires_in'; | ||
DEVICE_CODE_RESPONSE_MAP['expires_in'] = 'expiresIn'; | ||
DEVICE_CODE_RESPONSE_MAP['error'] = 'error'; | ||
DEVICE_CODE_RESPONSE_MAP['error_description'] = 'error_description'; | ||
DEVICE_CODE_RESPONSE_MAP['error_description'] = 'errorDescription'; | ||
@@ -221,2 +222,8 @@ function mapFields(inObj, outObj, map) { | ||
/** | ||
* Create response based on the given options and iteration number. | ||
* @options Options is used to flex the reponse creation, i.e authority, resource and isMRRT. | ||
* @param iteration Iteraton will be used to create a distinct token for each value of iteration and it will always return that same token | ||
* for same value of iteration. | ||
*/ | ||
util.createResponse = function(options, iteration) { | ||
@@ -283,2 +290,3 @@ options = options || {}; | ||
var cachedResponse = _.clone(decodedResponse); | ||
cachedResponse['_clientId'] = parameters.clientId; | ||
@@ -285,0 +293,0 @@ cachedResponse['_authority'] = authority; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1170424
71
8666
Updatedrequest@>= 2.52.0