From d14c0b1196de6271b36251d7aff63cfeadcad799 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Mon, 28 Sep 2020 09:39:22 -0700 Subject: [PATCH] Feature | Add support for user-defined ApplicationClientId (#740) --- ...tionClientIdAzureAuthenticationProvider.cs | 26 ++++++++++++++++++ .../ActiveDirectoryAuthenticationProvider.xml | 27 ++++++++++++++++--- .../netcore/ref/Microsoft.Data.SqlClient.cs | 4 ++- ...uthenticationProviderManager.NetCoreApp.cs | 24 ++++++++++++++--- ...thenticationProviderManager.NetStandard.cs | 2 +- .../SqlAuthenticationProviderManager.cs | 1 + .../src/Microsoft/Data/SqlClient/SqlUtil.cs | 4 +-- .../netfx/ref/Microsoft.Data.SqlClient.cs | 4 ++- .../SqlAuthenticationProviderManager.cs | 27 +++++++++++++++---- .../src/Microsoft/Data/SqlClient/SqlUtil.cs | 4 +-- .../ActiveDirectoryAuthenticationProvider.cs | 16 ++++++++--- 11 files changed, 115 insertions(+), 24 deletions(-) create mode 100644 doc/samples/ApplicationClientIdAzureAuthenticationProvider.cs diff --git a/doc/samples/ApplicationClientIdAzureAuthenticationProvider.cs b/doc/samples/ApplicationClientIdAzureAuthenticationProvider.cs new file mode 100644 index 0000000000..f206a1c29e --- /dev/null +++ b/doc/samples/ApplicationClientIdAzureAuthenticationProvider.cs @@ -0,0 +1,26 @@ +// +using System; +using Microsoft.Data.SqlClient; + +namespace CustomAuthenticationProviderExamples +{ + public class Program + { + public static void Main() + { + // Supported for all authentication modes supported by ActiveDirectoryAuthenticationProvider + ActiveDirectoryAuthenticationProvider provider = new ActiveDirectoryAuthenticationProvider(""); + if (provider.IsSupported(SqlAuthenticationMethod.ActiveDirectoryInteractive)) + { + SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryInteractive, provider); + } + + using (SqlConnection sqlConnection = new SqlConnection("Server=.database.windows.net;Authentication=Active Directory Interactive;Database=;")) + { + sqlConnection.Open(); + Console.WriteLine("Connected successfully!"); + } + } + } +} +// diff --git a/doc/snippets/Microsoft.Data.SqlClient/ActiveDirectoryAuthenticationProvider.xml b/doc/snippets/Microsoft.Data.SqlClient/ActiveDirectoryAuthenticationProvider.xml index 49a2e5616a..052cd0cc30 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/ActiveDirectoryAuthenticationProvider.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/ActiveDirectoryAuthenticationProvider.xml @@ -11,19 +11,38 @@ - The callback method to be used when performing 'Active Directory Device Code Flow' authentication. + Client Application Id to be used for acquiring an access token for federated authentication. The driver uses its own application client id by default. - Initializes the class with the provided device code flow callback method. + Initializes the class with the provided application client id. + + + + + + + The callback method to be used with 'Active Directory Device Code Flow' authentication. + (Optional) Client Application Id to be used for acquiring an access token for federated authentication. The driver uses its own application client id by default. + + Initializes the class with the provided device code flow callback method and application client id. + + The Active Directory authentication parameters passed to authentication providers. Acquires a security token from the authority. Represents an asynchronous operation that returns the authentication token. - The callback method to be used when performing 'Active Directory Device Code Flow' authentication. - Sets the callback method, overriding the default implementation that processes the result when performing 'Active Directory Device Code Flow' authentication. + The callback method to be used with 'Active Directory Device Code Flow' authentication. + Sets the callback method, overriding the default implementation that processes the result for 'Active Directory Device Code Flow' authentication. The parent as an object, in order to be used from shared .NET Standard assemblies. 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 3a75ca8d09..f08e1835ed 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs @@ -36,7 +36,9 @@ public sealed partial class ActiveDirectoryAuthenticationProvider : SqlAuthentic /// public ActiveDirectoryAuthenticationProvider() { } /// - public ActiveDirectoryAuthenticationProvider(System.Func deviceCodeFlowCallbackMethod) { } + public ActiveDirectoryAuthenticationProvider(string applicationClientId) { } + /// + public ActiveDirectoryAuthenticationProvider(System.Func deviceCodeFlowCallbackMethod, string applicationClientId = null) { } /// public override System.Threading.Tasks.Task AcquireTokenAsync(SqlAuthenticationParameters parameters) { throw 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 bf7c1632d1..06d9c786ce 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 @@ -15,7 +15,6 @@ internal partial class SqlAuthenticationProviderManager static SqlAuthenticationProviderManager() { - var activeDirectoryAuthProvider = new ActiveDirectoryAuthenticationProvider(); SqlAuthenticationProviderConfigurationSection configurationSection = null; try @@ -31,10 +30,11 @@ static SqlAuthenticationProviderManager() catch (ConfigurationErrorsException e) { // Don't throw an error for invalid config files - SqlClientEventSource.Log.TryTraceEvent("Unable to load custom SqlAuthenticationProviders or SqlClientAuthenticationProviders. ConfigurationManager failed to load due to configuration errors: {0}", e); + SqlClientEventSource.Log.TryTraceEvent("static SqlAuthenticationProviderManager: Unable to load custom SqlAuthenticationProviders or SqlClientAuthenticationProviders. ConfigurationManager failed to load due to configuration errors: {0}", e); } Instance = new SqlAuthenticationProviderManager(configurationSection); + var activeDirectoryAuthProvider = new ActiveDirectoryAuthenticationProvider(Instance._applicationClientId); Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryIntegrated, activeDirectoryAuthProvider); Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, activeDirectoryAuthProvider); Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryInteractive, activeDirectoryAuthProvider); @@ -59,6 +59,16 @@ public SqlAuthenticationProviderManager(SqlAuthenticationProviderConfigurationSe return; } + if (!string.IsNullOrEmpty(configSection.ApplicationClientId)) + { + _applicationClientId = configSection.ApplicationClientId; + _sqlAuthLogger.LogInfo(_typeName, methodName, "Received user-defined Application Client Id"); + } + else + { + _sqlAuthLogger.LogInfo(_typeName, methodName, "No user-defined Application Client Id found."); + } + // Create user-defined auth initializer, if any. if (!string.IsNullOrEmpty(configSection.InitializerType)) { @@ -159,13 +169,19 @@ internal class SqlAuthenticationProviderConfigurationSection : ConfigurationSect /// User-defined auth providers. /// [ConfigurationProperty("providers")] - public ProviderSettingsCollection Providers => (ProviderSettingsCollection)base["providers"]; + public ProviderSettingsCollection Providers => (ProviderSettingsCollection)this["providers"]; /// /// User-defined initializer. /// [ConfigurationProperty("initializerType")] - public string InitializerType => base["initializerType"] as string; + public string InitializerType => this["initializerType"] as string; + + /// + /// Application Client Id + /// + [ConfigurationProperty("applicationClientId", IsRequired = false)] + public string ApplicationClientId => this["applicationClientId"] as string; } /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetStandard.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetStandard.cs index e3d5fd5e11..d283f58400 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetStandard.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetStandard.cs @@ -8,8 +8,8 @@ internal partial class SqlAuthenticationProviderManager { static SqlAuthenticationProviderManager() { - var activeDirectoryAuthProvider = new ActiveDirectoryAuthenticationProvider(); Instance = new SqlAuthenticationProviderManager(); + var activeDirectoryAuthProvider = new ActiveDirectoryAuthenticationProvider(Instance._applicationClientId); Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, activeDirectoryAuthProvider); Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryIntegrated, activeDirectoryAuthProvider); Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryInteractive, activeDirectoryAuthProvider); 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 8d85802486..512f3d4b04 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 @@ -24,6 +24,7 @@ internal partial class SqlAuthenticationProviderManager private readonly IReadOnlyCollection _authenticationsWithAppSpecifiedProvider; private readonly ConcurrentDictionary _providers; private readonly SqlClientLogger _sqlAuthLogger = new SqlClientLogger(); + private readonly string _applicationClientId = ActiveDirectoryAuthentication.AdoClientId; public static readonly SqlAuthenticationProviderManager Instance; 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 ebfe96b769..744ab20402 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 @@ -903,11 +903,11 @@ internal static Exception BulkLoadUnspecifiedSortOrder() internal static Exception BulkLoadInvalidOrderHint() { return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_BulkLoadInvalidOrderHint)); - } + } internal static Exception BulkLoadOrderHintInvalidColumn(string columnName) { return ADP.InvalidOperation(string.Format(System.StringsHelper.GetString(Strings.SQL_BulkLoadOrderHintInvalidColumn), columnName)); - } + } internal static Exception BulkLoadOrderHintDuplicateColumn(string columnName) { return ADP.InvalidOperation(string.Format(System.StringsHelper.GetString(Strings.SQL_BulkLoadOrderHintDuplicateColumn), columnName)); 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 6f29fe813c..8fb8e1fed5 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs @@ -41,7 +41,9 @@ public sealed class ActiveDirectoryAuthenticationProvider : SqlAuthenticationPro /// public ActiveDirectoryAuthenticationProvider() { } /// - public ActiveDirectoryAuthenticationProvider(System.Func deviceCodeFlowCallbackMethod) { } + public ActiveDirectoryAuthenticationProvider(string applicationClientId) { } + /// + public ActiveDirectoryAuthenticationProvider(System.Func deviceCodeFlowCallbackMethod, string applicationClientId = null) { } /// public override System.Threading.Tasks.Task AcquireTokenAsync(SqlAuthenticationParameters parameters) { throw null; } /// 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 06bdc0bb6b..b875c0160d 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 @@ -23,7 +23,6 @@ internal class SqlAuthenticationProviderManager static SqlAuthenticationProviderManager() { - var activeDirectoryAuthProvider = new ActiveDirectoryAuthenticationProvider(); SqlAuthenticationProviderConfigurationSection configurationSection = null; try { @@ -38,9 +37,11 @@ static SqlAuthenticationProviderManager() catch (ConfigurationErrorsException e) { // Don't throw an error for invalid config files - SqlClientEventSource.Log.TryTraceEvent("Unable to load custom SqlAuthenticationProviders or SqlClientAuthenticationProviders. ConfigurationManager failed to load due to configuration errors: {0}", e); + SqlClientEventSource.Log.TryTraceEvent("static SqlAuthenticationProviderManager: Unable to load custom SqlAuthenticationProviders or SqlClientAuthenticationProviders. ConfigurationManager failed to load due to configuration errors: {0}", e); } Instance = new SqlAuthenticationProviderManager(configurationSection); + + var activeDirectoryAuthProvider = new ActiveDirectoryAuthenticationProvider(Instance._applicationClientId); Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryIntegrated, activeDirectoryAuthProvider); Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, activeDirectoryAuthProvider); Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryInteractive, activeDirectoryAuthProvider); @@ -54,6 +55,7 @@ static SqlAuthenticationProviderManager() private readonly IReadOnlyCollection _authenticationsWithAppSpecifiedProvider; private readonly ConcurrentDictionary _providers; private readonly SqlClientLogger _sqlAuthLogger = new SqlClientLogger(); + private readonly string _applicationClientId = ActiveDirectoryAuthentication.AdoClientId; /// /// Constructor. @@ -72,8 +74,17 @@ public SqlAuthenticationProviderManager(SqlAuthenticationProviderConfigurationSe return; } + if (!string.IsNullOrEmpty(configSection.ApplicationClientId)) + { + _applicationClientId = configSection.ApplicationClientId; + _sqlAuthLogger.LogInfo(_typeName, methodName, "Received user-defined Application Client Id"); + } + else + { + _sqlAuthLogger.LogInfo(_typeName, methodName, "No user-defined Application Client Id found."); + } + // Create user-defined auth initializer, if any. - // if (!string.IsNullOrEmpty(configSection.InitializerType)) { try @@ -226,13 +237,19 @@ internal class SqlAuthenticationProviderConfigurationSection : ConfigurationSect /// User-defined auth providers. /// [ConfigurationProperty("providers")] - public ProviderSettingsCollection Providers => (ProviderSettingsCollection)base["providers"]; + public ProviderSettingsCollection Providers => (ProviderSettingsCollection)this["providers"]; /// /// User-defined initializer. /// [ConfigurationProperty("initializerType")] - public string InitializerType => base["initializerType"] as string; + public string InitializerType => this["initializerType"] as string; + + /// + /// Application Client Id + /// + [ConfigurationProperty("applicationClientId", IsRequired = false)] + public string ApplicationClientId => this["applicationClientId"] as string; } /// 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 6318a1491f..fee169e994 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 @@ -772,7 +772,7 @@ static internal Exception UDTUnexpectedResult(string exceptionText) static internal Exception CannotCompleteDelegatedTransactionWithOpenResults(SqlInternalConnectionTds internalConnection, bool marsOn) { SqlErrorCollection errors = new SqlErrorCollection(); - errors.Add(new SqlError(TdsEnums.TIMEOUT_EXPIRED, (byte)0x00, TdsEnums.MIN_ERROR_CLASS, null, (StringsHelper.GetString(Strings.ADP_OpenReaderExists, marsOn? ADP.Command : ADP.Connection)), "", 0, TdsEnums.SNI_WAIT_TIMEOUT)); + errors.Add(new SqlError(TdsEnums.TIMEOUT_EXPIRED, (byte)0x00, TdsEnums.MIN_ERROR_CLASS, null, (StringsHelper.GetString(Strings.ADP_OpenReaderExists, marsOn ? ADP.Command : ADP.Connection)), "", 0, TdsEnums.SNI_WAIT_TIMEOUT)); return SqlException.CreateException(errors, null, internalConnection); } static internal SysTx.TransactionPromotionException PromotionFailed(Exception inner) @@ -858,7 +858,7 @@ static internal Exception UDTInvalidSqlType(string typeName) { return ADP.Argument(StringsHelper.GetString(Strings.SQLUDT_InvalidSqlType, typeName)); } - + static internal Exception UDTInvalidSize(int maxSize, int maxSupportedSize) { throw ADP.ArgumentOutOfRange(StringsHelper.GetString(Strings.SQLUDT_InvalidSize, maxSize, maxSupportedSize)); 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 fe3317f17b..fd3ddfe932 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs @@ -20,13 +20,21 @@ public sealed class ActiveDirectoryAuthenticationProvider : SqlAuthenticationPro private readonly SqlClientLogger _logger = new SqlClientLogger(); private Func _deviceCodeFlowCallback; private ICustomWebUi _customWebUI = null; + private readonly string _applicationClientId = ActiveDirectoryAuthentication.AdoClientId; /// public ActiveDirectoryAuthenticationProvider() => new ActiveDirectoryAuthenticationProvider(DefaultDeviceFlowCallback); /// - public ActiveDirectoryAuthenticationProvider(Func deviceCodeFlowCallbackMethod) + public ActiveDirectoryAuthenticationProvider(string applicationClientId) => new ActiveDirectoryAuthenticationProvider(DefaultDeviceFlowCallback, applicationClientId); + + /// + public ActiveDirectoryAuthenticationProvider(Func deviceCodeFlowCallbackMethod, string applicationClientId = null) { + if (applicationClientId != null) + { + _applicationClientId = applicationClientId; + } SetDeviceCodeFlowCallback(deviceCodeFlowCallbackMethod); } @@ -112,7 +120,7 @@ public override void BeforeUnload(SqlAuthenticationMethod authentication) #if netstandard if (parentActivityOrWindowFunc != null) { - app = PublicClientApplicationBuilder.Create(ActiveDirectoryAuthentication.AdoClientId) + app = PublicClientApplicationBuilder.Create(_applicationClientId) .WithAuthority(parameters.Authority) .WithClientName(Common.DbConnectionStringDefaults.ApplicationName) .WithClientVersion(Common.ADP.GetAssemblyVersion().ToString()) @@ -124,7 +132,7 @@ public override void BeforeUnload(SqlAuthenticationMethod authentication) #if netfx if (_iWin32WindowFunc != null) { - app = PublicClientApplicationBuilder.Create(ActiveDirectoryAuthentication.AdoClientId) + app = PublicClientApplicationBuilder.Create(_applicationClientId) .WithAuthority(parameters.Authority) .WithClientName(Common.DbConnectionStringDefaults.ApplicationName) .WithClientVersion(Common.ADP.GetAssemblyVersion().ToString()) @@ -137,7 +145,7 @@ public override void BeforeUnload(SqlAuthenticationMethod authentication) else #endif { - app = PublicClientApplicationBuilder.Create(ActiveDirectoryAuthentication.AdoClientId) + app = PublicClientApplicationBuilder.Create(_applicationClientId) .WithAuthority(parameters.Authority) .WithClientName(Common.DbConnectionStringDefaults.ApplicationName) .WithClientVersion(Common.ADP.GetAssemblyVersion().ToString())