public void AfterAccess(TokenCacheNotificationArgs args) { if (args.TokenCache.HasStateChanged) { try { ISharedPreferences preferences = Application.Context.GetSharedPreferences(SharedPreferencesName, FileCreationMode.Private); ISharedPreferencesEditor editor = preferences.Edit(); editor.Remove(SharedPreferencesKey); if (args.TokenCache.Count > 0) { byte[] state = args.TokenCache.Serialize(); string stateString = Convert.ToBase64String(state); editor.PutString(SharedPreferencesKey, stateString); } editor.Apply(); args.TokenCache.HasStateChanged = false; } catch (Exception ex) { PlatformPlugin.Logger.Warning(null, "Failed to save cache: " + ex); } } }
// Triggered right after MSAL accessed the cache. private void AfterAccessNotification(TokenCacheNotificationArgs args) { // if the access operation resulted in a cache update if (HasStateChanged) { Persist(); } }
public void BeforeAccess(TokenCacheNotificationArgs args) { if (args.TokenCache.Count > 0) { // We assume that the cache has not changed since last write return; } try { ISharedPreferences preferences = Application.Context.GetSharedPreferences(SharedPreferencesName, FileCreationMode.Private); string stateString = preferences.GetString(SharedPreferencesKey, null); if (stateString != null) { byte[] state = Convert.FromBase64String(stateString); args.TokenCache.Deserialize(state); } } catch (Exception ex) { PlatformPlugin.Logger.Warning(null, "Failed to load cache: " + ex); // Ignore as the cache seems to be corrupt } }
async Task <MsalRefreshTokenCacheItem> ITokenCacheInternal.FindRefreshTokenAsync( AuthenticationRequestParameters requestParams, string familyId) { var cacheEvent = new CacheEvent( CacheEvent.TokenCacheLookup, requestParams.RequestContext.TelemetryCorrelationId) { TokenType = CacheEvent.TokenTypes.RT }; using (ServiceBundle.TelemetryManager.CreateTelemetryHelper(cacheEvent)) { if (requestParams.Authority == null) { return(null); } var instanceDiscoveryMetadataEntry = await GetCachedOrDiscoverAuthorityMetaDataAsync( requestParams.AuthorityInfo.CanonicalAuthority, requestParams.RequestContext).ConfigureAwait(false); var environmentAliases = GetEnvironmentAliases( requestParams.AuthorityInfo.CanonicalAuthority, instanceDiscoveryMetadataEntry); var preferredEnvironmentHost = GetPreferredEnvironmentHost( requestParams.AuthorityInfo.Host, instanceDiscoveryMetadataEntry); await _semaphoreSlim.WaitAsync().ConfigureAwait(false); try { requestParams.RequestContext.Logger.Info("Looking up refresh token in the cache.."); TokenCacheNotificationArgs args = new TokenCacheNotificationArgs { TokenCache = new NoLockTokenCacheProxy(this), ClientId = ClientId, Account = requestParams.Account }; // make sure to check preferredEnvironmentHost first var allEnvAliases = new List <string>() { preferredEnvironmentHost }; allEnvAliases.AddRange(environmentAliases); var keysAcrossEnvs = allEnvAliases.Select(ea => new MsalRefreshTokenCacheKey( ea, requestParams.ClientId, requestParams.Account?.HomeAccountId?.Identifier, familyId)); await OnBeforeAccessAsync(args).ConfigureAwait(false); try { // Try to load from all env aliases, but stop at the first valid one MsalRefreshTokenCacheItem msalRefreshTokenCacheItem = keysAcrossEnvs .Select(key => _accessor.GetRefreshToken(key)) .FirstOrDefault(item => item != null); requestParams.RequestContext.Logger.Info("Refresh token found in the cache? - " + (msalRefreshTokenCacheItem != null)); if (msalRefreshTokenCacheItem != null) { return(msalRefreshTokenCacheItem); } requestParams.RequestContext.Logger.Info("Checking ADAL cache for matching RT"); string upn = string.IsNullOrWhiteSpace(requestParams.LoginHint) ? requestParams.Account?.Username : requestParams.LoginHint; // ADAL legacy cache does not store FRTs if (requestParams.Account != null && string.IsNullOrEmpty(familyId)) { return(CacheFallbackOperations.GetAdalEntryForMsal( Logger, LegacyCachePersistence, preferredEnvironmentHost, environmentAliases, requestParams.ClientId, upn, requestParams.Account.HomeAccountId?.Identifier)); } return(null); } finally { await OnAfterAccessAsync(args).ConfigureAwait(false); } } finally { _semaphoreSlim.Release(); } } }
async Task <MsalAccessTokenCacheItem> ITokenCacheInternal.FindAccessTokenAsync(AuthenticationRequestParameters requestParams) { ISet <string> environmentAliases = new HashSet <string>(StringComparer.OrdinalIgnoreCase); string preferredEnvironmentAlias = null; if (requestParams.AuthorityInfo != null) { var instanceDiscoveryMetadataEntry = await GetCachedOrDiscoverAuthorityMetaDataAsync( requestParams.AuthorityInfo.CanonicalAuthority, requestParams.RequestContext).ConfigureAwait(false); environmentAliases.UnionWith(GetEnvironmentAliases( requestParams.AuthorityInfo.CanonicalAuthority, instanceDiscoveryMetadataEntry)); if (requestParams.AuthorityInfo.AuthorityType == AuthorityType.Adfs) { preferredEnvironmentAlias = requestParams.AuthorityInfo.CanonicalAuthority; } else if (requestParams.AuthorityInfo.AuthorityType != AuthorityType.B2C) { preferredEnvironmentAlias = instanceDiscoveryMetadataEntry.PreferredCache; } } // no authority passed if (!environmentAliases.Any()) { requestParams.RequestContext.Logger.Warning("No authority provided. Skipping cache lookup "); return(null); } await _semaphoreSlim.WaitAsync().ConfigureAwait(false); try { requestParams.RequestContext.Logger.Info("Looking up access token in the cache."); MsalAccessTokenCacheItem msalAccessTokenCacheItem; TokenCacheNotificationArgs args = new TokenCacheNotificationArgs { TokenCache = new NoLockTokenCacheProxy(this), ClientId = ClientId, Account = requestParams.Account }; List <MsalAccessTokenCacheItem> tokenCacheItems; await OnBeforeAccessAsync(args).ConfigureAwait(false); try { // filtered by client id. tokenCacheItems = GetAllAccessTokensWithNoLocks(true).ToList(); } finally { await OnAfterAccessAsync(args).ConfigureAwait(false); } tokenCacheItems = FilterByHomeAccountTenantOrAssertion(requestParams, tokenCacheItems); // no match found after initial filtering if (!tokenCacheItems.Any()) { requestParams.RequestContext.Logger.Info("No matching entry found for user or assertion"); return(null); } requestParams.RequestContext.Logger.Info("Matching entry count -" + tokenCacheItems.Count); IEnumerable <MsalAccessTokenCacheItem> filteredItems = tokenCacheItems.Where(item => ScopeHelper.ScopeContains(item.ScopeSet, requestParams.Scope)).ToList(); requestParams.RequestContext.Logger.Info("Matching entry count after filtering by scopes - " + filteredItems.Count()); // filter by authority var filteredByPreferredAlias = filteredItems.Where (item => item.Environment.Equals(preferredEnvironmentAlias, StringComparison.OrdinalIgnoreCase)).ToList(); if (filteredByPreferredAlias.Any()) { filteredItems = filteredByPreferredAlias; } else { filteredItems = filteredItems.Where( item => environmentAliases.Contains(item.Environment) && (item.IsAdfs || item.TenantId.Equals(requestParams.Authority.GetTenantId(), StringComparison.OrdinalIgnoreCase))); } // no match if (!filteredItems.Any()) { requestParams.RequestContext.Logger.Info("No tokens found for matching authority, client_id, user and scopes."); return(null); } // if only one cached token found if (filteredItems.Count() == 1) { msalAccessTokenCacheItem = filteredItems.First(); } else { requestParams.RequestContext.Logger.Error("Multiple tokens found for matching authority, client_id, user and scopes."); throw new MsalClientException( MsalError.MultipleTokensMatchedError, MsalErrorMessage.MultipleTokensMatched); } if (msalAccessTokenCacheItem != null) { if (msalAccessTokenCacheItem.ExpiresOn > DateTime.UtcNow + TimeSpan.FromMinutes(DefaultExpirationBufferInMinutes)) { requestParams.RequestContext.Logger.Info( "Access token is not expired. Returning the found cache entry. " + GetAccessTokenExpireLogMessageContent(msalAccessTokenCacheItem)); return(msalAccessTokenCacheItem); } if (ServiceBundle.Config.IsExtendedTokenLifetimeEnabled && msalAccessTokenCacheItem.ExtendedExpiresOn > DateTime.UtcNow + TimeSpan.FromMinutes(DefaultExpirationBufferInMinutes)) { requestParams.RequestContext.Logger.Info( "Access token is expired. IsExtendedLifeTimeEnabled=TRUE and ExtendedExpiresOn is not exceeded. Returning the found cache entry. " + GetAccessTokenExpireLogMessageContent(msalAccessTokenCacheItem)); msalAccessTokenCacheItem.IsExtendedLifeTimeToken = true; return(msalAccessTokenCacheItem); } requestParams.RequestContext.Logger.Info( "Access token has expired or about to expire. " + GetAccessTokenExpireLogMessageContent(msalAccessTokenCacheItem)); } return(null); } finally { _semaphoreSlim.Release(); } }
async Task <Tuple <MsalAccessTokenCacheItem, MsalIdTokenCacheItem> > ITokenCacheInternal.SaveTokenResponseAsync( AuthenticationRequestParameters requestParams, MsalTokenResponse response) { var tenantId = Authority .CreateAuthority(ServiceBundle, requestParams.TenantUpdatedCanonicalAuthority) .GetTenantId(); bool isAdfsAuthority = requestParams.AuthorityInfo.AuthorityType == AuthorityType.Adfs; IdToken idToken = IdToken.Parse(response.IdToken); string subject = idToken?.Subject; if (idToken != null && string.IsNullOrEmpty(subject)) { requestParams.RequestContext.Logger.Warning("Subject not present in Id token"); } string preferredUsername; // The preferred_username value cannot be null or empty in order to comply with the ADAL/MSAL Unified cache schema. // It will be set to "preferred_username not in idtoken" if (idToken == null) { preferredUsername = NullPreferredUsernameDisplayLabel; } else if (string.IsNullOrWhiteSpace(idToken.PreferredUsername)) { if (isAdfsAuthority) { //The direct to adfs scenario does not return preferred_username in the id token so it needs to be set to the upn preferredUsername = !string.IsNullOrEmpty(idToken.Upn) ? idToken.Upn : NullPreferredUsernameDisplayLabel; } else { preferredUsername = NullPreferredUsernameDisplayLabel; } } else { preferredUsername = idToken.PreferredUsername; } var instanceDiscoveryMetadataEntry = GetCachedAuthorityMetaData(requestParams.TenantUpdatedCanonicalAuthority); var environmentAliases = GetEnvironmentAliases( requestParams.TenantUpdatedCanonicalAuthority, instanceDiscoveryMetadataEntry); var preferredEnvironmentHost = GetPreferredEnvironmentHost( requestParams.AuthorityInfo.Host, instanceDiscoveryMetadataEntry); var msalAccessTokenCacheItem = new MsalAccessTokenCacheItem(preferredEnvironmentHost, requestParams.ClientId, response, tenantId, subject) { UserAssertionHash = requestParams.UserAssertion?.AssertionHash, IsAdfs = isAdfsAuthority }; MsalRefreshTokenCacheItem msalRefreshTokenCacheItem = null; MsalIdTokenCacheItem msalIdTokenCacheItem = null; if (idToken != null) { msalIdTokenCacheItem = new MsalIdTokenCacheItem( preferredEnvironmentHost, requestParams.ClientId, response, tenantId, subject) { IsAdfs = isAdfsAuthority }; } await _semaphoreSlim.WaitAsync().ConfigureAwait(false); try { try { Account account = null; string username = isAdfsAuthority ? idToken?.Upn : preferredUsername; if (msalAccessTokenCacheItem.HomeAccountId != null) { account = new Account( msalAccessTokenCacheItem.HomeAccountId, username, preferredEnvironmentHost); } var args = new TokenCacheNotificationArgs { TokenCache = new NoLockTokenCacheProxy(this), ClientId = ClientId, Account = account, HasStateChanged = true }; #pragma warning disable CS0618 // Type or member is obsolete HasStateChanged = true; #pragma warning restore CS0618 // Type or member is obsolete await OnBeforeAccessAsync(args).ConfigureAwait(false); try { await OnBeforeWriteAsync(args).ConfigureAwait(false); DeleteAccessTokensWithIntersectingScopes( requestParams, environmentAliases, tenantId, msalAccessTokenCacheItem.ScopeSet, msalAccessTokenCacheItem.HomeAccountId); _accessor.SaveAccessToken(msalAccessTokenCacheItem); if (idToken != null) { _accessor.SaveIdToken(msalIdTokenCacheItem); var msalAccountCacheItem = new MsalAccountCacheItem( preferredEnvironmentHost, response, preferredUsername, tenantId); //The ADFS direct scenrio does not return client info so the home account id is acquired from the subject if (isAdfsAuthority && String.IsNullOrEmpty(msalAccountCacheItem.HomeAccountId)) { msalAccountCacheItem.HomeAccountId = idToken.Subject; } _accessor.SaveAccount(msalAccountCacheItem); } // if server returns the refresh token back, save it in the cache. if (response.RefreshToken != null) { msalRefreshTokenCacheItem = new MsalRefreshTokenCacheItem( preferredEnvironmentHost, requestParams.ClientId, response, subject); if (!_featureFlags.IsFociEnabled) { msalRefreshTokenCacheItem.FamilyId = null; } requestParams.RequestContext.Logger.Info("Saving RT in cache..."); _accessor.SaveRefreshToken(msalRefreshTokenCacheItem); } UpdateAppMetadata(requestParams.ClientId, preferredEnvironmentHost, response.FamilyId); // save RT in ADAL cache for public clients // do not save RT in ADAL cache for MSAL B2C scenarios if (!requestParams.IsClientCredentialRequest && !requestParams.AuthorityInfo.AuthorityType.Equals(AuthorityType.B2C)) { CacheFallbackOperations.WriteAdalRefreshToken( Logger, LegacyCachePersistence, msalRefreshTokenCacheItem, msalIdTokenCacheItem, Authority.CreateAuthorityUriWithHost( requestParams.TenantUpdatedCanonicalAuthority, preferredEnvironmentHost), msalIdTokenCacheItem.IdToken.ObjectId, response.Scope); } } finally { await OnAfterAccessAsync(args).ConfigureAwait(false); } return(Tuple.Create(msalAccessTokenCacheItem, msalIdTokenCacheItem)); } finally { #pragma warning disable CS0618 // Type or member is obsolete HasStateChanged = false; #pragma warning restore CS0618 // Type or member is obsolete } } finally { _semaphoreSlim.Release(); } }
/// <summary> /// Reads a copy of the list of all items in the cache. /// </summary> /// <returns>The items in the cache</returns> public IEnumerable<TokenCacheItem> ReadItems(string clientId) { lock (lockObject) { TokenCacheNotificationArgs args = new TokenCacheNotificationArgs {TokenCache = this}; this.OnBeforeAccess(args); List<TokenCacheItem> items = ReadItemsFromCache(clientId); this.OnAfterAccess(args); return items; } }
public void OnBeforeAccess(TokenCacheNotificationArgs args) { BeforeAccess?.Invoke(args); }
protected abstract Task <byte[]> GetMsalCacheData(TokenCacheNotificationArgs context);
internal void OnBeforeWrite(TokenCacheNotificationArgs args) { if (BeforeWrite != null) { BeforeWrite(args); } }
internal void OnBeforeAccess(TokenCacheNotificationArgs args) { if (BeforeAccess != null) { BeforeAccess(args); } }
internal void OnAfterAccess(TokenCacheNotificationArgs args) { if (AfterAccess != null) { AfterAccess(args); } }
internal IEnumerable<User> GetUsers(string clientId) { lock (lockObject) { TokenCacheNotificationArgs args = new TokenCacheNotificationArgs { TokenCache = this }; this.OnBeforeAccess(args); this.OnBeforeWrite(args); List<User> users = new List<User>(); IEnumerable<string> homeOids = this.GetHomeObjectIdsFromCache(clientId); foreach (string homeOid in homeOids) { User localUser = this.ReadItems(clientId).First(item => !string.IsNullOrEmpty(item.HomeObjectId) && item.HomeObjectId.Equals(homeOid)).User; localUser.ClientId = clientId; localUser.TokenCache = this; users.Add(localUser); } this.HasStateChanged = true; this.OnAfterAccess(args); return users; } }
/// <summary> /// Clears the cache by deleting all the items. Note that if the cache is the default shared cache, clearing it would /// impact all the instances of <see cref="PublicClientApplication" /> which share that cache. /// </summary> public virtual void Clear(string clientId) { lock (lockObject) { TokenCacheNotificationArgs args = new TokenCacheNotificationArgs {TokenCache = this}; this.OnBeforeAccess(args); this.OnBeforeWrite(args); foreach (var item in this.ReadItemsFromCache(clientId)) { this.DeleteItemFromCache(item); } this.HasStateChanged = true; this.OnAfterAccess(args); } }
/// <summary> /// Deletes an item from the cache. /// </summary> /// <param name="item">The item to delete from the cache</param> internal void DeleteItem(TokenCacheItem item) { lock (lockObject) { if (item == null) { throw new ArgumentNullException("item"); } PlatformPlugin.Logger.Information(null, "Deleting token in the cache"); TokenCacheNotificationArgs args = new TokenCacheNotificationArgs { TokenCache = this, Scope = item.Scope.AsArray(), ClientId = item.ClientId, User = item.User, Policy = item.Policy }; this.OnBeforeAccess(args); this.OnBeforeWrite(args); DeleteItemFromCache(item); this.HasStateChanged = true; this.OnAfterAccess(args); } }
/// <remarks> /// Get accounts should not make a network call, if possible. /// </remarks> async Task <IEnumerable <IAccount> > ITokenCacheInternal.GetAccountsAsync(string authority, RequestContext requestContext) { var environment = Authority.GetEnviroment(authority); // FetchAllAccountItemsFromCacheAsync... IEnumerable <MsalRefreshTokenCacheItem> rtCacheItems; IEnumerable <MsalAccountCacheItem> accountCacheItems; AdalUsersForMsal adalUsersResult; bool filterByClientId = !_featureFlags.IsFociEnabled; await _semaphoreSlim.WaitAsync().ConfigureAwait(false); try { var args = new TokenCacheNotificationArgs { TokenCache = new NoLockTokenCacheProxy(this), ClientId = ClientId, Account = null }; await OnBeforeAccessAsync(args).ConfigureAwait(false); try { rtCacheItems = GetAllRefreshTokensWithNoLocks(filterByClientId); accountCacheItems = _accessor.GetAllAccounts(); adalUsersResult = CacheFallbackOperations.GetAllAdalUsersForMsal( Logger, LegacyCachePersistence, ClientId); } finally { await OnAfterAccessAsync(args).ConfigureAwait(false); } } finally { _semaphoreSlim.Release(); } // Multi-cloud support - must filter by env. // Use all env aliases to filter, in case PreferredCacheEnv changes in the future ISet <string> existingEnvs = new HashSet <string>( accountCacheItems.Select(aci => aci.Environment), StringComparer.OrdinalIgnoreCase); var aliases = await GetEnvAliasesTryAvoidNetworkCallAsync( authority, adalUsersResult.GetAdalUserEnviroments(), existingEnvs, requestContext) .ConfigureAwait(false); rtCacheItems = rtCacheItems.Where(rt => aliases.ContainsOrdinalIgnoreCase(rt.Environment)); accountCacheItems = accountCacheItems.Where(acc => aliases.ContainsOrdinalIgnoreCase(acc.Environment)); IDictionary <string, Account> clientInfoToAccountMap = new Dictionary <string, Account>(); foreach (MsalRefreshTokenCacheItem rtItem in rtCacheItems) { foreach (MsalAccountCacheItem account in accountCacheItems) { if (RtMatchesAccount(rtItem, account)) { clientInfoToAccountMap[rtItem.HomeAccountId] = new Account( account.HomeAccountId, account.PreferredUsername, environment); // Preserve the env passed in by the user break; } } } List <IAccount> accounts = UpdateWithAdalAccounts( environment, aliases, adalUsersResult, clientInfoToAccountMap); return(accounts); }
public void OnAfterAccess(TokenCacheNotificationArgs args) { AfterAccess?.Invoke(args); }
protected abstract Task StoreMsalCacheData( TokenCacheNotificationArgs context, byte[] msalTokenCacheData);
public void OnBeforeWrite(TokenCacheNotificationArgs args) { BeforeWrite?.Invoke(args); }
async Task <Tuple <MsalAccessTokenCacheItem, MsalIdTokenCacheItem> > ITokenCacheInternal.SaveTokenResponseAsync( AuthenticationRequestParameters requestParams, MsalTokenResponse response) { MsalAccessTokenCacheItem msalAccessTokenCacheItem = null; MsalRefreshTokenCacheItem msalRefreshTokenCacheItem = null; MsalIdTokenCacheItem msalIdTokenCacheItem = null; MsalAccountCacheItem msalAccountCacheItem = null; IdToken idToken = IdToken.Parse(response.IdToken); var tenantId = Authority .CreateAuthority(requestParams.TenantUpdatedCanonicalAuthority.AuthorityInfo.CanonicalAuthority) .TenantId; bool isAdfsAuthority = requestParams.AuthorityInfo.AuthorityType == AuthorityType.Adfs; string preferredUsername = GetPreferredUsernameFromIdToken(isAdfsAuthority, idToken); string username = isAdfsAuthority ? idToken?.Upn : preferredUsername; string homeAccountId = GetHomeAccountId(requestParams, response, idToken); // 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.TenantUpdatedCanonicalAuthority.AuthorityInfo.CanonicalAuthority, requestParams.RequestContext) .ConfigureAwait(false); #region Create Cache Objects if (!string.IsNullOrEmpty(response.AccessToken)) { msalAccessTokenCacheItem = new MsalAccessTokenCacheItem( instanceDiscoveryMetadata.PreferredCache, requestParams.ClientId, response, tenantId, homeAccountId, requestParams.AuthenticationScheme.KeyId) { UserAssertionHash = requestParams.UserAssertion?.AssertionHash, IsAdfs = isAdfsAuthority }; } if (!string.IsNullOrEmpty(response.RefreshToken)) { msalRefreshTokenCacheItem = new MsalRefreshTokenCacheItem( instanceDiscoveryMetadata.PreferredCache, requestParams.ClientId, response, homeAccountId); if (!_featureFlags.IsFociEnabled) { msalRefreshTokenCacheItem.FamilyId = null; } } if (idToken != null) { msalIdTokenCacheItem = new MsalIdTokenCacheItem( instanceDiscoveryMetadata.PreferredCache, requestParams.ClientId, response, tenantId, homeAccountId) { IsAdfs = isAdfsAuthority }; msalAccountCacheItem = new MsalAccountCacheItem( instanceDiscoveryMetadata.PreferredCache, response.ClientInfo, homeAccountId, idToken, preferredUsername, tenantId); } #endregion Account account = new Account( homeAccountId, username, instanceDiscoveryMetadata.PreferredCache); await _semaphoreSlim.WaitAsync().ConfigureAwait(false); try { var args = new TokenCacheNotificationArgs( this, ClientId, account, hasStateChanged: true, (this as ITokenCacheInternal).IsApplicationCache); #pragma warning disable CS0618 // Type or member is obsolete HasStateChanged = true; #pragma warning restore CS0618 // Type or member is obsolete try { await(this as ITokenCacheInternal).OnBeforeAccessAsync(args).ConfigureAwait(false); await(this as ITokenCacheInternal).OnBeforeWriteAsync(args).ConfigureAwait(false); 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); _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.ClientId, instanceDiscoveryMetadata.PreferredCache, response.FamilyId); // Do not save RT in ADAL cache for confidential client or B2C if (!requestParams.IsClientCredentialRequest && requestParams.AuthorityInfo.AuthorityType != AuthorityType.B2C) { var authorityWithPrefferedCache = Authority.CreateAuthorityWithEnvironment( requestParams.TenantUpdatedCanonicalAuthority.AuthorityInfo, instanceDiscoveryMetadata.PreferredCache); CacheFallbackOperations.WriteAdalRefreshToken( Logger, LegacyCachePersistence, msalRefreshTokenCacheItem, msalIdTokenCacheItem, authorityWithPrefferedCache.AuthorityInfo.CanonicalAuthority, msalIdTokenCacheItem.IdToken.ObjectId, response.Scope); } } finally { await(this as ITokenCacheInternal).OnAfterAccessAsync(args).ConfigureAwait(false); #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)); } finally { _semaphoreSlim.Release(); } }
public void BeforeAccess(TokenCacheNotificationArgs args) { }
public void AfterAccess(TokenCacheNotificationArgs args) { }
// Triggered right before MSAL needs to access the cache. // Reload the cache from the persistent store in case it changed since the last access. private void BeforeAccessNotification(TokenCacheNotificationArgs args) { Load(); }
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(); } }