diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml index 54b8f3d0a7..a52a2ec41a 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml @@ -41,5 +41,9 @@ Alias for "Active Directory Managed Identity" authentication method. Use System Assigned or User Assigned Managed Identity to connect to SQL Database from Azure client environments that have enabled support for Managed Identity. For User Assigned Managed Identity, 'User Id' or 'UID' is required to be set to the "client ID" of the user identity. 8 + + The authentication method uses Active Directory Default. Use this mode to connect to a SQL Database using multiple non-interactive authentication methods tried sequentially to acquire an access token. This method does not fallback to the "Active Directory Interactive" authentication method. + 9 + diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs index b6956df25b..770215005c 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs @@ -99,6 +99,8 @@ public enum SqlAuthenticationMethod ActiveDirectoryManagedIdentity = 7, /// ActiveDirectoryMSI = 8, + /// + ActiveDirectoryDefault = 9, /// NotSpecified = 0, /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs index eb23fcdfef..8353e7d8a0 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs @@ -98,6 +98,7 @@ internal static string ConvertToString(object value) private const string ApplicationIntentReadWriteString = "ReadWrite"; private const string ApplicationIntentReadOnlyString = "ReadOnly"; + const string SqlPasswordString = "Sql Password"; const string ActiveDirectoryPasswordString = "Active Directory Password"; const string ActiveDirectoryIntegratedString = "Active Directory Integrated"; @@ -106,13 +107,48 @@ internal static string ConvertToString(object value) const string ActiveDirectoryDeviceCodeFlowString = "Active Directory Device Code Flow"; internal const string ActiveDirectoryManagedIdentityString = "Active Directory Managed Identity"; internal const string ActiveDirectoryMSIString = "Active Directory MSI"; + internal const string ActiveDirectoryDefaultString = "Active Directory Default"; - internal static bool TryConvertToAuthenticationType(string value, out SqlAuthenticationMethod result) +#if DEBUG + private static string[] s_supportedAuthenticationModes = + { + "NotSpecified", + "SqlPassword", + "ActiveDirectoryPassword", + "ActiveDirectoryIntegrated", + "ActiveDirectoryInteractive", + "ActiveDirectoryServicePrincipal", + "ActiveDirectoryDeviceCodeFlow", + "ActiveDirectoryManagedIdentity", + "ActiveDirectoryMSI", + "ActiveDirectoryDefault" + }; + + private static bool IsValidAuthenticationMethodEnum() { - Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 9, "SqlAuthenticationMethod enum has changed, update needed"); + string[] names = Enum.GetNames(typeof(SqlAuthenticationMethod)); + int l = s_supportedAuthenticationModes.Length; + bool listValid; + if (listValid = names.Length == l) + { + for (int i = 0; i < l; i++) + { + if (s_supportedAuthenticationModes[i].CompareTo(names[i]) != 0) + { + listValid = false; + } + } + } + return listValid; + } +#endif + internal static bool TryConvertToAuthenticationType(string value, out SqlAuthenticationMethod result) + { +#if DEBUG + Debug.Assert(IsValidAuthenticationMethodEnum(), "SqlAuthenticationMethod enum has changed, update needed"); +#endif bool isSuccess = false; - if (StringComparer.InvariantCultureIgnoreCase.Equals(value, SqlPasswordString) || StringComparer.InvariantCultureIgnoreCase.Equals(value, Convert.ToString(SqlAuthenticationMethod.SqlPassword, CultureInfo.InvariantCulture))) { @@ -161,6 +197,12 @@ internal static bool TryConvertToAuthenticationType(string value, out SqlAuthent result = SqlAuthenticationMethod.ActiveDirectoryMSI; isSuccess = true; } + else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, ActiveDirectoryDefaultString) + || StringComparer.InvariantCultureIgnoreCase.Equals(value, Convert.ToString(SqlAuthenticationMethod.ActiveDirectoryDefault, CultureInfo.InvariantCulture))) + { + result = SqlAuthenticationMethod.ActiveDirectoryDefault; + isSuccess = true; + } else { result = DbConnectionStringDefaults.Authentication; @@ -501,7 +543,7 @@ internal static ApplicationIntent ConvertToApplicationIntent(string keyword, obj internal static bool IsValidAuthenticationTypeValue(SqlAuthenticationMethod value) { - Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 9, "SqlAuthenticationMethod enum has changed, update needed"); + Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 10, "SqlAuthenticationMethod enum has changed, update needed"); return value == SqlAuthenticationMethod.SqlPassword || value == SqlAuthenticationMethod.ActiveDirectoryPassword || value == SqlAuthenticationMethod.ActiveDirectoryIntegrated @@ -510,6 +552,7 @@ internal static bool IsValidAuthenticationTypeValue(SqlAuthenticationMethod valu || value == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow || value == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || value == SqlAuthenticationMethod.ActiveDirectoryMSI + || value == SqlAuthenticationMethod.ActiveDirectoryDefault || value == SqlAuthenticationMethod.NotSpecified; } @@ -535,6 +578,8 @@ internal static string AuthenticationTypeToString(SqlAuthenticationMethod value) return ActiveDirectoryManagedIdentityString; case SqlAuthenticationMethod.ActiveDirectoryMSI: return ActiveDirectoryMSIString; + case SqlAuthenticationMethod.ActiveDirectoryDefault: + return ActiveDirectoryDefaultString; default: return null; } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetCoreApp.cs index e253829b2c..b204c8df81 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetCoreApp.cs @@ -152,6 +152,8 @@ private static SqlAuthenticationMethod AuthenticationEnumFromString(string authe return SqlAuthenticationMethod.ActiveDirectoryManagedIdentity; case ActiveDirectoryMSI: return SqlAuthenticationMethod.ActiveDirectoryMSI; + case ActiveDirectoryDefault: + return SqlAuthenticationMethod.ActiveDirectoryDefault; default: throw SQL.UnsupportedAuthentication(authentication); } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs index 1841f016d0..4c101d30df 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs @@ -21,6 +21,7 @@ internal partial class SqlAuthenticationProviderManager private const string ActiveDirectoryDeviceCodeFlow = "active directory device code flow"; private const string ActiveDirectoryManagedIdentity = "active directory managed identity"; private const string ActiveDirectoryMSI = "active directory msi"; + private const string ActiveDirectoryDefault = "active directory default"; private readonly string _typeName; private readonly IReadOnlyCollection _authenticationsWithAppSpecifiedProvider; @@ -46,6 +47,7 @@ private static void SetDefaultAuthProviders(SqlAuthenticationProviderManager ins instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow, activeDirectoryAuthProvider); instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryManagedIdentity, activeDirectoryAuthProvider); instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryMSI, activeDirectoryAuthProvider); + instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDefault, activeDirectoryAuthProvider); } } /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index 33fd700673..5d74e0241e 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -184,11 +184,15 @@ public SqlConnection(string connectionString, SqlCredential credential) : this() } else if (UsesActiveDirectoryManagedIdentity(connectionOptions)) { - throw SQL.SettingCredentialWithManagedIdentityArgument(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); + throw SQL.SettingCredentialWithNonInteractiveArgument(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); } else if (UsesActiveDirectoryMSI(connectionOptions)) { - throw SQL.SettingCredentialWithManagedIdentityArgument(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + throw SQL.SettingCredentialWithNonInteractiveArgument(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + } + else if (UsesActiveDirectoryDefault(connectionOptions)) + { + throw SQL.SettingCredentialWithNonInteractiveArgument(DbConnectionStringBuilderUtil.ActiveDirectoryDefaultString); } Credential = credential; @@ -463,6 +467,11 @@ private bool UsesActiveDirectoryMSI(SqlConnectionString opt) return opt != null && opt.Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI; } + private bool UsesActiveDirectoryDefault(SqlConnectionString opt) + { + return opt != null && opt.Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault; + } + private bool UsesAuthentication(SqlConnectionString opt) { return opt != null && opt.Authentication != SqlAuthenticationMethod.NotSpecified; @@ -520,7 +529,7 @@ public override string ConnectionString if (_credential != null) { // Check for Credential being used with Authentication=ActiveDirectoryIntegrated | ActiveDirectoryInteractive | - // ActiveDirectoryDeviceCodeFlow | ActiveDirectoryManagedIdentity/ActiveDirectoryMSI. Since a different error string is used + // ActiveDirectoryDeviceCodeFlow | ActiveDirectoryManagedIdentity/ActiveDirectoryMSI | ActiveDirectoryDefault. Since a different error string is used // for this case in ConnectionString setter vs in Credential setter, check for this error case before calling // CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential, which is common to both setters. if (UsesActiveDirectoryIntegrated(connectionOptions)) @@ -537,11 +546,15 @@ public override string ConnectionString } else if (UsesActiveDirectoryManagedIdentity(connectionOptions)) { - throw SQL.SettingManagedIdentityWithCredential(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); + throw SQL.SettingNonInteractiveWithCredential(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); } else if (UsesActiveDirectoryMSI(connectionOptions)) { - throw SQL.SettingManagedIdentityWithCredential(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + throw SQL.SettingNonInteractiveWithCredential(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + } + else if (UsesActiveDirectoryDefault(connectionOptions)) + { + throw SQL.SettingNonInteractiveWithCredential(DbConnectionStringBuilderUtil.ActiveDirectoryDefaultString); } CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential(connectionOptions); @@ -833,7 +846,7 @@ public SqlCredential Credential { var connectionOptions = (SqlConnectionString)ConnectionOptions; // Check for Credential being used with Authentication=ActiveDirectoryIntegrated | ActiveDirectoryInteractive | - // ActiveDirectoryDeviceCodeFlow | ActiveDirectoryManagedIdentity/ActiveDirectoryMSI. Since a different error string is used + // ActiveDirectoryDeviceCodeFlow | ActiveDirectoryManagedIdentity/ActiveDirectoryMSI | ActiveDirectoryDefault. Since a different error string is used // for this case in ConnectionString setter vs in Credential setter, check for this error case before calling // CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential, which is common to both setters. if (UsesActiveDirectoryIntegrated(connectionOptions)) @@ -850,11 +863,15 @@ public SqlCredential Credential } else if (UsesActiveDirectoryManagedIdentity(connectionOptions)) { - throw SQL.SettingCredentialWithManagedIdentityInvalid(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); + throw SQL.SettingCredentialWithNonInteractiveInvalid(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); } else if (UsesActiveDirectoryMSI(connectionOptions)) { - throw SQL.SettingCredentialWithManagedIdentityInvalid(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + throw SQL.SettingCredentialWithNonInteractiveInvalid(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + } + else if (UsesActiveDirectoryDefault(connectionOptions)) + { + throw SQL.SettingCredentialWithNonInteractiveInvalid(DbConnectionStringBuilderUtil.ActiveDirectoryDefaultString); } CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential(connectionOptions); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs index aa9023139c..1890129e22 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs @@ -478,12 +478,17 @@ internal SqlConnectionString(string connectionString) : base(connectionString, G if (Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity && HasPasswordKeyword) { - throw SQL.ManagedIdentityWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); + throw SQL.NonInteractiveWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); } if (Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI && HasPasswordKeyword) { - throw SQL.ManagedIdentityWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + throw SQL.NonInteractiveWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + } + + if (Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault && HasPasswordKeyword) + { + throw SQL.NonInteractiveWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryDefaultString); } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 5dcda47e0f..d144e91fab 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -1308,6 +1308,7 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryServicePrincipal || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI + || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault // Since AD Integrated may be acting like Windows integrated, additionally check _fedAuthRequired || (ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated && _fedAuthRequired)) { @@ -2116,6 +2117,7 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI + || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault || (ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated && _fedAuthRequired), "Credentials aren't provided for calling MSAL"); Debug.Assert(fedAuthInfo != null, "info should not be null."); @@ -2358,6 +2360,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) case SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow: case SqlAuthenticationMethod.ActiveDirectoryManagedIdentity: case SqlAuthenticationMethod.ActiveDirectoryMSI: + case SqlAuthenticationMethod.ActiveDirectoryDefault: if (_activeDirectoryAuthTimeoutRetryHelper.State == ActiveDirectoryAuthenticationTimeoutRetryState.Retrying) { _fedAuthToken = _activeDirectoryAuthTimeoutRetryHelper.CachedToken; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs index 8a0f5293b4..0ede264635 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs @@ -283,9 +283,9 @@ internal static Exception DeviceFlowWithUsernamePassword() { return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_DeviceFlowWithUsernamePassword)); } - internal static Exception ManagedIdentityWithPassword(string authenticationMode) + internal static Exception NonInteractiveWithPassword(string authenticationMode) { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_ManagedIdentityWithPassword, authenticationMode)); + return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_NonInteractiveWithPassword, authenticationMode)); } static internal Exception SettingIntegratedWithCredential() { @@ -299,9 +299,9 @@ static internal Exception SettingDeviceFlowWithCredential() { return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_SettingDeviceFlowWithCredential)); } - static internal Exception SettingManagedIdentityWithCredential(string authenticationMode) + static internal Exception SettingNonInteractiveWithCredential(string authenticationMode) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_SettingManagedIdentityWithCredential, authenticationMode)); + return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_SettingNonInteractiveWithCredential, authenticationMode)); } static internal Exception SettingCredentialWithIntegratedArgument() { @@ -315,9 +315,9 @@ static internal Exception SettingCredentialWithDeviceFlowArgument() { return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_SettingCredentialWithDeviceFlow)); } - static internal Exception SettingCredentialWithManagedIdentityArgument(string authenticationMode) + static internal Exception SettingCredentialWithNonInteractiveArgument(string authenticationMode) { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_SettingCredentialWithManagedIdentity, authenticationMode)); + return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_SettingCredentialWithNonInteractive, authenticationMode)); } static internal Exception SettingCredentialWithIntegratedInvalid() { @@ -331,9 +331,9 @@ static internal Exception SettingCredentialWithDeviceFlowInvalid() { return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_SettingCredentialWithDeviceFlow)); } - static internal Exception SettingCredentialWithManagedIdentityInvalid(string authenticationMode) + static internal Exception SettingCredentialWithNonInteractiveInvalid(string authenticationMode) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_SettingCredentialWithManagedIdentity, authenticationMode)); + return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_SettingCredentialWithNonInteractive, authenticationMode)); } internal static Exception NullEmptyTransactionName() { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs index 416ec86fd0..2d31893fe8 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs @@ -252,6 +252,7 @@ public enum FedAuthLibrary : byte public const byte MSALWORKFLOW_ACTIVEDIRECTORYSERVICEPRINCIPAL = 0x01; // Using the Password byte as that is the closest we have public const byte MSALWORKFLOW_ACTIVEDIRECTORYDEVICECODEFLOW = 0x03; // Using the Interactive byte as that is the closest we have public const byte MSALWORKFLOW_ACTIVEDIRECTORYMANAGEDIDENTITY = 0x03; // Using the Interactive byte as that's supported for Identity based authentication + public const byte MSALWORKFLOW_ACTIVEDIRECTORYDEFAULT = 0x03; // Using the Interactive byte as that is the closest we have to non-password based authentication modes public enum ActiveDirectoryWorkflow : byte { @@ -261,6 +262,7 @@ public enum ActiveDirectoryWorkflow : byte ServicePrincipal = MSALWORKFLOW_ACTIVEDIRECTORYSERVICEPRINCIPAL, DeviceCodeFlow = MSALWORKFLOW_ACTIVEDIRECTORYDEVICECODEFLOW, ManagedIdentity = MSALWORKFLOW_ACTIVEDIRECTORYMANAGEDIDENTITY, + Default = MSALWORKFLOW_ACTIVEDIRECTORYDEFAULT, } // The string used for username in the error message when Authentication = Active Directory Integrated with FedAuth is used, if authentication fails. @@ -1142,7 +1144,10 @@ public enum SqlAuthenticationMethod ActiveDirectoryManagedIdentity, /// - ActiveDirectoryMSI + ActiveDirectoryMSI, + + /// + ActiveDirectoryDefault } // This enum indicates the state of TransparentNetworkIPResolution // The first attempt when TNIR is on should be sequential. If the first attempt failes next attempts should be parallel. diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index e0ebfdf669..3b522dface 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -7793,6 +7793,9 @@ internal int WriteFedAuthFeatureRequest(FederatedAuthenticationFeatureExtensionD case SqlAuthenticationMethod.ActiveDirectoryMSI: workflow = TdsEnums.MSALWORKFLOW_ACTIVEDIRECTORYMANAGEDIDENTITY; break; + case SqlAuthenticationMethod.ActiveDirectoryDefault: + workflow = TdsEnums.MSALWORKFLOW_ACTIVEDIRECTORYDEFAULT; + break; default: Debug.Assert(false, "Unrecognized Authentication type for fedauth MSAL request"); break; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs index 8a95a52351..1b0d00e069 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs @@ -2799,9 +2799,9 @@ internal static string SQL_KerberosTicketMissingError { /// /// Looks up a localized string similar to Cannot use 'Authentication={0}' with 'Password' or 'PWD' connection string keywords.. /// - internal static string SQL_ManagedIdentityWithPassword { + internal static string SQL_NonInteractiveWithPassword { get { - return ResourceManager.GetString("SQL_ManagedIdentityWithPassword", resourceCulture); + return ResourceManager.GetString("SQL_NonInteractiveWithPassword", resourceCulture); } } @@ -3087,9 +3087,9 @@ internal static string SQL_SettingCredentialWithInteractive { /// /// Looks up a localized string similar to Cannot set the Credential property if 'Authentication={0}' has been specified in the connection string.. /// - internal static string SQL_SettingCredentialWithManagedIdentity { + internal static string SQL_SettingCredentialWithNonInteractive { get { - return ResourceManager.GetString("SQL_SettingCredentialWithManagedIdentity", resourceCulture); + return ResourceManager.GetString("SQL_SettingCredentialWithNonInteractive", resourceCulture); } } @@ -3123,9 +3123,9 @@ internal static string SQL_SettingInteractiveWithCredential { /// /// Looks up a localized string similar to Cannot use 'Authentication={0}', if the Credential property has been set.. /// - internal static string SQL_SettingManagedIdentityWithCredential { + internal static string SQL_SettingNonInteractiveWithCredential { get { - return ResourceManager.GetString("SQL_SettingManagedIdentityWithCredential", resourceCulture); + return ResourceManager.GetString("SQL_SettingNonInteractiveWithCredential", resourceCulture); } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx index 5dd36f38ac..47e117053c 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx @@ -411,7 +411,7 @@ Cannot use 'Authentication=Active Directory Device Code Flow' with 'User ID', 'UID', 'Password' or 'PWD' connection string keywords. - + Cannot use 'Authentication={0}' with 'Password' or 'PWD' connection string keywords. @@ -1905,13 +1905,13 @@ Cannot set the Credential property if 'Authentication=Active Directory Device Code Flow' has been specified in the connection string. - + Cannot set the Credential property if 'Authentication={0}' has been specified in the connection string. Cannot use 'Authentication=Active Directory Device Code Flow', if the Credential property has been set. - + Cannot use 'Authentication={0}', if the Credential property has been set. diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs index 16227cc0c8..be5a512f4c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs @@ -117,6 +117,8 @@ public enum SqlAuthenticationMethod ActiveDirectoryManagedIdentity = 7, /// ActiveDirectoryMSI = 8, + /// + ActiveDirectoryDefault = 9, /// NotSpecified = 0, /// diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs index 71ab0deea3..afc091710b 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionStringCommon.cs @@ -57,7 +57,8 @@ internal ReadOnlyCollection(T[] items) { _items = items; #if DEBUG - for(int i = 0; i < items.Length; ++i) { + for (int i = 0; i < items.Length; ++i) + { Debug.Assert(null != items[i], "null item"); } #endif @@ -524,12 +525,48 @@ internal static ApplicationIntent ConvertToApplicationIntent(string keyword, obj const string ActiveDirectoryDeviceCodeFlowString = "Active Directory Device Code Flow"; internal const string ActiveDirectoryManagedIdentityString = "Active Directory Managed Identity"; internal const string ActiveDirectoryMSIString = "Active Directory MSI"; - const string SqlCertificateString = "Sql Certificate"; + internal const string ActiveDirectoryDefaultString = "Active Directory Default"; + // const string SqlCertificateString = "Sql Certificate"; - internal static bool TryConvertToAuthenticationType(string value, out SqlAuthenticationMethod result) +#if DEBUG + private static string[] s_supportedAuthenticationModes = { - Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 9, "SqlAuthenticationMethod enum has changed, update needed"); + "NotSpecified", + "SqlPassword", + "ActiveDirectoryPassword", + "ActiveDirectoryIntegrated", + "ActiveDirectoryInteractive", + "ActiveDirectoryServicePrincipal", + "ActiveDirectoryDeviceCodeFlow", + "ActiveDirectoryManagedIdentity", + "ActiveDirectoryMSI", + "ActiveDirectoryDefault" + }; + + private static bool IsValidAuthenticationMethodEnum() + { + string[] names = Enum.GetNames(typeof(SqlAuthenticationMethod)); + int l = s_supportedAuthenticationModes.Length; + bool listValid; + if (listValid = names.Length == l) + { + for (int i = 0; i < l; i++) + { + if (s_supportedAuthenticationModes[i].CompareTo(names[i]) != 0) + { + listValid = false; + } + } + } + return listValid; + } +#endif + internal static bool TryConvertToAuthenticationType(string value, out SqlAuthenticationMethod result) + { +#if DEBUG + Debug.Assert(IsValidAuthenticationMethodEnum(), "SqlAuthenticationMethod enum has changed, update needed"); +#endif bool isSuccess = false; if (StringComparer.InvariantCultureIgnoreCase.Equals(value, SqlPasswordString) @@ -580,6 +617,12 @@ internal static bool TryConvertToAuthenticationType(string value, out SqlAuthent result = SqlAuthenticationMethod.ActiveDirectoryMSI; isSuccess = true; } + else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, ActiveDirectoryDefaultString) + || StringComparer.InvariantCultureIgnoreCase.Equals(value, Convert.ToString(SqlAuthenticationMethod.ActiveDirectoryDefault, CultureInfo.InvariantCulture))) + { + result = SqlAuthenticationMethod.ActiveDirectoryDefault; + isSuccess = true; + } #if ADONET_CERT_AUTH else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, SqlCertificateString) || StringComparer.InvariantCultureIgnoreCase.Equals(value, Convert.ToString(SqlAuthenticationMethod.SqlCertificate, CultureInfo.InvariantCulture))) { @@ -671,6 +714,7 @@ internal static bool IsValidAuthenticationTypeValue(SqlAuthenticationMethod valu || value == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow || value == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || value == SqlAuthenticationMethod.ActiveDirectoryMSI + || value == SqlAuthenticationMethod.ActiveDirectoryDefault #if ADONET_CERT_AUTH || value == SqlAuthenticationMethod.SqlCertificate #endif @@ -699,6 +743,8 @@ internal static string AuthenticationTypeToString(SqlAuthenticationMethod value) return ActiveDirectoryManagedIdentityString; case SqlAuthenticationMethod.ActiveDirectoryMSI: return ActiveDirectoryMSIString; + case SqlAuthenticationMethod.ActiveDirectoryDefault: + return ActiveDirectoryDefaultString; #if ADONET_CERT_AUTH case SqlAuthenticationMethod.SqlCertificate: return SqlCertificateString; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs index 561c0cd101..d0ab1507d9 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs @@ -22,6 +22,7 @@ internal class SqlAuthenticationProviderManager private const string ActiveDirectoryDeviceCodeFlow = "active directory device code flow"; private const string ActiveDirectoryManagedIdentity = "active directory managed identity"; private const string ActiveDirectoryMSI = "active directory msi"; + private const string ActiveDirectoryDefault = "active directory default"; static SqlAuthenticationProviderManager() { @@ -51,6 +52,7 @@ static SqlAuthenticationProviderManager() Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow, activeDirectoryAuthProvider); Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryManagedIdentity, activeDirectoryAuthProvider); Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryMSI, activeDirectoryAuthProvider); + Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDefault, activeDirectoryAuthProvider); } public static readonly SqlAuthenticationProviderManager Instance; @@ -221,6 +223,8 @@ private static SqlAuthenticationMethod AuthenticationEnumFromString(string authe return SqlAuthenticationMethod.ActiveDirectoryManagedIdentity; case ActiveDirectoryMSI: return SqlAuthenticationMethod.ActiveDirectoryMSI; + case ActiveDirectoryDefault: + return SqlAuthenticationMethod.ActiveDirectoryDefault; default: throw SQL.UnsupportedAuthentication(authentication); } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs index a7b0c5cfd4..eba097b8b5 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -373,12 +373,17 @@ public SqlConnection(string connectionString, SqlCredential credential) : this() if (UsesActiveDirectoryManagedIdentity(connectionOptions)) { - throw SQL.SettingCredentialWithManagedIdentityArgument(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); + throw SQL.SettingCredentialWithNonInteractiveArgument(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); } if (UsesActiveDirectoryMSI(connectionOptions)) { - throw SQL.SettingCredentialWithManagedIdentityArgument(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + throw SQL.SettingCredentialWithNonInteractiveArgument(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + } + + if (UsesActiveDirectoryDefault(connectionOptions)) + { + throw SQL.SettingCredentialWithNonInteractiveArgument(DbConnectionStringBuilderUtil.ActiveDirectoryDefaultString); } Credential = credential; @@ -584,6 +589,11 @@ private bool UsesActiveDirectoryMSI(SqlConnectionString opt) return opt != null && opt.Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI; } + private bool UsesActiveDirectoryDefault(SqlConnectionString opt) + { + return opt != null && opt.Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault; + } + private bool UsesAuthentication(SqlConnectionString opt) { return opt != null && opt.Authentication != SqlAuthenticationMethod.NotSpecified; @@ -742,7 +752,7 @@ override public string ConnectionString if (_credential != null) { // Check for Credential being used with Authentication=ActiveDirectoryIntegrated | ActiveDirectoryInteractive | - // ActiveDirectoryDeviceCodeFlow | ActiveDirectoryManagedIdentity/ActiveDirectoryMSI. Since a different error string is used + // ActiveDirectoryDeviceCodeFlow | ActiveDirectoryManagedIdentity/ActiveDirectoryMSI | ActiveDirectoryDefault. Since a different error string is used // for this case in ConnectionString setter vs in Credential setter, check for this error case before calling // CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential, which is common to both setters. if (UsesActiveDirectoryIntegrated(connectionOptions)) @@ -759,11 +769,15 @@ override public string ConnectionString } else if (UsesActiveDirectoryManagedIdentity(connectionOptions)) { - throw SQL.SettingManagedIdentityWithCredential(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); + throw SQL.SettingNonInteractiveWithCredential(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); } else if (UsesActiveDirectoryMSI(connectionOptions)) { - throw SQL.SettingManagedIdentityWithCredential(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + throw SQL.SettingNonInteractiveWithCredential(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + } + else if (UsesActiveDirectoryDefault(connectionOptions)) + { + throw SQL.SettingNonInteractiveWithCredential(DbConnectionStringBuilderUtil.ActiveDirectoryDefaultString); } CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential(connectionOptions); @@ -1081,7 +1095,7 @@ public SqlCredential Credential { var connectionOptions = (SqlConnectionString)ConnectionOptions; // Check for Credential being used with Authentication=ActiveDirectoryIntegrated | ActiveDirectoryInteractive | - // ActiveDirectoryDeviceCodeFlow | ActiveDirectoryManagedIdentity/ActiveDirectoryMSI. Since a different error string is used + // ActiveDirectoryDeviceCodeFlow | ActiveDirectoryManagedIdentity/ActiveDirectoryMSI | ActiveDirectoryDefault. Since a different error string is used // for this case in ConnectionString setter vs in Credential setter, check for this error case before calling // CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential, which is common to both setters. if (UsesActiveDirectoryIntegrated(connectionOptions)) @@ -1098,11 +1112,15 @@ public SqlCredential Credential } else if (UsesActiveDirectoryManagedIdentity(connectionOptions)) { - throw SQL.SettingCredentialWithManagedIdentityInvalid(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); + throw SQL.SettingCredentialWithNonInteractiveInvalid(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); } else if (UsesActiveDirectoryMSI(connectionOptions)) { - throw SQL.SettingCredentialWithManagedIdentityInvalid(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + throw SQL.SettingCredentialWithNonInteractiveInvalid(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + } + else if (UsesActiveDirectoryDefault(connectionOptions)) + { + throw SQL.SettingCredentialWithNonInteractiveInvalid(DbConnectionStringBuilderUtil.ActiveDirectoryDefaultString); } CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential(connectionOptions); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs index 2b3ffa9d70..33949f03f4 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs @@ -570,12 +570,17 @@ internal SqlConnectionString(string connectionString) : base(connectionString, G if (Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity && HasPasswordKeyword) { - throw SQL.ManagedIdentityWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); + throw SQL.NonInteractiveWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); } if (Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI && HasPasswordKeyword) { - throw SQL.ManagedIdentityWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + throw SQL.NonInteractiveWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + } + + if (Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault && HasPasswordKeyword) + { + throw SQL.NonInteractiveWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryDefaultString); } #if ADONET_CERT_AUTH diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index e66081de70..42a6eec32c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -1580,6 +1580,7 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI + || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault // Since AD Integrated may be acting like Windows integrated, additionally check _fedAuthRequired || (ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated && _fedAuthRequired)) { @@ -1975,7 +1976,8 @@ private bool ShouldDisableTnir(SqlConnectionString connectionOptions) connectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryServicePrincipal || connectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow || connectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || - connectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI; + connectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI || + connectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault; // Check if the user had explicitly specified the TNIR option in the connection string or the connection string builder. // If the user has specified the option in the connection string explicitly, then we shouldn't disable TNIR. @@ -2563,6 +2565,7 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryInteractive || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI + || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow || (ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated && _fedAuthRequired), "Credentials aren't provided for calling MSAL"); @@ -2795,6 +2798,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) case SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow: case SqlAuthenticationMethod.ActiveDirectoryManagedIdentity: case SqlAuthenticationMethod.ActiveDirectoryMSI: + case SqlAuthenticationMethod.ActiveDirectoryDefault: if (_activeDirectoryAuthTimeoutRetryHelper.State == ActiveDirectoryAuthenticationTimeoutRetryState.Retrying) { fedAuthToken = _activeDirectoryAuthTimeoutRetryHelper.CachedToken; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs index bdec925681..3dd69c5299 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs @@ -334,9 +334,9 @@ static internal Exception DeviceFlowWithUsernamePassword() { return ADP.Argument(StringsHelper.GetString(Strings.SQL_DeviceFlowWithUsernamePassword)); } - static internal Exception ManagedIdentityWithPassword(string authenticationMode) + static internal Exception NonInteractiveWithPassword(string authenticationMode) { - return ADP.Argument(StringsHelper.GetString(Strings.SQL_ManagedIdentityWithPassword, authenticationMode)); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_NonInteractiveWithPassword, authenticationMode)); } static internal Exception SettingIntegratedWithCredential() { @@ -350,9 +350,9 @@ static internal Exception SettingDeviceFlowWithCredential() { return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_SettingDeviceFlowWithCredential)); } - static internal Exception SettingManagedIdentityWithCredential(string authenticationMode) + static internal Exception SettingNonInteractiveWithCredential(string authenticationMode) { - return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_SettingManagedIdentityWithCredential, authenticationMode)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_SettingNonInteractiveWithCredential, authenticationMode)); } static internal Exception SettingCredentialWithIntegratedArgument() { @@ -366,9 +366,9 @@ static internal Exception SettingCredentialWithDeviceFlowArgument() { return ADP.Argument(StringsHelper.GetString(Strings.SQL_SettingCredentialWithDeviceFlow)); } - static internal Exception SettingCredentialWithManagedIdentityArgument(string authenticationMode) + static internal Exception SettingCredentialWithNonInteractiveArgument(string authenticationMode) { - return ADP.Argument(StringsHelper.GetString(Strings.SQL_SettingCredentialWithManagedIdentity, authenticationMode)); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_SettingCredentialWithNonInteractive, authenticationMode)); } static internal Exception SettingCredentialWithIntegratedInvalid() { @@ -382,9 +382,9 @@ static internal Exception SettingCredentialWithDeviceFlowInvalid() { return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_SettingCredentialWithDeviceFlow)); } - static internal Exception SettingCredentialWithManagedIdentityInvalid(string authenticationMode) + static internal Exception SettingCredentialWithNonInteractiveInvalid(string authenticationMode) { - return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_SettingCredentialWithManagedIdentity, authenticationMode)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_SettingCredentialWithNonInteractive, authenticationMode)); } static internal Exception InvalidSQLServerVersionUnknown() { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs index 5e422fef74..1a9c2be00f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs @@ -244,6 +244,7 @@ public enum FedAuthLibrary : byte public const byte MSALWORKFLOW_ACTIVEDIRECTORYSERVICEPRINCIPAL = 0x01; // Using the Password byte as that is the closest we have public const byte MSALWORKFLOW_ACTIVEDIRECTORYDEVICECODEFLOW = 0x03; // Using the Interactive byte as that is the closest we have public const byte MSALWORKFLOW_ACTIVEDIRECTORYMANAGEDIDENTITY = 0x03; // Using the Interactive byte as that's supported for Identity based authentication + public const byte MSALWORKFLOW_ACTIVEDIRECTORYDEFAULT = 0x03; // Using the Interactive byte as that is the closest we have to non-password based authentication modes public enum ActiveDirectoryWorkflow : byte { @@ -253,6 +254,7 @@ public enum ActiveDirectoryWorkflow : byte ServicePrincipal = MSALWORKFLOW_ACTIVEDIRECTORYSERVICEPRINCIPAL, DeviceCodeFlow = MSALWORKFLOW_ACTIVEDIRECTORYDEVICECODEFLOW, ManagedIdentity = MSALWORKFLOW_ACTIVEDIRECTORYMANAGEDIDENTITY, + Default = MSALWORKFLOW_ACTIVEDIRECTORYDEFAULT, } // The string used for username in the error message when Authentication = Active Directory Integrated with FedAuth is used, if authentication fails. @@ -1105,6 +1107,9 @@ public enum SqlAuthenticationMethod /// ActiveDirectoryMSI, + + /// + ActiveDirectoryDefault, #if ADONET_CERT_AUTH SqlCertificate #endif diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index 6b4d322c1f..ff84354f14 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -554,6 +554,9 @@ internal void Connect(ServerInfo serverInfo, case SqlAuthenticationMethod.ActiveDirectoryMSI: SqlClientEventSource.Log.TryTraceEvent(" Active Directory MSI authentication"); break; + case SqlAuthenticationMethod.ActiveDirectoryDefault: + SqlClientEventSource.Log.TryTraceEvent(" Active Directory Default authentication"); + break; case SqlAuthenticationMethod.SqlPassword: SqlClientEventSource.Log.TryTraceEvent(" SQL Password authentication"); break; @@ -8585,6 +8588,9 @@ internal int WriteFedAuthFeatureRequest(FederatedAuthenticationFeatureExtensionD case SqlAuthenticationMethod.ActiveDirectoryMSI: workflow = TdsEnums.MSALWORKFLOW_ACTIVEDIRECTORYMANAGEDIDENTITY; break; + case SqlAuthenticationMethod.ActiveDirectoryDefault: + workflow = TdsEnums.MSALWORKFLOW_ACTIVEDIRECTORYDEFAULT; + break; default: Debug.Assert(false, "Unrecognized Authentication type for fedauth MSAL request"); break; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs index 666ff47326..47a8fb681f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs @@ -9606,9 +9606,9 @@ internal static string SQL_InvalidUdt3PartNameFormat { /// /// Looks up a localized string similar to Cannot use 'Authentication={0}' with 'Password' or 'PWD' connection string keywords.. /// - internal static string SQL_ManagedIdentityWithPassword { + internal static string SQL_NonInteractiveWithPassword { get { - return ResourceManager.GetString("SQL_ManagedIdentityWithPassword", resourceCulture); + return ResourceManager.GetString("SQL_NonInteractiveWithPassword", resourceCulture); } } @@ -9948,9 +9948,9 @@ internal static string SQL_SettingCredentialWithInteractive { /// /// Looks up a localized string similar to Cannot set the Credential property if 'Authentication={0}' has been specified in the connection string.. /// - internal static string SQL_SettingCredentialWithManagedIdentity { + internal static string SQL_SettingCredentialWithNonInteractive { get { - return ResourceManager.GetString("SQL_SettingCredentialWithManagedIdentity", resourceCulture); + return ResourceManager.GetString("SQL_SettingCredentialWithNonInteractive", resourceCulture); } } @@ -9984,9 +9984,9 @@ internal static string SQL_SettingInteractiveWithCredential { /// /// Looks up a localized string similar to Cannot use 'Authentication={0}', if the Credential property has been set.. /// - internal static string SQL_SettingManagedIdentityWithCredential { + internal static string SQL_SettingNonInteractiveWithCredential { get { - return ResourceManager.GetString("SQL_SettingManagedIdentityWithCredential", resourceCulture); + return ResourceManager.GetString("SQL_SettingNonInteractiveWithCredential", resourceCulture); } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx index 6b5765a690..106e9c3f11 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx @@ -2502,7 +2502,7 @@ "Authentication=Active Directory Device Code Flow" kann nicht mit den Schlüsselwörtern "User ID", "UID", "Password" oder "PWD" für die Verbindungszeichenfolge verwendet werden. - + "Authentication={0}" kann nicht mit den Schlüsselwörtern "Password" oder "PWD" für die Verbindungszeichenfolge verwendet werden. @@ -4575,13 +4575,13 @@ Die Eigenschaft "Credential" kann nicht festgelegt werden, wenn in der Verbindungszeichenfolge "Authentication=Active Directory Device Code Flow" angegeben wurde. - + Die Eigenschaft "Credential" kann nicht festgelegt werden, wenn in der Verbindungszeichenfolge "Authentication={0}" angegeben wurde. "Authentication=Active Directory Device Code Flow" kann nicht verwendet werden, wenn die Eigenschaft "Credential" festgelegt wurde. - + "Authentication={0}" kann nicht verwendet werden, wenn die Eigenschaft "Credential" festgelegt wurde. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx index fb5432e665..38a76d19d3 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx @@ -2502,7 +2502,7 @@ No se puede usar "Authentication=Active Directory Device Code Flow" con las palabras clave de cadena de conexión "User ID", "UID", "Password" ni "PWD". - + No se puede usar "Authentication={0}" con las palabras clave de cadena de conexión "Password" ni "PWD". @@ -4575,13 +4575,13 @@ No se puede establecer la propiedad Credential si se ha especificado "Authentication=Active Directory Device Code Flow" en la cadena de conexión. - + No se puede establecer la propiedad Credential si se ha especificado "Authentication={0}" en la cadena de conexión. No se puede usar "Active Directory Device Code Flow" si se ha establecido la propiedad Credential. - + No se puede usar "Authentication={0}" si se ha establecido la propiedad Credential. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx index 8ab8d6ecca..24736da85e 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx @@ -2502,7 +2502,7 @@ Impossible d'utiliser « Authentication=Active Directory Device Code Flow » avec les mots clés de chaîne de connexion « User ID », « UID », « Password » et « PWD ». - + Impossible d'utiliser « Authentication={0} » avec les mots clés de chaîne de connexion « Password » ou « PWD ». @@ -4575,13 +4575,13 @@ Impossible de définir la propriété Credential si « Authentication=Active Directory Device Code Flow » est spécifié dans la chaîne de connexion. - + Impossible de définir la propriété Credential si « Authentication={0} » a été spécifié dans la chaîne de connexion. Impossible d'utiliser « Authentication=Active Directory Device Code Flow » si la propriété Credential est définie. - + Impossible d'utiliser « Authentication={0} », si la propriété Credential a été définie. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx index 65bf14742d..0fbf3cd419 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx @@ -2502,7 +2502,7 @@ Non è possibile usare 'Authentication=Active Directory Device Code Flow' con le parole chiave della stringa di connessione 'User ID', 'UID', 'Password' o 'PWD'. - + Non è possibile usare 'Authentication={0}' con le parole chiave della stringa di connessione 'Password' o 'PWD'. @@ -4575,13 +4575,13 @@ Non è possibile impostare la proprietà Credential se nella stringa di connessione è stato specificato 'Authentication=Active Directory Device Code Flow'. - + Non è possibile impostare la proprietà Credential se nella stringa di connessione è stato specificato 'Authentication={0}'. Non è possibile usare 'Authentication=Active Directory Device Code Flow' se è stata impostata la proprietà Credential. - + Non è possibile usare 'Authentication={0}' se è stata impostata la proprietà Credential. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx index 42a8c9b56a..be84e91fe6 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx @@ -2502,7 +2502,7 @@ 'Authentication=Active Directory Device Code Flow' と接続文字列キーワード 'User ID'、'UID'、'Password'、'PWD' を一緒に使用することはできません。 - + 'Authentication={0}' と接続文字列キーワード 'Password'、'PWD' を一緒に使用することはできません。 @@ -4575,13 +4575,13 @@ 接続文字列で 'Authentication=Active Directory Device Code Flow' が指定されている場合は、Credential プロパティを設定できません。 - + 接続文字列で 'Authentication={0}' が指定されている場合は、Credential プロパティを設定できません。 Credential プロパティが設定されている場合は、'Authentication=Active Directory Device Code Flow' を使用できません。 - + Credential プロパティが設定されている場合は、'Authentication={0}' を使用できません。 diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx index 85c72f105f..01480f0a51 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx @@ -2502,7 +2502,7 @@ 'Authentication=Active Directory Device Code Flow'를 'User ID', 'UID', 'Password' 또는 'PWD' 연결 문자열 키워드와 함께 사용할 수 없습니다. - + 'Authentication={0}'을(를) 'Password' 또는 'PWD' 연결 문자열 키워드와 함께 사용할 수 없습니다. @@ -4575,13 +4575,13 @@ 'Authentication=Active Directory Device Code Flow'가 연결 문자열에 지정된 경우 Credential 속성을 설정할 수 없습니다. - + 'Authentication={0}'이(가) 연결 문자열에 지정된 경우 자격 증명 속성을 설정할 수 없습니다. Credential 속성이 설정된 경우 'Authentication=Active Directory Device Code Flow'를 사용할 수 없습니다. - + 자격 증명 속성이 설정된 경우 'Authentication={0}'을(를) 사용할 수 없습니다. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx index bd242cca25..31016c02de 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx @@ -2502,7 +2502,7 @@ Não é possível usar 'Authentication=Active Directory Device Code Flow' com as palavras-chave de cadeia de conexão 'User ID', 'UID', 'Password' ou 'PWD'. - + Não é possível usar 'Authentication={0}' com as palavras-chave de cadeia de conexão 'Password' ou 'PWD'. @@ -4575,13 +4575,13 @@ Não é possível definir a propriedade Credential quando 'Authentication=Active Directory Device Code Flow' está especificado na cadeia de conexão. - + Não é possível definir a propriedade Credential quando 'Authentication={0}' é especificado na cadeia de conexão. Não é possível usar 'Authentication=Active Directory Device Code Flow' quando a propriedade Credential está configurada. - + Não é possível usar 'Authentication={0}' quando a propriedade Credential está configurada. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx index 6f12cd0ac6..99245436c0 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx @@ -2502,7 +2502,7 @@ Cannot use 'Authentication=Active Directory Device Code Flow' with 'User ID', 'UID', 'Password' or 'PWD' connection string keywords. - + Cannot use 'Authentication={0}' with 'Password' or 'PWD' connection string keywords. @@ -4575,13 +4575,13 @@ Cannot set the Credential property if 'Authentication=Active Directory Device Code Flow' has been specified in the connection string. - + Cannot set the Credential property if 'Authentication={0}' has been specified in the connection string. Cannot use 'Authentication=Active Directory Device Code Flow', if the Credential property has been set. - + Cannot use 'Authentication={0}', if the Credential property has been set. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx index 5151c1ec6a..cd591172a3 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx @@ -2502,7 +2502,7 @@ Невозможно использовать "Authentication=Active Directory Device Code Flow" с ключевыми словами строки подключения "User ID", "UID", "Password" или "PWD". - + Невозможно использовать "Authentication={0}" с ключевыми словами Password или PWD в строке подключения. @@ -4575,13 +4575,13 @@ Не удается задать свойство Credential, если в строке подключения указано "Authentication=Active Directory Device Code Flow". - + Невозможно задать свойство Credential, если в строке подключения указано "Authentication={0}". Невозможно использовать ", если задано свойство Credential. - + Невозможно использовать "Authentication={0}", если задано свойство Credential. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx index fb77f3bf7a..3e0d1b95c7 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx @@ -2502,7 +2502,7 @@ 无法结合使用 "Authentication=Active Directory Device Code Flow" 与 "User ID"、"UID"、"Password" 或 "PWD" 连接字符串关键字。 - + 无法将 "Authentication={0}" 与 "Password" 或 "PWD" 连接字符串关键字结合使用。 @@ -4575,13 +4575,13 @@ 如果在连接字符串中指定了 "Authentication=Active Directory Device Code Flow",则无法设置 Credential 属性。 - + 如果在连接字符串中指定了 "Authentication={0}",则无法设置 Credential 属性。 如果设置了 Credential 属性,则无法使用 "Authentication=Active Directory Device Code Flow"。 - + 如果设置了 Credential 属性,则无法使用 "Authentication={0}"。 diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx index 0d2aae5c16..3eb6d9911f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx @@ -2502,7 +2502,7 @@ 無法搭配 'User ID'、'UID'、'Password' 或 'PWD' 連接字串關鍵字使用 'Authentication=Active Directory Device Code Flow'。 - + 使用 'Authentication={0}' 時,無法搭配 'Password' 或 'PWD' 連接字串關鍵字一起使用。 @@ -4575,13 +4575,13 @@ 如果連接字串中已指定 'Authentication=Active Directory Device Code Flow',則無法設定 Credential 屬性。 - + 如果連接字串中已指定 'Authentication={0}',則無法設定 Credential 屬性。 如果已設定 Credential 屬性,則無法使用 'Authentication=Active Directory Device Code Flow'。 - + 如果已設定 Credential 屬性,就無法使用 'Authentication={0}'。 diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs index 65aeec11c8..25f591f1c2 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Concurrent; -using System.Linq; using System.Security; using System.Threading; using System.Threading.Tasks; @@ -73,7 +72,8 @@ public override bool IsSupported(SqlAuthenticationMethod authentication) || authentication == SqlAuthenticationMethod.ActiveDirectoryServicePrincipal || authentication == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow || authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity - || authentication == SqlAuthenticationMethod.ActiveDirectoryMSI; + || authentication == SqlAuthenticationMethod.ActiveDirectoryMSI + || authentication == SqlAuthenticationMethod.ActiveDirectoryDefault; } /// @@ -117,12 +117,31 @@ public override async Task AcquireTokenAsync(SqlAuthenti string tenantId = parameters.Authority.Substring(seperatorIndex + 1); string authority = parameters.Authority.Remove(seperatorIndex + 1); - TokenCredentialOptions tokenCredentialOptions = new TokenCredentialOptions() { AuthorityHost = new Uri(authority) }; TokenRequestContext tokenRequestContext = new TokenRequestContext(scopes); + string clientId = string.IsNullOrWhiteSpace(parameters.UserId) ? null : parameters.UserId; + + if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryDefault) + { + DefaultAzureCredentialOptions defaultAzureCredentialOptions = new DefaultAzureCredentialOptions() + { + AuthorityHost = new Uri(authority), + ManagedIdentityClientId = clientId, + InteractiveBrowserTenantId = tenantId, + SharedTokenCacheTenantId = tenantId, + SharedTokenCacheUsername = clientId, + VisualStudioCodeTenantId = tenantId, + VisualStudioTenantId = tenantId, + ExcludeInteractiveBrowserCredential = true // Force disabled, even though it's disabled by default to respect driver specifications. + }; + AccessToken accessToken = await new DefaultAzureCredential(defaultAzureCredentialOptions).GetTokenAsync(tokenRequestContext, cts.Token); + SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Default auth mode. Expiry Time: {0}", accessToken.ExpiresOn); + return new SqlAuthenticationToken(accessToken.Token, accessToken.ExpiresOn); + } + + TokenCredentialOptions tokenCredentialOptions = new TokenCredentialOptions() { AuthorityHost = new Uri(authority) }; if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryMSI) { - string clientId = string.IsNullOrWhiteSpace(parameters.UserId) ? null : parameters.UserId; AccessToken accessToken = await new ManagedIdentityCredential(clientId, tokenCredentialOptions).GetTokenAsync(tokenRequestContext, cts.Token); SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Managed Identity auth mode. Expiry Time: {0}", accessToken.ExpiresOn); return new SqlAuthenticationToken(accessToken.Token, accessToken.ExpiresOn); @@ -195,15 +214,28 @@ public override async Task AcquireTokenAsync(SqlAuthenti parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow) { // Fetch available accounts from 'app' instance - System.Collections.Generic.IEnumerable accounts = await app.GetAccountsAsync(); - IAccount account; - if (!string.IsNullOrEmpty(parameters.UserId)) + System.Collections.Generic.IEnumerator accounts = (await app.GetAccountsAsync()).GetEnumerator(); + + IAccount account = default; + if (accounts.MoveNext()) { - account = accounts.FirstOrDefault(a => parameters.UserId.Equals(a.Username, System.StringComparison.InvariantCultureIgnoreCase)); - } - else - { - account = accounts.FirstOrDefault(); + if (!string.IsNullOrEmpty(parameters.UserId)) + { + do + { + IAccount currentVal = accounts.Current; + if (string.Compare(parameters.UserId, currentVal.Username, StringComparison.InvariantCultureIgnoreCase) == 0) + { + account = currentVal; + break; + } + } + while (accounts.MoveNext()); + } + else + { + account = accounts.Current; + } } if (null != account) diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs index 9796b297c2..4b4e1b0060 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs @@ -29,6 +29,8 @@ public partial class SqlConnectionStringBuilderTest [InlineData("Authentication = ActiveDirectoryManagedIdentity ")] [InlineData("Authentication = Active Directory MSI ")] [InlineData("Authentication = ActiveDirectoryMSI ")] + [InlineData("Authentication = Active Directory Default ")] + [InlineData("Authentication = ActiveDirectoryDefault ")] [InlineData("Command Timeout = 5")] [InlineData("Command Timeout = 15")] [InlineData("Command Timeout = 0")] diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs index d487b187fb..5723958636 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs @@ -427,6 +427,52 @@ public static void ActiveDirectoryMSIWithPasswordMustFail() Assert.Contains(expectedMessage, e.Message); } + [ConditionalFact(nameof(IsAADConnStringsSetup))] + public static void ActiveDirectoryDefaultWithCredentialsMustFail() + { + // connection fails with expected error message. + string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; + string connStrWithNoCred = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys) + + "Authentication=Active Directory Default;"; + + SecureString str = new SecureString(); + foreach (char c in "hello") + { + str.AppendChar(c); + } + str.MakeReadOnly(); + SqlCredential credential = new SqlCredential("someuser", str); + InvalidOperationException e = Assert.Throws(() => ConnectAndDisconnect(connStrWithNoCred, credential)); + + string expectedMessage = "Cannot set the Credential property if 'Authentication=Active Directory Default' has been specified in the connection string."; + Assert.Contains(expectedMessage, e.Message); + } + + [ConditionalFact(nameof(IsAADConnStringsSetup))] + public static void ActiveDirectoryDefaultWithPasswordMustFail() + { + // connection fails with expected error message. + string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; + string connStrWithNoCred = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys) + + "Authentication=ActiveDirectoryDefault; Password=anything"; + + ArgumentException e = Assert.Throws(() => ConnectAndDisconnect(connStrWithNoCred)); + + string expectedMessage = "Cannot use 'Authentication=Active Directory Default' with 'Password' or 'PWD' connection string keywords."; + Assert.Contains(expectedMessage, e.Message); + } + + [ConditionalFact(nameof(IsAADConnStringsSetup))] + public static void ActiveDirectoryDefaultMustPass() + { + string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" }; + string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys) + + "Authentication=ActiveDirectoryDefault;"; + + // Connection should be established using Managed Identity by default. + ConnectAndDisconnect(connStr); + } + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsIntegratedSecuritySetup), nameof(DataTestUtility.AreConnStringsSetup))] public static void ADInteractiveUsingSSPI() { diff --git a/src/Microsoft.Data.SqlClient/tests/NuGet.config b/src/Microsoft.Data.SqlClient/tests/NuGet.config new file mode 100644 index 0000000000..366141ab39 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/NuGet.config @@ -0,0 +1,8 @@ + + + + + + + +