Skip to content

Commit ea6e14c

Browse files
[Hotfix 4.1.1] | Handle NRE on Azure federated authentication (#1625) (#1710)
1 parent 9e4bdaa commit ea6e14c

File tree

19 files changed

+121
-41
lines changed

19 files changed

+121
-41
lines changed

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

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ public void AssertUnrecoverableStateCountIsCorrect()
103103

104104
internal sealed class SqlInternalConnectionTds : SqlInternalConnection, IDisposable
105105
{
106+
// https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/retry-after#simple-retry-for-errors-with-http-error-codes-500-600
107+
internal const int MsalHttpRetryStatusCode = 429;
108+
106109
// CONNECTION AND STATE VARIABLES
107110
private readonly SqlConnectionPoolGroupProviderInfo _poolGroupProviderInfo; // will only be null when called for ChangePassword, or creating SSE User Instance
108111
private TdsParser _parser;
@@ -2421,7 +2424,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo)
24212424
// Deal with Msal service exceptions first, retry if 429 received.
24222425
catch (MsalServiceException serviceException)
24232426
{
2424-
if (429 == serviceException.StatusCode)
2427+
if (serviceException.StatusCode == MsalHttpRetryStatusCode)
24252428
{
24262429
RetryConditionHeaderValue retryAfter = serviceException.Headers.RetryAfter;
24272430
if (retryAfter.Delta.HasValue)
@@ -2440,9 +2443,15 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo)
24402443
}
24412444
else
24422445
{
2443-
break;
2446+
SqlClientEventSource.Log.TryTraceEvent("<sc.SqlInternalConnectionTds.GetFedAuthToken.MsalServiceException error:> Timeout: {0}", serviceException.ErrorCode);
2447+
throw SQL.ActiveDirectoryTokenRetrievingTimeout(Enum.GetName(typeof(SqlAuthenticationMethod), ConnectionOptions.Authentication), serviceException.ErrorCode, serviceException);
24442448
}
24452449
}
2450+
else
2451+
{
2452+
SqlClientEventSource.Log.TryTraceEvent("<sc.SqlInternalConnectionTds.GetFedAuthToken.MsalServiceException error:> {0}", serviceException.ErrorCode);
2453+
throw ADP.CreateSqlException(serviceException, ConnectionOptions, this, username);
2454+
}
24462455
}
24472456
// Deal with normal MsalExceptions.
24482457
catch (MsalException msalException)
@@ -2453,21 +2462,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo)
24532462
{
24542463
SqlClientEventSource.Log.TryTraceEvent("<sc.SqlInternalConnectionTds.GetFedAuthToken.MSALException error:> {0}", msalException.ErrorCode);
24552464

2456-
// Error[0]
2457-
SqlErrorCollection sqlErs = new();
2458-
sqlErs.Add(new SqlError(0, (byte)0x00, (byte)TdsEnums.MIN_ERROR_CLASS, ConnectionOptions.DataSource, StringsHelper.GetString(Strings.SQL_MSALFailure, username, ConnectionOptions.Authentication.ToString("G")), ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName, 0));
2459-
2460-
// Error[1]
2461-
string errorMessage1 = StringsHelper.GetString(Strings.SQL_MSALInnerException, msalException.ErrorCode);
2462-
sqlErs.Add(new SqlError(0, (byte)0x00, (byte)TdsEnums.MIN_ERROR_CLASS, ConnectionOptions.DataSource, errorMessage1, ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName, 0));
2463-
2464-
// Error[2]
2465-
if (!string.IsNullOrEmpty(msalException.Message))
2466-
{
2467-
sqlErs.Add(new SqlError(0, (byte)0x00, (byte)TdsEnums.MIN_ERROR_CLASS, ConnectionOptions.DataSource, msalException.Message, ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName, 0));
2468-
}
2469-
SqlException exc = SqlException.CreateException(sqlErs, "", this);
2470-
throw exc;
2465+
throw ADP.CreateSqlException(msalException, ConnectionOptions, this, username);
24712466
}
24722467

