In google-auth, the configure_mtls_channel() method in transport adapters (requests, urllib3, and aiohttp) exits early when it detects that mTLS should not be used (i.e. check_use_client_cert() evaluates to False).
However, if mTLS was previously enabled on the session, exiting early sets self._is_mtls = False but leaves the existing mTLS adapters attached to the transport layer. This creates a "zombie state" mismatch: the SDK bypasses future mTLS rotation logic because it believes mTLS is disabled, but the active network session continues to use the old, invalid client certificates indefinitely, resulting in a perpetual loop of 401 Unauthorized responses.
Expected behavior:
While it might seem intuitive to gracefully tear down the old mTLS adapters and fall back to standard TLS, dynamically unmounting adapters or replacing connection pools mid-lifecycle is unsafe in concurrent environments. It violates the thread-safety design of the underlying HTTP libraries (requests, urllib3, aiohttp) and causes unpredictable crashes (e.g., RuntimeError: dictionary changed size during iteration) and connection drops.
Therefore, when transitioning from an mTLS-enabled state to an mTLS-disabled state mid-lifecycle, configure_mtls_channel() should fail loudly by raising a MutualTLSChannelError. This halts the infinite 401 retry loop, guarantees thread-safety for other active requests, and explicitly signals to the developer that the environment has shifted and a new AuthorizedSession must be instantiated.
Affected paths:
google/auth/transport/requests.py
google/auth/transport/urllib3.py
google/auth/aio/transport/sessions.py
In
google-auth, theconfigure_mtls_channel()method in transport adapters (requests,urllib3, andaiohttp) exits early when it detects that mTLS should not be used (i.e.check_use_client_cert()evaluates toFalse).However, if mTLS was previously enabled on the session, exiting early sets
self._is_mtls = Falsebut leaves the existing mTLS adapters attached to the transport layer. This creates a "zombie state" mismatch: the SDK bypasses future mTLS rotation logic because it believes mTLS is disabled, but the active network session continues to use the old, invalid client certificates indefinitely, resulting in a perpetual loop of401 Unauthorizedresponses.Expected behavior:
While it might seem intuitive to gracefully tear down the old mTLS adapters and fall back to standard TLS, dynamically unmounting adapters or replacing connection pools mid-lifecycle is unsafe in concurrent environments. It violates the thread-safety design of the underlying HTTP libraries (
requests,urllib3,aiohttp) and causes unpredictable crashes (e.g.,RuntimeError: dictionary changed size during iteration) and connection drops.Therefore, when transitioning from an mTLS-enabled state to an mTLS-disabled state mid-lifecycle,
configure_mtls_channel()should fail loudly by raising aMutualTLSChannelError. This halts the infinite 401 retry loop, guarantees thread-safety for other active requests, and explicitly signals to the developer that the environment has shifted and a newAuthorizedSessionmust be instantiated.Affected paths:
google/auth/transport/requests.pygoogle/auth/transport/urllib3.pygoogle/auth/aio/transport/sessions.py