Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature | Add support for user-defined ApplicationClientId #740

Merged
merged 4 commits into from Sep 28, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
26 changes: 26 additions & 0 deletions doc/samples/ApplicationClientIdAzureAuthenticationProvider.cs
@@ -0,0 +1,26 @@
//<Snippet1>
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("<application_client_id>");
if (provider.IsSupported(SqlAuthenticationMethod.ActiveDirectoryInteractive))
{
SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryInteractive, provider);
}

using (SqlConnection sqlConnection = new SqlConnection("Server=<myserver>.database.windows.net;Authentication=Active Directory Interactive;Database=<db>;"))
{
sqlConnection.Open();
Console.WriteLine("Connected successfully!");
}
}
}
}
//</Snippet1>
Expand Up @@ -11,19 +11,38 @@
</summary>
</ctor>
<ctor2>
<param name="deviceCodeFlowCallbackMethod">The callback method to be used when performing 'Active Directory Device Code Flow' authentication.</param>
<param name="applicationClientId">Client Application Id to be used for acquiring an access token for federated authentication. The driver uses its own application client id by default.</param>
<summary>
Initializes the <see cref="T:Microsoft.Data.SqlClient.ActiveDirectoryAuthenticationProvider" /> class with the provided device code flow callback method.
Initializes the <see cref="T:Microsoft.Data.SqlClient.ActiveDirectoryAuthenticationProvider" /> class with the provided application client id.
</summary>
<remarks>
<format type="text/markdown">
<![CDATA[

## Examples
The following example demonstrates providing a user-defined application client id to SqlClient for the "Active Directory Interactive" authentication method:

[!code-csharp[ActiveDirectory_ApplicationClientId Example#1](~/../sqlclient/doc/samples/ApplicationClientIdAzureAuthenticationProvider.cs#1)]

]]>
</format>
</remarks>
</ctor2>
<ctor3>
<param name="deviceCodeFlowCallbackMethod">The callback method to be used with 'Active Directory Device Code Flow' authentication.</param>
<param name="applicationClientId">(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.</param>
<summary>
Initializes the <see cref="T:Microsoft.Data.SqlClient.ActiveDirectoryAuthenticationProvider" /> class with the provided device code flow callback method and application client id.
</summary>
</ctor3>
<AcquireTokenAsync>
<param name="parameters">The Active Directory authentication parameters passed to authentication providers.</param>
<summary>Acquires a security token from the authority.</summary>
<returns>Represents an asynchronous operation that returns the authentication token.</returns>
</AcquireTokenAsync>
<SetDeviceCodeFlowCallback>
<param name="deviceCodeFlowCallbackMethod">The callback method to be used when performing 'Active Directory Device Code Flow' authentication.</param>
<summary>Sets the callback method, overriding the default implementation that processes the result when performing 'Active Directory Device Code Flow' authentication.</summary>
<param name="deviceCodeFlowCallbackMethod">The callback method to be used with 'Active Directory Device Code Flow' authentication.</param>
<summary>Sets the callback method, overriding the default implementation that processes the result for 'Active Directory Device Code Flow' authentication.</summary>
</SetDeviceCodeFlowCallback>
<SetParentActivityOrWindowFunc>
<param name="parentActivityOrWindowFunc">The parent as an object, in order to be used from shared .NET Standard assemblies.</param>
Expand Down
Expand Up @@ -36,7 +36,9 @@ public sealed partial class ActiveDirectoryAuthenticationProvider : SqlAuthentic
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/ActiveDirectoryAuthenticationProvider.xml' path='docs/members[@name="ActiveDirectoryAuthenticationProvider"]/ctor/*'/>
public ActiveDirectoryAuthenticationProvider() { }
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/ActiveDirectoryAuthenticationProvider.xml' path='docs/members[@name="ActiveDirectoryAuthenticationProvider"]/ctor2/*'/>
public ActiveDirectoryAuthenticationProvider(System.Func<Microsoft.Identity.Client.DeviceCodeResult, System.Threading.Tasks.Task> deviceCodeFlowCallbackMethod) { }
public ActiveDirectoryAuthenticationProvider(string applicationClientId) { }
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/ActiveDirectoryAuthenticationProvider.xml' path='docs/members[@name="ActiveDirectoryAuthenticationProvider"]/ctor3/*'/>
public ActiveDirectoryAuthenticationProvider(System.Func<Microsoft.Identity.Client.DeviceCodeResult, System.Threading.Tasks.Task> deviceCodeFlowCallbackMethod, string applicationClientId = null) { }
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/ActiveDirectoryAuthenticationProvider.xml' path='docs/members[@name="ActiveDirectoryAuthenticationProvider"]/AcquireTokenAsync/*'/>
public override System.Threading.Tasks.Task<SqlAuthenticationToken> AcquireTokenAsync(SqlAuthenticationParameters parameters) { throw null; }
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/ActiveDirectoryAuthenticationProvider.xml' path='docs/members[@name="ActiveDirectoryAuthenticationProvider"]/SetDeviceCodeFlowCallback/*'/>
Expand Down
Expand Up @@ -15,7 +15,6 @@ internal partial class SqlAuthenticationProviderManager

static SqlAuthenticationProviderManager()
{
var activeDirectoryAuthProvider = new ActiveDirectoryAuthenticationProvider();
SqlAuthenticationProviderConfigurationSection configurationSection = null;

try
Expand All @@ -35,6 +34,7 @@ static SqlAuthenticationProviderManager()
}

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);
Expand All @@ -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))
{
Expand Down Expand Up @@ -159,13 +169,19 @@ internal class SqlAuthenticationProviderConfigurationSection : ConfigurationSect
/// User-defined auth providers.
/// </summary>
[ConfigurationProperty("providers")]
public ProviderSettingsCollection Providers => (ProviderSettingsCollection)base["providers"];
public ProviderSettingsCollection Providers => (ProviderSettingsCollection)this["providers"];

/// <summary>
/// User-defined initializer.
/// </summary>
[ConfigurationProperty("initializerType")]
public string InitializerType => base["initializerType"] as string;
public string InitializerType => this["initializerType"] as string;

/// <summary>
/// Application Client Id
/// </summary>
[ConfigurationProperty("applicationClientId", IsRequired = false)]
public string ApplicationClientId => this["applicationClientId"] as string;
}

/// <summary>
Expand Down
Expand Up @@ -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);
Expand Down
Expand Up @@ -24,6 +24,7 @@ internal partial class SqlAuthenticationProviderManager
private readonly IReadOnlyCollection<SqlAuthenticationMethod> _authenticationsWithAppSpecifiedProvider;
private readonly ConcurrentDictionary<SqlAuthenticationMethod, SqlAuthenticationProvider> _providers;
private readonly SqlClientLogger _sqlAuthLogger = new SqlClientLogger();
private readonly string _applicationClientId = ActiveDirectoryAuthentication.AdoClientId;

public static readonly SqlAuthenticationProviderManager Instance;

Expand Down
Expand Up @@ -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));
Expand Down
Expand Up @@ -41,7 +41,9 @@ public sealed class ActiveDirectoryAuthenticationProvider : SqlAuthenticationPro
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/ActiveDirectoryAuthenticationProvider.xml' path='docs/members[@name="ActiveDirectoryAuthenticationProvider"]/ctor/*'/>
public ActiveDirectoryAuthenticationProvider() { }
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/ActiveDirectoryAuthenticationProvider.xml' path='docs/members[@name="ActiveDirectoryAuthenticationProvider"]/ctor2/*'/>
public ActiveDirectoryAuthenticationProvider(System.Func<Microsoft.Identity.Client.DeviceCodeResult, System.Threading.Tasks.Task> deviceCodeFlowCallbackMethod) { }
public ActiveDirectoryAuthenticationProvider(string applicationClientId) { }
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/ActiveDirectoryAuthenticationProvider.xml' path='docs/members[@name="ActiveDirectoryAuthenticationProvider"]/ctor3/*'/>
public ActiveDirectoryAuthenticationProvider(System.Func<Microsoft.Identity.Client.DeviceCodeResult, System.Threading.Tasks.Task> deviceCodeFlowCallbackMethod, string applicationClientId = null) { }
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/ActiveDirectoryAuthenticationProvider.xml' path='docs/members[@name="ActiveDirectoryAuthenticationProvider"]/AcquireTokenAsync/*'/>
public override System.Threading.Tasks.Task<SqlAuthenticationToken> AcquireTokenAsync(SqlAuthenticationParameters parameters) { throw null; }
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/ActiveDirectoryAuthenticationProvider.xml' path='docs/members[@name="ActiveDirectoryAuthenticationProvider"]/SetDeviceCodeFlowCallback/*'/>
Expand Down
Expand Up @@ -23,7 +23,6 @@ internal class SqlAuthenticationProviderManager

static SqlAuthenticationProviderManager()
{
var activeDirectoryAuthProvider = new ActiveDirectoryAuthenticationProvider();
SqlAuthenticationProviderConfigurationSection configurationSection = null;
try
{
Expand All @@ -41,6 +40,8 @@ static SqlAuthenticationProviderManager()
SqlClientEventSource.Log.TryTraceEvent("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);
Expand All @@ -54,6 +55,7 @@ static SqlAuthenticationProviderManager()
private readonly IReadOnlyCollection<SqlAuthenticationMethod> _authenticationsWithAppSpecifiedProvider;
private readonly ConcurrentDictionary<SqlAuthenticationMethod, SqlAuthenticationProvider> _providers;
private readonly SqlClientLogger _sqlAuthLogger = new SqlClientLogger();
private readonly string _applicationClientId = ActiveDirectoryAuthentication.AdoClientId;

/// <summary>
/// Constructor.
Expand All @@ -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
Expand Down Expand Up @@ -226,13 +237,19 @@ internal class SqlAuthenticationProviderConfigurationSection : ConfigurationSect
/// User-defined auth providers.
/// </summary>
[ConfigurationProperty("providers")]
public ProviderSettingsCollection Providers => (ProviderSettingsCollection)base["providers"];
public ProviderSettingsCollection Providers => (ProviderSettingsCollection)this["providers"];

/// <summary>
/// User-defined initializer.
/// </summary>
[ConfigurationProperty("initializerType")]
public string InitializerType => base["initializerType"] as string;
public string InitializerType => this["initializerType"] as string;

/// <summary>
/// Application Client Id
/// </summary>
[ConfigurationProperty("applicationClientId", IsRequired = false)]
public string ApplicationClientId => this["applicationClientId"] as string;
}

/// <summary>
Expand Down
Expand Up @@ -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)
Expand Down Expand Up @@ -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));
Expand Down