24732468
SqlClientEventSource.Log.TryAdvancedTraceEvent("<sc.SqlInternalConnectionTds.GetFedAuthToken|ADV> {0}, sleeping {1}[Milliseconds]", ObjectID, sleepInterval);

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,10 @@ internal static Exception ActiveDirectoryDeviceFlowTimeout()
510510
return ADP.TimeoutException(Strings.SQL_Timeout_Active_Directory_DeviceFlow_Authentication);
511511
}
512512

513+
internal static Exception ActiveDirectoryTokenRetrievingTimeout(string authenticaton, string errorCode, Exception exception)
514+
{
515+
return ADP.TimeoutException(StringsHelper.GetString(Strings.AAD_Token_Retrieving_Timeout, authenticaton, errorCode, exception?.Message), exception);
516+
}
513517

514518
//
515519
// SQL.DataCommand

src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs

Lines changed: 11 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1932,4 +1932,7 @@
19321932
<data name="SQL_ParameterDirectionInvalidForOptimizedBinding" xml:space="preserve">
19331933
<value>Parameter '{0}' cannot have Direction Output or InputOutput when EnableOptimizedParameterBinding is enabled on the parent command.</value>
19341934
</data>
1935-
</root>
1935+
<data name="AAD_Token_Retrieving_Timeout" xml:space="preserve">
1936+
<value>Connection timed out while retrieving an access token using '{0}' authentication method. Last error: {1}: {2}</value>
1937+
</data>
1938+
</root>

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

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ public void AssertUnrecoverableStateCountIsCorrect()
105105

106106
sealed internal class SqlInternalConnectionTds : SqlInternalConnection, IDisposable
107107
{
108+
// https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/retry-after#simple-retry-for-errors-with-http-error-codes-500-600
109+
internal const int MsalHttpRetryStatusCode = 429;
108110

109111
// Connection re-route limit
110112
internal const int _maxNumberOfRedirectRoute = 10;
@@ -2859,7 +2861,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo)
28592861
// Deal with Msal service exceptions first, retry if 429 received.
28602862
catch (MsalServiceException serviceException)
28612863
{
2862-
if (429 == serviceException.StatusCode)
2864+
if (serviceException.StatusCode == MsalHttpRetryStatusCode)
28632865
{
28642866
RetryConditionHeaderValue retryAfter = serviceException.Headers.RetryAfter;
28652867
if (retryAfter.Delta.HasValue)
@@ -2878,9 +2880,15 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo)
28782880
}
28792881
else
28802882
{
2881-
break;
2883+
SqlClientEventSource.Log.TryTraceEvent("<sc.SqlInternalConnectionTds.GetFedAuthToken.MsalServiceException error:> Timeout: {0}", serviceException.ErrorCode);
2884+
throw SQL.ActiveDirectoryTokenRetrievingTimeout(Enum.GetName(typeof(SqlAuthenticationMethod), ConnectionOptions.Authentication), serviceException.ErrorCode, serviceException);
28822885
}
28832886
}
2887+
else
2888+
{
2889+
SqlClientEventSource.Log.TryTraceEvent("<sc.SqlInternalConnectionTds.GetFedAuthToken.MsalServiceException error:> {0}", serviceException.ErrorCode);
2890+
throw ADP.CreateSqlException(serviceException, ConnectionOptions, this, username);
2891+
}
28842892
}
28852893
// Deal with normal MsalExceptions.
28862894
catch (MsalException msalException)
@@ -2891,21 +2899,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo)
28912899
{
28922900
SqlClientEventSource.Log.TryTraceEvent("<sc.SqlInternalConnectionTds.GetFedAuthToken.MSALException error:> {0}", msalException.ErrorCode);
28932901

2894-
// Error[0]
2895-
SqlErrorCollection sqlErs = new SqlErrorCollection();
2896-
sqlErs.Add(new SqlError(0, (byte)0x00, (byte)TdsEnums.MIN_ERROR_CLASS, ConnectionOptions.DataSource, StringsHelper.GetString(Strings.SQL_MSALFailure, username, ConnectionOptions.Authentication.ToString("G")), ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName, 0));
2897-
2898-
// Error[1]
2899-
string errorMessage1 = StringsHelper.GetString(Strings.SQL_MSALInnerException, msalException.ErrorCode);
2900-
sqlErs.Add(new SqlError(0, (byte)0x00, (byte)TdsEnums.MIN_ERROR_CLASS, ConnectionOptions.DataSource, errorMessage1, ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName, 0));
2901-
2902-
// Error[2]
2903-
if (!string.IsNullOrEmpty(msalException.Message))
2904-
{
2905-
sqlErs.Add(new SqlError(0, (byte)0x00, (byte)TdsEnums.MIN_ERROR_CLASS, ConnectionOptions.DataSource, msalException.Message, ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName, 0));
2906-
}
2907-
SqlException exc = SqlException.CreateException(sqlErs, "", this);
2908-
throw exc;
2902+
throw ADP.CreateSqlException(msalException, ConnectionOptions, this, username);
29092903
}
29102904

