Fix AccessTokenCallback to match AccessToken behavior for TNIR and connection pool keys - #4518
Fix AccessTokenCallback to match AccessToken behavior for TNIR and connection pool keys#4518cheenamalhotra wants to merge 4 commits into
Conversation
On .NET Framework the driver disables Transparent Network IP Resolution by default whenever federated authentication is in use, unless the caller explicitly specified the TransparentNetworkIPResolution keyword. However, ShouldDisableTnir only tested _accessTokenInBytes (SqlConnection.AccessToken) and ignored _accessTokenCallback (SqlConnection.AccessTokenCallback), so the two token-supplying APIs behaved differently. Raised in review discussion on dotnet#4493. Changes: * Add SqlConnectionInternal.IsAccessTokenProvided, a single source of truth for "the caller supplied a token, either literally or via a callback", and use it in all three places that previously inlined the field checks (ShouldDisableTnir plus two spots in TdsParser.ConsumePreLoginHandshake). The duplicated, hand-written expression is what allowed the two paths to drift apart. * Fix the AccessToken, AccessTokenCallback and SspiContextProvider setters, which each rebuilt the ConnectionPoolKey with the sibling authentication values hard-coded to null. Setting SspiContextProvider silently dropped a previously assigned access token or callback from the pool key, so it never reached the internal connection even though the public property still reported it as set. These now preserve sibling state, matching the ConnectionString setter. (AccessToken and AccessTokenCallback are already mutually exclusive, so that pairing was benign; SspiContextProvider is not.) * Expose ShouldDisableTnir as internal static so it can be unit tested, and add coverage for the TNIR decision matrix and for pool-key preservation. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5368f578-219b-40a6-92a9-4742b56edbe6
There was a problem hiding this comment.
Pull request overview
This PR fixes a behavioral inconsistency in the .NET Framework TNIR (Transparent Network IP Resolution) defaulting logic by treating SqlConnection.AccessTokenCallback the same as SqlConnection.AccessToken, and hardens connection pooling behavior by ensuring authentication-related properties don’t silently drop each other from the ConnectionPoolKey.
Changes:
- Added a single internal “token was supplied” signal (
SqlConnectionInternal.IsAccessTokenProvided) and used it in TNIR-related and pre-login handshake logic. - Updated
AccessToken,AccessTokenCallback, andSspiContextProvidersetters to preserve sibling authentication state when rebuilding the connection pool key. - Added/extended unit tests to cover TNIR disabling logic (netfx) and pool-key/auth-property invariants.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionTests.cs | Adds regression tests ensuring pool key preserves access-token state when setting SspiContextProvider, and that AccessToken/AccessTokenCallback remain mutually exclusive. |
| src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlConnectionOptionsTest.cs | Adds netfx-only unit test coverage for ShouldDisableTnir across token-provided, Azure endpoint, and explicit TNIR keyword cases. |
| src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParser.cs | Uses IsAccessTokenProvided for fed-auth-required detection and certificate-validation gating during pre-login handshake. |
| src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnection.cs | Preserves sibling authentication values when rebuilding ConnectionPoolKey in AccessToken, AccessTokenCallback, and SspiContextProvider setters. |
| src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Connection/SqlConnectionInternal.cs | Introduces IsAccessTokenProvided and updates netfx TNIR decision logic to take a unified access-token-provided input. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Suppressed comments (1)
src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Connection/SqlConnectionInternal.cs:3938
<see cref="SqlConnection.AccessToken"/>/<see cref="SqlConnection.AccessTokenCallback"/>in this new XML doc block is likely unresolved in this namespace, which can raise doc warnings (CS1574) and fail builds due to warnings-as-errors. Use fully-qualifiedcreftargets here as well.
/// <param name="isAccessTokenProvided">
/// True when the caller supplied a federated authentication access token directly, either
/// via <see cref="SqlConnection.AccessToken"/> or
/// <see cref="SqlConnection.AccessTokenCallback"/>.
/// </param>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.
Suppressed comments (1)
src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Connection/SqlConnectionInternal.cs:158
- The XML doc comment for
IsAccessTokenProvidednests<remarks>inside<summary>, which is invalid XML documentation structure and can lead to doc build warnings or the remarks being ignored. Close the<summary>before starting<remarks>.
/// <summary>
/// True when the caller supplied a federated authentication access token directly, either
/// as a literal token via <see cref="global::Microsoft.Data.SqlClient.SqlConnection.AccessToken"/> or as a token provider
/// via <see cref="global::Microsoft.Data.SqlClient.SqlConnection.AccessTokenCallback"/>.
/// <remarks>
Co-authored-by: Cheena Malhotra <13396919+cheenamalhotra@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.
Suppressed comments (1)
src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Connection/SqlConnectionInternal.cs:3937
- The XML doc
<see cref="SqlConnection.*"/>references in this file are not in scope (this namespace doesn’tusing Microsoft.Data.SqlClient;), which will produce CS1574 XML comment warnings; with TreatWarningsAsErrors enabled, that can break net462 builds. Use fully-qualifiedglobal::Microsoft.Data.SqlClient.SqlConnection.*like the other XML docs in this file.
/// via <see cref="SqlConnection.AccessToken"/> or
/// <see cref="SqlConnection.AccessTokenCallback"/>.
|
Superseded by #4520, which is branched directly in |
Follow-up to the review discussion on #4493, where it was noted that the TNIR behavior documented there does not actually apply when
SqlConnection.AccessTokenCallbackis used:The bug
On .NET Framework the driver disables Transparent Network IP Resolution by default whenever federated authentication is in use, unless the caller explicitly specified the
TransparentNetworkIPResolutionkeyword.SqlConnectionInternal.ShouldDisableTnironly tested_accessTokenInBytes(SqlConnection.AccessToken) and ignored_accessTokenCallback(SqlConnection.AccessTokenCallback), so the two token-supplying APIs behaved differently for no good reason.Changes
1. Single source of truth for "a token was supplied."
Added
SqlConnectionInternal.IsAccessTokenProvidedand used it in all three places that previously inlined the field checks —ShouldDisableTnirplus two spots inTdsParser.ConsumePreLoginHandshake. The duplicated hand-written expression is exactly what let these paths drift apart, and the existing@TODOinOnFedAuthInfopredicted this ("we're gonna forget one in one spot and cause a big ol bug someday").2. Pool key no longer drops sibling authentication state.
The
AccessToken,AccessTokenCallbackandSspiContextProvidersetters each rebuilt theConnectionPoolKeywith the sibling authentication values hard-coded tonull. SettingSspiContextProvidersilently dropped a previously assigned access token or callback from the pool key, so it never reached the internal connection even though the public property still reported it as set — which would also have defeated the TNIR fix above. These now preserve sibling state, matching theConnectionStringsetter, which already did this correctly.AccessTokenandAccessTokenCallbackare already mutually exclusive (validated inCheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessToken*), so that pairing was benign;SspiContextProvideris not mutually exclusive with either, so that one was a live defect.3. Tests.
ShouldDisableTniris nowinternal staticso the decision matrix can be unit tested directly (constructing aSqlConnectionInternalin a unit test is impractical). Added:SqlConnectionOptionsTest.TestShouldDisableTnirWithAccessToken(netfx only) — token/no-token x Azure/non-Azure endpoint x explicit/absent TNIR keyword.ConnectionTests.AccessTokenStateIsPreservedInPoolKeyWhenSspiContextProviderIsSet— verified to fail without theSqlConnection.cschange.ConnectionTests.AccessTokenAndAccessTokenCallbackAreMutuallyExclusive— pins the invariant that makes the token pairing safe.Compatibility
Behavior change is limited to .NET Framework, and only for connections using
AccessTokenCallback, which now get the same TNIR default asAccessToken. Users who explicitly setTransparentNetworkIPResolutionin the connection string are unaffected — the explicit keyword still takes precedence, so the escape hatch documented in #4493 continues to work.Checklist
Suggested release note
Fixed
SqlConnection.AccessTokenCallbacknot disabling Transparent Network IP Resolution by default on .NET Framework, making it consistent withSqlConnection.AccessToken. Also fixed theAccessToken,AccessTokenCallbackandSspiContextProvidersetters discarding each other's values from the connection pool key.Notes for reviewers