Skip to content

feat(auth): [aiohttp] Add mTLS reconfiguration logic when certificate mismatch for existing credentials & Agent Identity workloads - #18224

Open
agrawalradhika-cell wants to merge 51 commits into
mainfrom
cert-rotation-aiohttp
Open

feat(auth): [aiohttp] Add mTLS reconfiguration logic when certificate mismatch for existing credentials & Agent Identity workloads #18224
agrawalradhika-cell wants to merge 51 commits into
mainfrom
cert-rotation-aiohttp

Conversation

@agrawalradhika-cell

@agrawalradhika-cell agrawalradhika-cell commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

feat: [aiohttp] Add mTLS reconfiguration logic when certificate mismatch for existing credentials & Agent Identity workloads

Changes included:

  • Adds mTLS reconfiguration logic when a certificate mismatch occurs.
  • Aligns async session behavior with the synchronous session by triggering a credential refresh and request retry on all 401 Unauthorized responses (not just mTLS).
  • Make sure to open an issue as a bug/issue before writing your code! That way we can discuss the change, evaluate designs, and agree on the general idea
  • Ensure the tests and linter pass
  • Code coverage does not decrease (if any source code was changed)
  • Appropriate docs were updated (if necessary)

Fixes #18227 #18227 🦕

@agrawalradhika-cell
agrawalradhika-cell requested review from a team as code owners August 26, 2026 04:00

@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 introduces client certificate rotation handling for asynchronous authorized sessions when encountering an unauthorized response under mTLS. The review feedback highlights a violation of the repository style guide regarding exception contract compliance, suggesting that the certificate parameter check should be wrapped in a try-except block to gracefully fall back to the original response rather than crashing. Additionally, the feedback recommends updating the corresponding unit tests to assert this resilient fallback behavior.

Comment thread packages/google-auth/google/auth/aio/transport/sessions.py Outdated
Comment thread packages/google-auth/tests/transport/aio/test_sessions_mtls.py Outdated
@agrawalradhika-cell agrawalradhika-cell changed the title feat: [aiohttp] Add reconfiguration logic when certificate mismatch for existing credentials & Agent Identity workloads feat: [aiohttp] Add mTLS reconfiguration logic when certificate mismatch for existing credentials & Agent Identity workloads Aug 26, 2026
agrawalradhika-cell and others added 2 commits August 26, 2026 10:34
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Handle exceptions during mTLS reconfiguration with warnings instead of errors.
…logs

Updated test logic to assert response instead of expecting an error.
…sync executor

Refactor unauthorized response handling to use async executor for MTLS parameter checks.
chore: Reset mTLS init task upon client certificate change
Signed-off-by: Radhika Agrawal <agrawalradhika@google.com>
Comment thread packages/google-auth/google/auth/aio/transport/sessions.py Outdated
Comment thread packages/google-auth/google/auth/aio/transport/sessions.py Outdated
Comment thread packages/google-auth/google/auth/aio/transport/sessions.py Outdated
Comment thread packages/google-auth/google/auth/aio/transport/sessions.py Outdated
Comment thread packages/google-auth/google/auth/aio/transport/sessions.py Outdated
Comment thread packages/google-auth/google/auth/aio/transport/sessions.py Outdated
Comment thread packages/google-auth/google/auth/aio/transport/sessions.py Outdated
Comment thread packages/google-auth/tests/transport/aio/test_sessions_mtls.py
Comment thread packages/google-auth/tests/transport/aio/test_sessions_mtls.py Outdated
Comment thread packages/google-auth/tests/transport/aio/test_sessions_mtls.py
…eck after 401 check

chore: Refactor mTLS channel reconfiguration logic for adding mTLS check after 401 check
Implement mTLS rotation lock to prevent race conditions during certificate reconfiguration.
chore: Change warning to error log for mTLS channel reconfiguration failure.
chore: Refactor mTLS handling for unauthorized responses
Remove unnecessary continue statement after mTLS configuration.
Refactor tests for certificate rotation and error handling in AsyncAuthorizedSession. Update test names for clarity and ensure proper logging of errors.
Signed-off-by: Radhika Agrawal <agrawalradhika@google.com>
Signed-off-by: Radhika Agrawal <agrawalradhika@google.com>
Handle RefreshError during credential refresh to prevent unhandled exceptions.
Signed-off-by: Radhika Agrawal <agrawalradhika@google.com>
Comment thread packages/google-auth/google/auth/aio/transport/sessions.py Outdated
Comment thread packages/google-auth/google/auth/aio/transport/sessions.py Outdated
Comment thread packages/google-auth/google/auth/aio/transport/sessions.py Outdated
Comment thread packages/google-auth/google/auth/aio/transport/sessions.py Outdated
Comment thread packages/google-auth/google/auth/aio/transport/sessions.py Outdated
Comment thread packages/google-auth/google/auth/aio/transport/sessions.py Outdated
Comment thread packages/google-auth/google/auth/aio/transport/sessions.py Outdated
Comment thread packages/google-auth/google/auth/aio/transport/sessions.py Outdated
Comment thread packages/google-auth/tests/transport/aio/test_sessions_mtls.py Outdated
Comment thread packages/google-auth/tests/transport/aio/test_sessions_mtls.py
Comment thread packages/google-auth/google/auth/aio/transport/sessions.py Outdated
Comment thread packages/google-auth/google/auth/aio/transport/sessions.py Outdated
Comment thread packages/google-auth/google/auth/aio/transport/sessions.py Outdated
Comment thread packages/google-auth/google/auth/aio/transport/sessions.py
Comment thread packages/google-auth/google/auth/aio/transport/sessions.py Outdated
Comment thread packages/google-auth/google/auth/aio/transport/sessions.py Outdated
Comment thread packages/google-auth/tests/transport/aio/test_sessions_mtls.py Outdated
@daniel-sanche daniel-sanche changed the title feat: [aiohttp] Add mTLS reconfiguration logic when certificate mismatch for existing credentials & Agent Identity workloads feat(auth): Add mTLS reconfiguration logic when certificate mismatch for existing credentials & Agent Identity workloads Aug 31, 2026
fix: Refactor type annotations and error handling
@agrawalradhika-cell agrawalradhika-cell changed the title feat(auth): Add mTLS reconfiguration logic when certificate mismatch for existing credentials & Agent Identity workloads feat(auth): [aiohttp] Add mTLS reconfiguration logic when certificate mismatch for existing credentials & Agent Identity workloads Aug 31, 2026
Comment thread packages/google-auth/google/auth/aio/transport/sessions.py Outdated
Comment thread packages/google-auth/google/auth/aio/transport/sessions.py
Comment thread packages/google-auth/google/auth/aio/transport/sessions.py Outdated

