public void TestCacheKeyForTenantAuthority() { // Arrange const string tenantId = TestConstants.AadTenantId; var appTokenCache = new TokenCache(this._serviceBundle, isApplicationTokenCache: true); var requestContext = new RequestContext(this._serviceBundle, Guid.NewGuid()); var tenantAuthority = AuthorityInfo.FromAadAuthority(AzureCloudInstance.AzurePublic, tenant: tenantId, validateAuthority: false); var acquireTokenCommonParameters = new AcquireTokenCommonParameters { ApiId = ApiEvent.ApiIds.AcquireTokenForClient, AuthorityOverride = tenantAuthority }; var parameters = new AuthenticationRequestParameters( this._serviceBundle, appTokenCache, acquireTokenCommonParameters, requestContext, Authority.CreateAuthority(tenantAuthority)); // Act var actualKey = SuggestedWebCacheKeyFactory.GetKeyFromRequest(parameters); // Assert Assert.IsNotNull(actualKey); var expectedKey = $"{this._serviceBundle.Config.ClientId}_{tenantId}_AppTokenCache"; Assert.AreEqual(expectedKey, actualKey); }
public void TestCacheKeyForADFSAuthority() { // Arrange var appTokenCache = new TokenCache(this._serviceBundle, isApplicationTokenCache: true); var requestContext = new RequestContext(this._serviceBundle, Guid.NewGuid()); var authority = Authority.CreateAuthority(TestConstants.ADFSAuthority, true); requestContext.ServiceBundle.Config.AuthorityInfo = authority.AuthorityInfo; var acquireTokenCommonParameters = new AcquireTokenCommonParameters { ApiId = ApiEvent.ApiIds.AcquireTokenForClient, }; var parameters = new AuthenticationRequestParameters( this._serviceBundle, appTokenCache, acquireTokenCommonParameters, requestContext, authority); // Act var actualKey = SuggestedWebCacheKeyFactory.GetKeyFromRequest(parameters); // Assert Assert.IsNotNull(actualKey); var expectedKey = $"{this._serviceBundle.Config.ClientId}__AppTokenCache"; Assert.AreEqual(expectedKey, actualKey); }
internal static void PopulateDefaultAppTokenCache( ConfidentialClientApplication app, MsalAccessTokenCacheItem atItem = null) { if (atItem == null) { atItem = CreateAccessTokenItem(); } InMemoryTokenCacheAccessor accessor = new InMemoryTokenCacheAccessor(new NullLogger()); accessor.SaveAccessToken(atItem); string key = SuggestedWebCacheKeyFactory.GetClientCredentialKey(atItem.ClientId, atItem.TenantId); byte[] bytes = new TokenCacheJsonSerializer(accessor).Serialize(null); app.InMemoryPartitionedCacheSerializer.CachePartition[key] = bytes; // force a cache read var args = new TokenCacheNotificationArgs( app.AppTokenCacheInternal, app.AppConfig.ClientId, null, hasStateChanged: false, true, hasTokens: true, suggestedCacheKey: key); app.AppTokenCacheInternal.OnBeforeAccessAsync(args).GetAwaiter().GetResult(); }
public void TestCacheKeyForRemoveAccount() { // Arrange var appTokenCache = new TokenCache(_serviceBundle, isApplicationTokenCache: true); var requestContext = new RequestContext(_serviceBundle, Guid.NewGuid()); var tenantAuthority = AuthorityInfo.FromAadAuthority(AzureCloudInstance.AzurePublic, tenant: TestConstants.AadTenantId, validateAuthority: false); var acquireTokenCommonParameters = new AcquireTokenCommonParameters { ApiId = ApiEvent.ApiIds.RemoveAccount, AuthorityOverride = tenantAuthority }; var parameters = new AuthenticationRequestParameters( _serviceBundle, appTokenCache, acquireTokenCommonParameters, requestContext, Authority.CreateAuthority(tenantAuthority), TestConstants.HomeAccountId) { Account = new Account(TestConstants.HomeAccountId, TestConstants.Username, TestConstants.ProductionPrefCacheEnvironment) }; // Act var actualKey = SuggestedWebCacheKeyFactory.GetKeyFromRequest(parameters); // Assert Assert.IsNotNull(actualKey); Assert.AreEqual(parameters.HomeAccountId, actualKey); }
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; 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); requestParams.RequestContext.Logger.Verbose("[SaveTokenResponseAsync] Entering token cache semaphore. "); await _semaphoreSlim.WaitAsync().ConfigureAwait(false); requestParams.RequestContext.Logger.Verbose("[SaveTokenResponseAsync] Entered token cache semaphore. "); 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(), requestParams.RequestContext.UserCancellationToken, suggestedCacheKey: suggestedWebCacheKey); Stopwatch sw = Stopwatch.StartNew(); 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(), requestParams.RequestContext.UserCancellationToken, suggestedCacheKey: suggestedWebCacheKey); Stopwatch sw = Stopwatch.StartNew(); 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(); requestParams.RequestContext.Logger.Verbose("[SaveTokenResponseAsync] Released token cache semaphore. "); } }
private void PopulateAppCache(ConfidentialClientApplication cca, TokenDifference tokenDifference, int size) { Dictionary <string, InMemoryTokenCacheAccessor> accessors = new Dictionary <string, InMemoryTokenCacheAccessor>(); string key = ""; for (int i = 0; i < size; i++) { string tenantId = "tid"; string scope = "scope"; switch (tokenDifference) { case TokenDifference.ByScope: scope = $"scope_{i}"; break; case TokenDifference.ByTenant: tenantId = $"tid_{i}"; break; default: throw new NotImplementedException(); } MsalAccessTokenCacheItem atItem = new MsalAccessTokenCacheItem( TestConstants.ProductionPrefCacheEnvironment, TestConstants.ClientId, scope, tenantId, "", new DateTimeOffset(DateTime.UtcNow + TimeSpan.FromSeconds(3600)), new DateTimeOffset(DateTime.UtcNow + TimeSpan.FromSeconds(3600)), null, null); key = SuggestedWebCacheKeyFactory.GetClientCredentialKey(atItem.ClientId, atItem.TenantId); InMemoryTokenCacheAccessor accessor; if (!accessors.TryGetValue(key, out accessor)) { accessor = new InMemoryTokenCacheAccessor(new NullLogger()); accessors[key] = accessor; } accessor.SaveAccessToken(atItem); if (tokenDifference == TokenDifference.ByTenant || (tokenDifference == TokenDifference.ByScope && i == size - 1)) { byte[] bytes = new TokenCacheJsonSerializer(accessor).Serialize(null); cca.InMemoryPartitionedCacheSerializer.CachePartition[key] = bytes; } } // force a cache read, otherwise MSAL won't have the tokens in memory // force a cache read var args = new TokenCacheNotificationArgs( cca.AppTokenCacheInternal, cca.AppConfig.ClientId, null, hasStateChanged: false, true, hasTokens: true, cancellationToken: CancellationToken.None, suggestedCacheKey: key); cca.AppTokenCacheInternal.OnBeforeAccessAsync(args).GetAwaiter().GetResult(); }
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); 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.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, hasTokens: (this as ITokenCacheInternal).HasTokensNoLocks(), suggestedCacheKey: suggestedWebCacheKey); #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 authorityWithPreferredCache = Authority.CreateAuthorityWithEnvironment( requestParams.TenantUpdatedCanonicalAuthority.AuthorityInfo, instanceDiscoveryMetadata.PreferredCache); CacheFallbackOperations.WriteAdalRefreshToken( Logger, LegacyCachePersistence, msalRefreshTokenCacheItem, msalIdTokenCacheItem, authorityWithPreferredCache.AuthorityInfo.CanonicalAuthority, msalIdTokenCacheItem.IdToken.ObjectId, response.Scope); } } finally { var args2 = new TokenCacheNotificationArgs( this, ClientId, account, hasStateChanged: true, (this as ITokenCacheInternal).IsApplicationCache, (this as ITokenCacheInternal).HasTokensNoLocks(), suggestedCacheKey: suggestedWebCacheKey); await(this as ITokenCacheInternal).OnAfterAccessAsync(args2).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(); } }