-
Notifications
You must be signed in to change notification settings - Fork 321
[feat/azure-split] Refactors to simplify WAM support diff #3888
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
base: feat/azure-split
Are you sure you want to change the base?
Conversation
…AuthenticationProvider.cs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR refactors the ActiveDirectoryAuthenticationProvider class to improve code modularity and readability in preparation for WAM (Windows Authentication Manager) support. The main changes include extracting authentication logic into separate methods, adding comprehensive XML documentation comments, and introducing a new GitHub Copilot prompt for documentation generation.
Changes:
- Refactored
AcquireTokenAsyncto use a switch statement and delegate to specialized methods for each authentication type - Extracted authentication flows into dedicated methods (AcquireTokenIntegratedAsync, AcquireTokenByUsernamePasswordAsync, AcquireTokenInteractiveAsync, AcquireTokenDeviceFlowAsync)
- Added comprehensive XML documentation comments to all internal members and helper classes
- Fixed a bug in the connection timeout overflow check logic
- Added obsolete attribute to ActiveDirectoryIntegrated authentication method
- Created a new prompt file for generating documentation comments
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 10 comments.
| File | Description |
|---|---|
| src/Microsoft.Data.SqlClient.Extensions/Azure/src/ActiveDirectoryAuthenticationProvider.cs | Major refactoring with extracted methods, added doc comments, and fixed timeout logic bug |
| src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/SqlAuthenticationMethod.cs | Added obsolete attribute to ActiveDirectoryIntegrated, corrected comment grammar |
| .github/prompts/doc-comments.prompt.md | New GitHub Copilot prompt for generating XML documentation |
Comments suppressed due to low confidence (1)
src/Microsoft.Data.SqlClient.Extensions/Azure/src/ActiveDirectoryAuthenticationProvider.cs:193
- The condition was inverted from the original, which appears to fix a bug. The original code
if (int.MaxValue / 1000 > parameters.ConnectionTimeout)would set CancelAfter to int.MaxValue when the timeout was SMALL (safe to multiply), which is backwards. The new code correctly checks if the timeout is too large to safely multiply by 1000 before capping it. This is a beneficial bug fix, but it represents a behavior change that should be noted in testing.
if (parameters.ConnectionTimeout > int.MaxValue / 1000)
{
cts.CancelAfter(int.MaxValue);
}
else
{
cts.CancelAfter(parameters.ConnectionTimeout * 1000);
}
| /// </summary> | ||
| /// <param name="publicClientAppKey">The key containing configuration for the application.</param> | ||
| /// <returns>A new <see cref="IPublicClientApplication"/> instance.</returns> | ||
| private static IPublicClientApplication CreateClientAppInstance(PublicClientAppKey publicClientAppKey) |
Copilot
AI
Jan 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The CreateClientAppInstance method was changed from an instance method to a static method, but it attempts to reference _iWin32WindowFunc (an instance field) within the NETFRAMEWORK conditional block. This will cause a compilation error on NETFRAMEWORK builds. The method should either remain an instance method, or _iWin32WindowFunc should be passed as a parameter (e.g., via the PublicClientAppKey).
| private static IPublicClientApplication CreateClientAppInstance(PublicClientAppKey publicClientAppKey) | |
| private IPublicClientApplication CreateClientAppInstance(PublicClientAppKey publicClientAppKey) |
| default: | ||
| { | ||
| SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | {0} authentication mode not supported by ActiveDirectoryAuthenticationProvider class.", parameters.AuthenticationMethod); | ||
|
|
Copilot
AI
Jan 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove trailing whitespace on this line.
| .ExecuteAsync(ctsInteractive.Token) | ||
| .ConfigureAwait(false); | ||
| } | ||
| SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (silent) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, cachedResult .ExpiresOn); |
Copilot
AI
Jan 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is an extra space before the closing parenthesis in the format string. This appears in the tracing output and should be removed for consistency with other log messages.
| SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (silent) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, cachedResult .ExpiresOn); | |
| SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (silent) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, cachedResult.ExpiresOn); |
| #endif | ||
| PublicClientAppKey pcaKey = | ||
| #if NETFRAMEWORK | ||
| new(parameters.Authority, redirectUri, applicationClientId, _iWin32WindowFunc); |
Copilot
AI
Jan 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The GetPublicClientAppInstanceAsync method was changed from an instance method to a static method, but it constructs a PublicClientAppKey that references _iWin32WindowFunc, which is an instance field only available on NETFRAMEWORK builds. This will cause a compilation error. The method should either remain an instance method, or _iWin32WindowFunc should be passed as a parameter from the calling context.
| new(parameters.Authority, redirectUri, applicationClientId, _iWin32WindowFunc); | |
| new(parameters.Authority, redirectUri, applicationClientId); |
| AuthenticationResult result = await app.AcquireTokenInteractive(scopes) | ||
| .WithCorrelationId(connectionId) | ||
| .WithCustomWebUi(customWebUI) |
Copilot
AI
Jan 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The refactored code now unconditionally calls WithCustomWebUi(customWebUI) even when customWebUI is null, whereas the original code had separate branches that only called WithCustomWebUi when customWebUI was not null. If the MSAL library does not handle null values gracefully for this method, this could cause a runtime error. Consider adding a conditional check: if customWebUI is not null, call WithCustomWebUi(customWebUI) on the builder before calling WithLoginHint.
| AuthenticationResult result = await app.AcquireTokenInteractive(scopes) | |
| .WithCorrelationId(connectionId) | |
| .WithCustomWebUi(customWebUI) | |
| var interactiveBuilder = app.AcquireTokenInteractive(scopes) | |
| .WithCorrelationId(connectionId); | |
| if (customWebUI is not null) | |
| { | |
| interactiveBuilder = interactiveBuilder.WithCustomWebUi(customWebUI); | |
| } | |
| AuthenticationResult result = await interactiveBuilder |
| .WithCorrelationId(connectionId) | ||
| .ExecuteAsync(cancellationToken: cts.Token) | ||
| .ConfigureAwait(false); | ||
| SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (silent) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, cachedResult .ExpiresOn); |
Copilot
AI
Jan 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is an extra space before the closing parenthesis in the format string. This appears in the tracing output and should be removed for consistency with other log messages.
| SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (silent) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, cachedResult .ExpiresOn); | |
| SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (silent) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, cachedResult.ExpiresOn); |
| AuthenticationResult result = await builder | ||
| .ExecuteAsync(cancellationToken: cts.Token) | ||
| .ConfigureAwait(false); | ||
| SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (interactive) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, result.ExpiresOn); |
Copilot
AI
Jan 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The log message says "Acquired access token (interactive)" but this should say "Acquired access token for Active Directory Integrated auth mode" to match the original log message format for this authentication method.
| SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (interactive) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, result.ExpiresOn); | |
| SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Active Directory Integrated auth mode. Expiry Time: {0}", result.ExpiresOn); |
|
|
||
| try | ||
| { | ||
| CancellationTokenSource ctsInteractive = new(); |
Copilot
AI
Jan 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Disposable 'CancellationTokenSource' is created but not disposed.
| CancellationTokenSource ctsInteractive = new(); | |
| using CancellationTokenSource ctsInteractive = new(); |
| } | ||
|
|
||
| #pragma warning disable CS0618 // Type or member is obsolete | ||
| AuthenticationResult result = await app.AcquireTokenByUsernamePassword(scopes, parameters.UserId, parameters.Password) |
Copilot
AI
Jan 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Call to obsolete method AcquireTokenByUsernamePassword.
| // to acquire tokens for Active Directory Integrated | ||
| // authentication. | ||
| #pragma warning disable CS0618 // Type or member is obsolete | ||
| var builder = app.AcquireTokenByIntegratedWindowsAuth(scopes) |
Copilot
AI
Jan 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Call to obsolete method AcquireTokenByIntegratedWindowsAuth.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.
| { | ||
| // Safely convert to milliseconds. | ||
| if (int.MaxValue / 1000 > parameters.ConnectionTimeout) | ||
| if (parameters.ConnectionTimeout > int.MaxValue / 1000) |
Copilot
AI
Jan 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The timeout comparison logic is inverted. This condition checks if the timeout is greater than the safe conversion threshold, but then applies int.MaxValue as the timeout. The original logic int.MaxValue / 1000 > parameters.ConnectionTimeout was correct: it checked if the value was safe to convert, then converted it. The inverted logic will now apply int.MaxValue when the timeout is too large, and attempt to convert potentially unsafe values when the timeout is within range.
| default: | ||
| { | ||
| SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | {0} authentication mode not supported by ActiveDirectoryAuthenticationProvider class.", parameters.AuthenticationMethod); | ||
|
|
Copilot
AI
Jan 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the trailing whitespace on this empty line for consistency with the codebase's formatting standards.
| .ExecuteAsync(ctsInteractive.Token) | ||
| .ConfigureAwait(false); | ||
| } | ||
| SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (silent) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, cachedResult .ExpiresOn); |
Copilot
AI
Jan 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the extra space before .ExpiresOn to maintain consistent spacing in member access.
| SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (silent) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, cachedResult .ExpiresOn); | |
| SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (silent) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, cachedResult.ExpiresOn); |
| .WithCorrelationId(connectionId) | ||
| .ExecuteAsync(cancellationToken: cts.Token) | ||
| .ConfigureAwait(false); | ||
| SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (silent) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, cachedResult .ExpiresOn); |
Copilot
AI
Jan 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the extra space before .ExpiresOn to maintain consistent spacing in member access.
| SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (silent) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, cachedResult .ExpiresOn); | |
| SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (silent) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, cachedResult.ExpiresOn); |
| #endif | ||
| PublicClientAppKey pcaKey = | ||
| #if NETFRAMEWORK | ||
| new(parameters.Authority, redirectUri, applicationClientId, _iWin32WindowFunc); |
Copilot
AI
Jan 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line references _iWin32WindowFunc which is an instance field, but it's being accessed in a static method. The method GetPublicClientAppInstanceAsync is now static (line 778), so it cannot access the instance field _iWin32WindowFunc. This will cause a compilation error.
| new(parameters.Authority, redirectUri, applicationClientId, _iWin32WindowFunc); | |
| new(parameters.Authority, redirectUri, applicationClientId, null); |
| return string.CompareOrdinal(_authority, pcaKey._authority) == 0 | ||
| && string.CompareOrdinal(_redirectUri, pcaKey._redirectUri) == 0 | ||
| && string.CompareOrdinal(_applicationClientId, pcaKey._applicationClientId) == 0 | ||
| #if NETFRAMEWORK | ||
| && pcaKey._iWin32WindowFunc == _iWin32WindowFunc | ||
| #endif |
Copilot
AI
Jan 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The standalone semicolon on line 1088 is unnecessary and reduces readability. Place the semicolon at the end of the last condition on line 1087 (or line 1084 for non-NETFRAMEWORK builds) instead.
Description
Refactors code in ActiveDirectoryAuthenticationProvider to improve readability and modularity.
Adds a new prompt for doc comment generation and adds doc comments to ActiveDirectoryAuthenticationProvider.
The Integrated auth method is marked as obsolete by MSAL. This PR carries forward that obsolete designation and is a public API change.
A targeted PR for WAM support will build on top of these refactors.
Issues
#3418
Testing
No new logic, only refactors. Existing tests should provide coverage.