29112905
SqlClientEventSource.Log.TryAdvancedTraceEvent("<sc.SqlInternalConnectionTds.GetFedAuthToken|ADV> {0}, sleeping {1}[Milliseconds]", ObjectID, sleepInterval);

src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,11 @@ static internal Exception ActiveDirectoryDeviceFlowTimeout()
654654
return ADP.TimeoutException(Strings.SQL_Timeout_Active_Directory_DeviceFlow_Authentication);
655655
}
656656

657+
internal static Exception ActiveDirectoryTokenRetrievingTimeout(string authenticaton, string errorCode, Exception exception)
658+
{
659+
return ADP.TimeoutException(StringsHelper.GetString(Strings.AAD_Token_Retrieving_Timeout, authenticaton, errorCode, exception?.Message), exception);
660+
}
661+
657662
//
658663
// SQL.DataCommand
659664
//

src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs

Lines changed: 10 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4617,4 +4617,7 @@
46174617
<data name="SQL_ParameterDirectionInvalidForOptimizedBinding" xml:space="preserve">
46184618
<value>Der Parameter "{0}" kann keine Ausgaberichtung oder InputOutput aufweisen, wenn EnableOptimizedParameterBinding für den übergeordneten Befehl aktiviert ist.</value>
46194619
</data>
4620+
<data name="AAD_Token_Retrieving_Timeout" xml:space="preserve">
4621+
<value>Timeout bei der Verbindung beim Abrufen eines Zugriffstokens mithilfe der Authentifizierungsmethode "{0}". Letzter Fehler: {1}: {2}</value>
4622+
</data>
46204623
</root>

src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4617,4 +4617,7 @@
46174617
<data name="SQL_ParameterDirectionInvalidForOptimizedBinding" xml:space="preserve">
46184618
<value>El parámetro “{0}” no puede tener la Dirección de salida ni InputOutput cuando EnableOptimizedParameterBinding está habilitado en el comando primario.</value>
46194619
</data>
4620+
<data name="AAD_Token_Retrieving_Timeout" xml:space="preserve">
4621+
<value>Se agotó el tiempo de espera de la conexión al recuperar un token de acceso mediante el método de autenticación "{0}". Último error: {1}: {2}</value>
4622+
</data>
46204623
</root>

src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4617,4 +4617,7 @@
46174617
<data name="SQL_ParameterDirectionInvalidForOptimizedBinding" xml:space="preserve">
46184618
<value>Le paramètre « {0} » ne peut pas avoir Direction Output ou InputOutput lorsque EnableOptimizedParameterBinding est activé sur la commande parente.</value>
46194619
</data>
4620+
<data name="AAD_Token_Retrieving_Timeout" xml:space="preserve">
4621+
<value>La connexion a expiré lors de la récupération d’un jeton d’accès à l’aide de '{0}' méthode d’authentification. Dernière erreur : {1} : {2}</value>
4622+
</data>
46204623
</root>

0 commit comments

Comments
 (0)