public async Task TestInstanceDiscovery_WhenAuthorityIsInvalidButValidationIsNotRequired_ShouldCacheTheProvidedAuthorityAsync()
        {
            for (int i = 0; i < 2; i++) // Prepare 2 mock responses
            {
                HttpMessageHandlerFactory.AddMockHandler(new MockHttpMessageHandler(TestConstants.DefaultAuthorityHomeTenant)
                {
                    Method          = HttpMethod.Get,
                    Url             = $"https://{InstanceDiscovery.DefaultTrustedAuthority}/common/discovery/instance",
                    ResponseMessage = MockHelpers.CreateFailureResponseMessage("{\"error\":\"invalid_instance\"}")
                });
            }

            CallState callState = new CallState(Guid.NewGuid());
            string    host      = "invalid_instance.example.com";

            // ADAL still behaves correctly using developer provided authority
            var entry = await InstanceDiscovery.GetMetadataEntryAsync(new Uri($"https://{host}/tenant"), false, callState).ConfigureAwait(false);

            Assert.AreEqual(host, entry.PreferredNetwork);                     // No exception raised, the host is returned as-is
            Assert.AreEqual(1, HttpMessageHandlerFactory.MockHandlersCount()); // 1 mock response is consumed, 1 remaining

            // Subsequent requests do not result in further authority validation network requests for the process lifetime
            var entry2 = await InstanceDiscovery.GetMetadataEntryAsync(new Uri($"https://{host}/tenant"), false, callState).ConfigureAwait(false);

            Assert.AreEqual(host, entry2.PreferredNetwork);
            Assert.AreEqual(1, HttpMessageHandlerFactory.MockHandlersCount()); // Still 1 mock response remaining, so no new request was attempted
        }
        public async Task UpdateFromTemplateAsync(CallState callState)
        {
            if (!this.updatedFromTemplate)
            {
                var    authorityUri = new Uri(this.Authority);
                var    host         = authorityUri.Host;
                string path         = authorityUri.AbsolutePath.Substring(1);
                string tenant       = path.Substring(0, path.IndexOf("/", StringComparison.Ordinal));

                if (this.AuthorityType == AuthorityType.AAD)
                {
                    var metadata = await InstanceDiscovery.GetMetadataEntryAsync(authorityUri, this.ValidateAuthority, callState).ConfigureAwait(false);

                    host = metadata.PreferredNetwork;
                    // All the endpoints will use this updated host, and it affects future network calls, as desired.
                    // The Authority remains its original host, and will be used in TokenCache later.
                }
                else
                {
                    InstanceDiscovery.AddMetadataEntry(host);
                }
                this.AuthorizationUri      = InstanceDiscovery.FormatAuthorizeEndpoint(host, tenant);
                this.DeviceCodeUri         = string.Format(CultureInfo.InvariantCulture, "https://{0}/{1}/oauth2/devicecode", host, tenant);
                this.TokenUri              = string.Format(CultureInfo.InvariantCulture, "https://{0}/{1}/oauth2/token", host, tenant);
                this.UserRealmUri          = EnsureUrlEndsWithForwardSlash(string.Format(CultureInfo.InvariantCulture, "https://{0}/common/userrealm", host));
                this.IsTenantless          = (string.Compare(tenant, TenantlessTenantName, StringComparison.OrdinalIgnoreCase) == 0);
                this.SelfSignedJwtAudience = this.TokenUri;
                this.updatedFromTemplate   = true;
            }
        }
        public void TestInstanceDiscovery_WhenMultipleSimultaneousCallsWithTheSameAuthority_ShouldMakeOnlyOneRequest()
        {
            for (int i = 0; i < 2; i++) // Prepare 2 mock responses
            {
                HttpMessageHandlerFactory.AddMockHandler(MockHelpers.CreateInstanceDiscoveryMockHandler("https://login.windows.net/common/discovery/instance"));
            }

            CallState callState = new CallState(Guid.NewGuid());
            string    host      = "login.windows.net";

            Task.WaitAll( // Simulate several simultaneous calls
                InstanceDiscovery.GetMetadataEntryAsync(new Uri($"https://{host}/tenant"), true, callState),
                InstanceDiscovery.GetMetadataEntryAsync(new Uri($"https://{host}/tenant"), true, callState));
            Assert.AreEqual(1, HttpMessageHandlerFactory.MockHandlersCount()); // 1 mock response is consumed, 1 remaining
        }
        public async Task TestInstanceDiscovery_WhenAuthorityIsValidAndMetadataIsReturned_ShouldCacheAllReturnedAliasesAsync()
        {
            string host = "login.windows.net";

            for (int i = 0; i < 2; i++) // Prepare 2 mock responses
            {
                HttpMessageHandlerFactory.AddMockHandler(new MockHttpMessageHandler(TestConstants.DefaultAuthorityHomeTenant)
                {
                    Method          = HttpMethod.Get,
                    Url             = $"https://{host}/common/discovery/instance",
                    ResponseMessage = new HttpResponseMessage(HttpStatusCode.OK)
                    {
                        Content = new StringContent(
                            @"{
                            ""tenant_discovery_endpoint"" : ""https://login.microsoftonline.com/v1/.well-known/openid-configuration"",
                            ""metadata"": [
                                {
                                ""preferred_network"": ""login.microsoftonline.com"",
                                ""preferred_cache"": ""login.windows.net"",
                                ""aliases"": [""login.microsoftonline.com"", ""login.windows.net"", ""sts.microsoft.com""]
                                }
                            ]
                            }"
                            )
                    }
                });
            }

            CallState callState = new CallState(Guid.NewGuid());
            // ADAL still behaves correctly using developer provided authority
            var entry = await InstanceDiscovery.GetMetadataEntryAsync(new Uri($"https://{host}/tenant"), true, callState).ConfigureAwait(false);

            Assert.AreEqual("login.microsoftonline.com", entry.PreferredNetwork); // No exception raised, the host is returned as-is
            Assert.AreEqual(1, HttpMessageHandlerFactory.MockHandlersCount());    // 1 mock response is consumed, 1 remaining

            // Subsequent requests do not result in further authority validation network requests for the process lifetime
            var entry2 = await InstanceDiscovery.GetMetadataEntryAsync(new Uri("https://sts.microsoft.com/tenant"), true, callState).ConfigureAwait(false);

            Assert.AreEqual("login.microsoftonline.com", entry2.PreferredNetwork);
            Assert.AreEqual(1, HttpMessageHandlerFactory.MockHandlersCount()); // Still 1 mock response remaining, so no new request was attempted
        }
        public async Task TestInstanceDiscovery_WhenAuthorityIsValidButNoMetadataIsReturned_ShouldCacheTheProvidedAuthorityAsync()
        {
            string    host      = "login.windows.net"; // A whitelisted host
            CallState callState = new CallState(Guid.NewGuid());

            for (int i = 0; i < 2; i++) // Prepare 2 mock responses
            {
                HttpMessageHandlerFactory.AddMockHandler(MockHelpers.CreateInstanceDiscoveryMockHandler(
                                                             $"https://{host}/common/discovery/instance",
                                                             @"{""tenant_discovery_endpoint"":""https://login.microsoftonline.com/tenant/.well-known/openid-configuration""}"));
            }

            // ADAL still behaves correctly using developer provided authority
            var entry = await InstanceDiscovery.GetMetadataEntryAsync(new Uri($"https://{host}/tenant"), true, callState).ConfigureAwait(false);

            Assert.AreEqual(host, entry.PreferredNetwork);                     // No exception raised, the host is returned as-is
            Assert.AreEqual(1, HttpMessageHandlerFactory.MockHandlersCount()); // 1 mock response is consumed, 1 remaining

            // Subsequent requests do not result in further authority validation network requests for the process lifetime
            var entry2 = await InstanceDiscovery.GetMetadataEntryAsync(new Uri($"https://{host}/tenant"), true, callState).ConfigureAwait(false);

            Assert.AreEqual(host, entry2.PreferredNetwork);
            Assert.AreEqual(1, HttpMessageHandlerFactory.MockHandlersCount()); // Still 1 mock response remaining, so no new request was attempted
        }