AOT-safe auth provider with feature switch and trimmer support#4348
AOT-safe auth provider with feature switch and trimmer support#4348paulmedynski wants to merge 11 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR exposes an AOT-friendly public authentication provider registration surface (SqlAuthenticationProviderManager) and introduces a feature switch to enable trimming away reflection-based Azure extension provider discovery for NativeAOT/trimming scenarios.
Changes:
- Made
SqlAuthenticationProviderManagerpublic withGetProvider/SetProviderAPIs and addedApplicationClientIdas part of the public surface. - Added
Microsoft.Data.SqlClient.EnableReflectionBasedAuthenticationProviderDiscoveryfeature switch with trimmer support ([FeatureSwitchDefinition]on .NET 9+ andILLink.Substitutions.xmlfor .NET 8). - Added an
tools/AotCompatibilitytest app plus new/updated unit & functional tests covering the new public API and config behavior.
Reviewed changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/AotCompatibility/README.md | Documents the AOT compatibility test app and feature switch usage. |
| tools/AotCompatibility/Program.cs | Implements runtime checks and trimming verification via the ILC map file. |
| tools/AotCompatibility/Directory.Packages.props | Enables CPM for the tool (project-mode, no package versions). |
| tools/AotCompatibility/Directory.Build.props | Prevents inheriting repo-wide build props for the tool. |
| tools/AotCompatibility/AotCompatibility.csproj | Adds the NativeAOT/trimming test app and propagates the feature switch via RuntimeHostConfigurationOption. |
| src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlAuthenticationProviderManagerTests.cs | Adds unit tests for the new public manager API and interop with Abstractions. |
| src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/LocalAppContextSwitchesTest.cs | Extends default-switch tests to cover the new auth discovery switch. |
| src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlAuthenticationProviderManagerTests.cs | Adjusts tests (pragma) and adds validation for reading ApplicationClientId from config. |
| src/Microsoft.Data.SqlClient/tests/FunctionalTests/app.config | Adds applicationClientId attribute to the auth provider config section. |
| src/Microsoft.Data.SqlClient/tests/FunctionalTests/AADAuthenticationTests.cs | Adds pragma suppression around newly-obsoleted Abstractions API usage. |
| src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs | Adds helper plumbing to reset the new switch in tests. |
| src/Microsoft.Data.SqlClient/src/Resources/ILLink.Substitutions.xml | Adds substitutions for the new feature switch (and retains UseManagedNetworking stubs). |
| src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs | Makes the type public and guards reflection-based discovery behind the new switch; adds AOT/trimming annotations. |
| src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs | Introduces the new switch property and [FeatureSwitchDefinition] for .NET 9+. |
| src/Microsoft.Data.SqlClient/src/Microsoft.Data.SqlClient.csproj | Embeds ILLink.Substitutions.xml for all non-net462 TFMs. |
| src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.cs | Adds the new public API to the reference assembly (without nullable annotations). |
| src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/SqlAuthenticationProvider.Internal.cs | Updates reflection binding flags to target public manager methods. |
| src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/SqlAuthenticationProvider.cs | Marks legacy Abstractions APIs obsolete in favor of the new manager APIs. |
| doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationProviderManager.xml | Adds public XML doc content for the new manager API. |
Comments suppressed due to low confidence (1)
src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs:346
- SetProvider is now a public API but does not validate the provider argument; passing null currently results in a NullReferenceException. Public APIs should throw ArgumentNullException for null arguments.
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationProviderManager.xml' path='docs/members[@name="SqlAuthenticationProviderManager"]/SetProvider/*'/>
public static bool SetProvider(SqlAuthenticationMethod authenticationMethod, SqlAuthenticationProvider provider)
{
if (!provider.IsSupported(authenticationMethod))
{
throw SQL.UnsupportedAuthenticationByProvider(authenticationMethod.ToString(), provider.GetType().Name);
- Fix SetProvider exception docs: SqlException → NotSupportedException - Clarify AOT remarks: static ctor uses reflection by default; describe how to disable it for full AOT compatibility - Return non-zero exit code on SqlConnection construction failure - Replace Directory.GetFiles with EnumerateFiles(IgnoreInaccessible) - Clarify README: 'the test app default' vs library default - Add XML summary docs to all new unit test methods - Remove [Obsolete] from SqlAuthenticationProvider.GetProvider/SetProvider; add remarks pointing to SqlAuthenticationProviderManager equivalents noting future deprecation and removal - Remove corresponding #pragma CS0618 suppressions from tests - Document applicationClientId in test app.config
paulmedynski
left a comment
There was a problem hiding this comment.
I decided against the obsoletion.
… doc fixes - Wrap `using System.Diagnostics.CodeAnalysis` in #if NET (unused on net462) - Gate ILLink substitution tests by TFM; add NETFRAMEWORK negative assertions - Fix remarks: "on all TFMs" -> "on .NET 8+" (net462 has no trimmer) - Add -f/-r flags to AotCompatibility README publish examples
|
Addressed all actionable review feedback in 11d75ab:
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #4348 +/- ##
==========================================
- Coverage 65.39% 63.42% -1.98%
==========================================
Files 285 280 -5
Lines 43331 66217 +22886
==========================================
+ Hits 28337 41999 +13662
- Misses 14994 24218 +9224
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
- Rename s_useManagedNetworking → s_useManagedNetworkingOnWindows in LocalAppContextSwitchesHelper to match field rename in LocalAppContextSwitches (3 sites: ctor, Dispose, property) - Use parameterless ActiveDirectoryAuthenticationProvider ctor when ApplicationClientId is null to preserve built-in default client ID - Add -f net9.0 -r linux-x64 to AOT README publish-with-reflection example
- Stream ILC map file line-by-line instead of File.ReadAllText to avoid memory pressure from large map files (tools/AotCompatibility) - Add warning when AppContext feature switch is absent (expected in JIT mode) - Fix stale app.config comment referencing removed public ApplicationClientId - Move app.config auth provider tests from FunctionalTests to UnitTests - Add separate token-acquisition test for config-registered provider - Remove duplicate IsDummySqlAuthenticationProviderSetByDefault test, its DummySqlAuthenticationProvider, and app.config from FunctionalTests - Add internal ApplicationClientId property for test verification
Pull request was converted to draft
|
We have another design approach that we would like to explore, so pausing this PR for the time being. |
…stractions Bare-minimum changes to produce and test signed assemblies in the CI Package pipeline, plus the Abstractions strong-name verification of the SqlClient assembly. - Add the download-assembly-signing-key pipeline step and thread SigningKeyPath / TestSigningKeyPath through the CI Package build, pack, and test jobs so the SqlClient package and its tests are produced and run signed. - Thread isInternalBuild through the SqlClient and Abstractions package stages. - In SqlAuthenticationProvider (Abstractions), verify the loaded Microsoft.Data.SqlClient assembly's public key token when STRONG_NAME_SIGNING is defined, with defense-in-depth post-load verification on .NET Core. - Add conditional signed InternalsVisibleTo and test-assembly signing for the Abstractions project and its tests.
- Make SqlAuthenticationProviderManager public with static GetProvider/SetProvider API - Add FeatureSwitchDefinition for reflection-based provider discovery (Microsoft.Data.SqlClient.EnableReflectionBasedAuthenticationProviderDiscovery) - Add ILLink.Substitutions.xml for net8.0 trimmer support - Add AotCompatibility test app (tools/AotCompatibility) validating Native AOT publish and trimmer behavior - Update ref assembly (no nullable annotations, per codebase convention) - Add unit tests for provider manager public API
- Fix SetProvider exception docs: SqlException → NotSupportedException - Clarify AOT remarks: static ctor uses reflection by default; describe how to disable it for full AOT compatibility - Return non-zero exit code on SqlConnection construction failure - Replace Directory.GetFiles with EnumerateFiles(IgnoreInaccessible) - Clarify README: 'the test app default' vs library default - Add XML summary docs to all new unit test methods - Remove [Obsolete] from SqlAuthenticationProvider.GetProvider/SetProvider; add remarks pointing to SqlAuthenticationProviderManager equivalents noting future deprecation and removal - Remove corresponding #pragma CS0618 suppressions from tests - Document applicationClientId in test app.config
Separate UseManagedNetworkingOnWindows entries into a Windows-only ILLink.Substitutions.Windows.xml file to prevent a breaking change for cross-platform consumers who set the switch to false expecting it to be a no-op on Unix. Add unit tests verifying the correct substitution files are embedded per platform.
… doc fixes - Wrap `using System.Diagnostics.CodeAnalysis` in #if NET (unused on net462) - Gate ILLink substitution tests by TFM; add NETFRAMEWORK negative assertions - Fix remarks: "on all TFMs" -> "on .NET 8+" (net462 has no trimmer) - Add -f/-r flags to AotCompatibility README publish examples
- Remove 'using Microsoft.Data.SqlClient.Tests.Common;' from SqlAuthenticationProviderManagerTests.cs - Remove 'using System.Reflection;' from ILLinkSubstitutionsTests.cs Both would cause CS8019 with warnings-as-errors.
Restructure UseManagedNetworking so it no longer needs a per-OS ILLink substitution file: public static bool UseManagedNetworking => The platform guard guarantees managed SNI on non-Windows regardless of the trimmer-substituted value of UseManagedNetworkingOnWindows, making it safe to embed the substitution entries in the cross-platform ILLink.Substitutions.xml on all platforms. Changes: - LocalAppContextSwitches: split UseManagedNetworking into a public expression-bodied property with platform guard + private UseManagedNetworkingOnWindows property (trimmer target) - Widen UseManagedNetworkingOnWindowsString and cache field from '#if NET && _WINDOWS' to '#if NET' - Merge ILLink.Substitutions.Windows.xml entries into the cross-platform ILLink.Substitutions.xml (targeting get_UseManagedNetworkingOnWindows instead of get_UseManagedNetworking) - Delete ILLink.Substitutions.Windows.xml - Remove conditional Windows-only embedding from csproj - Update ILLinkSubstitutionsTests to match: single cross-platform resource on all .NET TFMs, no Windows-specific resource
- Rename s_useManagedNetworking → s_useManagedNetworkingOnWindows in LocalAppContextSwitchesHelper to match field rename in LocalAppContextSwitches (3 sites: ctor, Dispose, property) - Use parameterless ActiveDirectoryAuthenticationProvider ctor when ApplicationClientId is null to preserve built-in default client ID - Add -f net9.0 -r linux-x64 to AOT README publish-with-reflection example
- Remove the public static ApplicationClientId property from the Manager - Remove it from the ref assembly and XML docs - Skip app.config loading when reflection-based discovery is disabled - Split into two constructors: no-arg (AOT-safe) and object-arg (config-driven) - Update AotCompatibility test app to supply a random GUID as client ID - Remove related unit and functional tests
- Stream ILC map file line-by-line instead of File.ReadAllText to avoid memory pressure from large map files (tools/AotCompatibility) - Add warning when AppContext feature switch is absent (expected in JIT mode) - Fix stale app.config comment referencing removed public ApplicationClientId - Move app.config auth provider tests from FunctionalTests to UnitTests - Add separate token-acquisition test for config-registered provider - Remove duplicate IsDummySqlAuthenticationProviderSetByDefault test, its DummySqlAuthenticationProvider, and app.config from FunctionalTests - Add internal ApplicationClientId property for test verification
f1da5b1 to
99a0cb9
Compare
| /// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationProviderManager.xml' path='docs/members[@name="SqlAuthenticationProviderManager"]/ApplicationClientId/*'/> | ||
| public static string? ApplicationClientId => Instance._applicationClientId; | ||
| /// <summary> | ||
| /// Gets the application client ID read from the app.config configuration section, | ||
| /// or <see langword="null"/> if none was configured. | ||
| /// </summary> | ||
| internal static string? ApplicationClientId => Instance._applicationClientId; | ||
|
|
| using Xunit; | ||
|
|
… bridge Relocates the authentication provider registry into the shared Abstractions assembly and removes the Abstractions->core reflection bridge. A lazy, core-side AuthenticationBootstrapper performs config-driven and Azure extension provider discovery, seeding the Abstractions registry on first federated authentication. - Add internal AuthenticationProviderRegistry + resource infra (AbstractionsStrings) to Abstractions; wire SqlAuthenticationProvider Get/SetProvider to it directly. - Delete SqlAuthenticationProvider.Internal reflection bridge. - Replace public SqlAuthenticationProviderManager with internal AuthenticationBootstrapper in core; remove it from the public ref surface and notsupported stubs. - Trigger bootstrap from SqlConnectionInternal.GetFedAuthToken. - Redistribute manager tests into Abstractions.Test (registry) and UnitTests (bootstrapper); Azure DefaultAuthProviderTests triggers bootstrap via reflection (TODO: switch to IVT once PR #4385 signs Azure.Test). - Update AotCompatibility tool/docs and doc-comment references.
Summary
Make
SqlAuthenticationProviderManagerpublic with a staticGetProvider/SetProviderAPI and add trimmer/AOT support via a feature switch.Changes
SqlAuthenticationProviderManager.GetProvider()andSetProvider()as the public surface for authentication provider registrationMicrosoft.Data.SqlClient.EnableReflectionBasedAuthenticationProviderDiscovery[FeatureSwitchDefinition]on .NET 9+ for linker-integrated trimmingILLink.Substitutions.xmlfor .NET 8 trimmer supportfalse, the reflection-basedLoadAzureExtensionProvider()is trimmed awayILLink.Substitutions.xml(embedded on all non-net462 TFMs) contains both the auth provider feature switch andUseManagedNetworkingOnWindowsentries. TheUseManagedNetworkingproperty uses a runtime platform guard (!OperatingSystem.IsWindows() || UseManagedNetworkingOnWindows) so the trimmer substitution ofUseManagedNetworkingOnWindowsis safe on all platforms — Unix always returnstrueregardless of the substituted value.tools/AotCompatibility/): Validates Native AOT publish succeeds and verifies trimmer removes reflection code when feature switch is disabledILLinkSubstitutionsTestsverifying correct embedded resources per platformVerification
dotnet build -t:Buildsucceeds (0 warnings, 0 errors)dotnet publishwith Native AOT succeeds for net9.0/net10.0LoadAzureExtensionProvideris trimmed when feature switch isfalseILLink.Substitutions.xmlembedded in assembly on all platformsNotes
SspiAuthenticationParameters, column encryption providers)