AOT-safe authentication provider discovery with feature switch and trimmer support - #4573
Draft
paulmedynski wants to merge 4 commits into
Draft
AOT-safe authentication provider discovery with feature switch and trimmer support#4573paulmedynski wants to merge 4 commits into
paulmedynski wants to merge 4 commits into
Conversation
Gate the reflection-based config/Azure-extension provider discovery behind a
feature switch so the trimmer can remove it, and relocate the provider registry
into the shared Abstractions assembly. The public surface stays
SqlAuthenticationProvider.GetProvider/SetProvider (no new public type).
- Registry: move into Abstractions as internal AuthenticationProviderRegistry
(one ConcurrentDictionary of a ProviderEntry record struct; IsExternalInit
polyfill for netstandard2.0); delete the Abstractions->core reflection bridge.
- Bootstrapper: replace SqlAuthenticationProviderManager with an internal,
core-side AuthenticationBootstrapper that lazily (Lazy<T>) discovers
config-driven and Azure-extension providers and seeds the registry, triggered
once from SqlConnectionInternal.GetFedAuthToken.
- AOT: feature switch
Microsoft.Data.SqlClient.EnableReflectionBasedAuthenticationProviderDiscovery
([FeatureSwitchDefinition] on .NET 9+, ILLink.Substitutions.xml on .NET 8)
trims LoadAzureExtensionProvider when disabled; AotCompatibility tool
validates Native AOT publish and trimming.
- Behavior: app-config ("permanent") providers take precedence and are
last-in-wins; BeforeLoad fires on first registration; lifecycle callbacks are
isolated from exceptions and documented as idempotent.
- Tests: registry and bootstrapper tests reorganized -- UnitTests covers the
Azure-absent and stub paths, Azure.Test covers the real provider (global vs
non-global split); serialized collection renamed to SqlAuthenticationProviderGlobal.
… tests - AuthenticationBootstrapper: replace backing fields with internal auto-properties (Registry, ApplicationClientId, UseWamBroker). - AuthenticationProviderRegistry: remove PermanentProviderException sentinel; document SetProvider null behavior. - UnitTests: assert Registry/ApplicationClientId/UseWamBroker; add UseWamBroker config tests (app.config sets useWamBroker=true).
Integration fix for replaying the AOT commits onto current main. Main's LocalAppContextSwitchesHelper resolves switch defaults by reflecting over public static properties, but EnableReflectionBasedAuthenticationProviderDiscovery is internal, so the helper cannot see it. Assert it directly on LocalAppContextSwitches, as the original AOT test did, and keep the helper's field-based accessor unchanged.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR replays and updates the AOT-/trimmer-safe authentication provider discovery work on top of the newer assembly-signing stack by (1) moving the auth-provider registry into the Abstractions assembly, (2) isolating reflection-based discovery behind a feature switch + trimmer substitutions, and (3) adding/reshaping tests and an AOT compatibility tool to validate trimming behavior.
Changes:
- Introduces
AuthenticationBootstrapperto lazily seed providers from app.config and the optional Azure extension, gated byMicrosoft.Data.SqlClient.EnableReflectionBasedAuthenticationProviderDiscoveryfor AOT/trimming. - Moves the provider registry into
Microsoft.Data.SqlClient.Extensions.Abstractionsand rewiresSqlAuthenticationProvider.GetProvider/SetProviderto use it directly (removing the Abstractions→SqlClient reflection bridge). - Adds/updates unit tests and a
tools/AotCompatibilityapp to validate NativeAOT publish and trimming of the reflection-based discovery path.
Reviewed changes
Copilot reviewed 53 out of 54 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tools/AotCompatibility/README.md | Documents what the AOT compatibility tool validates and how to run/publish it. |
| tools/AotCompatibility/Program.cs | Implements runtime checks and ILC map-file verification for trimming behavior. |
| tools/AotCompatibility/Directory.Packages.props | Enables central package management for the tool without specifying versions (project-mode). |
| tools/AotCompatibility/Directory.Build.props | Prevents inheriting repo-wide Directory.Build.props settings for the tool. |
| tools/AotCompatibility/AotCompatibility.csproj | Defines the AOT/trimming validation app and sets the runtime host configuration option for the feature switch. |
| src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlAuthenticationProviderManagerTests.cs | Removes legacy manager-based tests replaced by bootstrapper/registry coverage. |
| src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/LocalAppContextSwitchesTest.cs | Asserts the new internal reflection-discovery switch default directly. |
| src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/ILLinkSubstitutionsTests.cs | Adds tests ensuring the unified ILLink substitution resource is embedded on non-net462 TFMs. |
| src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/DummySqlAuthenticationProvider.cs | Adds a dummy provider used by net462 UnitTests app.config-based registration tests. |
| src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/AuthenticationBootstrapperTests.cs | Adds core bootstrapper tests (Azure extension absent) covering config and constructor-selection logic via stubs. |
| src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft.Data.SqlClient.UnitTests.csproj | Ensures net462 app.config is copied to output for config-driven provider tests. |
| src/Microsoft.Data.SqlClient/tests/UnitTests/app.config | Adds UnitTests app.config configuring a dummy provider and bootstrapper settings for net462 tests. |
| src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlAuthenticationProviderManagerTests.cs | Removes legacy functional tests tied to the old manager/app.config flow. |
| src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.FunctionalTests.csproj | Removes copying FunctionalTests app.config for net462 (tests moved/restructured). |
| src/Microsoft.Data.SqlClient/tests/FunctionalTests/DataCommon/DummySqlAuthenticationProvider.cs | Removes functional-tests dummy provider implementation (test moved/rewired). |
| src/Microsoft.Data.SqlClient/tests/FunctionalTests/app.config | Removes functional-tests app.config for dummy provider registration. |
| src/Microsoft.Data.SqlClient/tests/FunctionalTests/AADAuthenticationTests.cs | Removes dummy-provider registration test from FunctionalTests (moved to Azure.Test/global bootstrap tests). |
| src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs | Updates helper to reflect new cached-field naming and adds helper for the new internal switch. |
| src/Microsoft.Data.SqlClient/src/Resources/ILLink.Substitutions.xml | Unifies trimmer substitutions for auth-provider discovery and managed-networking switch in one cross-platform file. |
| src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs | Adds the new reflection-discovery switch and refactors managed-networking switch into a trimmer substitution target property. |
| src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Connection/SqlConnectionInternal.cs | Ensures authentication bootstrap runs before resolving a provider on the fed-auth path. |
| src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AuthenticationBootstrapper.cs | Replaces legacy manager with a lazy bootstrapper that loads config + Azure extension behind a feature switch and AOT annotations. |
| src/Microsoft.Data.SqlClient/src/Microsoft.Data.SqlClient.csproj | Embeds the unified ILLink substitution resource for all non-net462 TFMs. |
| src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.cs | Adds a note clarifying ref assemblies intentionally omit nullable annotations. |
| src/Microsoft.Data.SqlClient.Extensions/Azure/test/WamBrokerTests.cs | Updates comments/collection usage and removes a global-state mutation test that’s no longer needed in this form. |
| src/Microsoft.Data.SqlClient.Extensions/Azure/test/SqlAuthenticationProviderGlobalCollection.cs | Renames the serialized collection definition used for tests mutating the global registry. |
| src/Microsoft.Data.SqlClient.Extensions/Azure/test/DefaultAuthProviderTests.cs | Removes legacy “default provider installed” test tied to old manager static-init behavior. |
| src/Microsoft.Data.SqlClient.Extensions/Azure/test/AuthenticationBootstrapperTests.cs | Adds Azure-present, non-global bootstrapper tests (constructor selection via reflection access). |
| src/Microsoft.Data.SqlClient.Extensions/Azure/test/AuthenticationBootstrapperGlobalTests.cs | Adds Azure-present tests that force the full bootstrap and verify global provider registration. |
| src/Microsoft.Data.SqlClient.Extensions/Azure/test/AADAuthenticationTests.cs | Moves these tests into the global serialized collection to account for shared registry mutations. |
| src/Microsoft.Data.SqlClient.Extensions/Abstractions/test/SqlAuthenticationProviderTest.cs | Updates tests to validate the public API delegates to the shared registry instance (no SqlClient assembly required). |
| src/Microsoft.Data.SqlClient.Extensions/Abstractions/test/AuthenticationProviderRegistryTest.cs | Adds comprehensive isolated tests for registry behavior (overrides, permanence, lifecycle callbacks). |
| src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Strings.zh-Hant.resx | Adds localized string resources used by Abstractions exception messages. |
| src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Strings.zh-Hans.resx | Adds localized string resources used by Abstractions exception messages. |
| src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Strings.tr.resx | Adds localized string resources used by Abstractions exception messages. |
| src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Strings.ru.resx | Adds localized string resources used by Abstractions exception messages. |
| src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Strings.resx | Adds the invariant resource string(s) used by Abstractions exception messages. |
| src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Strings.pt-BR.resx | Adds localized string resources used by Abstractions exception messages. |
| src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Strings.pl.resx | Adds localized string resources used by Abstractions exception messages. |
| src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Strings.ko.resx | Adds localized string resources used by Abstractions exception messages. |
| src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Strings.ja.resx | Adds localized string resources used by Abstractions exception messages. |
| src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Strings.it.resx | Adds localized string resources used by Abstractions exception messages. |
| src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Strings.fr.resx | Adds localized string resources used by Abstractions exception messages. |
| src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Strings.es.resx | Adds localized string resources used by Abstractions exception messages. |
| src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Strings.Designer.cs | Adds the strongly-typed resource accessor for Abstractions strings. |
| src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Strings.de.resx | Adds localized string resources used by Abstractions exception messages. |
| src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Strings.cs.resx | Adds localized string resources used by Abstractions exception messages. |
| src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/SqlAuthenticationProvider.Internal.cs | Removes the Abstractions→SqlClient reflection bridge for provider get/set. |
| src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/SqlAuthenticationProvider.cs | Rewires GetProvider/SetProvider to call the shared registry directly. |
| src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/IsExternalInit.cs | Adds an IsExternalInit polyfill for record/init support on non-NET TFMs. |
| src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/AuthenticationProviderRegistry.cs | Introduces the shared concurrent provider registry with permanence semantics and lifecycle callback handling. |
| src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Abstractions.csproj | Updates InternalsVisibleTo to include SqlClient and UnitTests under signing conditions. |
| src/Microsoft.Data.SqlClient.Extensions/Abstractions/doc/SqlAuthenticationProvider.xml | Updates docs to reflect concurrency/idempotency expectations and clarifies canonical Get/Set usage. |
| aot-auth-provider-proposal.md | Adds/updates design proposal documentation for the AOT-safe registration approach. |
Files not reviewed (1)
- src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Strings.Designer.cs: Generated file
Suppressed comments (2)
src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AuthenticationBootstrapper.cs:280
- These trace calls build interpolated/concatenated strings at the call site, which allocates even when tracing is disabled. Since SqlClientEventSource.TryTraceEvent only formats when enabled, switch to a parameterized call and compute expensive arguments (like the token string) only inside an IsTraceEnabled() guard.
src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs:388 - The remarks still refer to the old cached-field name (s_useManagedNetworking). After the rename to s_useManagedNetworkingOnWindows, this comment is now inaccurate and could mislead future edits to this reflection-based helper.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+102
to
+108
| /// <exception cref="NullReferenceException"> | ||
| /// <paramref name="provider"/> is <see langword="null"/>. | ||
| /// </exception> | ||
| internal bool SetProvider(SqlAuthenticationMethod authenticationMethod, SqlAuthenticationProvider provider) | ||
| { | ||
| if (!provider.IsSupported(authenticationMethod)) | ||
| { |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replays the AOT work from #4348 on top of the new assembly-signing stack (#4566–#4570), so it no longer carries a stale copy of the signing changes.
dev/paul/aotwas based on a pre-rebase copy ofdev/paul/assembly-signing-coreand therefore included an old duplicate of the signing commit. This branch drops that commit and keeps only the AOT work, rebased onto currentmainvia the stack.Commits
Conflict resolutions worth reviewing
Replaying onto current
mainrequired resolving conflicts introduced by work that landed after the AOT branch point:LocalAppContextSwitches.cs— kept the AOT two-property design (UseManagedNetworkingplatform guard +UseManagedNetworkingOnWindowsas the trimmer substitution target) and thes_useManagedNetworkingOnWindowsfield. Retained main's default-value documentation.AuthenticationBootstrapper.cs— main'sSqlAuthenticationProviderManager.csis renamed here. Took the AOT version; the strong-name–verified Azure extension loading is preserved, relocated into a[RequiresUnreferencedCode]/[RequiresDynamicCode]-annotated method.LocalAppContextSwitchesHelper.cs— kept main'sGetLocalAppContextSwitchesType()refactor and runtime OS check, adopting the AOT field rename.LocalAppContextSwitchesTest.cs— kept main'sswitchesHelperassertions (includingUseLegacyIdleTimeoutBehavior) and added the new switch assertion.Known follow-up
The relocated Azure-loading trace calls use interpolated strings, so this branch does not carry #4528's parameterized trace-call conversion for that code. That conversion applied only to the block the AOT commit rewrites. Worth re-applying before this leaves draft.
Validation
Microsoft.Data.SqlClientbuilds clean fornet8.0net8.0)Supersedes the AOT content of #4348.