Skip to content

Commit 2b40206

Browse files
author
David Engel
authored
Additional AAD authentication options (#560)
1 parent c2eb0c0 commit 2b40206

38 files changed

+787
-293
lines changed

BUILDGUIDE.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ Manual Tests require the below setup to run:
108108
|NPConnectionString | Connection String for a Named Pipes enabled SQL Server instance.| `Server=\\{servername}\pipe\sql\query;Database={Database_Name};Trusted_Connection=True;` <br/> OR <br/> `Data Source=np:{servername};Initial Catalog={Database_Name};Integrated Security=True;`|
109109
|AADAuthorityURL | (Optional) Identifies the OAuth2 authority resource for `Server` specified in `AADPasswordConnectionString` | `https://login.windows.net/<tenant>`, where `<tenant>` is the tenant ID of the Azure Active Directory (Azure AD) tenant |
110110
|AADPasswordConnectionString | (Optional) Connection String for testing Azure Active Directory Password Authentication. | `Data Source={server.database.windows.net}; Initial Catalog={Azure_DB_Name};Authentication=Active Directory Password; User ID={AAD_User}; Password={AAD_User_Password};`|
111+
|AADSecurePrincipalId | (Optional) The Application Id of a registered application which has been granted permission to the database defined in the AADPasswordConnectionString. | {Application ID} |
112+
|AADSecurePrincipalSecret | (Optional) A Secret defined for a registered application which has been granted permission to the database defined in the AADPasswordConnectionString. | {Secret} |
111113
|AzureKeyVaultURL | (Optional) Azure Key Vault Identifier URL | `https://{keyvaultname}.vault.azure.net/` |
112114
|AzureKeyVaultClientId | (Optional) "Application (client) ID" of an Active Directory registered application, granted access to the Azure Key Vault specified in `AZURE_KEY_VAULT_URL`. Requires the key permissions Get, List, Import, Decrypt, Encrypt, Unwrap, Wrap, Verify, and Sign. | _{Client Application ID}_ |
113115
|AzureKeyVaultClientSecret | (Optional) "Client Secret" of the Active Directory registered application, granted access to the Azure Key Vault specified in `AZURE_KEY_VAULT_URL` | _{Client Application Secret}_ |

doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,16 @@
1818
<value>2</value>
1919
</ActiveDirectoryPassword>
2020
<ActiveDirectoryIntegrated>
21-
<summary>The authentication method uses Active Directory Integrated. Use Active Directory Integrated to connect to a SQL Database using integrated Windows authentication. Available for .NET Framework applications only.</summary>
21+
<summary>The authentication method uses Active Directory Integrated. Use Active Directory Integrated to connect to a SQL Database using integrated Windows authentication.</summary>
2222
<value>3</value>
2323
</ActiveDirectoryIntegrated>
2424
<ActiveDirectoryInteractive>
25-
<summary>The authentication method uses Active Directory Interactive. Available since the .NET Framework 4.7.2 and for .NET Framework applications only.</summary>
25+
<summary>The authentication method uses Active Directory Interactive. Use Active Directory Interactive to connect to a SQL Database with an interactive authentication flow.</summary>
2626
<value>4</value>
2727
</ActiveDirectoryInteractive>
28+
<ActiveDirectoryServicePrincipal>
29+
<summary>The authentication method uses Active Directory Service Principal. Use Active Directory Service Principal to connect to a SQL Database using the client ID and secret of a service principal identity.</summary>
30+
<value>5</value>
31+
</ActiveDirectoryServicePrincipal>
2832
</members>
2933
</docs>

src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ public enum SqlAuthenticationMethod
6767
ActiveDirectoryInteractive = 4,
6868
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml' path='docs/members[@name="SqlAuthenticationMethod"]/ActiveDirectoryPassword/*'/>
6969
ActiveDirectoryPassword = 2,
70+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml' path='docs/members[@name="SqlAuthenticationMethod"]/ActiveDirectoryServicePrincipal/*'/>
71+
ActiveDirectoryServicePrincipal = 5,
7072
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml' path='docs/members[@name="SqlAuthenticationMethod"]/NotSpecified/*'/>
7173
NotSpecified = 0,
7274
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml' path='docs/members[@name="SqlAuthenticationMethod"]/SqlPassword/*'/>

src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,12 @@ protected internal void DoomThisConnection()
259259
SqlClientEventSource.Log.PoolerTraceEvent("<prov.DbConnectionInternal.DoomThisConnection|RES|INFO|CPOOL> {0}, Dooming", ObjectID);
260260
}
261261

262+
// Reset connection doomed status so it can be re-connected and pooled.
263+
protected internal void UnDoomThisConnection()
264+
{
265+
_connectionIsDoomed = false;
266+
}
267+
262268
protected internal virtual DataTable GetSchema(DbConnectionFactory factory, DbConnectionPoolGroup poolGroup, DbConnection outerConnection, string collectionName, string[] restrictions)
263269
{
264270
Debug.Assert(outerConnection != null, "outerConnection may not be null.");

src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/TimeoutTimer.cs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
using Microsoft.Data.Common;
66
using System;
7-
using System.Data.Common;
87
using System.Diagnostics;
98

109
namespace Microsoft.Data.ProviderBase
@@ -24,6 +23,7 @@ internal class TimeoutTimer
2423
//-------------------
2524
private long _timerExpire;
2625
private bool _isInfiniteTimeout;
26+
private long _originalTimerTicks;
2727

2828
//-------------------
2929
// Timeout-setting methods
@@ -59,7 +59,8 @@ internal static TimeoutTimer StartMillisecondsTimeout(long milliseconds)
5959
//--------------------
6060
// Method body
6161
var timeout = new TimeoutTimer();
62-
timeout._timerExpire = checked(ADP.TimerCurrent() + (milliseconds * TimeSpan.TicksPerMillisecond));
62+
timeout._originalTimerTicks = milliseconds * TimeSpan.TicksPerMillisecond;
63+
timeout._timerExpire = checked(ADP.TimerCurrent() + timeout._originalTimerTicks);
6364
timeout._isInfiniteTimeout = false;
6465

6566
//---------------------
@@ -88,13 +89,29 @@ internal void SetTimeoutSeconds(int seconds)
8889
else
8990
{
9091
// Stash current time + timeout
91-
_timerExpire = checked(ADP.TimerCurrent() + ADP.TimerFromSeconds(seconds));
92+
_originalTimerTicks = ADP.TimerFromSeconds(seconds);
93+
_timerExpire = checked(ADP.TimerCurrent() + _originalTimerTicks);
9294
_isInfiniteTimeout = false;
9395
}
96+
9497
//---------------------
9598
// Postconditions:None
9699
}
97100

101+
// Reset timer to original duration.
102+
internal void Reset()
103+
{
104+
if (InfiniteTimeout == _originalTimerTicks)
105+
{
106+
_isInfiniteTimeout = true;
107+
}
108+
else
109+
{
110+
_timerExpire = checked(ADP.TimerCurrent() + _originalTimerTicks);
111+
_isInfiniteTimeout = false;
112+
}
113+
}
114+
98115
//-------------------
99116
// Timeout info properties
100117
//-------------------

src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@
6060
<Compile Include="..\..\src\Microsoft\Data\SqlClient\ActiveDirectoryAuthenticationTimeoutRetryHelper.cs">
6161
<Link>Microsoft\Data\SqlClient\ActiveDirectoryAuthenticationTimeoutRetryHelper.cs</Link>
6262
</Compile>
63+
<Compile Include="..\..\src\Microsoft\Data\SqlClient\ActiveDirectoryAuthenticationProvider.cs">
64+
<Link>Microsoft\Data\SqlClient\ActiveDirectoryAuthenticationProvider.cs</Link>
65+
</Compile>
6366
<Compile Include="..\..\src\Microsoft\Data\SqlClient\Server\IBinarySerialize.cs">
6467
<Link>Microsoft\Data\SqlClient\Server\IBinarySerialize.cs</Link>
6568
</Compile>
@@ -253,7 +256,6 @@
253256
<Compile Include="Microsoft\Data\ProviderBase\DbConnectionPool.cs" />
254257
<Compile Include="Microsoft\Data\ProviderBase\DbConnectionPoolIdentity.cs" />
255258
<Compile Include="Microsoft\Data\ProviderBase\DbConnectionPoolOptions.cs" />
256-
<Compile Include="Microsoft\Data\SqlClient\ActiveDirectoryNativeAuthenticationProvider.cs" />
257259
<Compile Include="Microsoft\Data\SqlClient\SqlAuthenticationProviderManager.cs" />
258260
<Compile Include="Microsoft\Data\SqlClient\SqlAuthenticationToken.cs" />
259261
<Compile Include="Microsoft\Data\SqlClient\Server\InvalidUdtException.cs" />

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,13 @@ internal static string ConvertToString(object value)
100100
private const string ApplicationIntentReadOnlyString = "ReadOnly";
101101
const string SqlPasswordString = "Sql Password";
102102
const string ActiveDirectoryPasswordString = "Active Directory Password";
103+
const string ActiveDirectoryIntegratedString = "Active Directory Integrated";
104+
const string ActiveDirectoryInteractiveString = "Active Directory Interactive";
105+
const string ActiveDirectoryServicePrincipalString = "Active Directory Service Principal";
103106

104107
internal static bool TryConvertToAuthenticationType(string value, out SqlAuthenticationMethod result)
105108
{
106-
Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 5, "SqlAuthenticationMethod enum has changed, update needed");
109+
Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 6, "SqlAuthenticationMethod enum has changed, update needed");
107110

108111
bool isSuccess = false;
109112

@@ -119,6 +122,24 @@ internal static bool TryConvertToAuthenticationType(string value, out SqlAuthent
119122
result = SqlAuthenticationMethod.ActiveDirectoryPassword;
120123
isSuccess = true;
121124
}
125+
else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, ActiveDirectoryIntegratedString)
126+
|| StringComparer.InvariantCultureIgnoreCase.Equals(value, Convert.ToString(SqlAuthenticationMethod.ActiveDirectoryIntegrated, CultureInfo.InvariantCulture)))
127+
{
128+
result = SqlAuthenticationMethod.ActiveDirectoryIntegrated;
129+
isSuccess = true;
130+
}
131+
else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, ActiveDirectoryInteractiveString)
132+
|| StringComparer.InvariantCultureIgnoreCase.Equals(value, Convert.ToString(SqlAuthenticationMethod.ActiveDirectoryInteractive, CultureInfo.InvariantCulture)))
133+
{
134+
result = SqlAuthenticationMethod.ActiveDirectoryInteractive;
135+
isSuccess = true;
136+
}
137+
else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, ActiveDirectoryServicePrincipalString)
138+
|| StringComparer.InvariantCultureIgnoreCase.Equals(value, Convert.ToString(SqlAuthenticationMethod.ActiveDirectoryServicePrincipal, CultureInfo.InvariantCulture)))
139+
{
140+
result = SqlAuthenticationMethod.ActiveDirectoryServicePrincipal;
141+
isSuccess = true;
142+
}
122143
else
123144
{
124145
result = DbConnectionStringDefaults.Authentication;
@@ -459,11 +480,12 @@ internal static ApplicationIntent ConvertToApplicationIntent(string keyword, obj
459480

460481
internal static bool IsValidAuthenticationTypeValue(SqlAuthenticationMethod value)
461482
{
462-
Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 5, "SqlAuthenticationMethod enum has changed, update needed");
483+
Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 6, "SqlAuthenticationMethod enum has changed, update needed");
463484
return value == SqlAuthenticationMethod.SqlPassword
464485
|| value == SqlAuthenticationMethod.ActiveDirectoryPassword
465486
|| value == SqlAuthenticationMethod.ActiveDirectoryIntegrated
466487
|| value == SqlAuthenticationMethod.ActiveDirectoryInteractive
488+
|| value == SqlAuthenticationMethod.ActiveDirectoryServicePrincipal
467489
|| value == SqlAuthenticationMethod.NotSpecified;
468490
}
469491

@@ -477,6 +499,12 @@ internal static string AuthenticationTypeToString(SqlAuthenticationMethod value)
477499
return SqlPasswordString;
478500
case SqlAuthenticationMethod.ActiveDirectoryPassword:
479501
return ActiveDirectoryPasswordString;
502+
case SqlAuthenticationMethod.ActiveDirectoryIntegrated:
503+
return ActiveDirectoryIntegratedString;
504+
case SqlAuthenticationMethod.ActiveDirectoryInteractive:
505+
return ActiveDirectoryInteractiveString;
506+
case SqlAuthenticationMethod.ActiveDirectoryServicePrincipal:
507+
return ActiveDirectoryServicePrincipalString;
480508
default:
481509
return null;
482510
}

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/ActiveDirectoryNativeAuthenticationProvider.cs

Lines changed: 0 additions & 67 deletions
This file was deleted.

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetCoreApp.cs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,24 @@ internal partial class SqlAuthenticationProviderManager
1515

1616
static SqlAuthenticationProviderManager()
1717
{
18-
var activeDirectoryAuthNativeProvider = new ActiveDirectoryNativeAuthenticationProvider();
18+
var activeDirectoryAuthProvider = new ActiveDirectoryAuthenticationProvider();
1919
SqlAuthenticationProviderConfigurationSection configurationSection = null;
2020

2121
try
2222
{
2323
configurationSection = (SqlAuthenticationProviderConfigurationSection)ConfigurationManager.GetSection(SqlAuthenticationProviderConfigurationSection.Name);
2424
}
25-
catch (ConfigurationErrorsException)
25+
catch (ConfigurationErrorsException e)
2626
{
2727
// Don't throw an error for invalid config files
28+
SqlClientEventSource.Log.TraceEvent("Unable to load custom SqlAuthenticationProviders. ConfigurationManager failed to load due to configuration errors: {0}", e);
2829
}
2930

3031
Instance = new SqlAuthenticationProviderManager(configurationSection);
31-
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, activeDirectoryAuthNativeProvider);
32+
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryIntegrated, activeDirectoryAuthProvider);
33+
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, activeDirectoryAuthProvider);
34+
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryInteractive, activeDirectoryAuthProvider);
35+
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryServicePrincipal, activeDirectoryAuthProvider);
3236
}
3337

3438
/// <summary>
@@ -104,8 +108,14 @@ private static SqlAuthenticationMethod AuthenticationEnumFromString(string authe
104108
{
105109
switch (authentication.ToLowerInvariant())
106110
{
111+
case ActiveDirectoryIntegrated:
112+
return SqlAuthenticationMethod.ActiveDirectoryIntegrated;
107113
case ActiveDirectoryPassword:
108114
return SqlAuthenticationMethod.ActiveDirectoryPassword;
115+
case ActiveDirectoryInteractive:
116+
return SqlAuthenticationMethod.ActiveDirectoryInteractive;
117+
case ActiveDirectoryServicePrincipal:
118+
return SqlAuthenticationMethod.ActiveDirectoryServicePrincipal;
109119
default:
110120
throw SQL.UnsupportedAuthentication(authentication);
111121
}

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetStandard.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@ internal partial class SqlAuthenticationProviderManager
88
{
99
static SqlAuthenticationProviderManager()
1010
{
11-
var activeDirectoryAuthNativeProvider = new ActiveDirectoryNativeAuthenticationProvider();
11+
var activeDirectoryAuthProvider = new ActiveDirectoryAuthenticationProvider();
1212
Instance = new SqlAuthenticationProviderManager();
13-
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, activeDirectoryAuthNativeProvider);
13+
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, activeDirectoryAuthProvider);
14+
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryIntegrated, activeDirectoryAuthProvider);
15+
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryInteractive, activeDirectoryAuthProvider);
16+
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryServicePrincipal, activeDirectoryAuthProvider);
1417
}
1518
}
1619
}

0 commit comments

Comments
 (0)