private async Task UpdateAuthorityWithPreferredNetworkHostAsync() { InstanceDiscoveryMetadataEntry metadata = await ServiceBundle.InstanceDiscoveryManager.GetMetadataEntryAsync( AuthenticationRequestParameters.AuthorityInfo.CanonicalAuthority, AuthenticationRequestParameters.RequestContext) .ConfigureAwait(false); AuthenticationRequestParameters.Authority = Authority.CreateAuthorityWithEnvironment( AuthenticationRequestParameters.AuthorityInfo, metadata.PreferredNetwork); }
async Task <Tuple <MsalAccessTokenCacheItem, MsalIdTokenCacheItem> > ITokenCacheInternal.SaveTokenResponseAsync( AuthenticationRequestParameters requestParams, MsalTokenResponse response) { var tenantId = Authority .CreateAuthority(requestParams.TenantUpdatedCanonicalAuthority.AuthorityInfo.CanonicalAuthority) .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 = GetPreferredUsernameFromIdToken(isAdfsAuthority, 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); var msalAccessTokenCacheItem = new MsalAccessTokenCacheItem( instanceDiscoveryMetadata.PreferredCache, requestParams.ClientId, response, tenantId, subject, requestParams.AuthenticationScheme.KeyId) { UserAssertionHash = requestParams.UserAssertion?.AssertionHash, IsAdfs = isAdfsAuthority }; MsalRefreshTokenCacheItem msalRefreshTokenCacheItem = null; MsalIdTokenCacheItem msalIdTokenCacheItem = null; if (idToken != null) { msalIdTokenCacheItem = new MsalIdTokenCacheItem( instanceDiscoveryMetadata.PreferredCache, 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, instanceDiscoveryMetadata.PreferredCache); } var args = new TokenCacheNotificationArgs(this, ClientId, account, 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 await(this as ITokenCacheInternal).OnBeforeAccessAsync(args).ConfigureAwait(false); try { await(this as ITokenCacheInternal).OnBeforeWriteAsync(args).ConfigureAwait(false); DeleteAccessTokensWithIntersectingScopes( requestParams, instanceDiscoveryMetadata.Aliases, tenantId, msalAccessTokenCacheItem.ScopeSet, msalAccessTokenCacheItem.HomeAccountId, msalAccessTokenCacheItem.TokenType); _accessor.SaveAccessToken(msalAccessTokenCacheItem); if (idToken != null) { _accessor.SaveIdToken(msalIdTokenCacheItem); var msalAccountCacheItem = new MsalAccountCacheItem( instanceDiscoveryMetadata.PreferredCache, response, preferredUsername, tenantId); //The ADFS direct scenario 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( instanceDiscoveryMetadata.PreferredCache, requestParams.ClientId, response, subject); if (!_featureFlags.IsFociEnabled) { msalRefreshTokenCacheItem.FamilyId = null; } requestParams.RequestContext.Logger.Info("Saving RT in cache..."); _accessor.SaveRefreshToken(msalRefreshTokenCacheItem); } UpdateAppMetadata(requestParams.ClientId, instanceDiscoveryMetadata.PreferredCache, 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)) { 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); } 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(); } }
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, hasTokens: (this as ITokenCacheInternal).HasTokensNoLocks(), requestParams.SuggestedWebAppCacheKey); #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(), requestParams.SuggestedWebAppCacheKey); 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(); } }
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 async Task <MsalTokenResponse> GetTokenResponseAsync(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (_requestParams.AppConfig.IsBrokerEnabled) { _logger.Info("Broker is configured. Starting broker flow without knowing the broker installation app link. "); MsalTokenResponse brokerTokenResponse = await FetchTokensFromBrokerAsync( null, // we don't have an installation URI yet cancellationToken) .ConfigureAwait(false); // if we don't get back a result, then continue with the WebUi if (brokerTokenResponse != null) { _logger.Info("Broker attempt completed successfully. "); Metrics.IncrementTotalAccessTokensFromBroker(); return(brokerTokenResponse); } if (string.Equals(_requestParams.AuthenticationScheme.AccessTokenType, Constants.PoPTokenType)) { _logger.Error("A broker application is required for Proof-of-Possesion, but one could not be found or communicated with. See https://aka.ms/msal-net-pop"); throw new MsalClientException(MsalError.BrokerApplicationRequired, MsalErrorMessage.CannotInvokeBrokerForPop); } _logger.Info("Broker attempt did not complete, most likely because the broker is not installed. Attempting to use a browser / web UI. "); cancellationToken.ThrowIfCancellationRequested(); } if (_requestParams.AppConfig.MultiCloudSupportEnabled) { _logger.Info("Instance Aware was configured."); _requestParams.AppConfig.ExtraQueryParameters[InstanceAwareParam] = "true"; } IAuthCodeRequestComponent authorizationFetcher = _authCodeRequestComponentOverride ?? new AuthCodeRequestComponent( _requestParams, _interactiveParameters); var result = await authorizationFetcher.FetchAuthCodeAndPkceVerifierAsync(cancellationToken) .ConfigureAwait(false); _logger.Info("An authorization code was retrieved from the /authorize endpoint. "); AuthorizationResult authResult = result.Item1; string authCode = authResult.Code; string pkceCodeVerifier = result.Item2; if (BrokerInteractiveRequestComponent.IsBrokerRequiredAuthCode(authCode, out string brokerInstallUri)) { return(await RunBrokerWithInstallUriAsync(brokerInstallUri, cancellationToken).ConfigureAwait(false)); } if (_requestParams.AppConfig.MultiCloudSupportEnabled && !string.IsNullOrEmpty(authResult.CloudInstanceHost)) { _logger.Info("Updating the authority to the cloud specific authority."); _requestParams.AuthorityManager = new AuthorityManager( _requestParams.RequestContext, Authority.CreateAuthorityWithEnvironment(_requestParams.Authority.AuthorityInfo, authResult.CloudInstanceHost)); await ResolveAuthorityAsync().ConfigureAwait(false); } _logger.Info("Exchanging the auth code for tokens. "); var authCodeExchangeComponent = _authCodeExchangeComponentOverride ?? new AuthCodeExchangeComponent( _requestParams, _interactiveParameters, authCode, pkceCodeVerifier, authResult.ClientInfo); MsalTokenResponse idpTokenResponse = await authCodeExchangeComponent.FetchTokensAsync(cancellationToken) .ConfigureAwait(false); Metrics.IncrementTotalAccessTokensFromIdP(); return(idpTokenResponse); }