From 41f237c69449355c273e115c6827bc2b7c61a0d8 Mon Sep 17 00:00:00 2001 From: psx95 Date: Thu, 16 Oct 2025 17:15:55 -0400 Subject: [PATCH 1/2] feat: Add getProjectId to ComputeEngineCredentials --- .../auth/oauth2/ComputeEngineCredentials.java | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/oauth2_http/java/com/google/auth/oauth2/ComputeEngineCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ComputeEngineCredentials.java index 5faf29fdb..b18084edc 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ComputeEngineCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ComputeEngineCredentials.java @@ -130,6 +130,7 @@ public class ComputeEngineCredentials extends GoogleCredentials private transient HttpTransportFactory transportFactory; private String universeDomainFromMetadata = null; + private String projectId = null; /** * Experimental Feature. @@ -341,6 +342,72 @@ private String getUniverseDomainFromMetadata() throws IOException { return responseString; } + /** + * Retrieves the Google Cloud project ID from the Compute Engine (GCE) metadata server. + *

+ * On its first successful execution, it fetches the project ID and caches it for the lifetime + * of the object. Subsequent calls will return the cached value without making additional network + * requests. + *

+ * If the request to the metadata server fails (e.g., due to network issues, or if the VM lacks + * the required service account permissions), the method will attempt to fall back to a default + * project ID provider which could be {@code null}. + * + * @return the GCP project ID string, or {@code null} if the metadata server is + * inaccessible and no fallback project ID can be determined. + */ + @Override + public String getProjectId() { + synchronized (this) { + if (this.projectId != null) { + return this.projectId; + } + } + + String projectIdFromMetadata = getProjectIdFromMetadata(); + synchronized (this) { + this.projectId = projectIdFromMetadata; + } + return projectIdFromMetadata; + } + + private String getProjectIdFromMetadata() { + try { + HttpResponse response = getMetadataResponse(getProjectIdUrl(), RequestType.UNTRACKED, false); + int statusCode = response.getStatusCode(); + if (statusCode == HttpStatusCodes.STATUS_CODE_NOT_FOUND) { + LoggingUtils.log(LOGGER_PROVIDER, Level.WARNING, Collections.emptyMap(), String.format( + "Error code %s trying to get project ID from" + + " Compute Engine metadata. This may be because the virtual machine instance" + + " does not have permission scopes specified.", + statusCode)); + return super.getProjectId(); + } + if (statusCode != HttpStatusCodes.STATUS_CODE_OK) { + LoggingUtils.log( + LOGGER_PROVIDER, + Level.WARNING, + Collections.emptyMap(), + String.format( + "Unexpected Error code %s trying to get project ID" + + " from Compute Engine metadata for the default service account: %s", + statusCode, response.parseAsString())); + return super.getProjectId(); + } + return response.parseAsString(); + } catch (IOException e) { + LoggingUtils.log( + LOGGER_PROVIDER, + Level.WARNING, + Collections.emptyMap(), + String.format( + "Unexpected Error: %s trying to get project ID" + + " from Compute Engine metadata server. Reason: %s", + e.getMessage(), e.getCause().toString())); + return super.getProjectId(); + } + } + /** Refresh the access token by getting it from the GCE metadata server */ @Override public AccessToken refreshAccessToken() throws IOException { @@ -642,6 +709,11 @@ public static String getIdentityDocumentUrl() { + "/computeMetadata/v1/instance/service-accounts/default/identity"; } + public static String getProjectIdUrl() { + return getMetadataServerUrl(DefaultCredentialsProvider.DEFAULT) + + "/computeMetadata/v1/project/project-id"; + } + @Override public int hashCode() { return Objects.hash(transportFactoryClassName); From 1f831f05faddfc58c85233aa7d11280cd7197065 Mon Sep 17 00:00:00 2001 From: psx95 Date: Thu, 16 Oct 2025 17:28:26 -0400 Subject: [PATCH 2/2] chore: Fix lint issues --- .../auth/oauth2/ComputeEngineCredentials.java | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/ComputeEngineCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ComputeEngineCredentials.java index b18084edc..4d4980f02 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ComputeEngineCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ComputeEngineCredentials.java @@ -344,17 +344,17 @@ private String getUniverseDomainFromMetadata() throws IOException { /** * Retrieves the Google Cloud project ID from the Compute Engine (GCE) metadata server. - *

- * On its first successful execution, it fetches the project ID and caches it for the lifetime + * + *

On its first successful execution, it fetches the project ID and caches it for the lifetime * of the object. Subsequent calls will return the cached value without making additional network * requests. - *

- * If the request to the metadata server fails (e.g., due to network issues, or if the VM lacks + * + *

If the request to the metadata server fails (e.g., due to network issues, or if the VM lacks * the required service account permissions), the method will attempt to fall back to a default * project ID provider which could be {@code null}. * - * @return the GCP project ID string, or {@code null} if the metadata server is - * inaccessible and no fallback project ID can be determined. + * @return the GCP project ID string, or {@code null} if the metadata server is inaccessible and + * no fallback project ID can be determined. */ @Override public String getProjectId() { @@ -376,11 +376,15 @@ private String getProjectIdFromMetadata() { HttpResponse response = getMetadataResponse(getProjectIdUrl(), RequestType.UNTRACKED, false); int statusCode = response.getStatusCode(); if (statusCode == HttpStatusCodes.STATUS_CODE_NOT_FOUND) { - LoggingUtils.log(LOGGER_PROVIDER, Level.WARNING, Collections.emptyMap(), String.format( - "Error code %s trying to get project ID from" - + " Compute Engine metadata. This may be because the virtual machine instance" - + " does not have permission scopes specified.", - statusCode)); + LoggingUtils.log( + LOGGER_PROVIDER, + Level.WARNING, + Collections.emptyMap(), + String.format( + "Error code %s trying to get project ID from" + + " Compute Engine metadata. This may be because the virtual machine instance" + + " does not have permission scopes specified.", + statusCode)); return super.getProjectId(); } if (statusCode != HttpStatusCodes.STATUS_CODE_OK) { @@ -403,7 +407,7 @@ private String getProjectIdFromMetadata() { String.format( "Unexpected Error: %s trying to get project ID" + " from Compute Engine metadata server. Reason: %s", - e.getMessage(), e.getCause().toString())); + e.getMessage(), e.getCause().toString())); return super.getProjectId(); } }