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 2 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,11 +11,30 @@
</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 access token for federated authentication. The driver uses its own application client id by default.</param>
cheenamalhotra marked this conversation as resolved.
Show resolved Hide resolved
<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 user-defined application client id to SqlClient for performing "Active Directory Interactive" authentication method:
cheenamalhotra marked this conversation as resolved.
Show resolved Hide resolved

[!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 when performing 'Active Directory Device Code Flow' authentication.</param>
cheenamalhotra marked this conversation as resolved.
Show resolved Hide resolved
<param name="applicationClientId">(Optional) Client Application Id to be used for acquiring access token for federated authentication. The driver uses its own application client id by default.</param>
cheenamalhotra marked this conversation as resolved.
Show resolved Hide resolved
<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>
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 @@ -432,7 +432,7 @@ internal static Exception CannotCreateSqlAuthInitializer(string type, Exception
{
return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_CannotCreateAuthInitializer, type), e);
}

cheenamalhotra marked this conversation as resolved.
Show resolved Hide resolved
internal static Exception CannotInitializeAuthProvider(string type, Exception e)
{
return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_CannotInitializeAuthProvider, type), e);
Expand Down
Expand Up @@ -1905,4 +1905,4 @@
<data name="SQL_SettingDeviceFlowWithCredential" xml:space="preserve">
<value>Cannot use 'Authentication=Active Directory Device Code Flow', if the Credential property has been set.</value>
</data>
</root>
</root>
cheenamalhotra marked this conversation as resolved.
Show resolved Hide resolved
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
Expand Up @@ -4575,4 +4575,4 @@
<data name="SQL_SettingDeviceFlowWithCredential" xml:space="preserve">
<value>Cannot use 'Authentication=Active Directory Device Code Flow', if the Credential property has been set.</value>
</data>
</root>
</root>
cheenamalhotra marked this conversation as resolved.
Show resolved Hide resolved
Expand Up @@ -20,13 +20,21 @@ public sealed class ActiveDirectoryAuthenticationProvider : SqlAuthenticationPro
private readonly SqlClientLogger _logger = new SqlClientLogger();
private Func<DeviceCodeResult, Task> _deviceCodeFlowCallback;
private ICustomWebUi _customWebUI = null;
private readonly string _applicationClientId = ActiveDirectoryAuthentication.AdoClientId;

/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/ActiveDirectoryAuthenticationProvider.xml' path='docs/members[@name="ActiveDirectoryAuthenticationProvider"]/ctor/*'/>
public ActiveDirectoryAuthenticationProvider() => new ActiveDirectoryAuthenticationProvider(DefaultDeviceFlowCallback);

/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/ActiveDirectoryAuthenticationProvider.xml' path='docs/members[@name="ActiveDirectoryAuthenticationProvider"]/ctor2/*'/>
public ActiveDirectoryAuthenticationProvider(Func<DeviceCodeResult, Task> deviceCodeFlowCallbackMethod)
public ActiveDirectoryAuthenticationProvider(string applicationClientId) => new ActiveDirectoryAuthenticationProvider(DefaultDeviceFlowCallback, applicationClientId);

/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/ActiveDirectoryAuthenticationProvider.xml' path='docs/members[@name="ActiveDirectoryAuthenticationProvider"]/ctor3/*'/>
public ActiveDirectoryAuthenticationProvider(Func<DeviceCodeResult, Task> deviceCodeFlowCallbackMethod, string applicationClientId = null)
{
if (applicationClientId != null)
{
_applicationClientId = applicationClientId;
}
SetDeviceCodeFlowCallback(deviceCodeFlowCallbackMethod);
}

Expand Down Expand Up @@ -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())
Expand All @@ -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())
Expand All @@ -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())
Expand Down