private void RunAcquireTokenSilentCacheOnlyTest(string authority, bool expectNetworkDiscovery) { var receiver = new MyReceiver(); using (MockHttpAndServiceBundle testHarness = base.CreateTestHarness()) { PublicClientApplication app = PublicClientApplicationBuilder.Create(TestConstants.ClientId) .WithAuthority(authority, true) .WithHttpManager(testHarness.HttpManager) .WithTelemetry(receiver.HandleTelemetryEvents) .BuildConcrete(); var tokenCacheHelper = new TokenCacheHelper(); tokenCacheHelper.PopulateCache(app.UserTokenCacheInternal.Accessor); app.UserTokenCacheInternal.Accessor.DeleteAccessToken(new MsalAccessTokenCacheKey( TestConstants.ProductionPrefNetworkEnvironment, TestConstants.Utid, TestConstants.s_userIdentifier, TestConstants.ClientId, TestConstants.ScopeForAnotherResourceStr, TestConstants.Bearer)); if (expectNetworkDiscovery) { string host = new Uri(authority).Host; string discoveryHost = KnownMetadataProvider.IsKnownEnvironment(host) ? host : AadAuthority.DefaultTrustedHost; string discoveryEndpoint = $"https://{discoveryHost}/common/discovery/instance"; var jsonResponse = TestConstants.DiscoveryJsonResponse.Replace("login.microsoft.com", host); testHarness.HttpManager.AddMockHandler( MockHelpers.CreateInstanceDiscoveryMockHandler(discoveryEndpoint, jsonResponse)); } Task <AuthenticationResult> task = app .AcquireTokenSilent( TestConstants.s_scope.ToArray(), new Account(TestConstants.s_userIdentifier, TestConstants.DisplayableId, null)) .WithAuthority(app.Authority, false) .WithForceRefresh(false) .ExecuteAsync(CancellationToken.None); AuthenticationResult result = task.Result; Assert.IsNotNull(result); Assert.AreEqual(TestConstants.DisplayableId, result.Account.Username); Assert.AreEqual(TestConstants.s_scope.AsSingleString(), result.Scopes.AsSingleString()); Assert.AreEqual(2, app.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Count()); Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.GetAllRefreshTokens().Count()); Assert.IsNotNull(receiver.EventsReceived.Find(anEvent => // Expect finding such an event anEvent[EventBase.EventNameKey].EndsWith("api_event") && anEvent[ApiEvent.WasSuccessfulKey] == "true" && anEvent[MsalTelemetryBlobEventNames.ApiIdConstStrKey] == "1007")); } }
public void KnownMetadataProvider_IsKnown() { Assert.IsFalse(KnownMetadataProvider.IsKnownEnvironment(null)); Assert.IsFalse(KnownMetadataProvider.IsKnownEnvironment("")); Assert.IsFalse(KnownMetadataProvider.IsKnownEnvironment("bogus")); Assert.IsTrue(KnownMetadataProvider.IsKnownEnvironment("login.microsoftonline.de")); Assert.IsTrue(KnownMetadataProvider.IsKnownEnvironment("LOGIN.microsoftonline.de")); }
public void KnownMetadataProvider_publicEnvironment() { Assert.IsFalse(KnownMetadataProvider.IsPublicEnvironment("")); Assert.IsFalse(KnownMetadataProvider.IsPublicEnvironment(null)); Assert.IsFalse(KnownMetadataProvider.IsPublicEnvironment("unknown")); Assert.IsFalse(KnownMetadataProvider.IsPublicEnvironment("login.microsoftonline.de")); Assert.IsTrue(KnownMetadataProvider.IsPublicEnvironment("login.microsoft.com")); Assert.IsTrue(KnownMetadataProvider.IsPublicEnvironment("login.microsoftonline.com")); Assert.IsTrue(KnownMetadataProvider.IsPublicEnvironment("Login.microsoftonline.com")); }
public static void AddInstanceDiscoveryMockHandler(this MockHttpManager httpManager, string authority) { Uri authorityURI = new Uri(authority); string discoveryHost = KnownMetadataProvider.IsKnownEnvironment(authorityURI.Host) ? authorityURI.Host : AadAuthority.DefaultTrustedHost; string discoveryEndpoint = UriBuilderExtensions.GetHttpsUriWithOptionalPort($"https://{discoveryHost}/common/discovery/instance", authorityURI.Port); httpManager.AddMockHandler( MockHelpers.CreateInstanceDiscoveryMockHandler(discoveryEndpoint)); }
public static void AddInstanceDiscoveryMockHandler(this MockHttpManager httpManager, string authority) { string host = new Uri(authority).Host; string discoveryHost = KnownMetadataProvider.IsKnownEnvironment(host) ? host : AadAuthority.DefaultTrustedHost; string discoveryEndpoint = $"https://{discoveryHost}/common/discovery/instance"; httpManager.AddMockHandler( MockHelpers.CreateInstanceDiscoveryMockHandler(discoveryEndpoint)); }
private string GetRegionalizedEnviroment(Uri authority, string region) { var builder = new UriBuilder(authority); // special rule for Global cloud if (KnownMetadataProvider.IsPublicEnvironment(authority.Host)) { return($"{region}.login.microsoft.com"); } return($"{region}.{builder.Host}"); }
private async Task <Uri> BuildAuthorityWithRegionAsync(Uri canonicalAuthority, RequestContext requestContext) { string regionToUse = requestContext.ServiceBundle.Config.AuthorityInfo.RegionToUse; if (s_region.IsNullOrEmpty()) { try { s_region = await GetRegionAsync(requestContext).ConfigureAwait(false); if (!regionToUse.IsNullOrEmpty()) { requestContext.ApiEvent.IsValidUserProvidedRegion = s_region.Equals(regionToUse); requestContext.Logger.Info($"The auto detected region is {s_region}."); if (s_region.Equals(regionToUse)) { requestContext.Logger.Info("The region provided by the user is valid and equal to the auto detected region."); } else { requestContext.Logger.Info($"The region provided by the user is invalid. Region detected: {s_region} Region provided: {regionToUse}"); } } } catch (MsalServiceException e) { if (regionToUse.IsNullOrEmpty()) { throw e; } s_region = regionToUse; requestContext.Logger.Info($"Region auto detection failed. Region provided by the user will be used: ${regionToUse}."); LogTelemetryData(s_region, RegionSource.UserProvided, requestContext); } } var builder = new UriBuilder(canonicalAuthority); if (KnownMetadataProvider.IsPublicEnvironment(canonicalAuthority.Host)) { builder.Host = $"{s_region}.login.microsoft.com"; } else { builder.Host = $"{s_region}.{builder.Host}"; } return(builder.Uri); }
/// <summary> /// AAD performs authority validation by calling the instance metadata endpoint. This is a bit unfortunate, /// because instance metadata is used for aliasing, and authority validation is orthogonal to that. /// MSAL must figure out aliasing even if ValidateAuthority is set to false. /// </summary> public async Task ValidateAuthorityAsync( AuthorityInfo authorityInfo, RequestContext requestContext) { var authorityUri = new Uri(authorityInfo.CanonicalAuthority); if (authorityInfo.ValidateAuthority && !KnownMetadataProvider.IsKnownEnvironment(authorityUri.Host)) { // MSAL will throw if the instance discovery URI does not respond with a valid json await _serviceBundle.InstanceDiscoveryManager.GetMetadataEntryAsync( authorityInfo.CanonicalAuthority, requestContext).ConfigureAwait(false); } }
/// <inheritdoc /> public async Task <string> ValidateAuthorityAndGetOpenIdDiscoveryEndpointAsync( AuthorityInfo authorityInfo, string userPrincipalName, RequestContext requestContext) { var authorityUri = new Uri(authorityInfo.CanonicalAuthority); if (authorityInfo.ValidateAuthority && !KnownMetadataProvider.IsKnownEnvironment(authorityUri.Host)) { // MSAL will throw if the instance discovery URI does not respond with a valid json await _serviceBundle.InstanceDiscoveryManager.GetMetadataEntryAsync( authorityInfo.CanonicalAuthority, requestContext).ConfigureAwait(false); } return(authorityInfo.CanonicalAuthority + Constants.OpenIdConfigurationEndpoint); }
private async Task <Uri> BuildAuthorityWithRegionAsync(Uri canonicalAuthority, RequestContext requestContext) { string region = await GetRegionAsync(requestContext).ConfigureAwait(false); var builder = new UriBuilder(canonicalAuthority); if (KnownMetadataProvider.IsPublicEnvironment(canonicalAuthority.Host)) { builder.Host = $"{region}.login.microsoft.com"; } else { builder.Host = $"{region}.{builder.Host}"; } return(builder.Uri); }
private string GetRegionalizedEnviroment(Uri authority, string region) { string host = authority.Host; if (KnownMetadataProvider.IsPublicEnvironment(host)) { return($"{region}.{PublicEnvForRegional}"); } // Regional business rule - use the PreferredNetwork value for public and sovereign clouds // but do not do instance discovery for it - rely on cached values only if (KnownMetadataProvider.TryGetKnownEnviromentPreferredNetwork(host, out var preferredNetworkEnv)) { host = preferredNetworkEnv; } return($"{region}.{host}"); }
/// <summary> /// AAD performs authority validation by calling the instance metadata endpoint. This is a bit unfortunate, /// because instance metadata is used for aliasing, and authority validation is orthogonal to that. /// MSAL must figure out aliasing even if ValidateAuthority is set to false. /// </summary> public async Task ValidateAuthorityAsync( AuthorityInfo authorityInfo) { var authorityUri = new Uri(authorityInfo.CanonicalAuthority); bool isKnownEnv = KnownMetadataProvider.IsKnownEnvironment(authorityUri.Host); _requestContext.Logger.Info($"Authority validation enabled? {authorityInfo.ValidateAuthority}. "); _requestContext.Logger.Info($"Authority validation - is known env? {isKnownEnv}. "); if (authorityInfo.ValidateAuthority && !isKnownEnv) { _requestContext.Logger.Info($"Authority validation is being performed. "); // MSAL will throw if the instance discovery URI does not respond with a valid json await _requestContext.ServiceBundle.InstanceDiscoveryManager.GetMetadataEntryAsync( authorityInfo, _requestContext).ConfigureAwait(false); } }
public void KnownMetadataProvider_RespondsIfEnvironmentsAreKnown() { // Arrange KnownMetadataProvider knownMetadataProvider = new KnownMetadataProvider(); InstanceDiscoveryMetadataEntry result = knownMetadataProvider.GetMetadata( LoginMicrosoftOnlineCom, null, _logger); Assert.IsNotNull(result); result = knownMetadataProvider.GetMetadata( LoginMicrosoftOnlineCom, Enumerable.Empty <string>(), _logger); Assert.IsNotNull(result); result = knownMetadataProvider.GetMetadata( LoginMicrosoftOnlineCom, new[] { LoginMicrosoftOnlineCom }, _logger); Assert.IsNotNull(result); result = knownMetadataProvider.GetMetadata( LoginMicrosoftOnlineCom, new[] { LoginMicrosoftOnlineCom }, _logger); Assert.IsNotNull(result); result = knownMetadataProvider.GetMetadata( LoginMicrosoftOnlineCom, new[] { "login.windows.net", "login.microsoft.com", "login.partner.microsoftonline.cn" }, _logger); Assert.IsNotNull(result); result = knownMetadataProvider.GetMetadata( "login.partner.microsoftonline.cn", new[] { "login.windows.net", "login.microsoft.com", "login.partner.microsoftonline.cn" }, _logger); Assert.IsNotNull(result); result = knownMetadataProvider.GetMetadata( "login.windows-ppe.net", new[] { "login.windows-ppe.net", "sts.windows-ppe.net", "login.microsoft-ppe.com" }, _logger); Assert.IsNotNull(result); result = knownMetadataProvider.GetMetadata( LoginMicrosoftOnlineCom, new[] { "login.windows.net", "bogus", "login.partner.microsoftonline.cn" }, _logger); Assert.IsNull(result); result = knownMetadataProvider.GetMetadata( "bogus", new[] { "login.windows.net", "login.microsoft.com", "login.partner.microsoftonline.cn" }, _logger); Assert.IsNull(result); }
private void RunAcquireTokenSilentCacheOnlyTest(string authority, bool expectNetworkDiscovery) { using (MockHttpAndServiceBundle testHarness = base.CreateTestHarness()) { PublicClientApplication app = PublicClientApplicationBuilder.Create(TestConstants.ClientId) .WithAuthority(authority, true) .WithHttpManager(testHarness.HttpManager) .BuildConcrete(); TokenCacheHelper.PopulateCache(app.UserTokenCacheInternal.Accessor); if (expectNetworkDiscovery) { string host = new Uri(authority).Host; string discoveryHost = KnownMetadataProvider.IsKnownEnvironment(host) ? host : AadAuthority.DefaultTrustedHost; string discoveryEndpoint = $"https://{discoveryHost}/common/discovery/instance"; var jsonResponse = TestConstants.DiscoveryJsonResponse.Replace("login.microsoft.com", host); testHarness.HttpManager.AddMockHandler( MockHelpers.CreateInstanceDiscoveryMockHandler(discoveryEndpoint, jsonResponse)); } Task <AuthenticationResult> task = app .AcquireTokenSilent( TestConstants.s_scope.ToArray(), new Account(TestConstants.s_userIdentifier, TestConstants.DisplayableId, null)) .WithForceRefresh(false) .ExecuteAsync(CancellationToken.None); AuthenticationResult result = task.Result; Assert.IsNotNull(result); Assert.AreEqual(TestConstants.DisplayableId, result.Account.Username); Assert.AreEqual(TestConstants.s_scope.AsSingleString(), result.Scopes.AsSingleString()); Assert.AreEqual(2, app.UserTokenCacheInternal.Accessor.GetAllAccessTokens().Count()); Assert.AreEqual(1, app.UserTokenCacheInternal.Accessor.GetAllRefreshTokens().Count()); } }
private void LogRequestStarted(AuthenticationRequestParameters authenticationRequestParameters) { if (authenticationRequestParameters.RequestContext.Logger.IsLoggingEnabled(LogLevel.Info)) { int appHashCode = authenticationRequestParameters.AppConfig.GetHashCode(); string scopes = authenticationRequestParameters.Scope.AsSingleString(); string messageWithPii = string.Format( CultureInfo.InvariantCulture, "=== Token Acquisition ({3}) started:\n\tAuthority: {0}\n\tScope: {1}\n\tClientId: {2}\n\tAppHashCode: {4}", authenticationRequestParameters.AuthorityInfo?.CanonicalAuthority, scopes, authenticationRequestParameters.AppConfig.ClientId, GetType().Name, appHashCode); string messageWithoutPii = string.Format( CultureInfo.InvariantCulture, "=== Token Acquisition ({0}) started:\n\t Scopes: {1} AppHashCode: {2}", GetType().Name, scopes, appHashCode); if (authenticationRequestParameters.AuthorityInfo != null && KnownMetadataProvider.IsKnownEnvironment(authenticationRequestParameters.AuthorityInfo?.Host)) { messageWithoutPii += string.Format( CultureInfo.CurrentCulture, "\n\tAuthority Host: {0}", authenticationRequestParameters.AuthorityInfo?.Host); } authenticationRequestParameters.RequestContext.Logger.InfoPii(messageWithPii, messageWithoutPii); } if (authenticationRequestParameters.IsConfidentialClient && !CacheManager.TokenCacheInternal.IsTokenCacheSerialized()) { authenticationRequestParameters.RequestContext.Logger.Error("The default token cache provided by MSAL is not designed to be performant when used in confidential client applications. Please use token cache serialization. See https://aka.ms/msal-net-cca-token-cache-serialization."); } }
public async Task KnownInstanceMetadataIsUpToDateAsync() { const string validDiscoveryUri = @"https://login.microsoftonline.com/common/discovery/instance?api-version=1.1&authorization_endpoint=https%3A%2F%2Flogin.microsoftonline.com%2Fcommon%2Foauth2%2Fv2.0%2Fauthorize"; const string validPpeDiscoveryUri = @"https://login.windows-ppe.net/common/discovery/instance?api-version=1.1&authorization_endpoint=https%3A%2F%2Flogin.microsoftonline.com%2Fcommon%2Foauth2%2Fv2.0%2Fauthorize"; HttpClient httpClient = new HttpClient(); HttpResponseMessage discoveryResponse = await httpClient.SendAsync( new HttpRequestMessage( HttpMethod.Get, validDiscoveryUri)).ConfigureAwait(false); string discoveryJson = await discoveryResponse.Content.ReadAsStringAsync().ConfigureAwait(false); HttpResponseMessage ppeDiscoveryResponse = await httpClient.SendAsync( new HttpRequestMessage( HttpMethod.Get, validPpeDiscoveryUri)).ConfigureAwait(false); string ppeDiscoveryJson = await ppeDiscoveryResponse.Content.ReadAsStringAsync().ConfigureAwait(false); InstanceDiscoveryMetadataEntry[] actualMetadata = JsonHelper.DeserializeFromJson <InstanceDiscoveryResponse>(discoveryJson).Metadata; InstanceDiscoveryMetadataEntry[] actualPpeMetadata = JsonHelper.DeserializeFromJson <InstanceDiscoveryResponse>(ppeDiscoveryJson).Metadata; var processedMetadata = new Dictionary <string, InstanceDiscoveryMetadataEntry>(); foreach (InstanceDiscoveryMetadataEntry entry in actualMetadata.Concat(actualPpeMetadata)) { foreach (var alias in entry.Aliases) { processedMetadata[alias] = entry; } } IDictionary <string, InstanceDiscoveryMetadataEntry> expectedMetadata = KnownMetadataProvider.GetAllEntriesForTest(); CoreAssert.AssertDictionariesAreEqual( expectedMetadata, processedMetadata, new InstanceDiscoveryMetadataEntryComparer()); }
private void LogRequestStarted(AuthenticationRequestParameters authenticationRequestParameters) { if (authenticationRequestParameters.RequestContext.Logger.IsLoggingEnabled(LogLevel.Info)) { string scopes = authenticationRequestParameters.Scope.AsSingleString(); string messageWithPii = string.Format( CultureInfo.InvariantCulture, "=== Token Acquisition ({3}) started:\n\tAuthority: {0}\n\tScope: {1}\n\tClientId: {2}\n\t", authenticationRequestParameters.AuthorityInfo?.CanonicalAuthority, scopes, authenticationRequestParameters.AppConfig.ClientId, GetType().Name); string messageWithoutPii = string.Format( CultureInfo.InvariantCulture, "=== Token Acquisition ({0}) started:\n\t Scopes: {1}", GetType().Name, scopes); if (authenticationRequestParameters.AuthorityInfo != null && KnownMetadataProvider.IsKnownEnvironment(authenticationRequestParameters.AuthorityInfo?.Host)) { messageWithoutPii += string.Format( CultureInfo.CurrentCulture, "\n\tAuthority Host: {0}", authenticationRequestParameters.AuthorityInfo?.Host); } authenticationRequestParameters.RequestContext.Logger.InfoPii(messageWithPii, messageWithoutPii); } if (authenticationRequestParameters.IsConfidentialClient && !authenticationRequestParameters.IsClientCredentialRequest && !CacheManager.TokenCacheInternal.IsAppSubscribedToSerializationEvents()) { authenticationRequestParameters.RequestContext.Logger.Warning( "Only in-memory caching is used. The cache is not persisted and will be lost if the machine is restarted. It also does not scale for a web app or web API, where the number of users can grow large. In production, web apps and web APIs should use distributed caching like Redis. See https://aka.ms/msal-net-cca-token-cache-serialization"); } }
public void KnownMetadataProvider_RespondsIfEnvironmentsAreKnown() { // Arrange KnownMetadataProvider knownMetadataProvider = new KnownMetadataProvider(); var result = knownMetadataProvider.GetMetadata( LoginMicrosoftOnlineCom, null); Assert.IsNotNull(result); result = knownMetadataProvider.GetMetadata( LoginMicrosoftOnlineCom, Enumerable.Empty <string>()); Assert.IsNotNull(result); result = knownMetadataProvider.GetMetadata( LoginMicrosoftOnlineCom, new[] { LoginMicrosoftOnlineCom }); Assert.IsNotNull(result); result = knownMetadataProvider.GetMetadata( LoginMicrosoftOnlineCom, new[] { LoginMicrosoftOnlineCom }); Assert.IsNotNull(result); result = knownMetadataProvider.GetMetadata( LoginMicrosoftOnlineCom, new[] { "login.windows.net", "login.microsoft.com", "login.partner.microsoftonline.cn" }); Assert.IsNotNull(result); result = knownMetadataProvider.GetMetadata( "login.partner.microsoftonline.cn", new[] { "login.windows.net", "login.microsoft.com", "login.partner.microsoftonline.cn" }); Assert.IsNotNull(result); result = knownMetadataProvider.GetMetadata( LoginMicrosoftOnlineCom, new[] { "login.windows.net", "bogus", "login.partner.microsoftonline.cn" }); Assert.IsNull(result); result = knownMetadataProvider.GetMetadata( "bogus", new[] { "login.windows.net", "login.microsoft.com", "login.partner.microsoftonline.cn" }); Assert.IsNull(result); }