From f8d66d73fa41b3035ebd77ab134f1755430ab7d9 Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Tue, 9 Dec 2025 10:34:23 -0500 Subject: [PATCH 1/4] chore: Only enable renovate bot for CVE PRs (#1819) --- renovate.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/renovate.json b/renovate.json index 96b91bb21..3f59841a8 100644 --- a/renovate.json +++ b/renovate.json @@ -13,6 +13,12 @@ "ignorePaths": [ ".kokoro/requirements.txt" ], + "vulnerabilityAlerts": { + "enabled": true + }, + "maven": { + "enabled": false + }, "customManagers": [ { "customType": "regex", From e4c5b3cfd886c4bb3b709ba1209620a34f13b31e Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Wed, 3 Dec 2025 18:28:12 -0500 Subject: [PATCH 2/4] fix: Simplify call to directly retrieve the default service account from MDS (#1844) * fix: Simplify call to directly retrieve the default service account from MDS * chore: Make getDefaultServiceAccountUrl package-private --- .../auth/oauth2/ComputeEngineCredentials.java | 16 ++++++------- .../oauth2/ComputeEngineCredentialsTest.java | 4 ++-- .../oauth2/MockMetadataServerTransport.java | 23 ++++++------------- 3 files changed, 17 insertions(+), 26 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/ComputeEngineCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ComputeEngineCredentials.java index 48a6fbe6b..d36b5c3df 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ComputeEngineCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ComputeEngineCredentials.java @@ -69,7 +69,6 @@ import java.util.Collections; import java.util.Date; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.logging.Level; import java.util.logging.Logger; @@ -632,6 +631,12 @@ public static String getServiceAccountsUrl() { + "/computeMetadata/v1/instance/service-accounts/?recursive=true"; } + /** Url to retrieve the default service account entry from the Metadata Server. */ + static String getDefaultServiceAccountUrl() { + return getMetadataServerUrl(DefaultCredentialsProvider.DEFAULT) + + "/computeMetadata/v1/instance/service-accounts/default/email"; + } + public static String getIdentityDocumentUrl() { return getMetadataServerUrl(DefaultCredentialsProvider.DEFAULT) + "/computeMetadata/v1/instance/service-accounts/default/identity"; @@ -733,7 +738,7 @@ public byte[] sign(byte[] toSign) { private String getDefaultServiceAccount() throws IOException { HttpResponse response = - getMetadataResponse(getServiceAccountsUrl(), RequestType.UNTRACKED, false); + getMetadataResponse(getDefaultServiceAccountUrl(), RequestType.UNTRACKED, false); int statusCode = response.getStatusCode(); if (statusCode == HttpStatusCodes.STATUS_CODE_NOT_FOUND) { throw new IOException( @@ -756,12 +761,7 @@ private String getDefaultServiceAccount() throws IOException { // Mock transports will have success code with empty content by default. throw new IOException(METADATA_RESPONSE_EMPTY_CONTENT_ERROR_MESSAGE); } - GenericData responseData = response.parseAs(GenericData.class); - LoggingUtils.logResponsePayload( - responseData, LOGGER_PROVIDER, "Received default service account payload"); - Map defaultAccount = - OAuth2Utils.validateMap(responseData, "default", PARSE_ERROR_ACCOUNT); - return OAuth2Utils.validateString(defaultAccount, "email", PARSE_ERROR_ACCOUNT); + return response.parseAsString(); } public static class Builder extends GoogleCredentials.Builder { diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ComputeEngineCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ComputeEngineCredentialsTest.java index 4b1f9c1ca..a3df59e1a 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ComputeEngineCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ComputeEngineCredentialsTest.java @@ -590,7 +590,7 @@ public void getAccount_missing_throws() { new MockMetadataServerTransport() { @Override public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { - if (isGetServiceAccountsUrl(url)) { + if (isGetDefaultServiceAccountsUrl(url)) { return new MockLowLevelHttpRequest(url) { @Override public LowLevelHttpResponse execute() throws IOException { @@ -626,7 +626,7 @@ public void getAccount_emptyContent_throws() { new MockMetadataServerTransport() { @Override public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { - if (isGetServiceAccountsUrl(url)) { + if (isGetDefaultServiceAccountsUrl(url)) { return new MockLowLevelHttpRequest(url) { @Override public LowLevelHttpResponse execute() throws IOException { diff --git a/oauth2_http/javatests/com/google/auth/oauth2/MockMetadataServerTransport.java b/oauth2_http/javatests/com/google/auth/oauth2/MockMetadataServerTransport.java index e7ac6c09d..725a124fc 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/MockMetadataServerTransport.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/MockMetadataServerTransport.java @@ -129,8 +129,8 @@ public LowLevelHttpRequest buildRequest(String method, String url) throws IOExce if (url.startsWith(ComputeEngineCredentials.getTokenServerEncodedUrl())) { this.request = getMockRequestForTokenEndpoint(url); return this.request; - } else if (isGetServiceAccountsUrl(url)) { - this.request = getMockRequestForServiceAccount(url); + } else if (isGetDefaultServiceAccountsUrl(url)) { + this.request = getMockRequestForDefaultServiceAccount(url); return this.request; } else if (isSignRequestUrl(url)) { this.request = getMockRequestForSign(url); @@ -176,22 +176,13 @@ public LowLevelHttpResponse execute() throws IOException { }; } - private MockLowLevelHttpRequest getMockRequestForServiceAccount(String url) { + private MockLowLevelHttpRequest getMockRequestForDefaultServiceAccount(String url) { return new MockLowLevelHttpRequest(url) { @Override - public LowLevelHttpResponse execute() throws IOException { - // Create the JSON response - GenericJson serviceAccountsContents = new GenericJson(); - serviceAccountsContents.setFactory(OAuth2Utils.JSON_FACTORY); - GenericJson defaultAccount = new GenericJson(); - defaultAccount.put("email", serviceAccountEmail); - serviceAccountsContents.put("default", defaultAccount); - - String serviceAccounts = serviceAccountsContents.toPrettyString(); - + public LowLevelHttpResponse execute() { return new MockLowLevelHttpResponse() .setContentType(Json.MEDIA_TYPE) - .setContent(serviceAccounts); + .setContent(serviceAccountEmail); } }; } @@ -341,8 +332,8 @@ public LowLevelHttpResponse execute() throws IOException { }; } - protected boolean isGetServiceAccountsUrl(String url) { - return url.equals(ComputeEngineCredentials.getServiceAccountsUrl()); + protected boolean isGetDefaultServiceAccountsUrl(String url) { + return url.equals(ComputeEngineCredentials.getDefaultServiceAccountUrl()); } protected boolean isSignRequestUrl(String url) { From 9abaca9d71f2292f37d54a4be15adf0f621775c2 Mon Sep 17 00:00:00 2001 From: Lawrence Qiu Date: Wed, 3 Dec 2025 13:09:31 -0500 Subject: [PATCH 3/4] fix: Add configurable connect and read timeouts for IdentityPoolCredentials requests (#1847) * fix: Add configurable connect and read timeouts to STS requests * chore: Fix lint issues * chore: Add connect and read timeout to identity pool creds * chore: Propogate connect and read timeouts to all calls --- .../oauth2/ExternalAccountCredentials.java | 31 +++++++++++- .../auth/oauth2/ImpersonatedCredentials.java | 33 +++++++++++++ .../google/auth/oauth2/StsRequestHandler.java | 49 ++++++++++++------- 3 files changed, 95 insertions(+), 18 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java index 2e95379ba..7f9f0c207 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java @@ -36,6 +36,7 @@ import com.google.api.client.http.HttpHeaders; import com.google.api.client.json.GenericJson; import com.google.api.client.util.Data; +import com.google.api.core.InternalApi; import com.google.auth.RequestMetadataCallback; import com.google.auth.http.HttpTransportFactory; import com.google.common.base.MoreObjects; @@ -97,6 +98,9 @@ public abstract class ExternalAccountCredentials extends GoogleCredentials { private EnvironmentProvider environmentProvider; + private int connectTimeout; + private int readTimeout; + /** * Constructor with minimum identifying information and custom HTTP transport. Does not support * workforce credentials. @@ -271,6 +275,8 @@ protected ExternalAccountCredentials(ExternalAccountCredentials.Builder builder) : builder.metricsHandler; this.name = GoogleCredentialsInfo.EXTERNAL_ACCOUNT_CREDENTIALS.getCredentialName(); + this.connectTimeout = builder.connectTimeout; + this.readTimeout = builder.readTimeout; } ImpersonatedCredentials buildImpersonatedCredentials() { @@ -305,6 +311,8 @@ ImpersonatedCredentials buildImpersonatedCredentials() { .setScopes(new ArrayList<>(scopes)) .setLifetime(this.serviceAccountImpersonationOptions.lifetime) .setIamEndpointOverride(serviceAccountImpersonationUrl) + .setConnectTimeout(connectTimeout) + .setReadTimeout(readTimeout) .build(); } @@ -533,7 +541,9 @@ protected AccessToken exchangeExternalCredentialForAccessToken( StsRequestHandler.Builder requestHandler = StsRequestHandler.newBuilder( - tokenUrl, stsTokenExchangeRequest, transportFactory.create().createRequestFactory()); + tokenUrl, stsTokenExchangeRequest, transportFactory.create().createRequestFactory()) + .setConnectTimeout(connectTimeout) + .setReadTimeout(readTimeout); // If this credential was initialized with a Workforce configuration then the // workforcePoolUserProject must be passed to the Security Token Service via the internal @@ -771,6 +781,9 @@ public abstract static class Builder extends GoogleCredentials.Builder { @Nullable protected String workforcePoolUserProject; @Nullable protected ServiceAccountImpersonationOptions serviceAccountImpersonationOptions; + protected int connectTimeout = 20000; // Default to 20000ms = 20s + protected int readTimeout = 20000; // Default to 20000ms = 20s + /* The field is not being used and value not set. Superseded by the same field in the {@link GoogleCredentials.Builder}. */ @@ -796,6 +809,8 @@ protected Builder(ExternalAccountCredentials credentials) { this.workforcePoolUserProject = credentials.workforcePoolUserProject; this.serviceAccountImpersonationOptions = credentials.serviceAccountImpersonationOptions; this.metricsHandler = credentials.metricsHandler; + this.connectTimeout = credentials.connectTimeout; + this.readTimeout = credentials.readTimeout; } /** @@ -988,6 +1003,20 @@ public Builder setUniverseDomain(String universeDomain) { return this; } + /** Warning: Not for public use and can be removed at any time. */ + @InternalApi + public Builder setConnectTimeout(int connectTimeout) { + this.connectTimeout = connectTimeout; + return this; + } + + /** Warning: Not for public use and can be removed at any time. */ + @InternalApi + public Builder setReadTimeout(int readTimeout) { + this.readTimeout = readTimeout; + return this; + } + /** * Sets the optional Environment Provider. * diff --git a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java index 77c319e0b..5b3df6fec 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java @@ -44,6 +44,7 @@ import com.google.api.client.json.GenericJson; import com.google.api.client.json.JsonObjectParser; import com.google.api.client.util.GenericData; +import com.google.api.core.InternalApi; import com.google.auth.CredentialTypeForMetrics; import com.google.auth.ServiceAccountSigner; import com.google.auth.http.HttpCredentialsAdapter; @@ -117,6 +118,9 @@ public class ImpersonatedCredentials extends GoogleCredentials private transient Calendar calendar; + private int connectTimeout; + private int readTimeout; + /** * @param sourceCredentials the source credential used to acquire the impersonated credentials. It * should be either a user account credential or a service account credential. @@ -559,6 +563,8 @@ private ImpersonatedCredentials(Builder builder) throws IOException { + "does not match %s universe domain set for impersonated credentials.", this.sourceCredentials.getUniverseDomain(), builder.getUniverseDomain())); } + this.connectTimeout = builder.connectTimeout; + this.readTimeout = builder.readTimeout; } /** @@ -587,6 +593,12 @@ public AccessToken refreshAccessToken() throws IOException { || (isDefaultUniverseDomain() && ((ServiceAccountCredentials) this.sourceCredentials) .shouldUseAssertionFlowForGdu())) { + if (this.sourceCredentials instanceof IdentityPoolCredentials) { + this.sourceCredentials = + ((IdentityPoolCredentials) this.sourceCredentials) + .toBuilder().setConnectTimeout(connectTimeout).setReadTimeout(readTimeout).build(); + } + try { this.sourceCredentials.refreshIfExpired(); } catch (IOException e) { @@ -616,6 +628,8 @@ public AccessToken refreshAccessToken() throws IOException { HttpContent requestContent = new JsonHttpContent(parser.getJsonFactory(), body); HttpRequest request = requestFactory.buildPostRequest(url, requestContent); + request.setConnectTimeout(connectTimeout); + request.setReadTimeout(readTimeout); adapter.initialize(request); request.setParser(parser); MetricsUtils.setMetricsHeader( @@ -746,6 +760,9 @@ public static class Builder extends GoogleCredentials.Builder { private String iamEndpointOverride; private Calendar calendar = Calendar.getInstance(); + private int connectTimeout = 20000; // Default to 20000ms = 20s + private int readTimeout = 20000; // Default to 20000ms = 20s + protected Builder() {} /** @@ -769,6 +786,8 @@ protected Builder(ImpersonatedCredentials credentials) { this.lifetime = credentials.lifetime; this.transportFactory = credentials.transportFactory; this.iamEndpointOverride = credentials.iamEndpointOverride; + this.connectTimeout = credentials.connectTimeout; + this.readTimeout = credentials.readTimeout; } @CanIgnoreReturnValue @@ -860,6 +879,20 @@ public Builder setCalendar(Calendar calendar) { return this; } + /** Warning: Not for public use and can be removed at any time. */ + @InternalApi + public Builder setConnectTimeout(int connectTimeout) { + this.connectTimeout = connectTimeout; + return this; + } + + /** Warning: Not for public use and can be removed at any time. */ + @InternalApi + public Builder setReadTimeout(int readTimeout) { + this.readTimeout = readTimeout; + return this; + } + public Calendar getCalendar() { return this.calendar; } diff --git a/oauth2_http/java/com/google/auth/oauth2/StsRequestHandler.java b/oauth2_http/java/com/google/auth/oauth2/StsRequestHandler.java index bd7a2d21f..b2e635524 100644 --- a/oauth2_http/java/com/google/auth/oauth2/StsRequestHandler.java +++ b/oauth2_http/java/com/google/auth/oauth2/StsRequestHandler.java @@ -42,6 +42,7 @@ import com.google.api.client.json.JsonObjectParser; import com.google.api.client.json.JsonParser; import com.google.api.client.util.GenericData; +import com.google.api.core.InternalApi; import com.google.common.base.Joiner; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.IOException; @@ -73,26 +74,22 @@ public final class StsRequestHandler { @Nullable private final HttpHeaders headers; @Nullable private final String internalOptions; + private final int connectTimeout; + private final int readTimeout; + /** * Internal constructor. * - * @param tokenExchangeEndpoint the token exchange endpoint - * @param request the token exchange request - * @param headers optional additional headers to pass along the request - * @param internalOptions optional GCP specific STS options * @return an StsTokenExchangeResponse instance if the request was successful */ - private StsRequestHandler( - String tokenExchangeEndpoint, - StsTokenExchangeRequest request, - HttpRequestFactory httpRequestFactory, - @Nullable HttpHeaders headers, - @Nullable String internalOptions) { - this.tokenExchangeEndpoint = tokenExchangeEndpoint; - this.request = request; - this.httpRequestFactory = httpRequestFactory; - this.headers = headers; - this.internalOptions = internalOptions; + private StsRequestHandler(Builder builder) { + this.tokenExchangeEndpoint = builder.tokenExchangeEndpoint; + this.request = builder.request; + this.httpRequestFactory = builder.httpRequestFactory; + this.headers = builder.headers; + this.internalOptions = builder.internalOptions; + this.connectTimeout = builder.connectTimeout; + this.readTimeout = builder.readTimeout; } /** @@ -120,6 +117,8 @@ public StsTokenExchangeResponse exchangeToken() throws IOException { if (headers != null) { httpRequest.setHeaders(headers); } + httpRequest.setConnectTimeout(connectTimeout); + httpRequest.setReadTimeout(readTimeout); try { HttpResponse response = httpRequest.execute(); @@ -214,6 +213,9 @@ public static class Builder { @Nullable private HttpHeaders headers; @Nullable private String internalOptions; + private int connectTimeout = 20000; // Default to 20000ms = 20s + private int readTimeout = 20000; // Default to 20000ms = 20s + private Builder( String tokenExchangeEndpoint, StsTokenExchangeRequest stsTokenExchangeRequest, @@ -235,9 +237,22 @@ public StsRequestHandler.Builder setInternalOptions(String internalOptions) { return this; } + /** Warning: Not for public use and can be removed at any time. */ + @InternalApi + public StsRequestHandler.Builder setConnectTimeout(int connectTimeout) { + this.connectTimeout = connectTimeout; + return this; + } + + /** Warning: Not for public use and can be removed at any time. */ + @InternalApi + public StsRequestHandler.Builder setReadTimeout(int readTimeout) { + this.readTimeout = readTimeout; + return this; + } + public StsRequestHandler build() { - return new StsRequestHandler( - tokenExchangeEndpoint, request, httpRequestFactory, headers, internalOptions); + return new StsRequestHandler(this); } } } From 7f9f991eb45a105dc5f42fd27e16a2c962c71a6e Mon Sep 17 00:00:00 2001 From: diegomarquezp Date: Wed, 10 Dec 2025 17:25:54 +0000 Subject: [PATCH 4/4] chore: set base branch last release to 1.41.0 --- .release-please-manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 3d6e0227e..4bce58a11 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.41.0-rc1" + ".": "1.41.0" } \ No newline at end of file