Skip to content

Commit 4316474

Browse files
Feature | Introduce "Active Directory Default" authentication mode (#1043)
1 parent 5e067c4 commit 4316474

38 files changed

+372
-106
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,9 @@
4141
<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 "client ID" of the user identity.</summary>
4242
<value>8</value>
4343
</ActiveDirectoryMSI>
44+
<ActiveDirectoryDefault>
45+
<summary>The authentication method uses Active Directory Default. Use this mode to connect to a SQL Database using multiple non-interactive authentication methods tried sequentially to acquire an access token. This method does not fallback to the "Active Directory Interactive" authentication method.</summary>
46+
<value>9</value>
47+
</ActiveDirectoryDefault>
4448
</members>
4549
</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
@@ -99,6 +99,8 @@ public enum SqlAuthenticationMethod
9999
ActiveDirectoryManagedIdentity = 7,
100100
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml' path='docs/members[@name="SqlAuthenticationMethod"]/ActiveDirectoryMSI/*'/>
101101
ActiveDirectoryMSI = 8,
102+
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml' path='docs/members[@name="SqlAuthenticationMethod"]/ActiveDirectoryDefault/*'/>
103+
ActiveDirectoryDefault = 9,
102104
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml' path='docs/members[@name="SqlAuthenticationMethod"]/NotSpecified/*'/>
103105
NotSpecified = 0,
104106
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml' path='docs/members[@name="SqlAuthenticationMethod"]/SqlPassword/*'/>

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

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ internal static string ConvertToString(object value)
9999

100100
private const string ApplicationIntentReadWriteString = "ReadWrite";
101101
private const string ApplicationIntentReadOnlyString = "ReadOnly";
102+
102103
const string SqlPasswordString = "Sql Password";
103104
const string ActiveDirectoryPasswordString = "Active Directory Password";
104105
const string ActiveDirectoryIntegratedString = "Active Directory Integrated";
@@ -107,13 +108,48 @@ internal static string ConvertToString(object value)
107108
const string ActiveDirectoryDeviceCodeFlowString = "Active Directory Device Code Flow";
108109
internal const string ActiveDirectoryManagedIdentityString = "Active Directory Managed Identity";
109110
internal const string ActiveDirectoryMSIString = "Active Directory MSI";
111+
internal const string ActiveDirectoryDefaultString = "Active Directory Default";
110112

111-
internal static bool TryConvertToAuthenticationType(string value, out SqlAuthenticationMethod result)
113+
#if DEBUG
114+
private static string[] s_supportedAuthenticationModes =
115+
{
116+
"NotSpecified",
117+
"SqlPassword",
118+
"ActiveDirectoryPassword",
119+
"ActiveDirectoryIntegrated",
120+
"ActiveDirectoryInteractive",
121+
"ActiveDirectoryServicePrincipal",
122+
"ActiveDirectoryDeviceCodeFlow",
123+
"ActiveDirectoryManagedIdentity",
124+
"ActiveDirectoryMSI",
125+
"ActiveDirectoryDefault"
126+
};
127+
128+
private static bool IsValidAuthenticationMethodEnum()
112129
{
113-
Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 9, "SqlAuthenticationMethod enum has changed, update needed");
130+
string[] names = Enum.GetNames(typeof(SqlAuthenticationMethod));
131+
int l = s_supportedAuthenticationModes.Length;
132+
bool listValid;
133+
if (listValid = names.Length == l)
134+
{
135+
for (int i = 0; i < l; i++)
136+
{
137+
if (s_supportedAuthenticationModes[i].CompareTo(names[i]) != 0)
138+
{
139+
listValid = false;
140+
}
141+
}
142+
}
143+
return listValid;
144+
}
145+
#endif
114146

147+
internal static bool TryConvertToAuthenticationType(string value, out SqlAuthenticationMethod result)
148+
{
149+
#if DEBUG
150+
Debug.Assert(IsValidAuthenticationMethodEnum(), "SqlAuthenticationMethod enum has changed, update needed");
151+
#endif
115152
bool isSuccess = false;
116-
117153
if (StringComparer.InvariantCultureIgnoreCase.Equals(value, SqlPasswordString)
118154
|| StringComparer.InvariantCultureIgnoreCase.Equals(value, Convert.ToString(SqlAuthenticationMethod.SqlPassword, CultureInfo.InvariantCulture)))
119155
{
@@ -162,6 +198,12 @@ internal static bool TryConvertToAuthenticationType(string value, out SqlAuthent
162198
result = SqlAuthenticationMethod.ActiveDirectoryMSI;
163199
isSuccess = true;
164200
}
201+
else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, ActiveDirectoryDefaultString)
202+
|| StringComparer.InvariantCultureIgnoreCase.Equals(value, Convert.ToString(SqlAuthenticationMethod.ActiveDirectoryDefault, CultureInfo.InvariantCulture)))
203+
{
204+
result = SqlAuthenticationMethod.ActiveDirectoryDefault;
205+
isSuccess = true;
206+
}
165207
else
166208
{
167209
result = DbConnectionStringDefaults.Authentication;
@@ -606,7 +648,7 @@ internal static ApplicationIntent ConvertToApplicationIntent(string keyword, obj
606648

607649
internal static bool IsValidAuthenticationTypeValue(SqlAuthenticationMethod value)
608650
{
609-
Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 9, "SqlAuthenticationMethod enum has changed, update needed");
651+
Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 10, "SqlAuthenticationMethod enum has changed, update needed");
610652
return value == SqlAuthenticationMethod.SqlPassword
611653
|| value == SqlAuthenticationMethod.ActiveDirectoryPassword
612654
|| value == SqlAuthenticationMethod.ActiveDirectoryIntegrated
@@ -615,6 +657,7 @@ internal static bool IsValidAuthenticationTypeValue(SqlAuthenticationMethod valu
615657
|| value == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow
616658
|| value == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity
617659
|| value == SqlAuthenticationMethod.ActiveDirectoryMSI
660+
|| value == SqlAuthenticationMethod.ActiveDirectoryDefault
618661
|| value == SqlAuthenticationMethod.NotSpecified;
619662
}
620663

@@ -640,6 +683,8 @@ internal static string AuthenticationTypeToString(SqlAuthenticationMethod value)
640683
return ActiveDirectoryManagedIdentityString;
641684
case SqlAuthenticationMethod.ActiveDirectoryMSI:
642685
return ActiveDirectoryMSIString;
686+
case SqlAuthenticationMethod.ActiveDirectoryDefault:
687+
return ActiveDirectoryDefaultString;
643688
default:
644689
return null;
645690
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ private static SqlAuthenticationMethod AuthenticationEnumFromString(string authe
152152
return SqlAuthenticationMethod.ActiveDirectoryManagedIdentity;
153153
case ActiveDirectoryMSI:
154154
return SqlAuthenticationMethod.ActiveDirectoryMSI;
155+
case ActiveDirectoryDefault:
156+
return SqlAuthenticationMethod.ActiveDirectoryDefault;
155157
default:
156158
throw SQL.UnsupportedAuthentication(authentication);
157159
}

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
@@ -21,6 +21,7 @@ internal partial class SqlAuthenticationProviderManager
2121
private const string ActiveDirectoryDeviceCodeFlow = "active directory device code flow";
2222
private const string ActiveDirectoryManagedIdentity = "active directory managed identity";
2323
private const string ActiveDirectoryMSI = "active directory msi";
24+
private const string ActiveDirectoryDefault = "active directory default";
2425

2526
private readonly string _typeName;
2627
private readonly IReadOnlyCollection<SqlAuthenticationMethod> _authenticationsWithAppSpecifiedProvider;
@@ -46,6 +47,7 @@ private static void SetDefaultAuthProviders(SqlAuthenticationProviderManager ins
4647
instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow, activeDirectoryAuthProvider);
4748
instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryManagedIdentity, activeDirectoryAuthProvider);
4849
instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryMSI, activeDirectoryAuthProvider);
50+
instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDefault, activeDirectoryAuthProvider);
4951
}
5052
}
5153
/// <summary>

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

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -193,11 +193,15 @@ public SqlConnection(string connectionString, SqlCredential credential) : this()
193193
}
194194
else if (UsesActiveDirectoryManagedIdentity(connectionOptions))
195195
{
196-
throw SQL.SettingCredentialWithManagedIdentityArgument(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString);
196+
throw SQL.SettingCredentialWithNonInteractiveArgument(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString);
197197
}
198198
else if (UsesActiveDirectoryMSI(connectionOptions))
199199
{
200-
throw SQL.SettingCredentialWithManagedIdentityArgument(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString);
200+
throw SQL.SettingCredentialWithNonInteractiveArgument(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString);
201+
}
202+
else if (UsesActiveDirectoryDefault(connectionOptions))
203+
{
204+
throw SQL.SettingCredentialWithNonInteractiveArgument(DbConnectionStringBuilderUtil.ActiveDirectoryDefaultString);
201205
}
202206

203207
Credential = credential;
@@ -508,6 +512,11 @@ private bool UsesActiveDirectoryMSI(SqlConnectionString opt)
508512
return opt != null && opt.Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI;
509513
}
510514

515+
private bool UsesActiveDirectoryDefault(SqlConnectionString opt)
516+
{
517+
return opt != null && opt.Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault;
518+
}
519+
511520
private bool UsesAuthentication(SqlConnectionString opt)
512521
{
513522
return opt != null && opt.Authentication != SqlAuthenticationMethod.NotSpecified;
@@ -565,7 +574,7 @@ public override string ConnectionString
565574
if (_credential != null)
566575
{
567576
// Check for Credential being used with Authentication=ActiveDirectoryIntegrated | ActiveDirectoryInteractive |
568-
// ActiveDirectoryDeviceCodeFlow | ActiveDirectoryManagedIdentity/ActiveDirectoryMSI. Since a different error string is used
577+
// ActiveDirectoryDeviceCodeFlow | ActiveDirectoryManagedIdentity/ActiveDirectoryMSI | ActiveDirectoryDefault. Since a different error string is used
569578
// for this case in ConnectionString setter vs in Credential setter, check for this error case before calling
570579
// CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential, which is common to both setters.
571580
if (UsesActiveDirectoryIntegrated(connectionOptions))
@@ -582,11 +591,15 @@ public override string ConnectionString
582591
}
583592
else if (UsesActiveDirectoryManagedIdentity(connectionOptions))
584593
{
585-
throw SQL.SettingManagedIdentityWithCredential(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString);
594+
throw SQL.SettingNonInteractiveWithCredential(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString);
586595
}
587596
else if (UsesActiveDirectoryMSI(connectionOptions))
588597
{
589-
throw SQL.SettingManagedIdentityWithCredential(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString);
598+
throw SQL.SettingNonInteractiveWithCredential(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString);
599+
}
600+
else if (UsesActiveDirectoryDefault(connectionOptions))
601+
{
602+
throw SQL.SettingNonInteractiveWithCredential(DbConnectionStringBuilderUtil.ActiveDirectoryDefaultString);
590603
}
591604

592605
CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential(connectionOptions);
@@ -878,7 +891,7 @@ public SqlCredential Credential
878891
{
879892
var connectionOptions = (SqlConnectionString)ConnectionOptions;
880893
// Check for Credential being used with Authentication=ActiveDirectoryIntegrated | ActiveDirectoryInteractive |
881-
// ActiveDirectoryDeviceCodeFlow | ActiveDirectoryManagedIdentity/ActiveDirectoryMSI. Since a different error string is used
894+
// ActiveDirectoryDeviceCodeFlow | ActiveDirectoryManagedIdentity/ActiveDirectoryMSI | ActiveDirectoryDefault. Since a different error string is used
882895
// for this case in ConnectionString setter vs in Credential setter, check for this error case before calling
883896
// CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential, which is common to both setters.
884897
if (UsesActiveDirectoryIntegrated(connectionOptions))
@@ -895,11 +908,15 @@ public SqlCredential Credential
895908
}
896909
else if (UsesActiveDirectoryManagedIdentity(connectionOptions))
897910
{
898-
throw SQL.SettingCredentialWithManagedIdentityInvalid(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString);
911+
throw SQL.SettingCredentialWithNonInteractiveInvalid(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString);
899912
}
900913
else if (UsesActiveDirectoryMSI(connectionOptions))
901914
{
902-
throw SQL.SettingCredentialWithManagedIdentityInvalid(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString);
915+
throw SQL.SettingCredentialWithNonInteractiveInvalid(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString);
916+
}
917+
else if (UsesActiveDirectoryDefault(connectionOptions))
918+
{
919+
throw SQL.SettingCredentialWithNonInteractiveInvalid(DbConnectionStringBuilderUtil.ActiveDirectoryDefaultString);
903920
}
904921

905922
CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential(connectionOptions);

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -484,12 +484,17 @@ internal SqlConnectionString(string connectionString) : base(connectionString, G
484484

485485
if (Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity && HasPasswordKeyword)
486486
{
487-
throw SQL.ManagedIdentityWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString);
487+
throw SQL.NonInteractiveWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString);
488488
}
489489

490490
if (Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI && HasPasswordKeyword)
491491
{
492-
throw SQL.ManagedIdentityWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString);
492+
throw SQL.NonInteractiveWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString);
493+
}
494+
495+
if (Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault && HasPasswordKeyword)
496+
{
497+
throw SQL.NonInteractiveWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryDefaultString);
493498
}
494499
}
495500

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1308,6 +1308,7 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword,
13081308
|| ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryServicePrincipal
13091309
|| ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity
13101310
|| ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI
1311+
|| ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault
13111312
// Since AD Integrated may be acting like Windows integrated, additionally check _fedAuthRequired
13121313
|| (ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated && _fedAuthRequired))
13131314
{
@@ -2116,6 +2117,7 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo)
21162117
|| ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow
21172118
|| ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity
21182119
|| ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI
2120+
|| ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault
21192121
|| (ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated && _fedAuthRequired),
21202122
"Credentials aren't provided for calling MSAL");
21212123
Debug.Assert(fedAuthInfo != null, "info should not be null.");
@@ -2358,6 +2360,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo)
23582360
case SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow:
23592361
case SqlAuthenticationMethod.ActiveDirectoryManagedIdentity:
23602362
case SqlAuthenticationMethod.ActiveDirectoryMSI:
2363+
case SqlAuthenticationMethod.ActiveDirectoryDefault:
23612364
if (_activeDirectoryAuthTimeoutRetryHelper.State == ActiveDirectoryAuthenticationTimeoutRetryState.Retrying)
23622365
{
23632366
_fedAuthToken = _activeDirectoryAuthTimeoutRetryHelper.CachedToken;

0 commit comments

Comments
 (0)