Skip to content

Commit 0d4c9bb

Browse files
Feature | Azure Active Directory Managed Identity authentication support (#730)
1 parent f49c44d commit 0d4c9bb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1458
-297
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@
8888
|<xref:Microsoft.Data.SqlClient.SqlAuthenticationMethod.ActiveDirectoryInteractive%2A>|
8989
|<xref:Microsoft.Data.SqlClient.SqlAuthenticationMethod.ActiveDirectoryServicePrincipal%2A>|
9090
|<xref:Microsoft.Data.SqlClient.SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow%2A>|
91+
|<xref:Microsoft.Data.SqlClient.SqlAuthenticationMethod.ActiveDirectoryManagedIdentity%2A>|
92+
|<xref:Microsoft.Data.SqlClient.SqlAuthenticationMethod.ActiveDirectoryMSI%2A>|
9193
9294
## Examples
9395
The following example demonstrates providing a custom device flow callback to SqlClient for the Device Code Flow authentication method:

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,13 @@
3333
<summary>The authentication method uses Active Directory Device Code Flow. Use Active Directory Device Code Flow to connect to a SQL Database from devices and operating systems that do not provide a Web browser, using another device to perform interactive authentication.</summary>
3434
<value>6</value>
3535
</ActiveDirectoryDeviceCodeFlow>
36+
<ActiveDirectoryManagedIdentity>
37+
<summary>The authentication method uses Active Directory Managed Identity. 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 object ID of the user identity.</summary>
38+
<value>7</value>
39+
</ActiveDirectoryManagedIdentity>
40+
<ActiveDirectoryMSI>
41+
<summary>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 object ID of the user identity.</summary>
42+
<value>8</value>
43+
</ActiveDirectoryMSI>
3644
</members>
3745
</docs>

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@
1212
<param name="userId">The user login name/ID.</param>
1313
<param name="password">The user password.</param>
1414
<param name="connectionId">The connection ID.</param>
15-
<summary>Initializes a new instance of the <see cref="T:Microsoft.Data.SqlClient.SqlAuthenticationParameters" />
16-
class using the specified authentication method, server name, database name, resource URI, authority URI, user login name/ID, user password and connection ID.</summary>
15+
<summary>Initializes a new instance of the <see cref="T:Microsoft.Data.SqlClient.SqlAuthenticationParameters" /> class using the specified authentication method, server name, database name, resource URI, authority URI, user login name/ID, user password and connection ID.</summary>
1716
</ctor>
1817
<AuthenticationMethod>
1918
<summary>Gets the authentication method.</summary>

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ public enum SqlAuthenticationMethod
9393
ActiveDirectoryServicePrincipal = 5,
9494
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml' path='docs/members[@name="SqlAuthenticationMethod"]/ActiveDirectoryDeviceCodeFlow/*'/>
9595
ActiveDirectoryDeviceCodeFlow = 6,
96+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml' path='docs/members[@name="SqlAuthenticationMethod"]/ActiveDirectoryManagedIdentity/*'/>
97+
ActiveDirectoryManagedIdentity = 7,
98+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml' path='docs/members[@name="SqlAuthenticationMethod"]/ActiveDirectoryMSI/*'/>
99+
ActiveDirectoryMSI = 8,
96100
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml' path='docs/members[@name="SqlAuthenticationMethod"]/NotSpecified/*'/>
97101
NotSpecified = 0,
98102
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml' path='docs/members[@name="SqlAuthenticationMethod"]/SqlPassword/*'/>
@@ -580,12 +584,12 @@ public SqlConnection(string connectionString, Microsoft.Data.SqlClient.SqlCreden
580584
[System.ComponentModel.DesignerSerializationVisibilityAttribute(0)]
581585
public System.Guid ClientConnectionId { get { throw null; } }
582586

583-
///
587+
///
584588
/// for internal test only
585589
///
586590
[System.ComponentModel.DesignerSerializationVisibilityAttribute(0)]
587591
internal string SQLDNSCachingSupportedState { get { throw null; } }
588-
///
592+
///
589593
/// for internal test only
590594
///
591595
[System.ComponentModel.DesignerSerializationVisibilityAttribute(0)]

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@
7878
<Compile Include="..\..\src\Microsoft\Data\SqlClient\ActiveDirectoryAuthenticationProvider.cs">
7979
<Link>Microsoft\Data\SqlClient\ActiveDirectoryAuthenticationProvider.cs</Link>
8080
</Compile>
81+
<Compile Include="..\..\src\Microsoft\Data\SqlClient\AzureManagedIdentityAuthenticationProvider.cs">
82+
<Link>Microsoft\Data\SqlClient\AzureManagedIdentityAuthenticationProvider.cs</Link>
83+
</Compile>
8184
<Compile Include="..\..\src\Microsoft\Data\SqlClient\Server\ExtendedClrTypeCode.cs">
8285
<Link>Microsoft\Data\SqlClient\Server\ExtendedClrTypeCode.cs</Link>
8386
</Compile>

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

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,12 @@ internal static string ConvertToString(object value)
104104
const string ActiveDirectoryInteractiveString = "Active Directory Interactive";
105105
const string ActiveDirectoryServicePrincipalString = "Active Directory Service Principal";
106106
const string ActiveDirectoryDeviceCodeFlowString = "Active Directory Device Code Flow";
107+
internal const string ActiveDirectoryManagedIdentityString = "Active Directory Managed Identity";
108+
internal const string ActiveDirectoryMSIString = "Active Directory MSI";
107109

108110
internal static bool TryConvertToAuthenticationType(string value, out SqlAuthenticationMethod result)
109111
{
110-
Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 7, "SqlAuthenticationMethod enum has changed, update needed");
112+
Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 9, "SqlAuthenticationMethod enum has changed, update needed");
111113

112114
bool isSuccess = false;
113115

@@ -147,6 +149,18 @@ internal static bool TryConvertToAuthenticationType(string value, out SqlAuthent
147149
result = SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow;
148150
isSuccess = true;
149151
}
152+
else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, ActiveDirectoryManagedIdentityString)
153+
|| StringComparer.InvariantCultureIgnoreCase.Equals(value, Convert.ToString(SqlAuthenticationMethod.ActiveDirectoryManagedIdentity, CultureInfo.InvariantCulture)))
154+
{
155+
result = SqlAuthenticationMethod.ActiveDirectoryManagedIdentity;
156+
isSuccess = true;
157+
}
158+
else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, ActiveDirectoryMSIString)
159+
|| StringComparer.InvariantCultureIgnoreCase.Equals(value, Convert.ToString(SqlAuthenticationMethod.ActiveDirectoryMSI, CultureInfo.InvariantCulture)))
160+
{
161+
result = SqlAuthenticationMethod.ActiveDirectoryMSI;
162+
isSuccess = true;
163+
}
150164
else
151165
{
152166
result = DbConnectionStringDefaults.Authentication;
@@ -367,7 +381,7 @@ internal static SqlConnectionAttestationProtocol ConvertToAttestationProtocol(st
367381
}
368382
catch (ArgumentException e)
369383
{
370-
// to be consistent with the messages we send in case of wrong type usage, replace
384+
// to be consistent with the messages we send in case of wrong type usage, replace
371385
// the error with our exception, and keep the original one as inner one for troubleshooting
372386
throw ADP.ConvertFailed(value.GetType(), typeof(SqlConnectionAttestationProtocol), e);
373387
}
@@ -411,7 +425,7 @@ internal static string ApplicationIntentToString(ApplicationIntent value)
411425
/// * if the value is from type ApplicationIntent, it will be used as is
412426
/// * if the value is from integral type (SByte, Int16, Int32, Int64, Byte, UInt16, UInt32, or UInt64), it will be converted to enum
413427
/// * if the value is another enum or any other type, it will be blocked with an appropriate ArgumentException
414-
///
428+
///
415429
/// in any case above, if the converted value is out of valid range, the method raises ArgumentOutOfRangeException.
416430
/// </summary>
417431
/// <returns>application intent value in the valid range</returns>
@@ -467,7 +481,7 @@ internal static ApplicationIntent ConvertToApplicationIntent(string keyword, obj
467481
}
468482
catch (ArgumentException e)
469483
{
470-
// to be consistent with the messages we send in case of wrong type usage, replace
484+
// to be consistent with the messages we send in case of wrong type usage, replace
471485
// the error with our exception, and keep the original one as inner one for troubleshooting
472486
throw ADP.ConvertFailed(value.GetType(), typeof(ApplicationIntent), e);
473487
}
@@ -487,13 +501,15 @@ internal static ApplicationIntent ConvertToApplicationIntent(string keyword, obj
487501

488502
internal static bool IsValidAuthenticationTypeValue(SqlAuthenticationMethod value)
489503
{
490-
Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 7, "SqlAuthenticationMethod enum has changed, update needed");
504+
Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 9, "SqlAuthenticationMethod enum has changed, update needed");
491505
return value == SqlAuthenticationMethod.SqlPassword
492506
|| value == SqlAuthenticationMethod.ActiveDirectoryPassword
493507
|| value == SqlAuthenticationMethod.ActiveDirectoryIntegrated
494508
|| value == SqlAuthenticationMethod.ActiveDirectoryInteractive
495509
|| value == SqlAuthenticationMethod.ActiveDirectoryServicePrincipal
496510
|| value == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow
511+
|| value == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity
512+
|| value == SqlAuthenticationMethod.ActiveDirectoryMSI
497513
|| value == SqlAuthenticationMethod.NotSpecified;
498514
}
499515

@@ -515,6 +531,10 @@ internal static string AuthenticationTypeToString(SqlAuthenticationMethod value)
515531
return ActiveDirectoryServicePrincipalString;
516532
case SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow:
517533
return ActiveDirectoryDeviceCodeFlowString;
534+
case SqlAuthenticationMethod.ActiveDirectoryManagedIdentity:
535+
return ActiveDirectoryManagedIdentityString;
536+
case SqlAuthenticationMethod.ActiveDirectoryMSI:
537+
return ActiveDirectoryMSIString;
518538
default:
519539
return null;
520540
}
@@ -572,7 +592,7 @@ internal static SqlAuthenticationMethod ConvertToAuthenticationType(string keywo
572592
}
573593
catch (ArgumentException e)
574594
{
575-
// to be consistent with the messages we send in case of wrong type usage, replace
595+
// to be consistent with the messages we send in case of wrong type usage, replace
576596
// the error with our exception, and keep the original one as inner one for troubleshooting
577597
throw ADP.ConvertFailed(value.GetType(), typeof(SqlAuthenticationMethod), e);
578598
}
@@ -648,7 +668,7 @@ internal static SqlConnectionColumnEncryptionSetting ConvertToColumnEncryptionSe
648668
}
649669
catch (ArgumentException e)
650670
{
651-
// to be consistent with the messages we send in case of wrong type usage, replace
671+
// to be consistent with the messages we send in case of wrong type usage, replace
652672
// the error with our exception, and keep the original one as inner one for troubleshooting
653673
throw ADP.ConvertFailed(value.GetType(), typeof(SqlConnectionColumnEncryptionSetting), e);
654674
}
@@ -672,40 +692,41 @@ internal static partial class DbConnectionStringDefaults
672692
// all
673693
// internal const string NamedConnection = "";
674694

695+
private const string _emptyString = "";
675696
// SqlClient
676697
internal const ApplicationIntent ApplicationIntent = Microsoft.Data.SqlClient.ApplicationIntent.ReadWrite;
677698
internal const string ApplicationName = "Core Microsoft SqlClient Data Provider";
678-
internal const string AttachDBFilename = "";
699+
internal const string AttachDBFilename = _emptyString;
679700
internal const int CommandTimeout = 30;
680701
internal const int ConnectTimeout = 15;
681-
internal const string CurrentLanguage = "";
682-
internal const string DataSource = "";
702+
internal const string CurrentLanguage = _emptyString;
703+
internal const string DataSource = _emptyString;
683704
internal const bool Encrypt = false;
684705
internal const bool Enlist = true;
685-
internal const string FailoverPartner = "";
686-
internal const string InitialCatalog = "";
706+
internal const string FailoverPartner = _emptyString;
707+
internal const string InitialCatalog = _emptyString;
687708
internal const bool IntegratedSecurity = false;
688709
internal const int LoadBalanceTimeout = 0; // default of 0 means don't use
689710
internal const bool MultipleActiveResultSets = false;
690711
internal const bool MultiSubnetFailover = false;
691712
internal const int MaxPoolSize = 100;
692713
internal const int MinPoolSize = 0;
693714
internal const int PacketSize = 8000;
694-
internal const string Password = "";
715+
internal const string Password = _emptyString;
695716
internal const bool PersistSecurityInfo = false;
696717
internal const bool Pooling = true;
697718
internal const bool TrustServerCertificate = false;
698719
internal const string TypeSystemVersion = "Latest";
699-
internal const string UserID = "";
720+
internal const string UserID = _emptyString;
700721
internal const bool UserInstance = false;
701722
internal const bool Replication = false;
702-
internal const string WorkstationID = "";
723+
internal const string WorkstationID = _emptyString;
703724
internal const string TransactionBinding = "Implicit Unbind";
704725
internal const int ConnectRetryCount = 1;
705726
internal const int ConnectRetryInterval = 10;
706727
internal static readonly SqlAuthenticationMethod Authentication = SqlAuthenticationMethod.NotSpecified;
707728
internal const SqlConnectionColumnEncryptionSetting ColumnEncryptionSetting = SqlConnectionColumnEncryptionSetting.Disabled;
708-
internal const string EnclaveAttestationUrl = "";
729+
internal const string EnclaveAttestationUrl = _emptyString;
709730
internal const SqlConnectionAttestationProtocol AttestationProtocol = SqlConnectionAttestationProtocol.NotSpecified;
710731
}
711732

@@ -813,7 +834,7 @@ internal static class DbConnectionStringSynonyms
813834

814835
//internal const string MultiSubnetFailover = MULTISUBNETFAILOVER;
815836
internal const string MULTISUBNETFAILOVER = "MultiSubnetFailover";
816-
837+
817838
//internal const string NetworkLibrary = NET+","+NETWORK;
818839
internal const string NET = "net";
819840
internal const string NETWORK = "network";

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ internal partial class SqlAuthenticationProviderManager
1515

1616
static SqlAuthenticationProviderManager()
1717
{
18+
var azureManagedIdentityAuthenticationProvider = new AzureManagedIdentityAuthenticationProvider();
1819
SqlAuthenticationProviderConfigurationSection configurationSection = null;
1920

2021
try
@@ -40,6 +41,8 @@ static SqlAuthenticationProviderManager()
4041
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryInteractive, activeDirectoryAuthProvider);
4142
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryServicePrincipal, activeDirectoryAuthProvider);
4243
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow, activeDirectoryAuthProvider);
44+
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryManagedIdentity, azureManagedIdentityAuthenticationProvider);
45+
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryMSI, azureManagedIdentityAuthenticationProvider);
4346
}
4447

4548
/// <summary>
@@ -153,6 +156,10 @@ private static SqlAuthenticationMethod AuthenticationEnumFromString(string authe
153156
return SqlAuthenticationMethod.ActiveDirectoryServicePrincipal;
154157
case ActiveDirectoryDeviceCodeFlow:
155158
return SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow;
159+
case ActiveDirectoryManagedIdentity:
160+
return SqlAuthenticationMethod.ActiveDirectoryManagedIdentity;
161+
case ActiveDirectoryMSI:
162+
return SqlAuthenticationMethod.ActiveDirectoryMSI;
156163
default:
157164
throw SQL.UnsupportedAuthentication(authentication);
158165
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,17 @@ internal partial class SqlAuthenticationProviderManager
88
{
99
static SqlAuthenticationProviderManager()
1010
{
11+
var azureManagedIdentityAuthenticationProvider = new AzureManagedIdentityAuthenticationProvider();
12+
1113
Instance = new SqlAuthenticationProviderManager();
1214
var activeDirectoryAuthProvider = new ActiveDirectoryAuthenticationProvider(Instance._applicationClientId);
1315
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, activeDirectoryAuthProvider);
1416
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryIntegrated, activeDirectoryAuthProvider);
1517
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryInteractive, activeDirectoryAuthProvider);
1618
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryServicePrincipal, activeDirectoryAuthProvider);
1719
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow, activeDirectoryAuthProvider);
20+
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryManagedIdentity, azureManagedIdentityAuthenticationProvider);
21+
Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryMSI, azureManagedIdentityAuthenticationProvider);
1822
}
1923
}
2024
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ internal partial class SqlAuthenticationProviderManager
1919
private const string ActiveDirectoryInteractive = "active directory interactive";
2020
private const string ActiveDirectoryServicePrincipal = "active directory service principal";
2121
private const string ActiveDirectoryDeviceCodeFlow = "active directory device code flow";
22+
private const string ActiveDirectoryManagedIdentity = "active directory managed identity";
23+
private const string ActiveDirectoryMSI = "active directory msi";
2224

2325
private readonly string _typeName;
2426
private readonly IReadOnlyCollection<SqlAuthenticationMethod> _authenticationsWithAppSpecifiedProvider;

0 commit comments

Comments
 (0)