async Task <Tuple <MsalAccessTokenCacheItem, MsalIdTokenCacheItem, Account> > ITokenCacheInternal.SaveTokenResponseAsync( AuthenticationRequestParameters requestParams, MsalTokenResponse response) { response.Log(requestParams.RequestContext.Logger, LogLevel.Verbose); MsalAccessTokenCacheItem msalAccessTokenCacheItem = null; MsalRefreshTokenCacheItem msalRefreshTokenCacheItem = null; MsalIdTokenCacheItem msalIdTokenCacheItem = null; MsalAccountCacheItem msalAccountCacheItem = null; IdToken idToken = IdToken.Parse(response.IdToken); if (idToken == null) { requestParams.RequestContext.Logger.Info("ID Token not present in response. "); } var tenantId = GetTenantId(idToken, requestParams); bool isAdfsAuthority = requestParams.AuthorityInfo.AuthorityType == AuthorityType.Adfs; string preferredUsername = GetPreferredUsernameFromIdToken(isAdfsAuthority, idToken); string username = isAdfsAuthority ? idToken?.Upn : preferredUsername; string homeAccountId = GetHomeAccountId(requestParams, response, idToken); string suggestedWebCacheKey = SuggestedWebCacheKeyFactory.GetKeyFromResponse(requestParams, homeAccountId); // Do a full instance discovery when saving tokens (if not cached), // so that the PreferredNetwork environment is up to date. var instanceDiscoveryMetadata = await ServiceBundle.InstanceDiscoveryManager .GetMetadataEntryAsync( requestParams.Authority.AuthorityInfo, requestParams.RequestContext) .ConfigureAwait(false); #region Create Cache Objects if (!string.IsNullOrEmpty(response.AccessToken)) { msalAccessTokenCacheItem = new MsalAccessTokenCacheItem( instanceDiscoveryMetadata.PreferredCache, requestParams.AppConfig.ClientId, response, tenantId, homeAccountId, requestParams.AuthenticationScheme.KeyId) { UserAssertionHash = requestParams.UserAssertion?.AssertionHash, IsAdfs = isAdfsAuthority }; } if (!string.IsNullOrEmpty(response.RefreshToken)) { msalRefreshTokenCacheItem = new MsalRefreshTokenCacheItem( instanceDiscoveryMetadata.PreferredCache, requestParams.AppConfig.ClientId, response, homeAccountId); if (!_featureFlags.IsFociEnabled) { msalRefreshTokenCacheItem.FamilyId = null; } } Dictionary <string, string> wamAccountIds = GetWamAccountIds(requestParams, response); Account account = null; if (idToken != null) { msalIdTokenCacheItem = new MsalIdTokenCacheItem( instanceDiscoveryMetadata.PreferredCache, requestParams.AppConfig.ClientId, response, tenantId, homeAccountId) { IsAdfs = isAdfsAuthority }; msalAccountCacheItem = new MsalAccountCacheItem( instanceDiscoveryMetadata.PreferredCache, response.ClientInfo, homeAccountId, idToken, preferredUsername, tenantId, wamAccountIds); } #endregion account = new Account( homeAccountId, username, instanceDiscoveryMetadata.PreferredNetwork, wamAccountIds); await _semaphoreSlim.WaitAsync().ConfigureAwait(false); try { #pragma warning disable CS0618 // Type or member is obsolete HasStateChanged = true; #pragma warning restore CS0618 // Type or member is obsolete try { ITokenCacheInternal tokenCacheInternal = this; if (tokenCacheInternal.IsTokenCacheSerialized()) { var args = new TokenCacheNotificationArgs( this, ClientId, account, hasStateChanged: true, tokenCacheInternal.IsApplicationCache, hasTokens: tokenCacheInternal.HasTokensNoLocks(), suggestedCacheKey: suggestedWebCacheKey); Stopwatch sw = new Stopwatch(); sw.Start(); await tokenCacheInternal.OnBeforeAccessAsync(args).ConfigureAwait(false); await tokenCacheInternal.OnBeforeWriteAsync(args).ConfigureAwait(false); requestParams.RequestContext.ApiEvent.DurationInCacheInMs += sw.ElapsedMilliseconds; } if (msalAccessTokenCacheItem != null) { requestParams.RequestContext.Logger.Info("Saving AT in cache and removing overlapping ATs..."); DeleteAccessTokensWithIntersectingScopes( requestParams, instanceDiscoveryMetadata.Aliases, tenantId, msalAccessTokenCacheItem.ScopeSet, msalAccessTokenCacheItem.HomeAccountId, msalAccessTokenCacheItem.TokenType); _accessor.SaveAccessToken(msalAccessTokenCacheItem); } if (idToken != null) { requestParams.RequestContext.Logger.Info("Saving Id Token and Account in cache ..."); _accessor.SaveIdToken(msalIdTokenCacheItem); MergeWamAccountIds(msalAccountCacheItem); _accessor.SaveAccount(msalAccountCacheItem); } // if server returns the refresh token back, save it in the cache. if (msalRefreshTokenCacheItem != null) { requestParams.RequestContext.Logger.Info("Saving RT in cache..."); _accessor.SaveRefreshToken(msalRefreshTokenCacheItem); } UpdateAppMetadata(requestParams.AppConfig.ClientId, instanceDiscoveryMetadata.PreferredCache, response.FamilyId); // Do not save RT in ADAL cache for client credentials flow or B2C if (ServiceBundle.Config.LegacyCacheCompatibilityEnabled && !requestParams.IsClientCredentialRequest && requestParams.AuthorityInfo.AuthorityType != AuthorityType.B2C) { var tenatedAuthority = Authority.CreateAuthorityWithTenant(requestParams.AuthorityInfo, tenantId); var authorityWithPreferredCache = Authority.CreateAuthorityWithEnvironment( tenatedAuthority.AuthorityInfo, instanceDiscoveryMetadata.PreferredCache); CacheFallbackOperations.WriteAdalRefreshToken( Logger, LegacyCachePersistence, msalRefreshTokenCacheItem, msalIdTokenCacheItem, authorityWithPreferredCache.AuthorityInfo.CanonicalAuthority, msalIdTokenCacheItem.IdToken.ObjectId, response.Scope); } } finally { ITokenCacheInternal tokenCacheInternal = this; if (tokenCacheInternal.IsTokenCacheSerialized()) { var args = new TokenCacheNotificationArgs( this, ClientId, account, hasStateChanged: true, tokenCacheInternal.IsApplicationCache, tokenCacheInternal.HasTokensNoLocks(), suggestedCacheKey: suggestedWebCacheKey); Stopwatch sw = new Stopwatch(); sw.Start(); await tokenCacheInternal.OnAfterAccessAsync(args).ConfigureAwait(false); requestParams.RequestContext.ApiEvent.DurationInCacheInMs += sw.ElapsedMilliseconds; } #pragma warning disable CS0618 // Type or member is obsolete HasStateChanged = false; #pragma warning restore CS0618 // Type or member is obsolete } return(Tuple.Create(msalAccessTokenCacheItem, msalIdTokenCacheItem, account)); } finally { _semaphoreSlim.Release(); } }
private bool IsAtExpired(MsalAccessTokenCacheItem at) { return(at.ExpiresOn < DateTime.UtcNow + AccessTokenExpirationBuffer); }
protected override async Task <AuthenticationResult> ExecuteAsync(CancellationToken cancellationToken) { var logger = AuthenticationRequestParameters.RequestContext.Logger; MsalAccessTokenCacheItem cachedAccessTokenItem = null; if (BrokerIsInstalledAndSupportsSilentAuth) { var msalTokenResponse = await ExecuteBrokerAsync(cancellationToken).ConfigureAwait(false); return(await CacheTokenResponseAndCreateAuthenticationResultAsync(msalTokenResponse).ConfigureAwait(false)); } ThrowIfNoScopesOnB2C(); if (!_silentParameters.ForceRefresh && !AuthenticationRequestParameters.HasClaims) { cachedAccessTokenItem = await CacheManager.FindAccessTokenAsync().ConfigureAwait(false); if (cachedAccessTokenItem != null && !cachedAccessTokenItem.NeedsRefresh()) { logger.Info("Returning access token found in cache. RefreshOn exists ? " + cachedAccessTokenItem.RefreshOn.HasValue); AuthenticationRequestParameters.RequestContext.ApiEvent.IsAccessTokenCacheHit = true; return(await CreateAuthenticationResultAsync(cachedAccessTokenItem).ConfigureAwait(false)); } } else { logger.Info("Skipped looking for an Access Token because ForceRefresh or Claims were set"); } // No AT or AT.RefreshOn > Now --> refresh the RT try { return(await RefreshRtOrFailAsync(cancellationToken).ConfigureAwait(false)); } catch (MsalServiceException e) { //Remove the account from cache in case of bad_token sub error if (MsalError.BadToken.Equals(e.SubError, StringComparison.OrdinalIgnoreCase)) { await CacheManager.TokenCacheInternal.RemoveAccountAsync(AuthenticationRequestParameters.Account, AuthenticationRequestParameters.RequestContext).ConfigureAwait(false); logger.Warning("Failed to refresh access token because the refresh token is invalid, removing account from cache."); throw; } bool isAadUnavailable = e.IsAadUnavailable(); logger.Warning($"Refreshing the RT failed. Is AAD down? {isAadUnavailable}. Is there an AT in the cache that is usable? {cachedAccessTokenItem != null}"); if (cachedAccessTokenItem != null && isAadUnavailable) { logger.Info("Returning existing access token. It is not expired, but should be refreshed."); return(await CreateAuthenticationResultAsync(cachedAccessTokenItem).ConfigureAwait(false)); } logger.Warning("Failed to refresh the RT and cannot use existing AT (expired or missing)."); throw; } }
public string FormatAccessToken(MsalAccessTokenCacheItem msalAccessTokenCacheItem) { return(msalAccessTokenCacheItem.Secret); }
internal static void PopulateCache( ITokenCacheAccessor accessor, string uid = TestConstants.Uid, string utid = TestConstants.Utid, string clientId = TestConstants.ClientId, string environment = TestConstants.ProductionPrefCacheEnvironment, string displayableId = TestConstants.DisplayableId, string rtSecret = TestConstants.RTSecret, string overridenScopes = null, string userAssertion = null, bool expiredAccessTokens = false, bool addSecondAt = true) { string clientInfo = MockHelpers.CreateClientInfo(uid, utid); string homeAccId = ClientInfo.CreateFromJson(clientInfo).ToAccountIdentifier(); var accessTokenExpiresOn = expiredAccessTokens ? new DateTimeOffset(DateTime.UtcNow) : new DateTimeOffset(DateTime.UtcNow + TimeSpan.FromSeconds(ValidExpiresIn)); var extendedAccessTokenExpiresOn = expiredAccessTokens ? new DateTimeOffset(DateTime.UtcNow) : new DateTimeOffset(DateTime.UtcNow + TimeSpan.FromSeconds(ValidExtendedExpiresIn)); MsalAccessTokenCacheItem atItem = new MsalAccessTokenCacheItem( environment, clientId, overridenScopes ?? TestConstants.s_scope.AsSingleString(), utid, "", accessTokenExpiresOn, extendedAccessTokenExpiresOn, clientInfo, homeAccId); if (userAssertion != null) { var crypto = PlatformProxyFactory.CreatePlatformProxy(null).CryptographyManager; atItem.UserAssertionHash = crypto.CreateBase64UrlEncodedSha256Hash(userAssertion); } // add access token accessor.SaveAccessToken(atItem); var idTokenCacheItem = new MsalIdTokenCacheItem( environment, clientId, MockHelpers.CreateIdToken(TestConstants.UniqueId + "more", displayableId), clientInfo, homeAccId, tenantId: utid); accessor.SaveIdToken(idTokenCacheItem); // add another access token if (addSecondAt) { atItem = new MsalAccessTokenCacheItem( environment, clientId, TestConstants.s_scopeForAnotherResource.AsSingleString(), utid, "", accessTokenExpiresOn, extendedAccessTokenExpiresOn, clientInfo, homeAccId); accessor.SaveAccessToken(atItem); } var accountCacheItem = new MsalAccountCacheItem( environment, null, clientInfo, homeAccId, null, displayableId, utid, null, null, null); accessor.SaveAccount(accountCacheItem); AddRefreshTokenToCache(accessor, uid, utid, clientId, environment, rtSecret); var appMetadataItem = new MsalAppMetadataCacheItem( clientId, environment, null); accessor.SaveAppMetadata(appMetadataItem); }
public static void AddAccessTokenCacheItem(this ITokenCacheInternal tokenCache, MsalAccessTokenCacheItem accessTokenItem) { tokenCache.Semaphore.Wait(); try { tokenCache.Accessor.SaveAccessToken(accessTokenItem); } finally { tokenCache.Semaphore.Release(); } }
internal async Task <AuthenticationResult> HandleTokenRefreshErrorAsync(MsalServiceException e, MsalAccessTokenCacheItem cachedAccessTokenItem) { var logger = AuthenticationRequestParameters.RequestContext.Logger; bool isAadUnavailable = e.IsAadUnavailable(); logger.Warning($"Fetching a new AT failed. Is AAD down? {isAadUnavailable}. Is there an AT in the cache that is usable? {cachedAccessTokenItem != null}"); if (cachedAccessTokenItem != null && isAadUnavailable) { logger.Info("Returning existing access token. It is not expired, but should be refreshed. "); var idToken = await CacheManager.GetIdTokenCacheItemAsync(cachedAccessTokenItem).ConfigureAwait(false); var account = await CacheManager.GetAccountAssociatedWithAccessTokenAsync(cachedAccessTokenItem).ConfigureAwait(false); return(new AuthenticationResult( cachedAccessTokenItem, idToken, AuthenticationRequestParameters.AuthenticationScheme, AuthenticationRequestParameters.RequestContext.CorrelationId, TokenSource.Cache, AuthenticationRequestParameters.RequestContext.ApiEvent, account)); } logger.Warning("Either the exception does not indicate a problem with AAD or the token cache does not have an AT that is usable. "); throw e; }