Skip to content

fix(google-auth): fail loudly when dynamically disabling mTLS on active sessions - #18005

Open
gorthitk wants to merge 1 commit into
googleapis:mainfrom
gorthitk:fix/mtls-prevent-disable-on-active-session
Open

fix(google-auth): fail loudly when dynamically disabling mTLS on active sessions#18005
gorthitk wants to merge 1 commit into
googleapis:mainfrom
gorthitk:fix/mtls-prevent-disable-on-active-session

Conversation

@gorthitk

@gorthitk gorthitk commented Aug 5, 2026

Copy link
Copy Markdown

When mTLS is previously enabled on an active transport session (requests, urllib3, or aiohttp), attempting to disable mTLS mid-lifecycle in configure_mtls_channel() now raises a MutualTLSChannelError instead of exiting early or replacing adapters.

This prevents thread-safety violations from connection pool mutation and eliminates zombie state mismatches where active sessions continue sending client certificates while auth checks treat mTLS as disabled.

Fixes #17761

…ve sessions (googleapis#17761)

When mTLS is previously enabled on an active transport session (requests,
urllib3, or aiohttp), attempting to disable mTLS mid-lifecycle in
configure_mtls_channel() now raises a MutualTLSChannelError instead of
exiting early or replacing adapters.

This prevents thread-safety violations from connection pool mutation and
eliminates zombie state mismatches where active sessions continue sending
client certificates while auth checks treat mTLS as disabled.
@gorthitk
gorthitk requested review from a team as code owners August 5, 2026 19:08
@gorthitk gorthitk changed the title fix(google-auth): fail loudly when dynamically disabling mTLS on acti… fix(google-auth): fail loudly when dynamically disabling mTLS on active sessions Aug 5, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request prevents mid-lifecycle transitions from mTLS-enabled to mTLS-disabled states on active sessions across the aio, requests, and urllib3 transports by raising a MutualTLSChannelError. However, the review feedback highlights that raising this exception replaces historical graceful fallback behaviors (such as falling back to standard TLS or returning False), which introduces breaking changes for downstream users and violates backwards compatibility.

Comment on lines +188 to +191
if getattr(self, "_is_mtls", False):
raise exceptions.MutualTLSChannelError(
"Cannot disable mTLS on an active session. A new AuthorizedSession must be created."
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Raising a MutualTLSChannelError exception here replaces the historical graceful fallback behavior with an exception. This introduces breaking changes for downstream users and violates backwards compatibility.

References
  1. Do not replace historical graceful fallback behaviors (such as returning False/falling back to standard TLS) with exceptions if doing so would introduce breaking changes for downstream users and violate backwards compatibility.

Comment on lines +202 to +205
if getattr(self, "_is_mtls", False) and not is_mtls:
raise exceptions.MutualTLSChannelError(
"Cannot disable mTLS on an active session. A new AuthorizedSession must be created."
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Raising a MutualTLSChannelError exception here replaces the historical graceful fallback behavior with an exception. This introduces breaking changes for downstream users and violates backwards compatibility.

References
  1. Do not replace historical graceful fallback behaviors (such as returning False/falling back to standard TLS) with exceptions if doing so would introduce breaking changes for downstream users and violate backwards compatibility.

Comment on lines +475 to +478
if getattr(self, "_is_mtls", False):
raise exceptions.MutualTLSChannelError(
"Cannot disable mTLS on an active session. A new AuthorizedSession must be created."
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Raising a MutualTLSChannelError exception here replaces the historical graceful fallback behavior (falling back to standard TLS/HTTPAdapter) with an exception. This introduces breaking changes for downstream users and violates backwards compatibility.

References
  1. Do not replace historical graceful fallback behaviors (such as returning False/falling back to standard TLS) with exceptions if doing so would introduce breaking changes for downstream users and violate backwards compatibility.

Comment on lines +491 to +494
if getattr(self, "_is_mtls", False) and not is_mtls:
raise exceptions.MutualTLSChannelError(
"Cannot disable mTLS on an active session. A new AuthorizedSession must be created."
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Raising a MutualTLSChannelError exception here replaces the historical graceful fallback behavior with an exception. This introduces breaking changes for downstream users and violates backwards compatibility.

References
  1. Do not replace historical graceful fallback behaviors (such as returning False/falling back to standard TLS) with exceptions if doing so would introduce breaking changes for downstream users and violate backwards compatibility.

Comment on lines +356 to +359
if getattr(self, "_is_mtls", False):
raise exceptions.MutualTLSChannelError(
"Cannot disable mTLS on an active session. A new AuthorizedHttp must be created."
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Raising a MutualTLSChannelError exception here replaces the historical graceful fallback behavior (returning False and falling back to standard TLS/PoolManager) with an exception. This introduces breaking changes for downstream users and violates backwards compatibility.

References
  1. Do not replace historical graceful fallback behaviors (such as returning False/falling back to standard TLS) with exceptions if doing so would introduce breaking changes for downstream users and violate backwards compatibility.

Comment on lines +368 to +371
if getattr(self, "_is_mtls", False) and not found_cert_key:
raise exceptions.MutualTLSChannelError(
"Cannot disable mTLS on an active session. A new AuthorizedHttp must be created."
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Raising a MutualTLSChannelError exception here replaces the historical graceful fallback behavior with an exception. This introduces breaking changes for downstream users and violates backwards compatibility.

References
  1. Do not replace historical graceful fallback behaviors (such as returning False/falling back to standard TLS) with exceptions if doing so would introduce breaking changes for downstream users and violate backwards compatibility.

)
if not use_client_cert:
# Dynamically disabling mTLS on an active session is unsafe in concurrent
# environments and can cause a zombie state mismatch where mTLS contexts

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
# environments and can cause a zombie state mismatch where mTLS contexts
# environments and can cause a state mismatch where mTLS contexts

# Dynamically disabling mTLS on an active session is unsafe in concurrent
# environments and can cause a zombie state mismatch where mTLS contexts
# remain attached while auth checks believe mTLS is disabled.
if getattr(self, "_is_mtls", False):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

self._is_mtls is explicitly initialized to False in init across AsyncAuthorizedSession, AuthorizedSession, and AuthorizedHttp. Using getattr(self, "_is_mtls", False) is unnecessary, we can access self._is_mtls directly.

# remain attached while auth checks believe mTLS is disabled.
if getattr(self, "_is_mtls", False):
raise exceptions.MutualTLSChannelError(
"Cannot disable mTLS on an active session. A new AuthorizedSession must be created."

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please update this to reference AsyncAuthorizedSession instead of AuthorizedSession so async users aren't directed to the synchronous requests transport class.

Suggested change
"Cannot disable mTLS on an active session. A new AuthorizedSession must be created."
"Cannot disable mTLS on an active session. A new AsyncAuthorizedSession must be created."

# Prevent mid-lifecycle transition from mTLS-enabled to mTLS-disabled state.
if getattr(self, "_is_mtls", False) and not is_mtls:
raise exceptions.MutualTLSChannelError(
"Cannot disable mTLS on an active session. A new AuthorizedSession must be created."

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

Suggested change
"Cannot disable mTLS on an active session. A new AuthorizedSession must be created."
"Cannot disable mTLS on an active session. A new AsyncAuthorizedSession must be created."

use_client_cert = google.auth.transport._mtls_helper.check_use_client_cert()
if not use_client_cert:
# Dynamically disabling mTLS on an active session is unsafe in concurrent
# environments and can cause a zombie state mismatch where mTLS adapters

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit

Suggested change
# environments and can cause a zombie state mismatch where mTLS adapters
# environments and can cause a state mismatch where mTLS adapters

use_client_cert = transport._mtls_helper.check_use_client_cert()
if not use_client_cert:
# Dynamically disabling mTLS on an active session is unsafe in concurrent
# environments and can cause a zombie state mismatch where mTLS connection

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
# environments and can cause a zombie state mismatch where mTLS connection
# environments and can cause a state mismatch where mTLS connection

# Dynamically disabling mTLS on an active session is unsafe in concurrent
# environments and can cause a zombie state mismatch where mTLS contexts
# remain attached while auth checks believe mTLS is disabled.
if getattr(self, "_is_mtls", False):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checking is_mtls is a good start, but I think it might not be enough. Since this is a flag that we set manually when attempting to establish mTLS, it might not always accurately reflect the transport state. We have previously identified and resolved bugs where is_mtls was out of sync with the reality of the transport. Relying on this check on its own makes me a bit nervous, especially since we're changing the behavior to raise an exception where we used to silently pass. Can we verify the transport adapter type or SSL context presence directly?

This applies to the other transports as well.


# Prevent mid-lifecycle transition from mTLS-enabled to mTLS-disabled state.
if getattr(self, "_is_mtls", False) and not is_mtls:
raise exceptions.MutualTLSChannelError(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Raising exceptions.MutualTLSChannelError here causes it to be caught by the generic except Exception as caught_exc: handler at line 243. The handler then wraps it into MutualTLSChannelError(caught_exc), resulting in a double-wrapped exception (MutualTLSChannelError(MutualTLSChannelError(...))).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

google-auth: configure_mtls_channel() creates zombie state when dynamically disabled and should fail loudly

2 participants