Fix AccessTokenCallback to match AccessToken behavior for TNIR and connection pool keys - #4520
Open
cheenamalhotra wants to merge 5 commits into
Open
Fix AccessTokenCallback to match AccessToken behavior for TNIR and connection pool keys#4520cheenamalhotra wants to merge 5 commits into
cheenamalhotra wants to merge 5 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 #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
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Cheena Malhotra <13396919+cheenamalhotra@users.noreply.github.com>
Closed
4 tasks
Copilot stopped reviewing on behalf of
cheenamalhotra due to an error
August 8, 2026 06:34
Contributor
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR tightens behavior around federated authentication state by ensuring access-token state is consistently represented in connection pooling keys and by unifying “caller-supplied token” detection for TNIR and pre-login logic.
Changes:
- Preserve
AccessToken/AccessTokenCallback/SspiContextProviderinConnectionPoolKeyupdates to avoid silently dropping authentication state. - Introduce
SqlConnectionInternal.IsAccessTokenProvidedand apply it in pre-login and certificate validation checks. - Add NETFRAMEWORK TNIR tests and simulated server tests covering token/callback state and mutual exclusivity.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionTests.cs | Adds regression tests for pool-key preservation and token/callback mutual exclusivity. |
| src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlConnectionOptionsTest.cs | Adds NETFRAMEWORK theory coverage for TNIR disablement with caller-supplied tokens. |
| src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParser.cs | Switches token detection to unified IsAccessTokenProvided. |
| src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnection.cs | Updates pool-key rebuilds to preserve other auth-related properties. |
| src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Connection/SqlConnectionInternal.cs | Adds IsAccessTokenProvided and refactors NETFRAMEWORK TNIR logic to accept it as an input. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
* Fully qualify the SqlConnection crefs on ShouldDisableTnir so they match the form already used on IsAccessTokenProvided. * Rename TestShouldDisableTnirWithAccessToken to TestShouldDisableTnirWithCallerSuppliedToken, since the parameter is isAccessTokenProvided and the case covers both AccessToken and AccessTokenCallback. * Assign a real non-null SspiContextProvider in the pool-key test instead of null, via a minimal TestSspiContextProvider stub, so the test matches its name and exercises the setter the way callers actually do. * Add SspiContextProviderIsPreservedInPoolKeyWhenAccessTokenStateIsSet, the reciprocal case: assigning a token must not drop a configured SspiContextProvider from the pool key. Both pool-key tests were verified to fail when the SqlConnection.cs fix is reverted. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5368f578-219b-40a6-92a9-4742b56edbe6
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.
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
dotnet/SqlClientso the full CI/ADO pipeline suite runs.