Skip to content

fix(auth): retry dropped token connections - #14873

Open
huong8373tt-beep wants to merge 2 commits into
googleapis:mainfrom
huong8373tt-beep:fix/auth-token-request-retries-14772
Open

huong8373tt-beep wants to merge 2 commits into
googleapis:mainfrom
huong8373tt-beep:fix/auth-token-request-retries-14772

Conversation

@huong8373tt-beep

Copy link
Copy Markdown

Summary

Retries dropped connections for OAuth2 token requests made by UserCredentials, ImpersonatedCredentials, and StsRequestHandler using the existing auth-library exponential-backoff settings.

The existing STS error tests now queue failures through the full retry budget, preserving their assertions that terminal OAuth and I/O failures are still surfaced.

Fixes #14772.

Validation

  • mvn -Dmaven.repo.local=E:\toolchains\m2repo -Dtest=UserCredentialsTest,ImpersonatedCredentialsTest,StsRequestHandlerTest -DfailIfNoTests=false -Dcheckstyle.skip=true -Dfmt.skip=true test
    • 101 tests passed
  • mvn -Dmaven.repo.local=E:\toolchains\m2repo -Dcheckstyle.skip=true fmt:format
  • git diff --check

Disclosure

Slai Agent Runtime performed the investigation, implementation, and test execution. The account owner authorized this contribution and is responsible for its content.

@huong8373tt-beep
huong8373tt-beep requested review from a team as code owners October 31, 2026 08:30

@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 adds automatic retry logic using ExponentialBackOff and HttpBackOffIOExceptionHandler to handle transient IOExceptions in ImpersonatedCredentials, StsRequestHandler, and UserCredentials, along with corresponding unit tests. The review feedback suggests refactoring the duplicated retry configuration code into a reusable utility method. Additionally, it highlights a discrepancy in the test setup where HTTP error responses are mocked as thrown IOExceptions, causing incorrect retry behavior in tests compared to production.

Comment on lines +813 to +820
request.setNumberOfRetries(OAuth2Utils.DEFAULT_NUMBER_OF_RETRIES);
ExponentialBackOff backoff =
new ExponentialBackOff.Builder()
.setInitialIntervalMillis(OAuth2Utils.INITIAL_RETRY_INTERVAL_MILLIS)
.setRandomizationFactor(OAuth2Utils.RETRY_RANDOMIZATION_FACTOR)
.setMultiplier(OAuth2Utils.RETRY_MULTIPLIER)
.build();
request.setIOExceptionHandler(new HttpBackOffIOExceptionHandler(backoff));

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.

medium

The exact same block of code for configuring ExponentialBackOff and HttpBackOffIOExceptionHandler is duplicated across ImpersonatedCredentials.java, StsRequestHandler.java, and UserCredentials.java. To improve maintainability and ensure consistency, consider refactoring this setup into a reusable helper method in a dedicated utility class (e.g., OAuth2Utils.initializeRetrySettings(HttpRequest request)) rather than duplicating it locally, avoiding tight coupling and inverted dependencies.

References
  1. When sharing constants or configuration logic across different modules, extract them into a dedicated utility class rather than duplicating them locally or importing them from high-level wrappers, to avoid tight coupling and inverted dependencies.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Addressed in c476a5f0: the shared ExponentialBackOff / HttpBackOffIOExceptionHandler setup now lives in OAuth2Utils.setDefaultRetrySettings(HttpRequest) and is reused by UserCredentials, ImpersonatedCredentials, and StsRequestHandler. The three focused test classes pass (101 tests).

Comment on lines +175 to +178
IOException error =
TestUtils.buildHttpResponseException(
"invalidRequest", /* errorDescription= */ null, /* errorUri= */ null));
"invalidRequest", /* errorDescription= */ null, /* errorUri= */ null);
transport.addResponseErrorSequence(error, error, error, error);

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.

medium

There is a subtle discrepancy between the test environment and production behavior here. In production, an HTTP error response (like 400 Bad Request representing invalidRequest) is returned by the transport as a standard HTTP response and is not thrown as an IOException by the low-level transport. Therefore, HttpBackOffIOExceptionHandler will not intercept it, and no retries will occur for HTTP error responses. However, in this test, TestUtils.buildHttpResponseException is queued in addResponseErrorSequence, which causes the mock transport to throw it directly from LowLevelHttpRequest.execute(). Because HttpResponseException extends IOException, the HttpBackOffIOExceptionHandler treats it as a network-level IOException and retries it, forcing us to queue the error 4 times to exhaust the retry budget. While queuing the error 4 times is a functional workaround to make the test pass, it is misleading because it implies that HTTP error responses are retried in production. If the mock transport supports it, consider configuring it to return a mock LowLevelHttpResponse with a 400 status code and the error JSON body instead of throwing an HttpResponseException directly.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Addressed in c476a5f0: this test now returns an actual HTTP 400 LowLevelHttpResponse with the OAuth error JSON. Only the dedicated dropped-connection tests use the IOException path, so the tests now match production retry behavior.

@huong8373tt-beep

Copy link
Copy Markdown
Author

Addressed the review feedback in commit c476a5f0:

  • Extracted the shared HttpBackOffIOExceptionHandler / ExponentialBackOff setup into OAuth2Utils.setDefaultRetrySettings(HttpRequest) and reused it from UserCredentials, ImpersonatedCredentials, and StsRequestHandler.
  • Changed the STS error tests to return a real HTTP 400 LowLevelHttpResponse with the OAuth error JSON instead of throwing HttpResponseException as an IOException. The dropped-connection tests still use the IOException path.

Validation after the change:

  • UserCredentialsTest, ImpersonatedCredentialsTest, and StsRequestHandlerTest: 101 passed
  • fmt:format: passed

Slai Agent Runtime performed the investigation, implementation, and test execution. The account owner authorized this contribution and is responsible for its content.

This branch has not been deployed

No deployments
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-library-java] Impersonated, user and STS token requests don't retry a dropped connection

1 participant