Skip to content
This repository has been archived by the owner on Jun 30, 2023. It is now read-only.

Commit

Permalink
Fix for #1463 - more strict token cache removal when using several re…
Browse files Browse the repository at this point in the history
…sources
  • Loading branch information
bgavrilMS committed Dec 19, 2018
1 parent ce075c8 commit b4d3cb0
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,9 @@ public async Task<AuthenticationResult> RunAsync()
ResultEx = await this.tokenCache.LoadFromCacheAsync(CacheQueryData, RequestContext).ConfigureAwait(false);
extendedLifetimeResultEx = ResultEx;

// Check if we need to get an AT from the RT
if (ResultEx?.Result != null &&
((ResultEx.Result.AccessToken == null && ResultEx.RefreshToken != null) ||
((ResultEx.Result.AccessToken == null && ResultEx.RefreshToken != null) ||
(ResultEx.Result.ExtendedLifeTimeToken && ResultEx.RefreshToken != null)))
{
ResultEx = await this.RefreshAccessTokenAsync(ResultEx).ConfigureAwait(false);
Expand All @@ -175,7 +176,7 @@ public async Task<AuthenticationResult> RunAsync()
}
}
}

if (ResultEx == null || ResultEx.Exception != null)
{
if (brokerHelper.CanInvokeBroker)
Expand All @@ -199,6 +200,7 @@ public async Task<AuthenticationResult> RunAsync()
notifiedBeforeAccessCache = await StoreResultExToCacheAsync(notifiedBeforeAccessCache).ConfigureAwait(false);
}

// At this point we have an Acess Token - return it
await this.PostRunAsync(ResultEx.Result).ConfigureAwait(false);
return new AuthenticationResult(ResultEx.Result);
}
Expand Down
73 changes: 50 additions & 23 deletions src/Microsoft.IdentityModel.Clients.ActiveDirectory/TokenCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -407,10 +407,10 @@ internal AdalResultWrapper LoadFromCacheCommon(CacheQueryData cacheQueryData, Re
AdalTokenCacheKey cacheKey = kvp.Value.Key;
resultEx = kvp.Value.Value.Clone();

bool tokenNearExpiry = (resultEx.Result.ExpiresOn <=
DateTime.UtcNow + TimeSpan.FromMinutes(ExpirationMarginInMinutes));
bool tokenExtendedLifeTimeExpired = (resultEx.Result.ExtendedExpiresOn <=
DateTime.UtcNow);
bool tokenNearExpiry = resultEx.Result.ExpiresOn <=
DateTime.UtcNow + TimeSpan.FromMinutes(ExpirationMarginInMinutes);
bool tokenExtendedLifeTimeExpired = resultEx.Result.ExtendedExpiresOn <= DateTime.UtcNow;
bool tokenIsForSameResource = cacheKey.ResourceEquals(cacheQueryData.Resource);

//check for cross-tenant authority
if (!cacheKey.Authority.Equals(cacheQueryData.Authority, StringComparison.OrdinalIgnoreCase))
Expand All @@ -426,23 +426,10 @@ internal AdalResultWrapper LoadFromCacheCommon(CacheQueryData cacheQueryData, Re

requestContext.Logger.Info("An expired or near expiry token was found in the cache");
}
else if (!cacheKey.ResourceEquals(cacheQueryData.Resource))
else if (!tokenIsForSameResource)
{
requestContext.Logger.InfoPii(
string.Format(CultureInfo.CurrentCulture,
"Multi resource refresh token for resource '{0}' will be used to acquire token for '{1}'",
cacheKey.Resource, cacheQueryData.Resource),
string.Empty);
var newResultEx = new AdalResultWrapper
{
Result = new AdalResult(null, null, DateTimeOffset.MinValue),
RefreshToken = resultEx.RefreshToken,
ResourceInResponse = resultEx.ResourceInResponse
};

newResultEx.Result.UpdateTenantAndUserInfo(resultEx.Result.TenantId, resultEx.Result.IdToken,
resultEx.Result.UserInfo);
resultEx = newResultEx;
resultEx = HandleMrrtCase(cacheQueryData, requestContext.Logger, resultEx, cacheKey);

}
else if (!tokenExtendedLifeTimeExpired && cacheQueryData.ExtendedLifeTimeEnabled && tokenNearExpiry)
{
Expand All @@ -454,7 +441,6 @@ internal AdalResultWrapper LoadFromCacheCommon(CacheQueryData cacheQueryData, Re
else if (tokenExtendedLifeTimeExpired)
{
resultEx.Result.AccessToken = null;

requestContext.Logger.Info("The AT has expired its ExtendedLifeTime");
}
else
Expand All @@ -463,7 +449,7 @@ internal AdalResultWrapper LoadFromCacheCommon(CacheQueryData cacheQueryData, Re
(resultEx.Result.ExpiresOn - DateTime.UtcNow).TotalMinutes));
}

if (resultEx.Result.AccessToken == null && resultEx.RefreshToken == null)
if (resultEx?.Result?.AccessToken == null && resultEx?.RefreshToken == null && tokenIsForSameResource)
{
this.tokenCacheDictionary.Remove(cacheKey);
requestContext.Logger.Info("An old item was removed from the cache");
Expand Down Expand Up @@ -492,6 +478,47 @@ internal AdalResultWrapper LoadFromCacheCommon(CacheQueryData cacheQueryData, Re
}
}

private static AdalResultWrapper HandleMrrtCase(
CacheQueryData cacheQueryData,
ICoreLogger logger,
AdalResultWrapper resultEx,
AdalTokenCacheKey cacheKey)
{
logger.InfoPii(
string.Format(CultureInfo.CurrentCulture,
"Multi resource refresh token for resource '{0}' will be used to acquire token for '{1}'.",
cacheKey.Resource, cacheQueryData.Resource),
"Multi resource refresh token will be used to acquire a token");

bool mrrtIsInCache = resultEx.RefreshToken != null;

if (mrrtIsInCache)
{
logger.Info("MRRT will be used to fetch an access token. ");

// Instruct the downflow logic to fetch an AT from the MRRT
var newResultEx = new AdalResultWrapper
{
Result = new AdalResult(null, null, DateTimeOffset.MinValue),
RefreshToken = resultEx.RefreshToken,
ResourceInResponse = resultEx.ResourceInResponse
};

newResultEx.Result.UpdateTenantAndUserInfo(resultEx.Result.TenantId, resultEx.Result.IdToken,
resultEx.Result.UserInfo);
resultEx = newResultEx;
}
else
{
logger.Info("MRRT is not present in the cache. This is most likely a broker scenario and the MRRT is on the broker. ");

// This AT is for a different resource, it can't be used
return null;
}

return resultEx;
}

internal async Task StoreToCacheAsync(AdalResultWrapper result, string authority, string resource, string clientId,
TokenSubjectType subjectType, RequestContext requestContext)
{
Expand Down Expand Up @@ -531,7 +558,7 @@ internal void StoreToCacheCommon(AdalResultWrapper result, string authority, str
if (subjectType == TokenSubjectType.User && Authenticator.DetectAuthorityType(authority) == Internal.Instance.AuthorityType.AAD)
{
Identity.Core.IdToken idToken = Identity.Core.IdToken.Parse(result.Result.IdToken);

CacheFallbackOperations.WriteMsalRefreshToken(tokenCacheAccessor, result, authority, clientId, displayableId,
result.Result.UserInfo.GivenName,
result.Result.UserInfo.FamilyName, idToken?.ObjectId);
Expand Down

0 comments on commit b4d3cb0

Please sign in to comment.