old_auth_request = self._auth_request
self._auth_request = AiohttpRequest(session=new_session)
self._old_auth_requests.append(old_auth_request)

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.

Every rotation appends the old AiohttpRequest to self._old_auth_requests. These requests stay open until session.close(). For long running services with periodic certificate rotation, this list grows without bounds, leaking connection pools and open sockets. Prune or close old sessions in the background after in flight requests finish.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the feedback!

It was an intentional decision to tie the cleanup of old sessions to AsyncAuthorizedSession.close() for a few practical reasons:

  • Low Frequency of Rotation: Certificate rotations for mTLS and Agent Identity typically occur on the scale of hours or days (e.g., every 12 to 24 hours). Even for a long-running service operating continuously for weeks or months, the list will only accumulate a very small number of stale session objects. The memory overhead is negligible in practice.
  • Natural Connection Timeouts (No Socket Leaks): Retaining an aiohttp.ClientSession object in a list does not mean its underlying TCP sockets stay open forever. HTTP connection pools rely on keep-alive timeouts. Once the final in-flight request finishes, the connection sits idle in the pool. Shortly after, the client-side timeout or the server-side idle timeout will trigger and cleanly close the TCP socket. The lightweight session object is retained in Python memory, but the underlying system sockets and connection pool resources are properly freed.
  • Complexity of Background Pruning: As noted in previous comments, closing the active ClientSession immediately aborts concurrent in-flight requests. Working around this by implementing a mechanism to "prune after in-flight requests finish" introduces significant architectural complexity. We would have to implement manual request reference-counting or orchestrate a background asyncio.Task to monitor the session. Background tasks in library code are notoriously tricky and carry a high risk of dangling task warnings on shutdown or unhandled exceptions.

Given the infrequency of rotations and the fact that idle sockets naturally time out and close on their own, accepting a slow-growing list of empty session objects seemed like the safest tradeoff compared to the complexity and risk of implementing background garbage collection.

Let me know if you still have concerns here

Comment thread packages/google-auth/google/auth/aio/transport/sessions.py Outdated
Comment thread packages/google-auth/tests/transport/aio/test_sessions_mtls.py Outdated
Comment thread packages/google-auth/tests/transport/aio/test_sessions_mtls.py
Refactor mTLS handling and improve timeout logic
Add a counter to track mTLS configuration checks and prevent redundant operations.
Add test for certificate rotation lock contention without cert change.
fix: Refactor mTLS configuration and error handling
fix: Fix indentation for asyncio test decorator
Added a delay in mock_check to ensure lock contention during asyncio.gather tasks. Adjusted assertions to verify behavior when non-mtls URL is used.
Remove assignment of stale_cert when is_mtls_endpoint is true.
Signed-off-by: Radhika Agrawal <agrawalradhika@google.com>
@agrawalradhika-cell

Copy link
Copy Markdown
Contributor Author

@agrawalradhika-cell agrawalradhika-cell added the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Sep 1, 2026
@yoshi-kokoro yoshi-kokoro removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Sep 1, 2026

@nolanleastin nolanleastin 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.

LGTM

Comment thread packages/google-auth/google/auth/aio/transport/sessions.py Outdated
Comment thread packages/google-auth/google/auth/aio/transport/sessions.py Outdated
method,
url,
data=data,
headers=headers,

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.

Passing headers=headers into the recursive self.request call forwards a dictionary that was already modified in place by self._credentials.before_request during the initial attempt.

To address this, consider creating a shallow copy on entry via request_headers = dict(headers) if headers is not None else {} for before_request and _auth_request, while passing the untouched original headers to the recursive retry (matching synchronous requests.py:L611, L706).

Comment on lines +410 to +412
await self.configure_mtls_channel(
self._client_cert_callback
)

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.

Calling await self.configure_mtls_channel(self._client_cert_callback) discards the (call_cert_bytes, call_key_bytes) already retrieved and validated at lines 374–375.

Re-running discovery causes two problems:

  1. It reads certificates from disk a second time unnecessarily, creating a race condition if files on disk change between check and use.
  2. In Enterprise Certificate Provider (ECP) setups where self._client_cert_callback is None, discovery via aio.transport.mtls fails because it does not support ECP. This returns False and silently disables mTLS instead of rotating the certificate.

Note that passing a temporary callback directly (e.g. returning the validated bytes) will overwrite self._client_cert_callback at line 191, which breaks future rotations.

To fix this, consider either:

  • Extracting transport recreation into a helper (e.g. _apply_client_cert(cert, key)) so the 401 handler can apply the validated bytes directly without touching self._client_cert_callback.
  • Preserving self._client_cert_callback around configure_mtls_channel with a try...finally block if reusing the existing method.

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.

Add cert rotation handling for aiohttp (Async HTTP)

5 participants