diff --git a/src/Accounts/Authentication.Test/AuthenticatorsTest/ManagedServiceIdentityAuthenticatorTests.cs b/src/Accounts/Authentication.Test/AuthenticatorsTest/ManagedServiceIdentityAuthenticatorTests.cs index f3714584da13..cb7193d161bb 100644 --- a/src/Accounts/Authentication.Test/AuthenticatorsTest/ManagedServiceIdentityAuthenticatorTests.cs +++ b/src/Accounts/Authentication.Test/AuthenticatorsTest/ManagedServiceIdentityAuthenticatorTests.cs @@ -23,6 +23,7 @@ using Microsoft.Azure.PowerShell.Authentication.Test.Mocks; using Microsoft.Azure.PowerShell.Authenticators; using Microsoft.Azure.PowerShell.Authenticators.Factories; +using Microsoft.CodeAnalysis; using Microsoft.WindowsAzure.Commands.ScenarioTest; using Moq; diff --git a/src/Accounts/Authentication/Authentication/Parameters/AuthenticationParameters.cs b/src/Accounts/Authentication/Authentication/Parameters/AuthenticationParameters.cs index 202a878b2c12..5e394a425d15 100644 --- a/src/Accounts/Authentication/Authentication/Parameters/AuthenticationParameters.cs +++ b/src/Accounts/Authentication/Authentication/Parameters/AuthenticationParameters.cs @@ -13,6 +13,9 @@ // ---------------------------------------------------------------------------------- using Microsoft.Azure.Commands.Common.Authentication.Abstractions; +using Microsoft.Azure.Commands.Common.Exceptions; +using Microsoft.Azure.Commands.Shared.Config; +using Microsoft.Azure.PowerShell.Common.Config; namespace Microsoft.Azure.Commands.Common.Authentication { @@ -28,6 +31,8 @@ public abstract class AuthenticationParameters public string ResourceId { get; set; } + public bool? DisableInstanceDiscovery { get; set; } = null; + public AuthenticationParameters( PowerShellTokenCacheProvider tokenCacheProvider, IAzureEnvironment environment, @@ -41,6 +46,17 @@ public AuthenticationParameters( TokenCache = tokenCache; TenantId = tenantId; ResourceId = resourceId; + try + { + if (AzureSession.Instance.TryGetComponent(nameof(IConfigManager), out var config)) + { + DisableInstanceDiscovery = config.GetConfigValue(ConfigKeys.DisableInstanceDiscovery); + } + } + catch(AzPSArgumentException) + { + DisableInstanceDiscovery = null; + } } } } diff --git a/src/Accounts/Authentication/Config/ConfigInitializer.cs b/src/Accounts/Authentication/Config/ConfigInitializer.cs index 2480565a2fc4..bc1806b95a2c 100644 --- a/src/Accounts/Authentication/Config/ConfigInitializer.cs +++ b/src/Accounts/Authentication/Config/ConfigInitializer.cs @@ -226,6 +226,7 @@ private void RegisterConfigs(IConfigManager configManager) configManager.RegisterConfig(new EnableLoginByWamConfig()); configManager.RegisterConfig(new EnableInterceptSurveyConfig()); configManager.RegisterConfig(new DisplayBreakingChangeWarningsConfig()); + configManager.RegisterConfig(new DisableInstanceDiscoveryConfig()); } } } diff --git a/src/Accounts/Authentication/Config/Definitions/DisableInstanceDiscoveryConfig.cs b/src/Accounts/Authentication/Config/Definitions/DisableInstanceDiscoveryConfig.cs new file mode 100644 index 000000000000..56f533597a18 --- /dev/null +++ b/src/Accounts/Authentication/Config/Definitions/DisableInstanceDiscoveryConfig.cs @@ -0,0 +1,48 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.Azure.Commands.Common.Authentication.Abstractions; +using Microsoft.Azure.Commands.Common.Authentication.Properties; +using Microsoft.Azure.Commands.ResourceManager.Common; +using Microsoft.Azure.Commands.Shared.Config; +using Microsoft.Azure.PowerShell.Common.Config; +using System; +using System.Collections.Generic; + +namespace Microsoft.Azure.Commands.Common.Authentication.Config.Definitions +{ + /// + /// Definition of the config to control whether login by WAM (web account manager) or not. + /// + internal class DisableInstanceDiscoveryConfig : TypedConfig + { + public override object DefaultValue => false; // Opt-in. Will change to opt-out. + + public override string Key => ConfigKeys.DisableInstanceDiscovery; + + public override string HelpMessage => Resources.HelpMessageOfDisableInstanceDiscovery; + + public override IReadOnlyCollection CanApplyTo => new[] { AppliesTo.Az }; + + protected override void ApplyTyped(bool value) + { + base.ApplyTyped(value); + EventHandler writeWarningEvent; + if (AzureSession.Instance.TryGetComponent(AzureRMCmdlet.WriteWarningKey, out writeWarningEvent) && value) + { + writeWarningEvent(this, new StreamEventArgs() { Message = Resources.DisableInstanceDiscoveryWarning }); + } + } + } +} diff --git a/src/Accounts/Authentication/Properties/Resources.Designer.cs b/src/Accounts/Authentication/Properties/Resources.Designer.cs index 6135be3ae2ca..79ebfba751a9 100644 --- a/src/Accounts/Authentication/Properties/Resources.Designer.cs +++ b/src/Accounts/Authentication/Properties/Resources.Designer.cs @@ -222,6 +222,15 @@ public static string CredentialOrganizationIdMessage { } } + /// + /// Looks up a localized string similar to Set it to true to disable both instance discovery and authority validation. This functionality is intended for use in scenarios where the metadata endpoint cannot be reached, such as in private clouds or Azure Stack. It is crucial to ensure that the configured authority host is valid and trustworthy.. + /// + public static string DisableInstanceDiscoveryWarning { + get { + return ResourceManager.GetString("DisableInstanceDiscoveryWarning", resourceCulture); + } + } + /// /// Looks up a localized string similar to Environment name needs to be specified. /// @@ -312,6 +321,15 @@ public static string HelpMessageOfDisableErrorRecordsPersistence { } } + /// + /// Looks up a localized string similar to Set it to true to disable both instance discovery and authority validation. This functionality is intended for use in scenarios where the metadata endpoint cannot be reached, such as in private clouds or Azure Stack. The process of instance discovery entails retrieving authority metadata from https://login.microsoft.com/ to validate the authority. By setting this to true, the validation of the authority is disabled. As a result, it is crucial to ensure that the configured authority host is valid and trustwo [rest of string was truncated]";. + /// + public static string HelpMessageOfDisableInstanceDiscovery { + get { + return ResourceManager.GetString("HelpMessageOfDisableInstanceDiscovery", resourceCulture); + } + } + /// /// Looks up a localized string similar to Controls if warning messages for breaking changes are displayed or suppressed. When enabled, a breaking change warning is displayed when executing cmdlets with breaking changes in a future release.. /// diff --git a/src/Accounts/Authentication/Properties/Resources.resx b/src/Accounts/Authentication/Properties/Resources.resx index 2804fe71bcfd..5959871dc1a6 100644 --- a/src/Accounts/Authentication/Properties/Resources.resx +++ b/src/Accounts/Authentication/Properties/Resources.resx @@ -402,4 +402,10 @@ INITIALIZATION: Failed to migrate MSAL token cache of the legacy name with error : {0} + + Set it to true to disable both instance discovery and authority validation. This functionality is intended for use in scenarios where the metadata endpoint cannot be reached, such as in private clouds or Azure Stack. It is crucial to ensure that the configured authority host is valid and trustworthy. + + + Set it to true to disable both instance discovery and authority validation. This functionality is intended for use in scenarios where the metadata endpoint cannot be reached, such as in private clouds or Azure Stack. The process of instance discovery entails retrieving authority metadata from https://login.microsoft.com/ to validate the authority. By setting this to true, the validation of the authority is disabled. As a result, it is crucial to ensure that the configured authority host is valid and trustworthy. + \ No newline at end of file diff --git a/src/Accounts/Authenticators/ClientAssertionAuthenticator.cs b/src/Accounts/Authenticators/ClientAssertionAuthenticator.cs index de2faa9f11a3..94af4a71ca6f 100644 --- a/src/Accounts/Authenticators/ClientAssertionAuthenticator.cs +++ b/src/Accounts/Authenticators/ClientAssertionAuthenticator.cs @@ -45,6 +45,7 @@ public override Task Authenticate(AuthenticationParameters paramet AuthorityHost = new Uri(authority), TokenCachePersistenceOptions = spParameters.TokenCacheProvider.GetTokenCachePersistenceOptions() }; + options.DisableInstanceDiscovery = spParameters.DisableInstanceDiscovery ?? options.DisableInstanceDiscovery; options.Diagnostics.IsTelemetryEnabled = false; // disable telemetry to avoid error thrown from Azure.Core that AssemblyInformationalVersion is null TokenCredential tokenCredential = new ClientAssertionCredential(tenantId, spParameters.ClientId, () => GetClientAssertion(spParameters), options); string parametersLog = $"- ClientId:'{spParameters.ClientId}', TenantId:'{tenantId}', ClientAssertion:'***' Scopes:'{string.Join(",", scopes)}'"; diff --git a/src/Accounts/Authenticators/DeviceCodeAuthenticator.cs b/src/Accounts/Authenticators/DeviceCodeAuthenticator.cs index 4bba654f0262..7aaaa5138e55 100644 --- a/src/Accounts/Authenticators/DeviceCodeAuthenticator.cs +++ b/src/Accounts/Authenticators/DeviceCodeAuthenticator.cs @@ -53,6 +53,7 @@ public override Task Authenticate(AuthenticationParameters paramet TenantId = tenantId, TokenCachePersistenceOptions = tokenCacheProvider.GetTokenCachePersistenceOptions(), }; + options.DisableInstanceDiscovery = deviceCodeParameters.DisableInstanceDiscovery ?? options.DisableInstanceDiscovery; var codeCredential = new DeviceCodeCredential(options); TracingAdapter.Information($"{DateTime.Now:T} - [DeviceCodeAuthenticator] Calling DeviceCodeCredential.AuthenticateAsync - TenantId:'{options.TenantId}', Scopes:'{string.Join(",", scopes)}', AuthorityHost:'{options.AuthorityHost}'"); diff --git a/src/Accounts/Authenticators/InteractiveUserAuthenticator.cs b/src/Accounts/Authenticators/InteractiveUserAuthenticator.cs index 44652081d58d..21fe1f9d0c2a 100644 --- a/src/Accounts/Authenticators/InteractiveUserAuthenticator.cs +++ b/src/Accounts/Authenticators/InteractiveUserAuthenticator.cs @@ -12,23 +12,20 @@ // limitations under the License. // ---------------------------------------------------------------------------------- -using System; -using System.Diagnostics; -using System.Net; -using System.Net.Sockets; -using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; - using Azure.Core; using Azure.Identity; -using Azure.Identity.Broker; using Hyak.Common; using Microsoft.Azure.Commands.Common.Authentication; using Microsoft.Azure.Commands.Common.Authentication.Abstractions; +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + namespace Microsoft.Azure.PowerShell.Authenticators { /// @@ -68,6 +65,7 @@ public override Task Authenticate(AuthenticationParameters paramet RedirectUri = GetReplyUrl(onPremise, interactiveParameters.PromptAction), LoginHint = interactiveParameters.UserId, }; + options.DisableInstanceDiscovery = interactiveParameters.DisableInstanceDiscovery ?? options.DisableInstanceDiscovery; var browserCredential = new InteractiveBrowserCredential(options); TracingAdapter.Information($"{DateTime.Now:T} - [InteractiveUserAuthenticator] Calling InteractiveBrowserCredential.AuthenticateAsync with TenantId:'{options.TenantId}', Scopes:'{string.Join(",", scopes)}', AuthorityHost:'{options.AuthorityHost}', RedirectUri:'{options.RedirectUri}'"); diff --git a/src/Accounts/Authenticators/InteractiveWamAuthenticator.cs b/src/Accounts/Authenticators/InteractiveWamAuthenticator.cs index 29c530c3be34..7a693e3afc0d 100644 --- a/src/Accounts/Authenticators/InteractiveWamAuthenticator.cs +++ b/src/Accounts/Authenticators/InteractiveWamAuthenticator.cs @@ -60,6 +60,7 @@ public override Task Authenticate(AuthenticationParameters paramet RedirectUri = GetReplyUrl(onPremise, interactiveParameters.PromptAction), LoginHint = interactiveParameters.UserId }; + options.DisableInstanceDiscovery = interactiveParameters.DisableInstanceDiscovery ?? options.DisableInstanceDiscovery; var browserCredential = new InteractiveBrowserCredential(options); TracingAdapter.Information($"{DateTime.Now:T} - [InteractiveWamAuthenticator] Calling InteractiveBrowserCredential.AuthenticateAsync with TenantId:'{options.TenantId}', Scopes:'{string.Join(",", scopes)}', AuthorityHost:'{options.AuthorityHost}', RedirectUri:'{options.RedirectUri}'"); diff --git a/src/Accounts/Authenticators/ManagedServiceIdentityAuthenticator.cs b/src/Accounts/Authenticators/ManagedServiceIdentityAuthenticator.cs index efe10544e7dd..15fed5ae0f6a 100644 --- a/src/Accounts/Authenticators/ManagedServiceIdentityAuthenticator.cs +++ b/src/Accounts/Authenticators/ManagedServiceIdentityAuthenticator.cs @@ -12,20 +12,16 @@ // limitations under the License. // ---------------------------------------------------------------------------------- -using System; -using System.Text.RegularExpressions; -using System.Threading; -using System.Threading.Tasks; - using Azure.Core; -using Azure.Identity; - -using Hyak.Common; using Microsoft.Azure.Commands.Common.Authentication; using Microsoft.Azure.Commands.Common.Authentication.Abstractions; using Microsoft.Azure.PowerShell.Authenticators.Factories; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; + namespace Microsoft.Azure.PowerShell.Authenticators { public class ManagedServiceIdentityAuthenticator : DelegatingAuthenticator diff --git a/src/Accounts/Authenticators/ServicePrincipalAuthenticator.cs b/src/Accounts/Authenticators/ServicePrincipalAuthenticator.cs index 5f10bde558c1..4c4a9357c492 100644 --- a/src/Accounts/Authenticators/ServicePrincipalAuthenticator.cs +++ b/src/Accounts/Authenticators/ServicePrincipalAuthenticator.cs @@ -55,6 +55,7 @@ public override Task Authenticate(AuthenticationParameters paramet SendCertificateChain = spParameters.SendCertificateChain ?? default(bool) }; + options.DisableInstanceDiscovery = spParameters.DisableInstanceDiscovery ?? options.DisableInstanceDiscovery; TokenCredential tokenCredential = null; string parametersLog = null; if (!string.IsNullOrEmpty(spParameters.Thumbprint)) diff --git a/src/Accounts/Authenticators/SilentAuthenticator.cs b/src/Accounts/Authenticators/SilentAuthenticator.cs index e793d8dd56cd..da4832e899cd 100644 --- a/src/Accounts/Authenticators/SilentAuthenticator.cs +++ b/src/Accounts/Authenticators/SilentAuthenticator.cs @@ -68,6 +68,7 @@ private static SharedTokenCacheCredentialOptions GetTokenCredentialOptions(Silen options.Username = silentParameters.UserId; options.AuthorityHost = new Uri(authority); options.TenantId = tenantId; + options.DisableInstanceDiscovery = silentParameters.DisableInstanceDiscovery ?? options.DisableInstanceDiscovery; return options; } diff --git a/src/Accounts/Authenticators/UsernamePasswordAuthenticator.cs b/src/Accounts/Authenticators/UsernamePasswordAuthenticator.cs index 1c50f7eb08a2..d442aeefcc0b 100644 --- a/src/Accounts/Authenticators/UsernamePasswordAuthenticator.cs +++ b/src/Accounts/Authenticators/UsernamePasswordAuthenticator.cs @@ -59,6 +59,7 @@ public override Task Authenticate(AuthenticationParameters paramet AuthorityHost = new Uri(authority), TokenCachePersistenceOptions = tokenCacheProvider.GetTokenCachePersistenceOptions() }; + credentialOptions.DisableInstanceDiscovery = upParameters.DisableInstanceDiscovery ?? credentialOptions.DisableInstanceDiscovery; if (upParameters.Password != null) { passwordCredential = new UsernamePasswordCredential(upParameters.UserId, upParameters.Password.ConvertToString(), tenantId, clientId, credentialOptions); diff --git a/src/shared/ConfigKeys.cs b/src/shared/ConfigKeys.cs index a249ed943626..2828bc1b6f1e 100644 --- a/src/shared/ConfigKeys.cs +++ b/src/shared/ConfigKeys.cs @@ -35,6 +35,7 @@ internal static class ConfigKeys //Use DisableErrorRecordsPersistence as opt-out for now, will replace it with EnableErrorRecordsPersistence as opt-in at next major release (November 2023) public const string DisableErrorRecordsPersistence = "DisableErrorRecordsPersistence"; public const string EnableErrorRecordsPersistence = "EnableErrorRecordsPersistence"; + public const string DisableInstanceDiscovery = "DisableInstanceDiscovery"; public const string CheckForUpgrade = "CheckForUpgrade"; public const string EnvCheckForUpgrade = "AZUREPS_CHECK_FOR_UPGRADE"; }